Botan 3.1.1
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
17namespace Botan {
18
19namespace {
20
21std::vector<uint8_t> iso9796_encoding(const std::vector<uint8_t>& msg,
22 size_t output_bits,
23 std::unique_ptr<HashFunction>& hash,
24 size_t SALT_SIZE,
25 bool implicit,
26 RandomNumberGenerator& rng) {
27 const size_t output_length = (output_bits + 7) / 8;
28
29 //set trailer length
30 size_t tLength = 1;
31 if(!implicit) {
32 tLength = 2;
33 }
34 const size_t HASH_SIZE = hash->output_length();
35
36 if(output_length <= HASH_SIZE + SALT_SIZE + tLength) {
37 throw Encoding_Error("ISO9796-2::encoding_of: Output length is too small");
38 }
39
40 //calculate message capacity
41 const size_t capacity = output_length - HASH_SIZE - SALT_SIZE - tLength - 1;
42
43 //msg1 is the recoverable and msg2 the unrecoverable message part.
44 std::vector<uint8_t> msg1;
45 std::vector<uint8_t> msg2;
46 if(msg.size() > capacity) {
47 msg1 = std::vector<uint8_t>(msg.begin(), msg.begin() + capacity);
48 msg2 = std::vector<uint8_t>(msg.begin() + capacity, msg.end());
49 hash->update(msg2);
50 } else {
51 msg1 = msg;
52 }
53 msg2 = hash->final_stdvec();
54
55 //compute H(C||msg1 ||H(msg2)||S)
56 const size_t msgLength = msg1.size();
57 const auto salt = rng.random_vec<std::vector<uint8_t>>(SALT_SIZE);
58 hash->update_be(static_cast<uint64_t>(msgLength) * 8);
59 hash->update(msg1);
60 hash->update(msg2);
61 hash->update(salt);
62 const std::vector<uint8_t> H = hash->final_stdvec();
63
64 std::vector<uint8_t> EM(output_length);
65
66 //compute message offset.
67 const size_t offset = output_length - HASH_SIZE - SALT_SIZE - tLength - msgLength - 1;
68
69 //insert message border (0x01), msg1 and salt into the output buffer
70 EM[offset] = 0x01;
71 buffer_insert(EM, offset + 1, msg1);
72 buffer_insert(EM, offset + 1 + msgLength, salt);
73
74 //apply mask
75 mgf1_mask(*hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - tLength);
76 buffer_insert(EM, output_length - HASH_SIZE - tLength, H);
77 //set implicit/ISO trailer
78 if(!implicit) {
79 uint8_t hash_id = ieee1363_hash_id(hash->name());
80 if(!hash_id) {
81 throw Encoding_Error("ISO9796-2::encoding_of: no hash identifier for " + hash->name());
82 }
83 EM[output_length - 1] = 0xCC;
84 EM[output_length - 2] = hash_id;
85
86 } else {
87 EM[output_length - 1] = 0xBC;
88 }
89 //clear the leftmost bit (confer bouncy castle)
90 EM[0] &= 0x7F;
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 std::vector<uint8_t> msg2;
167 if(raw.size() > capacity) {
168 msg1raw = std::vector<uint8_t>(raw.begin(), raw.begin() + capacity);
169 msg2 = std::vector<uint8_t>(raw.begin() + capacity, raw.end());
170 hash->update(msg2);
171 } else {
172 msg1raw = raw;
173 }
174 msg2 = hash->final_stdvec();
175
176 const uint64_t msg1rawLength = msg1raw.size();
177 hash->update_be(msg1rawLength * 8);
178 hash->update(msg1raw);
179 hash->update(msg2);
180 hash->update(salt);
181 std::vector<uint8_t> H3 = hash->final_stdvec();
182
183 //compute H3(C*||msg1*||H(msg2)||S*) * indicates a recovered value
184 const uint64_t msgLength = msg1.size();
185 hash->update_be(msgLength * 8);
186 hash->update(msg1);
187 hash->update(msg2);
188 hash->update(salt);
189 std::vector<uint8_t> H2 = hash->final_stdvec();
190
191 //check if H3 == H2
192 bad_input |= CT::Mask<uint8_t>::is_zero(ct_compare_u8(H3.data(), H2.data(), HASH_SIZE));
193
194 CT::unpoison(bad_input);
195 return (bad_input.is_set() == false);
196}
197
198} // namespace
199
200/*
201 * ISO-9796-2 signature scheme 2
202 * DS 2 is probabilistic
203 */
204void ISO_9796_DS2::update(const uint8_t input[], size_t length) {
205 //need to buffer message completely, before digest
206 m_msg_buffer.insert(m_msg_buffer.end(), input, input + length);
207}
208
209/*
210 * Return the raw (unencoded) data
211 */
212std::vector<uint8_t> ISO_9796_DS2::raw_data() {
213 std::vector<uint8_t> retbuffer = m_msg_buffer;
214 m_msg_buffer.clear();
215 return retbuffer;
216}
217
218/*
219 * ISO-9796-2 scheme 2 encode operation
220 */
221std::vector<uint8_t> ISO_9796_DS2::encoding_of(const std::vector<uint8_t>& msg,
222 size_t output_bits,
223 RandomNumberGenerator& rng) {
224 return iso9796_encoding(msg, output_bits, m_hash, m_SALT_SIZE, m_implicit, rng);
225}
226
227/*
228 * ISO-9796-2 scheme 2 verify operation
229 */
230bool ISO_9796_DS2::verify(const std::vector<uint8_t>& const_coded, const std::vector<uint8_t>& raw, size_t key_bits) {
231 return iso9796_verification(const_coded, raw, key_bits, m_hash, m_SALT_SIZE);
232}
233
234/*
235 * Return the SCAN name
236 */
237std::string ISO_9796_DS2::name() const {
238 return "ISO_9796_DS2(" + m_hash->name() + "," + (m_implicit ? "imp" : "exp") + "," + std::to_string(m_SALT_SIZE) +
239 ")";
240}
241
242/*
243 * ISO-9796-2 signature scheme 3
244 * DS 3 is deterministic and equals DS2 without salt
245 */
246void ISO_9796_DS3::update(const uint8_t input[], size_t length) {
247 //need to buffer message completely, before digest
248 m_msg_buffer.insert(m_msg_buffer.end(), input, input + length);
249}
250
251/*
252 * Return the raw (unencoded) data
253 */
254std::vector<uint8_t> ISO_9796_DS3::raw_data() {
255 std::vector<uint8_t> retbuffer = m_msg_buffer;
256 m_msg_buffer.clear();
257 return retbuffer;
258}
259
260/*
261 * ISO-9796-2 scheme 3 encode operation
262 */
263std::vector<uint8_t> ISO_9796_DS3::encoding_of(const std::vector<uint8_t>& msg,
264 size_t output_bits,
265 RandomNumberGenerator& rng) {
266 return iso9796_encoding(msg, output_bits, m_hash, 0, m_implicit, rng);
267}
268
269/*
270 * ISO-9796-2 scheme 3 verify operation
271 */
272bool ISO_9796_DS3::verify(const std::vector<uint8_t>& const_coded, const std::vector<uint8_t>& raw, size_t key_bits) {
273 return iso9796_verification(const_coded, raw, key_bits, m_hash, 0);
274}
275
276/*
277 * Return the SCAN name
278 */
279std::string ISO_9796_DS3::name() const {
280 return "ISO_9796_DS3(" + m_hash->name() + "," + (m_implicit ? "imp" : "exp") + ")";
281}
282} // namespace Botan
static Mask< T > is_equal(T x, T y)
Definition: ct_utils.h:128
static Mask< T > is_zero(T x)
Definition: ct_utils.h:123
static Mask< T > expand(T v)
Definition: ct_utils.h:109
static Mask< T > set()
Definition: ct_utils.h:99
static Mask< T > is_lt(T x, T y)
Definition: ct_utils.h:133
static Mask< T > cleared()
Definition: ct_utils.h:104
std::string name() const override
Definition: iso9796.cpp:237
std::string name() const override
Definition: iso9796.cpp:279
void poison(const T *p, size_t n)
Definition: ct_utils.h:48
void unpoison(const T *p, size_t n)
Definition: ct_utils.h:57
Definition: alg_id.cpp:13
void mgf1_mask(HashFunction &hash, const uint8_t in[], size_t in_len, uint8_t out[], size_t out_len)
Definition: mgf1.cpp:15
size_t buffer_insert(std::vector< T, Alloc > &buf, size_t buf_offset, const T input[], size_t input_length)
Definition: mem_ops.h:211
uint8_t ct_compare_u8(const uint8_t x[], const uint8_t y[], size_t len)
Definition: mem_ops.cpp:70
uint8_t ieee1363_hash_id(std::string_view name)
Definition: hash_id.cpp:144