/src/botan/src/lib/kdf/xmd/xmd.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * (C) 2019,2020,2021,2024 Jack Lloyd |
3 | | * |
4 | | * Botan is released under the Simplified BSD License (see license.txt) |
5 | | */ |
6 | | |
7 | | #include <botan/internal/xmd.h> |
8 | | |
9 | | #include <botan/exceptn.h> |
10 | | #include <botan/hash.h> |
11 | | #include <botan/internal/fmt.h> |
12 | | #include <vector> |
13 | | |
14 | | namespace Botan { |
15 | | |
16 | | void expand_message_xmd(std::string_view hash_fn, |
17 | | std::span<uint8_t> output, |
18 | | std::span<const uint8_t> input, |
19 | 0 | std::span<const uint8_t> domain_sep) { |
20 | 0 | if(domain_sep.size() > 0xFF) { |
21 | | // RFC 9380 has a specification for handling this |
22 | 0 | throw Not_Implemented("XMD does not currently implement oversize DST handling"); |
23 | 0 | } |
24 | | |
25 | 0 | const uint8_t domain_sep_len = static_cast<uint8_t>(domain_sep.size()); |
26 | |
|
27 | 0 | auto hash = HashFunction::create_or_throw(hash_fn); |
28 | 0 | const size_t block_size = hash->hash_block_size(); |
29 | 0 | if(block_size == 0) { |
30 | 0 | throw Invalid_Argument(fmt("expand_message_xmd cannot be used with {}", hash_fn)); |
31 | 0 | } |
32 | | |
33 | 0 | const size_t hash_output_size = hash->output_length(); |
34 | 0 | if(output.size() > 255 * hash_output_size || output.size() > 0xFFFF) { |
35 | 0 | throw Invalid_Argument("expand_message_xmd requested output length too long"); |
36 | 0 | } |
37 | | |
38 | | // Compute b_0 = H(msg_prime) = H(Z_pad || msg || l_i_b_str || 0x00 || DST_prime) |
39 | | |
40 | 0 | hash->update(std::vector<uint8_t>(block_size)); |
41 | 0 | hash->update(input); |
42 | 0 | hash->update_be(static_cast<uint16_t>(output.size())); |
43 | 0 | hash->update(0x00); |
44 | 0 | hash->update(domain_sep); |
45 | 0 | hash->update(domain_sep_len); |
46 | |
|
47 | 0 | const secure_vector<uint8_t> b_0 = hash->final(); |
48 | | |
49 | | // Compute b_1 = H(b_0 || 0x01 || DST_prime) |
50 | |
|
51 | 0 | hash->update(b_0); |
52 | 0 | hash->update(0x01); |
53 | 0 | hash->update(domain_sep); |
54 | 0 | hash->update(domain_sep_len); |
55 | |
|
56 | 0 | secure_vector<uint8_t> b_i = hash->final(); |
57 | |
|
58 | 0 | uint8_t cnt = 2; |
59 | 0 | for(;;) { |
60 | 0 | const size_t produced = std::min(output.size(), hash_output_size); |
61 | |
|
62 | 0 | copy_mem(&output[0], b_i.data(), produced); |
63 | 0 | output = output.subspan(produced); |
64 | |
|
65 | 0 | if(output.empty()) { |
66 | 0 | break; |
67 | 0 | } |
68 | | |
69 | | // Now compute the next b_i if needed |
70 | | |
71 | 0 | b_i ^= b_0; |
72 | 0 | hash->update(b_i); |
73 | 0 | hash->update(cnt); |
74 | 0 | hash->update(domain_sep); |
75 | 0 | hash->update(domain_sep_len); |
76 | 0 | hash->final(b_i); |
77 | 0 | cnt += 1; |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | } // namespace Botan |