iso9796.hpp
Go to the documentation of this file.
1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2020 Mikhail Komarov <nemo@nil.foundation>
3 //
4 // MIT License
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in all
14 // copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 // SOFTWARE.
23 //---------------------------------------------------------------------------//
24 
25 #ifndef CRYPTO3_ISO9796_HPP
26 #define CRYPTO3_ISO9796_HPP
27 
30 
31 namespace nil {
32  namespace crypto3 {
33  namespace pubkey {
34  namespace padding {
35  template<typename Hash>
36  secure_vector<uint8_t> iso9796_encoding(const secure_vector<uint8_t> &msg, size_t output_bits,
37  size_t SALT_SIZE, bool implicit, random_number_generator &rng) {
38  const size_t output_length = (output_bits + 7) / 8;
39 
40  // set trailer length
41  size_t tLength = 1;
42  if (!implicit) {
43  tLength = 2;
44  }
45  const size_t HASH_SIZE = hash->output_length();
46 
47  if (output_length <= HASH_SIZE + SALT_SIZE + tLength) {
48  throw encoding_error("ISO9796-2::encoding_of: Output length is too small");
49  }
50 
51  // calculate message capacity
52  const size_t capacity = output_length - HASH_SIZE - SALT_SIZE - tLength - 1;
53 
54  // msg1 is the recoverable and msg2 the unrecoverable message part.
55  secure_vector<uint8_t> msg1;
56  secure_vector<uint8_t> msg2;
57  if (msg.size() > capacity) {
58  msg1 = secure_vector<uint8_t>(msg.begin(), msg.begin() + capacity);
59  msg2 = secure_vector<uint8_t>(msg.begin() + capacity, msg.end());
60  hash->update(msg2);
61  } else {
62  msg1 = msg;
63  }
64  msg2 = hash->final();
65 
66  // compute H(C||msg1 ||H(msg2)||S)
67  uint64_t msgLength = msg1.size();
68  secure_vector<uint8_t> salt = rng.random_vec(SALT_SIZE);
69  hash->update_be(msgLength * 8);
70  hash->update(msg1);
71  hash->update(msg2);
72  hash->update(salt);
73  secure_vector<uint8_t> H = hash->final();
74 
75  secure_vector<uint8_t> EM(output_length);
76 
77  // compute message offset.
78  size_t offset = output_length - HASH_SIZE - SALT_SIZE - tLength - msgLength - 1;
79 
80  // insert message border (0x01), msg1 and salt into the output buffer
81  EM[offset] = 0x01;
82  buffer_insert(EM, offset + 1, msg1);
83  buffer_insert(EM, offset + 1 + msgLength, salt);
84 
85  // apply mask
86  mgf1_mask(*hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - tLength);
87  buffer_insert(EM, output_length - HASH_SIZE - tLength, H);
88  // set implicit/ISO trailer
89  if (!implicit) {
90  uint8_t hash_id = ieee1363_hash_id(hash->name());
91  if (!hash_id) {
92  throw encoding_error("ISO9796-2::encoding_of: no hash identifier for " + hash->name());
93  }
94  EM[output_length - 1] = 0xCC;
95  EM[output_length - 2] = hash_id;
96 
97  } else {
98  EM[output_length - 1] = 0xBC;
99  }
100  // clear the leftmost bit (confer bouncy castle)
101  EM[0] &= 0x7F;
102 
103  return EM;
104  }
105 
106  bool iso9796_verification(const secure_vector<uint8_t> &const_coded, const secure_vector<uint8_t> &raw,
107  size_t key_bits, std::unique_ptr<HashFunction> &hash, size_t SALT_SIZE) {
108  const size_t HASH_SIZE = hash->output_length();
109  const size_t KEY_BYTES = (key_bits + 7) / 8;
110 
111  if (const_coded.size() != KEY_BYTES) {
112  return false;
113  }
114  // get trailer length
115  size_t tLength;
116  if (const_coded[const_coded.size() - 1] == 0xBC) {
117  tLength = 1;
118  } else {
119  uint8_t hash_id = ieee1363_hash_id(hash->name());
120  if ((!const_coded[const_coded.size() - 2]) ||
121  (const_coded[const_coded.size() - 2] != hash_id) ||
122  (const_coded[const_coded.size() - 1] != 0xCC)) {
123  return false; // in case of wrong ISO trailer.
124  }
125  tLength = 2;
126  }
127 
128  secure_vector<uint8_t> coded = const_coded;
129 
130  ct::poison(coded.data(), coded.size());
131  // remove mask
132  uint8_t *DB = coded.data();
133  const size_t DB_size = coded.size() - HASH_SIZE - tLength;
134 
135  const uint8_t *H = &coded[DB_size];
136 
137  mgf1_mask(*hash, H, HASH_SIZE, DB, DB_size);
138  // clear the leftmost bit (confer bouncy castle)
139  DB[0] &= 0x7F;
140 
141  // recover msg1 and salt
142  size_t msg1_offset = 1;
143  uint8_t waiting_for_delim = 0xFF;
144  uint8_t bad_input = 0;
145  for (size_t j = 0; j < DB_size; ++j) {
146  const uint8_t one_m = ct::is_equal<uint8_t>(DB[j], 0x01);
147  const uint8_t zero_m = ct::is_zero(DB[j]);
148  const uint8_t add_m = waiting_for_delim & zero_m;
149 
150  bad_input |= waiting_for_delim & ~(zero_m | one_m);
151  msg1_offset += ct::select<uint8_t>(add_m, 1, 0);
152 
153  waiting_for_delim &= zero_m;
154  }
155 
156  // invalid, if delimiter 0x01 was not found or msg1_offset is too big
157  bad_input |= waiting_for_delim;
158  bad_input |= ct::is_less(coded.size(), tLength + HASH_SIZE + msg1_offset + SALT_SIZE);
159 
160  // in case that msg1_offset is too big, just continue with offset = 0.
161  msg1_offset = ct::select<size_t>(bad_input, 0, msg1_offset);
162 
163  ct::unpoison(coded.data(), coded.size());
164  ct::unpoison(msg1_offset);
165 
166  secure_vector<uint8_t> msg1(coded.begin() + msg1_offset,
167  coded.end() - tLength - HASH_SIZE - SALT_SIZE);
168  secure_vector<uint8_t> salt(coded.begin() + msg1_offset + msg1.size(),
169  coded.end() - tLength - HASH_SIZE);
170 
171  // compute H2(C||msg1||H(msg2)||S*). * indicates a recovered value
172  const size_t capacity = (key_bits - 2 + 7) / 8 - HASH_SIZE - SALT_SIZE - tLength - 1;
173  secure_vector<uint8_t> msg1raw;
174  secure_vector<uint8_t> msg2;
175  if (raw.size() > capacity) {
176  msg1raw = secure_vector<uint8_t>(raw.begin(), raw.begin() + capacity);
177  msg2 = secure_vector<uint8_t>(raw.begin() + capacity, raw.end());
178  hash->update(msg2);
179  } else {
180  msg1raw = raw;
181  }
182  msg2 = hash->final();
183 
184  uint64_t msg1rawLength = msg1raw.size();
185  hash->update_be(msg1rawLength * 8);
186  hash->update(msg1raw);
187  hash->update(msg2);
188  hash->update(salt);
189  secure_vector<uint8_t> H3 = hash->final();
190 
191  // compute H3(C*||msg1*||H(msg2)||S*) * indicates a recovered value
192  uint64_t msgLength = msg1.size();
193  hash->update_be(msgLength * 8);
194  hash->update(msg1);
195  hash->update(msg2);
196  hash->update(salt);
197  secure_vector<uint8_t> H2 = hash->final();
198 
199  // check if H3 == H2
200  bad_input |= ct::is_equal<uint8_t>(constant_time_compare(H3.data(), H2.data(), HASH_SIZE), false);
201 
202  ct::unpoison(bad_input);
203  return (bad_input == 0);
204  }
205  } // namespace padding
206  } // namespace pubkey
207 
208  template<typename Scheme, typename Hash>
209  struct iso_9796 : public emsa<Scheme, Hash> {
210  protected:
211  template<typename InputIterator, typename OutputIterator>
212  OutputIterator iso9796_encoding(InputIterator first, InputIterator last, size_t output_bits,
213  size_t SALT_SIZE, bool implicit, random_number_generator &rng) {
214  std::ptrdiff_t message_size = std::distance(first, last);
215 
216  const size_t output_length = (output_bits + 7) / 8;
217 
218  // set trailer length
219  size_t t_length = 1;
220  if (!implicit) {
221  t_length = 2;
222  }
223  const size_t HASH_SIZE = this->hash.output_length();
224 
225  if (output_length <= HASH_SIZE + SALT_SIZE + t_length) {
226  throw encoding_error("ISO9796-2::encoding_of: Output length is too small");
227  }
228 
229  // calculate message capacity
230  const size_t capacity = output_length - HASH_SIZE - SALT_SIZE - t_length - 1;
231 
232  // msg1 is the recoverable and msg2 the unrecoverable message part.
233  secure_vector<uint8_t> msg1;
234  secure_vector<uint8_t> msg2;
235  if (message_size > capacity) {
236  msg1 = secure_vector<uint8_t>(first, first + capacity);
237  msg2 = secure_vector<uint8_t>(first + capacity, last);
238  hash->update(msg2);
239  } else {
240  std::copy(first, last, msg1);
241  }
242  msg2 = hash->final();
243 
244  // compute H(C||msg1 ||H(msg2)||S)
245  uint64_t msgLength = msg1.size();
246  secure_vector<uint8_t> salt = rng.random_vec(SALT_SIZE);
247  hash->update_be(msgLength * 8);
248  hash->update(msg1);
249  hash->update(msg2);
250  hash->update(salt);
251  secure_vector<uint8_t> H = hash->final();
252 
253  secure_vector<uint8_t> EM(output_length);
254 
255  // compute message offset.
256  size_t offset = output_length - HASH_SIZE - SALT_SIZE - t_length - msgLength - 1;
257 
258  // insert message border (0x01), msg1 and salt into the output buffer
259  EM[offset] = 0x01;
260  buffer_insert(EM, offset + 1, msg1);
261  buffer_insert(EM, offset + 1 + msgLength, salt);
262 
263  // apply mask
264  mgf1_mask(*hash, H.data(), HASH_SIZE, EM.data(), output_length - HASH_SIZE - t_length);
265  buffer_insert(EM, output_length - HASH_SIZE - t_length, H);
266  // set implicit/ISO trailer
267  if (!implicit) {
268  uint8_t hash_id = ieee1363_hash_id(hash->name());
269  if (!hash_id) {
270  throw encoding_error("ISO9796-2::encoding_of: no hash identifier for " + hash->name());
271  }
272  EM[output_length - 1] = 0xCC;
273  EM[output_length - 2] = hash_id;
274 
275  } else {
276  EM[output_length - 1] = 0xBC;
277  }
278  // clear the leftmost bit (confer bouncy castle)
279  EM[0] &= 0x7F;
280 
281  return EM;
282  }
283 
284  template<typename InputCodedIterator, typename InputRawIterator, typename OutputIterator>
285  bool iso9796_verification(InputCodedIterator first_coded, InputCodedIterator last_coded,
286  InputRawIterator first_raw, InputRawIterator last_raw, size_t key_bits,
287  std::unique_ptr<HashFunction> &hash, size_t SALT_SIZE) {
288  std::ptrdiff_t coded_size = std::distance(first_coded, last_coded);
289  std::ptrdiff_t raw_size = std::distance(first_raw, last_raw);
290 
291  const size_t HASH_SIZE = hash->output_length();
292  const size_t KEY_BYTES = (key_bits + 7) / 8;
293 
294  if (coded_size != KEY_BYTES) {
295  return false;
296  }
297  // get trailer length
298  size_t tLength;
299  if (const_coded[coded_size - 1] == 0xBC) {
300  tLength = 1;
301  } else {
302  uint8_t hash_id = ieee1363_hash_id(hash->name());
303  if ((!const_coded[coded_size - 2]) || (const_coded[coded_size - 2] != hash_id) ||
304  (const_coded[coded_size - 1] != 0xCC)) {
305  return false; // in case of wrong ISO trailer.
306  }
307  tLength = 2;
308  }
309 
310  secure_vector<uint8_t> coded = const_coded;
311 
312  ct::poison(coded.data(), coded.size());
313  // remove mask
314  uint8_t *DB = coded.data();
315  const size_t DB_size = coded.size() - HASH_SIZE - tLength;
316 
317  const uint8_t *H = &coded[DB_size];
318 
319  mgf1_mask(*hash, H, HASH_SIZE, DB, DB_size);
320  // clear the leftmost bit (confer bouncy castle)
321  DB[0] &= 0x7F;
322 
323  // recover msg1 and salt
324  size_t msg1_offset = 1;
325  uint8_t waiting_for_delim = 0xFF;
326  uint8_t bad_input = 0;
327  for (size_t j = 0; j < DB_size; ++j) {
328  const uint8_t one_m = ct::is_equal<uint8_t>(DB[j], 0x01);
329  const uint8_t zero_m = ct::is_zero(DB[j]);
330  const uint8_t add_m = waiting_for_delim & zero_m;
331 
332  bad_input |= waiting_for_delim & ~(zero_m | one_m);
333  msg1_offset += ct::select<uint8_t>(add_m, 1, 0);
334 
335  waiting_for_delim &= zero_m;
336  }
337 
338  // invalid, if delimiter 0x01 was not found or msg1_offset is too big
339  bad_input |= waiting_for_delim;
340  bad_input |= ct::is_less(coded.size(), tLength + HASH_SIZE + msg1_offset + SALT_SIZE);
341 
342  // in case that msg1_offset is too big, just continue with offset = 0.
343  msg1_offset = ct::select<size_t>(bad_input, 0, msg1_offset);
344 
345  ct::unpoison(coded.data(), coded.size());
346  ct::unpoison(msg1_offset);
347 
348  secure_vector<uint8_t> msg1(coded.begin() + msg1_offset, coded.end() - tLength - HASH_SIZE - SALT_SIZE);
349  secure_vector<uint8_t> salt(coded.begin() + msg1_offset + msg1.size(),
350  coded.end() - tLength - HASH_SIZE);
351 
352  // compute H2(C||msg1||H(msg2)||S*). * indicates a recovered value
353  const size_t capacity = (key_bits - 2 + 7) / 8 - HASH_SIZE - SALT_SIZE - tLength - 1;
354  secure_vector<uint8_t> msg1raw;
355  secure_vector<uint8_t> msg2;
356  if (raw_size > capacity) {
357  msg1raw = secure_vector<uint8_t>(first_raw, first_raw + capacity);
358  msg2 = secure_vector<uint8_t>(first_raw + capacity, last_raw);
359  hash->update(msg2);
360  } else {
361  std::copy(first_raw, last_raw, msg1raw);
362  }
363  msg2 = hash->final();
364 
365  uint64_t msg1rawLength = msg1raw.size();
366  hash->update_be(msg1rawLength * 8);
367  hash->update(msg1raw);
368  hash->update(msg2);
369  hash->update(salt);
370  secure_vector<uint8_t> H3 = hash->final();
371 
372  // compute H3(C*||msg1*||H(msg2)||S*) * indicates a recovered value
373  uint64_t msgLength = msg1.size();
374  hash->update_be(msgLength * 8);
375  hash->update(msg1);
376  hash->update(msg2);
377  hash->update(salt);
378  secure_vector<uint8_t> H2 = hash->final();
379 
380  // check if H3 == H2
381  bad_input |= ct::is_equal<uint8_t>(constant_time_compare(H3.data(), H2.data(), HASH_SIZE), false);
382 
383  ct::unpoison(bad_input);
384  return (bad_input == 0);
385  }
386  };
387 
388  template<typename Scheme, typename Hash>
389  struct iso_9796_ds2 : public iso_9796<Scheme, Hash> {
390  template<typename InputIterator1, typename InputIterator2>
391  bool verify(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
392  std::size_t key_bits) const {
393  return iso9796_verification(first1, last1, first2, last2, key_bits, m_hash, m_SALT_SIZE);
394  }
395 
396  template<typename SinglePassRange1, typename SinglePassRange2>
397  bool verify(const SinglePassRange1 &range1, const SinglePassRange2 &range2, std::size_t key_bits) const {
398  return verify(boost::begin(range1), boost::end(range1), boost::begin(range2), boost::end(range2), 0);
399  }
400  };
401 
402  template<typename Scheme, typename Hash>
403  struct iso_9796_ds3 : public iso_9796<Scheme, Hash> {
404  template<typename InputIterator1, typename InputIterator2>
405  bool verify(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
406  std::size_t key_bits) const {
407  return iso9796_verification(first1, last1, first2, last2, key_bits, m_hash, 0);
408  }
409 
410  template<typename SinglePassRange1, typename SinglePassRange2>
411  bool verify(const SinglePassRange1 &range1, const SinglePassRange2 &range2, std::size_t key_bits) const {
412  return verify(boost::begin(range1), boost::end(range1), boost::begin(range2), boost::end(range2), 0);
413  }
414  };
415  } // namespace crypto3
416 } // namespace nil
417 
418 #endif
std::enable_if<!boost::accumulators::detail::is_accumulator_set< OutputIterator >::value, OutputIterator >::type hash(InputIterator first, InputIterator last, OutputIterator out)
Definition: algorithm/hash.hpp:78
bool is_zero(const Range &a)
Definition: basic_operations.hpp:43
boost::mpl::apply< AccumulatorSet, tag::pubkey< ProcessingMode > >::type::result_type pubkey(const AccumulatorSet &acc)
Definition: accumulators/pubkey.hpp:106
OutputIterator mgf1_mask(InputIterator first, InputIterator last, OutputIterator out, StreamHash sh=StreamHash())
MGF1 from PKCS #1 v2.0.
Definition: mgf1.hpp:48
bool iso9796_verification(const secure_vector< uint8_t > &const_coded, const secure_vector< uint8_t > &raw, size_t key_bits, std::unique_ptr< HashFunction > &hash, size_t SALT_SIZE)
Definition: iso9796.hpp:106
secure_vector< uint8_t > iso9796_encoding(const secure_vector< uint8_t > &msg, size_t output_bits, size_t SALT_SIZE, bool implicit, random_number_generator &rng)
Definition: iso9796.hpp:36
bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len)
Definition: memory_operations.hpp:143
Definition: pair.hpp:31
EMSA, from IEEE 1363s Encoding Method for Signatures, Appendix.
Definition: emsa.hpp:48
Definition: iso9796.hpp:389
bool verify(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, std::size_t key_bits) const
Definition: iso9796.hpp:391
bool verify(const SinglePassRange1 &range1, const SinglePassRange2 &range2, std::size_t key_bits) const
Definition: iso9796.hpp:397
Definition: iso9796.hpp:403
bool verify(const SinglePassRange1 &range1, const SinglePassRange2 &range2, std::size_t key_bits) const
Definition: iso9796.hpp:411
bool verify(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, std::size_t key_bits) const
Definition: iso9796.hpp:405
Definition: iso9796.hpp:209
bool iso9796_verification(InputCodedIterator first_coded, InputCodedIterator last_coded, InputRawIterator first_raw, InputRawIterator last_raw, size_t key_bits, std::unique_ptr< HashFunction > &hash, size_t SALT_SIZE)
Definition: iso9796.hpp:285
OutputIterator iso9796_encoding(InputIterator first, InputIterator last, size_t output_bits, size_t SALT_SIZE, bool implicit, random_number_generator &rng)
Definition: iso9796.hpp:212