Trust Token Key Generation.

Trust Token is a new protocol to issue and redeem provably anonymized
tokens. To allow for development of key storage/management for Trust
Token, this CL provides the function to generate Trust Token keys.

Bug: chromium:1014199
Change-Id: If764e027b202f07be13c64f7be66dfaff71c45e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/40064
Commit-Queue: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index c6073d2..2d244d5 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -184,6 +184,7 @@
   err/pkcs8.errordata
   err/rsa.errordata
   err/ssl.errordata
+  err/trust_token.errordata
   err/x509.errordata
   err/x509v3.errordata
   WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/err
@@ -334,6 +335,7 @@
   thread_none.c
   thread_pthread.c
   thread_win.c
+  trust_token/privacy_pass.c
   x509/a_digest.c
   x509/a_sign.c
   x509/a_strex.c
@@ -533,6 +535,7 @@
   siphash/siphash_test.cc
   test/file_test_gtest.cc
   thread_test.cc
+  trust_token/trust_token_test.cc
   x509/x509_test.cc
   x509/x509_time_test.cc
   x509v3/tab_test.cc
diff --git a/crypto/err/err_data_generate.go b/crypto/err/err_data_generate.go
index da965df..153768d 100644
--- a/crypto/err/err_data_generate.go
+++ b/crypto/err/err_data_generate.go
@@ -64,6 +64,7 @@
 	"CIPHER",
 	"HKDF",
 	"USER",
+	"TRUST_TOKEN",
 }
 
 // stringList is a map from uint32 -> string which can output data for a sorted
diff --git a/crypto/err/trust_token.errordata b/crypto/err/trust_token.errordata
new file mode 100644
index 0000000..9f0b60c
--- /dev/null
+++ b/crypto/err/trust_token.errordata
@@ -0,0 +1,2 @@
+TRUST_TOKEN,101,BUFFER_TOO_SMALL
+TRUST_TOKEN,100,KEYGEN_FAILURE
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index 1d3f9f9..d8f0a0b 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -284,6 +284,18 @@
 OPENSSL_EXPORT int ec_bignum_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
                                        const BIGNUM *in);
 
+// ec_scalar_to_bytes serializes |in| as a big-endian bytestring to |out| and
+// sets |*out_len| to the number of bytes written. The number of bytes written
+// is |BN_num_bytes(&group->order)|, which is at most |EC_MAX_BYTES|.
+void ec_scalar_to_bytes(const EC_GROUP *group, uint8_t *out, size_t *out_len,
+                        const EC_SCALAR *in);
+
+// ec_scalar_from_bytes deserializes |in| and stores the resulting scalar over
+// group |group| to |out|. It returns one on success and zero if |in| is
+// invalid.
+int ec_scalar_from_bytes(const EC_GROUP *group, EC_SCALAR *out,
+                         const uint8_t *in, size_t len);
+
 // ec_random_nonzero_scalar sets |out| to a uniformly selected random value from
 // 1 to |group->order| - 1. It returns one on success and zero on error.
 int ec_random_nonzero_scalar(const EC_GROUP *group, EC_SCALAR *out,
diff --git a/crypto/fipsmodule/ec/scalar.c b/crypto/fipsmodule/ec/scalar.c
index 82f2a50..4b4f874 100644
--- a/crypto/fipsmodule/ec/scalar.c
+++ b/crypto/fipsmodule/ec/scalar.c
@@ -51,6 +51,36 @@
                              additional_data);
 }
 
