Update TrustTokenV2 to use VOPRFs and assemble RR. Change-Id: I2f1f6b187bf42ebfdb61def73726d95740a9d55c Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42965 Commit-Queue: Steven Valdez <svaldez@google.com> Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index a872626..2771768 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt
@@ -340,6 +340,7 @@ thread_win.c trust_token/pmbtoken.c trust_token/trust_token.c + trust_token/voprf.c x509/a_digest.c x509/a_sign.c x509/a_strex.c
diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h index c935888..0aa1936 100644 --- a/crypto/trust_token/internal.h +++ b/crypto/trust_token/internal.h
@@ -30,16 +30,20 @@ #endif -// PMBTokens. -// -// 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. +// For the following cryptographic schemes, we use P-384 instead of our usual +// choice of P-256. See Appendix I of +// https://eprint.iacr.org/2020/072/20200324:214215 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. -// PMBTOKEN_NONCE_SIZE is the size of nonces used as part of the PMBToken + +// TRUST_TOKEN_NONCE_SIZE is the size of nonces used as part of the Trust_Token // protocol. -#define PMBTOKEN_NONCE_SIZE 64 +#define TRUST_TOKEN_NONCE_SIZE 64 typedef struct { // TODO(https://crbug.com/boringssl/334): These should store |EC_PRECOMP| so @@ -47,7 +51,7 @@ EC_AFFINE pub0; EC_AFFINE pub1; EC_AFFINE pubs; -} PMBTOKEN_CLIENT_KEY; +} TRUST_TOKEN_CLIENT_KEY; typedef struct { EC_SCALAR x0; @@ -62,47 +66,47 @@ EC_PRECOMP pub1_precomp; EC_AFFINE pubs; EC_PRECOMP pubs_precomp; -} PMBTOKEN_ISSUER_KEY; +} TRUST_TOKEN_ISSUER_KEY; -// PMBTOKEN_PRETOKEN represents the intermediate state a client keeps during a -// PMBToken issuance operation. +// TRUST_TOKEN_PRETOKEN represents the intermediate state a client keeps during +// a Trust_Token issuance operation. typedef struct pmb_pretoken_st { - uint8_t t[PMBTOKEN_NONCE_SIZE]; + uint8_t t[TRUST_TOKEN_NONCE_SIZE]; EC_SCALAR r; EC_AFFINE Tp; -} PMBTOKEN_PRETOKEN; +} TRUST_TOKEN_PRETOKEN; -// PMBTOKEN_PRETOKEN_free releases the memory associated with |token|. -OPENSSL_EXPORT void PMBTOKEN_PRETOKEN_free(PMBTOKEN_PRETOKEN *token); +// TRUST_TOKEN_PRETOKEN_free releases the memory associated with |token|. +OPENSSL_EXPORT void TRUST_TOKEN_PRETOKEN_free(TRUST_TOKEN_PRETOKEN *token); -DEFINE_STACK_OF(PMBTOKEN_PRETOKEN) +DEFINE_STACK_OF(TRUST_TOKEN_PRETOKEN) + + +// PMBTokens. +// +// 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. // 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, +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(PMBTOKEN_ISSUER_KEY *key, +int pmbtoken_exp1_issuer_key_from_bytes(TRUST_TOKEN_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, +STACK_OF(TRUST_TOKEN_PRETOKEN) * pmbtoken_exp1_blind(CBB *cbb, size_t count); +int pmbtoken_exp1_sign(const TRUST_TOKEN_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, + pmbtoken_exp1_unblind(const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_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], +int pmbtoken_exp1_read(const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], uint8_t *out_private_metadata, const uint8_t *token, size_t token_len); @@ -113,29 +117,21 @@ // The following functions implement the corresponding |TRUST_TOKENS_METHOD| // functions for |TRUST_TOKENS_experiment_v2|'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_exp2_generate_key(CBB *out_private, CBB *out_public); -int pmbtoken_exp2_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, +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(PMBTOKEN_ISSUER_KEY *key, +int pmbtoken_exp2_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key, const uint8_t *in, size_t len); -STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp2_blind(CBB *cbb, size_t count); -int pmbtoken_exp2_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, +STACK_OF(TRUST_TOKEN_PRETOKEN) * pmbtoken_exp2_blind(CBB *cbb, size_t count); +int pmbtoken_exp2_sign(const TRUST_TOKEN_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_exp2_unblind(const PMBTOKEN_CLIENT_KEY *key, - const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, + pmbtoken_exp2_unblind(const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, CBS *cbs, size_t count, uint32_t key_id); -int pmbtoken_exp2_read(const PMBTOKEN_ISSUER_KEY *key, - uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], +int pmbtoken_exp2_read(const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], uint8_t *out_private_metadata, const uint8_t *token, size_t token_len); @@ -144,6 +140,37 @@ OPENSSL_EXPORT int pmbtoken_exp2_get_h_for_testing(uint8_t out[97]); +// VOPRF. +// +// VOPRFs are described in https://tools.ietf.org/html/draft-irtf-cfrg-voprf-04 +// and provide anonymous tokens. This implementation uses TrustToken DSTs and +// the DLEQ batching primitive from +// https://eprint.iacr.org/2020/072/20200324:214215. +// VOPRF only uses the |pub|' field of the TRUST_TOKEN_CLIENT_KEY and +// |xs|/|pubs| fields of the TRUST_TOKEN_ISSUER_KEY. + +// The following functions implement the corresponding |TRUST_TOKENS_METHOD| +// 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_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, + const uint8_t *in, size_t len); +STACK_OF(TRUST_TOKEN_PRETOKEN) * voprf_exp2_blind(CBB *cbb, size_t count); +int voprf_exp2_sign(const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata); +STACK_OF(TRUST_TOKEN) * + voprf_exp2_unblind(const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, + CBS *cbs, size_t count, uint32_t key_id); +int voprf_exp2_read(const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len); + + // Trust Tokens internals. struct trust_token_method_st { @@ -155,23 +182,23 @@ // 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. - int (*client_key_from_bytes)(PMBTOKEN_CLIENT_KEY *key, const uint8_t *in, + int (*client_key_from_bytes)(TRUST_TOKEN_CLIENT_KEY *key, const uint8_t *in, size_t len); // issuer_key_from_bytes decodes a issuer key from |in| and sets |key| // to the resulting key. It returns one on success and zero // on failure. - int (*issuer_key_from_bytes)(PMBTOKEN_ISSUER_KEY *key, const uint8_t *in, + int (*issuer_key_from_bytes)(TRUST_TOKEN_ISSUER_KEY *key, const uint8_t *in, size_t len); // blind generates a new issuance request for |count| tokens. On - // success, it returns a newly-allocated |STACK_OF(PMBTOKEN_PRETOKEN)| and + // success, it returns a newly-allocated |STACK_OF(TRUST_TOKEN_PRETOKEN)| and // writes a request to the issuer to |cbb|. On failure, it returns NULL. The - // |STACK_OF(PMBTOKEN_PRETOKEN)|s should be passed to |pmbtoken_unblind| when + // |STACK_OF(TRUST_TOKEN_PRETOKEN)|s should be passed to |pmbtoken_unblind| when // the server responds. // // This function implements the AT.Usr0 operation. - STACK_OF(PMBTOKEN_PRETOKEN) *(*blind)(CBB *cbb, size_t count); + STACK_OF(TRUST_TOKEN_PRETOKEN) * (*blind)(CBB *cbb, size_t count); // sign parses a request for |num_requested| tokens from |cbs| and // issues |num_to_issue| tokens with |key| and a private metadata value of @@ -179,7 +206,7 @@ // success and zero on failure. // // This function implements the AT.Sig operation. - int (*sign)(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + int (*sign)(const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, size_t num_requested, size_t num_to_issue, uint8_t private_metadata); @@ -192,8 +219,8 @@ // // This function implements the AT.Usr1 operation. STACK_OF(TRUST_TOKEN) * - (*unblind)(const PMBTOKEN_CLIENT_KEY *key, - const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, CBS *cbs, + (*unblind)(const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, CBS *cbs, size_t count, uint32_t key_id); // read parses a PMBToken from |token| and verifies it using |key|. On @@ -201,8 +228,8 @@ // |out_nonce| and |*out_private_metadata|. Otherwise, it returns zero. Note // that, unlike the output of |unblind|, |token| does not have a // four-byte key ID prepended. - int (*read)(const PMBTOKEN_ISSUER_KEY *key, - uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], + int (*read)(const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], uint8_t *out_private_metadata, const uint8_t *token, size_t token_len); @@ -219,14 +246,14 @@ // Structure representing a single Trust Token public key with the specified ID. struct trust_token_client_key_st { uint32_t id; - PMBTOKEN_CLIENT_KEY key; + TRUST_TOKEN_CLIENT_KEY key; }; // Structure representing a single Trust Token private key with the specified // ID. struct trust_token_issuer_key_st { uint32_t id; - PMBTOKEN_ISSUER_KEY key; + TRUST_TOKEN_ISSUER_KEY key; }; struct trust_token_client_st { @@ -243,7 +270,7 @@ size_t num_keys; // pretokens is the intermediate state during an active issuance. - STACK_OF(PMBTOKEN_PRETOKEN)* pretokens; + STACK_OF(TRUST_TOKEN_PRETOKEN)* pretokens; // srr_key is the public key used to verify the signature of the SRR. EVP_PKEY *srr_key; @@ -281,7 +308,7 @@ BSSL_NAMESPACE_BEGIN -BORINGSSL_MAKE_DELETER(PMBTOKEN_PRETOKEN, PMBTOKEN_PRETOKEN_free) +BORINGSSL_MAKE_DELETER(TRUST_TOKEN_PRETOKEN, TRUST_TOKEN_PRETOKEN_free) BSSL_NAMESPACE_END
diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c index f9132e6..a6549b9 100644 --- a/crypto/trust_token/pmbtoken.c +++ b/crypto/trust_token/pmbtoken.c
@@ -31,10 +31,10 @@ typedef int (*hash_t_func_t)(const EC_GROUP *group, EC_RAW_POINT *out, - const uint8_t t[PMBTOKEN_NONCE_SIZE]); + const uint8_t t[TRUST_TOKEN_NONCE_SIZE]); typedef int (*hash_s_func_t)(const EC_GROUP *group, EC_RAW_POINT *out, const EC_AFFINE *t, - const uint8_t s[PMBTOKEN_NONCE_SIZE]); + 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); @@ -165,10 +165,6 @@ scalars, 3); } -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; @@ -211,7 +207,7 @@ } static int pmbtoken_client_key_from_bytes(const PMBTOKEN_METHOD *method, - PMBTOKEN_CLIENT_KEY *key, + TRUST_TOKEN_CLIENT_KEY *key, const uint8_t *in, size_t len) { CBS cbs; CBS_init(&cbs, in, len); @@ -230,7 +226,7 @@ } static int pmbtoken_issuer_key_from_bytes(const PMBTOKEN_METHOD *method, - PMBTOKEN_ISSUER_KEY *key, + TRUST_TOKEN_ISSUER_KEY *key, const uint8_t *in, size_t len) { const EC_GROUP *group = method->group; CBS cbs, tmp; @@ -269,10 +265,10 @@ return 1; } -static STACK_OF(PMBTOKEN_PRETOKEN) * +static STACK_OF(TRUST_TOKEN_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(); + STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens = sk_TRUST_TOKEN_PRETOKEN_new_null(); if (pretokens == NULL) { OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); goto err; @@ -280,11 +276,11 @@ 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)); + TRUST_TOKEN_PRETOKEN *pretoken = OPENSSL_malloc(sizeof(TRUST_TOKEN_PRETOKEN)); if (pretoken == NULL || - !sk_PMBTOKEN_PRETOKEN_push(pretokens, pretoken)) { + !sk_TRUST_TOKEN_PRETOKEN_push(pretokens, pretoken)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - PMBTOKEN_PRETOKEN_free(pretoken); + TRUST_TOKEN_PRETOKEN_free(pretoken); goto err; } @@ -319,7 +315,7 @@ return pretokens; err: - sk_PMBTOKEN_PRETOKEN_pop_free(pretokens, PMBTOKEN_PRETOKEN_free); + sk_TRUST_TOKEN_PRETOKEN_pop_free(pretokens, TRUST_TOKEN_PRETOKEN_free); return NULL; } @@ -455,9 +451,10 @@ // 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 TRUST_TOKEN_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; // We generate a DLEQ proof for the validity token and a DLEQOR2 proof for the @@ -616,7 +613,7 @@ } static int dleq_verify(const PMBTOKEN_METHOD *method, CBS *cbs, - const PMBTOKEN_CLIENT_KEY *pub, const EC_RAW_POINT *T, + const TRUST_TOKEN_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; @@ -735,7 +732,7 @@ } static int pmbtoken_sign(const PMBTOKEN_METHOD *method, - const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + const TRUST_TOKEN_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; @@ -785,8 +782,8 @@ 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); + uint8_t s[TRUST_TOKEN_NONCE_SIZE]; + RAND_bytes(s, TRUST_TOKEN_NONCE_SIZE); // The |jacobians| and |affines| contain Sp, Wp, and Wsp. EC_RAW_POINT jacobians[3]; EC_AFFINE affines[3]; @@ -796,9 +793,11 @@ !ec_point_mul_scalar_batch(group, &jacobians[2], &Tp, &key->xs, &jacobians[0], &key->ys, NULL, NULL) || !ec_jacobian_to_affine_batch(group, affines, jacobians, 3) || - !CBB_add_bytes(cbb, s, PMBTOKEN_NONCE_SIZE) || - !cbb_add_prefixed_point(cbb, group, &affines[1], method->prefix_point) || - !cbb_add_prefixed_point(cbb, group, &affines[2], method->prefix_point)) { + !CBB_add_bytes(cbb, s, TRUST_TOKEN_NONCE_SIZE) || + !cbb_add_prefixed_point(cbb, group, &affines[1], + method->prefix_point) || + !cbb_add_prefixed_point(cbb, group, &affines[2], + method->prefix_point)) { goto err; } @@ -877,11 +876,11 @@ static STACK_OF(TRUST_TOKEN) * pmbtoken_unblind(const PMBTOKEN_METHOD *method, - const PMBTOKEN_CLIENT_KEY *key, - const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, CBS *cbs, + const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, CBS *cbs, size_t count, uint32_t key_id) { const EC_GROUP *group = method->group; - if (count > sk_PMBTOKEN_PRETOKEN_num(pretokens)) { + if (count > sk_TRUST_TOKEN_PRETOKEN_num(pretokens)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); return NULL; } @@ -919,12 +918,12 @@ } for (size_t i = 0; i < count; i++) { - const PMBTOKEN_PRETOKEN *pretoken = - sk_PMBTOKEN_PRETOKEN_value(pretokens, i); + const TRUST_TOKEN_PRETOKEN *pretoken = + sk_TRUST_TOKEN_PRETOKEN_value(pretokens, i); - uint8_t s[PMBTOKEN_NONCE_SIZE]; + uint8_t s[TRUST_TOKEN_NONCE_SIZE]; EC_AFFINE Wp_affine, Wsp_affine; - if (!CBS_copy_bytes(cbs, s, PMBTOKEN_NONCE_SIZE) || + if (!CBS_copy_bytes(cbs, s, TRUST_TOKEN_NONCE_SIZE) || !cbs_get_prefixed_point(cbs, group, &Wp_affine, method->prefix_point) || !cbs_get_prefixed_point(cbs, group, &Wsp_affine, method->prefix_point)) { @@ -963,9 +962,10 @@ // above. CBB token_cbb; size_t point_len = 1 + 2 * BN_num_bytes(&group->field); - if (!CBB_init(&token_cbb, 4 + PMBTOKEN_NONCE_SIZE + 3 * (2 + point_len)) || + if (!CBB_init(&token_cbb, + 4 + TRUST_TOKEN_NONCE_SIZE + 3 * (2 + point_len)) || !CBB_add_u32(&token_cbb, key_id) || - !CBB_add_bytes(&token_cbb, pretoken->t, PMBTOKEN_NONCE_SIZE) || + !CBB_add_bytes(&token_cbb, pretoken->t, TRUST_TOKEN_NONCE_SIZE) || !cbb_add_prefixed_point(&token_cbb, group, &affines[0], method->prefix_point) || !cbb_add_prefixed_point(&token_cbb, group, &affines[1], @@ -1034,15 +1034,15 @@ } static int pmbtoken_read(const PMBTOKEN_METHOD *method, - const PMBTOKEN_ISSUER_KEY *key, - uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], + const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_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_AFFINE S, W, Ws; - if (!CBS_copy_bytes(&cbs, out_nonce, PMBTOKEN_NONCE_SIZE) || + if (!CBS_copy_bytes(&cbs, out_nonce, TRUST_TOKEN_NONCE_SIZE) || !cbs_get_prefixed_point(&cbs, group, &S, method->prefix_point) || !cbs_get_prefixed_point(&cbs, group, &W, method->prefix_point) || !cbs_get_prefixed_point(&cbs, group, &Ws, method->prefix_point) || @@ -1101,15 +1101,15 @@ // PMBTokens experiment v1. static int pmbtoken_exp1_hash_t(const EC_GROUP *group, EC_RAW_POINT *out, - const uint8_t t[PMBTOKEN_NONCE_SIZE]) { + const uint8_t t[TRUST_TOKEN_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); + group, out, kHashTLabel, sizeof(kHashTLabel), t, TRUST_TOKEN_NONCE_SIZE); } static int pmbtoken_exp1_hash_s(const EC_GROUP *group, EC_RAW_POINT *out, const EC_AFFINE *t, - const uint8_t s[PMBTOKEN_NONCE_SIZE]) { + const uint8_t s[TRUST_TOKEN_NONCE_SIZE]) { const uint8_t kHashSLabel[] = "PMBTokens Experiment V1 HashS"; int ret = 0; CBB cbb; @@ -1117,7 +1117,7 @@ size_t len; if (!CBB_init(&cbb, 0) || !point_to_cbb(&cbb, group, t) || - !CBB_add_bytes(&cbb, s, PMBTOKEN_NONCE_SIZE) || + !CBB_add_bytes(&cbb, s, TRUST_TOKEN_NONCE_SIZE) || !CBB_finish(&cbb, &buf, &len) || !ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( group, out, kHashSLabel, sizeof(kHashSLabel), buf, len)) { @@ -1182,7 +1182,7 @@ return pmbtoken_generate_key(&pmbtoken_exp1_method, out_private, out_public); } -int pmbtoken_exp1_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, +int pmbtoken_exp1_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key, const uint8_t *in, size_t len) { if (!pmbtoken_exp1_init_method()) { return 0; @@ -1190,7 +1190,7 @@ return pmbtoken_client_key_from_bytes(&pmbtoken_exp1_method, key, in, len); } -int pmbtoken_exp1_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, +int pmbtoken_exp1_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key, const uint8_t *in, size_t len) { if (!pmbtoken_exp1_init_method()) { return 0; @@ -1198,14 +1198,14 @@ return pmbtoken_issuer_key_from_bytes(&pmbtoken_exp1_method, key, in, len); } -STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp1_blind(CBB *cbb, size_t count) { +STACK_OF(TRUST_TOKEN_PRETOKEN) * pmbtoken_exp1_blind(CBB *cbb, size_t count) { if (!pmbtoken_exp1_init_method()) { return NULL; } return pmbtoken_blind(&pmbtoken_exp1_method, cbb, count); } -int pmbtoken_exp1_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, +int pmbtoken_exp1_sign(const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, size_t num_requested, size_t num_to_issue, uint8_t private_metadata) { if (!pmbtoken_exp1_init_method()) { @@ -1216,8 +1216,8 @@ } STACK_OF(TRUST_TOKEN) * - pmbtoken_exp1_unblind(const PMBTOKEN_CLIENT_KEY *key, - const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, + pmbtoken_exp1_unblind(const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, CBS *cbs, size_t count, uint32_t key_id) { if (!pmbtoken_exp1_init_method()) { return NULL; @@ -1226,8 +1226,8 @@ key_id); } -int pmbtoken_exp1_read(const PMBTOKEN_ISSUER_KEY *key, - uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], +int pmbtoken_exp1_read(const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], uint8_t *out_private_metadata, const uint8_t *token, size_t token_len) { if (!pmbtoken_exp1_init_method()) { @@ -1251,15 +1251,15 @@ // PMBTokens experiment v2. static int pmbtoken_exp2_hash_t(const EC_GROUP *group, EC_RAW_POINT *out, - const uint8_t t[PMBTOKEN_NONCE_SIZE]) { + const uint8_t t[TRUST_TOKEN_NONCE_SIZE]) { const uint8_t kHashTLabel[] = "PMBTokens Experiment V2 HashT"; return ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( - group, out, kHashTLabel, sizeof(kHashTLabel), t, PMBTOKEN_NONCE_SIZE); + group, out, kHashTLabel, sizeof(kHashTLabel), t, TRUST_TOKEN_NONCE_SIZE); } static int pmbtoken_exp2_hash_s(const EC_GROUP *group, EC_RAW_POINT *out, const EC_AFFINE *t, - const uint8_t s[PMBTOKEN_NONCE_SIZE]) { + const uint8_t s[TRUST_TOKEN_NONCE_SIZE]) { const uint8_t kHashSLabel[] = "PMBTokens Experiment V2 HashS"; int ret = 0; CBB cbb; @@ -1267,7 +1267,7 @@ size_t len; if (!CBB_init(&cbb, 0) || !point_to_cbb(&cbb, group, t) || - !CBB_add_bytes(&cbb, s, PMBTOKEN_NONCE_SIZE) || + !CBB_add_bytes(&cbb, s, TRUST_TOKEN_NONCE_SIZE) || !CBB_finish(&cbb, &buf, &len) || !ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( group, out, kHashSLabel, sizeof(kHashSLabel), buf, len)) { @@ -1332,7 +1332,7 @@ return pmbtoken_generate_key(&pmbtoken_exp2_method, out_private, out_public); } -int pmbtoken_exp2_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, +int pmbtoken_exp2_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key, const uint8_t *in, size_t len) { if (!pmbtoken_exp2_init_method()) { return 0; @@ -1340,7 +1340,7 @@ return pmbtoken_client_key_from_bytes(&pmbtoken_exp2_method, key, in, len); } -int pmbtoken_exp2_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, +int pmbtoken_exp2_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key, const uint8_t *in, size_t len) { if (!pmbtoken_exp2_init_method()) { return 0; @@ -1348,14 +1348,14 @@ return pmbtoken_issuer_key_from_bytes(&pmbtoken_exp2_method, key, in, len); } -STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp2_blind(CBB *cbb, size_t count) { +STACK_OF(TRUST_TOKEN_PRETOKEN) * pmbtoken_exp2_blind(CBB *cbb, size_t count) { if (!pmbtoken_exp2_init_method()) { return NULL; } return pmbtoken_blind(&pmbtoken_exp2_method, cbb, count); } -int pmbtoken_exp2_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, +int pmbtoken_exp2_sign(const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, size_t num_requested, size_t num_to_issue, uint8_t private_metadata) { if (!pmbtoken_exp2_init_method()) { @@ -1366,8 +1366,8 @@ } STACK_OF(TRUST_TOKEN) * - pmbtoken_exp2_unblind(const PMBTOKEN_CLIENT_KEY *key, - const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, + pmbtoken_exp2_unblind(const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, CBS *cbs, size_t count, uint32_t key_id) { if (!pmbtoken_exp2_init_method()) { return NULL; @@ -1376,8 +1376,8 @@ key_id); } -int pmbtoken_exp2_read(const PMBTOKEN_ISSUER_KEY *key, - uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], +int pmbtoken_exp2_read(const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], uint8_t *out_private_metadata, const uint8_t *token, size_t token_len) { if (!pmbtoken_exp2_init_method()) {
diff --git a/crypto/trust_token/trust_token.c b/crypto/trust_token/trust_token.c index fea619e..a4891d8 100644 --- a/crypto/trust_token/trust_token.c +++ b/crypto/trust_token/trust_token.c
@@ -43,15 +43,15 @@ return &kMethod; } -const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_pp(void) { +const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_voprf(void) { static const TRUST_TOKEN_METHOD kMethod = { - pmbtoken_exp2_generate_key, - pmbtoken_exp2_client_key_from_bytes, - pmbtoken_exp2_issuer_key_from_bytes, - pmbtoken_exp2_blind, - pmbtoken_exp2_sign, - pmbtoken_exp2_unblind, - pmbtoken_exp2_read, + voprf_exp2_generate_key, + voprf_exp2_client_key_from_bytes, + voprf_exp2_issuer_key_from_bytes, + voprf_exp2_blind, + voprf_exp2_sign, + voprf_exp2_unblind, + voprf_exp2_read, 0, /* has_private_metadata */ 6, /* max_keys */ 0, /* has_srr */ @@ -75,6 +75,10 @@ return &kMethod; } +void TRUST_TOKEN_PRETOKEN_free(TRUST_TOKEN_PRETOKEN *pretoken) { + OPENSSL_free(pretoken); +} + TRUST_TOKEN *TRUST_TOKEN_new(const uint8_t *data, size_t len) { TRUST_TOKEN *ret = OPENSSL_malloc(sizeof(TRUST_TOKEN)); if (ret == NULL) { @@ -160,7 +164,7 @@ return; } EVP_PKEY_free(ctx->srr_key); - sk_PMBTOKEN_PRETOKEN_pop_free(ctx->pretokens, PMBTOKEN_PRETOKEN_free); + sk_TRUST_TOKEN_PRETOKEN_pop_free(ctx->pretokens, TRUST_TOKEN_PRETOKEN_free); OPENSSL_free(ctx); } @@ -206,7 +210,7 @@ int ret = 0; CBB request; - STACK_OF(PMBTOKEN_PRETOKEN) *pretokens = NULL; + STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens = NULL; if (!CBB_init(&request, 0) || !CBB_add_u16(&request, count)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); @@ -223,14 +227,14 @@ goto err; } - sk_PMBTOKEN_PRETOKEN_pop_free(ctx->pretokens, PMBTOKEN_PRETOKEN_free); + sk_TRUST_TOKEN_PRETOKEN_pop_free(ctx->pretokens, TRUST_TOKEN_PRETOKEN_free); ctx->pretokens = pretokens; pretokens = NULL; ret = 1; err: CBB_cleanup(&request); - sk_PMBTOKEN_PRETOKEN_pop_free(pretokens, PMBTOKEN_PRETOKEN_free); + sk_TRUST_TOKEN_PRETOKEN_pop_free(pretokens, TRUST_TOKEN_PRETOKEN_free); return ret; } @@ -264,7 +268,7 @@ return NULL; } - if (count > sk_PMBTOKEN_PRETOKEN_num(ctx->pretokens)) { + if (count > sk_TRUST_TOKEN_PRETOKEN_num(ctx->pretokens)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); return NULL; } @@ -281,7 +285,7 @@ return NULL; } - sk_PMBTOKEN_PRETOKEN_pop_free(ctx->pretokens, PMBTOKEN_PRETOKEN_free); + sk_TRUST_TOKEN_PRETOKEN_pop_free(ctx->pretokens, TRUST_TOKEN_PRETOKEN_free); ctx->pretokens = NULL; *out_key_index = key_index; @@ -315,30 +319,39 @@ size_t response_len) { CBS in, srr, sig; CBS_init(&in, response, response_len); + if (!ctx->method->has_srr) { + if (!CBS_stow(&in, out_rr, out_rr_len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return 0; + } + + *out_sig = NULL; + *out_sig_len = 0; + return 1; + } + if (!CBS_get_u16_length_prefixed(&in, &srr) || - !CBS_get_u16_length_prefixed(&in, &sig)) { + !CBS_get_u16_length_prefixed(&in, &sig) || + CBS_len(&in) != 0) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_ERROR); return 0; } - if (ctx->method->has_srr) { - if (ctx->srr_key == NULL) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_NO_SRR_KEY_CONFIGURED); - return 0; - } + if (ctx->srr_key == NULL) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_NO_SRR_KEY_CONFIGURED); + return 0; + } - EVP_MD_CTX md_ctx; - EVP_MD_CTX_init(&md_ctx); - int sig_ok = - EVP_DigestVerifyInit(&md_ctx, NULL, NULL, NULL, ctx->srr_key) && - EVP_DigestVerify(&md_ctx, CBS_data(&sig), CBS_len(&sig), CBS_data(&srr), - CBS_len(&srr)); - EVP_MD_CTX_cleanup(&md_ctx); + EVP_MD_CTX md_ctx; + EVP_MD_CTX_init(&md_ctx); + int sig_ok = EVP_DigestVerifyInit(&md_ctx, NULL, NULL, NULL, ctx->srr_key) && + EVP_DigestVerify(&md_ctx, CBS_data(&sig), CBS_len(&sig), + CBS_data(&srr), CBS_len(&srr)); + EVP_MD_CTX_cleanup(&md_ctx); - if (!sig_ok) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_SRR_SIGNATURE_ERROR); - return 0; - } + if (!sig_ok) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_SRR_SIGNATURE_ERROR); + return 0; } uint8_t *srr_buf = NULL, *sig_buf = NULL; @@ -588,7 +601,7 @@ const struct trust_token_issuer_key_st *key = trust_token_issuer_get_key(ctx, public_metadata); - uint8_t nonce[PMBTOKEN_NONCE_SIZE]; + uint8_t nonce[TRUST_TOKEN_NONCE_SIZE]; if (key == NULL || !ctx->method->read(&key->key, nonce, &private_metadata, CBS_data(&token_cbs), CBS_len(&token_cbs))) { @@ -672,16 +685,56 @@ goto err; } - CBB child; - uint8_t *ptr; - if (!CBB_add_u16_length_prefixed(&response, &child) || - !CBB_add_bytes(&child, srr_buf, srr_len) || - !CBB_add_u16_length_prefixed(&response, &child) || - !CBB_reserve(&child, &ptr, sig_len) || - !EVP_DigestSign(&md_ctx, ptr, &sig_len, srr_buf, srr_len) || - !CBB_did_write(&child, sig_len)) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); - goto err; + // Merge SRR and Signature into single string. + // TODO(svaldez): Expose API to construct this from the caller. + if (!ctx->method->has_srr) { + static const char kSRRHeader[] = "body=:"; + static const char kSRRSplit[] = ":, signature=:"; + static const char kSRREnd[] = ":"; + + size_t srr_b64_len, sig_b64_len; + if (!EVP_EncodedLength(&srr_b64_len, srr_len) || + !EVP_EncodedLength(&sig_b64_len, sig_len)) { + goto err; + } + + sig_buf = OPENSSL_malloc(sig_len); + uint8_t *srr_b64_buf = OPENSSL_malloc(srr_b64_len); + uint8_t *sig_b64_buf = OPENSSL_malloc(sig_b64_len); + if (!sig_buf || + !srr_b64_buf || + !sig_b64_buf || + !EVP_DigestSign(&md_ctx, sig_buf, &sig_len, srr_buf, srr_len) || + !CBB_add_bytes(&response, (const uint8_t *)kSRRHeader, + strlen(kSRRHeader)) || + !CBB_add_bytes(&response, srr_b64_buf, + EVP_EncodeBlock(srr_b64_buf, srr_buf, srr_len)) || + !CBB_add_bytes(&response, (const uint8_t *)kSRRSplit, + strlen(kSRRSplit)) || + !CBB_add_bytes(&response, sig_b64_buf, + EVP_EncodeBlock(sig_b64_buf, sig_buf, sig_len)) || + !CBB_add_bytes(&response, (const uint8_t *)kSRREnd, strlen(kSRREnd))) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + OPENSSL_free(srr_b64_buf); + OPENSSL_free(sig_b64_buf); + goto err; + } + + OPENSSL_free(srr_b64_buf); + OPENSSL_free(sig_b64_buf); + } else { + CBB child; + uint8_t *ptr; + if (!CBB_add_u16_length_prefixed(&response, &child) || + !CBB_add_bytes(&child, srr_buf, srr_len) || + !CBB_add_u16_length_prefixed(&response, &child) || + !CBB_reserve(&child, &ptr, sig_len) || + !EVP_DigestSign(&md_ctx, ptr, &sig_len, srr_buf, srr_len) || + !CBB_did_write(&child, sig_len) || + !CBB_flush(&response)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } } if (!CBS_stow(&client_data, &client_data_buf, &client_data_len) || @@ -690,7 +743,7 @@ goto err; } - TRUST_TOKEN *token = TRUST_TOKEN_new(nonce, PMBTOKEN_NONCE_SIZE); + TRUST_TOKEN *token = TRUST_TOKEN_new(nonce, TRUST_TOKEN_NONCE_SIZE); if (token == NULL) { OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); goto err;
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc index b282500..7f9b79e 100644 --- a/crypto/trust_token/trust_token_test.cc +++ b/crypto/trust_token/trust_token_test.cc
@@ -56,16 +56,16 @@ ASSERT_EQ(301u, pub_key_len); } -TEST(TrustTokenTest, KeyGenExp2PP) { +TEST(TrustTokenTest, KeyGenExp2VOPRF) { 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_v2_pp(), priv_key, &priv_key_len, + 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)); - ASSERT_EQ(292u, priv_key_len); - ASSERT_EQ(295u, pub_key_len); + ASSERT_EQ(52u, priv_key_len); + ASSERT_EQ(101u, pub_key_len); } TEST(TrustTokenTest, KeyGenExp2PMB) { @@ -127,7 +127,7 @@ static std::vector<const TRUST_TOKEN_METHOD *> AllMethods() { return { TRUST_TOKEN_experiment_v1(), - TRUST_TOKEN_experiment_v2_pp(), + TRUST_TOKEN_experiment_v2_voprf(), TRUST_TOKEN_experiment_v2_pmb() }; } @@ -389,10 +389,14 @@ Bytes(client_data, client_data_len)); resp_len = 10; + // If the protocol doesn't use SRRs, TRUST_TOKEN_CLIENT_finish_redemtpion + // leaves all SRR validation to the caller. uint8_t *srr = NULL, *sig = NULL; size_t srr_len, sig_len; - ASSERT_FALSE(TRUST_TOKEN_CLIENT_finish_redemption( - client.get(), &srr, &srr_len, &sig, &sig_len, redeem_resp, resp_len)); + bool expect_failure = !method()->has_srr; + ASSERT_EQ(expect_failure, TRUST_TOKEN_CLIENT_finish_redemption( + client.get(), &srr, &srr_len, &sig, &sig_len, + redeem_resp, resp_len)); bssl::UniquePtr<uint8_t> free_srr(srr); bssl::UniquePtr<uint8_t> free_sig(sig); } @@ -534,6 +538,27 @@ bssl::UniquePtr<uint8_t> free_srr(srr); bssl::UniquePtr<uint8_t> free_sig(sig); + if (!method()->has_srr) { + size_t b64_len; + ASSERT_TRUE(EVP_EncodedLength(&b64_len, sizeof(kExpectedSRR) - 1)); + b64_len -= 1; + + const char kSRRHeader[] = "body=:"; + ASSERT_LT(sizeof(kSRRHeader) - 1 + b64_len, srr_len); + + ASSERT_EQ(Bytes(kSRRHeader, sizeof(kSRRHeader) - 1), + Bytes(srr, sizeof(kSRRHeader) - 1)); + uint8_t *decoded_srr = + (uint8_t *)OPENSSL_malloc(sizeof(kExpectedSRR) + 1); + ASSERT_TRUE(decoded_srr); + ASSERT_LT( + int(sizeof(kExpectedSRR) - 1), + EVP_DecodeBlock(decoded_srr, srr + sizeof(kSRRHeader) - 1, b64_len)); + srr = decoded_srr; + srr_len = sizeof(kExpectedSRR) - 1; + free_srr.reset(srr); + } + const uint8_t kTokenHashDSTLabel[] = "TrustTokenV0 TokenHash"; uint8_t token_hash[SHA256_DIGEST_LENGTH]; SHA256_CTX sha_ctx; @@ -547,8 +572,8 @@ uint8_t decode_private_metadata; ASSERT_TRUE(TRUST_TOKEN_decode_private_metadata( - method(), &decode_private_metadata, metadata_key, sizeof(metadata_key), - token_hash, sizeof(token_hash), srr[27])); + method(), &decode_private_metadata, metadata_key, + sizeof(metadata_key), token_hash, sizeof(token_hash), srr[27])); ASSERT_EQ(srr[18], public_metadata()); ASSERT_EQ(decode_private_metadata, private_metadata()); @@ -623,10 +648,13 @@ const EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp384r1); size_t token_length = - PMBTOKEN_NONCE_SIZE + 2 * (1 + 2 * BN_num_bytes(&group->field)); + TRUST_TOKEN_NONCE_SIZE + 2 * (1 + 2 * BN_num_bytes(&group->field)); if (method() == TRUST_TOKEN_experiment_v1()) { token_length += 4; } + if (method() == TRUST_TOKEN_experiment_v2_voprf()) { + token_length = 1 + 2 * BN_num_bytes(&group->field); + } for (size_t i = 0; i < count; i++) { ASSERT_TRUE(CBB_add_bytes(bad_response.get(), CBS_data(&real_response), token_length)); @@ -683,10 +711,13 @@ const EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp384r1); size_t token_length = - PMBTOKEN_NONCE_SIZE + 2 * (1 + 2 * BN_num_bytes(&group->field)); + TRUST_TOKEN_NONCE_SIZE + 2 * (1 + 2 * BN_num_bytes(&group->field)); if (method() == TRUST_TOKEN_experiment_v1()) { token_length += 4; } + if (method() == TRUST_TOKEN_experiment_v2_voprf()) { + token_length = 1 + 2 * BN_num_bytes(&group->field); + } for (size_t i = 0; i < count; i++) { ASSERT_TRUE(CBB_add_bytes(bad_response.get(), CBS_data(&real_response), token_length)); @@ -734,7 +765,11 @@ }; TEST_P(TrustTokenBadKeyTest, BadKey) { - if (!method()->has_private_metadata && private_metadata()) { + // For versions without private metadata, only corruptions of 'xs' (the 4th + // entry in |scalars| below) result in a bad key, as the other scalars are + // unused internally. + if (!method()->has_private_metadata && + (private_metadata() || corrupted_key() != 4)) { return; }
diff --git a/crypto/trust_token/voprf.c b/crypto/trust_token/voprf.c new file mode 100644 index 0000000..f93ee9c --- /dev/null +++ b/crypto/trust_token/voprf.c
@@ -0,0 +1,766 @@ +/* Copyright (c) 2020, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/trust_token.h> + +#include <openssl/bn.h> +#include <openssl/bytestring.h> +#include <openssl/ec.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/nid.h> +#include <openssl/rand.h> + +#include "../ec_extra/internal.h" +#include "../fipsmodule/ec/internal.h" + +#include "internal.h" + + +typedef int (*hash_to_group_func_t)(const EC_GROUP *group, EC_RAW_POINT *out, + const uint8_t t[TRUST_TOKEN_NONCE_SIZE]); +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; + + // hash_to_group implements the HashToGroup operation for VOPRFs. It returns + // one on success and zero on error. + hash_to_group_func_t hash_to_group; + // hash_to_scalar implements the HashToScalar operation for VOPRFs. It returns + // one on success and zero on error. + hash_to_scalar_func_t hash_to_scalar; +} VOPRF_METHOD; + +static const uint8_t kDefaultAdditionalData[32] = {0}; + +static int voprf_init_method(VOPRF_METHOD *method, int curve_nid, + hash_to_group_func_t hash_to_group, + hash_to_scalar_func_t hash_to_scalar) { + method->group = EC_GROUP_new_by_curve_name(curve_nid); + if (method->group == NULL) { + return 0; + } + + method->hash_to_group = hash_to_group; + method->hash_to_scalar = hash_to_scalar; + + return 1; +} + +static int cbb_add_point(CBB *out, const EC_GROUP *group, + const EC_AFFINE *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 && + CBB_flush(out); +} + +static int cbs_get_point(CBS *cbs, const EC_GROUP *group, EC_AFFINE *out) { + CBS child; + size_t plen = 1 + 2 * BN_num_bytes(&group->field); + if (!CBS_get_bytes(cbs, &child, plen) || + !ec_point_from_uncompressed(group, out, CBS_data(&child), + CBS_len(&child))) { + return 0; + } + return 1; +} + +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 voprf_generate_key(const VOPRF_METHOD *method, CBB *out_private, + CBB *out_public) { + 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) || + !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) || + !cbb_add_point(out_public, group, &pub_affine)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL); + return 0; + } + + return 1; +} + +static int voprf_client_key_from_bytes(const VOPRF_METHOD *method, + TRUST_TOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len) { + const EC_GROUP *group = method->group; + if (!ec_point_from_uncompressed(group, &key->pubs, in, len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + return 1; +} + +static int voprf_issuer_key_from_bytes(const VOPRF_METHOD *method, + TRUST_TOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len) { + const EC_GROUP *group = method->group; + if (!ec_scalar_from_bytes(group, &key->xs, in, len)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + // Recompute the public key. + EC_RAW_POINT pub; + if (!ec_point_mul_scalar_base(group, &pub, &key->xs) || + !ec_jacobian_to_affine(group, &key->pubs, &pub)) { + return 0; + } + + return 1; +} + +static STACK_OF(TRUST_TOKEN_PRETOKEN) * + voprf_blind(const VOPRF_METHOD *method, CBB *cbb, size_t count) { + const EC_GROUP *group = method->group; + STACK_OF(TRUST_TOKEN_PRETOKEN) *pretokens = + sk_TRUST_TOKEN_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. + TRUST_TOKEN_PRETOKEN *pretoken = + OPENSSL_malloc(sizeof(TRUST_TOKEN_PRETOKEN)); + if (pretoken == NULL || + !sk_TRUST_TOKEN_PRETOKEN_push(pretokens, pretoken)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + TRUST_TOKEN_PRETOKEN_free(pretoken); + goto err; + } + + RAND_bytes(pretoken->t, sizeof(pretoken->t)); + + // We sample r in Montgomery form to simplify inverting. + EC_SCALAR r; + if (!ec_random_nonzero_scalar(group, &r, + kDefaultAdditionalData)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + // pretoken->r is rinv. + ec_scalar_inv0_montgomery(group, &pretoken->r, &r); + // Convert both out of Montgomery form. + ec_scalar_from_montgomery(group, &r, &r); + ec_scalar_from_montgomery(group, &pretoken->r, &pretoken->r); + + // Tp is the blinded token in the VOPRF protocol. + EC_RAW_POINT P, Tp; + if (!method->hash_to_group(group, &P, pretoken->t) || + !ec_point_mul_scalar(group, &Tp, &P, &r) || + !ec_jacobian_to_affine(group, &pretoken->Tp, &Tp)) { + goto err; + } + + if (!cbb_add_point(cbb, group, &pretoken->Tp)) { + goto err; + } + } + + return pretokens; + +err: + sk_TRUST_TOKEN_PRETOKEN_pop_free(pretokens, TRUST_TOKEN_PRETOKEN_free); + return NULL; +} + +static int hash_to_scalar_dleq(const VOPRF_METHOD *method, EC_SCALAR *out, + const EC_AFFINE *X, const EC_AFFINE *T, + const EC_AFFINE *W, const EC_AFFINE *K0, + const EC_AFFINE *K1) { + static const uint8_t kDLEQLabel[] = "DLEQ"; + + int ok = 0; + CBB cbb; + CBB_zero(&cbb); + uint8_t *buf = NULL; + size_t len; + if (!CBB_init(&cbb, 0) || + !CBB_add_bytes(&cbb, kDLEQLabel, sizeof(kDLEQLabel)) || + !cbb_add_point(&cbb, method->group, X) || + !cbb_add_point(&cbb, method->group, T) || + !cbb_add_point(&cbb, method->group, W) || + !cbb_add_point(&cbb, method->group, K0) || + !cbb_add_point(&cbb, method->group, K1) || + !CBB_finish(&cbb, &buf, &len) || + !method->hash_to_scalar(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_to_scalar_batch(const VOPRF_METHOD *method, EC_SCALAR *out, + const CBB *points, size_t index) { + static const uint8_t kDLEQBatchLabel[] = "DLEQ BATCH"; + if (index > 0xffff) { + // The protocol supports only two-byte batches. + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_OVERFLOW); + return 0; + } + + int ok = 0; + CBB cbb; + CBB_zero(&cbb); + uint8_t *buf = NULL; + size_t len; + if (!CBB_init(&cbb, 0) || + !CBB_add_bytes(&cbb, kDLEQBatchLabel, sizeof(kDLEQBatchLabel)) || + !CBB_add_bytes(&cbb, CBB_data(points), CBB_len(points)) || + !CBB_add_u16(&cbb, (uint16_t)index) || + !CBB_finish(&cbb, &buf, &len) || + !method->hash_to_scalar(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 dleq_generate(const VOPRF_METHOD *method, CBB *cbb, + const TRUST_TOKEN_ISSUER_KEY *priv, + const EC_RAW_POINT *T, const EC_RAW_POINT *W) { + const EC_GROUP *group = method->group; + + enum { + idx_T, + idx_W, + idx_k0, + idx_k1, + num_idx, + }; + EC_RAW_POINT jacobians[num_idx]; + + // Setup the DLEQ proof. + EC_SCALAR r; + if (// r <- Zp + !ec_random_nonzero_scalar(group, &r, kDefaultAdditionalData) || + // k0;k1 = r*(G;T) + !ec_point_mul_scalar_base(group, &jacobians[idx_k0], &r) || + !ec_point_mul_scalar(group, &jacobians[idx_k1], T, &r)) { + return 0; + } + + EC_AFFINE affines[num_idx]; + jacobians[idx_T] = *T; + jacobians[idx_W] = *W; + if (!ec_jacobian_to_affine_batch(group, affines, jacobians, num_idx)) { + return 0; + } + + // Compute c = Hc(...). + EC_SCALAR c; + if (!hash_to_scalar_dleq(method, &c, &priv->pubs, &affines[idx_T], + &affines[idx_W], &affines[idx_k0], + &affines[idx_k1])) { + return 0; + } + + + EC_SCALAR c_mont; + ec_scalar_to_montgomery(group, &c_mont, &c); + + // u = r + c*xs + EC_SCALAR u; + ec_scalar_mul_montgomery(group, &u, &priv->xs, &c_mont); + ec_scalar_add(group, &u, &r, &u); + + // Store DLEQ proof in transcript. + if (!scalar_to_cbb(cbb, group, &c) || + !scalar_to_cbb(cbb, group, &u)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return 0; + } + + return 1; +} + +static int mul_public_2(const EC_GROUP *group, EC_RAW_POINT *out, + const EC_RAW_POINT *p0, const EC_SCALAR *scalar0, + const EC_RAW_POINT *p1, const EC_SCALAR *scalar1) { + EC_RAW_POINT points[2] = {*p0, *p1}; + EC_SCALAR scalars[2] = {*scalar0, *scalar1}; + return ec_point_mul_scalar_public_batch(group, out, /*g_scalar=*/NULL, points, + scalars, 2); +} + +static int dleq_verify(const VOPRF_METHOD *method, CBS *cbs, + const TRUST_TOKEN_CLIENT_KEY *pub, const EC_RAW_POINT *T, + const EC_RAW_POINT *W) { + const EC_GROUP *group = method->group; + + + enum { + idx_T, + idx_W, + idx_k0, + idx_k1, + num_idx, + }; + EC_RAW_POINT jacobians[num_idx]; + + // Decode the DLEQ proof. + EC_SCALAR c, u; + if (!scalar_from_cbs(cbs, group, &c) || + !scalar_from_cbs(cbs, group, &u)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return 0; + } + + // k0;k1 = u*(G;T) - c*(pub;W) + EC_RAW_POINT pubs; + ec_affine_to_jacobian(group, &pubs, &pub->pubs); + EC_SCALAR minus_c; + ec_scalar_neg(group, &minus_c, &c); + if (!ec_point_mul_scalar_public(group, &jacobians[idx_k0], &u, &pubs, + &minus_c) || + !mul_public_2(group, &jacobians[idx_k1], T, &u, W, &minus_c)) { + return 0; + } + + // Check the DLEQ proof. + EC_AFFINE affines[num_idx]; + jacobians[idx_T] = *T; + jacobians[idx_W] = *W; + if (!ec_jacobian_to_affine_batch(group, affines, jacobians, num_idx)) { + return 0; + } + + // Compute c = Hc(...). + EC_SCALAR calculated; + if (!hash_to_scalar_dleq(method, &calculated, &pub->pubs, &affines[idx_T], + &affines[idx_W], &affines[idx_k0], + &affines[idx_k1])) { + return 0; + } + + // 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 voprf_sign(const VOPRF_METHOD *method, + const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue) { + const EC_GROUP *group = method->group; + if (num_requested < num_to_issue) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (num_to_issue > ((size_t)-1) / sizeof(EC_RAW_POINT) || + num_to_issue > ((size_t)-1) / sizeof(EC_SCALAR)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_OVERFLOW); + return 0; + } + + int ret = 0; + EC_RAW_POINT *BTs = OPENSSL_malloc(num_to_issue * sizeof(EC_RAW_POINT)); + EC_RAW_POINT *Zs = OPENSSL_malloc(num_to_issue * sizeof(EC_RAW_POINT)); + EC_SCALAR *es = OPENSSL_malloc(num_to_issue * sizeof(EC_SCALAR)); + CBB batch_cbb; + CBB_zero(&batch_cbb); + if (!BTs || + !Zs || + !es || + !CBB_init(&batch_cbb, 0) || + !cbb_add_point(&batch_cbb, method->group, &key->pubs)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + for (size_t i = 0; i < num_to_issue; i++) { + EC_AFFINE BT_affine, Z_affine; + EC_RAW_POINT BT, Z; + if (!cbs_get_point(cbs, group, &BT_affine)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + goto err; + } + ec_affine_to_jacobian(group, &BT, &BT_affine); + if (!ec_point_mul_scalar(group, &Z, &BT, &key->xs) || + !ec_jacobian_to_affine(group, &Z_affine, &Z) || + !cbb_add_point(cbb, group, &Z_affine)) { + goto err; + } + + if (!cbb_add_point(&batch_cbb, group, &BT_affine) || + !cbb_add_point(&batch_cbb, group, &Z_affine)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + BTs[i] = BT; + Zs[i] = Z; + + if (!CBB_flush(cbb)) { + goto err; + } + } + + // The DLEQ batching construction is described in appendix B of + // https://eprint.iacr.org/2020/072/20200324:214215. Note the additional + // computations all act on public inputs. + for (size_t i = 0; i < num_to_issue; i++) { + if (!hash_to_scalar_batch(method, &es[i], &batch_cbb, i)) { + goto err; + } + } + + EC_RAW_POINT BT_batch, Z_batch; + if (!ec_point_mul_scalar_public_batch(group, &BT_batch, + /*g_scalar=*/NULL, BTs, es, + num_to_issue) || + !ec_point_mul_scalar_public_batch(group, &Z_batch, + /*g_scalar=*/NULL, Zs, es, + num_to_issue)) { + goto err; + } + + CBB proof; + if (!CBB_add_u16_length_prefixed(cbb, &proof) || + !dleq_generate(method, &proof, key, &BT_batch, &Z_batch) || + !CBB_flush(cbb)) { + goto err; + } + + // Skip over any unused requests. + size_t point_len = 1 + 2 * BN_num_bytes(&group->field); + if (!CBS_skip(cbs, point_len * (num_requested - num_to_issue))) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + goto err; + } + + ret = 1; + +err: + OPENSSL_free(BTs); + OPENSSL_free(Zs); + OPENSSL_free(es); + CBB_cleanup(&batch_cbb); + return ret; +} + +static STACK_OF(TRUST_TOKEN) * + voprf_unblind(const VOPRF_METHOD *method, const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, CBS *cbs, + size_t count, uint32_t key_id) { + const EC_GROUP *group = method->group; + if (count > sk_TRUST_TOKEN_PRETOKEN_num(pretokens)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + return NULL; + } + + int ok = 0; + STACK_OF(TRUST_TOKEN) *ret = sk_TRUST_TOKEN_new_null(); + if (ret == NULL) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + return NULL; + } + + if (count > ((size_t)-1) / sizeof(EC_RAW_POINT) || + count > ((size_t)-1) / sizeof(EC_SCALAR)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_OVERFLOW); + return 0; + } + EC_RAW_POINT *BTs = OPENSSL_malloc(count * sizeof(EC_RAW_POINT)); + EC_RAW_POINT *Zs = OPENSSL_malloc(count * sizeof(EC_RAW_POINT)); + EC_SCALAR *es = OPENSSL_malloc(count * sizeof(EC_SCALAR)); + CBB batch_cbb; + CBB_zero(&batch_cbb); + if (!BTs || + !Zs || + !es || + !CBB_init(&batch_cbb, 0) || + !cbb_add_point(&batch_cbb, method->group, &key->pubs)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + for (size_t i = 0; i < count; i++) { + const TRUST_TOKEN_PRETOKEN *pretoken = + sk_TRUST_TOKEN_PRETOKEN_value(pretokens, i); + + EC_AFFINE Z_affine; + if (!cbs_get_point(cbs, group, &Z_affine)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); + goto err; + } + + ec_affine_to_jacobian(group, &BTs[i], &pretoken->Tp); + ec_affine_to_jacobian(group, &Zs[i], &Z_affine); + + if (!cbb_add_point(&batch_cbb, group, &pretoken->Tp) || + !cbb_add_point(&batch_cbb, group, &Z_affine)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE); + goto err; + } + + // Unblind the token. + // pretoken->r is rinv. + EC_RAW_POINT N; + EC_AFFINE N_affine; + if (!ec_point_mul_scalar(group, &N, &Zs[i], &pretoken->r) || + !ec_jacobian_to_affine(group, &N_affine, &N)) { + goto err; + } + + // Serialize the token. Include |key_id| to avoid an extra copy in the layer + // above. + CBB token_cbb; + size_t point_len = 1 + 2 * BN_num_bytes(&group->field); + if (!CBB_init(&token_cbb, 4 + TRUST_TOKEN_NONCE_SIZE + (2 + point_len)) || + !CBB_add_u32(&token_cbb, key_id) || + !CBB_add_bytes(&token_cbb, pretoken->t, TRUST_TOKEN_NONCE_SIZE) || + !cbb_add_point(&token_cbb, group, &N_affine) || + !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; + } + } + + // The DLEQ batching construction is described in appendix B of + // https://eprint.iacr.org/2020/072/20200324:214215. Note the additional + // computations all act on public inputs. + for (size_t i = 0; i < count; i++) { + if (!hash_to_scalar_batch(method, &es[i], &batch_cbb, i)) { + goto err; + } + } + + EC_RAW_POINT BT_batch, Z_batch; + if (!ec_point_mul_scalar_public_batch(group, &BT_batch, + /*g_scalar=*/NULL, BTs, es, count) || + !ec_point_mul_scalar_public_batch(group, &Z_batch, + /*g_scalar=*/NULL, Zs, es, count)) { + goto err; + } + + CBS proof; + if (!CBS_get_u16_length_prefixed(cbs, &proof) || + !dleq_verify(method, &proof, key, &BT_batch, &Z_batch) || + CBS_len(&proof) != 0) { + goto err; + } + + ok = 1; + +err: + OPENSSL_free(BTs); + OPENSSL_free(Zs); + OPENSSL_free(es); + CBB_cleanup(&batch_cbb); + if (!ok) { + sk_TRUST_TOKEN_pop_free(ret, TRUST_TOKEN_free); + ret = NULL; + } + return ret; +} + +static int voprf_read(const VOPRF_METHOD *method, + const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], + const uint8_t *token, size_t token_len) { + const EC_GROUP *group = method->group; + CBS cbs; + CBS_init(&cbs, token, token_len); + EC_AFFINE Ws; + if (!CBS_copy_bytes(&cbs, out_nonce, TRUST_TOKEN_NONCE_SIZE) || + !cbs_get_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_to_group(group, &T, out_nonce)) { + return 0; + } + + EC_RAW_POINT Ws_calculated; + if (!ec_point_mul_scalar(group, &Ws_calculated, &T, &key->xs) || + !ec_affine_jacobian_equal(group, &Ws, &Ws_calculated)) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BAD_VALIDITY_CHECK); + return 0; + } + + return 1; +} + + +// VOPRF experiment v2. + +static int voprf_exp2_hash_to_group(const EC_GROUP *group, EC_RAW_POINT *out, + const uint8_t t[TRUST_TOKEN_NONCE_SIZE]) { + const uint8_t kHashTLabel[] = "TrustToken VOPRF Experiment V2 HashToGroup"; + return ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( + group, out, kHashTLabel, sizeof(kHashTLabel), t, TRUST_TOKEN_NONCE_SIZE); +} + +static int voprf_exp2_hash_to_scalar(const EC_GROUP *group, EC_SCALAR *out, + uint8_t *buf, size_t len) { + const uint8_t kHashCLabel[] = "TrustToken VOPRF Experiment V2 HashToScalar"; + return ec_hash_to_scalar_p384_xmd_sha512_draft07( + group, out, kHashCLabel, sizeof(kHashCLabel), buf, len); +} + +static int voprf_exp2_ok = 0; +static VOPRF_METHOD voprf_exp2_method; +static CRYPTO_once_t voprf_exp2_method_once = CRYPTO_ONCE_INIT; + +static void voprf_exp2_init_method_impl(void) { + voprf_exp2_ok = + voprf_init_method(&voprf_exp2_method, NID_secp384r1, + voprf_exp2_hash_to_group, voprf_exp2_hash_to_scalar); +} + +static int voprf_exp2_init_method(void) { + CRYPTO_once(&voprf_exp2_method_once, voprf_exp2_init_method_impl); + if (!voprf_exp2_ok) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} + +int voprf_exp2_generate_key(CBB *out_private, CBB *out_public) { + if (!voprf_exp2_init_method()) { + return 0; + } + + return voprf_generate_key(&voprf_exp2_method, out_private, out_public); +} + +int voprf_exp2_client_key_from_bytes(TRUST_TOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len) { + if (!voprf_exp2_init_method()) { + return 0; + } + return voprf_client_key_from_bytes(&voprf_exp2_method, key, in, len); +} + +int voprf_exp2_issuer_key_from_bytes(TRUST_TOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len) { + if (!voprf_exp2_init_method()) { + return 0; + } + return voprf_issuer_key_from_bytes(&voprf_exp2_method, key, in, len); +} + +STACK_OF(TRUST_TOKEN_PRETOKEN) * voprf_exp2_blind(CBB *cbb, size_t count) { + if (!voprf_exp2_init_method()) { + return NULL; + } + return voprf_blind(&voprf_exp2_method, cbb, count); +} + +int voprf_exp2_sign(const TRUST_TOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata) { + if (!voprf_exp2_init_method() || private_metadata != 0) { + return 0; + } + return voprf_sign(&voprf_exp2_method, key, cbb, cbs, num_requested, + num_to_issue); +} + +STACK_OF(TRUST_TOKEN) * + voprf_exp2_unblind(const TRUST_TOKEN_CLIENT_KEY *key, + const STACK_OF(TRUST_TOKEN_PRETOKEN) * pretokens, + CBS *cbs, size_t count, uint32_t key_id) { + if (!voprf_exp2_init_method()) { + return NULL; + } + return voprf_unblind(&voprf_exp2_method, key, pretokens, cbs, count, + key_id); +} + +int voprf_exp2_read(const TRUST_TOKEN_ISSUER_KEY *key, + uint8_t out_nonce[TRUST_TOKEN_NONCE_SIZE], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len) { + if (!voprf_exp2_init_method()) { + return 0; + } + return voprf_read(&voprf_exp2_method, key, out_nonce, token, token_len); +}
diff --git a/include/openssl/trust_token.h b/include/openssl/trust_token.h index b6c00b2..7146995 100644 --- a/include/openssl/trust_token.h +++ b/include/openssl/trust_token.h
@@ -40,13 +40,11 @@ // PMBTokens and P-384. OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v1(void); -// TRUST_TOKEN_experiment_v2_pp is an experimental Trust Tokens protocol using -// PMBTokens (with no private metadata) and P-384 with up to 6 keys, without RR -// verification. +// TRUST_TOKEN_experiment_v2_voprf is an experimental Trust Tokens protocol +// using VOPRFs and P-384 with up to 6 keys, without RR verification. // // This version is incomplete and should not be used. -// TODO(svaldez): Update to use the PrivacyPass primitive -OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_pp(void); +OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_voprf(void); // TRUST_TOKEN_experiment_v2_pmb is an experimental Trust Tokens protocol using // PMBTokens and P-384 with up to 3 keys, without RR verification. @@ -165,12 +163,8 @@ // |*out_rr| and |*out_rr_len| (respectively, |*out_sig| and |*out_sig_len|) // to a newly-allocated buffer containing the SRR (respectively, the SRR // signature). In other versions, it sets |*out_rr| and |*out_rr_len| -// (respectively, |*out_sig| and |*out_sig_len|) to a newly-allocated buffer -// containing the SRR (respectively, the SRR signature). It returns one on -// success or zero on failure. -// -// TODO(svaldez): Return the entire response in |*out_rr| and omit |*out_sig| in -// non-|TRUST_TOKEN_experiment_v1| versions. +// to a newly-allocated buffer containing |response| and leaves all validation +// to the caller. It returns one on success or zero on failure. OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_finish_redemption( TRUST_TOKEN_CLIENT *ctx, uint8_t **out_rr, size_t *out_rr_len, uint8_t **out_sig, size_t *out_sig_len, const uint8_t *response,
diff --git a/tool/speed.cc b/tool/speed.cc index af81a60..5d724e7 100644 --- a/tool/speed.cc +++ b/tool/speed.cc
@@ -984,11 +984,12 @@ return true; } -static PMBTOKEN_PRETOKEN *pmbtoken_pretoken_dup(PMBTOKEN_PRETOKEN *in) { - PMBTOKEN_PRETOKEN *out = - (PMBTOKEN_PRETOKEN *)OPENSSL_malloc(sizeof(PMBTOKEN_PRETOKEN)); +static TRUST_TOKEN_PRETOKEN *trust_token_pretoken_dup( + TRUST_TOKEN_PRETOKEN *in) { + TRUST_TOKEN_PRETOKEN *out = + (TRUST_TOKEN_PRETOKEN *)OPENSSL_malloc(sizeof(TRUST_TOKEN_PRETOKEN)); if (out) { - OPENSSL_memcpy(out, in, sizeof(PMBTOKEN_PRETOKEN)); + OPENSSL_memcpy(out, in, sizeof(TRUST_TOKEN_PRETOKEN)); } return out; } @@ -1059,9 +1060,9 @@ &msg_len, batchsize); OPENSSL_free(issue_msg); // Clear pretokens. - sk_PMBTOKEN_PRETOKEN_pop_free(client->pretokens, - PMBTOKEN_PRETOKEN_free); - client->pretokens = sk_PMBTOKEN_PRETOKEN_new_null(); + sk_TRUST_TOKEN_PRETOKEN_pop_free(client->pretokens, + TRUST_TOKEN_PRETOKEN_free); + client->pretokens = sk_TRUST_TOKEN_PRETOKEN_new_null(); return ok; })) { fprintf(stderr, "TRUST_TOKEN_CLIENT_begin_issuance failed.\n"); @@ -1078,9 +1079,10 @@ } bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg); - bssl::UniquePtr<STACK_OF(PMBTOKEN_PRETOKEN)> pretokens( - sk_PMBTOKEN_PRETOKEN_deep_copy(client->pretokens, pmbtoken_pretoken_dup, - PMBTOKEN_PRETOKEN_free)); + bssl::UniquePtr<STACK_OF(TRUST_TOKEN_PRETOKEN)> pretokens( + sk_TRUST_TOKEN_PRETOKEN_deep_copy(client->pretokens, + trust_token_pretoken_dup, + TRUST_TOKEN_PRETOKEN_free)); if (!TimeFunction(&results, [&]() -> bool { uint8_t *issue_resp = NULL; @@ -1116,8 +1118,9 @@ issue_resp, resp_len)); // Reset pretokens. - client->pretokens = sk_PMBTOKEN_PRETOKEN_deep_copy( - pretokens.get(), pmbtoken_pretoken_dup, PMBTOKEN_PRETOKEN_free); + client->pretokens = sk_TRUST_TOKEN_PRETOKEN_deep_copy( + pretokens.get(), trust_token_pretoken_dup, + TRUST_TOKEN_PRETOKEN_free); return !!tokens; })) { fprintf(stderr, "TRUST_TOKEN_CLIENT_finish_issuance failed.\n"); @@ -1203,9 +1206,9 @@ if (!TimeFunction(&results, [&]() -> bool { uint8_t *srr = NULL, *sig = NULL; size_t srr_len, sig_len; - int ok = TRUST_TOKEN_CLIENT_finish_redemption(client.get(), &srr, - &srr_len, &sig, &sig_len, - redeem_resp, resp_len); + int ok = TRUST_TOKEN_CLIENT_finish_redemption( + client.get(), &srr, &srr_len, &sig, &sig_len, redeem_resp, + redeem_resp_len); OPENSSL_free(srr); OPENSSL_free(sig); return ok; @@ -1352,7 +1355,15 @@ !SpeedTrustToken("TrustToken-Exp1-Batch1", TRUST_TOKEN_experiment_v1(), 1, selected) || !SpeedTrustToken("TrustToken-Exp1-Batch10", TRUST_TOKEN_experiment_v1(), - 10, selected)) { + 10, selected) || + !SpeedTrustToken("TrustToken-Exp2VOPRF-Batch1", + TRUST_TOKEN_experiment_v2_voprf(), 1, selected) || + !SpeedTrustToken("TrustToken-Exp2VOPRF-Batch10", + TRUST_TOKEN_experiment_v2_voprf(), 10, selected) || + !SpeedTrustToken("TrustToken-Exp2PMB-Batch1", + TRUST_TOKEN_experiment_v2_pmb(), 1, selected) || + !SpeedTrustToken("TrustToken-Exp2PMB-Batch10", + TRUST_TOKEN_experiment_v2_pmb(), 10, selected)) { return false; } if (g_print_json) {