diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h index 0aa19363f..31ecc49b5 100644 --- a/crypto/trust_token/internal.h +++ b/crypto/trust_token/internal.h @@ -93,6 +93,9 @@ DEFINE_STACK_OF(TRUST_TOKEN_PRETOKEN) // 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 @@ OPENSSL_EXPORT int pmbtoken_exp1_get_h_for_testing(uint8_t out[97]); // 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 @@ OPENSSL_EXPORT int pmbtoken_exp2_get_h_for_testing(uint8_t out[97]); // 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 @@ struct trust_token_method_st { // 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 a6549b9c5..68d8909b9 100644 --- a/crypto/trust_token/pmbtoken.c +++ b/crypto/trust_token/pmbtoken.c @@ -37,6 +37,8 @@ typedef int (*hash_s_func_t)(const EC_GROUP *group, EC_RAW_POINT *out, 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 @@ typedef struct { // 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 const uint8_t kDefaultAdditionalData[32] = {0}; 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 @@ static int pmbtoken_init_method(PMBTOKEN_METHOD *method, int curve_nid, 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 @@ static int pmbtoken_init_method(PMBTOKEN_METHOD *method, int curve_nid, 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 @@ static int mul_public_3(const EC_GROUP *group, EC_RAW_POINT *out, 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 @@ static int pmbtoken_generate_key(const PMBTOKEN_METHOD *method, 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 @@ static int pmbtoken_exp1_hash_c(const EC_GROUP *group, EC_SCALAR *out, 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 @@ static void pmbtoken_exp1_init_method_impl(void) { 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 @@ int pmbtoken_exp1_generate_key(CBB *out_private, CBB *out_public) { 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 @@ static int pmbtoken_exp2_hash_c(const EC_GROUP *group, EC_SCALAR *out, 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 @@ static void pmbtoken_exp2_init_method_impl(void) { 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 @@ int pmbtoken_exp2_generate_key(CBB *out_private, CBB *out_public) { 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 3334fba87..5afb487c0 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_v1(void) { 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_voprf(void) { 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 @@ err: 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 72d555b95..5ab80cddc 100644 --- a/crypto/trust_token/trust_token_test.cc +++ b/crypto/trust_token/trust_token_test.cc @@ -54,6 +54,73 @@ TEST(TrustTokenTest, KeyGenExp1) { 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 @@ TEST(TrustTokenTest, KeyGenExp2VOPRF) { 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 @@ TEST(TrustTokenTest, KeyGenExp2PMB) { 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 f93ee9cd1..cedee1e80 100644 --- a/crypto/trust_token/voprf.c +++ b/crypto/trust_token/voprf.c @@ -110,20 +110,18 @@ static int scalar_from_cbs(CBS *cbs, const EC_GROUP *group, EC_SCALAR *out) { 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 @@ static int voprf_generate_key(const VOPRF_METHOD *method, CBB *out_private, 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 @@ int voprf_exp2_generate_key(CBB *out_private, CBB *out_public) { 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 d9247f79f..745a860b0 100644 --- a/include/openssl/trust_token.h +++ b/include/openssl/trust_token.h @@ -78,15 +78,30 @@ OPENSSL_EXPORT void TRUST_TOKEN_free(TRUST_TOKEN *token); // 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. //