Trust Token Implementation.
Trust Token implementation based on PrivacyPass using the PMBToken construction.
This implementation currently omits the DLEQ proofs.
https://github.com/alxdavids/privacy-pass-ietf/blob/master/draft-davidson-pp-protocol.md
https://eprint.iacr.org/2020/072.pdf
Change-Id: If236cc8beaf33a80bdad2991c3163f9dd0cb7571
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/39244
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 f4dd08f..1d35b97 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -336,7 +336,8 @@
thread_none.c
thread_pthread.c
thread_win.c
- trust_token/privacy_pass.c
+ trust_token/pmbtoken.c
+ trust_token/trust_token.c
x509/a_digest.c
x509/a_sign.c
x509/a_strex.c
diff --git a/crypto/err/trust_token.errordata b/crypto/err/trust_token.errordata
index 9f0b60c..d7d9946 100644
--- a/crypto/err/trust_token.errordata
+++ b/crypto/err/trust_token.errordata
@@ -1,2 +1,14 @@
+TRUST_TOKEN,111,BAD_VALIDITY_CHECK
TRUST_TOKEN,101,BUFFER_TOO_SMALL
+TRUST_TOKEN,103,DECODE_ERROR
+TRUST_TOKEN,105,DECODE_FAILURE
+TRUST_TOKEN,109,INVALID_KEY_ID
+TRUST_TOKEN,106,INVALID_METADATA
+TRUST_TOKEN,113,INVALID_METADATA_KEY
+TRUST_TOKEN,110,INVALID_TOKEN
TRUST_TOKEN,100,KEYGEN_FAILURE
+TRUST_TOKEN,108,NO_KEYS_CONFIGURED
+TRUST_TOKEN,112,NO_SRR_KEY_CONFIGURED
+TRUST_TOKEN,102,OVER_BATCHSIZE
+TRUST_TOKEN,104,SRR_SIGNATURE_ERROR
+TRUST_TOKEN,107,TOO_MANY_KEYS
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index 9778efb..ebae837 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -172,6 +172,11 @@
int ec_scalar_to_montgomery_inv_vartime(const EC_GROUP *group, EC_SCALAR *r,
const EC_SCALAR *a);
+// ec_scalar_select, in constant time, sets |out| to |a| if |mask| is all ones
+// and |b| if |mask| is all zeros.
+void ec_scalar_select(const EC_GROUP *group, EC_SCALAR *out, BN_ULONG mask,
+ const EC_SCALAR *a, const EC_SCALAR *b);
+
// Field elements.
diff --git a/crypto/fipsmodule/ec/scalar.c b/crypto/fipsmodule/ec/scalar.c
index 2d49682..038e5ff 100644
--- a/crypto/fipsmodule/ec/scalar.c
+++ b/crypto/fipsmodule/ec/scalar.c
@@ -98,6 +98,12 @@
OPENSSL_cleanse(tmp, sizeof(tmp));
}
+void ec_scalar_select(const EC_GROUP *group, EC_SCALAR *out, BN_ULONG mask,
+ const EC_SCALAR *a, const EC_SCALAR *b) {
+ const BIGNUM *order = &group->order;
+ bn_select_words(out->words, mask, a->words, b->words, order->width);
+}
+
void ec_scalar_to_montgomery(const EC_GROUP *group, EC_SCALAR *r,
const EC_SCALAR *a) {
const BIGNUM *order = &group->order;
diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h
new file mode 100644
index 0000000..92be6ee
--- /dev/null
+++ b/crypto/trust_token/internal.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2019, 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. */
+
+#ifndef OPENSSL_HEADER_TRUST_TOKEN_INTERNAL_H
+#define OPENSSL_HEADER_TRUST_TOKEN_INTERNAL_H
+
+#include <openssl/base.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/nid.h>
+
+#include "../fipsmodule/ec/internal.h"
+
+#include <openssl/trust_token.h>
+
+
+// PMBTokens is described in https://eprint.iacr.org/2020/072/20200324:214215
+// and provides anonymous tokens with private metadata. We implement the
+// construction with validity verification, described in appendix H,
+// construction 6, using P-521 as the group.
+
+// PMBTOKEN_NONCE_SIZE is the size of nonces used as part of the PMBToken
+// protocol.
+#define PMBTOKEN_NONCE_SIZE 64
+
+// PMBTOKEN_PRETOKEN represents the intermediate state a client keeps during a
+// PMBToken issuance operation.
+typedef struct pmb_pretoken_st {
+ uint8_t t[PMBTOKEN_NONCE_SIZE];
+ EC_SCALAR r;
+ EC_RAW_POINT T;
+ EC_RAW_POINT Tp;
+} PMBTOKEN_PRETOKEN;
+
+// PMBTOKEN_PRETOKEN_free releases the memory associated with |token|.
+void PMBTOKEN_PRETOKEN_free(PMBTOKEN_PRETOKEN *token);
+
+DEFINE_STACK_OF(PMBTOKEN_PRETOKEN)
+
+// PMBTOKEN_TOKEN represents the final token generated as part of a PMBToken
+// issuance operation.
+typedef struct pmb_token_st {
+ uint8_t t[PMBTOKEN_NONCE_SIZE];
+ EC_RAW_POINT S;
+ EC_RAW_POINT W;
+ EC_RAW_POINT Ws;
+} PMBTOKEN_TOKEN;
+
+// PMBTOKEN_TOKEN_free releases the memory associated with |token|.
+void PMBTOKEN_TOKEN_free(PMBTOKEN_TOKEN *token);
+
+// pmbtoken_blind generates a new blinded pretoken based on the configuration of
+// |ctx| as per the first stage of the AT.Usr operation and returns the
+// resulting pretoken.
+PMBTOKEN_PRETOKEN *pmbtoken_blind(void);
+
+// pmbtoken_sign signs a blinded point based on the configuration of |ctx|
+// and the key specified by |key_id| with a private metadata value of
+// |private_metadata| as per the AT.Sig operation and stores the resulting nonce
+// and points in |*out_s|, |*out_Wp|, and |*out_Wsp|. It returns one on success
+// and zero on failure.
+int pmbtoken_sign(const TRUST_TOKEN_ISSUER *ctx,
+ uint8_t out_s[PMBTOKEN_NONCE_SIZE], EC_RAW_POINT *out_Wp,
+ EC_RAW_POINT *out_Wsp, const EC_RAW_POINT *Tp,
+ uint32_t key_id, uint8_t private_metadata);
+
+// pmbtoken_unblind unblinds the result of an AT.Sig operation as per the final
+// stage of the AT.Usr operation and sets |*out_token| to the resulting token.
+// It returns one on success and zero on failure.
+int pmbtoken_unblind(PMBTOKEN_TOKEN *out_token,
+ const uint8_t s[PMBTOKEN_NONCE_SIZE],
+ const EC_RAW_POINT *Wp, const EC_RAW_POINT *Wsp,
+ const PMBTOKEN_PRETOKEN *pretoken);
+
+// pmbtoken_read verifies the validity of a PMBToken |token| using the key
+// specified by |key_id| and stores the value of the private metadata
+// bit in |*out_private_metadata|. It returns one if the token is valid and zero
+// otherwise.
+int pmbtoken_read(const TRUST_TOKEN_ISSUER *ctx, uint8_t *out_private_metadata,
+ const PMBTOKEN_TOKEN *token, uint32_t key_id);
+
+// Structure representing a single Trust Token public key with the specified ID.
+struct trust_token_client_key_st {
+ uint32_t id;
+ EC_RAW_POINT pub0;
+ EC_RAW_POINT pub1;
+ EC_RAW_POINT pubs;
+};
+
+// Structure representing a single Trust Token private key with the specified
+// ID.
+struct trust_token_issuer_key_st {
+ uint32_t id;
+ EC_SCALAR x0;
+ EC_SCALAR y0;
+ EC_SCALAR x1;
+ EC_SCALAR y1;
+ EC_SCALAR xs;
+ EC_SCALAR ys;
+};
+
+struct trust_token_client_st {
+ // max_batchsize is the maximum supported batchsize.
+ uint16_t max_batchsize;
+
+ // keys is the set of public keys that are supported by the client for
+ // issuance/redemptions.
+ struct trust_token_client_key_st keys[3];
+
+ // num_keys is the number of keys currently configured.
+ size_t num_keys;
+
+ // pretokens is the intermediate state during an active issuance.
+ STACK_OF(PMBTOKEN_PRETOKEN)* pretokens;
+
+ // srr_key is the public key used to verify the signature of the SRR.
+ EVP_PKEY *srr_key;
+};
+
+
+struct trust_token_issuer_st {
+ // max_batchsize is the maximum supported batchsize.
+ uint16_t max_batchsize;
+
+ // 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];
+
+ // num_keys is the number of keys currently configured.
+ size_t num_keys;
+
+ // srr_key is the private key used to sign the SRR.
+ EVP_PKEY *srr_key;
+
+ // metadata_key is the secret material used to encode the private metadata bit
+ // in the SRR.
+ uint8_t *metadata_key;
+ size_t metadata_key_len;
+};
+
+#endif // OPENSSL_HEADER_TRUST_TOKEN_INTERNAL_H
diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c
new file mode 100644
index 0000000..a7a87c8
--- /dev/null
+++ b/crypto/trust_token/pmbtoken.c
@@ -0,0 +1,444 @@
+/* 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/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 <openssl/sha.h>
+#include <openssl/trust_token.h>
+
+#include "../ec_extra/internal.h"
+#include "../fipsmodule/bn/internal.h"
+#include "../fipsmodule/ec/internal.h"
+
+#include "internal.h"
+
+
+// get_h returns the generator H for PMBTokens.
+//
+// x: 66591746412783875033873351891229753622964683369847172829242944646280287810
+// 81195403447871073952234683395256591180452378091073292247502091640572714366
+// 588045092
+// y: 12347430519393087872533727997980072129796839266949808299436682045034861065
+// 18810630511924722292325611253427311923464047364545304196431830383014967865
+// 162306253
+//
+// This point was generated with the following Python code.
+
+/*
+import hashlib
+
+SEED_H = 'PrivacyPass H'
+
+A = -3
+B = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00
+P = 2**521 - 1
+
+def get_y(x):
+ y2 = (x**3 + A*x + B) % P
+ y = pow(y2, (P+1)/4, P)
+ if (y*y) % P != y2:
+ raise ValueError("point not on curve")
+ return y
+
+def bit(h,i):
+ return (ord(h[i/8]) >> (i%8)) & 1
+
+b = 521
+def decode_point(so):
+ s = hashlib.sha256(so + '0').digest() + hashlib.sha256(so + '1').digest() + \
+ hashlib.sha256(so + '2').digest()
+
+ x = 0
+ for i in range(0,b):
+ x = x + (long(bit(s,i))<<i)
+ if x >= P:
+ raise ValueError("x out of range")
+ y = get_y(x)
+ if y & 1 != bit(s,b-1): y = P-y
+ return (x, y)
+
+
+def gen_point(seed):
+ v = hashlib.sha256(seed).digest()
+ it = 1
+ while True:
+ try:
+ x,y = decode_point(v)
+ except Exception, e:
+ print e
+ it += 1
+ v = hashlib.sha256(v).digest()
+ continue
+ print "Found in %d iterations:" % it
+ print " x = %d" % x
+ print " y = %d" % y
+ print " Encoded (hex): (%x, %x)" % (x, y)
+ return (x, y)
+
+if __name__ == "__main__":
+ gen_point(SEED_H)
+*/
+
+static const uint8_t kDefaultAdditionalData[32] = {0};
+
+// TODO(svaldez): Update to use hash2curve to generate H.
+static int get_h(EC_RAW_POINT *out_h) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ static const uint8_t kH[] = {
+ 0x04, 0x01, 0xf0, 0xa9, 0xf7, 0x9e, 0xbc, 0x12, 0x6c, 0xef, 0xd1, 0xab,
+ 0x29, 0x10, 0x03, 0x6f, 0x4e, 0xf5, 0xbd, 0xeb, 0x0f, 0x6b, 0xc0, 0x5c,
+ 0x0e, 0xce, 0xfe, 0x59, 0x45, 0xd1, 0x3e, 0x25, 0x33, 0x7e, 0x4c, 0xda,
+ 0x64, 0x53, 0x54, 0x4e, 0xf9, 0x76, 0x0d, 0x6d, 0xc5, 0x39, 0x2a, 0xd4,
+ 0xce, 0x84, 0x6e, 0x31, 0xc2, 0x86, 0x21, 0xf9, 0x5c, 0x98, 0xb9, 0x3d,
+ 0x01, 0x74, 0x9f, 0xc5, 0x1e, 0x47, 0x24, 0x00, 0x5c, 0x17, 0x62, 0x51,
+ 0x7d, 0x32, 0x5e, 0x29, 0xac, 0x52, 0x14, 0x75, 0x6f, 0x36, 0xd9, 0xc7,
+ 0xfa, 0xbb, 0xa9, 0x3b, 0x9d, 0x70, 0x49, 0x1e, 0xb4, 0x53, 0xbc, 0x55,
+ 0xea, 0xad, 0x8f, 0x26, 0x1d, 0xe0, 0xbc, 0xf3, 0x50, 0x5c, 0x7e, 0x66,
+ 0x41, 0xb5, 0x61, 0x70, 0x12, 0x72, 0xac, 0x6a, 0xb0, 0x6e, 0x78, 0x3d,
+ 0x17, 0x08, 0xe3, 0xdf, 0x3c, 0xff, 0xa6, 0xa0, 0xea, 0x96, 0x67, 0x92,
+ 0xcd,
+ };
+
+ return ec_point_from_uncompressed(group, out_h, kH, sizeof(kH));
+}
+
+static int mul_g_and_p(const EC_GROUP *group, EC_RAW_POINT *out,
+ const EC_RAW_POINT *g, const EC_SCALAR *g_scalar,
+ const EC_RAW_POINT *p, const EC_SCALAR *p_scalar) {
+ EC_RAW_POINT tmp1, tmp2;
+ if (!ec_point_mul_scalar(group, &tmp1, g, g_scalar) ||
+ !ec_point_mul_scalar(group, &tmp2, p, p_scalar)) {
+ return 0;
+ }
+
+ group->meth->add(group, out, &tmp1, &tmp2);
+ return 1;
+}
+
+// generate_keypair generates a keypair for the PMBTokens construction.
+// |out_x| and |out_y| are set to the secret half of the keypair, while
+// |*out_pub| is set to the public half of the keypair. It returns one on
+// success and zero on failure.
+static int generate_keypair(EC_SCALAR *out_x, EC_SCALAR *out_y,
+ EC_RAW_POINT *out_pub, const EC_GROUP *group) {
+ EC_RAW_POINT h, tmp1, tmp2;
+ if (!get_h(&h) ||
+ !ec_random_nonzero_scalar(group, out_x, kDefaultAdditionalData) ||
+ !ec_random_nonzero_scalar(group, out_y, kDefaultAdditionalData) ||
+ !ec_point_mul_scalar_base(group, &tmp1, out_x) ||
+ !ec_point_mul_scalar(group, &tmp2, &h, out_y)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ group->meth->add(group, out_pub, &tmp1, &tmp2);
+ return 1;
+}
+
+static int point_to_cbb(CBB *out, const EC_GROUP *group,
+ const EC_RAW_POINT *point) {
+ size_t len =
+ ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0);
+ if (len == 0) {
+ return 0;
+ }
+ uint8_t *p;
+ return CBB_add_space(out, &p, len) &&
+ ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, p,
+ len) == len;
+}
+
+int TRUST_TOKEN_generate_key(uint8_t *out_priv_key, size_t *out_priv_key_len,
+ size_t max_priv_key_len, uint8_t *out_pub_key,
+ size_t *out_pub_key_len, size_t max_pub_key_len,
+ uint32_t id) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ EC_RAW_POINT pub0, pub1, pubs;
+ EC_SCALAR x0, y0, x1, y1, xs, ys;
+ if (!generate_keypair(&x0, &y0, &pub0, group) ||
+ !generate_keypair(&x1, &y1, &pub1, group) ||
+ !generate_keypair(&xs, &ys, &pubs, group)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
+ return 0;
+ }
+
+ int ret = 0;
+ CBB cbb;
+ CBB_zero(&cbb);
+ size_t scalar_len = BN_num_bytes(&group->order);
+ if (!CBB_init_fixed(&cbb, out_priv_key, max_priv_key_len) ||
+ !CBB_add_u32(&cbb, id)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+ goto err;
+ }
+
+ const EC_SCALAR *scalars[] = {&x0, &y0, &x1, &y1, &xs, &ys};
+ for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(scalars); i++) {
+ uint8_t *buf;
+ if (!CBB_add_space(&cbb, &buf, scalar_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+ goto err;
+ }
+ ec_scalar_to_bytes(group, buf, &scalar_len, scalars[i]);
+ }
+
+ if (!CBB_finish(&cbb, NULL, out_priv_key_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+ goto err;
+ }
+
+ CBB pub_cbb;
+ if (!CBB_init_fixed(&cbb, out_pub_key, max_pub_key_len) ||
+ !CBB_add_u32(&cbb, id) ||
+ !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
+ !point_to_cbb(&pub_cbb, group, &pub0) ||
+ !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
+ !point_to_cbb(&pub_cbb, group, &pub1) ||
+ !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
+ !point_to_cbb(&pub_cbb, group, &pubs) ||
+ !CBB_finish(&cbb, NULL, out_pub_key_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ CBB_cleanup(&cbb);
+ return ret;
+}
+
+void PMBTOKEN_PRETOKEN_free(PMBTOKEN_PRETOKEN *pretoken) {
+ OPENSSL_free(pretoken);
+}
+
+void PMBTOKEN_TOKEN_free(PMBTOKEN_TOKEN *token) {
+ OPENSSL_free(token);
+}
+
+// hash_t implements the H_t operation in PMBTokens. It returns on on success
+// and zero on error.
+static int hash_t(EC_GROUP *group, EC_RAW_POINT *out,
+ const uint8_t t[PMBTOKEN_NONCE_SIZE]) {
+ const uint8_t kHashTLabel[] = "PMBTokensV0 HashT";
+ return ec_hash_to_curve_p521_xmd_sha512_sswu(
+ group, out, kHashTLabel, sizeof(kHashTLabel), t, PMBTOKEN_NONCE_SIZE);
+}
+
+// hash_s implements the H_s operation in PMBTokens. It returns on on success
+// and zero on error.
+static int hash_s(EC_GROUP *group, EC_RAW_POINT *out, const EC_RAW_POINT *t,
+ const uint8_t s[PMBTOKEN_NONCE_SIZE]) {
+ const uint8_t kHashSLabel[] = "PMBTokensV0 HashS";
+ int ret = 0;
+ CBB cbb;
+ uint8_t *buf = NULL;
+ size_t len;
+ if (!CBB_init(&cbb, 0) ||
+ !point_to_cbb(&cbb, group, t) ||
+ !CBB_add_bytes(&cbb, s, PMBTOKEN_NONCE_SIZE) ||
+ !CBB_finish(&cbb, &buf, &len) ||
+ !ec_hash_to_curve_p521_xmd_sha512_sswu(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;
+}
+
+PMBTOKEN_PRETOKEN *pmbtoken_blind(void) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return NULL;
+ }
+
+ PMBTOKEN_PRETOKEN *pretoken = OPENSSL_malloc(sizeof(PMBTOKEN_PRETOKEN));
+ if (pretoken == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ RAND_bytes(pretoken->t, sizeof(pretoken->t));
+
+ // We sample |pretoken->r| in Montgomery form to simplify inverting.
+ if (!ec_random_nonzero_scalar(group, &pretoken->r,
+ kDefaultAdditionalData)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ EC_SCALAR rinv;
+ ec_scalar_inv0_montgomery(group, &rinv, &pretoken->r);
+ // Convert both out of Montgomery form.
+ ec_scalar_from_montgomery(group, &pretoken->r, &pretoken->r);
+ ec_scalar_from_montgomery(group, &rinv, &rinv);
+
+ if (!hash_t(group, &pretoken->T, pretoken->t) ||
+ !ec_point_mul_scalar(group, &pretoken->Tp, &pretoken->T, &rinv)) {
+ goto err;
+ }
+
+ return pretoken;
+
+err:
+ OPENSSL_free(pretoken);
+ return NULL;
+}
+
+int pmbtoken_sign(const TRUST_TOKEN_ISSUER *ctx,
+ uint8_t out_s[PMBTOKEN_NONCE_SIZE], EC_RAW_POINT *out_Wp,
+ EC_RAW_POINT *out_Wsp, const EC_RAW_POINT *Tp,
+ uint32_t key_id, uint8_t private_metadata) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ if (ctx->num_keys == 0) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_NO_KEYS_CONFIGURED);
+ return 0;
+ }
+ const struct trust_token_issuer_key_st *key = NULL;
+ for (size_t i = 0; i < ctx->num_keys; i++) {
+ if (ctx->keys[i].id == key_id) {
+ key = &ctx->keys[i];
+ break;
+ }
+ }
+
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_KEY_ID);
+ return 0;
+ }
+
+ EC_SCALAR xb, yb;
+ BN_ULONG mask = ((BN_ULONG)0) - (private_metadata&1);
+ ec_scalar_select(group, &xb, mask, &key->x1, &key->x0);
+ ec_scalar_select(group, &yb, mask, &key->y1, &key->y0);
+
+ RAND_bytes(out_s, PMBTOKEN_NONCE_SIZE);
+
+ EC_RAW_POINT Sp;
+ if (!hash_s(group, &Sp, Tp, out_s)) {
+ return 0;
+ }
+
+ if (!mul_g_and_p(group, out_Wp, Tp, &xb, &Sp, &yb) ||
+ !mul_g_and_p(group, out_Wsp, Tp, &key->xs, &Sp, &key->ys)) {
+ return 0;
+ }
+
+ // TODO: DLEQ Proofs
+ return 1;
+}
+
+int pmbtoken_unblind(PMBTOKEN_TOKEN *out_token,
+ const uint8_t s[PMBTOKEN_NONCE_SIZE],
+ const EC_RAW_POINT *Wp, const EC_RAW_POINT *Wsp,
+ const PMBTOKEN_PRETOKEN *pretoken) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ // TODO: Check DLEQ Proofs
+
+ EC_RAW_POINT Sp;
+ if (!hash_s(group, &Sp, &pretoken->Tp, s)) {
+ return 0;
+ }
+
+ OPENSSL_memcpy(out_token->t, pretoken->t, PMBTOKEN_NONCE_SIZE);
+ if (!ec_point_mul_scalar(group, &out_token->S, &Sp, &pretoken->r) ||
+ !ec_point_mul_scalar(group, &out_token->W, Wp, &pretoken->r) ||
+ !ec_point_mul_scalar(group, &out_token->Ws, Wsp, &pretoken->r)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int pmbtoken_read(const TRUST_TOKEN_ISSUER *ctx, uint8_t *out_private_metadata,
+ const PMBTOKEN_TOKEN *token, uint32_t key_id) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ if (ctx->num_keys == 0) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_NO_KEYS_CONFIGURED);
+ return 0;
+ }
+ const struct trust_token_issuer_key_st *key = NULL;
+ for (size_t i = 0; i < ctx->num_keys; i++) {
+ if (ctx->keys[i].id == key_id) {
+ key = &ctx->keys[i];
+ break;
+ }
+ }
+
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_KEY_ID);
+ return 0;
+ }
+
+ EC_RAW_POINT T;
+ if (!hash_t(group, &T, token->t)) {
+ return 0;
+ }
+
+ EC_RAW_POINT calculated;
+ // Check the validity of the token.
+ if (!mul_g_and_p(group, &calculated, &T, &key->xs, &token->S, &key->ys) ||
+ !ec_GFp_simple_points_equal(group, &calculated, &token->Ws)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BAD_VALIDITY_CHECK);
+ return 0;
+ }
+
+ EC_RAW_POINT W0, W1;
+ if (!mul_g_and_p(group, &W0, &T, &key->x0, &token->S, &key->y0) ||
+ !mul_g_and_p(group, &W1, &T, &key->x1, &token->S, &key->y1)) {
+ return 0;
+ }
+
+ const int is_W0 = ec_GFp_simple_points_equal(group, &W0, &token->W);
+ const int is_W1 = ec_GFp_simple_points_equal(group, &W1, &token->W);
+ const int is_valid = is_W0 ^ is_W1;
+ if (!is_valid) {
+ // Invalid tokens will fail the validity check above.
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ *out_private_metadata = is_W1;
+ return 1;
+}
diff --git a/crypto/trust_token/privacy_pass.c b/crypto/trust_token/privacy_pass.c
deleted file mode 100644
index accb28a..0000000
--- a/crypto/trust_token/privacy_pass.c
+++ /dev/null
@@ -1,241 +0,0 @@
-/* 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/bn.h>
-#include <openssl/bytestring.h>
-#include <openssl/ec.h>
-#include <openssl/err.h>
-#include <openssl/mem.h>
-#include <openssl/nid.h>
-#include <openssl/trust_token.h>
-
-#include "../fipsmodule/bn/internal.h"
-#include "../fipsmodule/ec/internal.h"
-
-
-// Privacy Pass uses a custom elliptic curve construction described in
-// https://eprint.iacr.org/2020/072.pdf (section 7, construction 4). Ths
-// construction provides anonymous tokens with private metadata and validity
-// verification.
-
-// get_h returns a randomly selected point for the Privacy Pass protocol.
-//
-// x: 66591746412783875033873351891229753622964683369847172829242944646280287810
-// 81195403447871073952234683395256591180452378091073292247502091640572714366
-// 588045092
-// y: 12347430519393087872533727997980072129796839266949808299436682045034861065
-// 18810630511924722292325611253427311923464047364545304196431830383014967865
-// 162306253
-//
-// This point was generated with the following Python code.
-
-/*
-import hashlib
-
-SEED_H = 'PrivacyPass H'
-
-A = -3
-B = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00
-P = 2**521 - 1
-
-def get_y(x):
- y2 = (x**3 + A*x + B) % P
- y = pow(y2, (P+1)/4, P)
- if (y*y) % P != y2:
- raise ValueError("point not on curve")
- return y
-
-def bit(h,i):
- return (ord(h[i/8]) >> (i%8)) & 1
-
-b = 521
-def decode_point(so):
- s = hashlib.sha256(so + '0').digest() + hashlib.sha256(so + '1').digest() + \
- hashlib.sha256(so + '2').digest()
-
- x = 0
- for i in range(0,b):
- x = x + (long(bit(s,i))<<i)
- if x >= P:
- raise ValueError("x out of range")
- y = get_y(x)
- if y & 1 != bit(s,b-1): y = P-y
- return (x, y)
-
-
-def gen_point(seed):
- v = hashlib.sha256(seed).digest()
- it = 1
- while True:
- try:
- x,y = decode_point(v)
- except Exception, e:
- print e
- it += 1
- v = hashlib.sha256(v).digest()
- continue
- print "Found in %d iterations:" % it
- print " x = %d" % x
- print " y = %d" % y
- print " Encoded (hex): (%x, %x)" % (x, y)
- return (x, y)
-
-if __name__ == "__main__":
- gen_point(SEED_H)
-*/
-
-static EC_POINT *get_h(void) {
- EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
- if (group == NULL) {
- return NULL;
- }
-
- static const BN_ULONG kHGenX[] = {
- TOBN(0x3d01749f, 0xc51e4724),
- TOBN(0x31c28621, 0xf95c98b9),
- TOBN(0x6dc5392a, 0xd4ce846e),
- TOBN(0xda645354, 0x4ef9760d),
- TOBN(0x5945d13e, 0x25337e4c),
- TOBN(0xeb0f6bc0, 0x5c0ecefe),
- TOBN(0xab291003, 0x6f4ef5bd),
- TOBN(0xa9f79ebc, 0x126cefd1),
- 0x000001f0,
- };
- static const BIGNUM kX = STATIC_BIGNUM(kHGenX);
-
- static const BN_ULONG kHGenY[] = {
- TOBN(0xffa6a0ea, 0x966792cd),
- TOBN(0x6e783d17, 0x08e3df3c),
- TOBN(0xb5617012, 0x72ac6ab0),
- TOBN(0xe0bcf350, 0x5c7e6641),
- TOBN(0x53bc55ea, 0xad8f261d),
- TOBN(0xbba93b9d, 0x70491eb4),
- TOBN(0x5214756f, 0x36d9c7fa),
- TOBN(0x1762517d, 0x325e29ac),
- 0x0000005c,
- };
- static const BIGNUM kY = STATIC_BIGNUM(kHGenY);
-
- EC_POINT *h = EC_POINT_new(group);
- if (h == NULL ||
- !EC_POINT_set_affine_coordinates_GFp(group, h, &kX, &kY, NULL)) {
- EC_POINT_free(h);
- return NULL;
- }
- return h;
-}
-
-// generate_keypair generates a keypair for the Private Metadata construction.
-// |out_x| and |out_y| are set to the secret half of the keypair, while
-// |*out_pub| is set to the public half of the keypair. It returns one on
-// success and zero on failure.
-static int generate_keypair(EC_SCALAR *out_x, EC_SCALAR *out_y,
- EC_RAW_POINT *out_pub, const EC_GROUP *group) {
- EC_POINT *h = get_h();
- if (h == NULL) {
- return 0;
- }
-
- static const uint8_t kDefaultAdditionalData[32] = {0};
- EC_RAW_POINT tmp1, tmp2;
- if (!ec_random_nonzero_scalar(group, out_x, kDefaultAdditionalData) ||
- !ec_random_nonzero_scalar(group, out_y, kDefaultAdditionalData) ||
- !ec_point_mul_scalar_base(group, &tmp1, out_x) ||
- !ec_point_mul_scalar(group, &tmp2, &h->raw, out_y)) {
- EC_POINT_free(h);
- OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
- return 0;
- }
- group->meth->add(group, out_pub, &tmp1, &tmp2);
-
- EC_POINT_free(h);
- return 1;
-}
-
-static int point_to_cbb(CBB *out, const EC_GROUP *group,
- const EC_RAW_POINT *point) {
- size_t len =
- ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0);
- if (len == 0) {
- return 0;
- }
- uint8_t *p;
- return CBB_add_space(out, &p, len) &&
- ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, p,
- len) == len;
-}
-
-int TRUST_TOKEN_generate_key(uint8_t *out_priv_key, size_t *out_priv_key_len,
- size_t max_priv_key_len, uint8_t *out_pub_key,
- size_t *out_pub_key_len, size_t max_pub_key_len,
- uint32_t id) {
- int ok = 0;
- CBB cbb;
- CBB_zero(&cbb);
- EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
- if (group == NULL) {
- return 0;
- }
-
- EC_RAW_POINT pub0, pub1, pubs;
- EC_SCALAR x0, y0, x1, y1, xs, ys;
- if (!generate_keypair(&x0, &y0, &pub0, group) ||
- !generate_keypair(&x1, &y1, &pub1, group) ||
- !generate_keypair(&xs, &ys, &pubs, group)) {
- OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_KEYGEN_FAILURE);
- goto err;
- }
-
- size_t scalar_len = BN_num_bytes(&group->order);
- if (!CBB_init_fixed(&cbb, out_priv_key, max_priv_key_len) ||
- !CBB_add_u32(&cbb, id)) {
- OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
- goto err;
- }
-
- const EC_SCALAR *scalars[] = {&x0, &y0, &x1, &y1, &xs, &ys};
- for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(scalars); i++) {
- uint8_t *buf;
- if (!CBB_add_space(&cbb, &buf, scalar_len)) {
- OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
- goto err;
- }
- ec_scalar_to_bytes(group, buf, &scalar_len, scalars[i]);
- }
-
- if (!CBB_finish(&cbb, NULL, out_priv_key_len)) {
- OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
- goto err;
- }
-
- CBB pub_cbb;
- if (!CBB_init_fixed(&cbb, out_pub_key, max_pub_key_len) ||
- !CBB_add_u32(&cbb, id) ||
- !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
- !point_to_cbb(&pub_cbb, group, &pub0) ||
- !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
- !point_to_cbb(&pub_cbb, group, &pub1) ||
- !CBB_add_u16_length_prefixed(&cbb, &pub_cbb) ||
- !point_to_cbb(&pub_cbb, group, &pubs) ||
- !CBB_finish(&cbb, NULL, out_pub_key_len)) {
- OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BUFFER_TOO_SMALL);
- goto err;
- }
-
- ok = 1;
-
-err:
- CBB_cleanup(&cbb);
- return ok;
-}
diff --git a/crypto/trust_token/trust_token.c b/crypto/trust_token/trust_token.c
new file mode 100644
index 0000000..186fdbc
--- /dev/null
+++ b/crypto/trust_token/trust_token.c
@@ -0,0 +1,720 @@
+/* Copyright (c) 2019, 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/bytestring.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/sha.h>
+#include <openssl/trust_token.h>
+
+#include "internal.h"
+
+
+// The Trust Token API is described in
+// https://github.com/WICG/trust-token-api/blob/master/README.md and provides a
+// protocol for issuing and redeeming tokens built on top of the PMBTokens
+// construction.
+
+static int cbb_add_raw_point(CBB *cbb, const EC_GROUP *group,
+ const EC_RAW_POINT *point) {
+ size_t len =
+ ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0);
+ if (len == 0) {
+ return 0;
+ }
+ uint8_t *p;
+ return CBB_add_u16(cbb, len) && CBB_add_space(cbb, &p, len) &&
+ ec_point_to_bytes(group, point, POINT_CONVERSION_UNCOMPRESSED, p,
+ len) == len;
+}
+
+static int cbs_get_raw_point(CBS *cbs, const EC_GROUP *group,
+ EC_RAW_POINT *out) {
+ CBS child;
+ return CBS_get_u16_length_prefixed(cbs, &child) &&
+ ec_point_from_uncompressed(group, out, CBS_data(&child),
+ CBS_len(&child));
+}
+
+TRUST_TOKEN *TRUST_TOKEN_new(const uint8_t *data, size_t len) {
+ TRUST_TOKEN *ret = OPENSSL_malloc(sizeof(TRUST_TOKEN));
+ if (ret == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ OPENSSL_memset(ret, 0, sizeof(TRUST_TOKEN));
+ ret->data = OPENSSL_memdup(data, len);
+ if (len != 0 && ret->data == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(ret);
+ return NULL;
+ }
+ ret->len = len;
+ return ret;
+}
+
+void TRUST_TOKEN_free(TRUST_TOKEN *token) {
+ if (token == NULL) {
+ return;
+ }
+ OPENSSL_free(token->data);
+ OPENSSL_free(token);
+}
+
+TRUST_TOKEN_CLIENT *TRUST_TOKEN_CLIENT_new(uint16_t max_batchsize) {
+ TRUST_TOKEN_CLIENT *ret = OPENSSL_malloc(sizeof(TRUST_TOKEN_CLIENT));
+ if (ret == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ OPENSSL_memset(ret, 0, sizeof(TRUST_TOKEN_CLIENT));
+ ret->max_batchsize = max_batchsize;
+ ret->pretokens = sk_PMBTOKEN_PRETOKEN_new_null();
+ if (ret->pretokens == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(ret);
+ return NULL;
+ }
+ return ret;
+}
+
+void TRUST_TOKEN_CLIENT_free(TRUST_TOKEN_CLIENT *ctx) {
+ if (ctx == NULL) {
+ return;
+ }
+ EVP_PKEY_free(ctx->srr_key);
+ sk_PMBTOKEN_PRETOKEN_pop_free(ctx->pretokens, PMBTOKEN_PRETOKEN_free);
+ OPENSSL_free(ctx);
+}
+
+int TRUST_TOKEN_CLIENT_add_key(TRUST_TOKEN_CLIENT *ctx, size_t *out_key_index,
+ const uint8_t *key, size_t key_len) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ if (ctx->num_keys == OPENSSL_ARRAY_SIZE(ctx->keys)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_TOO_MANY_KEYS);
+ return 0;
+ }
+
+ struct trust_token_client_key_st *key_s = &ctx->keys[ctx->num_keys];
+
+ CBS cbs;
+ CBS_init(&cbs, key, key_len);
+ uint32_t key_id;
+ if (!CBS_get_u32(&cbs, &key_id) ||
+ !cbs_get_raw_point(&cbs, group, &key_s->pub0) ||
+ !cbs_get_raw_point(&cbs, group, &key_s->pub1) ||
+ !cbs_get_raw_point(&cbs, group, &key_s->pubs) ||
+ CBS_len(&cbs) != 0) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ return 0;
+ }
+ key_s->id = key_id;
+ *out_key_index = ctx->num_keys;
+ ctx->num_keys += 1;
+ return 1;
+}
+
+int TRUST_TOKEN_CLIENT_set_srr_key(TRUST_TOKEN_CLIENT *ctx, EVP_PKEY *key) {
+ EVP_PKEY_free(ctx->srr_key);
+ EVP_PKEY_up_ref(key);
+ ctx->srr_key = key;
+ return 1;
+}
+
+int TRUST_TOKEN_CLIENT_begin_issuance(TRUST_TOKEN_CLIENT *ctx, uint8_t **out,
+ size_t *out_len, size_t count) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ if (count > ctx->max_batchsize) {
+ count = ctx->max_batchsize;
+ }
+
+ int ret = 0;
+ CBB request;
+ if (!CBB_init(&request, 0) ||
+ !CBB_add_u16(&request, count)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ PMBTOKEN_PRETOKEN *pretoken = pmbtoken_blind();
+ if (pretoken == NULL ||
+ !cbb_add_raw_point(&request, group, &pretoken->Tp) ||
+ !sk_PMBTOKEN_PRETOKEN_push(ctx->pretokens, pretoken)) {
+ PMBTOKEN_PRETOKEN_free(pretoken);
+ goto err;
+ }
+ }
+
+ if (!CBB_finish(&request, out, out_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ CBB_cleanup(&request);
+ return ret;
+}
+
+STACK_OF(TRUST_TOKEN) *
+ TRUST_TOKEN_CLIENT_finish_issuance(TRUST_TOKEN_CLIENT *ctx,
+ size_t *out_key_index,
+ const uint8_t *response,
+ size_t response_len) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ CBS in;
+ CBS_init(&in, response, response_len);
+ uint16_t count;
+ uint32_t key_id;
+ if (!CBS_get_u16(&in, &count) ||
+ !CBS_get_u32(&in, &key_id)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ return NULL;
+ }
+
+ size_t key_index = 0;
+ const struct trust_token_client_key_st *key = NULL;
+ for (size_t i = 0; i < ctx->num_keys; i++) {
+ if (ctx->keys[i].id == key_id) {
+ key_index = i;
+ key = &ctx->keys[i];
+ break;
+ }
+ }
+
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_KEY_ID);
+ return NULL;
+ }
+
+ if (count > sk_PMBTOKEN_PRETOKEN_num(ctx->pretokens)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ return NULL;
+ }
+
+ int ok = 0;
+ STACK_OF(TRUST_TOKEN) *tokens = sk_TRUST_TOKEN_new_null();
+ if (tokens == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ uint8_t s[PMBTOKEN_NONCE_SIZE];
+ EC_RAW_POINT Wp, Wsp;
+ if (!CBS_copy_bytes(&in, s, PMBTOKEN_NONCE_SIZE) ||
+ !cbs_get_raw_point(&in, group, &Wp) ||
+ !cbs_get_raw_point(&in, group, &Wsp)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ goto err;
+ }
+
+ PMBTOKEN_PRETOKEN *pretoken = sk_PMBTOKEN_PRETOKEN_value(ctx->pretokens, i);
+ PMBTOKEN_TOKEN pmbtoken;
+ if (!pmbtoken_unblind(&pmbtoken, s, &Wp, &Wsp, pretoken)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ goto err;
+ }
+
+ int token_ok = 0;
+ TRUST_TOKEN *token = NULL;
+ CBB token_cbb;
+ if (!CBB_init(&token_cbb, 0) ||
+ !CBB_add_u32(&token_cbb, key_id) ||
+ !CBB_add_bytes(&token_cbb, pmbtoken.t, PMBTOKEN_NONCE_SIZE) ||
+ !cbb_add_raw_point(&token_cbb, group, &pmbtoken.S) ||
+ !cbb_add_raw_point(&token_cbb, group, &pmbtoken.W) ||
+ !cbb_add_raw_point(&token_cbb, group, &pmbtoken.Ws)) {
+ goto token_err;
+ }
+
+ token = TRUST_TOKEN_new(CBB_data(&token_cbb), CBB_len(&token_cbb));
+ if (token == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto token_err;
+ }
+
+ if (!sk_TRUST_TOKEN_push(tokens, token)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto token_err;
+ }
+ token_ok = 1;
+
+ token_err:
+ CBB_cleanup(&token_cbb);
+ if (!token_ok) {
+ TRUST_TOKEN_free(token);
+ goto err;
+ }
+ }
+
+ sk_PMBTOKEN_PRETOKEN_pop_free(ctx->pretokens, PMBTOKEN_PRETOKEN_free);
+ ctx->pretokens = NULL;
+
+ *out_key_index = key_index;
+ ok = 1;
+
+err:
+ if (!ok) {
+ sk_TRUST_TOKEN_pop_free(tokens, TRUST_TOKEN_free);
+ return NULL;
+ } else {
+ return tokens;
+ }
+}
+
+int TRUST_TOKEN_CLIENT_begin_redemption(TRUST_TOKEN_CLIENT *ctx, uint8_t **out,
+ size_t *out_len,
+ const TRUST_TOKEN *token,
+ const uint8_t *data, size_t data_len,
+ uint64_t time) {
+ CBB request, token_inner, inner;
+ if (!CBB_init(&request, 0) ||
+ !CBB_add_u16_length_prefixed(&request, &token_inner) ||
+ !CBB_add_bytes(&token_inner, token->data, token->len) ||
+ !CBB_add_u16_length_prefixed(&request, &inner) ||
+ !CBB_add_bytes(&inner, data, data_len) ||
+ !CBB_add_u64(&request, time) ||
+ !CBB_finish(&request, out, out_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ CBB_cleanup(&request);
+ return 0;
+ }
+ return 1;
+}
+
+int TRUST_TOKEN_CLIENT_finish_redemption(TRUST_TOKEN_CLIENT *ctx,
+ uint8_t **out_srr, size_t *out_srr_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) ||
+ !CBS_get_u16_length_prefixed(&in, &sig)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_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;
+ size_t srr_len, sig_len;
+ if (!CBS_stow(&srr, &srr_buf, &srr_len) ||
+ !CBS_stow(&sig, &sig_buf, &sig_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(srr_buf);
+ OPENSSL_free(sig_buf);
+ return 0;
+ }
+
+ *out_srr = srr_buf;
+ *out_srr_len = srr_len;
+ *out_sig = sig_buf;
+ *out_sig_len = sig_len;
+ return 1;
+}
+
+TRUST_TOKEN_ISSUER *TRUST_TOKEN_ISSUER_new(uint16_t max_batchsize) {
+ TRUST_TOKEN_ISSUER *ret = OPENSSL_malloc(sizeof(TRUST_TOKEN_ISSUER));
+ if (ret == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ OPENSSL_memset(ret, 0, sizeof(TRUST_TOKEN_ISSUER));
+ ret->max_batchsize = max_batchsize;
+ return ret;
+}
+
+void TRUST_TOKEN_ISSUER_free(TRUST_TOKEN_ISSUER *ctx) {
+ if (ctx == NULL) {
+ return;
+ }
+ EVP_PKEY_free(ctx->srr_key);
+ OPENSSL_free(ctx->metadata_key);
+ OPENSSL_free(ctx);
+}
+
+int TRUST_TOKEN_ISSUER_add_key(TRUST_TOKEN_ISSUER *ctx, const uint8_t *key,
+ size_t key_len) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ if (ctx->num_keys == OPENSSL_ARRAY_SIZE(ctx->keys)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_TOO_MANY_KEYS);
+ return 0;
+ }
+
+ CBS cbs, tmp;
+ CBS_init(&cbs, key, key_len);
+ uint32_t key_id;
+ if (!CBS_get_u32(&cbs, &key_id)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ return 0;
+ }
+
+ size_t scalar_len = BN_num_bytes(&group->order);
+ struct trust_token_issuer_key_st *key_s = &(ctx->keys[ctx->num_keys]);
+ EC_SCALAR *scalars[] = {&key_s->x0, &key_s->y0, &key_s->x1,
+ &key_s->y1, &key_s->xs, &key_s->ys};
+ for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(scalars); i++) {
+ if (!CBS_get_bytes(&cbs, &tmp, scalar_len) ||
+ !ec_scalar_from_bytes(group, scalars[i], CBS_data(&tmp),
+ CBS_len(&tmp))) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ return 0;
+ }
+ }
+ key_s->id = key_id;
+ ctx->num_keys += 1;
+ return 1;
+}
+
+int TRUST_TOKEN_ISSUER_set_srr_key(TRUST_TOKEN_ISSUER *ctx, EVP_PKEY *key) {
+ EVP_PKEY_free(ctx->srr_key);
+ EVP_PKEY_up_ref(key);
+ ctx->srr_key = key;
+ return 1;
+}
+
+int TRUST_TOKEN_ISSUER_set_metadata_key(TRUST_TOKEN_ISSUER *ctx,
+ const uint8_t *key, size_t len) {
+ if (len < 32) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_METADATA_KEY);
+ }
+ OPENSSL_free(ctx->metadata_key);
+ ctx->metadata_key_len = 0;
+ ctx->metadata_key = OPENSSL_memdup(key, len);
+ if (ctx->metadata_key == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ ctx->metadata_key_len = len;
+ return 1;
+}
+
+int TRUST_TOKEN_ISSUER_issue(const TRUST_TOKEN_ISSUER *ctx, uint8_t **out,
+ size_t *out_len, uint8_t *out_tokens_issued,
+ const uint8_t *request, size_t request_len,
+ uint32_t public_metadata, uint8_t private_metadata,
+ size_t max_issuance) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ if (max_issuance > ctx->max_batchsize) {
+ max_issuance = ctx->max_batchsize;
+ }
+
+ int found_public_metadata = 0;
+ for (size_t i = 0; i < ctx->num_keys; i++) {
+ if (ctx->keys[i].id == public_metadata) {
+ found_public_metadata = 1;
+ break;
+ }
+ }
+
+ if (!found_public_metadata || private_metadata > 1) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_METADATA);
+ return 0;
+ }
+
+ CBS in;
+ CBS_init(&in, request, request_len);
+
+ CBB response;
+ if (!CBB_init(&response, 0)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ int ret = 0;
+ uint16_t count;
+ if (!CBS_get_u16(&in, &count)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ goto err;
+ }
+
+ if (count > max_issuance) {
+ count = max_issuance;
+ }
+
+ if (!CBB_add_u16(&response, count) ||
+ !CBB_add_u32(&response, public_metadata)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ EC_RAW_POINT Tp;
+ if (!cbs_get_raw_point(&in, group, &Tp)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_FAILURE);
+ goto err;
+ }
+
+ uint8_t s[PMBTOKEN_NONCE_SIZE];
+ EC_RAW_POINT Wp, Wsp;
+ if (!pmbtoken_sign(ctx, s, &Wp, &Wsp, &Tp, public_metadata,
+ private_metadata)) {
+ goto err;
+ }
+
+ if (!CBB_add_bytes(&response, s, PMBTOKEN_NONCE_SIZE) ||
+ !cbb_add_raw_point(&response, group, &Wp) ||
+ !cbb_add_raw_point(&response, group, &Wsp)) {
+ goto err;
+ }
+ }
+
+ *out_tokens_issued = count;
+
+ if (!CBB_finish(&response, out, out_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ CBB_cleanup(&response);
+ return ret;
+}
+
+// https://tools.ietf.org/html/rfc7049#section-2.1
+static int add_cbor_int_with_type(CBB *cbb, uint8_t major_type,
+ uint64_t value) {
+ if (value <= 23) {
+ return CBB_add_u8(cbb, value | major_type);
+ }
+ if (value <= 0xff) {
+ return CBB_add_u8(cbb, 0x18 | major_type) && CBB_add_u8(cbb, value);
+ }
+ if (value <= 0xffff) {
+ return CBB_add_u8(cbb, 0x19 | major_type) && CBB_add_u16(cbb, value);
+ }
+ if (value <= 0xffffffff) {
+ return CBB_add_u8(cbb, 0x1a | major_type) && CBB_add_u32(cbb, value);
+ }
+ if (value <= 0xffffffffffffffff) {
+ return CBB_add_u8(cbb, 0x1b | major_type) && CBB_add_u64(cbb, value);
+ }
+
+ return 0;
+}
+
+// https://tools.ietf.org/html/rfc7049#section-2.1
+static int add_cbor_int(CBB *cbb, uint64_t value) {
+ return add_cbor_int_with_type(cbb, 0, value);
+}
+
+// https://tools.ietf.org/html/rfc7049#section-2.1
+static int add_cbor_text(CBB *cbb, const char *data, size_t len) {
+ return add_cbor_int_with_type(cbb, 0x60, len) &&
+ CBB_add_bytes(cbb, (const uint8_t *)data, len);
+}
+
+// https://tools.ietf.org/html/rfc7049#section-2.1
+static int add_cbor_map(CBB *cbb, uint8_t size) {
+ return add_cbor_int_with_type(cbb, 0xa0, size);
+}
+
+static uint8_t get_metadata_obfuscator(const uint8_t *key, size_t key_len,
+ const uint8_t *client_data,
+ size_t client_data_len) {
+ uint8_t metadata_obfuscator[SHA256_DIGEST_LENGTH];
+ SHA256_CTX sha_ctx;
+ SHA256_Init(&sha_ctx);
+ SHA256_Update(&sha_ctx, key, key_len);
+ SHA256_Update(&sha_ctx, client_data, client_data_len);
+ SHA256_Final(metadata_obfuscator, &sha_ctx);
+ return metadata_obfuscator[0] >> 7;
+}
+
+int TRUST_TOKEN_ISSUER_redeem(const TRUST_TOKEN_ISSUER *ctx, uint8_t **out,
+ size_t *out_len, TRUST_TOKEN **out_token,
+ uint8_t **out_client_data,
+ size_t *out_client_data_len,
+ uint64_t *out_redemption_time,
+ const uint8_t *request, size_t request_len,
+ uint64_t lifetime) {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp521r1);
+ if (group == NULL) {
+ return 0;
+ }
+
+ CBS request_cbs, token_cbs;
+ CBS_init(&request_cbs, request, request_len);
+ if (!CBS_get_u16_length_prefixed(&request_cbs, &token_cbs)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_ERROR);
+ return 0;
+ }
+
+ uint32_t public_metadata = 0;
+ uint8_t private_metadata = 0;
+
+ // Parse the token. If there is an error, treat it as an invalid token.
+ PMBTOKEN_TOKEN pmbtoken;
+ if (!CBS_get_u32(&token_cbs, &public_metadata) ||
+ !CBS_copy_bytes(&token_cbs, pmbtoken.t, PMBTOKEN_NONCE_SIZE) ||
+ !cbs_get_raw_point(&token_cbs, group, &pmbtoken.S) ||
+ !cbs_get_raw_point(&token_cbs, group, &pmbtoken.W) ||
+ !cbs_get_raw_point(&token_cbs, group, &pmbtoken.Ws) ||
+ CBS_len(&token_cbs) != 0 ||
+ !pmbtoken_read(ctx, &private_metadata, &pmbtoken, public_metadata)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_TOKEN);
+ return 0;
+ }
+
+ int ok = 0;
+ CBB response, srr;
+ uint8_t *srr_buf = NULL, *sig_buf = NULL, *client_data_buf = NULL;
+ size_t srr_len = 0, sig_len = 0, client_data_len = 0;
+ EVP_MD_CTX md_ctx;
+ EVP_MD_CTX_init(&md_ctx);
+ CBB_zero(&srr);
+ if (!CBB_init(&response, 0)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ CBS client_data;
+ uint64_t redemption_time;
+ if (!CBS_get_u16_length_prefixed(&request_cbs, &client_data) ||
+ !CBS_get_u64(&request_cbs, &redemption_time)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_ERROR);
+ goto err;
+ }
+
+ uint8_t metadata_obfuscator =
+ get_metadata_obfuscator(ctx->metadata_key, ctx->metadata_key_len,
+ CBS_data(&client_data), CBS_len(&client_data));
+
+ // The SRR is constructed as per the format described in
+ // https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#heading=h.7mkzvhpqb8l5
+
+ static const char kClientDataLabel[] = "client-data";
+ static const char kExpiryTimestampLabel[] = "expiry-timestamp";
+ static const char kMetadataLabel[] = "metadata";
+ static const char kPrivateLabel[] = "private";
+ static const char kPublicLabel[] = "public";
+
+ if (!CBB_init(&srr, 0) ||
+ !add_cbor_map(&srr, 3) || // SRR map
+ !add_cbor_text(&srr, kClientDataLabel, strlen(kClientDataLabel)) ||
+ !CBB_add_bytes(&srr, CBS_data(&client_data), CBS_len(&client_data)) ||
+ !add_cbor_text(&srr, kExpiryTimestampLabel,
+ strlen(kExpiryTimestampLabel)) ||
+ !add_cbor_int(&srr, redemption_time + lifetime) ||
+ !add_cbor_text(&srr, kMetadataLabel, strlen(kMetadataLabel)) ||
+ !add_cbor_map(&srr, 2) || // Metadata map
+ !add_cbor_text(&srr, kPrivateLabel, strlen(kPrivateLabel)) ||
+ !add_cbor_int(&srr, private_metadata ^ metadata_obfuscator) ||
+ !add_cbor_text(&srr, kPublicLabel, strlen(kPublicLabel)) ||
+ !add_cbor_int(&srr, public_metadata) ||
+ !CBB_finish(&srr, &srr_buf, &srr_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (!EVP_DigestSignInit(&md_ctx, NULL, NULL, NULL, ctx->srr_key) ||
+ !EVP_DigestSign(&md_ctx, NULL, &sig_len, srr_buf, srr_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_SRR_SIGNATURE_ERROR);
+ 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;
+ }
+
+ if (!CBS_stow(&client_data, &client_data_buf, &client_data_len) ||
+ !CBB_finish(&response, out, out_len)) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ TRUST_TOKEN *token = TRUST_TOKEN_new(pmbtoken.t, PMBTOKEN_NONCE_SIZE);
+ if (token == NULL) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ *out_token = token;
+ *out_client_data = client_data_buf;
+ *out_client_data_len = client_data_len;
+ *out_redemption_time = redemption_time;
+
+ ok = 1;
+
+err:
+ CBB_cleanup(&response);
+ CBB_cleanup(&srr);
+ OPENSSL_free(srr_buf);
+ OPENSSL_free(sig_buf);
+ EVP_MD_CTX_cleanup(&md_ctx);
+ if (!ok) {
+ OPENSSL_free(client_data_buf);
+ }
+ return ok;
+}
+
+int TRUST_TOKEN_decode_private_metadata(uint8_t *out_value, const uint8_t *key,
+ size_t key_len,
+ const uint8_t *client_data,
+ size_t client_data_len,
+ uint8_t encrypted_bit) {
+ uint8_t metadata_obfuscator =
+ get_metadata_obfuscator(key, key_len, client_data, client_data_len);
+ *out_value = encrypted_bit ^ metadata_obfuscator;
+ return 1;
+}
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc
index 0b24ee1..b9bd69e 100644
--- a/crypto/trust_token/trust_token_test.cc
+++ b/crypto/trust_token/trust_token_test.cc
@@ -12,10 +12,33 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <algorithm>
+#include <limits>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
#include <gtest/gtest.h>
+#include <openssl/bytestring.h>
+#include <openssl/curve25519.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/rand.h>
#include <openssl/trust_token.h>
+#include "../internal.h"
+#include "internal.h"
+
+
+BSSL_NAMESPACE_BEGIN
+
+namespace {
TEST(TrustTokenTest, KeyGen) {
uint8_t priv_key[TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE];
@@ -27,3 +50,398 @@
ASSERT_EQ(400u, priv_key_len);
ASSERT_EQ(409u, pub_key_len);
}
+
+class TrustTokenProtocolTest : public ::testing::Test {
+ public:
+ // KeyID returns the key ID associated with key index |i|.
+ static uint32_t KeyID(size_t i) {
+ // Use a different value from the indices to that we do not mix them up.
+ return 7 + i;
+ }
+
+ protected:
+ void SetupContexts() {
+ client.reset(TRUST_TOKEN_CLIENT_new(client_max_batchsize));
+ ASSERT_TRUE(client);
+ issuer.reset(TRUST_TOKEN_ISSUER_new(issuer_max_batchsize));
+ ASSERT_TRUE(issuer);
+
+ for (size_t i = 0; i < 3; 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;
+ ASSERT_TRUE(TRUST_TOKEN_generate_key(
+ priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, pub_key,
+ &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, KeyID(i)));
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_add_key(client.get(), &key_index, pub_key,
+ pub_key_len));
+ ASSERT_EQ(i, key_index);
+ ASSERT_TRUE(
+ TRUST_TOKEN_ISSUER_add_key(issuer.get(), priv_key, priv_key_len));
+ }
+
+ uint8_t public_key[32], private_key[64];
+ ED25519_keypair(public_key, private_key);
+ bssl::UniquePtr<EVP_PKEY> priv(EVP_PKEY_new_raw_private_key(
+ EVP_PKEY_ED25519, nullptr, private_key, 32));
+ ASSERT_TRUE(priv);
+ bssl::UniquePtr<EVP_PKEY> pub(
+ EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, public_key, 32));
+ ASSERT_TRUE(pub);
+
+ TRUST_TOKEN_CLIENT_set_srr_key(client.get(), pub.get());
+ TRUST_TOKEN_ISSUER_set_srr_key(issuer.get(), priv.get());
+ RAND_bytes(metadata_key, sizeof(metadata_key));
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_set_metadata_key(issuer.get(), metadata_key,
+ sizeof(metadata_key)));
+ }
+
+ uint16_t client_max_batchsize = 10;
+ uint16_t issuer_max_batchsize = 10;
+ bssl::UniquePtr<TRUST_TOKEN_CLIENT> client;
+ bssl::UniquePtr<TRUST_TOKEN_ISSUER> issuer;
+ uint8_t metadata_key[32];
+};
+
+TEST_F(TrustTokenProtocolTest, InvalidToken) {
+ ASSERT_NO_FATAL_FAILURE(SetupContexts());
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+
+ size_t key_index;
+ uint8_t tokens_issued;
+ ASSERT_TRUE(
+ TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg, &msg_len, 1));
+ 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,
+ /*max_issuance=*/10));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+ bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+ TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+ resp_len));
+ ASSERT_TRUE(tokens);
+
+ for (TRUST_TOKEN *token : tokens.get()) {
+ // Corrupt the token.
+ token->data[0] ^= 0x42;
+
+ uint8_t *redeem_msg = NULL, *redeem_resp = NULL;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_redemption(
+ client.get(), &redeem_msg, &msg_len, token, NULL, 0, 0));
+ bssl::UniquePtr<uint8_t> free_redeem_msg(redeem_msg);
+ TRUST_TOKEN *rtoken;
+ uint8_t *client_data;
+ size_t client_data_len;
+ uint64_t redemption_time;
+ ASSERT_FALSE(TRUST_TOKEN_ISSUER_redeem(
+ issuer.get(), &redeem_resp, &resp_len, &rtoken, &client_data,
+ &client_data_len, &redemption_time, redeem_msg, msg_len, 600));
+ bssl::UniquePtr<uint8_t> free_redeem_resp(redeem_resp);
+ }
+}
+
+TEST_F(TrustTokenProtocolTest, TruncatedIssuanceRequest) {
+ ASSERT_NO_FATAL_FAILURE(SetupContexts());
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
+ &msg_len, 10));
+ bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
+ msg_len = 10;
+ uint8_t tokens_issued;
+ ASSERT_FALSE(TRUST_TOKEN_ISSUER_issue(
+ issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+ /*public_metadata=*/KeyID(0), /*private_metadata=*/0,
+ /*max_issuance=*/10));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+}
+
+TEST_F(TrustTokenProtocolTest, TruncatedIssuanceResponse) {
+ ASSERT_NO_FATAL_FAILURE(SetupContexts());
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
+ &msg_len, 10));
+ bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
+ uint8_t tokens_issued;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue(
+ issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+ /*public_metadata=*/KeyID(0), /*private_metadata=*/0,
+ /*max_issuance=*/10));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+ resp_len = 10;
+ size_t key_index;
+ bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+ TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+ resp_len));
+ ASSERT_FALSE(tokens);
+}
+
+TEST_F(TrustTokenProtocolTest, TruncatedRedemptionRequest) {
+ ASSERT_NO_FATAL_FAILURE(SetupContexts());
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
+ &msg_len, 10));
+ bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
+ uint8_t tokens_issued;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue(
+ issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+ /*public_metadata=*/KeyID(0), /*private_metadata=*/0,
+ /*max_issuance=*/10));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+ size_t key_index;
+ bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+ TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+ resp_len));
+ ASSERT_TRUE(tokens);
+
+ for (TRUST_TOKEN *token : tokens.get()) {
+ const uint8_t kClientData[] = "TEST CLIENT DATA";
+ uint64_t kRedemptionTime = 13374242;
+
+ uint8_t *redeem_msg = NULL, *redeem_resp = NULL;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_redemption(
+ client.get(), &redeem_msg, &msg_len, token, kClientData,
+ sizeof(kClientData) - 1, kRedemptionTime));
+ bssl::UniquePtr<uint8_t> free_redeem_msg(redeem_msg);
+ msg_len = 10;
+
+ TRUST_TOKEN *rtoken;
+ uint8_t *client_data;
+ size_t client_data_len;
+ uint64_t redemption_time;
+ ASSERT_FALSE(TRUST_TOKEN_ISSUER_redeem(
+ issuer.get(), &redeem_resp, &resp_len, &rtoken, &client_data,
+ &client_data_len, &redemption_time, redeem_msg, msg_len, 600));
+ }
+}
+
+TEST_F(TrustTokenProtocolTest, TruncatedRedemptionResponse) {
+ ASSERT_NO_FATAL_FAILURE(SetupContexts());
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
+ &msg_len, 10));
+ bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
+ uint8_t tokens_issued;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue(
+ issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+ /*public_metadata=*/KeyID(0), /*private_metadata=*/0,
+ /*max_issuance=*/10));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+ size_t key_index;
+ bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+ TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+ resp_len));
+ ASSERT_TRUE(tokens);
+
+ for (TRUST_TOKEN *token : tokens.get()) {
+ const uint8_t kClientData[] = "TEST CLIENT DATA";
+ uint64_t kRedemptionTime = 13374242;
+
+ uint8_t *redeem_msg = NULL, *redeem_resp = NULL;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_redemption(
+ client.get(), &redeem_msg, &msg_len, token, kClientData,
+ sizeof(kClientData) - 1, kRedemptionTime));
+ bssl::UniquePtr<uint8_t> free_redeem_msg(redeem_msg);
+ TRUST_TOKEN *rtoken;
+ uint8_t *client_data;
+ size_t client_data_len;
+ uint64_t redemption_time;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_redeem(
+ issuer.get(), &redeem_resp, &resp_len, &rtoken, &client_data,
+ &client_data_len, &redemption_time, redeem_msg, msg_len, 600));
+ bssl::UniquePtr<uint8_t> free_redeem_resp(redeem_resp);
+ bssl::UniquePtr<uint8_t> free_client_data(client_data);
+ bssl::UniquePtr<TRUST_TOKEN> free_rtoken(rtoken);
+
+ ASSERT_EQ(redemption_time, kRedemptionTime);
+ ASSERT_TRUE(sizeof(kClientData) - 1 == client_data_len);
+ ASSERT_EQ(OPENSSL_memcmp(kClientData, client_data, client_data_len), 0);
+ resp_len = 10;
+
+ 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));
+ bssl::UniquePtr<uint8_t> free_srr(srr);
+ bssl::UniquePtr<uint8_t> free_sig(sig);
+ }
+}
+
+TEST_F(TrustTokenProtocolTest, IssuedWithBadKeyID) {
+ client.reset(TRUST_TOKEN_CLIENT_new(client_max_batchsize));
+ ASSERT_TRUE(client);
+ issuer.reset(TRUST_TOKEN_ISSUER_new(issuer_max_batchsize));
+ ASSERT_TRUE(issuer);
+
+ // We configure the client and the issuer with different key IDs and test
+ // that the client notices.
+ const uint32_t kClientKeyID = 0;
+ const uint32_t kIssuerKeyID = 42;
+
+ 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;
+ ASSERT_TRUE(TRUST_TOKEN_generate_key(
+ priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, pub_key,
+ &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, kClientKeyID));
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_add_key(client.get(), &key_index, pub_key,
+ pub_key_len));
+ ASSERT_EQ(0UL, key_index);
+
+ ASSERT_TRUE(TRUST_TOKEN_generate_key(
+ priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, pub_key,
+ &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, kIssuerKeyID));
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_add_key(issuer.get(), priv_key, priv_key_len));
+
+
+ uint8_t public_key[32], private_key[64];
+ ED25519_keypair(public_key, private_key);
+ bssl::UniquePtr<EVP_PKEY> priv(
+ EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, private_key, 32));
+ ASSERT_TRUE(priv);
+ bssl::UniquePtr<EVP_PKEY> pub(
+ EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, public_key, 32));
+ ASSERT_TRUE(pub);
+
+ TRUST_TOKEN_CLIENT_set_srr_key(client.get(), pub.get());
+ TRUST_TOKEN_ISSUER_set_srr_key(issuer.get(), priv.get());
+ RAND_bytes(metadata_key, sizeof(metadata_key));
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_set_metadata_key(issuer.get(), metadata_key,
+ sizeof(metadata_key)));
+
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
+ &msg_len, 10));
+ bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
+ uint8_t tokens_issued;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue(
+ issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+ /*public_metadata=*/42, /*private_metadata=*/0, /*max_issuance=*/10));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+ bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+ TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+ resp_len));
+ ASSERT_FALSE(tokens);
+}
+
+class TrustTokenMetadataTest
+ : public TrustTokenProtocolTest,
+ public testing::WithParamInterface<std::tuple<int, bool>> {};
+
+TEST_P(TrustTokenMetadataTest, SetAndGetMetadata) {
+ ASSERT_NO_FATAL_FAILURE(SetupContexts());
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
+ &msg_len, 10));
+ bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
+ uint8_t tokens_issued;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue(
+ issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+ std::get<0>(GetParam()), std::get<1>(GetParam()), /*max_issuance=*/1));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+ size_t key_index;
+ bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+ TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+ resp_len));
+ ASSERT_TRUE(tokens);
+
+ for (TRUST_TOKEN *token : tokens.get()) {
+ const uint8_t kClientData[] = "TEST CLIENT DATA";
+ uint64_t kRedemptionTime = 13374242;
+
+ const uint8_t kExpectedSRR[] =
+ "\xa3\x6b\x63\x6c\x69\x65\x6e\x74\x2d\x64\x61\x74\x61\x54\x45\x53\x54"
+ "\x20\x43\x4c\x49\x45\x4e\x54\x20\x44\x41\x54\x41\x70\x65\x78\x70\x69"
+ "\x72\x79\x2d\x74\x69\x6d\x65\x73\x74\x61\x6d\x70\x1a\x00\xcc\x15\x7a"
+ "\x68\x6d\x65\x74\x61\x64\x61\x74\x61\xa2\x67\x70\x72\x69\x76\x61\x74"
+ "\x65\x00\x66\x70\x75\x62\x6c\x69\x63\x00";
+
+ uint8_t *redeem_msg = NULL, *redeem_resp = NULL;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_redemption(
+ client.get(), &redeem_msg, &msg_len, token, kClientData,
+ sizeof(kClientData) - 1, kRedemptionTime));
+ bssl::UniquePtr<uint8_t> free_redeem_msg(redeem_msg);
+ TRUST_TOKEN *rtoken;
+ uint8_t *client_data;
+ size_t client_data_len;
+ uint64_t redemption_time;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_redeem(
+ issuer.get(), &redeem_resp, &resp_len, &rtoken, &client_data,
+ &client_data_len, &redemption_time, redeem_msg, msg_len, 600));
+ bssl::UniquePtr<uint8_t> free_redeem_resp(redeem_resp);
+ bssl::UniquePtr<uint8_t> free_client_data(client_data);
+ bssl::UniquePtr<TRUST_TOKEN> free_rtoken(rtoken);
+
+ ASSERT_EQ(redemption_time, kRedemptionTime);
+ ASSERT_TRUE(sizeof(kClientData) - 1 == client_data_len);
+ ASSERT_EQ(OPENSSL_memcmp(kClientData, client_data, client_data_len), 0);
+
+ uint8_t *srr = NULL, *sig = NULL;
+ size_t srr_len, sig_len;
+ ASSERT_TRUE(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);
+
+ uint8_t private_metadata;
+ ASSERT_TRUE(TRUST_TOKEN_decode_private_metadata(
+ &private_metadata, metadata_key, sizeof(metadata_key), kClientData,
+ sizeof(kClientData) - 1, srr[srr_len - 9]));
+ ASSERT_EQ(srr[srr_len - 1], std::get<0>(GetParam()));
+ ASSERT_EQ(private_metadata, std::get<1>(GetParam()));
+
+ // Clear out the metadata bits.
+ srr[srr_len - 9] = 0;
+ srr[srr_len - 1] = 0;
+
+ ASSERT_TRUE(sizeof(kExpectedSRR) - 1 == srr_len);
+ ASSERT_EQ(OPENSSL_memcmp(kExpectedSRR, srr, srr_len), 0);
+ }
+}
+
+TEST_P(TrustTokenMetadataTest, TooManyRequests) {
+ issuer_max_batchsize = 1;
+ ASSERT_NO_FATAL_FAILURE(SetupContexts());
+
+ uint8_t *issue_msg = NULL, *issue_resp = NULL;
+ size_t msg_len, resp_len;
+ ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_issuance(client.get(), &issue_msg,
+ &msg_len, 10));
+ bssl::UniquePtr<uint8_t> free_issue_msg(issue_msg);
+ uint8_t tokens_issued;
+ ASSERT_TRUE(TRUST_TOKEN_ISSUER_issue(
+ issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+ std::get<0>(GetParam()), std::get<1>(GetParam()), /*max_issuance=*/1));
+ bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+ ASSERT_EQ(tokens_issued, issuer_max_batchsize);
+ size_t key_index;
+ bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+ TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+ resp_len));
+ ASSERT_TRUE(tokens);
+ ASSERT_EQ(sk_TRUST_TOKEN_num(tokens.get()), 1UL);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ TrustTokenAllMetadataTest, TrustTokenMetadataTest,
+ testing::Combine(testing::Values(TrustTokenProtocolTest::KeyID(0),
+ TrustTokenProtocolTest::KeyID(1),
+ TrustTokenProtocolTest::KeyID(2)),
+ testing::Bool()));
+
+} // namespace
+BSSL_NAMESPACE_END
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 8d73f77..7218967 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -422,6 +422,9 @@
typedef struct ssl_st SSL;
typedef struct ssl_ticket_aead_method_st SSL_TICKET_AEAD_METHOD;
typedef struct st_ERR_FNS ERR_FNS;
+typedef struct trust_token_st TRUST_TOKEN;
+typedef struct trust_token_client_st TRUST_TOKEN_CLIENT;
+typedef struct trust_token_issuer_st TRUST_TOKEN_ISSUER;
typedef struct v3_ext_ctx X509V3_CTX;
typedef struct x509_attributes_st X509_ATTRIBUTE;
typedef struct x509_cert_aux_st X509_CERT_AUX;
diff --git a/include/openssl/trust_token.h b/include/openssl/trust_token.h
index bf5b5fa..473999d 100644
--- a/include/openssl/trust_token.h
+++ b/include/openssl/trust_token.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2019, Google Inc.
+/* 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
@@ -36,6 +36,23 @@
//
// WARNING: This API is unstable and subject to change.
+// 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.
+struct trust_token_st {
+ uint8_t *data;
+ size_t len;
+};
+
+DEFINE_STACK_OF(TRUST_TOKEN)
+
+// TRUST_TOKEN_new creates a newly-allocated |TRUST_TOKEN| with value |data| or
+// NULL on allocation failure.
+OPENSSL_EXPORT TRUST_TOKEN *TRUST_TOKEN_new(const uint8_t *data, size_t len);
+
+// TRUST_TOKEN_free releases memory associated with |token|.
+OPENSSL_EXPORT void TRUST_TOKEN_free(TRUST_TOKEN *token);
+
#define TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE 512
#define TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE 512
@@ -59,11 +76,202 @@
uint32_t id);
+// Trust Token client implementation.
+//
+// These functions implements the client half of the Trust Token protocol. A
+// single |TRUST_TOKEN_CLIENT| can perform a single protocol operation.
+
+// TRUST_TOKEN_CLIENT_new returns a newly-allocated |TRUST_TOKEN_CLIENT|
+// configured to use a max batchsize of |max_batchsize| or NULL on error.
+// Issuance requests must be made in batches smaller than |max_batchsize|.
+OPENSSL_EXPORT TRUST_TOKEN_CLIENT *TRUST_TOKEN_CLIENT_new(
+ uint16_t max_batchsize);
+
+// TRUST_TOKEN_CLIENT_free releases memory associated with |ctx|.
+OPENSSL_EXPORT void TRUST_TOKEN_CLIENT_free(TRUST_TOKEN_CLIENT *ctx);
+
+// TRUST_TOKEN_CLIENT_add_key configures the |ctx| to support the public key
+// |key|. It sets |*out_key_index| to the index this key has been configured to.
+// It returns one on success or zero on error if the |key| can't be parsed or
+// too many keys have been configured.
+OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_add_key(TRUST_TOKEN_CLIENT *ctx,
+ size_t *out_key_index,
+ const uint8_t *key,
+ size_t key_len);
+
+// TRUST_TOKEN_CLIENT_set_srr_key sets the public key used to verify the SRR. It
+// returns one on success and zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_set_srr_key(TRUST_TOKEN_CLIENT *ctx,
+ EVP_PKEY *key);
+
+// TRUST_TOKEN_CLIENT_begin_issuance produces a request for |count| trust tokens
+// and serializes the request into a newly-allocated buffer, setting |*out| to
+// that buffer and |*out_len| to its length. The caller takes ownership of the
+// buffer and must call |OPENSSL_free| when done. It returns one on success and
+// zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_begin_issuance(TRUST_TOKEN_CLIENT *ctx,
+ uint8_t **out,
+ size_t *out_len,
+ size_t count);
+
+// TRUST_TOKEN_CLIENT_finish_issuance consumes |response| from the issuer and
+// extracts the tokens, returning a list of tokens and the index of the key used
+// to sign the tokens in |*out_key_index|. The caller can use this to determine
+// what key was used in an issuance and to drop tokens if a new key commitment
+// arrives without the specified key present. The caller takes ownership of the
+// list and must call |sk_TRUST_TOKEN_pop_free| when done. The list is empty if
+// issuance fails.
+OPENSSL_EXPORT STACK_OF(TRUST_TOKEN) *
+ TRUST_TOKEN_CLIENT_finish_issuance(TRUST_TOKEN_CLIENT *ctx,
+ size_t *out_key_index,
+ const uint8_t *response,
+ size_t response_len);
+
+
+// TRUST_TOKEN_CLIENT_begin_redemption produces a request to redeem a token
+// |token| and receive a signature over |data| and serializes the request into
+// a newly-allocated buffer, setting |*out| to that buffer and |*out_len| to
+// its length. |time| is the number of seconds since the UNIX epoch and used to
+// verify the validity of the issuer's response. The caller takes ownership of
+// the buffer and must call |OPENSSL_free| when done. It returns one on success
+// or zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_begin_redemption(
+ TRUST_TOKEN_CLIENT *ctx, uint8_t **out, size_t *out_len,
+ 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.
+OPENSSL_EXPORT int TRUST_TOKEN_CLIENT_finish_redemption(
+ TRUST_TOKEN_CLIENT *ctx, uint8_t **out_srr, size_t *out_srr_len,
+ uint8_t **out_sig, size_t *out_sig_len, const uint8_t *response,
+ size_t response_len);
+
+
+// Trust Token issuer implementation.
+//
+// These functions implement the issuer half of the Trust Token protocol. A
+// |TRUST_TOKEN_ISSUER| can be reused across multiple protocol operations. It
+// may be used concurrently on multiple threads by non-mutating functions,
+// provided no other thread is concurrently calling a mutating function.
+// Functions which take a |const| pointer are non-mutating and functions which
+// take a non-|const| pointer are mutating.
+
+// TRUST_TOKEN_ISSUER_new returns a newly-allocated |TRUST_TOKEN_ISSUER|
+// configured to use a max batchsize of |max_batchsize| or NULL on error.
+// Issuance requests must be made in batches smaller than |max_batchsize|.
+OPENSSL_EXPORT TRUST_TOKEN_ISSUER *TRUST_TOKEN_ISSUER_new(
+ uint16_t max_batchsize);
+
+// TRUST_TOKEN_ISSUER_free releases memory associated with |ctx|.
+OPENSSL_EXPORT void TRUST_TOKEN_ISSUER_free(TRUST_TOKEN_ISSUER *ctx);
+
+// TRUST_TOKEN_ISSUER_add_key configures the |ctx| to support the private key
+// |key|. It must be a private key returned by |TRUST_TOKEN_generate_key|. It
+// returns one on success or zero on error. This function may fail if the |key|
+// can't be parsed or too many keys have been configured.
+OPENSSL_EXPORT int TRUST_TOKEN_ISSUER_add_key(TRUST_TOKEN_ISSUER *ctx,
+ const uint8_t *key,
+ size_t key_len);
+
+// TRUST_TOKEN_ISSUER_set_srr_key sets the private key used to sign the SRR. It
+// returns one on success and zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_ISSUER_set_srr_key(TRUST_TOKEN_ISSUER *ctx,
+ EVP_PKEY *key);
+
+// TRUST_TOKEN_ISSUER_set_metadata_key sets the key used to encrypt the private
+// metadata. The key is a randomly generated bytestring of at least 32 bytes
+// used to encode the private metadata bit in the SRR. It returns one on success
+// and zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_ISSUER_set_metadata_key(TRUST_TOKEN_ISSUER *ctx,
+ const uint8_t *key,
+ size_t len);
+
+// TRUST_TOKEN_ISSUER_issue ingests |request| for token issuance
+// and generates up to |max_issuance| valid tokens, producing a list of blinded
+// tokens and storing the response into a newly-allocated buffer and setting
+// |*out| to that buffer, |*out_len| to its length, and |*out_tokens_issued| to
+// the number of tokens issued. The tokens are issued with public metadata of
+// |public_metadata| and a private metadata value of |private_metadata|.
+// |public_metadata| must be one of the previously configured key IDs.
+// |private_metadata| must be 0 or 1. The caller takes ownership of the buffer
+// and must call |OPENSSL_free| when done. It returns one on success or zero on
+// error.
+OPENSSL_EXPORT int TRUST_TOKEN_ISSUER_issue(
+ const TRUST_TOKEN_ISSUER *ctx, uint8_t **out, size_t *out_len,
+ uint8_t *out_tokens_issued, const uint8_t *request, size_t request_len,
+ uint32_t public_metadata, uint8_t private_metadata, size_t max_issuance);
+
+// TRUST_TOKEN_ISSUER_redeem ingests a |request| for token redemption and
+// verifies the token. If the token is valid, a SRR is produced with a lifetime
+// of |lifetime| (in seconds), signing over the requested data from the request
+// and the value of the token, storing the result into a newly-allocated buffer
+// and setting |*out| to that buffer and |*out_len| to its length. The extracted
+// |TRUST_TOKEN| is stored into a newly-allocated buffer and stored in
+// |*out_token|. The extracted client data is stored into a newly-allocated
+// buffer and stored in |*out_client_data|. The extracted redemption time is
+// stored in |*out_redemption_time|. The caller takes ownership of each output
+// buffer and must call |OPENSSL_free| when done. It returns one on success or
+// zero on error.
+//
+// The caller must keep track of all values of |*out_token| and
+// |*out_client_data| and seen globally before returning the SRR to the client.
+// If either value has been repeated, the caller must discard the SRR and report
+// an error to the caller. Returning an SRR with replayed values allows an
+// attacker to double-spend tokens and query private metadata bits in SRRs.
+//
+// TODO(svaldez): The private metadata bit should not be leaked on replay. This
+// means callers cannot use eventual consistency to trade off double-spending
+// and distributed system performance. See https://crbug.com/boringssl/328.
+OPENSSL_EXPORT int TRUST_TOKEN_ISSUER_redeem(
+ const TRUST_TOKEN_ISSUER *ctx, uint8_t **out, size_t *out_len,
+ TRUST_TOKEN **out_token, uint8_t **out_client_data,
+ size_t *out_client_data_len, uint64_t *out_redemption_time,
+ const uint8_t *request, size_t request_len, uint64_t lifetime);
+
+// TRUST_TOKEN_decode_private_metadata decodes |encrypted_bit| using the
+// private metadata key specified by a |key| buffer of length |key_len| and the
+// client data specified by a |client_data| buffer of length |client_data_len|.
+// |*out_value is set to the decrypted value, either zero or one. It returns one
+// on success and zero on error.
+OPENSSL_EXPORT int TRUST_TOKEN_decode_private_metadata(
+ uint8_t *out_value, const uint8_t *key, size_t key_len,
+ const uint8_t *client_data, size_t client_data_len, uint8_t encrypted_bit);
+
+
#if defined(__cplusplus)
} // extern C
+
+extern "C++" {
+
+BSSL_NAMESPACE_BEGIN
+
+BORINGSSL_MAKE_DELETER(TRUST_TOKEN, TRUST_TOKEN_free)
+BORINGSSL_MAKE_DELETER(TRUST_TOKEN_CLIENT, TRUST_TOKEN_CLIENT_free)
+BORINGSSL_MAKE_DELETER(TRUST_TOKEN_ISSUER, TRUST_TOKEN_ISSUER_free)
+
+
+BSSL_NAMESPACE_END
+
+} // extern C++
#endif
#define TRUST_TOKEN_R_KEYGEN_FAILURE 100
#define TRUST_TOKEN_R_BUFFER_TOO_SMALL 101
+#define TRUST_TOKEN_R_OVER_BATCHSIZE 102
+#define TRUST_TOKEN_R_DECODE_ERROR 103
+#define TRUST_TOKEN_R_SRR_SIGNATURE_ERROR 104
+#define TRUST_TOKEN_R_DECODE_FAILURE 105
+#define TRUST_TOKEN_R_INVALID_METADATA 106
+#define TRUST_TOKEN_R_TOO_MANY_KEYS 107
+#define TRUST_TOKEN_R_NO_KEYS_CONFIGURED 108
+#define TRUST_TOKEN_R_INVALID_KEY_ID 109
+#define TRUST_TOKEN_R_INVALID_TOKEN 110
+#define TRUST_TOKEN_R_BAD_VALIDITY_CHECK 111
+#define TRUST_TOKEN_R_NO_SRR_KEY_CONFIGURED 112
+#define TRUST_TOKEN_R_INVALID_METADATA_KEY 113
#endif // OPENSSL_HEADER_TRUST_TOKEN_H