From 9adcb0aa7eb946bea9197276121412ef68776315 Mon Sep 17 00:00:00 2001 From: Steven Valdez Date: Thu, 10 Sep 2020 10:59:15 -0400 Subject: [PATCH] Add TrustTokenV2. Changes: - Remove point prefixes. - Don't verify SRR on the client. TODO: - Replace SRR generation with RR generation on issuer. - Add finalized PrivacyPass version. Change-Id: Ibfb04aaba2cf669639af77299da22ab668175edb Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42824 Commit-Queue: Steven Valdez Reviewed-by: David Benjamin --- crypto/trust_token/internal.h | 46 ++++- crypto/trust_token/pmbtoken.c | 270 ++++++++++++++++++++----- crypto/trust_token/trust_token.c | 84 ++++++-- crypto/trust_token/trust_token_test.cc | 123 ++++++++--- include/openssl/trust_token.h | 32 ++- 5 files changed, 449 insertions(+), 106 deletions(-) diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h index a44de76c0..c935888ea 100644 --- a/crypto/trust_token/internal.h +++ b/crypto/trust_token/internal.h @@ -110,6 +110,39 @@ int pmbtoken_exp1_read(const PMBTOKEN_ISSUER_KEY *key, // function is used to confirm H was computed as expected. OPENSSL_EXPORT int pmbtoken_exp1_get_h_for_testing(uint8_t out[97]); +// 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, + const uint8_t *in, size_t len); +int pmbtoken_exp2_issuer_key_from_bytes(PMBTOKEN_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, + 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, + 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], + uint8_t *out_private_metadata, const uint8_t *token, + size_t token_len); + +// pmbtoken_exp2_get_h_for_testing returns H in uncompressed coordinates. This +// function is used to confirm H was computed as expected. +OPENSSL_EXPORT int pmbtoken_exp2_get_h_for_testing(uint8_t out[97]); + // Trust Tokens internals. @@ -172,6 +205,15 @@ struct trust_token_method_st { uint8_t out_nonce[PMBTOKEN_NONCE_SIZE], uint8_t *out_private_metadata, const uint8_t *token, size_t token_len); + + // whether the construction supports private metadata. + int has_private_metadata; + + // max keys that can be configured. + size_t max_keys; + + // whether the SRR is part of the protocol. + int has_srr; }; // Structure representing a single Trust Token public key with the specified ID. @@ -195,7 +237,7 @@ struct trust_token_client_st { // keys is the set of public keys that are supported by the client for // issuance/redemptions. - struct trust_token_client_key_st keys[3]; + struct trust_token_client_key_st keys[6]; // num_keys is the number of keys currently configured. size_t num_keys; @@ -217,7 +259,7 @@ struct trust_token_issuer_st { // keys is the set of private keys that are supported by the issuer for // issuance/redemptions. The public metadata is an index into this list of // keys. - struct trust_token_issuer_key_st keys[3]; + struct trust_token_issuer_key_st keys[6]; // num_keys is the number of keys currently configured. size_t num_keys; diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c index 5ea60c347..f9132e664 100644 --- a/crypto/trust_token/pmbtoken.c +++ b/crypto/trust_token/pmbtoken.c @@ -52,6 +52,7 @@ typedef struct { // hash_c implements the H_c operation in PMBTokens. It returns one on success // and zero on error. hash_c_func_t hash_c; + int prefix_point : 1; } PMBTOKEN_METHOD; static const uint8_t kDefaultAdditionalData[32] = {0}; @@ -59,7 +60,7 @@ static const uint8_t kDefaultAdditionalData[32] = {0}; static int pmbtoken_init_method(PMBTOKEN_METHOD *method, int curve_nid, const uint8_t *h_bytes, size_t h_len, hash_t_func_t hash_t, hash_s_func_t hash_s, - hash_c_func_t hash_c) { + hash_c_func_t hash_c, int prefix_point) { method->group = EC_GROUP_new_by_curve_name(curve_nid); if (method->group == NULL) { return 0; @@ -68,6 +69,7 @@ static int pmbtoken_init_method(PMBTOKEN_METHOD *method, int curve_nid, method->hash_t = hash_t; method->hash_s = hash_s; method->hash_c = hash_c; + method->prefix_point = prefix_point; EC_AFFINE h; if (!ec_point_from_uncompressed(method->group, &h, h_bytes, h_len)) { @@ -113,11 +115,40 @@ static int point_to_cbb(CBB *out, const EC_GROUP *group, len) == len; } +static int cbb_add_prefixed_point(CBB *out, const EC_GROUP *group, + const EC_AFFINE *point, int prefix_point) { + if (prefix_point) { + CBB child; + if (!CBB_add_u16_length_prefixed(out, &child) || + !point_to_cbb(&child, group, point) || + !CBB_flush(out)) { + return 0; + } + } else { + if (!point_to_cbb(out, group, point) || + !CBB_flush(out)) { + return 0; + } + } + + return 1; +} + static int cbs_get_prefixed_point(CBS *cbs, const EC_GROUP *group, - EC_AFFINE *out) { + EC_AFFINE *out, int prefix_point) { CBS child; - if (!CBS_get_u16_length_prefixed(cbs, &child) || - !ec_point_from_uncompressed(group, out, CBS_data(&child), + if (prefix_point) { + if (!CBS_get_u16_length_prefixed(cbs, &child)) { + return 0; + } + } else { + size_t plen = 1 + 2 * BN_num_bytes(&group->field); + if (!CBS_get_bytes(cbs, &child, plen)) { + return 0; + } + } + + if (!ec_point_from_uncompressed(group, out, CBS_data(&child), CBS_len(&child))) { return 0; } @@ -166,16 +197,12 @@ static int pmbtoken_generate_key(const PMBTOKEN_METHOD *method, return 0; } - // 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, &pub_affine[0]) || - !CBB_add_u16_length_prefixed(out_public, &child) || - !point_to_cbb(&child, group, &pub_affine[1]) || - !CBB_add_u16_length_prefixed(out_public, &child) || - !point_to_cbb(&child, group, &pub_affine[2]) || - !CBB_flush(out_public)) { + if (!cbb_add_prefixed_point(out_public, group, &pub_affine[0], + method->prefix_point) || + !cbb_add_prefixed_point(out_public, group, &pub_affine[1], + method->prefix_point) || + !cbb_add_prefixed_point(out_public, group, &pub_affine[2], + method->prefix_point)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL); return 0; } @@ -186,13 +213,14 @@ static int pmbtoken_generate_key(const PMBTOKEN_METHOD *method, 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) || + if (!cbs_get_prefixed_point(&cbs, method->group, &key->pub0, + method->prefix_point) || + !cbs_get_prefixed_point(&cbs, method->group, &key->pub1, + method->prefix_point) || + !cbs_get_prefixed_point(&cbs, method->group, &key->pubs, + method->prefix_point) || CBS_len(&cbs) != 0) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); return 0; @@ -282,12 +310,8 @@ static STACK_OF(PMBTOKEN_PRETOKEN) * 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)) { + if (!cbb_add_prefixed_point(cbb, group, &pretoken->Tp, + method->prefix_point)) { goto err; } } @@ -750,7 +774,7 @@ static int pmbtoken_sign(const PMBTOKEN_METHOD *method, for (size_t i = 0; i < num_to_issue; i++) { EC_AFFINE Tp_affine; EC_RAW_POINT Tp; - if (!cbs_get_prefixed_point(cbs, group, &Tp_affine)) { + if (!cbs_get_prefixed_point(cbs, group, &Tp_affine, method->prefix_point)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); goto err; } @@ -766,7 +790,6 @@ static int pmbtoken_sign(const PMBTOKEN_METHOD *method, // The |jacobians| and |affines| contain Sp, Wp, and Wsp. EC_RAW_POINT jacobians[3]; EC_AFFINE affines[3]; - CBB child; if (!method->hash_s(group, &jacobians[0], &Tp_affine, s) || !ec_point_mul_scalar_batch(group, &jacobians[1], &Tp, &xb, &jacobians[0], &yb, NULL, NULL) || @@ -774,12 +797,8 @@ static int pmbtoken_sign(const PMBTOKEN_METHOD *method, &jacobians[0], &key->ys, NULL, NULL) || !ec_jacobian_to_affine_batch(group, affines, jacobians, 3) || !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, &affines[1]) || - !CBB_add_u16_length_prefixed(cbb, &child) || - !point_to_cbb(&child, group, &affines[2])) { + !cbb_add_prefixed_point(cbb, group, &affines[1], method->prefix_point) || + !cbb_add_prefixed_point(cbb, group, &affines[2], method->prefix_point)) { goto err; } @@ -835,7 +854,11 @@ static int pmbtoken_sign(const PMBTOKEN_METHOD *method, // 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))) { + size_t token_len = point_len; + if (method->prefix_point) { + token_len += 2; + } + if (!CBS_skip(cbs, token_len * (num_requested - num_to_issue))) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); goto err; } @@ -902,8 +925,9 @@ static STACK_OF(TRUST_TOKEN) * uint8_t s[PMBTOKEN_NONCE_SIZE]; EC_AFFINE Wp_affine, Wsp_affine; if (!CBS_copy_bytes(cbs, s, PMBTOKEN_NONCE_SIZE) || - !cbs_get_prefixed_point(cbs, group, &Wp_affine) || - !cbs_get_prefixed_point(cbs, group, &Wsp_affine)) { + !cbs_get_prefixed_point(cbs, group, &Wp_affine, method->prefix_point) || + !cbs_get_prefixed_point(cbs, group, &Wsp_affine, + method->prefix_point)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE); goto err; } @@ -937,19 +961,17 @@ static STACK_OF(TRUST_TOKEN) * // Serialize the token. Include |key_id| to avoid an extra copy in the layer // above. - CBB token_cbb, child; + 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)) || !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, &affines[0]) || - !CBB_add_u16_length_prefixed(&token_cbb, &child) || - !point_to_cbb(&child, group, &affines[1]) || - !CBB_add_u16_length_prefixed(&token_cbb, &child) || - !point_to_cbb(&child, group, &affines[2]) || + !cbb_add_prefixed_point(&token_cbb, group, &affines[0], + method->prefix_point) || + !cbb_add_prefixed_point(&token_cbb, group, &affines[1], + method->prefix_point) || + !cbb_add_prefixed_point(&token_cbb, group, &affines[2], + method->prefix_point) || !CBB_flush(&token_cbb)) { CBB_cleanup(&token_cbb); goto err; @@ -1021,9 +1043,9 @@ static int pmbtoken_read(const PMBTOKEN_METHOD *method, CBS_init(&cbs, token, token_len); EC_AFFINE 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_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) || CBS_len(&cbs) != 0) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_TOKEN); return 0; @@ -1140,7 +1162,7 @@ static void pmbtoken_exp1_init_method_impl(void) { pmbtoken_exp1_ok = pmbtoken_init_method(&pmbtoken_exp1_method, NID_secp384r1, kH, sizeof(kH), pmbtoken_exp1_hash_t, pmbtoken_exp1_hash_s, - pmbtoken_exp1_hash_c); + pmbtoken_exp1_hash_c, 1); } static int pmbtoken_exp1_init_method(void) { @@ -1225,3 +1247,153 @@ int pmbtoken_exp1_get_h_for_testing(uint8_t out[97]) { ec_point_to_bytes(pmbtoken_exp1_method.group, &h, POINT_CONVERSION_UNCOMPRESSED, out, 97) == 97; } + +// 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 kHashTLabel[] = "PMBTokens Experiment V2 HashT"; + return ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( + group, out, kHashTLabel, sizeof(kHashTLabel), t, PMBTOKEN_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 kHashSLabel[] = "PMBTokens Experiment V2 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_p384_xmd_sha512_sswu_draft07( + 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_exp2_hash_c(const EC_GROUP *group, EC_SCALAR *out, + uint8_t *buf, size_t len) { + const uint8_t kHashCLabel[] = "PMBTokens Experiment V2 HashC"; + return ec_hash_to_scalar_p384_xmd_sha512_draft07( + group, out, kHashCLabel, sizeof(kHashCLabel), buf, len); +} + +static int pmbtoken_exp2_ok = 0; +static PMBTOKEN_METHOD pmbtoken_exp2_method; +static CRYPTO_once_t pmbtoken_exp2_method_once = CRYPTO_ONCE_INIT; + +static void pmbtoken_exp2_init_method_impl(void) { + // This is the output of |ec_hash_to_scalar_p384_xmd_sha512_draft07| with DST + // "PMBTokens Experiment V2 HashH" and message "generator". + static const uint8_t kH[] = { + 0x04, 0xbc, 0x27, 0x24, 0x99, 0xfa, 0xc9, 0xa4, 0x74, 0x6f, 0xf9, + 0x07, 0x81, 0x55, 0xf8, 0x1f, 0x6f, 0xda, 0x09, 0xe7, 0x8c, 0x5d, + 0x9e, 0x4e, 0x14, 0x7c, 0x53, 0x14, 0xbc, 0x7e, 0x29, 0x57, 0x92, + 0x17, 0x94, 0x6e, 0xd2, 0xdf, 0xa5, 0x31, 0x1b, 0x4e, 0xb7, 0xfc, + 0x93, 0xe3, 0x6e, 0x14, 0x1f, 0x4f, 0x14, 0xf3, 0xe5, 0x47, 0x61, + 0x1c, 0x2c, 0x72, 0x25, 0xf0, 0x4a, 0x45, 0x23, 0x2d, 0x57, 0x93, + 0x0e, 0xb2, 0x55, 0xb8, 0x57, 0x25, 0x4c, 0x1e, 0xdb, 0xfd, 0x58, + 0x70, 0x17, 0x9a, 0xbb, 0x9e, 0x5e, 0x93, 0x9e, 0x92, 0xd3, 0xe8, + 0x25, 0x62, 0xbf, 0x59, 0xb2, 0xd2, 0x3d, 0x71, 0xff + }; + + pmbtoken_exp2_ok = + pmbtoken_init_method(&pmbtoken_exp2_method, NID_secp384r1, kH, sizeof(kH), + pmbtoken_exp2_hash_t, pmbtoken_exp2_hash_s, + pmbtoken_exp2_hash_c, 0); +} + +static int pmbtoken_exp2_init_method(void) { + CRYPTO_once(&pmbtoken_exp2_method_once, pmbtoken_exp2_init_method_impl); + if (!pmbtoken_exp2_ok) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} + +int pmbtoken_exp2_generate_key(CBB *out_private, CBB *out_public) { + if (!pmbtoken_exp2_init_method()) { + return 0; + } + + return pmbtoken_generate_key(&pmbtoken_exp2_method, out_private, out_public); +} + +int pmbtoken_exp2_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key, + const uint8_t *in, size_t len) { + if (!pmbtoken_exp2_init_method()) { + return 0; + } + return pmbtoken_client_key_from_bytes(&pmbtoken_exp2_method, key, in, len); +} + +int pmbtoken_exp2_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key, + const uint8_t *in, size_t len) { + if (!pmbtoken_exp2_init_method()) { + return 0; + } + return pmbtoken_issuer_key_from_bytes(&pmbtoken_exp2_method, key, in, len); +} + +STACK_OF(PMBTOKEN_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, + size_t num_requested, size_t num_to_issue, + uint8_t private_metadata) { + if (!pmbtoken_exp2_init_method()) { + return 0; + } + return pmbtoken_sign(&pmbtoken_exp2_method, key, cbb, cbs, num_requested, + num_to_issue, private_metadata); +} + +STACK_OF(TRUST_TOKEN) * + pmbtoken_exp2_unblind(const PMBTOKEN_CLIENT_KEY *key, + const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens, + CBS *cbs, size_t count, uint32_t key_id) { + if (!pmbtoken_exp2_init_method()) { + return NULL; + } + return pmbtoken_unblind(&pmbtoken_exp2_method, key, pretokens, cbs, count, + key_id); +} + +int pmbtoken_exp2_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) { + if (!pmbtoken_exp2_init_method()) { + return 0; + } + return pmbtoken_read(&pmbtoken_exp2_method, key, out_nonce, + out_private_metadata, token, token_len); +} + +int pmbtoken_exp2_get_h_for_testing(uint8_t out[97]) { + if (!pmbtoken_exp2_init_method()) { + return 0; + } + EC_AFFINE h; + return ec_jacobian_to_affine(pmbtoken_exp2_method.group, &h, + &pmbtoken_exp2_method.h) && + ec_point_to_bytes(pmbtoken_exp2_method.group, &h, + POINT_CONVERSION_UNCOMPRESSED, out, 97) == 97; +} diff --git a/crypto/trust_token/trust_token.c b/crypto/trust_token/trust_token.c index 87b827714..fea619ef5 100644 --- a/crypto/trust_token/trust_token.c +++ b/crypto/trust_token/trust_token.c @@ -36,6 +36,41 @@ const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v1(void) { pmbtoken_exp1_sign, pmbtoken_exp1_unblind, pmbtoken_exp1_read, + 1, /* has_private_metadata */ + 3, /* max_keys */ + 1, /* has_srr */ + }; + return &kMethod; +} + +const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_pp(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, + 0, /* has_private_metadata */ + 6, /* max_keys */ + 0, /* has_srr */ + }; + return &kMethod; +} + +const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_pmb(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, + 1, /* has_private_metadata */ + 3, /* max_keys */ + 0, /* has_srr */ }; return &kMethod; } @@ -131,7 +166,8 @@ void TRUST_TOKEN_CLIENT_free(TRUST_TOKEN_CLIENT *ctx) { int TRUST_TOKEN_CLIENT_add_key(TRUST_TOKEN_CLIENT *ctx, size_t *out_key_index, const uint8_t *key, size_t key_len) { - if (ctx->num_keys == OPENSSL_ARRAY_SIZE(ctx->keys)) { + if (ctx->num_keys == OPENSSL_ARRAY_SIZE(ctx->keys) || + ctx->num_keys >= ctx->method->max_keys) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_TOO_MANY_KEYS); return 0; } @@ -153,6 +189,9 @@ int TRUST_TOKEN_CLIENT_add_key(TRUST_TOKEN_CLIENT *ctx, size_t *out_key_index, } int TRUST_TOKEN_CLIENT_set_srr_key(TRUST_TOKEN_CLIENT *ctx, EVP_PKEY *key) { + if (!ctx->method->has_srr) { + return 1; + } EVP_PKEY_free(ctx->srr_key); EVP_PKEY_up_ref(key); ctx->srr_key = key; @@ -270,15 +309,10 @@ int TRUST_TOKEN_CLIENT_begin_redemption(TRUST_TOKEN_CLIENT *ctx, uint8_t **out, } int TRUST_TOKEN_CLIENT_finish_redemption(TRUST_TOKEN_CLIENT *ctx, - uint8_t **out_srr, size_t *out_srr_len, + uint8_t **out_rr, size_t *out_rr_len, uint8_t **out_sig, size_t *out_sig_len, const uint8_t *response, size_t response_len) { - if (ctx->srr_key == NULL) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_NO_SRR_KEY_CONFIGURED); - return 0; - } - CBS in, srr, sig; CBS_init(&in, response, response_len); if (!CBS_get_u16_length_prefixed(&in, &srr) || @@ -287,16 +321,24 @@ int TRUST_TOKEN_CLIENT_finish_redemption(TRUST_TOKEN_CLIENT *ctx, 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); + 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 (!sig_ok) { - OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_SRR_SIGNATURE_ERROR); - 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); + + if (!sig_ok) { + OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_SRR_SIGNATURE_ERROR); + return 0; + } } uint8_t *srr_buf = NULL, *sig_buf = NULL; @@ -309,8 +351,8 @@ int TRUST_TOKEN_CLIENT_finish_redemption(TRUST_TOKEN_CLIENT *ctx, return 0; } - *out_srr = srr_buf; - *out_srr_len = srr_len; + *out_rr = srr_buf; + *out_rr_len = srr_len; *out_sig = sig_buf; *out_sig_len = sig_len; return 1; @@ -346,7 +388,8 @@ void TRUST_TOKEN_ISSUER_free(TRUST_TOKEN_ISSUER *ctx) { int TRUST_TOKEN_ISSUER_add_key(TRUST_TOKEN_ISSUER *ctx, const uint8_t *key, size_t key_len) { - if (ctx->num_keys == OPENSSL_ARRAY_SIZE(ctx->keys)) { + if (ctx->num_keys == OPENSSL_ARRAY_SIZE(ctx->keys) || + ctx->num_keys >= ctx->method->max_keys) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_TOO_MANY_KEYS); return 0; } @@ -411,7 +454,8 @@ int TRUST_TOKEN_ISSUER_issue(const TRUST_TOKEN_ISSUER *ctx, uint8_t **out, const struct trust_token_issuer_key_st *key = trust_token_issuer_get_key(ctx, public_metadata); - if (key == NULL || private_metadata > 1) { + if (key == NULL || private_metadata > 1 || + (!ctx->method->has_private_metadata && private_metadata != 0)) { OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_METADATA); return 0; } diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc index f6ff86c2b..b282500ca 100644 --- a/crypto/trust_token/trust_token_test.cc +++ b/crypto/trust_token/trust_token_test.cc @@ -56,6 +56,30 @@ TEST(TrustTokenTest, KeyGenExp1) { ASSERT_EQ(301u, pub_key_len); } +TEST(TrustTokenTest, KeyGenExp2PP) { + 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_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); +} + +TEST(TrustTokenTest, KeyGenExp2PMB) { + 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_pmb(), 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); +} + // 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); @@ -78,8 +102,34 @@ TEST(TrustTokenTest, HExp1) { EXPECT_EQ(Bytes(h), Bytes(expected_bytes, expected_len)); } +// Test that H in |TRUST_TOKEN_experiment_v2_pmb| was computed correctly. +TEST(TrustTokenTest, HExp2) { + 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 V2 HashH"; + + bssl::UniquePtr expected_h(EC_POINT_new(group)); + ASSERT_TRUE(expected_h); + ASSERT_TRUE(ec_hash_to_curve_p384_xmd_sha512_sswu_draft07( + group, &expected_h->raw, kHLabel, sizeof(kHLabel), kHGen, sizeof(kHGen))); + uint8_t expected_bytes[1 + 2 * EC_MAX_BYTES]; + size_t expected_len = + EC_POINT_point2oct(group, expected_h.get(), POINT_CONVERSION_UNCOMPRESSED, + expected_bytes, sizeof(expected_bytes), nullptr); + + uint8_t h[97]; + ASSERT_TRUE(pmbtoken_exp2_get_h_for_testing(h)); + EXPECT_EQ(Bytes(h), Bytes(expected_bytes, expected_len)); +} + static std::vector AllMethods() { - return {TRUST_TOKEN_experiment_v1()}; + return { + TRUST_TOKEN_experiment_v1(), + TRUST_TOKEN_experiment_v2_pp(), + TRUST_TOKEN_experiment_v2_pmb() + }; } class TrustTokenProtocolTestBase : public ::testing::Test { @@ -102,7 +152,7 @@ class TrustTokenProtocolTestBase : public ::testing::Test { issuer.reset(TRUST_TOKEN_ISSUER_new(method(), issuer_max_batchsize)); ASSERT_TRUE(issuer); - for (size_t i = 0; i < 3; i++) { + for (size_t i = 0; i < method()->max_keys; i++) { 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, key_index; @@ -163,7 +213,7 @@ TEST_P(TrustTokenProtocolTest, InvalidToken) { bssl::UniquePtr free_issue_msg(issue_msg); ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue( issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len, - /*public_metadata=*/KeyID(0), /*private_metadata=*/1, + /*public_metadata=*/KeyID(0), /*private_metadata=*/0, /*max_issuance=*/10)); bssl::UniquePtr free_msg(issue_resp); bssl::UniquePtr tokens( @@ -428,9 +478,14 @@ TEST_P(TrustTokenMetadataTest, SetAndGetMetadata) { &msg_len, 10)); bssl::UniquePtr free_issue_msg(issue_msg); size_t tokens_issued; - ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue( + bool result = TRUST_TOKEN_ISSUER_issue( issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len, - public_metadata(), private_metadata(), /*max_issuance=*/1)); + public_metadata(), private_metadata(), /*max_issuance=*/1); + if (!method()->has_private_metadata && private_metadata()) { + ASSERT_FALSE(result); + return; + } + ASSERT_TRUE(result); bssl::UniquePtr free_msg(issue_resp); size_t key_index; bssl::UniquePtr tokens( @@ -510,6 +565,10 @@ TEST_P(TrustTokenMetadataTest, SetAndGetMetadata) { } TEST_P(TrustTokenMetadataTest, TooManyRequests) { + if (!method()->has_private_metadata && private_metadata()) { + return; + } + issuer_max_batchsize = 1; ASSERT_NO_FATAL_FAILURE(SetupContexts()); @@ -534,6 +593,10 @@ TEST_P(TrustTokenMetadataTest, TooManyRequests) { TEST_P(TrustTokenMetadataTest, TruncatedProof) { + if (!method()->has_private_metadata && private_metadata()) { + return; + } + ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; @@ -558,19 +621,16 @@ TEST_P(TrustTokenMetadataTest, TruncatedProof) { ASSERT_TRUE(CBS_get_u32(&real_response, &public_metadata)); ASSERT_TRUE(CBB_add_u32(bad_response.get(), public_metadata)); + 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)); + if (method() == TRUST_TOKEN_experiment_v1()) { + token_length += 4; + } for (size_t i = 0; i < count; i++) { - uint8_t s[PMBTOKEN_NONCE_SIZE]; - CBS tmp; - ASSERT_TRUE(CBS_copy_bytes(&real_response, s, PMBTOKEN_NONCE_SIZE)); - ASSERT_TRUE(CBB_add_bytes(bad_response.get(), s, PMBTOKEN_NONCE_SIZE)); - ASSERT_TRUE(CBS_get_u16_length_prefixed(&real_response, &tmp)); - ASSERT_TRUE(CBB_add_u16(bad_response.get(), CBS_len(&tmp))); - ASSERT_TRUE( - CBB_add_bytes(bad_response.get(), CBS_data(&tmp), CBS_len(&tmp))); - ASSERT_TRUE(CBS_get_u16_length_prefixed(&real_response, &tmp)); - ASSERT_TRUE(CBB_add_u16(bad_response.get(), CBS_len(&tmp))); - ASSERT_TRUE( - CBB_add_bytes(bad_response.get(), CBS_data(&tmp), CBS_len(&tmp))); + ASSERT_TRUE(CBB_add_bytes(bad_response.get(), CBS_data(&real_response), + token_length)); + ASSERT_TRUE(CBS_skip(&real_response, token_length)); } CBS tmp; @@ -593,6 +653,10 @@ TEST_P(TrustTokenMetadataTest, TruncatedProof) { } TEST_P(TrustTokenMetadataTest, ExcessDataProof) { + if (!method()->has_private_metadata && private_metadata()) { + return; + } + ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; @@ -617,19 +681,16 @@ TEST_P(TrustTokenMetadataTest, ExcessDataProof) { ASSERT_TRUE(CBS_get_u32(&real_response, &public_metadata)); ASSERT_TRUE(CBB_add_u32(bad_response.get(), public_metadata)); + 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)); + if (method() == TRUST_TOKEN_experiment_v1()) { + token_length += 4; + } for (size_t i = 0; i < count; i++) { - uint8_t s[PMBTOKEN_NONCE_SIZE]; - CBS tmp; - ASSERT_TRUE(CBS_copy_bytes(&real_response, s, PMBTOKEN_NONCE_SIZE)); - ASSERT_TRUE(CBB_add_bytes(bad_response.get(), s, PMBTOKEN_NONCE_SIZE)); - ASSERT_TRUE(CBS_get_u16_length_prefixed(&real_response, &tmp)); - ASSERT_TRUE(CBB_add_u16(bad_response.get(), CBS_len(&tmp))); - ASSERT_TRUE( - CBB_add_bytes(bad_response.get(), CBS_data(&tmp), CBS_len(&tmp))); - ASSERT_TRUE(CBS_get_u16_length_prefixed(&real_response, &tmp)); - ASSERT_TRUE(CBB_add_u16(bad_response.get(), CBS_len(&tmp))); - ASSERT_TRUE( - CBB_add_bytes(bad_response.get(), CBS_data(&tmp), CBS_len(&tmp))); + ASSERT_TRUE(CBB_add_bytes(bad_response.get(), CBS_data(&real_response), + token_length)); + ASSERT_TRUE(CBS_skip(&real_response, token_length)); } CBS tmp; @@ -673,6 +734,10 @@ class TrustTokenBadKeyTest }; TEST_P(TrustTokenBadKeyTest, BadKey) { + if (!method()->has_private_metadata && private_metadata()) { + return; + } + ASSERT_NO_FATAL_FAILURE(SetupContexts()); uint8_t *issue_msg = NULL, *issue_resp = NULL; diff --git a/include/openssl/trust_token.h b/include/openssl/trust_token.h index 9ecf75f19..b6c00b262 100644 --- a/include/openssl/trust_token.h +++ b/include/openssl/trust_token.h @@ -40,6 +40,20 @@ extern "C" { // 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. +// +// 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); + +// TRUST_TOKEN_experiment_v2_pmb is an experimental Trust Tokens protocol using +// PMBTokens and P-384 with up to 3 keys, without RR verification. +// +// This version is incomplete and should not be used. +OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_pmb(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. @@ -146,13 +160,19 @@ OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_begin_redemption( const TRUST_TOKEN *token, const uint8_t *data, size_t data_len, uint64_t time); -// TRUST_TOKEN_CLIENT_finish_redemption consumes |response| from the issuer and -// verifies the SRR. If valid, it returns one and sets |*out_srr| and -// |*out_srr_len| (respectively, |*out_sig| and |*out_sig_len|) to a -// newly-allocated buffer containing the SRR (respectively, the SRR signature). -// Otherwise, it returns zero. +// TRUST_TOKEN_CLIENT_finish_redemption consumes |response| from the issuer. In +// |TRUST_TOKEN_experiment_v1|, it then verifies the SRR and if valid 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). 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. OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_finish_redemption( - TRUST_TOKEN_CLIENT *ctx, uint8_t **out_srr, size_t *out_srr_len, + 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, size_t response_len);