Add hash-to-curve code for P384.

Change-Id: I34c3609641c23aed14f2324c6887250369ae8b5f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/40944
Commit-Queue: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/ec_extra/hash_to_curve.c b/crypto/ec_extra/hash_to_curve.c
index 407fa5d..04f37ad 100644
--- a/crypto/ec_extra/hash_to_curve.c
+++ b/crypto/ec_extra/hash_to_curve.c
@@ -120,7 +120,7 @@
   size_t bits = BN_num_bits(modulus);
   size_t L = (bits + k + 7) / 8;
   // We require 2^(8*L) < 2^(2*bits - 2) <= n^2 so to fit in bounds for
-  // |felem_reduce| and |ec_scalar_refuce|. All defined hash-to-curve suites
+  // |felem_reduce| and |ec_scalar_reduce|. All defined hash-to-curve suites
   // define |k| to be well under this bound. (|k| is usually around half of
   // |p_bits|.)
   if (L * 8 >= 2 * bits - 2 ||
@@ -318,6 +318,55 @@
   return ec_felem_from_bytes(group, out, bytes, len);
 }
 
+int ec_hash_to_curve_p384_xmd_sha512_sswu(const EC_GROUP *group,
+                                          EC_RAW_POINT *out, const uint8_t *dst,
+                                          size_t dst_len, const uint8_t *msg,
+                                          size_t msg_len) {
+  // See section 8.3 of draft-irtf-cfrg-hash-to-curve-06.
+  if (EC_GROUP_get_curve_name(group) != NID_secp384r1) {
+    OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
+    return 0;
+  }
+
+  // kSqrt1728 was computed as follows in python3:
+  //
+  // p = 2**384 - 2**128 - 2**96 + 2**32 - 1
+  // z3 = 12**3
+  // c2 = pow(z3, (p+1)//4, p)
+  // assert z3 == pow(c2, 2, p)
+  // ", ".join("0x%02x" % b for b in c2.to_bytes(384//8, 'big')
+
+  static const uint8_t kSqrt1728[] = {
+      0x01, 0x98, 0x77, 0xcc, 0x10, 0x41, 0xb7, 0x55, 0x57, 0x43, 0xc0, 0xae,
+      0x2e, 0x3a, 0x3e, 0x61, 0xfb, 0x2a, 0xaa, 0x2e, 0x0e, 0x87, 0xea, 0x55,
+      0x7a, 0x56, 0x3d, 0x8b, 0x59, 0x8a, 0x09, 0x40, 0xd0, 0xa6, 0x97, 0xa9,
+      0xe0, 0xb9, 0xe9, 0x2c, 0xfa, 0xa3, 0x14, 0xf5, 0x83, 0xc9, 0xd0, 0x66
+  };
+
+  // Z = -12, c2 = sqrt(1728)
+  EC_FELEM Z, c2;
+  if (!felem_from_u8(group, &Z, 12) ||
+      !ec_felem_from_bytes(group, &c2, kSqrt1728, sizeof(kSqrt1728))) {
+    return 0;
+  }
+  ec_felem_neg(group, &Z, &Z);
+
+  return hash_to_curve(group, EVP_sha512(), &Z, &c2, /*k=*/192, out, dst,
+                       dst_len, msg, msg_len);
+}
+
+int ec_hash_to_scalar_p384_xmd_sha512(const EC_GROUP *group, EC_SCALAR *out,
+                                      const uint8_t *dst, size_t dst_len,
+                                      const uint8_t *msg, size_t msg_len) {
+  if (EC_GROUP_get_curve_name(group) != NID_secp384r1) {
+    OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
+    return 0;
+  }
+
+  return hash_to_scalar(group, EVP_sha512(), out, dst, dst_len, /*k=*/192, msg,
+                        msg_len);
+}
+
 static int hash_to_curve_p521_xmd_sswu(const EC_GROUP *group, EC_RAW_POINT *out,
                                        const uint8_t *dst, size_t dst_len,
                                        const EVP_MD *md, unsigned k,
diff --git a/crypto/ec_extra/internal.h b/crypto/ec_extra/internal.h
index b05e9ea..eea282c 100644
--- a/crypto/ec_extra/internal.h
+++ b/crypto/ec_extra/internal.h
@@ -27,14 +27,30 @@
 // Hash-to-curve.
 //
 // The following functions implement primitives from
-// draft-irtf-cfrg-hash-to-curve-06. We currently only implement a P-521 suite,
-// but others can be added as needed.
+// draft-irtf-cfrg-hash-to-curve-06. We currently only implement the P-384 and
+// P-521 suites, but others can be added as needed. The |dst| parameter in each
+// function is the domain separation tag and must be unique for each protocol
+// and between the |hash_to_curve| and |hash_to_scalar| variants. See
+// section 3.1 of the spec for additional guidance on this parameter.
+
+// ec_hash_to_curve_p384_sha512_sswu hashes |msg| to a point on |group| and
+// writes the result to |out|, implementing the P384_XMD:SHA-512_SSWU_RO_ suite.
+// It returns one on success and zero on error.
+OPENSSL_EXPORT int ec_hash_to_curve_p384_xmd_sha512_sswu(
+    const EC_GROUP *group, EC_RAW_POINT *out, const uint8_t *dst,
+    size_t dst_len, const uint8_t *msg, size_t msg_len);
+
+// ec_hash_to_scalar_p384_xmd_sha512 hashes |msg| to a scalar on |group| and
+// writes the result to |out|, using the hash_to_field operation from the
+// P384_XMD:SHA-512_SSWU_RO_ suite, but generating a value modulo the group
+// order rather than a field element.
+OPENSSL_EXPORT int ec_hash_to_scalar_p384_xmd_sha512(
+    const EC_GROUP *group, EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
+    const uint8_t *msg, size_t msg_len);
 
 // ec_hash_to_curve_p521_sha512_sswu hashes |msg| to a point on |group| and
 // writes the result to |out|, implementing the P521_XMD:SHA-512_SSWU_RO_ suite.
-// It returns one on success and zero on error. |dst| is the domain separation
-// tag and must be unique for each protocol. See section 3.1 of
-// draft-irtf-cfrg-hash-to-curve-06 for additional guidance on this parameter.
+// It returns one on success and zero on error.
 OPENSSL_EXPORT int ec_hash_to_curve_p521_xmd_sha512_sswu(
     const EC_GROUP *group, EC_RAW_POINT *out, const uint8_t *dst,
     size_t dst_len, const uint8_t *msg, size_t msg_len);
@@ -51,14 +67,7 @@
 // ec_hash_to_scalar_p521_xmd_sha512 hashes |msg| to a scalar on |group| and
 // writes the result to |out|, using the hash_to_field operation from the
 // P521_XMD:SHA-512_SSWU_RO_ suite, but generating a value modulo the group
-// order rather than a field element. |dst| is the domain separation
-// tag and must be unique for each protocol. See section 3.1 of
-// draft-irtf-cfrg-hash-to-curve-06 for additional guidance on this parameter.
-//
-// Note the requirement to use a different tag for each encoding used in a
-// protocol extends to this function. Protocols which use both this function and
-// |ec_hash_to_scalar_p521_xmd_sha512| must use distinct values of |dst| for
-// each use.
+// order rather than a field element.
 OPENSSL_EXPORT int ec_hash_to_scalar_p521_xmd_sha512(
     const EC_GROUP *group, EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
     const uint8_t *msg, size_t msg_len);
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index 8697555..e08da93 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -1135,9 +1135,45 @@
     const char *y_hex;
   };
   static const HashToCurveTest kTests[] = {
-      // See draft-irtf-cfrg-hash-to-curve-06, appendix G.3.1. Note these test
-      // |ec_hash_to_curve_p521_xmd_sha512_sswu_ref_for_testing| due to a spec
-      // issue. See
+      // See draft-irtf-cfrg-hash-to-curve-06, appendix G.2.1.
+      {&ec_hash_to_curve_p384_xmd_sha512_sswu, NID_secp384r1,
+       "P384_XMD:SHA-512_SSWU_RO_TESTGEN", "",
+       "619d4168877421106aecb16ec35d84b7fdfa215cdb446d82bac49f"
+       "6eab51a34b4d5314823f7639293cf6471c6c981a99",
+       "e5035c694665ca25b2c57542673af6b91288110b0b0689657cd031"
+       "96976d82dec104fd9f91296c85d1ed94bc9309840e"},
+      {&ec_hash_to_curve_p384_xmd_sha512_sswu, NID_secp384r1,
+       "P384_XMD:SHA-512_SSWU_RO_TESTGEN", "abc",
+       "d7c33555606b86c3ffaa1a645f806bac9d553a769f5a735d75a395"
+       "d58a70956b6d3bdbd6a6a8c83121678a036005208a",
+       "e1c55f372a905040576f61fbc07e9664359e76f3e7b5be8dfe7224"
+       "720f85753a823e94a3f886ced2ec5ce13b1248147a"},
+      {&ec_hash_to_curve_p384_xmd_sha512_sswu, NID_secp384r1,
+       "P384_XMD:SHA-512_SSWU_RO_TESTGEN", "abcdef0123456789",
+       "d1f4bdd7ef9ee1c2d57ccd2b3b80123ccf3eb64b2f0a3ad26b8cd1"
+       "a8a8e411aadb9922d0e66a89ef0e78dba1489e23ea",
+       "9f376abd97ab8838c604a05ad17be1dfe0d924ddf0184341ec8e5a"
+       "ef9efb0f6559ab3048b4e1e0e42ac19ccb6d1dd892"},
+      {&ec_hash_to_curve_p384_xmd_sha512_sswu, NID_secp384r1,
+       "P384_XMD:SHA-512_SSWU_RO_TESTGEN",
+       "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+       "1b669293d6edfd55ca7f9725d72fc62b55a8829738404a1239f2ca"
+       "783ad8f8a3ebe2ce54a28fcb6e9eca85d1ce29a929",
+       "25556bc2c6382c261feb362519f1e5d616518810262c358fb80e45"
+       "803ebcd0ce830b594da7a0e9de9eb13ad2d9191d34"},
+
+      // See draft-irtf-cfrg-hash-to-curve-06, appendix G.3.1. Note these tests
+      // use |ec_hash_to_curve_p521_xmd_sha512_sswu_ref_for_testing| due to a
+      // spec issue. See
       // https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/issues/237. We
       // expose and test this function to check our consistency with the rest of
       // the reference implementation.
@@ -1265,27 +1301,55 @@
   static const uint8_t kMessage[] = {4, 5, 6, 7};
   EXPECT_FALSE(ec_hash_to_curve_p521_xmd_sha512_sswu(
       p224.get(), &p, kDST, sizeof(kDST), kMessage, sizeof(kMessage)));
+  EXPECT_FALSE(ec_hash_to_curve_p384_xmd_sha512_sswu(
+      p224.get(), &p, kDST, sizeof(kDST), kMessage, sizeof(kMessage)));
 }
 
 TEST(ECTest, HashToScalar) {
-  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(NID_secp521r1));
-  ASSERT_TRUE(group);
-
   struct HashToScalarTest {
+    int (*hash_to_scalar)(const EC_GROUP *group, EC_SCALAR *out,
+                          const uint8_t *dst, size_t dst_len,
+                          const uint8_t *msg, size_t msg_len);
+    int curve_nid;
     const char *dst;
     const char *msg;
     const char *result_hex;
   };
   static const HashToScalarTest kTests[] = {
-      {"P521_XMD:SHA-512_SCALAR_TEST", "",
+      {&ec_hash_to_scalar_p384_xmd_sha512, NID_secp384r1,
+       "P384_XMD:SHA-512_SCALAR_TEST", "",
+       "55f24775dd8ec265d9d1bf2f25b83806daf119db8646b3d263b156"
+       "8dbae4ee54aeafc757e4aa197da85504ad124d8ac8"},
+      {&ec_hash_to_scalar_p384_xmd_sha512, NID_secp384r1,
+       "P384_XMD:SHA-512_SCALAR_TEST", "abcdef0123456789",
+       "3af11c58bf0a3ee3560207c6bed9b5ecca5dc330426a6e1a601d36"
+       "c15d3818aeb48afb182036750bc5b46e6c20e8e2ff"},
+      {&ec_hash_to_scalar_p384_xmd_sha512, NID_secp384r1,
+       "P384_XMD:SHA-512_SCALAR_TEST",
+       "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+       "2d5f927d15283f89d9d4932f93b77ab837e90bdf26b5f8beb2c794"
+       "bbd83032b6f1a445ef9f343a2b9311657e86e6ad43"},
+      {&ec_hash_to_scalar_p521_xmd_sha512, NID_secp521r1,
+       "P521_XMD:SHA-512_SCALAR_TEST", "",
        "01a6206c2fc677c11d51807bf46d64a17f92396673074c5cee9299"
        "4d28eec5445d5ed89799b30b39c964ecf62f39d59e7d43de15d910"
        "c2c1d69f3ebc01eab241e5dc"},
-      {"P521_XMD:SHA-512_SCALAR_TEST", "abcdef0123456789",
+      {&ec_hash_to_scalar_p521_xmd_sha512, NID_secp521r1,
+       "P521_XMD:SHA-512_SCALAR_TEST", "abcdef0123456789",
        "00af484a5d9389a9912f555234c578d4b1b7c4a6f5009018d133a4"
        "069172c9f5ce2d853b8643fe7bb50a83427ed3520a7a793c41a455"
        "a02aa99431434fb6b5b0b26e"},
-      {"P521_XMD:SHA-512_SCALAR_TEST",
+      {&ec_hash_to_scalar_p521_xmd_sha512, NID_secp521r1,
+       "P521_XMD:SHA-512_SCALAR_TEST",
        "a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -1305,8 +1369,10 @@
     SCOPED_TRACE(test.dst);
     SCOPED_TRACE(test.msg);
 
+    bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.curve_nid));
+    ASSERT_TRUE(group);
     EC_SCALAR scalar;
-    ASSERT_TRUE(ec_hash_to_scalar_p521_xmd_sha512(
+    ASSERT_TRUE(test.hash_to_scalar(
         group.get(), &scalar, reinterpret_cast<const uint8_t *>(test.dst),
         strlen(test.dst), reinterpret_cast<const uint8_t *>(test.msg),
         strlen(test.msg)));
@@ -1324,4 +1390,6 @@
   static const uint8_t kMessage[] = {4, 5, 6, 7};
   EXPECT_FALSE(ec_hash_to_scalar_p521_xmd_sha512(
       p224.get(), &scalar, kDST, sizeof(kDST), kMessage, sizeof(kMessage)));
+  EXPECT_FALSE(ec_hash_to_scalar_p384_xmd_sha512(
+      p224.get(), &scalar, kDST, sizeof(kDST), kMessage, sizeof(kMessage)));
 }
diff --git a/tool/speed.cc b/tool/speed.cc
index 8a05a3b..f403fe8 100644
--- a/tool/speed.cc
+++ b/tool/speed.cc
@@ -949,35 +949,63 @@
     return true;
   }
 
-  EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
-  if (group == NULL) {
-    return false;
-  }
   uint8_t input[64];
   RAND_bytes(input, sizeof(input));
 
   static const uint8_t kLabel[] = "label";
 
   TimeResults results;
-  if (!TimeFunction(&results, [&]() -> bool {
-        EC_RAW_POINT out;
-        return ec_hash_to_curve_p521_xmd_sha512_sswu(
-            group, &out, kLabel, sizeof(kLabel), input, sizeof(input));
-      })) {
-    fprintf(stderr, "hash-to-curve failed.\n");
-    return false;
-  }
-  results.Print("hash-to-curve P521_XMD:SHA-512_SSWU_RO_");
+  {
+    EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+    if (group == NULL) {
+      return false;
+    }
+    if (!TimeFunction(&results, [&]() -> bool {
+          EC_RAW_POINT out;
+          return ec_hash_to_curve_p521_xmd_sha512_sswu(
+              group, &out, kLabel, sizeof(kLabel), input, sizeof(input));
+        })) {
+      fprintf(stderr, "hash-to-curve failed.\n");
+      return false;
+    }
+    results.Print("hash-to-curve P521_XMD:SHA-512_SSWU_RO_");
 
-  if (!TimeFunction(&results, [&]() -> bool {
-        EC_SCALAR out;
-        return ec_hash_to_scalar_p521_xmd_sha512(
-            group, &out, kLabel, sizeof(kLabel), input, sizeof(input));
-      })) {
-    fprintf(stderr, "hash-to-scalar failed.\n");
-    return false;
+    if (!TimeFunction(&results, [&]() -> bool {
+          EC_SCALAR out;
+          return ec_hash_to_scalar_p521_xmd_sha512(
+              group, &out, kLabel, sizeof(kLabel), input, sizeof(input));
+        })) {
+      fprintf(stderr, "hash-to-scalar failed.\n");
+      return false;
+    }
+    results.Print("hash-to-scalar P521_XMD:SHA-512");
   }
-  results.Print("hash-to-scalar P521_XMD:SHA-512");
+
+  {
+    EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp384r1);
+    if (group == NULL) {
+      return false;
+    }
+    if (!TimeFunction(&results, [&]() -> bool {
+          EC_RAW_POINT out;
+          return ec_hash_to_curve_p384_xmd_sha512_sswu(
+              group, &out, kLabel, sizeof(kLabel), input, sizeof(input));
+        })) {
+      fprintf(stderr, "hash-to-curve failed.\n");
+      return false;
+    }
+    results.Print("hash-to-curve P384_XMD:SHA-512_SSWU_RO_");
+
+    if (!TimeFunction(&results, [&]() -> bool {
+          EC_SCALAR out;
+          return ec_hash_to_scalar_p384_xmd_sha512(
+              group, &out, kLabel, sizeof(kLabel), input, sizeof(input));
+        })) {
+      fprintf(stderr, "hash-to-scalar failed.\n");
+      return false;
+    }
+    results.Print("hash-to-scalar P384_XMD:SHA-512");
+  }
 
   return true;
 }