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