+void ec_scalar_to_bytes(const EC_GROUP *group, uint8_t *out, size_t *out_len,
+                        const EC_SCALAR *in) {
+  size_t len = BN_num_bytes(&group->order);
+  for (size_t i = 0; i < len; i++) {
+    out[len - i - 1] = in->bytes[i];
+  }
+  *out_len = len;
+}
+
+int ec_scalar_from_bytes(const EC_GROUP *group, EC_SCALAR *out,
+                         const uint8_t *in, size_t len) {
+  if (len != BN_num_bytes(&group->order)) {
+    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
+    return 0;
+  }
+
+  OPENSSL_memset(out, 0, sizeof(EC_SCALAR));
+
+  for (size_t i = 0; i < len; i++) {
+    out->bytes[i] = in[len - i - 1];
+  }
+
+  if (!bn_less_than_words(out->words, group->order.d, group->order.width)) {
+    OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
+    return 0;
+  }
+
+  return 1;
+}
+
 void ec_scalar_add(const EC_GROUP *group, EC_SCALAR *r, const EC_SCALAR *a,
                    const EC_SCALAR *b) {
   const BIGNUM *order = &group->order;
diff --git a/crypto/trust_token/privacy_pass.c b/crypto/trust_token/privacy_pass.c
new file mode 100644
index 0000000..84525ea
--- /dev/null
+++ b/crypto/trust_token/privacy_pass.c
@@ -0,0 +1,238 @@
+/* Copyright (c) 2020, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/bn.h>
+#include <openssl/bytestring.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/nid.h>
+#include <openssl/trust_token.h>
+
+#include "../fipsmodule/bn/internal.h"
+#include "../fipsmodule/ec/internal.h"
+
+
+// Privacy Pass uses a custom elliptic curve construction described in
+// https://eprint.iacr.org/2020/072.pdf (section 7, construction 4). Ths
+// construction provides anonymous tokens with private metadata and validity
+// verification.
+
+// get_h returns a randomly selected point for the Privacy Pass protocol.
+//
+// x: 66591746412783875033873351891229753622964683369847172829242944646280287810
+//    81195403447871073952234683395256591180452378091073292247502091640572714366
+//    588045092
+// y: 12347430519393087872533727997980072129796839266949808299436682045034861065
+//    18810630511924722292325611253427311923464047364545304196431830383014967865
+//    162306253
+//
+// This point was generated with the following Python code.
+
+/*
+import hashlib
+
+SEED_H = 'PrivacyPass H'
+
+A = -3
+B = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00
+P = 2**521 - 1
+
+def get_y(x):
+  y2 = (x**3 + A*x + B) % P
+  y = pow(y2, (P+1)/4, P)
+  if (y*y) % P != y2:
+    raise ValueError("point not on curve")
+  return y
+
+def bit(h,i):
+  return (ord(h[i/8]) >> (i%8)) & 1
+
+b = 521
+def decode_point(so):
+  s = hashlib.sha256(so + '0').digest() + hashlib.sha256(so + '1').digest() + \
+      hashlib.sha256(so + '2').digest()
+
+  x = 0
+  for i in range(0,b):
+    x = x + (long(bit(s,i))<<i)
+  if x >= P:
+    raise ValueError("x out of range")
+  y = get_y(x)
+  if y & 1 != bit(s,b-1): y = P-y
+  return (x, y)
+
+
+def gen_point(seed):
+  v = hashlib.sha256(seed).digest()
+  it = 1
+  while True:
+    try:
+      x,y = decode_point(v)
+    except Exception, e:
+      print e
+      it += 1
+      v = hashlib.sha256(v).digest()
+      continue
+    print "Found in %d iterations:" % it
+    print "  x = %d" % x
+    print "  y = %d" % y
+    print " Encoded (hex): (%x, %x)" % (x, y)
+    return (x, y)
+
+if __name__ == "__main__":
+  gen_point(SEED_H)
+*/
+
+static EC_POINT *get_h(void) {
+  EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+  if (group == NULL) {
+    return NULL;
+  }
+
+  static const BN_ULONG kHGenX[] = {
+      TOBN(0x3d01749f, 0xc51e4724),
+      TOBN(0x31c28621, 0xf95c98b9),
+      TOBN(0x6dc5392a, 0xd4ce846e),
+      TOBN(0xda645354, 0x4ef9760d),
+      TOBN(0x5945d13e, 0x25337e4c),
+      TOBN(0xeb0f6bc0, 0x5c0ecefe),
+      TOBN(0xab291003, 0x6f4ef5bd),
+      TOBN(0xa9f79ebc, 0x126cefd1),
+      0x000001f0,
+  };
+  static const BIGNUM kX = STATIC_BIGNUM(kHGenX);
+
+  static const BN_ULONG kHGenY[] = {
+      TOBN(0xffa6a0ea, 0x966792cd),
+      TOBN(0x6e783d17, 0x08e3df3c),
+      TOBN(0xb5617012, 0x72ac6ab0),
+      TOBN(0xe0bcf350, 0x5c7e6641),
+      TOBN(0x53bc55ea, 0xad8f261d),
+      TOBN(0xbba93b9d, 0x70491eb4),
+      TOBN(0x5214756f, 0x36d9c7fa),
+      TOBN(0x1762517d, 0x325e29ac),
+      0x0000005c,
+  };
+  static const BIGNUM kY = STATIC_BIGNUM(kHGenY);
+
+  EC_POINT *h = EC_POINT_new(group);
+  if (h == NULL ||
+      !EC_POINT_set_affine_coordinates_GFp(group, h, &kX, &kY, NULL)) {
+    EC_POINT_free(h);
+    return NULL;
+  }
+  return h;
+}
+
+// generate_keypair generates a keypair for the Private Metadata construction.
+// |out_x| and |out_y| are set to the secret half of the keypair, while
+// |*out_pub| is set to the public half of the keypair. It returns one on
+// success and zero on failure.
+static int generate_keypair(EC_SCALAR *out_x, EC_SCALAR *out_y,
+                            EC_POINT **out_pub, const EC_GROUP *group) {
+  EC_POINT *h = get_h();
+  if (h == NULL) {
+    return 0;
+  }
+
+  static const uint8_t kDefaultAdditionalData[32] = {0};
+  EC_RAW_POINT tmp1, tmp2;
+  EC_POINT *pub = EC_POINT_new(group);
+  if (pub == NULL ||
+      !ec_random_nonzero_scalar(group, out_x, kDefaultAdditionalData) ||
+      !ec_random_nonzero_scalar(group, out_y, kDefaultAdditionalData) ||
+      !ec_point_mul_scalar_base(group, &tmp1, out_x) ||
+      !ec_point_mul_scalar(group, &tmp2, &h->raw, out_y)) {
+    EC_POINT_free(h);
+    EC_POINT_free(pub);
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  group->meth->add(group, &pub->raw, &tmp1, &tmp2);
+  *out_pub = pub;
+
+  EC_POINT_free(h);
+  return 1;
+}
+
+int TRUST_TOKEN_generate_key(uint8_t *out_priv_key, size_t *out_priv_key_len,
+                             size_t max_priv_key_len, uint8_t *out_pub_key,
+                             size_t *out_pub_key_len, size_t max_pub_key_len,
+                             uint32_t id) {
+  int ok = 0;
+  EC_POINT *pub0 = NULL, *pub1 = NULL, *pubs = NULL;
+  CBB cbb;
+  CBB_zero(&cbb);
+  uint8_t *buf = NULL;
+  EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+  if (group == NULL) {
+    return 0;
+  }
+
+  EC_SCALAR x0, y0, x1, y1, xs, ys;
+  if (!generate_keypair(&x0, &y0, &pub0, group) ||
+      !generate_keypair(&x1, &y1, &pub1, group) ||
+      !generate_keypair(&xs, &ys, &pubs, group)) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
+    goto err;
+  }
+
+  size_t scalar_len = BN_num_bytes(&group->order);
+  if (!CBB_init_fixed(&cbb, out_priv_key, max_priv_key_len) ||
+      !CBB_add_u32(&cbb, id)) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+    goto err;
+  }
+
+  const EC_SCALAR *scalars[] = {&x0, &y0, &x1, &y1, &xs, &ys};
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(scalars); i++) {
+    if (!CBB_add_space(&cbb, &buf, scalar_len)) {
+      OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+      goto err;
+    }
+    ec_scalar_to_bytes(group, buf, &scalar_len, scalars[i]);
+  }
+
+  if (!CBB_finish(&cbb, NULL, out_priv_key_len)) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+    goto err;
+  }
+
+  CBB pub_cbb;
+  if (!CBB_init_fixed(&cbb, out_pub_key, max_pub_key_len) ||
+      !CBB_add_u32(&cbb, id) ||
+      !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
+      !EC_POINT_point2cbb(&pub_cbb, group, pub0, POINT_CONVERSION_UNCOMPRESSED,
+                          NULL) ||
+      !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
+      !EC_POINT_point2cbb(&pub_cbb, group, pub1, POINT_CONVERSION_UNCOMPRESSED,
+                          NULL) ||
+      !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
+      !EC_POINT_point2cbb(&pub_cbb, group, pubs, POINT_CONVERSION_UNCOMPRESSED,
+                          NULL) ||
+      !CBB_finish(&cbb, NULL, out_pub_key_len)) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+    goto err;
+  }
+
+  ok = 1;
+
+err:
+  CBB_cleanup(&cbb);
+  EC_POINT_free(pub0);
+  EC_POINT_free(pub1);
+  EC_POINT_free(pubs);
+  return ok;
+}
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc
new file mode 100644
index 0000000..0b24ee1
--- /dev/null
+++ b/crypto/trust_token/trust_token_test.cc
@@ -0,0 +1,29 @@
+/* Copyright (c) 2020, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <gtest/gtest.h>
+
+#include <openssl/trust_token.h>
+
+
+TEST(TrustTokenTest, KeyGen) {
+  uint8_t priv_key[TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE];
+  uint8_t pub_key[TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE];
+  size_t priv_key_len, pub_key_len;
+  ASSERT_TRUE(TRUST_TOKEN_generate_key(
+      priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, pub_key,
+      &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0x0001));
+  ASSERT_EQ(400u, priv_key_len);
+  ASSERT_EQ(409u, pub_key_len);
+}
diff --git a/include/openssl/err.h b/include/openssl/err.h
index f9cd9f2..0f6824e 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -320,6 +320,7 @@
   ERR_LIB_CIPHER,
   ERR_LIB_HKDF,
   ERR_LIB_USER,
+  ERR_LIB_TRUST_TOKEN,
   ERR_NUM_LIBS
 };
 
diff --git a/include/openssl/trust_token.h b/include/openssl/trust_token.h
new file mode 100644
index 0000000..bf5b5fa
--- /dev/null
+++ b/include/openssl/trust_token.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_TRUST_TOKEN_H
+#define OPENSSL_HEADER_TRUST_TOKEN_H
+
+#include <openssl/base.h>
+#include <openssl/stack.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// Trust Token implementation.
+//
+// Trust Token is an implementation of an experimental mechanism similar to
+// Privacy Pass which allows issuance and redemption of anonymized tokens with
+// limited private metadata.
+//
+// References:
+// https://eprint.iacr.org/2020/072.pdf
+// https://github.com/alxdavids/privacy-pass-ietf/tree/master/drafts
+// https://github.com/WICG/trust-token-api/blob/master/README.md
+//
+// WARNING: This API is unstable and subject to change.
+
+#define TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE 512
+#define TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE 512
+
+// TRUST_TOKEN_generate_key creates a new Trust Token keypair labeled with |id|
+// and serializes the private and public keys, writing the private key to
+// |out_priv_key| and setting |*out_priv_key_len| to the number of bytes
+// written, and writing the public key to |out_pub_key| and setting
+// |*out_pub_key_len| to the number of bytes written.
+//
+// At most |max_priv_key_len| and |max_pub_key_len| bytes are written. In order
+// to ensure success, these should be at least
+// |TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE| and |TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE|.
+//
+// WARNING: This API is unstable and the serializations of these keys are
+// subject to change. Keys generated with this function may not be persisted.
+//
+// This function returns one on success or zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_generate_key(
+    uint8_t *out_priv_key, size_t *out_priv_key_len, size_t max_priv_key_len,
+    uint8_t *out_pub_key, size_t *out_pub_key_len, size_t max_pub_key_len,
+    uint32_t id);
+
+
+#if defined(__cplusplus)
+}  // extern C
+#endif
+
+#define TRUST_TOKEN_R_KEYGEN_FAILURE 100
+#define TRUST_TOKEN_R_BUFFER_TOO_SMALL 101
+
+#endif  // OPENSSL_HEADER_TRUST_TOKEN_H