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