Botan 3.11.0
Crypto and TLS for C&
iso9796.cpp
Go to the documentation of this file.
1/*
2 * ISO-9796-2 - Digital signature schemes giving message recovery schemes 2 and 3
3 * (C) 2016 Tobias Niemann, Hackmanit GmbH
4 * 2025 Jack Lloyd
5 *
6 * Botan is released under the Simplified BSD License (see license.txt)
7 */
8
9#include <botan/internal/iso9796.h>
10
11#include <botan/exceptn.h>
12#include <botan/hash.h>
13#include <botan/rng.h>
14#include <botan/internal/buffer_stuffer.h>
15#include <botan/internal/ct_utils.h>
16#include <botan/internal/fmt.h>
17#include <botan/internal/hash_id.h>
18#include <botan/internal/mgf1.h>
19
20namespace Botan {
21
22namespace {
23
24std::vector<uint8_t> iso9796_hash(HashFunction& hash,
25 std::span<const uint8_t> msg1,
26 std::span<const uint8_t> hmsg2,
27 std::span<const uint8_t> salt) {
28 // Compute H(C || msg1 || H(msg2) || S) as described in the ISO text
29 hash.update_be(static_cast<uint64_t>(msg1.size()) * 8);
30 hash.update(msg1);
31 hash.update(hmsg2);
32 hash.update(salt);
33 return hash.final_stdvec();
34}
35
36std::vector<uint8_t> iso9796_encoding(std::span<const uint8_t> msg,
37 size_t output_bits,
38 std::unique_ptr<HashFunction>& hash,
39 size_t salt_len,
40 bool implicit,
42 const size_t output_length = (output_bits + 7) / 8;
43
44 //set trailer length
45 const size_t trailer_len = (implicit) ? 1 : 2;
46
47 const size_t hash_len = hash->output_length();
48
49 if(output_length <= hash_len + salt_len + trailer_len) {
50 throw Encoding_Error("ISO9796-2::encoding_of: Output length is too small");
51 }
52
53 //calculate message capacity
54 const size_t capacity = output_length - hash_len - salt_len - trailer_len - 1;
55
56 // msg1 is the recoverable part and hmsg2 is the hash of the unrecoverable message part.
57 const size_t msg1_len = std::min(capacity, msg.size());
58 const auto msg1 = msg.first(msg1_len); // the first capacity bytes
59 const auto msg2 = msg.subspan(msg1_len); // the rest; possibly empty
60
61 const auto hmsg2 = hash->process<std::vector<uint8_t>>(msg2);
62 const auto salt = rng.random_vec<std::vector<uint8_t>>(salt_len);
63
64 const auto H = iso9796_hash(*hash, msg1, hmsg2, salt);
65
66 std::vector<uint8_t> EM(output_length);
67
68 BufferStuffer stuffer(EM);
69 stuffer.append(0x00, stuffer.remaining_capacity() - (hash_len + salt_len + trailer_len + msg1_len + 1));
70 stuffer.append(0x01);
71 stuffer.append(msg1);
72 stuffer.append(salt);
73
74 //apply mask
75 const size_t mgf1_bytes = EM.size() - hash_len - trailer_len;
76 mgf1_mask(*hash, H, std::span{EM}.first(mgf1_bytes));
77
78 //clear the leftmost bit (confer bouncy castle)
79 EM[0] &= 0x7F;
80
81 stuffer.append(H);
82
83 // set implicit/ISO trailer
84
85 if(implicit) {
86 stuffer.append(0xBC);
87 } else {
88 const uint8_t hash_id = ieee1363_hash_id(hash->name());
89 if(hash_id == 0) {
90 throw Encoding_Error("ISO-9796: no hash identifier for " + hash->name());
91 }
92 stuffer.append(hash_id);
93 stuffer.append(0xCC);
94 }
95
96 BOTAN_ASSERT_NOMSG(stuffer.full());
97
98 return EM;
99}
100
101bool iso9796_verification(std::span<const uint8_t> repr,
102 std::span<const uint8_t> raw,
103 size_t key_bits,
104 std::unique_ptr<HashFunction>& hash,
105 size_t salt_len) {
106 if(repr.size() != (key_bits + 7) / 8) {
107 return false;
108 }
109 //get trailer length
110
111 const uint8_t last = repr[repr.size() - 1];
112
113 if(last != 0xBC && last != 0xCC) {
114 return false;
115 }
116
117 const size_t trailer_len = last == 0xBC ? 1 : 2;
118
119 if(trailer_len == 2) {
120 const uint8_t hash_id = ieee1363_hash_id(hash->name());
121 if(hash_id == 0) {
122 throw Decoding_Error("ISO-9796: no hash identifier for " + hash->name());
123 }
124
125 const uint8_t trailer_0 = repr[repr.size() - 2];
126 const uint8_t trailer_1 = repr[repr.size() - 1];
127
128 if(trailer_0 != hash_id || trailer_1 != 0xCC) {
129 return false;
130 }
131 }
132
133 const size_t hash_len = hash->output_length();
134
135 if(repr.size() < hash_len + trailer_len + salt_len) {
136 return false;
137 }
138
139 std::vector<uint8_t> coded(repr.begin(), repr.end());
140
141 CT::poison(coded.data(), coded.size());
142 //remove mask
143 uint8_t* DB = coded.data();
144 const size_t DB_size = coded.size() - hash_len - trailer_len;
145
146 const uint8_t* H = &coded[DB_size];
147
148 mgf1_mask(*hash, {H, hash_len}, {DB, DB_size});
149 //clear the leftmost bit (confer bouncy castle)
150 DB[0] &= 0x7F;
151
152 //recover msg1 and salt
153 size_t msg1_offset = 1;
154
155 auto waiting_for_delim = CT::Mask<uint8_t>::set();
156 auto bad_input = CT::Mask<uint8_t>::cleared();
157
158 for(size_t j = 0; j < DB_size; ++j) {
159 const auto is_zero = CT::Mask<uint8_t>::is_zero(DB[j]);
160 const auto is_one = CT::Mask<uint8_t>::is_equal(DB[j], 0x01);
161
162 const auto add_m = waiting_for_delim & is_zero;
163
164 bad_input |= waiting_for_delim & ~(is_zero | is_one);
165 msg1_offset += add_m.if_set_return(1);
166
167 waiting_for_delim &= is_zero;
168 }
169
170 //invalid, if delimiter 0x01 was not found or msg1_offset is too big
171 bad_input |= waiting_for_delim;
172
173 const auto bad_offset = CT::Mask<size_t>::is_lt(coded.size(), trailer_len + hash_len + msg1_offset + salt_len);
174 bad_input |= CT::Mask<uint8_t>(bad_offset);
175
176 //in case that msg1_offset is too big, just continue with offset = 0.
177 msg1_offset = CT::Mask<size_t>::expand(bad_input.value()).if_not_set_return(msg1_offset);
178
179 CT::unpoison(coded.data(), coded.size());
180 CT::unpoison(msg1_offset);
181
182 const size_t msg1_len = coded.size() - (trailer_len + hash_len + msg1_offset + salt_len);
183
184 const auto msg1 = std::span(coded).subspan(msg1_offset, msg1_len);
185 const auto salt = std::span(coded).subspan(msg1_offset + msg1.size(), salt_len);
186
187 //compute H2(C||msg1||H(msg2)||S*). * indicates a recovered value
188 const size_t capacity = (key_bits - 2 + 7) / 8 - hash_len - salt_len - trailer_len - 1;
189
190 std::span<const uint8_t> msg1raw = raw;
191 if(msg1raw.size() > capacity) {
192 hash->update(msg1raw.subspan(capacity));
193 msg1raw = msg1raw.first(capacity);
194 }
195
196 const auto hmsg2 = hash->final_stdvec();
197
198 // Compute H(C*||msg1*||H(msg2)||S*) where '*' indicates a recovered value
199 const auto H2 = iso9796_hash(*hash, msg1, hmsg2, salt);
200
201 // Check if H == H2
202 bad_input |= CT::is_not_equal(H, H2.data(), hash_len);
203
204 // Check that msg after MGF1 matches msg in the original
205 bad_input |= ~CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(msg1.size(), msg1raw.size()));
206 bad_input |= ~CT::is_equal(msg1.data(), msg1raw.data(), std::min(msg1.size(), msg1raw.size()));
207
208 CT::unpoison(bad_input);
209 return (bad_input.as_bool() == false);
210}
211
212} // namespace
213
214/*
215 * ISO-9796-2 signature scheme 2
216 * DS 2 is probabilistic
217 */
218void ISO_9796_DS2::update(const uint8_t input[], size_t length) {
219 //need to buffer message completely, before digest
220 m_msg_buffer.insert(m_msg_buffer.end(), input, input + length);
221}
222
223/*
224 * Return the raw (unencoded) data
225 */
226std::vector<uint8_t> ISO_9796_DS2::raw_data() {
227 std::vector<uint8_t> retbuffer = m_msg_buffer;
228 m_msg_buffer.clear();
229 return retbuffer;
230}
231
232/*
233 * ISO-9796-2 scheme 2 encode operation
234 */
235std::vector<uint8_t> ISO_9796_DS2::encoding_of(std::span<const uint8_t> msg,
236 size_t output_bits,
238 return iso9796_encoding(msg, output_bits, m_hash, m_salt_len, m_implicit, rng);
239}
240
241/*
242 * ISO-9796-2 scheme 2 verify operation
243 */
244bool ISO_9796_DS2::verify(std::span<const uint8_t> repr, std::span<const uint8_t> raw, size_t key_bits) {
245 return iso9796_verification(repr, raw, key_bits, m_hash, m_salt_len);
246}
247
248std::string ISO_9796_DS2::hash_function() const {
249 return m_hash->name();
250}
251
252/*
253 * Return the SCAN name
254 */
255std::string ISO_9796_DS2::name() const {
256 return fmt("ISO_9796_DS2({},{},{})", m_hash->name(), (m_implicit ? "imp" : "exp"), m_salt_len);
257}
258
259/*
260 * ISO-9796-2 signature scheme 3
261 * DS 3 is deterministic and equals DS2 without salt
262 */
263void ISO_9796_DS3::update(const uint8_t input[], size_t length) {
264 //need to buffer message completely, before digest
265 m_msg_buffer.insert(m_msg_buffer.end(), input, input + length);
266}
267
268/*
269 * Return the raw (unencoded) data
270 */
271std::vector<uint8_t> ISO_9796_DS3::raw_data() {
272 std::vector<uint8_t> retbuffer = m_msg_buffer;
273 m_msg_buffer.clear();
274 return retbuffer;
275}
276
277/*
278 * ISO-9796-2 scheme 3 encode operation
279 */
280std::vector<uint8_t> ISO_9796_DS3::encoding_of(std::span<const uint8_t> msg,
281 size_t output_bits,
283 return iso9796_encoding(msg, output_bits, m_hash, 0, m_implicit, rng);
284}
285
286/*
287 * ISO-9796-2 scheme 3 verify operation
288 */
289bool ISO_9796_DS3::verify(std::span<const uint8_t> repr, std::span<const uint8_t> raw, size_t key_bits) {
290 return iso9796_verification(repr, raw, key_bits, m_hash, 0);
291}
292
293std::string ISO_9796_DS3::hash_function() const {
294 return m_hash->name();
295}
296
297/*
298 * Return the SCAN name
299 */
300std::string ISO_9796_DS3::name() const {
301 return fmt("ISO_9796_DS3({},{})", m_hash->name(), (m_implicit ? "imp" : "exp"));
302}
303
304} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
Helper class to ease in-place marshalling of concatenated fixed-length values.
static constexpr Mask< T > set()
Definition ct_utils.h:382
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:392
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:442
static constexpr Mask< T > is_lt(T x, T y)
Definition ct_utils.h:450
static constexpr Mask< T > is_zero(T x)
Definition ct_utils.h:437
static constexpr Mask< T > cleared()
Definition ct_utils.h:387
std::string name() const override
Definition iso9796.cpp:255
std::string hash_function() const override
Definition iso9796.cpp:248
std::string hash_function() const override
Definition iso9796.cpp:293
std::string name() const override
Definition iso9796.cpp:300
virtual std::vector< uint8_t > raw_data()=0
constexpr CT::Mask< T > is_not_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:838
constexpr void unpoison(const T *p, size_t n)
Definition ct_utils.h:67
constexpr void poison(const T *p, size_t n)
Definition ct_utils.h:56
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
void mgf1_mask(HashFunction &hash, std::span< const uint8_t > input, std::span< uint8_t > output)
Definition mgf1.cpp:15
uint8_t ieee1363_hash_id(std::string_view name)
Definition hash_id.cpp:144