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