Coverage Report

Created: 2026-02-14 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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