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