Add fixed key generation for Trust Token.
Certain applications of Trust Token need to be able to generate
a large number of keys, instead of storing them all, we provide
an API to take a secret that can be used to generate keys
in a deterministic manner.
Change-Id: I2b61958d1e949a3a47a3c91ab3a866c2e33a9d1d
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/53011
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Steven Valdez <svaldez@google.com>
diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h
index 0aa1936..31ecc49 100644
--- a/crypto/trust_token/internal.h
+++ b/crypto/trust_token/internal.h
@@ -93,6 +93,9 @@
// functions for |TRUST_TOKENS_experiment_v1|'s PMBTokens construction which
// uses P-384.
int pmbtoken_exp1_generate_key(CBB *out_private, CBB *out_public);
+int pmbtoken_exp1_derive_key_from_secret(CBB *out_private, CBB *out_public,
+ const uint8_t *secret,
+ size_t secret_len);
int pmbtoken_exp1_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len);
int pmbtoken_exp1_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key,
@@ -118,6 +121,9 @@
// functions for |TRUST_TOKENS_experiment_v2|'s PMBTokens construction which
// uses P-384.
int pmbtoken_exp2_generate_key(CBB *out_private, CBB *out_public);
+int pmbtoken_exp2_derive_key_from_secret(CBB *out_private, CBB *out_public,
+ const uint8_t *secret,
+ size_t secret_len);
int pmbtoken_exp2_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len);
int pmbtoken_exp2_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key,
@@ -153,6 +159,8 @@
// functions for |TRUST_TOKENS_experiment_v2|'s VOPRF construction which uses
// P-384.
int voprf_exp2_generate_key(CBB *out_private, CBB *out_public);
+int voprf_exp2_derive_key_from_secret(CBB *out_private, CBB *out_public,
+ const uint8_t *secret, size_t secret_len);
int voprf_exp2_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len);
int voprf_exp2_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key,
@@ -179,6 +187,12 @@
// zero on failure.
int (*generate_key)(CBB *out_private, CBB *out_public);
+ // derive_key_from_secret deterministically derives a keypair based on
+ // |secret| and writes their serialized forms into |out_private| and
+ // |out_public|. It returns one on success and zero on failure.
+ int (*derive_key_from_secret)(CBB *out_private, CBB *out_public,
+ const uint8_t *secret, size_t secret_len);
+
// client_key_from_bytes decodes a client key from |in| and sets |key|
// to the resulting key. It returns one on success and zero
// on failure.
diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c
index a6549b9..68d8909 100644
--- a/crypto/trust_token/pmbtoken.c
+++ b/crypto/trust_token/pmbtoken.c
@@ -37,6 +37,8 @@
const uint8_t s[TRUST_TOKEN_NONCE_SIZE]);
typedef int (*hash_c_func_t)(const EC_GROUP *group, EC_SCALAR *out,
uint8_t *buf, size_t len);
+typedef int (*hash_to_scalar_func_t)(const EC_GROUP *group, EC_SCALAR *out,
+ uint8_t *buf, size_t len);
typedef struct {
const EC_GROUP *group;
@@ -52,6 +54,9 @@
// hash_c implements the H_c operation in PMBTokens. It returns one on success
// and zero on error.
hash_c_func_t hash_c;
+ // hash_to_scalar implements the HashToScalar operation for PMBTokens. It
+ // returns one on success and zero on error.
+ hash_to_scalar_func_t hash_to_scalar;
int prefix_point : 1;
} PMBTOKEN_METHOD;
@@ -60,7 +65,9 @@
static int pmbtoken_init_method(PMBTOKEN_METHOD *method, int curve_nid,
const uint8_t *h_bytes, size_t h_len,
hash_t_func_t hash_t, hash_s_func_t hash_s,
- hash_c_func_t hash_c, int prefix_point) {
+ hash_c_func_t hash_c,
+ hash_to_scalar_func_t hash_to_scalar,
+ int prefix_point) {
method->group = EC_GROUP_new_by_curve_name(curve_nid);
if (method->group == NULL) {
return 0;
@@ -69,6 +76,7 @@
method->hash_t = hash_t;
method->hash_s = hash_s;
method->hash_c = hash_c;
+ method->hash_to_scalar = hash_to_scalar;
method->prefix_point = prefix_point;
EC_AFFINE h;
@@ -85,21 +93,32 @@
return 1;
}
-// generate_keypair generates a keypair for the PMBTokens 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(const PMBTOKEN_METHOD *method, EC_SCALAR *out_x,
- EC_SCALAR *out_y, EC_RAW_POINT *out_pub) {
- if (!ec_random_nonzero_scalar(method->group, out_x, kDefaultAdditionalData) ||
- !ec_random_nonzero_scalar(method->group, out_y, kDefaultAdditionalData) ||
- !ec_point_mul_scalar_precomp(method->group, out_pub, &method->g_precomp,
- out_x, &method->h_precomp, out_y, NULL,
- NULL)) {
- OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
- return 0;
+static int derive_scalar_from_secret(const PMBTOKEN_METHOD *method,
+ EC_SCALAR *out, const uint8_t *secret,
+ size_t secret_len, uint8_t scalar_id) {
+ static const uint8_t kKeygenLabel[] = "TrustTokenPMBTokenKeyGen";
+
+ int ok = 0;
+ CBB cbb;
+ CBB_zero(&cbb);
+ uint8_t *buf = NULL;
+ size_t len;
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_bytes(&cbb, kKeygenLabel, sizeof(kKeygenLabel)) ||
+ !CBB_add_u8(&cbb, scalar_id) ||
+ !CBB_add_bytes(&cbb, secret, secret_len) ||
+ !CBB_finish(&cbb, &buf, &len) ||
+ !method->hash_to_scalar(method->group, out, buf, len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
+ goto err;
}
- return 1;
+
+ ok = 1;
+
+err:
+ CBB_cleanup(&cbb);
+ OPENSSL_free(buf);
+ return ok;
}
static int point_to_cbb(CBB *out, const EC_GROUP *group,
@@ -165,19 +184,24 @@
scalars, 3);
}
-static int pmbtoken_generate_key(const PMBTOKEN_METHOD *method,
- CBB *out_private, CBB *out_public) {
+static int pmbtoken_compute_keys(const PMBTOKEN_METHOD *method,
+ CBB *out_private, CBB *out_public,
+ const EC_SCALAR *x0, const EC_SCALAR *y0,
+ const EC_SCALAR *x1, const EC_SCALAR *y1,
+ const EC_SCALAR *xs, const EC_SCALAR *ys) {
const EC_GROUP *group = method->group;
EC_RAW_POINT pub[3];
- EC_SCALAR x0, y0, x1, y1, xs, ys;
- if (!generate_keypair(method, &x0, &y0, &pub[0]) ||
- !generate_keypair(method, &x1, &y1, &pub[1]) ||
- !generate_keypair(method, &xs, &ys, &pub[2])) {
+ if (!ec_point_mul_scalar_precomp(group, &pub[0], &method->g_precomp,
+ x0, &method->h_precomp, y0, NULL, NULL) ||
+ !ec_point_mul_scalar_precomp(group, &pub[1], &method->g_precomp,
+ x1, &method->h_precomp, y1, NULL, NULL) ||
+ !ec_point_mul_scalar_precomp(method->group, &pub[2], &method->g_precomp,
+ xs, &method->h_precomp, ys, NULL, NULL)) {
OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
return 0;
}
- const EC_SCALAR *scalars[] = {&x0, &y0, &x1, &y1, &xs, &ys};
+ const EC_SCALAR *scalars[] = {x0, y0, x1, y1, xs, ys};
size_t scalar_len = BN_num_bytes(&group->order);
for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(scalars); i++) {
uint8_t *buf;
@@ -206,6 +230,42 @@
return 1;
}
+static int pmbtoken_generate_key(const PMBTOKEN_METHOD *method,
+ CBB *out_private, CBB *out_public) {
+ EC_SCALAR x0, y0, x1, y1, xs, ys;
+ if (!ec_random_nonzero_scalar(method->group, &x0, kDefaultAdditionalData) ||
+ !ec_random_nonzero_scalar(method->group, &y0, kDefaultAdditionalData) ||
+ !ec_random_nonzero_scalar(method->group, &x1, kDefaultAdditionalData) ||
+ !ec_random_nonzero_scalar(method->group, &y1, kDefaultAdditionalData) ||
+ !ec_random_nonzero_scalar(method->group, &xs, kDefaultAdditionalData) ||
+ !ec_random_nonzero_scalar(method->group, &ys, kDefaultAdditionalData)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
+ return 0;
+ }
+
+ return pmbtoken_compute_keys(method, out_private, out_public, &x0, &y0, &x1,
+ &y1, &xs, &ys);
+}
+
+static int pmbtoken_derive_key_from_secret(const PMBTOKEN_METHOD *method,
+ CBB *out_private, CBB *out_public,
+ const uint8_t *secret,
+ size_t secret_len) {
+ EC_SCALAR x0, y0, x1, y1, xs, ys;
+ if (!derive_scalar_from_secret(method, &x0, secret, secret_len, 0) ||
+ !derive_scalar_from_secret(method, &y0, secret, secret_len, 1) ||
+ !derive_scalar_from_secret(method, &x1, secret, secret_len, 2) ||
+ !derive_scalar_from_secret(method, &y1, secret, secret_len, 3) ||
+ !derive_scalar_from_secret(method, &xs, secret, secret_len, 4) ||
+ !derive_scalar_from_secret(method, &ys, secret, secret_len, 5)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
+ return 0;
+ }
+
+ return pmbtoken_compute_keys(method, out_private, out_public, &x0, &y0, &x1,
+ &y1, &xs, &ys);
+}
+
static int pmbtoken_client_key_from_bytes(const PMBTOKEN_METHOD *method,
TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len) {
@@ -1140,6 +1200,13 @@
group, out, kHashCLabel, sizeof(kHashCLabel), buf, len);
}
+static int pmbtoken_exp1_hash_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ uint8_t *buf, size_t len) {
+ const uint8_t kHashLabel[] = "PMBTokens Experiment V1 HashToScalar";
+ return ec_hash_to_scalar_p384_xmd_sha512_draft07(
+ group, out, kHashLabel, sizeof(kHashLabel), buf, len);
+}
+
static int pmbtoken_exp1_ok = 0;
static PMBTOKEN_METHOD pmbtoken_exp1_method;
static CRYPTO_once_t pmbtoken_exp1_method_once = CRYPTO_ONCE_INIT;
@@ -1159,10 +1226,10 @@
0x87, 0xc3, 0x95, 0xd0, 0x13, 0xb7, 0x0b, 0x5c, 0xc7,
};
- pmbtoken_exp1_ok =
- pmbtoken_init_method(&pmbtoken_exp1_method, NID_secp384r1, kH, sizeof(kH),
- pmbtoken_exp1_hash_t, pmbtoken_exp1_hash_s,
- pmbtoken_exp1_hash_c, 1);
+ pmbtoken_exp1_ok = pmbtoken_init_method(
+ &pmbtoken_exp1_method, NID_secp384r1, kH, sizeof(kH),
+ pmbtoken_exp1_hash_t, pmbtoken_exp1_hash_s, pmbtoken_exp1_hash_c,
+ pmbtoken_exp1_hash_to_scalar, 1);
}
static int pmbtoken_exp1_init_method(void) {
@@ -1182,6 +1249,17 @@
return pmbtoken_generate_key(&pmbtoken_exp1_method, out_private, out_public);
}
+int pmbtoken_exp1_derive_key_from_secret(CBB *out_private, CBB *out_public,
+ const uint8_t *secret,
+ size_t secret_len) {
+ if (!pmbtoken_exp1_init_method()) {
+ return 0;
+ }
+
+ return pmbtoken_derive_key_from_secret(&pmbtoken_exp1_method, out_private,
+ out_public, secret, secret_len);
+}
+
int pmbtoken_exp1_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len) {
if (!pmbtoken_exp1_init_method()) {
@@ -1290,6 +1368,13 @@
group, out, kHashCLabel, sizeof(kHashCLabel), buf, len);
}
+static int pmbtoken_exp2_hash_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
+ uint8_t *buf, size_t len) {
+ const uint8_t kHashLabel[] = "PMBTokens Experiment V2 HashToScalar";
+ return ec_hash_to_scalar_p384_xmd_sha512_draft07(
+ group, out, kHashLabel, sizeof(kHashLabel), buf, len);
+}
+
static int pmbtoken_exp2_ok = 0;
static PMBTOKEN_METHOD pmbtoken_exp2_method;
static CRYPTO_once_t pmbtoken_exp2_method_once = CRYPTO_ONCE_INIT;
@@ -1309,10 +1394,10 @@
0x25, 0x62, 0xbf, 0x59, 0xb2, 0xd2, 0x3d, 0x71, 0xff
};
- pmbtoken_exp2_ok =
- pmbtoken_init_method(&pmbtoken_exp2_method, NID_secp384r1, kH, sizeof(kH),
- pmbtoken_exp2_hash_t, pmbtoken_exp2_hash_s,
- pmbtoken_exp2_hash_c, 0);
+ pmbtoken_exp2_ok = pmbtoken_init_method(
+ &pmbtoken_exp2_method, NID_secp384r1, kH, sizeof(kH),
+ pmbtoken_exp2_hash_t, pmbtoken_exp2_hash_s, pmbtoken_exp2_hash_c,
+ pmbtoken_exp2_hash_to_scalar, 0);
}
static int pmbtoken_exp2_init_method(void) {
@@ -1332,6 +1417,18 @@
return pmbtoken_generate_key(&pmbtoken_exp2_method, out_private, out_public);
}
+
+int pmbtoken_exp2_derive_key_from_secret(CBB *out_private, CBB *out_public,
+ const uint8_t *secret,
+ size_t secret_len) {
+ if (!pmbtoken_exp2_init_method()) {
+ return 0;
+ }
+
+ return pmbtoken_derive_key_from_secret(&pmbtoken_exp2_method, out_private,
+ out_public, secret, secret_len);
+}
+
int pmbtoken_exp2_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len) {
if (!pmbtoken_exp2_init_method()) {
diff --git a/crypto/trust_token/trust_token.c b/crypto/trust_token/trust_token.c
index 3334fba..5afb487 100644
--- a/crypto/trust_token/trust_token.c
+++ b/crypto/trust_token/trust_token.c
@@ -30,6 +30,7 @@
const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v1(void) {
static const TRUST_TOKEN_METHOD kMethod = {
pmbtoken_exp1_generate_key,
+ pmbtoken_exp1_derive_key_from_secret,
pmbtoken_exp1_client_key_from_bytes,
pmbtoken_exp1_issuer_key_from_bytes,
pmbtoken_exp1_blind,
@@ -46,6 +47,7 @@
const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_voprf(void) {
static const TRUST_TOKEN_METHOD kMethod = {
voprf_exp2_generate_key,
+ voprf_exp2_derive_key_from_secret,
voprf_exp2_client_key_from_bytes,
voprf_exp2_issuer_key_from_bytes,
voprf_exp2_blind,
@@ -62,6 +64,7 @@
const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_pmb(void) {
static const TRUST_TOKEN_METHOD kMethod = {
pmbtoken_exp2_generate_key,
+ pmbtoken_exp2_derive_key_from_secret,
pmbtoken_exp2_client_key_from_bytes,
pmbtoken_exp2_issuer_key_from_bytes,
pmbtoken_exp2_blind,
@@ -140,6 +143,43 @@
return ret;
}
+int TRUST_TOKEN_derive_key_from_secret(
+ const TRUST_TOKEN_METHOD *method, 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,
+ const uint8_t *secret, size_t secret_len) {
+ // Prepend the key ID in front of the PMBTokens format.
+ int ret = 0;
+ CBB priv_cbb, pub_cbb;
+ CBB_zero(&priv_cbb);
+ CBB_zero(&pub_cbb);
+ if (!CBB_init_fixed(&priv_cbb, out_priv_key, max_priv_key_len) ||
+ !CBB_init_fixed(&pub_cbb, out_pub_key, max_pub_key_len) ||
+ !CBB_add_u32(&priv_cbb, id) ||
+ !CBB_add_u32(&pub_cbb, id)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+ goto err;
+ }
+
+ if (!method->derive_key_from_secret(&priv_cbb, &pub_cbb, secret,
+ secret_len)) {
+ goto err;
+ }
+
+ if (!CBB_finish(&priv_cbb, NULL, out_priv_key_len) ||
+ !CBB_finish(&pub_cbb, NULL, out_pub_key_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ CBB_cleanup(&priv_cbb);
+ CBB_cleanup(&pub_cbb);
+ return ret;
+}
+
TRUST_TOKEN_CLIENT *TRUST_TOKEN_CLIENT_new(const TRUST_TOKEN_METHOD *method,
size_t max_batchsize) {
if (max_batchsize > 0xffff) {
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc
index 72d555b..5ab80cd 100644
--- a/crypto/trust_token/trust_token_test.cc
+++ b/crypto/trust_token/trust_token_test.cc
@@ -54,6 +54,73 @@
TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0x0001));
ASSERT_EQ(292u, priv_key_len);
ASSERT_EQ(301u, pub_key_len);
+
+ const uint8_t kKeygenSecret[] = "SEED";
+ ASSERT_TRUE(TRUST_TOKEN_derive_key_from_secret(
+ TRUST_TOKEN_experiment_v1(), priv_key, &priv_key_len,
+ TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, pub_key, &pub_key_len,
+ TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0x0001, kKeygenSecret,
+ sizeof(kKeygenSecret) - 1));
+
+ const uint8_t kExpectedPriv[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x98, 0xaa, 0x32, 0xfc, 0x5f, 0x83, 0x35, 0xea,
+ 0x57, 0x4f, 0x9e, 0x61, 0x48, 0x6e, 0x89, 0x9d, 0x3d, 0xaa, 0x38, 0x5d,
+ 0xd0, 0x06, 0x96, 0x62, 0xe8, 0x0b, 0xd6, 0x5f, 0x12, 0xa4, 0xcc, 0xa9,
+ 0xb5, 0x20, 0x1b, 0x13, 0x8c, 0x1c, 0xaf, 0x36, 0x1b, 0xab, 0x0c, 0xc6,
+ 0xac, 0x38, 0xae, 0x96, 0x3d, 0x14, 0x9d, 0xb8, 0x8d, 0xf4, 0x7f, 0xe2,
+ 0x7d, 0xeb, 0x17, 0xc2, 0xbc, 0x63, 0x42, 0x93, 0x94, 0xe4, 0x97, 0xbf,
+ 0x97, 0xea, 0x02, 0x40, 0xac, 0xb6, 0xa5, 0x03, 0x4c, 0x6b, 0x4c, 0xb8,
+ 0x8c, 0xf4, 0x66, 0x1b, 0x4e, 0x02, 0x45, 0xf9, 0xcd, 0xb6, 0x0f, 0x59,
+ 0x09, 0x21, 0x03, 0x7e, 0x92, 0x1f, 0x3f, 0x40, 0x83, 0x50, 0xe3, 0xdc,
+ 0x9e, 0x6f, 0x65, 0xc5, 0xbd, 0x2c, 0x7d, 0xab, 0x74, 0x49, 0xc8, 0xa2,
+ 0x3c, 0xab, 0xcb, 0x4d, 0x63, 0x73, 0x81, 0x2b, 0xb2, 0x1e, 0x00, 0x8f,
+ 0x00, 0xb8, 0xd8, 0xb4, 0x5d, 0xc4, 0x3f, 0x3d, 0xa8, 0x4f, 0x4c, 0x72,
+ 0x0e, 0x20, 0x17, 0x4b, 0xac, 0x14, 0x8f, 0xb2, 0xa5, 0x20, 0x41, 0x2b,
+ 0xf7, 0x62, 0x25, 0x6a, 0xd6, 0x41, 0x26, 0x62, 0x10, 0xc1, 0xbc, 0x42,
+ 0xac, 0x54, 0x1b, 0x75, 0x05, 0xd6, 0x53, 0xb1, 0x7b, 0x84, 0x6a, 0x7b,
+ 0x5b, 0x2a, 0x34, 0x6e, 0x43, 0x4b, 0x43, 0xcc, 0x6c, 0xdb, 0x1d, 0x02,
+ 0x34, 0x7f, 0xd1, 0xe8, 0xfd, 0x42, 0x2c, 0xd9, 0x14, 0xdb, 0xd6, 0xf4,
+ 0xad, 0xb5, 0xe4, 0xac, 0xdd, 0x7e, 0xb5, 0x4c, 0x3f, 0x59, 0x24, 0xfa,
+ 0x04, 0xd9, 0xb6, 0xd2, 0xb7, 0x7d, 0xf1, 0xfa, 0x13, 0xc0, 0x4d, 0xd5,
+ 0xca, 0x3a, 0x4e, 0xa8, 0xdd, 0xa9, 0xfc, 0xcb, 0x06, 0xb2, 0xde, 0x4b,
+ 0x2a, 0x86, 0xbb, 0x0d, 0x41, 0xb6, 0x3d, 0xfb, 0x49, 0xc8, 0xdf, 0x9a,
+ 0x48, 0xe5, 0x68, 0x8a, 0xfc, 0x86, 0x9c, 0x79, 0x5a, 0x79, 0xc1, 0x09,
+ 0x33, 0x53, 0xdc, 0x3d, 0xe9, 0x93, 0x7c, 0x5b, 0x72, 0xf7, 0xa0, 0x8a,
+ 0x1f, 0x07, 0x6c, 0x38, 0x3c, 0x99, 0x0b, 0xe4, 0x4e, 0xa4, 0xbd, 0x41,
+ 0x1f, 0x83, 0xa6, 0xd3
+ };
+ ASSERT_EQ(Bytes(kExpectedPriv, sizeof(kExpectedPriv)),
+ Bytes(priv_key, priv_key_len));
+
+ const uint8_t kExpectedPub[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x61, 0x04, 0x5e, 0x06, 0x6b, 0x7b, 0xfd,
+ 0x54, 0x01, 0xe0, 0xd2, 0xb5, 0x12, 0xce, 0x48, 0x16, 0x66, 0xb2, 0xdf,
+ 0xfd, 0xa8, 0x38, 0x7c, 0x1f, 0x45, 0x1a, 0xb8, 0x21, 0x52, 0x17, 0x25,
+ 0xbb, 0x0b, 0x00, 0xd4, 0xa1, 0xbc, 0x28, 0xd9, 0x08, 0x36, 0x98, 0xb2,
+ 0x17, 0xd3, 0xb5, 0xad, 0xb6, 0x4e, 0x03, 0x5f, 0xd3, 0x66, 0x2c, 0x58,
+ 0x1c, 0xcc, 0xc6, 0x23, 0xa4, 0xf9, 0xa2, 0x7e, 0xb0, 0xe4, 0xd3, 0x95,
+ 0x41, 0x6f, 0xba, 0x23, 0x4a, 0x82, 0x93, 0x29, 0x73, 0x75, 0x38, 0x85,
+ 0x64, 0x9c, 0xaa, 0x12, 0x6d, 0x7d, 0xcd, 0x52, 0x02, 0x91, 0x9f, 0xa9,
+ 0xee, 0x4b, 0xfd, 0x68, 0x97, 0x40, 0xdc, 0x00, 0x61, 0x04, 0x14, 0x16,
+ 0x39, 0xf9, 0x63, 0x66, 0x94, 0x03, 0xfa, 0x0b, 0xbf, 0xca, 0x5a, 0x39,
+ 0x9f, 0x27, 0x5b, 0x3f, 0x69, 0x7a, 0xc9, 0xf7, 0x25, 0x7c, 0x84, 0x9e,
+ 0x1d, 0x61, 0x5a, 0x24, 0x53, 0xf2, 0x4a, 0x9d, 0xe9, 0x05, 0x53, 0xfd,
+ 0x12, 0x01, 0x2d, 0x9a, 0x69, 0x50, 0x74, 0x82, 0xa3, 0x45, 0x73, 0xdc,
+ 0x34, 0x36, 0x31, 0x44, 0x07, 0x0c, 0xda, 0x13, 0xbe, 0x94, 0x37, 0x65,
+ 0xa0, 0xab, 0x16, 0x52, 0x90, 0xe5, 0x8a, 0x03, 0xe5, 0x98, 0x79, 0x14,
+ 0x79, 0xd5, 0x17, 0xee, 0xd4, 0xb8, 0xda, 0x77, 0x76, 0x03, 0x20, 0x2a,
+ 0x7e, 0x3b, 0x76, 0x0b, 0x23, 0xb7, 0x72, 0x77, 0xb2, 0xeb, 0x00, 0x61,
+ 0x04, 0x68, 0x18, 0x4d, 0x23, 0x23, 0xf4, 0x45, 0xb8, 0x81, 0x0d, 0xa4,
+ 0x5d, 0x0b, 0x9e, 0x08, 0xfb, 0x45, 0xfb, 0x96, 0x29, 0x43, 0x2f, 0xab,
+ 0x93, 0x04, 0x4c, 0x04, 0xb6, 0x5e, 0x27, 0xf5, 0x39, 0x66, 0x94, 0x15,
+ 0x1d, 0xb1, 0x1c, 0x7c, 0x27, 0x6f, 0xa5, 0x19, 0x0c, 0x30, 0x12, 0xcc,
+ 0x77, 0x7f, 0x10, 0xa9, 0x7c, 0xe4, 0x08, 0x77, 0x3c, 0xd3, 0x6f, 0xa4,
+ 0xf4, 0xaf, 0xf1, 0x9d, 0x14, 0x1d, 0xd0, 0x02, 0x33, 0x50, 0x55, 0x00,
+ 0x6a, 0x47, 0x96, 0xe1, 0x8b, 0x4e, 0x44, 0x41, 0xad, 0xb3, 0xea, 0x0d,
+ 0x0d, 0xd5, 0x73, 0x8e, 0x62, 0x67, 0x8a, 0xb4, 0xe7, 0x5d, 0x17, 0xa9,
+ 0x24};
+ ASSERT_EQ(Bytes(kExpectedPub, sizeof(kExpectedPub)),
+ Bytes(pub_key, pub_key_len));
}
TEST(TrustTokenTest, KeyGenExp2VOPRF) {
@@ -66,6 +133,37 @@
TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0x0001));
ASSERT_EQ(52u, priv_key_len);
ASSERT_EQ(101u, pub_key_len);
+
+ const uint8_t kKeygenSecret[] = "SEED";
+ ASSERT_TRUE(TRUST_TOKEN_derive_key_from_secret(
+ TRUST_TOKEN_experiment_v2_voprf(), priv_key, &priv_key_len,
+ TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, pub_key, &pub_key_len,
+ TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0x0001, kKeygenSecret,
+ sizeof(kKeygenSecret) - 1));
+
+ const uint8_t kExpectedPriv[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x0b, 0xe2, 0xc4, 0x73, 0x92, 0xe7, 0xf8,
+ 0x3e, 0xba, 0xab, 0x85, 0xa7, 0x77, 0xd7, 0x0a, 0x02, 0xc5, 0x36,
+ 0xfe, 0x62, 0xa3, 0xca, 0x01, 0x75, 0xc7, 0x62, 0x19, 0xc7, 0xf0,
+ 0x30, 0xc5, 0x14, 0x60, 0x13, 0x97, 0x4f, 0x63, 0x05, 0x37, 0x92,
+ 0x7b, 0x76, 0x8e, 0x9f, 0xd0, 0x1a, 0x74, 0x44
+ };
+ ASSERT_EQ(Bytes(kExpectedPriv, sizeof(kExpectedPriv)),
+ Bytes(priv_key, priv_key_len));
+
+ const uint8_t kExpectedPub[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x04, 0x2c, 0x9c, 0x11, 0xc1, 0xe5, 0x52, 0x59,
+ 0x0b, 0x6d, 0x88, 0x8b, 0x6e, 0x28, 0xe8, 0xc5, 0xa3, 0xbe, 0x48, 0x18,
+ 0xf7, 0x1d, 0x31, 0xcf, 0xa2, 0x6e, 0x2a, 0xd6, 0xcb, 0x83, 0x26, 0x04,
+ 0xbd, 0x93, 0x67, 0xe4, 0x53, 0xf6, 0x11, 0x7d, 0x45, 0xe9, 0xfe, 0x27,
+ 0x33, 0x90, 0xdb, 0x1b, 0xfc, 0x9b, 0x31, 0x4d, 0x39, 0x1f, 0x1f, 0x8c,
+ 0x43, 0x06, 0x70, 0x2c, 0x84, 0xdc, 0x23, 0x18, 0xc7, 0x6a, 0x58, 0xcf,
+ 0x9e, 0xc1, 0xfa, 0xf2, 0x30, 0xdd, 0xad, 0x62, 0x24, 0xde, 0x11, 0xc1,
+ 0xba, 0x8d, 0xc3, 0x4f, 0xfb, 0xe5, 0xa5, 0xd4, 0x37, 0xba, 0x3b, 0x70,
+ 0xc0, 0xc3, 0xef, 0x20, 0x43
+ };
+ ASSERT_EQ(Bytes(kExpectedPub, sizeof(kExpectedPub)),
+ Bytes(pub_key, pub_key_len));
}
TEST(TrustTokenTest, KeyGenExp2PMB) {
@@ -78,6 +176,73 @@
TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0x0001));
ASSERT_EQ(292u, priv_key_len);
ASSERT_EQ(295u, pub_key_len);
+
+ const uint8_t kKeygenSecret[] = "SEED";
+ ASSERT_TRUE(TRUST_TOKEN_derive_key_from_secret(
+ TRUST_TOKEN_experiment_v2_pmb(), priv_key, &priv_key_len,
+ TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, pub_key, &pub_key_len,
+ TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0x0001, kKeygenSecret,
+ sizeof(kKeygenSecret) - 1));
+
+ const uint8_t kExpectedPriv[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x1b, 0x74, 0xdc, 0xf0, 0xa9, 0xa7, 0x6c, 0xfb,
+ 0x41, 0xef, 0xfa, 0x65, 0x52, 0xc9, 0x86, 0x4e, 0xfb, 0x16, 0x9d, 0xea,
+ 0x62, 0x3f, 0x47, 0xab, 0x1f, 0x1b, 0x05, 0xf2, 0x4f, 0x05, 0xfe, 0x64,
+ 0xb7, 0xe8, 0xcd, 0x2a, 0x10, 0xfa, 0xa2, 0x48, 0x3f, 0x0e, 0x8b, 0x94,
+ 0x39, 0xf1, 0xe7, 0x53, 0xe9, 0x50, 0x29, 0xe2, 0xb7, 0x0e, 0xc0, 0x94,
+ 0xa9, 0xd3, 0xef, 0x64, 0x10, 0x1d, 0x08, 0xd0, 0x60, 0xcb, 0x6d, 0x97,
+ 0x68, 0xc7, 0x04, 0x92, 0x07, 0xb2, 0x22, 0x83, 0xf7, 0xd9, 0x9b, 0x2c,
+ 0xf2, 0x52, 0x34, 0x0c, 0x42, 0x31, 0x47, 0x41, 0x19, 0xb9, 0xee, 0xfc,
+ 0x46, 0xbd, 0x14, 0xce, 0x42, 0xd7, 0x43, 0xc8, 0x32, 0x3b, 0x24, 0xed,
+ 0xdc, 0x69, 0xa3, 0x8e, 0x29, 0x01, 0xbe, 0xae, 0x24, 0x39, 0x14, 0xa7,
+ 0x52, 0xe5, 0xd5, 0xff, 0x9a, 0xc4, 0x15, 0x79, 0x29, 0x4c, 0x9b, 0x4e,
+ 0xfc, 0x61, 0xf2, 0x12, 0x6f, 0x4f, 0xd3, 0x96, 0x28, 0xb0, 0x79, 0xf0,
+ 0x4e, 0x6e, 0x7d, 0x56, 0x19, 0x1b, 0xc2, 0xd7, 0xf9, 0x3a, 0x58, 0x06,
+ 0xe5, 0xec, 0xa4, 0x33, 0x14, 0x1c, 0x78, 0x0c, 0x83, 0x94, 0x34, 0x22,
+ 0x5a, 0x8e, 0x2e, 0xa1, 0x72, 0x4a, 0x03, 0x35, 0xfe, 0x46, 0x92, 0x41,
+ 0x6b, 0xe6, 0x4b, 0x3f, 0xf0, 0xe7, 0x0b, 0xb5, 0xf3, 0x66, 0x6c, 0xc6,
+ 0x14, 0xcf, 0xce, 0x32, 0x0a, 0x2c, 0x28, 0xba, 0x4e, 0xb9, 0x75, 0x4a,
+ 0xa9, 0x2d, 0xb0, 0x8c, 0xd0, 0x62, 0x52, 0x29, 0x1f, 0x12, 0xfd, 0xfb,
+ 0xd3, 0x2a, 0x36, 0x0f, 0x89, 0x32, 0x86, 0x25, 0x56, 0xb9, 0xe7, 0x3c,
+ 0xeb, 0xb4, 0x84, 0x41, 0x2b, 0xa8, 0xf3, 0xa5, 0x3d, 0xfe, 0x56, 0x94,
+ 0x5b, 0x74, 0xb3, 0x5b, 0x27, 0x3f, 0xe7, 0xcf, 0xe4, 0xf8, 0x15, 0x95,
+ 0x2a, 0xd2, 0x5f, 0x92, 0xb4, 0x6a, 0x89, 0xa5, 0x54, 0xbd, 0x27, 0x5e,
+ 0xeb, 0x43, 0x07, 0x9b, 0x2b, 0x8b, 0x22, 0x59, 0x13, 0x4b, 0x9c, 0x56,
+ 0xd8, 0x63, 0xd9, 0xe6, 0x85, 0x15, 0x2c, 0x82, 0x52, 0x40, 0x8f, 0xb1,
+ 0xe7, 0x56, 0x07, 0x98
+ };
+ ASSERT_EQ(Bytes(kExpectedPriv, sizeof(kExpectedPriv)),
+ Bytes(priv_key, priv_key_len));
+
+ const uint8_t kExpectedPub[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x04, 0x48, 0xb1, 0x2d, 0xdd, 0x03, 0x32, 0xeb,
+ 0x93, 0x31, 0x3d, 0x59, 0x74, 0xf0, 0xcf, 0xaa, 0xa5, 0x39, 0x5f, 0x53,
+ 0xc4, 0x94, 0x98, 0xbe, 0x8f, 0x22, 0xd7, 0x30, 0xde, 0x1e, 0xb4, 0xf3,
+ 0x32, 0x23, 0x90, 0x0b, 0xa6, 0x37, 0x4a, 0x4b, 0x44, 0xb3, 0x26, 0x52,
+ 0x93, 0x7b, 0x4b, 0xa4, 0x79, 0xe8, 0x77, 0x6a, 0x19, 0x81, 0x2a, 0xdd,
+ 0x91, 0xfb, 0x90, 0x8b, 0x24, 0xb5, 0xbe, 0x20, 0x2e, 0xe8, 0xbc, 0xd3,
+ 0x83, 0x6c, 0xa8, 0xc5, 0xa1, 0x9a, 0x5b, 0x5e, 0x60, 0xda, 0x45, 0x2e,
+ 0x31, 0x7f, 0x54, 0x0e, 0x14, 0x40, 0xd2, 0x4d, 0x40, 0x2e, 0x21, 0x79,
+ 0xfc, 0x77, 0xdd, 0xc7, 0x2d, 0x04, 0xfe, 0xc6, 0xe3, 0xcf, 0x99, 0xef,
+ 0x88, 0xab, 0x76, 0x86, 0x16, 0x14, 0xed, 0x72, 0x35, 0xa7, 0x05, 0x13,
+ 0x9f, 0x2c, 0x53, 0xd5, 0xdf, 0x66, 0x75, 0x2e, 0x68, 0xdc, 0xd4, 0xc4,
+ 0x00, 0x36, 0x08, 0x6d, 0xb7, 0x15, 0xf7, 0xe5, 0x32, 0x59, 0x81, 0x16,
+ 0x57, 0xaa, 0x72, 0x06, 0xf0, 0xad, 0xd1, 0x85, 0xa0, 0x04, 0xd4, 0x11,
+ 0x95, 0x1d, 0xac, 0x0b, 0x25, 0xbe, 0x59, 0xa2, 0xb3, 0x30, 0xee, 0x97,
+ 0x07, 0x2a, 0x51, 0x15, 0xc1, 0x8d, 0xa8, 0xa6, 0x57, 0x9a, 0x4e, 0xbf,
+ 0xd7, 0x2d, 0x35, 0x07, 0x6b, 0xd6, 0xc9, 0x3c, 0xe4, 0xcf, 0x0b, 0x14,
+ 0x3e, 0x10, 0x51, 0x77, 0xd6, 0x84, 0x04, 0xbe, 0xd1, 0xd5, 0xa8, 0xf3,
+ 0x9d, 0x1d, 0x4f, 0xc1, 0xc9, 0xf1, 0x0c, 0x6d, 0xb6, 0xcb, 0xe2, 0x05,
+ 0x0b, 0x9c, 0x7a, 0x3a, 0x9a, 0x99, 0xe9, 0xa1, 0x93, 0xdc, 0x72, 0x2e,
+ 0xef, 0xf3, 0x8d, 0xb9, 0x7b, 0xb0, 0x19, 0x24, 0x95, 0x0d, 0x68, 0xa7,
+ 0xe0, 0xaa, 0x0b, 0xb1, 0xd1, 0xcc, 0x52, 0x14, 0xf9, 0x6c, 0x91, 0x59,
+ 0xe4, 0xe1, 0x9b, 0xf9, 0x12, 0x39, 0xb1, 0x79, 0xbb, 0x21, 0x92, 0x00,
+ 0xa4, 0x89, 0xf5, 0xbd, 0xd7, 0x89, 0x27, 0x40, 0xdc, 0xb1, 0x09, 0x38,
+ 0x63, 0x91, 0x8c, 0xa5, 0x27, 0x27, 0x97, 0x39, 0x35, 0xfa, 0x1a, 0x8a,
+ 0xa7, 0xe5, 0xc4, 0xd8, 0xbf, 0xe7, 0xbe
+ };
+ ASSERT_EQ(Bytes(kExpectedPub, sizeof(kExpectedPub)),
+ Bytes(pub_key, pub_key_len));
}
// Test that H in |TRUST_TOKEN_experiment_v1| was computed correctly.
diff --git a/crypto/trust_token/voprf.c b/crypto/trust_token/voprf.c
index f93ee9c..cedee1e 100644
--- a/crypto/trust_token/voprf.c
+++ b/crypto/trust_token/voprf.c
@@ -110,20 +110,18 @@
return 1;
}
-static int voprf_generate_key(const VOPRF_METHOD *method, CBB *out_private,
- CBB *out_public) {
+static int voprf_calculate_key(const VOPRF_METHOD *method, CBB *out_private,
+ CBB *out_public, const EC_SCALAR *priv) {
const EC_GROUP *group = method->group;
EC_RAW_POINT pub;
- EC_SCALAR priv;
EC_AFFINE pub_affine;
- if (!ec_random_nonzero_scalar(group, &priv, kDefaultAdditionalData) ||
- !ec_point_mul_scalar_base(group, &pub, &priv) ||
+ if (!ec_point_mul_scalar_base(group, &pub, priv) ||
!ec_jacobian_to_affine(group, &pub_affine, &pub)) {
OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
return 0;
}
- if (!scalar_to_cbb(out_private, group, &priv) ||
+ if (!scalar_to_cbb(out_private, group, priv) ||
!cbb_add_point(out_public, group, &pub_affine)) {
OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
return 0;
@@ -132,6 +130,46 @@
return 1;
}
+
+static int voprf_generate_key(const VOPRF_METHOD *method, CBB *out_private,
+ CBB *out_public) {
+ EC_SCALAR priv;
+ if (!ec_random_nonzero_scalar(method->group, &priv, kDefaultAdditionalData)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
+ return 0;
+ }
+ return voprf_calculate_key(method, out_private, out_public, &priv);
+}
+
+static int voprf_derive_key_from_secret(const VOPRF_METHOD *method,
+ CBB *out_private, CBB *out_public,
+ const uint8_t *secret,
+ size_t secret_len) {
+ static const uint8_t kKeygenLabel[] = "TrustTokenVOPRFKeyGen";
+
+ EC_SCALAR priv;
+ int ok = 0;
+ CBB cbb;
+ CBB_zero(&cbb);
+ uint8_t *buf = NULL;
+ size_t len;
+ if (!CBB_init(&cbb, 0) ||
+ !CBB_add_bytes(&cbb, kKeygenLabel, sizeof(kKeygenLabel)) ||
+ !CBB_add_bytes(&cbb, secret, secret_len) ||
+ !CBB_finish(&cbb, &buf, &len) ||
+ !method->hash_to_scalar(method->group, &priv, buf, len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
+ goto err;
+ }
+
+ ok = voprf_calculate_key(method, out_private, out_public, &priv);
+
+err:
+ CBB_cleanup(&cbb);
+ OPENSSL_free(buf);
+ return ok;
+}
+
static int voprf_client_key_from_bytes(const VOPRF_METHOD *method,
TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len) {
@@ -711,6 +749,17 @@
return voprf_generate_key(&voprf_exp2_method, out_private, out_public);
}
+int voprf_exp2_derive_key_from_secret(CBB *out_private, CBB *out_public,
+ const uint8_t *secret,
+ size_t secret_len) {
+ if (!voprf_exp2_init_method()) {
+ return 0;
+ }
+
+ return voprf_derive_key_from_secret(&voprf_exp2_method, out_private,
+ out_public, secret, secret_len);
+}
+
int voprf_exp2_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len) {
if (!voprf_exp2_init_method()) {
diff --git a/include/openssl/trust_token.h b/include/openssl/trust_token.h
index d9247f7..745a860 100644
--- a/include/openssl/trust_token.h
+++ b/include/openssl/trust_token.h
@@ -78,15 +78,30 @@
// 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(
const TRUST_TOKEN_METHOD *method, 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);
+// TRUST_TOKEN_derive_key_from_secret deterministically derives a new Trust
+// Token keypair labeled with |id| from an input |secret| 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|.
+//
+// This function returns one on success or zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_derive_key_from_secret(
+ const TRUST_TOKEN_METHOD *method, 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,
+ const uint8_t *secret, size_t secret_len);
+
// Trust Token client implementation.
//