Botan 3.0.0
Crypto and TLS for C&
ec_h2c.cpp
Go to the documentation of this file.
1/*
2* (C) 2019,2020,2021 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/internal/ec_h2c.h>
8#include <botan/internal/fmt.h>
9#include <botan/ec_group.h>
10#include <botan/numthry.h>
11#include <botan/reducer.h>
12#include <botan/hash.h>
13
14namespace Botan {
15
16void expand_message_xmd(std::string_view hash_fn,
17 uint8_t output[],
18 size_t output_len,
19 const uint8_t input[],
20 size_t input_len,
21 const uint8_t domain_sep[],
22 size_t domain_sep_len)
23 {
24 if(domain_sep_len > 0xFF)
25 throw Invalid_Argument("expand_message_xmd domain seperator too long");
26
27 auto hash = HashFunction::create_or_throw(hash_fn);
28 const size_t block_size = hash->hash_block_size();
29 if(block_size == 0)
30 throw Invalid_Argument(fmt("expand_message_xmd cannot be used with {}", hash_fn));
31
32 const size_t hash_output_size = hash->output_length();
33 if(output_len > 255*hash_output_size || output_len > 0xFFFF)
34 throw Invalid_Argument("expand_message_xmd requested output length too long");
35
36 // Compute b_0 = H(msg_prime) = H(Z_pad || msg || l_i_b_str || 0x00 || DST_prime)
37
38 hash->update(std::vector<uint8_t>(block_size));
39 hash->update(input, input_len);
40 hash->update_be(static_cast<uint16_t>(output_len));
41 hash->update(0x00);
42 hash->update(domain_sep, domain_sep_len);
43 hash->update(static_cast<uint8_t>(domain_sep_len));
44
45 const secure_vector<uint8_t> b_0 = hash->final();
46
47 // Compute b_1 = H(b_0 || 0x01 || DST_prime)
48
49 hash->update(b_0);
50 hash->update(0x01);
51 hash->update(domain_sep, domain_sep_len);
52 hash->update(static_cast<uint8_t>(domain_sep_len));
53
54 secure_vector<uint8_t> b_i = hash->final();
55
56 uint8_t cnt = 2;
57 while(output_len > 0)
58 {
59 const size_t produced = std::min(output_len, hash_output_size);
60
61 copy_mem(output, b_i.data(), produced);
62 output += produced;
63 output_len -= produced;
64
65 // Now compute the next b_i
66
67 b_i ^= b_0;
68 hash->update(b_i);
69 hash->update(cnt);
70 hash->update(domain_sep, domain_sep_len);
71 hash->update(static_cast<uint8_t>(domain_sep_len));
72 hash->final(b_i.data());
73 cnt += 1;
74 }
75 }
76
77namespace {
78
79std::vector<BigInt>
80hash_to_field(const EC_Group& group,
81 const Modular_Reducer& mod_p,
82 std::string_view hash_fn,
83 uint8_t count,
84 const uint8_t input[], size_t input_len,
85 const uint8_t domain_sep[], size_t domain_sep_len)
86 {
87 const size_t k = (group.get_order_bits() + 1) / 2;
88 const size_t L = (group.get_p_bits() + k + 7) / 8;
89
90 std::vector<BigInt> results;
91 results.reserve(count);
92
93 secure_vector<uint8_t> output(L * count);
94 expand_message_xmd(hash_fn,
95 output.data(), output.size(),
96 input, input_len,
97 domain_sep, domain_sep_len);
98
99 for(size_t i = 0; i != count; ++i)
100 {
101 BigInt v(&output[i*L], L);
102 results.push_back(mod_p.reduce(v));
103 }
104
105 return results;
106 }
107
108BigInt sswu_z(const EC_Group& group)
109 {
110 const BigInt& p = group.get_p();
111 const OID& oid = group.get_curve_oid();
112
113 if(oid == OID{1,2,840,10045,3,1,7}) // secp256r1
114 return p - 10;
115 if(oid == OID{1,3,132,0,34}) // secp384r1
116 return p - 12;
117 if(oid == OID{1,3,132,0,35}) // secp521r1
118 return p - 4;
119
120 return 0;
121 }
122
123BigInt ct_choose(bool first, const BigInt& x, const BigInt& y)
124 {
125 BigInt z = y;
126 z.ct_cond_assign(first, x);
127 return z;
128 }
129
130EC_Point map_to_curve_sswu(const EC_Group& group, const Modular_Reducer& mod_p, const BigInt& u)
131 {
132 const BigInt& p = group.get_p();
133 const BigInt& A = group.get_a();
134 const BigInt& B = group.get_b();
135 const BigInt Z = sswu_z(group);
136
137 if(Z.is_zero() || A.is_zero() || B.is_zero() || p % 4 != 3)
138 throw Invalid_Argument("map_to_curve_sswu does not support this curve");
139
140 // These values could be precomputed:
141 const BigInt c1 = mod_p.multiply(p - B, inverse_mod(A, p));
142 const BigInt c2 = mod_p.multiply(p - 1, inverse_mod(Z, p));
143
144 /*
145 * See Appendix F.2 of draft-irtf-cfrg-hash-to-curve
146 */
147
148 const BigInt tv1 = mod_p.multiply(Z, mod_p.square(u));
149 const BigInt tv2 = mod_p.square(tv1);
150
151 BigInt x1 = inverse_mod(tv1 + tv2, p);
152 const bool e1 = x1.is_zero();
153 x1 += 1;
154 x1.ct_cond_assign(e1, c2);
155 x1 = mod_p.multiply(x1, c1);
156
157 // gx1 = x1^3 + A*x1 + B;
158 BigInt gx1 = mod_p.square(x1);
159 gx1 += A;
160 gx1 = mod_p.multiply(gx1, x1);
161 gx1 += B;
162 gx1 = mod_p.reduce(gx1);
163
164 const BigInt x2 = mod_p.multiply(tv1, x1);
165
166 // gx2 = (Z * u^2)^3 * gx1
167 const BigInt gx2 = mod_p.multiply(gx1, mod_p.multiply(tv1, tv2));
168
169 // assumes p % 4 == 3
170 const bool gx1_is_square = (power_mod(gx1, (p-1)/2, p) <= 1);
171
172 const BigInt x = ct_choose(gx1_is_square, x1, x2);
173 const BigInt y2 = ct_choose(gx1_is_square, gx1, gx2);
174
175 // assumes p % 4 == 3
176 const BigInt y = power_mod(y2, (p + 1)/4, p);
177 const BigInt neg_y = p - y;
178
179 const bool uy_sign = u.get_bit(0) != y.get_bit(0);
180 return group.point(x, ct_choose(uy_sign, neg_y, y));
181 }
182
183}
184
186 std::string_view hash_fn,
187 const uint8_t input[],
188 size_t input_len,
189 const uint8_t domain_sep[],
190 size_t domain_sep_len,
191 bool random_oracle)
192 {
193 const Modular_Reducer mod_p(group.get_p());
194
195 const uint8_t count = (random_oracle ? 2 : 1);
196
197 const auto u = hash_to_field(group, mod_p, hash_fn, count,
198 input, input_len,
199 domain_sep, domain_sep_len);
200
201 EC_Point pt = map_to_curve_sswu(group, mod_p, u[0]);
202
203 for(size_t i = 1; i != u.size(); ++i)
204 pt += map_to_curve_sswu(group, mod_p, u[i]);
205
206 return pt;
207 }
208
209}
static SIMD_4x64 y
const BigInt & get_p() const
Definition: ec_group.cpp:525
bool is_zero() const
Definition: ed25519_fe.h:69
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition: hash.cpp:320
FE_25519 Z
Definition: ge.cpp:28
Definition: alg_id.cpp:12
void expand_message_xmd(std::string_view hash_fn, uint8_t output[], size_t output_len, const uint8_t input[], size_t input_len, const uint8_t domain_sep[], size_t domain_sep_len)
Definition: ec_h2c.cpp:16
BigInt power_mod(const BigInt &base, const BigInt &exp, const BigInt &mod)
Definition: numthry.cpp:296
std::string fmt(std::string_view format, const T &... args)
Definition: fmt.h:60
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition: mem_ops.h:126
EC_Point hash_to_curve_sswu(const EC_Group &group, std::string_view hash_fn, const uint8_t input[], size_t input_len, const uint8_t domain_sep[], size_t domain_sep_len, bool random_oracle)
Definition: ec_h2c.cpp:185
BigInt inverse_mod(const BigInt &n, const BigInt &mod)
Definition: mod_inv.cpp:177
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:64