Botan 3.9.0
Crypto and TLS for C&
pssr.cpp
Go to the documentation of this file.
1/*
2* PSSR
3* (C) 1999-2007,2017,2023 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/pssr.h>
9
10#include <botan/exceptn.h>
11#include <botan/hash.h>
12#include <botan/rng.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/mgf1.h>
16#include <array>
17
18namespace Botan {
19
20namespace {
21
22/*
23* PSSR Encode Operation
24*/
25std::vector<uint8_t> pss_encode(HashFunction& hash,
26 std::span<const uint8_t> msg,
27 std::span<const uint8_t> salt,
28 size_t output_bits) {
29 const size_t HASH_SIZE = hash.output_length();
30
31 if(msg.size() != HASH_SIZE) {
32 throw Encoding_Error("Cannot encode PSS string, input length invalid for hash");
33 }
34 if(output_bits < 8 * HASH_SIZE + 8 * salt.size() + 9) {
35 throw Encoding_Error("Cannot encode PSS string, output length too small");
36 }
37
38 const size_t output_length = ceil_tobytes(output_bits);
39 const uint8_t db0_mask = 0xFF >> (8 * output_length - output_bits);
40
41 std::array<uint8_t, 8> padding = {0};
42 hash.update(padding);
43 hash.update(msg);
44 hash.update(salt);
45 std::vector<uint8_t> H = hash.final_stdvec();
46
47 const size_t db_len = output_length - HASH_SIZE - 1;
48 std::vector<uint8_t> EM(output_length);
49
50 BufferStuffer stuffer(EM);
51 stuffer.append(0x00, stuffer.remaining_capacity() - (1 + salt.size() + H.size() + 1));
52 stuffer.append(0x01);
53 stuffer.append(salt);
54
55 mgf1_mask(hash, H.data(), H.size(), EM.data(), db_len);
56 EM[0] &= db0_mask;
57
58 stuffer.append(H);
59 stuffer.append(0xBC);
60 BOTAN_ASSERT_NOMSG(stuffer.full());
61
62 return EM;
63}
64
65bool pss_verify(HashFunction& hash,
66 std::span<const uint8_t> pss_repr,
67 std::span<const uint8_t> message_hash,
68 size_t key_bits,
69 size_t* out_salt_size) {
70 const size_t HASH_SIZE = hash.output_length();
71 const size_t key_bytes = ceil_tobytes(key_bits);
72
73 if(key_bits < 8 * HASH_SIZE + 9) {
74 return false;
75 }
76
77 if(message_hash.size() != HASH_SIZE) {
78 return false;
79 }
80
81 if(pss_repr.size() > key_bytes || pss_repr.size() <= 1) {
82 return false;
83 }
84
85 if(pss_repr[pss_repr.size() - 1] != 0xBC) {
86 return false;
87 }
88
89 std::vector<uint8_t> coded;
90 if(pss_repr.size() < key_bytes) {
91 coded.resize(key_bytes);
92 BufferStuffer stuffer(coded);
93 stuffer.append(0x00, key_bytes - pss_repr.size());
94 stuffer.append(pss_repr);
95 } else {
96 coded.assign(pss_repr.begin(), pss_repr.end());
97 }
98
99 // We have to check this after potential zero padding above
100 const size_t top_bits = 8 * ((key_bits + 7) / 8) - key_bits;
101 if(top_bits > 8 - high_bit(coded[0])) {
102 return false;
103 }
104
105 uint8_t* DB = coded.data();
106 const size_t DB_size = coded.size() - HASH_SIZE - 1;
107
108 const uint8_t* H = &coded[DB_size];
109 const size_t H_size = HASH_SIZE;
110
111 mgf1_mask(hash, H, H_size, DB, DB_size);
112 DB[0] &= 0xFF >> top_bits;
113
114 size_t salt_offset = 0;
115 for(size_t j = 0; j != DB_size; ++j) {
116 if(DB[j] == 0x01) {
117 salt_offset = j + 1;
118 break;
119 }
120 if(DB[j] != 0x00) {
121 return false;
122 }
123 }
124 if(salt_offset == 0) {
125 return false;
126 }
127
128 const size_t salt_size = DB_size - salt_offset;
129
130 std::array<uint8_t, 8> padding = {0};
131 hash.update(padding);
132 hash.update(message_hash);
133 hash.update(&DB[salt_offset], salt_size);
134
135 const std::vector<uint8_t> H2 = hash.final_stdvec();
136
137 const bool ok = CT::is_equal(H, H2.data(), HASH_SIZE).as_bool();
138
139 if(ok && out_salt_size != nullptr) {
140 *out_salt_size = salt_size;
141 }
142
143 return ok;
144}
145
146} // namespace
147
148PSSR::PSSR(std::unique_ptr<HashFunction> hash) :
149 m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {}
150
151PSSR::PSSR(std::unique_ptr<HashFunction> hash, size_t salt_size) :
152 m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {}
153
154/*
155* PSSR Update Operation
156*/
157void PSSR::update(const uint8_t input[], size_t length) {
158 m_hash->update(input, length);
159}
160
161/*
162* Return the raw (unencoded) data
163*/
164std::vector<uint8_t> PSSR::raw_data() {
165 return m_hash->final_stdvec();
166}
167
168std::vector<uint8_t> PSSR::encoding_of(std::span<const uint8_t> msg, size_t output_bits, RandomNumberGenerator& rng) {
169 const auto salt = rng.random_vec<std::vector<uint8_t>>(m_salt_size);
170 return pss_encode(*m_hash, msg, salt, output_bits);
171}
172
173/*
174* PSSR Decode/Verify Operation
175*/
176bool PSSR::verify(std::span<const uint8_t> coded, std::span<const uint8_t> raw, size_t key_bits) {
177 size_t salt_size = 0;
178 const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size);
179
180 if(m_required_salt_len && salt_size != m_salt_size) {
181 return false;
182 }
183
184 return ok;
185}
186
187std::string PSSR::hash_function() const {
188 return m_hash->name();
189}
190
191std::string PSSR::name() const {
192 return fmt("PSS({},MGF1,{})", m_hash->name(), m_salt_size);
193}
194
195PSS_Raw::PSS_Raw(std::unique_ptr<HashFunction> hash) :
196 m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {}
197
198PSS_Raw::PSS_Raw(std::unique_ptr<HashFunction> hash, size_t salt_size) :
199 m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {}
200
201/*
202* PSS_Raw Update Operation
203*/
204void PSS_Raw::update(const uint8_t input[], size_t length) {
205 m_msg.insert(m_msg.end(), input, input + length);
206}
207
208/*
209* Return the raw (unencoded) data
210*/
211std::vector<uint8_t> PSS_Raw::raw_data() {
212 std::vector<uint8_t> ret;
213 std::swap(ret, m_msg);
214
215 if(ret.size() != m_hash->output_length()) {
216 throw Encoding_Error("PSS_Raw Bad input length, did not match hash");
217 }
218
219 return ret;
220}
221
222std::vector<uint8_t> PSS_Raw::encoding_of(std::span<const uint8_t> msg,
223 size_t output_bits,
225 const auto salt = rng.random_vec<std::vector<uint8_t>>(m_salt_size);
226 return pss_encode(*m_hash, msg, salt, output_bits);
227}
228
229/*
230* PSS_Raw Decode/Verify Operation
231*/
232bool PSS_Raw::verify(std::span<const uint8_t> coded, std::span<const uint8_t> raw, size_t key_bits) {
233 size_t salt_size = 0;
234 const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size);
235
236 if(m_required_salt_len && salt_size != m_salt_size) {
237 return false;
238 }
239
240 return ok;
241}
242
243std::string PSS_Raw::hash_function() const {
244 return m_hash->name();
245}
246
247std::string PSS_Raw::name() const {
248 return fmt("PSS_Raw({},MGF1,{})", m_hash->name(), m_salt_size);
249}
250
251} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:134
std::string name() const override
Definition pssr.cpp:191
PSSR(std::unique_ptr< HashFunction > hash)
Definition pssr.cpp:148
std::string hash_function() const override
Definition pssr.cpp:187
std::string name() const override
Definition pssr.cpp:247
PSS_Raw(std::unique_ptr< HashFunction > hash)
Definition pssr.cpp:195
std::string hash_function() const override
Definition pssr.cpp:243
virtual std::vector< uint8_t > raw_data()=0
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:826
void mgf1_mask(HashFunction &hash, const uint8_t in[], size_t in_len, uint8_t out[], size_t out_len)
Definition mgf1.cpp:16
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
BOTAN_FORCE_INLINE constexpr T ceil_tobytes(T bits)
Definition bit_ops.h:155
BOTAN_FORCE_INLINE constexpr size_t high_bit(T n)
Definition bit_ops.h:56