Follow-up comments to hash_to_scalar.
See
https://boringssl-review.googlesource.com/c/boringssl/+/40646/3#message-ee607e82b0c62dd73a1b8a81f03acd9329cbbf02
Additionally, to be consistent with hash_to_field, we ought to use a
big-endian value. It's also probably time to have some common functions
for dealing with converting BN_ULONG[]s to and from big-endian bytes.
Coding all those free-handed is a little tedious and error-prone.
Change-Id: I6bdcd9362cee60e160e5a8eca25206b052206e1f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/40647
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/ec_extra/hash_to_curve.c b/crypto/ec_extra/hash_to_curve.c
index 5c0e2e5..5c454de 100644
--- a/crypto/ec_extra/hash_to_curve.c
+++ b/crypto/ec_extra/hash_to_curve.c
@@ -113,26 +113,6 @@
return ret;
}
-// reduce_to_felem implements step 7 of hash_to_field, described in section 5.2
-// of draft-irtf-cfrg-hash-to-curve-06.
-static void reduce_to_felem(const EC_GROUP *group, EC_FELEM *out,
- const uint8_t *in, size_t len) {
- union {
- BN_ULONG words[2 * EC_MAX_WORDS];
- uint8_t bytes[2 * EC_MAX_BYTES];
- } buf;
- OPENSSL_memset(&buf, 0, sizeof(buf));
-
- // |in| is encoded in big-endian.
- assert(len <= sizeof(buf.bytes));
- for (size_t i = 0; i < len; i++) {
- buf.bytes[len - i - 1] = in[i];
- }
-
- size_t num_words = group->field.width;
- group->meth->felem_reduce(group, out, buf.words, num_words * 2);
-}
-
// num_bytes_to_derive determines the number of bytes to derive when hashing to
// a number modulo |modulus|. See the hash_to_field operation defined in
// section 5.2 of draft-irtf-cfrg-hash-to-curve-06.
@@ -153,8 +133,22 @@
return 1;
}
+// big_endian_to_words decodes |in| as a big-endian integer and writes the
+// result to |out|. |num_words| must be large enough to contain the output.
+static void big_endian_to_words(BN_ULONG *out, size_t num_words,
+ const uint8_t *in, size_t len) {
+ assert(len <= num_words * sizeof(BN_ULONG));
+ // Ensure any excess bytes are zeroed.
+ OPENSSL_memset(out, 0, num_words * sizeof(BN_ULONG));
+ uint8_t *out_u8 = (uint8_t *)out;
+ for (size_t i = 0; i < len; i++) {
+ out_u8[len - 1 - i] = in[i];
+ }
+}
+
// hash_to_field implements the operation described in section 5.2
-// of draft-irtf-cfrg-hash-to-curve-06, with count = 2.
+// of draft-irtf-cfrg-hash-to-curve-06, with count = 2. |k| is the security
+// factor.
static int hash_to_field2(const EC_GROUP *group, const EVP_MD *md,
EC_FELEM *out1, EC_FELEM *out2, const uint8_t *dst,
size_t dst_len, unsigned k, const uint8_t *msg,
@@ -165,24 +159,31 @@
!expand_message_xmd(md, buf, 2 * L, msg, msg_len, dst, dst_len)) {
return 0;
}
- reduce_to_felem(group, out1, buf, L);
- reduce_to_felem(group, out2, buf + L, L);
+ BN_ULONG words[2 * EC_MAX_WORDS];
+ size_t num_words = 2 * group->field.width;
+ big_endian_to_words(words, num_words, buf, L);
+ group->meth->felem_reduce(group, out1, words, num_words);
+ big_endian_to_words(words, num_words, buf + L, L);
+ group->meth->felem_reduce(group, out2, words, num_words);
return 1;
}
// hash_to_scalar behaves like |hash_to_field2| but returns a value modulo the
-// group order rather than a field element.
+// group order rather than a field element. |k| is the security factor.
static int hash_to_scalar(const EC_GROUP *group, const EVP_MD *md,
EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
unsigned k, const uint8_t *msg, size_t msg_len) {
size_t L;
- BN_ULONG words[EC_MAX_WORDS * 2] = {0};
+ uint8_t buf[EC_MAX_BYTES * 2];
if (!num_bytes_to_derive(&L, &group->order, k) ||
- !expand_message_xmd(md, (uint8_t *)words, L, msg, msg_len, dst,
- dst_len)) {
+ !expand_message_xmd(md, buf, L, msg, msg_len, dst, dst_len)) {
return 0;
}
- ec_scalar_reduce(group, out, words, 2 * group->order.width);
+
+ BN_ULONG words[2 * EC_MAX_WORDS];
+ size_t num_words = 2 * group->order.width;
+ big_endian_to_words(words, num_words, buf, L);
+ ec_scalar_reduce(group, out, words, num_words);
return 1;
}
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index eb53b2f..95b3db0 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -1215,13 +1215,13 @@
};
static const HashToScalarTest kTests[] = {
{"P521_XMD:SHA-512_SCALAR_TEST", "",
- "01407998b20d948d6ef4e68c981d24f44ed3e65a49849a16296770"
- "14b48d4664e150074ccf9afcdf791c6afc648e69b94989881f1f0b"
- "4e2b86ce40b1dc2ce4bb20f0"},
+ "01a6206c2fc677c11d51807bf46d64a17f92396673074c5cee9299"
+ "4d28eec5445d5ed89799b30b39c964ecf62f39d59e7d43de15d910"
+ "c2c1d69f3ebc01eab241e5dc"},
{"P521_XMD:SHA-512_SCALAR_TEST", "abcdef0123456789",
- "019fab7021eeae5476d7ae7352793025a9aed0193831a42cbcd183"
- "e377a83d33ee178e11f34f9b6cffeffdee40c9260e5aff50ebf276"
- "c992b78d086dd4475d7b098e"},
+ "00af484a5d9389a9912f555234c578d4b1b7c4a6f5009018d133a4"
+ "069172c9f5ce2d853b8643fe7bb50a83427ed3520a7a793c41a455"
+ "a02aa99431434fb6b5b0b26e"},
{"P521_XMD:SHA-512_SCALAR_TEST",
"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -1233,9 +1233,9 @@
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- "00ab2c0feabe9bbd93d4570fe627fa88667bb8f2f117e76b77d41a"
- "15bb5dd995f61c64cd70a96dc9cda1f70b426dfd7a1c11a2865272"
- "f4698f501e57f8c4c2ed0008"},
+ "00b2db2ceb64ad055cafc5a0fc92560525d6dcc4975b86bbb79013"
+ "a1c3ab5d412320cb55df8088a658039a70c5657d5aefaaaa81cc5d"
+ "eecdd40c03eb0517fe2e158c"},
};
for (const auto &test : kTests) {