Introduce TRUST_TOKENS_experiment_v1. This starts a new branch of the protocol using P-384 and draft-07 of hash-to-curve. Mark it unstable for now as we aim to add batching and other fixes. Did 154 TrustToken-Exp0-Batch1 generate_key operations in 1033312us (149.0 ops/sec) Did 748 TrustToken-Exp0-Batch1 begin_issuance operations in 1078707us (693.4 ops/sec) Did 48 TrustToken-Exp0-Batch1 issue operations in 1030056us (46.6 ops/sec) Did 44 TrustToken-Exp0-Batch1 finish_issuance operations in 1092816us (40.3 ops/sec) Did 6040750 TrustToken-Exp0-Batch1 begin_redemption operations in 1000009us (6040695.6 ops/sec) Did 154 TrustToken-Exp0-Batch1 redeem operations in 1023722us (150.4 ops/sec) Did 18000 TrustToken-Exp0-Batch1 finish_redemption operations in 1021087us (17628.3 ops/sec) Did 154 TrustToken-Exp0-Batch10 generate_key operations in 1032878us (149.1 ops/sec) Did 72 TrustToken-Exp0-Batch10 begin_issuance operations in 1050908us (68.5 ops/sec) Did 4 TrustToken-Exp0-Batch10 issue operations in 1051989us (3.8 ops/sec) Did 4 TrustToken-Exp0-Batch10 finish_issuance operations in 1214996us (3.3 ops/sec) Did 5987000 TrustToken-Exp0-Batch10 begin_redemption operations in 1000068us (5986592.9 ops/sec) Did 154 TrustToken-Exp0-Batch10 redeem operations in 1037898us (148.4 ops/sec) Did 18000 TrustToken-Exp0-Batch10 finish_redemption operations in 1024788us (17564.6 ops/sec) Did 396 TrustToken-Exp1-Batch1 generate_key operations in 1060955us (373.2 ops/sec) Did 1925 TrustToken-Exp1-Batch1 begin_issuance operations in 1093039us (1761.1 ops/sec) Did 121 TrustToken-Exp1-Batch1 issue operations in 1022292us (118.4 ops/sec) Did 110 TrustToken-Exp1-Batch1 finish_issuance operations in 1076011us (102.2 ops/sec) Did 6322000 TrustToken-Exp1-Batch1 begin_redemption operations in 1000144us (6321089.8 ops/sec) Did 407 TrustToken-Exp1-Batch1 redeem operations in 1087774us (374.2 ops/sec) Did 18000 TrustToken-Exp1-Batch1 finish_redemption operations in 1031736us (17446.3 ops/sec) Did 407 TrustToken-Exp1-Batch10 generate_key operations in 1090415us (373.3 ops/sec) Did 187 TrustToken-Exp1-Batch10 begin_issuance operations in 1065754us (175.5 ops/sec) Did 11 TrustToken-Exp1-Batch10 issue operations in 1005738us (10.9 ops/sec) Did 10 TrustToken-Exp1-Batch10 finish_issuance operations in 1066494us (9.4 ops/sec) Did 6302000 TrustToken-Exp1-Batch10 begin_redemption operations in 1000029us (6301817.2 ops/sec) Did 407 TrustToken-Exp1-Batch10 redeem operations in 1087692us (374.2 ops/sec) Did 18000 TrustToken-Exp1-Batch10 finish_redemption operations in 1014611us (17740.8 ops/sec) Change-Id: I7ea9a8ab8ad48acfbf50026e251cc6e1d5b8ba7f Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41069 Reviewed-by: Steven Valdez <svaldez@google.com> Commit-Queue: Steven Valdez <svaldez@google.com>
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h index 47ccdbd..6333d43 100644 --- a/crypto/fipsmodule/ec/internal.h +++ b/crypto/fipsmodule/ec/internal.h
@@ -71,6 +71,7 @@ #include <openssl/base.h> #include <openssl/bn.h> +#include <openssl/ec.h> #include <openssl/ex_data.h> #include <openssl/type_check.h> @@ -304,9 +305,10 @@ // ec_point_to_bytes behaves like |EC_POINT_point2oct| but takes an // |EC_RAW_POINT|. -size_t ec_point_to_bytes(const EC_GROUP *group, const EC_RAW_POINT *point, - point_conversion_form_t form, uint8_t *buf, - size_t len); +OPENSSL_EXPORT size_t ec_point_to_bytes(const EC_GROUP *group, + const EC_RAW_POINT *point, + point_conversion_form_t form, + uint8_t *buf, size_t len); // ec_point_from_uncompressed parses |in| as a point in uncompressed form and // sets the result to |out|. It returns one on success and zero if the input was
diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h index f542c9a..5f65ced 100644 --- a/crypto/trust_token/internal.h +++ b/crypto/trust_token/internal.h
@@ -35,7 +35,7 @@ // PMBTokens is described in https://eprint.iacr.org/2020/072/20200324:214215 // and provides anonymous tokens with private metadata. We implement the // construction with validity verification, described in appendix H, -// construction 6, using P-521 as the group. +// construction 6. // PMBTOKEN_NONCE_SIZE is the size of nonces used as part of the PMBToken // protocol. @@ -73,24 +73,58 @@ DEFINE_STACK_OF(PMBTOKEN_PRETOKEN) // The following functions implement the corresponding |TRUST_TOKENS_METHOD| -// functions for |TRUST_TOKENS_experiment_v0|'s PMBTokens construction. -int pmbtoken_generate_key(CBB *out_private, CBB *out_public); -int pmbtoken_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, const uint8_t *in, - size_t len); -int pmbtoken_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, const uint8_t *in, - size_t len); -STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_blind(CBB *cbb, size_t count); -int pmbtoken_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, - size_t num_requested, size_t num_to_issue, - uint8_t private_metadata); +// functions for |TRUST_TOKENS_experiment_v0|'s PMBTokens construction which +// uses P-521. +int pmbtoken_exp0_generate_key(CBB *out_private, CBB *out_public); +int pmbtoken_exp0_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len); +int pmbtoken_exp0_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len); +STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp0_blind(CBB *cbb, size_t count); +int pmbtoken_exp0_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata); STACK_OF(TRUST_TOKEN) * - pmbtoken_unblind(const PMBTOKEN_CLIENT_KEY *key, - const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, CBS *cbs, - size_t count, uint32_t key_id); -int pmbtoken_read(const PMBTOKEN_ISSUER_KEY *key, - uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], - uint8_t *out_private_metadata, const uint8_t *token, - size_t token_len); + pmbtoken_exp0_unblind(const PMBTOKEN_CLIENT_KEY *key, + const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, + CBS *cbs, size_t count, uint32_t key_id); +int pmbtoken_exp0_read(const PMBTOKEN_ISSUER_KEY *key, + uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len); + +// The following functions implement the corresponding |TRUST_TOKENS_METHOD| +// functions for |TRUST_TOKENS_experiment_v1|'s PMBTokens construction which +// uses P-384. +// +// We use P-384 instead of our usual choice of P-256. See Appendix I which +// describes two attacks which may affect smaller curves. In particular, p-1 for +// P-256 is smooth, giving a low complexity for the p-1 attack. P-384's p-1 has +// a 281-bit prime factor, +// 3055465788140352002733946906144561090641249606160407884365391979704929268480326390471. +// This lower-bounds the p-1 attack at O(2^140). The p+1 attack is lower-bounded +// by O(p^(1/3)) or O(2^128), so we do not need to check the smoothness of p+1. +int pmbtoken_exp1_generate_key(CBB *out_private, CBB *out_public); +int pmbtoken_exp1_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len); +int pmbtoken_exp1_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len); +STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp1_blind(CBB *cbb, size_t count); +int pmbtoken_exp1_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata); +STACK_OF(TRUST_TOKEN) * + pmbtoken_exp1_unblind(const PMBTOKEN_CLIENT_KEY *key, + const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, + CBS *cbs, size_t count, uint32_t key_id); +int pmbtoken_exp1_read(const PMBTOKEN_ISSUER_KEY *key, + uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len); + +// pmbtoken_exp1_get_h_for_testing returns H in uncompressed coordinates. This +// function is used to confirm H was computed as expected. +OPENSSL_EXPORT int pmbtoken_exp1_get_h_for_testing(uint8_t out[97]); // Trust Tokens internals.
diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c index f86b644..3117b65 100644 --- a/crypto/trust_token/pmbtoken.c +++ b/crypto/trust_token/pmbtoken.c
@@ -30,17 +30,815 @@ #include "internal.h" -// get_h returns the generator H for PMBTokens. -// -// x: 66591746412783875033873351891229753622964683369847172829242944646280287810 -// 81195403447871073952234683395256591180452378091073292247502091640572714366 -// 588045092 -// y: 12347430519393087872533727997980072129796839266949808299436682045034861065 -// 18810630511924722292325611253427311923464047364545304196431830383014967865 -// 162306253 -// -// This point was generated with the following Python code. +typedef struct { + const EC_GROUP *group; + EC_RAW_POINT h; + // hash_t implements the H_t operation in PMBTokens. It returns on on success + // and zero on error. + int (*hash_t)(const EC_GROUP *group, EC_RAW_POINT *out, + const uint8_t t[PMBTOKEN_NONCE_SIZE]); + // hash_s implements the H_s operation in PMBTokens. It returns on on success + // and zero on error. + int (*hash_s)(const EC_GROUP *group, EC_RAW_POINT *out, const EC_RAW_POINT *t, + const uint8_t s[PMBTOKEN_NONCE_SIZE]); + // hash_c implements the H_c operation in PMBTokens. It returns on on success + // and zero on error. + int (*hash_c)(const EC_GROUP *group, EC_SCALAR *out, uint8_t *buf, + size_t len); +} PMBTOKEN_METHOD; +static const uint8_t kDefaultAdditionalData[32] = {0}; + +static int mul_twice(const EC_GROUP *group, EC_RAW_POINT *out, + const EC_RAW_POINT *g, const EC_SCALAR *g_scalar, + const EC_RAW_POINT *p, const EC_SCALAR *p_scalar) { + EC_RAW_POINT tmp1, tmp2; + if (!ec_point_mul_scalar(group, &tmp1, g, g_scalar) || + !ec_point_mul_scalar(group, &tmp2, p, p_scalar)) { + return 0; + } + + group->meth->add(group, out, &tmp1, &tmp2); + return 1; +} + +static int mul_twice_base(const EC_GROUP *group, EC_RAW_POINT *out, + const EC_SCALAR *base_scalar, const EC_RAW_POINT *p, + const EC_SCALAR *p_scalar) { + EC_RAW_POINT tmp1, tmp2; + if (!ec_point_mul_scalar_base(group, &tmp1, base_scalar) || + !ec_point_mul_scalar(group, &tmp2, p, p_scalar)) { + return 0; + } + + group->meth->add(group, out, &tmp1, &tmp2); + return 1; +} + +// (v0;v1) = p_scalar*(G;p1) + q_scalar*(q0;q1) - r_scalar*(r0;r1) +static int mul_add_and_sub(const EC_GROUP *group, EC_RAW_POINT *out_v0, + EC_RAW_POINT *out_v1, const EC_RAW_POINT *p1, + const EC_SCALAR *p_scalar, const EC_RAW_POINT *q0, + const EC_RAW_POINT *q1, const EC_SCALAR *q_scalar, + const EC_RAW_POINT *r0, const EC_RAW_POINT *r1, + const EC_SCALAR *r_scalar) { + EC_RAW_POINT tmp0, tmp1, v0, v1; + if (!mul_twice_base(group, &v0, p_scalar, q0, q_scalar) || + !mul_twice(group, &v1, p1, p_scalar, q1, q_scalar) || + !ec_point_mul_scalar(group, &tmp0, r0, r_scalar) || + !ec_point_mul_scalar(group, &tmp1, r1, r_scalar)) { + return 0; + } + ec_GFp_simple_invert(group, &tmp0); + ec_GFp_simple_invert(group, &tmp1); + group->meth->add(group, out_v0, &v0, &tmp0); + group->meth->add(group, out_v1, &v1, &tmp1); + 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) || + !mul_twice_base(method->group, out_pub, out_x, &method->h, out_y)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return 0; + } + return 1; +} + +static int point_to_cbb(CBB *out, const EC_GROUP *group, + const EC_RAW_POINT *point) { + size_t len = + ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0); + if (len == 0) { + return 0; + } + uint8_t *p; + return CBB_add_space(out, &p, len) && + ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, p, + len) == len; +} + +static int cbs_get_prefixed_point(CBS *cbs, const EC_GROUP *group, + EC_RAW_POINT *out) { + CBS child; + return CBS_get_u16_length_prefixed(cbs, &child) && + ec_point_from_uncompressed(group, out, CBS_data(&child), + CBS_len(&child)); +} + +void PMBTOKEN_PRETOKEN_free(PMBTOKEN_PRETOKEN *pretoken) { + OPENSSL_free(pretoken); +} + +static int pmbtoken_generate_key(const PMBTOKEN_METHOD *method, + CBB *out_private, CBB *out_public) { + const EC_GROUP *group = method->group; + EC_RAW_POINT pub0, pub1, pubs; + EC_SCALAR x0, y0, x1, y1, xs, ys; + if (!generate_keypair(method, &x0, &y0, &pub0) || + !generate_keypair(method, &x1, &y1, &pub1) || + !generate_keypair(method, &xs, &ys, &pubs)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE); + return 0; + } + + 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; + if (!CBB_add_space(out_private, &buf, scalar_len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL); + return 0; + } + ec_scalar_to_bytes(group, buf, &scalar_len, scalars[i]); + } + + // TODO(https://crbug.com/boringssl/331): When updating the key format, remove + // the redundant length prefixes. + CBB child; + if (!CBB_add_u16_length_prefixed(out_public, &child) || + !point_to_cbb(&child, group, &pub0) || + !CBB_add_u16_length_prefixed(out_public, &child) || + !point_to_cbb(&child, group, &pub1) || + !CBB_add_u16_length_prefixed(out_public, &child) || + !point_to_cbb(&child, group, &pubs) || + !CBB_flush(out_public)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL); + return 0; + } + + return 1; +} + +static int pmbtoken_client_key_from_bytes(const PMBTOKEN_METHOD *method, + PMBTOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len) { + // TODO(https://crbug.com/boringssl/331): When updating the key format, remove + // the redundant length prefixes. + CBS cbs; + CBS_init(&cbs, in, len); + if (!cbs_get_prefixed_point(&cbs, method->group, &key->pub0) || + !cbs_get_prefixed_point(&cbs, method->group, &key->pub1) || + !cbs_get_prefixed_point(&cbs, method->group, &key->pubs) || + CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + return 1; +} + +static int pmbtoken_issuer_key_from_bytes(const PMBTOKEN_METHOD *method, + PMBTOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len) { + const EC_GROUP *group = method->group; + CBS cbs, tmp; + CBS_init(&cbs, in, len); + size_t scalar_len = BN_num_bytes(&group->order); + EC_SCALAR *scalars[] = {&key->x0, &key->y0, &key->x1, + &key->y1, &key->xs, &key->ys}; + for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(scalars); i++) { + if (!CBS_get_bytes(&cbs, &tmp, scalar_len) || + !ec_scalar_from_bytes(group, scalars[i], CBS_data(&tmp), + CBS_len(&tmp))) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + } + + // Recompute the public key. + if (!mul_twice_base(group, &key->pubs, &key->xs, &method->h, &key->ys) || + !mul_twice_base(group, &key->pub0, &key->x0, &method->h, &key->y0) || + !mul_twice_base(group, &key->pub1, &key->x1, &method->h, &key->y1)) { + return 0; + } + + return 1; +} + +static STACK_OF(PMBTOKEN_PRETOKEN) * + pmbtoken_blind(const PMBTOKEN_METHOD *method, CBB *cbb, size_t count) { + const EC_GROUP *group = method->group; + STACK_OF(PMBTOKEN_PRETOKEN) *pretokens = sk_PMBTOKEN_PRETOKEN_new_null(); + if (pretokens == NULL) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + for (size_t i = 0; i < count; i++) { + // Insert |pretoken| into |pretokens| early to simplify error-handling. + PMBTOKEN_PRETOKEN *pretoken = OPENSSL_malloc(sizeof(PMBTOKEN_PRETOKEN)); + if (pretoken == NULL || + !sk_PMBTOKEN_PRETOKEN_push(pretokens, pretoken)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + PMBTOKEN_PRETOKEN_free(pretoken); + goto err; + } + + RAND_bytes(pretoken->t, sizeof(pretoken->t)); + + // We sample |pretoken->r| in Montgomery form to simplify inverting. + if (!ec_random_nonzero_scalar(group, &pretoken->r, + kDefaultAdditionalData)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + EC_SCALAR rinv; + ec_scalar_inv0_montgomery(group, &rinv, &pretoken->r); + // Convert both out of Montgomery form. + ec_scalar_from_montgomery(group, &pretoken->r, &pretoken->r); + ec_scalar_from_montgomery(group, &rinv, &rinv); + + EC_RAW_POINT T; + if (!method->hash_t(group, &T, pretoken->t) || + !ec_point_mul_scalar(group, &pretoken->Tp, &T, &rinv)) { + goto err; + } + + // TODO(https://crbug.com/boringssl/331): When updating the key format, + // remove the redundant length prefixes. + CBB child; + if (!CBB_add_u16_length_prefixed(cbb, &child) || + !point_to_cbb(&child, group, &pretoken->Tp) || + !CBB_flush(cbb)) { + goto err; + } + } + + return pretokens; + +err: + sk_PMBTOKEN_PRETOKEN_pop_free(pretokens, PMBTOKEN_PRETOKEN_free); + return NULL; +} + +static int scalar_to_cbb(CBB *out, const EC_GROUP *group, + const EC_SCALAR *scalar) { + uint8_t *buf; + size_t scalar_len = BN_num_bytes(&group->order); + if (!CBB_add_space(out, &buf, scalar_len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return 0; + } + ec_scalar_to_bytes(group, buf, &scalar_len, scalar); + return 1; +} + +static int scalar_from_cbs(CBS *cbs, const EC_GROUP *group, EC_SCALAR *out) { + size_t scalar_len = BN_num_bytes(&group->order); + CBS tmp; + if (!CBS_get_bytes(cbs, &tmp, scalar_len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + ec_scalar_from_bytes(group, out, CBS_data(&tmp), CBS_len(&tmp)); + return 1; +} + +static int hash_c_dleq(const PMBTOKEN_METHOD *method, EC_SCALAR *out, + const EC_RAW_POINT *X, const EC_RAW_POINT *T, + const EC_RAW_POINT *S, const EC_RAW_POINT *W, + const EC_RAW_POINT *K0, const EC_RAW_POINT *K1) { + static const uint8_t kDLEQ2Label[] = "DLEQ2"; + + int ok = 0; + CBB cbb; + CBB_zero(&cbb); + uint8_t *buf = NULL; + size_t len; + if (!CBB_init(&cbb, 0) || + !CBB_add_bytes(&cbb, kDLEQ2Label, sizeof(kDLEQ2Label)) || + !point_to_cbb(&cbb, method->group, X) || + !point_to_cbb(&cbb, method->group, T) || + !point_to_cbb(&cbb, method->group, S) || + !point_to_cbb(&cbb, method->group, W) || + !point_to_cbb(&cbb, method->group, K0) || + !point_to_cbb(&cbb, method->group, K1) || + !CBB_finish(&cbb, &buf, &len) || + !method->hash_c(method->group, out, buf, len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + ok = 1; + +err: + CBB_cleanup(&cbb); + OPENSSL_free(buf); + return ok; +} + +static int hash_c_dleqor(const PMBTOKEN_METHOD *method, EC_SCALAR *out, + const EC_RAW_POINT *X0, const EC_RAW_POINT *X1, + const EC_RAW_POINT *T, const EC_RAW_POINT *S, + const EC_RAW_POINT *W, const EC_RAW_POINT *K00, + const EC_RAW_POINT *K01, const EC_RAW_POINT *K10, + const EC_RAW_POINT *K11) { + static const uint8_t kDLEQOR2Label[] = "DLEQOR2"; + + int ok = 0; + CBB cbb; + CBB_zero(&cbb); + uint8_t *buf = NULL; + size_t len; + if (!CBB_init(&cbb, 0) || + !CBB_add_bytes(&cbb, kDLEQOR2Label, sizeof(kDLEQOR2Label)) || + !point_to_cbb(&cbb, method->group, X0) || + !point_to_cbb(&cbb, method->group, X1) || + !point_to_cbb(&cbb, method->group, T) || + !point_to_cbb(&cbb, method->group, S) || + !point_to_cbb(&cbb, method->group, W) || + !point_to_cbb(&cbb, method->group, K00) || + !point_to_cbb(&cbb, method->group, K01) || + !point_to_cbb(&cbb, method->group, K10) || + !point_to_cbb(&cbb, method->group, K11) || + !CBB_finish(&cbb, &buf, &len) || + !method->hash_c(method->group, out, buf, len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + ok = 1; + +err: + CBB_cleanup(&cbb); + OPENSSL_free(buf); + return ok; +} + +// The DLEQ2 and DLEQOR2 constructions are described in appendix B of +// https://eprint.iacr.org/2020/072/20200324:214215. DLEQ2 is an instance of +// DLEQOR2 with only one value (n=1). + +static int dleq_generate(const PMBTOKEN_METHOD *method, CBB *cbb, + const PMBTOKEN_ISSUER_KEY *priv, const EC_RAW_POINT *T, + const EC_RAW_POINT *S, const EC_RAW_POINT *W, + const EC_RAW_POINT *Ws, uint8_t private_metadata) { + const EC_GROUP *group = method->group; + // Generate DLEQ2 proof for the validity token. + + // ks0, ks1 <- Zp + EC_SCALAR ks0, ks1; + if (!ec_random_nonzero_scalar(group, &ks0, kDefaultAdditionalData) || + !ec_random_nonzero_scalar(group, &ks1, kDefaultAdditionalData)) { + return 0; + } + + // Ks = ks0*(G;T) + ks1*(H;S) + EC_RAW_POINT Ks0, Ks1; + if (!mul_twice_base(group, &Ks0, &ks0, &method->h, &ks1) || + !mul_twice(group, &Ks1, T, &ks0, S, &ks1)) { + return 0; + } + + // cs = Hc(...) + EC_SCALAR cs; + if (!hash_c_dleq(method, &cs, &priv->pubs, T, S, Ws, &Ks0, &Ks1)) { + return 0; + } + + EC_SCALAR cs_mont; + ec_scalar_to_montgomery(group, &cs_mont, &cs); + + // In each of these products, only one operand is in Montgomery form, so the + // product does not need to be converted. + + // us = ks0 + cs*xs + EC_SCALAR us; + ec_scalar_mul_montgomery(group, &us, &priv->xs, &cs_mont); + ec_scalar_add(group, &us, &ks0, &us); + + // vs = ks1 + cs*ys + EC_SCALAR vs; + ec_scalar_mul_montgomery(group, &vs, &priv->ys, &cs_mont); + ec_scalar_add(group, &vs, &ks1, &vs); + + // Store DLEQ2 proof in transcript. + if (!scalar_to_cbb(cbb, group, &cs) || + !scalar_to_cbb(cbb, group, &us) || + !scalar_to_cbb(cbb, group, &vs)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return 0; + } + + // Generate DLEQOR2 proof for the private metadata token. + BN_ULONG mask = ((BN_ULONG)0) - (private_metadata&1); + + // Select values of xb, yb (keys corresponding to the private metadata value) + // and pubo (public key corresponding to the other value) in constant time. + EC_RAW_POINT pubo; + EC_SCALAR xb, yb; + ec_scalar_select(group, &xb, mask, &priv->x1, &priv->x0); + ec_scalar_select(group, &yb, mask, &priv->y1, &priv->y0); + ec_point_select(group, &pubo, mask, &priv->pub0, &priv->pub1); + + // k0, k1 <- Zp + EC_SCALAR k0, k1; + if (!ec_random_nonzero_scalar(group, &k0, kDefaultAdditionalData) || + !ec_random_nonzero_scalar(group, &k1, kDefaultAdditionalData)) { + return 0; + } + + // Kb = k0*(G;T) + k1*(H;S) + EC_RAW_POINT Kb0, Kb1; + if (!mul_twice_base(group, &Kb0, &k0, &method->h, &k1) || + !mul_twice(group, &Kb1, T, &k0, S, &k1)) { + return 0; + } + + // co, uo, vo <- Zp + EC_SCALAR co, uo, vo; + if (!ec_random_nonzero_scalar(group, &co, kDefaultAdditionalData) || + !ec_random_nonzero_scalar(group, &uo, kDefaultAdditionalData) || + !ec_random_nonzero_scalar(group, &vo, kDefaultAdditionalData)) { + return 0; + } + + // Ko = uo*(G;T) + vo*(H;S) - co*(pubo;W) + EC_RAW_POINT Ko0, Ko1; + if (!mul_add_and_sub(group, &Ko0, &Ko1, T, &uo, &method->h, S, &vo, &pubo, W, + &co)) { + return 0; + } + + // Select the K corresponding to K0 and K1 in constant-time. + EC_RAW_POINT K00, K01, K10, K11; + ec_point_select(group, &K00, mask, &Ko0, &Kb0); + ec_point_select(group, &K01, mask, &Ko1, &Kb1); + ec_point_select(group, &K10, mask, &Kb0, &Ko0); + ec_point_select(group, &K11, mask, &Kb1, &Ko1); + + // c = Hc(...) + EC_SCALAR c; + if (!hash_c_dleqor(method, &c, &priv->pub0, &priv->pub1, T, S, W, &K00, &K01, + &K10, &K11)) { + return 0; + } + + // cb = c - co + EC_SCALAR cb, ub, vb; + ec_scalar_sub(group, &cb, &c, &co); + + EC_SCALAR cb_mont; + ec_scalar_to_montgomery(group, &cb_mont, &cb); + + // In each of these products, only one operand is in Montgomery form, so the + // product does not need to be converted. + + // ub = k0 + cb*xb + ec_scalar_mul_montgomery(group, &ub, &xb, &cb_mont); + ec_scalar_add(group, &ub, &k0, &ub); + + // vb = k1 + cb*yb + ec_scalar_mul_montgomery(group, &vb, &yb, &cb_mont); + ec_scalar_add(group, &vb, &k1, &vb); + + // Select c, u, v in constant-time. + EC_SCALAR c0, c1, u0, u1, v0, v1; + ec_scalar_select(group, &c0, mask, &co, &cb); + ec_scalar_select(group, &u0, mask, &uo, &ub); + ec_scalar_select(group, &v0, mask, &vo, &vb); + ec_scalar_select(group, &c1, mask, &cb, &co); + ec_scalar_select(group, &u1, mask, &ub, &uo); + ec_scalar_select(group, &v1, mask, &vb, &vo); + + // Store DLEQOR2 proof in transcript. + if (!scalar_to_cbb(cbb, group, &c0) || + !scalar_to_cbb(cbb, group, &c1) || + !scalar_to_cbb(cbb, group, &u0) || + !scalar_to_cbb(cbb, group, &u1) || + !scalar_to_cbb(cbb, group, &v0) || + !scalar_to_cbb(cbb, group, &v1)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return 0; + } + + return 1; +} + +static int dleq_verify(const PMBTOKEN_METHOD *method, CBS *cbs, + const PMBTOKEN_CLIENT_KEY *pub, const EC_RAW_POINT *T, + const EC_RAW_POINT *S, const EC_RAW_POINT *W, + const EC_RAW_POINT *Ws) { + const EC_GROUP *group = method->group; + // Verify the DLEQ2 proof over the validity token. + EC_SCALAR cs, us, vs; + if (!scalar_from_cbs(cbs, group, &cs) || + !scalar_from_cbs(cbs, group, &us) || + !scalar_from_cbs(cbs, group, &vs)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + // Ks = us*(G;T) + vs*(H;S) - cs*(pubs;Ws) + EC_RAW_POINT Ks0, Ks1; + if (!mul_add_and_sub(group, &Ks0, &Ks1, T, &us, &method->h, S, &vs, + &pub->pubs, Ws, &cs)) { + return 0; + } + + // calculated = Hc(...) + EC_SCALAR calculated; + if (!hash_c_dleq(method, &calculated, &pub->pubs, T, S, Ws, &Ks0, &Ks1)) { + return 0; + } + + // cs == calculated + if (!ec_scalar_equal_vartime(group, &cs, &calculated)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_PROOF); + return 0; + } + + // Verify the DLEQOR2 proof over the private metadata token. + + EC_SCALAR c0, c1, u0, u1, v0, v1; + if (!scalar_from_cbs(cbs, group, &c0) || + !scalar_from_cbs(cbs, group, &c1) || + !scalar_from_cbs(cbs, group, &u0) || + !scalar_from_cbs(cbs, group, &u1) || + !scalar_from_cbs(cbs, group, &v0) || + !scalar_from_cbs(cbs, group, &v1)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + // K0 = u0*(G;T) + v0*(H;S) - c0*(pub0;W) + EC_RAW_POINT K00, K01; + if (!mul_add_and_sub(group, &K00, &K01, T, &u0, &method->h, S, &v0, + &pub->pub0, W, &c0)) { + return 0; + } + + // K1 = u1*(G;T) + v1*(H;S) - c1*(pub1;Ws) + EC_RAW_POINT K10, K11; + if (!mul_add_and_sub(group, &K10, &K11, T, &u1, &method->h, S, &v1, + &pub->pub1, W, &c1)) { + return 0; + } + + // calculated = Hc(...) + if (!hash_c_dleqor(method, &calculated, &pub->pub0, &pub->pub1, T, S, W, &K00, + &K01, &K10, &K11)) { + return 0; + } + + // c = c0 + c1 + EC_SCALAR c; + ec_scalar_add(group, &c, &c0, &c1); + + // c == calculated + if (!ec_scalar_equal_vartime(group, &c, &calculated)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_PROOF); + return 0; + } + + return 1; +} + +static int pmbtoken_sign(const PMBTOKEN_METHOD *method, + const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata) { + const EC_GROUP *group = method->group; + if (num_requested < num_to_issue) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR); + return 0; + } + + for (size_t i = 0; i < num_to_issue; i++) { + EC_RAW_POINT Tp; + if (!cbs_get_prefixed_point(cbs, group, &Tp)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + EC_SCALAR xb, yb; + BN_ULONG mask = ((BN_ULONG)0) - (private_metadata & 1); + ec_scalar_select(group, &xb, mask, &key->x1, &key->x0); + ec_scalar_select(group, &yb, mask, &key->y1, &key->y0); + + uint8_t s[PMBTOKEN_NONCE_SIZE]; + RAND_bytes(s, PMBTOKEN_NONCE_SIZE); + EC_RAW_POINT Sp, Wp, Wsp; + CBB child; + if (!method->hash_s(group, &Sp, &Tp, s) || + !mul_twice(group, &Wp, &Tp, &xb, &Sp, &yb) || + !mul_twice(group, &Wsp, &Tp, &key->xs, &Sp, &key->ys) || + !CBB_add_bytes(cbb, s, PMBTOKEN_NONCE_SIZE) || + // TODO(https://crbug.com/boringssl/331): When updating the key format, + // remove the redundant length prefixes. + !CBB_add_u16_length_prefixed(cbb, &child) || + !point_to_cbb(&child, group, &Wp) || + !CBB_add_u16_length_prefixed(cbb, &child) || + !point_to_cbb(&child, group, &Wsp) || + !CBB_add_u16_length_prefixed(cbb, &child) || + !dleq_generate(method, &child, key, &Tp, &Sp, &Wp, &Wsp, + private_metadata) || + !CBB_flush(cbb)) { + return 0; + } + } + + // Skip over any unused requests. + size_t point_len = 1 + 2 * BN_num_bytes(&group->field); + if (!CBS_skip(cbs, (2 + point_len) * (num_requested - num_to_issue))) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + return 1; +} + +static STACK_OF(TRUST_TOKEN) * + pmbtoken_unblind(const PMBTOKEN_METHOD *method, + const PMBTOKEN_CLIENT_KEY *key, + const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, CBS *cbs, + size_t count, uint32_t key_id) { + const EC_GROUP *group = method->group; + if (count > sk_PMBTOKEN_PRETOKEN_num(pretokens)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return NULL; + } + + STACK_OF(TRUST_TOKEN) *ret = sk_TRUST_TOKEN_new_null(); + if (ret == NULL) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (size_t i = 0; i < count; i++) { + const PMBTOKEN_PRETOKEN *pretoken = + sk_PMBTOKEN_PRETOKEN_value(pretokens, i); + + uint8_t s[PMBTOKEN_NONCE_SIZE]; + EC_RAW_POINT Wp, Wsp; + CBS proof; + if (!CBS_copy_bytes(cbs, s, PMBTOKEN_NONCE_SIZE) || + !cbs_get_prefixed_point(cbs, group, &Wp) || + !cbs_get_prefixed_point(cbs, group, &Wsp) || + !CBS_get_u16_length_prefixed(cbs, &proof)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + goto err; + } + + EC_RAW_POINT Sp; + if (!method->hash_s(group, &Sp, &pretoken->Tp, s) || + !dleq_verify(method, &proof, key, &pretoken->Tp, &Sp, &Wp, &Wsp)) { + goto err; + } + + if (CBS_len(&proof) != 0) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + goto err; + } + + EC_RAW_POINT S, W, Ws; + if (!ec_point_mul_scalar(group, &S, &Sp, &pretoken->r) || + !ec_point_mul_scalar(group, &W, &Wp, &pretoken->r) || + !ec_point_mul_scalar(group, &Ws, &Wsp, &pretoken->r)) { + goto err; + } + + // Serialize the token. Include |key_id| to avoid an extra copy in the layer + // above. + CBB token_cbb, child; + size_t point_len = 1 + 2 * BN_num_bytes(&group->field); + if (!CBB_init(&token_cbb, 4 + PMBTOKEN_NONCE_SIZE + 3 * (2 + point_len)) || + !CBB_add_u32(&token_cbb, key_id) || + !CBB_add_bytes(&token_cbb, pretoken->t, PMBTOKEN_NONCE_SIZE) || + // TODO(https://crbug.com/boringssl/331): When updating the key format, + // remove the redundant length prefixes. + !CBB_add_u16_length_prefixed(&token_cbb, &child) || + !point_to_cbb(&child, group, &S) || + !CBB_add_u16_length_prefixed(&token_cbb, &child) || + !point_to_cbb(&child, group, &W) || + !CBB_add_u16_length_prefixed(&token_cbb, &child) || + !point_to_cbb(&child, group, &Ws) || + !CBB_flush(&token_cbb)) { + CBB_cleanup(&token_cbb); + goto err; + } + + TRUST_TOKEN *token = + TRUST_TOKEN_new(CBB_data(&token_cbb), CBB_len(&token_cbb)); + CBB_cleanup(&token_cbb); + if (token == NULL || + !sk_TRUST_TOKEN_push(ret, token)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + TRUST_TOKEN_free(token); + goto err; + } + } + + return ret; + +err: + sk_TRUST_TOKEN_pop_free(ret, TRUST_TOKEN_free); + return NULL; +} + +static int pmbtoken_read(const PMBTOKEN_METHOD *method, + const PMBTOKEN_ISSUER_KEY *key, + uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len) { + const EC_GROUP *group = method->group; + CBS cbs; + CBS_init(&cbs, token, token_len); + EC_RAW_POINT S, W, Ws; + if (!CBS_copy_bytes(&cbs, out_nonce, PMBTOKEN_NONCE_SIZE) || + !cbs_get_prefixed_point(&cbs, group, &S) || + !cbs_get_prefixed_point(&cbs, group, &W) || + !cbs_get_prefixed_point(&cbs, group, &Ws) || + CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_TOKEN); + return 0; + } + + + EC_RAW_POINT T; + if (!method->hash_t(group, &T, out_nonce)) { + return 0; + } + + EC_RAW_POINT calculated; + // Check the validity of the token. + if (!mul_twice(group, &calculated, &T, &key->xs, &S, &key->ys) || + !ec_GFp_simple_points_equal(group, &calculated, &Ws)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BAD_VALIDITY_CHECK); + return 0; + } + + EC_RAW_POINT W0, W1; + if (!mul_twice(group, &W0, &T, &key->x0, &S, &key->y0) || + !mul_twice(group, &W1, &T, &key->x1, &S, &key->y1)) { + return 0; + } + + const int is_W0 = ec_GFp_simple_points_equal(group, &W0, &W); + const int is_W1 = ec_GFp_simple_points_equal(group, &W1, &W); + const int is_valid = is_W0 ^ is_W1; + if (!is_valid) { + // Invalid tokens will fail the validity check above. + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR); + return 0; + } + + *out_private_metadata = is_W1; + return 1; +} + + +// PMBTokens experiment v0. + +static int pmbtoken_exp0_hash_t(const EC_GROUP *group, EC_RAW_POINT *out, + const uint8_t t[PMBTOKEN_NONCE_SIZE]) { + const uint8_t kHashTLabel[] = "PMBTokensV0 HashT"; + return ec_hash_to_curve_p521_xmd_sha512_sswu_draft06( + group, out, kHashTLabel, sizeof(kHashTLabel), t, PMBTOKEN_NONCE_SIZE); +} + +static int pmbtoken_exp0_hash_s(const EC_GROUP *group, EC_RAW_POINT *out, + const EC_RAW_POINT *t, + const uint8_t s[PMBTOKEN_NONCE_SIZE]) { + const uint8_t kHashSLabel[] = "PMBTokensV0 HashS"; + int ret = 0; + CBB cbb; + uint8_t *buf = NULL; + size_t len; + if (!CBB_init(&cbb, 0) || !point_to_cbb(&cbb, group, t) || + !CBB_add_bytes(&cbb, s, PMBTOKEN_NONCE_SIZE) || + !CBB_finish(&cbb, &buf, &len) || + !ec_hash_to_curve_p521_xmd_sha512_sswu_draft06( + group, out, kHashSLabel, sizeof(kHashSLabel), buf, len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + ret = 1; + +err: + OPENSSL_free(buf); + CBB_cleanup(&cbb); + return ret; +} + +static int pmbtoken_exp0_hash_c(const EC_GROUP *group, EC_SCALAR *out, + uint8_t *buf, size_t len) { + const uint8_t kHashCLabel[] = "PMBTokensV0 HashC"; + return ec_hash_to_scalar_p521_xmd_sha512_draft06( + group, out, kHashCLabel, sizeof(kHashCLabel), buf, len); +} + +// H for PMBTokens v0 was generated with the following Python code. /* import hashlib @@ -95,16 +893,16 @@ if __name__ == "__main__": gen_point(SEED_H) */ - -static const uint8_t kDefaultAdditionalData[32] = {0}; - -// TODO(svaldez): Update to use hash2curve to generate H. -static int get_h(EC_RAW_POINT *out_h) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { +static int pmbtoken_exp0_init_method(PMBTOKEN_METHOD *method) { + method->group = EC_GROUP_new_by_curve_name(NID_secp521r1); + if (method->group == NULL) { return 0; } + method->hash_t = pmbtoken_exp0_hash_t; + method->hash_c = pmbtoken_exp0_hash_c; + method->hash_s = pmbtoken_exp0_hash_s; + static const uint8_t kH[] = { 0x04, 0x01, 0xf0, 0xa9, 0xf7, 0x9e, 0xbc, 0x12, 0x6c, 0xef, 0xd1, 0xab, 0x29, 0x10, 0x03, 0x6f, 0x4e, 0xf5, 0xbd, 0xeb, 0x0f, 0x6b, 0xc0, 0x5c, @@ -120,210 +918,92 @@ 0xcd, }; - return ec_point_from_uncompressed(group, out_h, kH, sizeof(kH)); + return ec_point_from_uncompressed(method->group, &method->h, kH, sizeof(kH)); } -static int mul_twice(const EC_GROUP *group, EC_RAW_POINT *out, - const EC_RAW_POINT *g, const EC_SCALAR *g_scalar, - const EC_RAW_POINT *p, const EC_SCALAR *p_scalar) { - EC_RAW_POINT tmp1, tmp2; - if (!ec_point_mul_scalar(group, &tmp1, g, g_scalar) || - !ec_point_mul_scalar(group, &tmp2, p, p_scalar)) { +int pmbtoken_exp0_generate_key(CBB *out_private, CBB *out_public) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp0_init_method(&method)) { return 0; } - group->meth->add(group, out, &tmp1, &tmp2); - return 1; + return pmbtoken_generate_key(&method, out_private, out_public); } -static int mul_twice_base(const EC_GROUP *group, EC_RAW_POINT *out, - const EC_SCALAR *base_scalar, const EC_RAW_POINT *p, - const EC_SCALAR *p_scalar) { - EC_RAW_POINT tmp1, tmp2; - if (!ec_point_mul_scalar_base(group, &tmp1, base_scalar) || - !ec_point_mul_scalar(group, &tmp2, p, p_scalar)) { +int pmbtoken_exp0_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp0_init_method(&method)) { return 0; } - - group->meth->add(group, out, &tmp1, &tmp2); - return 1; + return pmbtoken_client_key_from_bytes(&method, key, in, len); } -// (v0;v1) = p_scalar*(G;p1) + q_scalar*(q0;q1) - r_scalar*(r0;r1) -static int mul_add_and_sub(const EC_GROUP *group, EC_RAW_POINT *out_v0, - EC_RAW_POINT *out_v1, const EC_RAW_POINT *p1, - const EC_SCALAR *p_scalar, const EC_RAW_POINT *q0, - const EC_RAW_POINT *q1, const EC_SCALAR *q_scalar, - const EC_RAW_POINT *r0, const EC_RAW_POINT *r1, - const EC_SCALAR *r_scalar) { - EC_RAW_POINT tmp0, tmp1, v0, v1; - if (!mul_twice_base(group, &v0, p_scalar, q0, q_scalar) || - !mul_twice(group, &v1, p1, p_scalar, q1, q_scalar) || - !ec_point_mul_scalar(group, &tmp0, r0, r_scalar) || - !ec_point_mul_scalar(group, &tmp1, r1, r_scalar)) { +int pmbtoken_exp0_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp0_init_method(&method)) { return 0; } - ec_GFp_simple_invert(group, &tmp0); - ec_GFp_simple_invert(group, &tmp1); - group->meth->add(group, out_v0, &v0, &tmp0); - group->meth->add(group, out_v1, &v1, &tmp1); - return 1; + return pmbtoken_issuer_key_from_bytes(&method, key, in, len); } -// 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(EC_SCALAR *out_x, EC_SCALAR *out_y, - EC_RAW_POINT *out_pub, const EC_GROUP *group) { - EC_RAW_POINT h; - if (!get_h(&h) || - !ec_random_nonzero_scalar(group, out_x, kDefaultAdditionalData) || - !ec_random_nonzero_scalar(group, out_y, kDefaultAdditionalData) || - !mul_twice_base(group, out_pub, out_x, &h, out_y)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - return 0; +STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp0_blind(CBB *cbb, size_t count) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp0_init_method(&method)) { + return NULL; } - return 1; + return pmbtoken_blind(&method, cbb, count); } -static int point_to_cbb(CBB *out, const EC_GROUP *group, - const EC_RAW_POINT *point) { - size_t len = - ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0); - if (len == 0) { +int pmbtoken_exp0_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp0_init_method(&method)) { return 0; } - uint8_t *p; - return CBB_add_space(out, &p, len) && - ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, p, - len) == len; + return pmbtoken_sign(&method, key, cbb, cbs, num_requested, num_to_issue, + private_metadata); } -static int cbs_get_prefixed_point(CBS *cbs, const EC_GROUP *group, - EC_RAW_POINT *out) { - CBS child; - return CBS_get_u16_length_prefixed(cbs, &child) && - ec_point_from_uncompressed(group, out, CBS_data(&child), - CBS_len(&child)); +STACK_OF(TRUST_TOKEN) * + pmbtoken_exp0_unblind(const PMBTOKEN_CLIENT_KEY *key, + const STACK_OF(PMBTOKEN_PRETOKEN) *pretokens, + CBS *cbs, size_t count, uint32_t key_id) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp0_init_method(&method)) { + return NULL; + } + return pmbtoken_unblind(&method, key, pretokens, cbs, count, key_id); } -void PMBTOKEN_PRETOKEN_free(PMBTOKEN_PRETOKEN *pretoken) { - OPENSSL_free(pretoken); +int pmbtoken_exp0_read(const PMBTOKEN_ISSUER_KEY *key, + uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp0_init_method(&method)) { + return 0; + } + return pmbtoken_read(&method, key, out_nonce, out_private_metadata, token, + token_len); } -int pmbtoken_generate_key(CBB *out_private, CBB *out_public) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { - return 0; - } - EC_RAW_POINT pub0, pub1, pubs; - 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); - return 0; - } +// PMBTokens experiment v1. - 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; - if (!CBB_add_space(out_private, &buf, scalar_len)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL); - return 0; - } - ec_scalar_to_bytes(group, buf, &scalar_len, scalars[i]); - } - - // TODO(https://crbug.com/boringssl/331): When updating the key format, remove - // the redundant length prefixes. - CBB child; - if (!CBB_add_u16_length_prefixed(out_public, &child) || - !point_to_cbb(&child, group, &pub0) || - !CBB_add_u16_length_prefixed(out_public, &child) || - !point_to_cbb(&child, group, &pub1) || - !CBB_add_u16_length_prefixed(out_public, &child) || - !point_to_cbb(&child, group, &pubs) || - !CBB_flush(out_public)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL); - return 0; - } - - return 1; -} - -int pmbtoken_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, const uint8_t *in, - size_t len) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { - return 0; - } - - // TODO(https://crbug.com/boringssl/331): When updating the key format, remove - // the redundant length prefixes. - CBS cbs; - CBS_init(&cbs, in, len); - if (!cbs_get_prefixed_point(&cbs, group, &key->pub0) || - !cbs_get_prefixed_point(&cbs, group, &key->pub1) || - !cbs_get_prefixed_point(&cbs, group, &key->pubs) || - CBS_len(&cbs) != 0) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - return 0; - } - - return 1; -} - -int pmbtoken_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, const uint8_t *in, - size_t len) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { - return 0; - } - - CBS cbs, tmp; - CBS_init(&cbs, in, len); - size_t scalar_len = BN_num_bytes(&group->order); - EC_SCALAR *scalars[] = {&key->x0, &key->y0, &key->x1, - &key->y1, &key->xs, &key->ys}; - for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(scalars); i++) { - if (!CBS_get_bytes(&cbs, &tmp, scalar_len) || - !ec_scalar_from_bytes(group, scalars[i], CBS_data(&tmp), - CBS_len(&tmp))) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - return 0; - } - } - - // Recompute the public key. - EC_RAW_POINT h; - if (!get_h(&h) || - !mul_twice_base(group, &key->pubs, &key->xs, &h, &key->ys) || - !mul_twice_base(group, &key->pub0, &key->x0, &h, &key->y0) || - !mul_twice_base(group, &key->pub1, &key->x1, &h, &key->y1)) { - return 0; - } - - return 1; -} - -// hash_t implements the H_t operation in PMBTokens. It returns on on success -// and zero on error. -static int hash_t(EC_GROUP *group, EC_RAW_POINT *out, - const uint8_t t[PMBTOKEN_NONCE_SIZE]) { - const uint8_t kHashTLabel[] = "PMBTokensV0 HashT"; - return ec_hash_to_curve_p521_xmd_sha512_sswu_draft06( +static int pmbtoken_exp1_hash_t(const EC_GROUP *group, EC_RAW_POINT *out, + const uint8_t t[PMBTOKEN_NONCE_SIZE]) { + const uint8_t kHashTLabel[] = "PMBTokens Experiment V1 HashT"; + return ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( group, out, kHashTLabel, sizeof(kHashTLabel), t, PMBTOKEN_NONCE_SIZE); } -// hash_s implements the H_s operation in PMBTokens. It returns on on success -// and zero on error. -static int hash_s(EC_GROUP *group, EC_RAW_POINT *out, const EC_RAW_POINT *t, - const uint8_t s[PMBTOKEN_NONCE_SIZE]) { - const uint8_t kHashSLabel[] = "PMBTokensV0 HashS"; +static int pmbtoken_exp1_hash_s(const EC_GROUP *group, EC_RAW_POINT *out, + const EC_RAW_POINT *t, + const uint8_t s[PMBTOKEN_NONCE_SIZE]) { + const uint8_t kHashSLabel[] = "PMBTokens Experiment V1 HashS"; int ret = 0; CBB cbb; uint8_t *buf = NULL; @@ -332,7 +1012,7 @@ !point_to_cbb(&cbb, group, t) || !CBB_add_bytes(&cbb, s, PMBTOKEN_NONCE_SIZE) || !CBB_finish(&cbb, &buf, &len) || - !ec_hash_to_curve_p521_xmd_sha512_sswu_draft06( + !ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( group, out, kHashSLabel, sizeof(kHashSLabel), buf, len)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); goto err; @@ -346,602 +1026,114 @@ return ret; } -STACK_OF(PMBTOKEN_PRETOKEN) *pmbtoken_blind(CBB *cbb, size_t count) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { - return NULL; - } - - STACK_OF(PMBTOKEN_PRETOKEN) *pretokens = sk_PMBTOKEN_PRETOKEN_new_null(); - if (pretokens == NULL) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - goto err; - } - - for (size_t i = 0; i < count; i++) { - // Insert |pretoken| into |pretokens| early to simplify error-handling. - PMBTOKEN_PRETOKEN *pretoken = OPENSSL_malloc(sizeof(PMBTOKEN_PRETOKEN)); - if (pretoken == NULL || - !sk_PMBTOKEN_PRETOKEN_push(pretokens, pretoken)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - PMBTOKEN_PRETOKEN_free(pretoken); - goto err; - } - - RAND_bytes(pretoken->t, sizeof(pretoken->t)); - - // We sample |pretoken->r| in Montgomery form to simplify inverting. - if (!ec_random_nonzero_scalar(group, &pretoken->r, - kDefaultAdditionalData)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - goto err; - } - - EC_SCALAR rinv; - ec_scalar_inv0_montgomery(group, &rinv, &pretoken->r); - // Convert both out of Montgomery form. - ec_scalar_from_montgomery(group, &pretoken->r, &pretoken->r); - ec_scalar_from_montgomery(group, &rinv, &rinv); - - EC_RAW_POINT T; - if (!hash_t(group, &T, pretoken->t) || - !ec_point_mul_scalar(group, &pretoken->Tp, &T, &rinv)) { - goto err; - } - - // TODO(https://crbug.com/boringssl/331): When updating the key format, - // remove the redundant length prefixes. - CBB child; - if (!CBB_add_u16_length_prefixed(cbb, &child) || - !point_to_cbb(&child, group, &pretoken->Tp) || - !CBB_flush(cbb)) { - goto err; - } - } - - return pretokens; - -err: - sk_PMBTOKEN_PRETOKEN_pop_free(pretokens, PMBTOKEN_PRETOKEN_free); - return NULL; -} - -static int hash_c(const EC_GROUP *group, EC_SCALAR *out, uint8_t *buf, - size_t len) { - const uint8_t kHashCLabel[] = "PMBTokensV0 HashC"; - return ec_hash_to_scalar_p521_xmd_sha512_draft06( +static int pmbtoken_exp1_hash_c(const EC_GROUP *group, EC_SCALAR *out, + uint8_t *buf, size_t len) { + const uint8_t kHashCLabel[] = "PMBTokens Experiment V1 HashC"; + return ec_hash_to_scalar_p384_xmd_sha512_draft07( group, out, kHashCLabel, sizeof(kHashCLabel), buf, len); } -static int scalar_to_cbb(CBB *out, const EC_GROUP *group, - const EC_SCALAR *scalar) { - uint8_t *buf; - size_t scalar_len = BN_num_bytes(&group->order); - if (!CBB_add_space(out, &buf, scalar_len)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); +static int pmbtoken_exp1_init_method(PMBTOKEN_METHOD *method) { + method->group = EC_GROUP_new_by_curve_name(NID_secp384r1); + if (method->group == NULL) { return 0; } - ec_scalar_to_bytes(group, buf, &scalar_len, scalar); - return 1; + + method->hash_t = pmbtoken_exp1_hash_t; + method->hash_c = pmbtoken_exp1_hash_c; + method->hash_s = pmbtoken_exp1_hash_s; + + // This is the output of |ec_hash_to_scalar_p384_xmd_sha512_draft07| with DST + // "PMBTokens Experiment V1 HashH" and message "generator". + static const uint8_t kH[] = { + 0x04, 0x82, 0xd5, 0x68, 0xf5, 0x39, 0xf6, 0x08, 0x19, 0xa1, 0x75, + 0x9f, 0x98, 0xb5, 0x10, 0xf5, 0x0b, 0x9d, 0x2b, 0xe1, 0x64, 0x4d, + 0x02, 0x76, 0x18, 0x11, 0xf8, 0x2f, 0xd3, 0x33, 0x25, 0x1f, 0x2c, + 0xb8, 0xf6, 0xf1, 0x9e, 0x93, 0x85, 0x79, 0xb3, 0xb7, 0x81, 0xa3, + 0xe6, 0x23, 0xc3, 0x1c, 0xff, 0x03, 0xd9, 0x40, 0x6c, 0xec, 0xe0, + 0x4d, 0xea, 0xdf, 0x9d, 0x94, 0xd1, 0x87, 0xab, 0x27, 0xf7, 0x4f, + 0x53, 0xea, 0xa3, 0x18, 0x72, 0xb9, 0xd1, 0x56, 0xa0, 0x4e, 0x81, + 0xaa, 0xeb, 0x1c, 0x22, 0x6d, 0x39, 0x1c, 0x5e, 0xb1, 0x27, 0xfc, + 0x87, 0xc3, 0x95, 0xd0, 0x13, 0xb7, 0x0b, 0x5c, 0xc7, + }; + + return ec_point_from_uncompressed(method->group, &method->h, kH, sizeof(kH)); } -static int scalar_from_cbs(CBS *cbs, const EC_GROUP *group, EC_SCALAR *out) { - size_t scalar_len = BN_num_bytes(&group->order); - CBS tmp; - if (!CBS_get_bytes(cbs, &tmp, scalar_len)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); +int pmbtoken_exp1_generate_key(CBB *out_private, CBB *out_public) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { return 0; } - ec_scalar_from_bytes(group, out, CBS_data(&tmp), CBS_len(&tmp)); - return 1; + return pmbtoken_generate_key(&method, out_private, out_public); } -static int hash_c_dleq(const EC_GROUP *group, EC_SCALAR *out, - const EC_RAW_POINT *X, const EC_RAW_POINT *T, - const EC_RAW_POINT *S, const EC_RAW_POINT *W, - const EC_RAW_POINT *K0, const EC_RAW_POINT *K1) { - static const uint8_t kDLEQ2Label[] = "DLEQ2"; - - int ok = 0; - CBB cbb; - CBB_zero(&cbb); - uint8_t *buf = NULL; - size_t len; - if (!CBB_init(&cbb, 0) || - !CBB_add_bytes(&cbb, kDLEQ2Label, sizeof(kDLEQ2Label)) || - !point_to_cbb(&cbb, group, X) || - !point_to_cbb(&cbb, group, T) || - !point_to_cbb(&cbb, group, S) || - !point_to_cbb(&cbb, group, W) || - !point_to_cbb(&cbb, group, K0) || - !point_to_cbb(&cbb, group, K1) || - !CBB_finish(&cbb, &buf, &len) || - !hash_c(group, out, buf, len)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - goto err; +int pmbtoken_exp1_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { + return 0; } - - ok = 1; - -err: - CBB_cleanup(&cbb); - OPENSSL_free(buf); - return ok; + return pmbtoken_client_key_from_bytes(&method, key, in, len); } -static int hash_c_dleqor(const EC_GROUP *group, EC_SCALAR *out, - const EC_RAW_POINT *X0, const EC_RAW_POINT *X1, - const EC_RAW_POINT *T, const EC_RAW_POINT *S, - const EC_RAW_POINT *W, const EC_RAW_POINT *K00, - const EC_RAW_POINT *K01, const EC_RAW_POINT *K10, - const EC_RAW_POINT *K11) { - static const uint8_t kDLEQOR2Label[] = "DLEQOR2"; - - int ok = 0; - CBB cbb; - CBB_zero(&cbb); - uint8_t *buf = NULL; - size_t len; - if (!CBB_init(&cbb, 0) || - !CBB_add_bytes(&cbb, kDLEQOR2Label, sizeof(kDLEQOR2Label)) || - !point_to_cbb(&cbb, group, X0) || - !point_to_cbb(&cbb, group, X1) || - !point_to_cbb(&cbb, group, T) || - !point_to_cbb(&cbb, group, S) || - !point_to_cbb(&cbb, group, W) || - !point_to_cbb(&cbb, group, K00) || - !point_to_cbb(&cbb, group, K01) || - !point_to_cbb(&cbb, group, K10) || - !point_to_cbb(&cbb, group, K11) || - !CBB_finish(&cbb, &buf, &len) || - !hash_c(group, out, buf, len)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - goto err; +int pmbtoken_exp1_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { + return 0; } - - ok = 1; - -err: - CBB_cleanup(&cbb); - OPENSSL_free(buf); - return ok; + return pmbtoken_issuer_key_from_bytes(&method, key, in, len); } -// The DLEQ2 and DLEQOR2 constructions are described in appendix B of -// https://eprint.iacr.org/2020/072/20200324:214215. DLEQ2 is an instance of -// DLEQOR2 with only one value (n=1). - -static int dleq_generate(const EC_GROUP *group, CBB *cbb, - const PMBTOKEN_ISSUER_KEY *priv, const EC_RAW_POINT *T, - const EC_RAW_POINT *S, const EC_RAW_POINT *W, - const EC_RAW_POINT *Ws, uint8_t private_metadata) { - EC_RAW_POINT h; - if (!get_h(&h)) { - return 0; +STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp1_blind(CBB *cbb, size_t count) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { + return NULL; } - - // Generate DLEQ2 proof for the validity token. - - // ks0, ks1 <- Zp - EC_SCALAR ks0, ks1; - if (!ec_random_nonzero_scalar(group, &ks0, kDefaultAdditionalData) || - !ec_random_nonzero_scalar(group, &ks1, kDefaultAdditionalData)) { - return 0; - } - - // Ks = ks0*(G;T) + ks1*(H;S) - EC_RAW_POINT Ks0, Ks1; - if (!mul_twice_base(group, &Ks0, &ks0, &h, &ks1) || - !mul_twice(group, &Ks1, T, &ks0, S, &ks1)) { - return 0; - } - - // cs = Hc(...) - EC_SCALAR cs; - if (!hash_c_dleq(group, &cs, &priv->pubs, T, S, Ws, &Ks0, &Ks1)) { - return 0; - } - - EC_SCALAR cs_mont; - ec_scalar_to_montgomery(group, &cs_mont, &cs); - - // In each of these products, only one operand is in Montgomery form, so the - // product does not need to be converted. - - // us = ks0 + cs*xs - EC_SCALAR us; - ec_scalar_mul_montgomery(group, &us, &priv->xs, &cs_mont); - ec_scalar_add(group, &us, &ks0, &us); - - // vs = ks1 + cs*ys - EC_SCALAR vs; - ec_scalar_mul_montgomery(group, &vs, &priv->ys, &cs_mont); - ec_scalar_add(group, &vs, &ks1, &vs); - - // Store DLEQ2 proof in transcript. - if (!scalar_to_cbb(cbb, group, &cs) || - !scalar_to_cbb(cbb, group, &us) || - !scalar_to_cbb(cbb, group, &vs)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - return 0; - } - - // Generate DLEQOR2 proof for the private metadata token. - BN_ULONG mask = ((BN_ULONG)0) - (private_metadata&1); - - // Select values of xb, yb (keys corresponding to the private metadata value) - // and pubo (public key corresponding to the other value) in constant time. - EC_RAW_POINT pubo; - EC_SCALAR xb, yb; - ec_scalar_select(group, &xb, mask, &priv->x1, &priv->x0); - ec_scalar_select(group, &yb, mask, &priv->y1, &priv->y0); - ec_point_select(group, &pubo, mask, &priv->pub0, &priv->pub1); - - // k0, k1 <- Zp - EC_SCALAR k0, k1; - if (!ec_random_nonzero_scalar(group, &k0, kDefaultAdditionalData) || - !ec_random_nonzero_scalar(group, &k1, kDefaultAdditionalData)) { - return 0; - } - - // Kb = k0*(G;T) + k1*(H;S) - EC_RAW_POINT Kb0, Kb1; - if (!mul_twice_base(group, &Kb0, &k0, &h, &k1) || - !mul_twice(group, &Kb1, T, &k0, S, &k1)) { - return 0; - } - - // co, uo, vo <- Zp - EC_SCALAR co, uo, vo; - if (!ec_random_nonzero_scalar(group, &co, kDefaultAdditionalData) || - !ec_random_nonzero_scalar(group, &uo, kDefaultAdditionalData) || - !ec_random_nonzero_scalar(group, &vo, kDefaultAdditionalData)) { - return 0; - } - - // Ko = uo*(G;T) + vo*(H;S) - co*(pubo;W) - EC_RAW_POINT Ko0, Ko1; - if (!mul_add_and_sub(group, &Ko0, &Ko1, T, &uo, &h, S, &vo, &pubo, W, &co)) { - return 0; - } - - // Select the K corresponding to K0 and K1 in constant-time. - EC_RAW_POINT K00, K01, K10, K11; - ec_point_select(group, &K00, mask, &Ko0, &Kb0); - ec_point_select(group, &K01, mask, &Ko1, &Kb1); - ec_point_select(group, &K10, mask, &Kb0, &Ko0); - ec_point_select(group, &K11, mask, &Kb1, &Ko1); - - // c = Hc(...) - EC_SCALAR c; - if (!hash_c_dleqor(group, &c, &priv->pub0, &priv->pub1, T, S, W, &K00, &K01, - &K10, &K11)) { - return 0; - } - - // cb = c - co - EC_SCALAR cb, ub, vb; - ec_scalar_sub(group, &cb, &c, &co); - - EC_SCALAR cb_mont; - ec_scalar_to_montgomery(group, &cb_mont, &cb); - - // In each of these products, only one operand is in Montgomery form, so the - // product does not need to be converted. - - // ub = k0 + cb*xb - ec_scalar_mul_montgomery(group, &ub, &xb, &cb_mont); - ec_scalar_add(group, &ub, &k0, &ub); - - // vb = k1 + cb*yb - ec_scalar_mul_montgomery(group, &vb, &yb, &cb_mont); - ec_scalar_add(group, &vb, &k1, &vb); - - // Select c, u, v in constant-time. - EC_SCALAR c0, c1, u0, u1, v0, v1; - ec_scalar_select(group, &c0, mask, &co, &cb); - ec_scalar_select(group, &u0, mask, &uo, &ub); - ec_scalar_select(group, &v0, mask, &vo, &vb); - ec_scalar_select(group, &c1, mask, &cb, &co); - ec_scalar_select(group, &u1, mask, &ub, &uo); - ec_scalar_select(group, &v1, mask, &vb, &vo); - - // Store DLEQOR2 proof in transcript. - if (!scalar_to_cbb(cbb, group, &c0) || - !scalar_to_cbb(cbb, group, &c1) || - !scalar_to_cbb(cbb, group, &u0) || - !scalar_to_cbb(cbb, group, &u1) || - !scalar_to_cbb(cbb, group, &v0) || - !scalar_to_cbb(cbb, group, &v1)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - return 0; - } - - return 1; + return pmbtoken_blind(&method, cbb, count); } -static int dleq_verify(const EC_GROUP *group, CBS *cbs, - const PMBTOKEN_CLIENT_KEY *pub, const EC_RAW_POINT *T, - const EC_RAW_POINT *S, const EC_RAW_POINT *W, - const EC_RAW_POINT *Ws) { - EC_RAW_POINT h; - if (!get_h(&h)) { +int pmbtoken_exp1_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { return 0; } - - // Verify the DLEQ2 proof over the validity token. - EC_SCALAR cs, us, vs; - if (!scalar_from_cbs(cbs, group, &cs) || - !scalar_from_cbs(cbs, group, &us) || - !scalar_from_cbs(cbs, group, &vs)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - return 0; - } - - // Ks = us*(G;T) + vs*(H;S) - cs*(pubs;Ws) - EC_RAW_POINT Ks0, Ks1; - if (!mul_add_and_sub(group, &Ks0, &Ks1, T, &us, &h, S, &vs, &pub->pubs, Ws, - &cs)) { - return 0; - } - - // calculated = Hc(...) - EC_SCALAR calculated; - if (!hash_c_dleq(group, &calculated, &pub->pubs, T, S, Ws, &Ks0, &Ks1)) { - return 0; - } - - // cs == calculated - if (!ec_scalar_equal_vartime(group, &cs, &calculated)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_PROOF); - return 0; - } - - // Verify the DLEQOR2 proof over the private metadata token. - - EC_SCALAR c0, c1, u0, u1, v0, v1; - if (!scalar_from_cbs(cbs, group, &c0) || - !scalar_from_cbs(cbs, group, &c1) || - !scalar_from_cbs(cbs, group, &u0) || - !scalar_from_cbs(cbs, group, &u1) || - !scalar_from_cbs(cbs, group, &v0) || - !scalar_from_cbs(cbs, group, &v1)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - return 0; - } - - // K0 = u0*(G;T) + v0*(H;S) - c0*(pub0;W) - EC_RAW_POINT K00, K01; - if (!mul_add_and_sub(group, &K00, &K01, T, &u0, &h, S, &v0, &pub->pub0, W, - &c0)) { - return 0; - } - - // K1 = u1*(G;T) + v1*(H;S) - c1*(pub1;Ws) - EC_RAW_POINT K10, K11; - if (!mul_add_and_sub(group, &K10, &K11, T, &u1, &h, S, &v1, &pub->pub1, W, - &c1)) { - return 0; - } - - // calculated = Hc(...) - if (!hash_c_dleqor(group, &calculated, &pub->pub0, &pub->pub1, T, S, W, &K00, - &K01, &K10, &K11)) { - return 0; - } - - // c = c0 + c1 - EC_SCALAR c; - ec_scalar_add(group, &c, &c0, &c1); - - // c == calculated - if (!ec_scalar_equal_vartime(group, &c, &calculated)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_PROOF); - return 0; - } - - return 1; -} - -int pmbtoken_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, - size_t num_requested, size_t num_to_issue, - uint8_t private_metadata) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { - return 0; - } - - if (num_requested < num_to_issue) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR); - return 0; - } - - for (size_t i = 0; i < num_to_issue; i++) { - EC_RAW_POINT Tp; - if (!cbs_get_prefixed_point(cbs, group, &Tp)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - return 0; - } - - EC_SCALAR xb, yb; - BN_ULONG mask = ((BN_ULONG)0) - (private_metadata & 1); - ec_scalar_select(group, &xb, mask, &key->x1, &key->x0); - ec_scalar_select(group, &yb, mask, &key->y1, &key->y0); - - uint8_t s[PMBTOKEN_NONCE_SIZE]; - RAND_bytes(s, PMBTOKEN_NONCE_SIZE); - EC_RAW_POINT Sp, Wp, Wsp; - CBB child; - if (!hash_s(group, &Sp, &Tp, s) || - !mul_twice(group, &Wp, &Tp, &xb, &Sp, &yb) || - !mul_twice(group, &Wsp, &Tp, &key->xs, &Sp, &key->ys) || - !CBB_add_bytes(cbb, s, PMBTOKEN_NONCE_SIZE) || - // TODO(https://crbug.com/boringssl/331): When updating the key format, - // remove the redundant length prefixes. - !CBB_add_u16_length_prefixed(cbb, &child) || - !point_to_cbb(&child, group, &Wp) || - !CBB_add_u16_length_prefixed(cbb, &child) || - !point_to_cbb(&child, group, &Wsp) || - !CBB_add_u16_length_prefixed(cbb, &child) || - !dleq_generate(group, &child, key, &Tp, &Sp, &Wp, &Wsp, - private_metadata) || - !CBB_flush(cbb)) { - return 0; - } - } - - // Skip over any unused requests. - size_t point_len = 1 + 2 * BN_num_bytes(&group->field); - if (!CBS_skip(cbs, (2 + point_len) * (num_requested - num_to_issue))) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - return 0; - } - - return 1; + return pmbtoken_sign(&method, key, cbb, cbs, num_requested, num_to_issue, + private_metadata); } STACK_OF(TRUST_TOKEN) * - pmbtoken_unblind(const PMBTOKEN_CLIENT_KEY *key, - const STACK_OF(PMBTOKEN_PRETOKEN) *pretokens, CBS *cbs, - size_t count, uint32_t key_id) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { + pmbtoken_exp1_unblind(const PMBTOKEN_CLIENT_KEY *key, + const STACK_OF(PMBTOKEN_PRETOKEN) *pretokens, + CBS *cbs, size_t count, uint32_t key_id) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { return NULL; } - - if (count > sk_PMBTOKEN_PRETOKEN_num(pretokens)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - return NULL; - } - - STACK_OF(TRUST_TOKEN) *ret = sk_TRUST_TOKEN_new_null(); - if (ret == NULL) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - return NULL; - } - - for (size_t i = 0; i < count; i++) { - const PMBTOKEN_PRETOKEN *pretoken = - sk_PMBTOKEN_PRETOKEN_value(pretokens, i); - - uint8_t s[PMBTOKEN_NONCE_SIZE]; - EC_RAW_POINT Wp, Wsp; - CBS proof; - if (!CBS_copy_bytes(cbs, s, PMBTOKEN_NONCE_SIZE) || - !cbs_get_prefixed_point(cbs, group, &Wp) || - !cbs_get_prefixed_point(cbs, group, &Wsp) || - !CBS_get_u16_length_prefixed(cbs, &proof)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - goto err; - } - - EC_RAW_POINT Sp; - if (!hash_s(group, &Sp, &pretoken->Tp, s) || - !dleq_verify(group, &proof, key, &pretoken->Tp, &Sp, &Wp, &Wsp)) { - goto err; - } - - if (CBS_len(&proof) != 0) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); - goto err; - } - - EC_RAW_POINT S, W, Ws; - if (!ec_point_mul_scalar(group, &S, &Sp, &pretoken->r) || - !ec_point_mul_scalar(group, &W, &Wp, &pretoken->r) || - !ec_point_mul_scalar(group, &Ws, &Wsp, &pretoken->r)) { - goto err; - } - - // Serialize the token. Include |key_id| to avoid an extra copy in the layer - // above. - CBB token_cbb, child; - size_t point_len = 1 + 2 * BN_num_bytes(&group->field); - if (!CBB_init(&token_cbb, 4 + PMBTOKEN_NONCE_SIZE + 3 * (2 + point_len)) || - !CBB_add_u32(&token_cbb, key_id) || - !CBB_add_bytes(&token_cbb, pretoken->t, PMBTOKEN_NONCE_SIZE) || - // TODO(https://crbug.com/boringssl/331): When updating the key format, - // remove the redundant length prefixes. - !CBB_add_u16_length_prefixed(&token_cbb, &child) || - !point_to_cbb(&child, group, &S) || - !CBB_add_u16_length_prefixed(&token_cbb, &child) || - !point_to_cbb(&child, group, &W) || - !CBB_add_u16_length_prefixed(&token_cbb, &child) || - !point_to_cbb(&child, group, &Ws) || - !CBB_flush(&token_cbb)) { - CBB_cleanup(&token_cbb); - goto err; - } - - TRUST_TOKEN *token = - TRUST_TOKEN_new(CBB_data(&token_cbb), CBB_len(&token_cbb)); - CBB_cleanup(&token_cbb); - if (token == NULL || - !sk_TRUST_TOKEN_push(ret, token)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - TRUST_TOKEN_free(token); - goto err; - } - } - - return ret; - -err: - sk_TRUST_TOKEN_pop_free(ret, TRUST_TOKEN_free); - return NULL; + return pmbtoken_unblind(&method, key, pretokens, cbs, count, key_id); } -int pmbtoken_read(const PMBTOKEN_ISSUER_KEY *key, - uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], - uint8_t *out_private_metadata, const uint8_t *token, - size_t token_len) { - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1); - if (group == NULL) { +int pmbtoken_exp1_read(const PMBTOKEN_ISSUER_KEY *key, + uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { return 0; } + return pmbtoken_read(&method, key, out_nonce, out_private_metadata, token, + token_len); +} - CBS cbs; - CBS_init(&cbs, token, token_len); - EC_RAW_POINT S, W, Ws; - if (!CBS_copy_bytes(&cbs, out_nonce, PMBTOKEN_NONCE_SIZE) || - !cbs_get_prefixed_point(&cbs, group, &S) || - !cbs_get_prefixed_point(&cbs, group, &W) || - !cbs_get_prefixed_point(&cbs, group, &Ws) || - CBS_len(&cbs) != 0) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_TOKEN); +int pmbtoken_exp1_get_h_for_testing(uint8_t out[97]) { + PMBTOKEN_METHOD method; + if (!pmbtoken_exp1_init_method(&method)) { return 0; } - - - EC_RAW_POINT T; - if (!hash_t(group, &T, out_nonce)) { - return 0; - } - - EC_RAW_POINT calculated; - // Check the validity of the token. - if (!mul_twice(group, &calculated, &T, &key->xs, &S, &key->ys) || - !ec_GFp_simple_points_equal(group, &calculated, &Ws)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BAD_VALIDITY_CHECK); - return 0; - } - - EC_RAW_POINT W0, W1; - if (!mul_twice(group, &W0, &T, &key->x0, &S, &key->y0) || - !mul_twice(group, &W1, &T, &key->x1, &S, &key->y1)) { - return 0; - } - - const int is_W0 = ec_GFp_simple_points_equal(group, &W0, &W); - const int is_W1 = ec_GFp_simple_points_equal(group, &W1, &W); - const int is_valid = is_W0 ^ is_W1; - if (!is_valid) { - // Invalid tokens will fail the validity check above. - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR); - return 0; - } - - *out_private_metadata = is_W1; - return 1; + return ec_point_to_bytes(method.group, &method.h, + POINT_CONVERSION_UNCOMPRESSED, out, 97) == 97; }
diff --git a/crypto/trust_token/trust_token.c b/crypto/trust_token/trust_token.c index a18fb20..5ced6cc 100644 --- a/crypto/trust_token/trust_token.c +++ b/crypto/trust_token/trust_token.c
@@ -29,13 +29,26 @@ const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v0(void) { static const TRUST_TOKEN_METHOD kMethod = { - pmbtoken_generate_key, - pmbtoken_client_key_from_bytes, - pmbtoken_issuer_key_from_bytes, - pmbtoken_blind, - pmbtoken_sign, - pmbtoken_unblind, - pmbtoken_read, + pmbtoken_exp0_generate_key, + pmbtoken_exp0_client_key_from_bytes, + pmbtoken_exp0_issuer_key_from_bytes, + pmbtoken_exp0_blind, + pmbtoken_exp0_sign, + pmbtoken_exp0_unblind, + pmbtoken_exp0_read, + }; + return &kMethod; +} + +const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v1(void) { + static const TRUST_TOKEN_METHOD kMethod = { + pmbtoken_exp1_generate_key, + pmbtoken_exp1_client_key_from_bytes, + pmbtoken_exp1_issuer_key_from_bytes, + pmbtoken_exp1_blind, + pmbtoken_exp1_sign, + pmbtoken_exp1_unblind, + pmbtoken_exp1_read, }; return &kMethod; }
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc index cf9ad7e..50ef729 100644 --- a/crypto/trust_token/trust_token_test.cc +++ b/crypto/trust_token/trust_token_test.cc
@@ -32,7 +32,10 @@ #include <openssl/rand.h> #include <openssl/trust_token.h> +#include "../ec_extra/internal.h" +#include "../fipsmodule/ec/internal.h" #include "../internal.h" +#include "../test/test_util.h" #include "internal.h" @@ -40,7 +43,7 @@ namespace { -TEST(TrustTokenTest, KeyGen) { +TEST(TrustTokenTest, KeyGenExp0) { 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; @@ -52,18 +55,55 @@ ASSERT_EQ(409u, pub_key_len); } -class TrustTokenProtocolTest : public ::testing::Test { +TEST(TrustTokenTest, KeyGenExp1) { + 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( + 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)); + ASSERT_EQ(292u, priv_key_len); + ASSERT_EQ(301u, pub_key_len); +} + +// Test that H in |TRUST_TOKEN_experiment_v1| was computed correctly. +TEST(TrustTokenTest, HExp1) { + const EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp384r1); + ASSERT_TRUE(group); + + const uint8_t kHGen[] = "generator"; + const uint8_t kHLabel[] = "PMBTokens Experiment V1 HashH"; + + EC_RAW_POINT expected_h; + ASSERT_TRUE(ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( + group, &expected_h, kHLabel, sizeof(kHLabel), kHGen, sizeof(kHGen))); + uint8_t expected_bytes[1 + 2 * EC_MAX_BYTES]; + size_t expected_len = + ec_point_to_bytes(group, &expected_h, POINT_CONVERSION_UNCOMPRESSED, + expected_bytes, sizeof(expected_bytes)); + + uint8_t h[97]; + ASSERT_TRUE(pmbtoken_exp1_get_h_for_testing(h)); + EXPECT_EQ(Bytes(h), Bytes(expected_bytes, expected_len)); +} + +static std::vector<const TRUST_TOKEN_METHOD *> AllMethods() { + return {TRUST_TOKEN_experiment_v0(), TRUST_TOKEN_experiment_v1()}; +} + +class TrustTokenProtocolTestBase : public ::testing::Test { public: + explicit TrustTokenProtocolTestBase(const TRUST_TOKEN_METHOD *method) + : method_(method) {} + // KeyID returns the key ID associated with key index |i|. static uint32_t KeyID(size_t i) { // Use a different value from the indices to that we do not mix them up. return 7 + i; } - // TODO(davidben): Parameterize this on the Trust Tokens method. - static const TRUST_TOKEN_METHOD *method() { - return TRUST_TOKEN_experiment_v0(); - } + const TRUST_TOKEN_METHOD *method() { return method_; } protected: void SetupContexts() { @@ -102,6 +142,7 @@ sizeof(metadata_key))); } + const TRUST_TOKEN_METHOD *method_; uint16_t client_max_batchsize = 10; uint16_t issuer_max_batchsize = 10; bssl::UniquePtr<TRUST_TOKEN_CLIENT> client; @@ -109,7 +150,17 @@ uint8_t metadata_key[32]; }; -TEST_F(TrustTokenProtocolTest, InvalidToken) { +class TrustTokenProtocolTest + : public TrustTokenProtocolTestBase, + public testing::WithParamInterface<const TRUST_TOKEN_METHOD *> { + public: + TrustTokenProtocolTest() : TrustTokenProtocolTestBase(GetParam()) {} +}; + +INSTANTIATE_TEST_SUITE_P(TrustTokenAllProtocolTest, TrustTokenProtocolTest, + testing::ValuesIn(AllMethods())); + +TEST_P(TrustTokenProtocolTest, InvalidToken) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; @@ -149,7 +200,7 @@ } } -TEST_F(TrustTokenProtocolTest, TruncatedIssuanceRequest) { +TEST_P(TrustTokenProtocolTest, TruncatedIssuanceRequest) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; @@ -166,7 +217,7 @@ bssl::UniquePtr<uint8_t> free_msg(issue_resp); } -TEST_F(TrustTokenProtocolTest, TruncatedIssuanceResponse) { +TEST_P(TrustTokenProtocolTest, TruncatedIssuanceResponse) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; @@ -188,7 +239,7 @@ ASSERT_FALSE(tokens); } -TEST_F(TrustTokenProtocolTest, ExtraDataIssuanceResponse) { +TEST_P(TrustTokenProtocolTest, ExtraDataIssuanceResponse) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *request = NULL, *response = NULL; @@ -212,7 +263,7 @@ ASSERT_FALSE(tokens); } -TEST_F(TrustTokenProtocolTest, TruncatedRedemptionRequest) { +TEST_P(TrustTokenProtocolTest, TruncatedRedemptionRequest) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; @@ -253,7 +304,7 @@ } } -TEST_F(TrustTokenProtocolTest, TruncatedRedemptionResponse) { +TEST_P(TrustTokenProtocolTest, TruncatedRedemptionResponse) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; @@ -307,7 +358,7 @@ } } -TEST_F(TrustTokenProtocolTest, IssuedWithBadKeyID) { +TEST_P(TrustTokenProtocolTest, IssuedWithBadKeyID) { client.reset(TRUST_TOKEN_CLIENT_new(method(), client_max_batchsize)); ASSERT_TRUE(client); issuer.reset(TRUST_TOKEN_ISSUER_new(method(), issuer_max_batchsize)); @@ -367,8 +418,16 @@ } class TrustTokenMetadataTest - : public TrustTokenProtocolTest, - public testing::WithParamInterface<std::tuple<int, bool>> {}; + : public TrustTokenProtocolTestBase, + public testing::WithParamInterface< + std::tuple<const TRUST_TOKEN_METHOD *, int, bool>> { + public: + TrustTokenMetadataTest() + : TrustTokenProtocolTestBase(std::get<0>(GetParam())) {} + + int public_metadata() { return std::get<1>(GetParam()); } + bool private_metadata() { return std::get<2>(GetParam()); } +}; TEST_P(TrustTokenMetadataTest, SetAndGetMetadata) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); @@ -381,7 +440,7 @@ size_t tokens_issued; ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue( issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len, - std::get<0>(GetParam()), std::get<1>(GetParam()), /*max_issuance=*/1)); + public_metadata(), private_metadata(), /*max_issuance=*/1)); bssl::UniquePtr<uint8_t> free_msg(issue_resp); size_t key_index; bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens( @@ -427,12 +486,12 @@ bssl::UniquePtr<uint8_t> free_srr(srr); bssl::UniquePtr<uint8_t> free_sig(sig); - uint8_t private_metadata; + uint8_t decode_private_metadata; ASSERT_TRUE(TRUST_TOKEN_decode_private_metadata( - method(), &private_metadata, metadata_key, sizeof(metadata_key), + method(), &decode_private_metadata, metadata_key, sizeof(metadata_key), kClientData, sizeof(kClientData) - 1, srr[27])); - ASSERT_EQ(srr[18], std::get<0>(GetParam())); - ASSERT_EQ(private_metadata, std::get<1>(GetParam())); + ASSERT_EQ(srr[18], public_metadata()); + ASSERT_EQ(decode_private_metadata, private_metadata()); // Clear out the metadata bits. srr[18] = 0; @@ -455,7 +514,7 @@ size_t tokens_issued; ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue( issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len, - std::get<0>(GetParam()), std::get<1>(GetParam()), /*max_issuance=*/1)); + public_metadata(), private_metadata(), /*max_issuance=*/1)); bssl::UniquePtr<uint8_t> free_msg(issue_resp); ASSERT_EQ(tokens_issued, issuer_max_batchsize); size_t key_index; @@ -478,7 +537,7 @@ size_t tokens_issued; ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue( issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len, - std::get<0>(GetParam()), std::get<1>(GetParam()), /*max_issuance=*/1)); + public_metadata(), private_metadata(), /*max_issuance=*/1)); bssl::UniquePtr<uint8_t> free_msg(issue_resp); CBS real_response; @@ -518,7 +577,8 @@ size_t key_index; bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens( - TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, bad_buf, bad_len)); + TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, bad_buf, + bad_len)); ASSERT_FALSE(tokens); } @@ -533,7 +593,7 @@ size_t tokens_issued; ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue( issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len, - std::get<0>(GetParam()), std::get<1>(GetParam()), /*max_issuance=*/1)); + public_metadata(), private_metadata(), /*max_issuance=*/1)); bssl::UniquePtr<uint8_t> free_msg(issue_resp); CBS real_response; @@ -581,15 +641,23 @@ INSTANTIATE_TEST_SUITE_P( TrustTokenAllMetadataTest, TrustTokenMetadataTest, - testing::Combine(testing::Values(TrustTokenProtocolTest::KeyID(0), + testing::Combine(testing::ValuesIn(AllMethods()), + testing::Values(TrustTokenProtocolTest::KeyID(0), TrustTokenProtocolTest::KeyID(1), TrustTokenProtocolTest::KeyID(2)), testing::Bool())); - class TrustTokenBadKeyTest - : public TrustTokenProtocolTest, - public testing::WithParamInterface<std::tuple<bool, int>> {}; + : public TrustTokenProtocolTestBase, + public testing::WithParamInterface< + std::tuple<const TRUST_TOKEN_METHOD *, bool, int>> { + public: + TrustTokenBadKeyTest() + : TrustTokenProtocolTestBase(std::get<0>(GetParam())) {} + + bool private_metadata() { return std::get<1>(GetParam()); } + int corrupted_key() { return std::get<2>(GetParam()); } +}; TEST_P(TrustTokenBadKeyTest, BadKey) { ASSERT_NO_FATAL_FAILURE(SetupContexts()); @@ -603,15 +671,14 @@ struct trust_token_issuer_key_st *key = &issuer->keys[0]; EC_SCALAR *scalars[] = {&key->key.x0, &key->key.y0, &key->key.x1, &key->key.y1, &key->key.xs, &key->key.ys}; - int corrupted_key = std::get<1>(GetParam()); // Corrupt private key scalar. - scalars[corrupted_key]->bytes[0] ^= 42; + scalars[corrupted_key()]->bytes[0] ^= 42; size_t tokens_issued; ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue( issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len, - /*public_metadata=*/7, std::get<0>(GetParam()), /*max_issuance=*/1)); + /*public_metadata=*/7, private_metadata(), /*max_issuance=*/1)); bssl::UniquePtr<uint8_t> free_msg(issue_resp); size_t key_index; bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens( @@ -619,18 +686,18 @@ resp_len)); // If the unused private key is corrupted, then the DLEQ proof should succeed. - if ((corrupted_key / 2 == 0 && std::get<0>(GetParam()) == true) || - (corrupted_key / 2 == 1 && std::get<0>(GetParam()) == false)) { + if ((corrupted_key() / 2 == 0 && private_metadata() == true) || + (corrupted_key() / 2 == 1 && private_metadata() == false)) { ASSERT_TRUE(tokens); } else { ASSERT_FALSE(tokens); } } -INSTANTIATE_TEST_SUITE_P( - TrustTokenAllBadKeyTest, TrustTokenBadKeyTest, - testing::Combine(testing::Bool(), - testing::Values(0, 1, 2, 3, 4, 5))); +INSTANTIATE_TEST_SUITE_P(TrustTokenAllBadKeyTest, TrustTokenBadKeyTest, + testing::Combine(testing::ValuesIn(AllMethods()), + testing::Bool(), + testing::Values(0, 1, 2, 3, 4, 5))); } // namespace BSSL_NAMESPACE_END
diff --git a/include/openssl/trust_token.h b/include/openssl/trust_token.h index e5837cf..4945172 100644 --- a/include/openssl/trust_token.h +++ b/include/openssl/trust_token.h
@@ -40,6 +40,11 @@ // PMBTokens and P-521. OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v0(void); +// TRUST_TOKEN_experiment_v1 is an experimental Trust Tokens protocol using +// PMBTokens and P-384. This version is still under developement and should not +// be used yet. +OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v1(void); + // trust_token_st represents a single-use token for the Trust Token protocol. // For the client, this is the token and its corresponding signature. For the // issuer, this is the token itself.
diff --git a/tool/speed.cc b/tool/speed.cc index ac71043..2c36c80 100644 --- a/tool/speed.cc +++ b/tool/speed.cc
@@ -1378,6 +1378,10 @@ !SpeedTrustToken("TrustToken-Exp0-Batch1", TRUST_TOKEN_experiment_v0(), 1, selected) || !SpeedTrustToken("TrustToken-Exp0-Batch10", TRUST_TOKEN_experiment_v0(), + 10, selected) || + !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, + selected) || + !SpeedTrustToken("TrustToken-Exp1-Batch10", TRUST_TOKEN_experiment_v1(), 10, selected)) { return false; }