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 <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h
index a44de76..c935888 100644
--- a/crypto/trust_token/internal.h
+++ b/crypto/trust_token/internal.h
@@ -110,6 +110,39 @@
// 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 @@
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 @@
// 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 @@
// 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 5ea60c3..f9132e6 100644
--- a/crypto/trust_token/pmbtoken.c
+++ b/crypto/trust_token/pmbtoken.c
@@ -52,6 +52,7 @@
// 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 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 @@
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 @@
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 @@
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_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 @@
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 @@
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 @@
// 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 @@
&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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 87b8277..fea619e 100644
--- a/crypto/trust_token/trust_token.c
+++ b/crypto/trust_token/trust_token.c
@@ -36,6 +36,41 @@
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 @@
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_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_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 @@
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 @@
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 @@
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 @@
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 f6ff86c..b282500 100644
--- a/crypto/trust_token/trust_token_test.cc
+++ b/crypto/trust_token/trust_token_test.cc
@@ -56,6 +56,30 @@
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 @@
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<EC_POINT> 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<const TRUST_TOKEN_METHOD *> 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 @@
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 @@
bssl::UniquePtr<uint8_t> 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<uint8_t> free_msg(issue_resp);
bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
@@ -428,9 +478,14 @@
&msg_len, 10));
bssl::UniquePtr<uint8_t> 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<uint8_t> free_msg(issue_resp);
size_t key_index;
bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
@@ -510,6 +565,10 @@
}
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, 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 @@
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, 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 @@
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 @@
};
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 9ecf75f..b6c00b2 100644
--- a/include/openssl/trust_token.h
+++ b/include/openssl/trust_token.h
@@ -40,6 +40,20 @@
// 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 @@
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);