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