Introduce a TRUST_TOKEN_METHOD hook to select TRUST_TOKEN variations.

For now, it does nothing. This will make it easier to transition between
versions of the experiment while the protocol evolves.

Update-Note: Pass TRUST_TOKEN_experiment_v0() into any existing code
that now needs a TRUST_TOKEN_METHOD.

Change-Id: I434e18c794ab30545e367eb902e434e6311b7497
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41066
Commit-Queue: Steven Valdez <svaldez@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h
index c9abf91..56c750a 100644
--- a/crypto/trust_token/internal.h
+++ b/crypto/trust_token/internal.h
@@ -30,6 +30,8 @@
 #endif
 
 
+// PMBTokens.
+//
 // PMBTokens is described in https://eprint.iacr.org/2020/072/20200324:214215
 // and provides anonymous tokens with private metadata. We implement the
 // construction with validity verification, described in appendix H,
@@ -130,6 +132,14 @@
                   size_t token_len);
 
 
+
+// Trust Tokens internals.
+
+struct trust_token_method_st {
+  // TODO(davidben): Add functions here to swap out the PMBTokens mechanism.
+  char empty;
+};
+
 // Structure representing a single Trust Token public key with the specified ID.
 struct trust_token_client_key_st {
   uint32_t id;
@@ -144,6 +154,8 @@
 };
 
 struct trust_token_client_st {
+  const TRUST_TOKEN_METHOD *method;
+
   // max_batchsize is the maximum supported batchsize.
   uint16_t max_batchsize;
 
@@ -163,6 +175,8 @@
 
 
 struct trust_token_issuer_st {
+  const TRUST_TOKEN_METHOD *method;
+
   // max_batchsize is the maximum supported batchsize.
   uint16_t max_batchsize;
 
diff --git a/crypto/trust_token/trust_token.c b/crypto/trust_token/trust_token.c
index 260368a..f34b41b 100644
--- a/crypto/trust_token/trust_token.c
+++ b/crypto/trust_token/trust_token.c
@@ -27,6 +27,11 @@
 // protocol for issuing and redeeming tokens built on top of the PMBTokens
 // construction.
 
+const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v0(void) {
+  static const TRUST_TOKEN_METHOD kMethod = {0};
+  return &kMethod;
+}
+
 TRUST_TOKEN *TRUST_TOKEN_new(const uint8_t *data, size_t len) {
   TRUST_TOKEN *ret = OPENSSL_malloc(sizeof(TRUST_TOKEN));
   if (ret == NULL) {
@@ -52,7 +57,8 @@
   OPENSSL_free(token);
 }
 
-int TRUST_TOKEN_generate_key(uint8_t *out_priv_key, size_t *out_priv_key_len,
+int TRUST_TOKEN_generate_key(const TRUST_TOKEN_METHOD *method,
+                             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) {
@@ -87,7 +93,8 @@
   return ret;
 }
 
-TRUST_TOKEN_CLIENT *TRUST_TOKEN_CLIENT_new(size_t max_batchsize) {
+TRUST_TOKEN_CLIENT *TRUST_TOKEN_CLIENT_new(const TRUST_TOKEN_METHOD *method,
+                                           size_t max_batchsize) {
   if (max_batchsize > 0xffff) {
     // The protocol supports only two-byte token counts.
     OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_OVERFLOW);
@@ -100,6 +107,7 @@
     return NULL;
   }
   OPENSSL_memset(ret, 0, sizeof(TRUST_TOKEN_CLIENT));
+  ret->method = method;
   ret->max_batchsize = (uint16_t)max_batchsize;
   return ret;
 }
@@ -300,7 +308,8 @@
   return 1;
 }
 
-TRUST_TOKEN_ISSUER *TRUST_TOKEN_ISSUER_new(size_t max_batchsize) {
+TRUST_TOKEN_ISSUER *TRUST_TOKEN_ISSUER_new(const TRUST_TOKEN_METHOD *method,
+                                           size_t max_batchsize) {
   if (max_batchsize > 0xffff) {
     // The protocol supports only two-byte token counts.
     OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_OVERFLOW);
@@ -313,6 +322,7 @@
     return NULL;
   }
   OPENSSL_memset(ret, 0, sizeof(TRUST_TOKEN_ISSUER));
+  ret->method = method;
   ret->max_batchsize = (uint16_t)max_batchsize;
   return ret;
 }
@@ -631,7 +641,8 @@
   return ok;
 }
 
-int TRUST_TOKEN_decode_private_metadata(uint8_t *out_value, const uint8_t *key,
+int TRUST_TOKEN_decode_private_metadata(const TRUST_TOKEN_METHOD *method,
+                                        uint8_t *out_value, const uint8_t *key,
                                         size_t key_len,
                                         const uint8_t *client_data,
                                         size_t client_data_len,
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc
index e312dc8..cf9ad7e 100644
--- a/crypto/trust_token/trust_token_test.cc
+++ b/crypto/trust_token/trust_token_test.cc
@@ -45,8 +45,9 @@
   uint8_t pub_key[TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE];
   size_t priv_key_len, pub_key_len;
   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, 0x0001));
+      TRUST_TOKEN_experiment_v0(), 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(400u, priv_key_len);
   ASSERT_EQ(409u, pub_key_len);
 }
@@ -59,11 +60,16 @@
     return 7 + i;
   }
 
+  // TODO(davidben): Parameterize this on the Trust Tokens method.
+  static const TRUST_TOKEN_METHOD *method() {
+    return TRUST_TOKEN_experiment_v0();
+  }
+
  protected:
   void SetupContexts() {
-    client.reset(TRUST_TOKEN_CLIENT_new(client_max_batchsize));
+    client.reset(TRUST_TOKEN_CLIENT_new(method(), client_max_batchsize));
     ASSERT_TRUE(client);
-    issuer.reset(TRUST_TOKEN_ISSUER_new(issuer_max_batchsize));
+    issuer.reset(TRUST_TOKEN_ISSUER_new(method(), issuer_max_batchsize));
     ASSERT_TRUE(issuer);
 
     for (size_t i = 0; i < 3; i++) {
@@ -71,8 +77,8 @@
       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)));
+          method(), 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);
@@ -302,9 +308,9 @@
 }
 
 TEST_F(TrustTokenProtocolTest, IssuedWithBadKeyID) {
-  client.reset(TRUST_TOKEN_CLIENT_new(client_max_batchsize));
+  client.reset(TRUST_TOKEN_CLIENT_new(method(), client_max_batchsize));
   ASSERT_TRUE(client);
-  issuer.reset(TRUST_TOKEN_ISSUER_new(issuer_max_batchsize));
+  issuer.reset(TRUST_TOKEN_ISSUER_new(method(), issuer_max_batchsize));
   ASSERT_TRUE(issuer);
 
   // We configure the client and the issuer with different key IDs and test
@@ -316,15 +322,15 @@
   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));
+      method(), 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));
+      method(), 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));
 
 
@@ -423,8 +429,8 @@
 
     uint8_t private_metadata;
     ASSERT_TRUE(TRUST_TOKEN_decode_private_metadata(
-        &private_metadata, metadata_key, sizeof(metadata_key), kClientData,
-        sizeof(kClientData) - 1, srr[27]));
+        method(), &private_metadata, metadata_key, sizeof(metadata_key),
+        kClientData, sizeof(kClientData) - 1, srr[27]));
     ASSERT_EQ(srr[18], std::get<0>(GetParam()));
     ASSERT_EQ(private_metadata, std::get<1>(GetParam()));
 
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 7218967..7f6acce 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -425,6 +425,7 @@
 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 trust_token_method_st TRUST_TOKEN_METHOD;
 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 008857e..e5837cf 100644
--- a/include/openssl/trust_token.h
+++ b/include/openssl/trust_token.h
@@ -36,6 +36,10 @@
 //
 // WARNING: This API is unstable and subject to change.
 
+// TRUST_TOKEN_experiment_v0 is an experimental Trust Tokens protocol using
+// PMBTokens and P-521.
+OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v0(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.
@@ -71,9 +75,9 @@
 //
 // This function returns one on success or zero on error.
 OPENSSL_EXPORT 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);
+    const TRUST_TOKEN_METHOD *method, 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);
 
 
 // Trust Token client implementation.
@@ -86,7 +90,8 @@
 // Issuance requests must be made in batches smaller than |max_batchsize|. This
 // function will return an error if |max_batchsize| is too large for Trust
 // Tokens.
-OPENSSL_EXPORT TRUST_TOKEN_CLIENT *TRUST_TOKEN_CLIENT_new(size_t max_batchsize);
+OPENSSL_EXPORT TRUST_TOKEN_CLIENT *TRUST_TOKEN_CLIENT_new(
+    const TRUST_TOKEN_METHOD *method, size_t max_batchsize);
 
 // TRUST_TOKEN_CLIENT_free releases memory associated with |ctx|.
 OPENSSL_EXPORT void TRUST_TOKEN_CLIENT_free(TRUST_TOKEN_CLIENT *ctx);
@@ -166,7 +171,8 @@
 // Issuance requests must be made in batches smaller than |max_batchsize|. This
 // function will return an error if |max_batchsize| is too large for Trust
 // Tokens.
-OPENSSL_EXPORT TRUST_TOKEN_ISSUER *TRUST_TOKEN_ISSUER_new(size_t max_batchsize);
+OPENSSL_EXPORT TRUST_TOKEN_ISSUER *TRUST_TOKEN_ISSUER_new(
+    const TRUST_TOKEN_METHOD *method, size_t max_batchsize);
 
 // TRUST_TOKEN_ISSUER_free releases memory associated with |ctx|.
 OPENSSL_EXPORT void TRUST_TOKEN_ISSUER_free(TRUST_TOKEN_ISSUER *ctx);
@@ -240,8 +246,9 @@
 // |*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);
+    const TRUST_TOKEN_METHOD *method, 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)
@@ -255,7 +262,6 @@
 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++
diff --git a/tool/speed.cc b/tool/speed.cc
index b1f530d..ac71043 100644
--- a/tool/speed.cc
+++ b/tool/speed.cc
@@ -1019,8 +1019,8 @@
   return out;
 }
 
-static bool SpeedTrustToken(std::string name, size_t batchsize,
-                            const std::string &selected) {
+static bool SpeedTrustToken(std::string name, const TRUST_TOKEN_METHOD *method,
+                            size_t batchsize, const std::string &selected) {
   if (!selected.empty() && selected.find("trusttoken") == std::string::npos) {
     return true;
   }
@@ -1031,24 +1031,25 @@
         uint8_t pub_key[TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE];
         size_t priv_key_len, pub_key_len;
         return 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, 0);
+            method, priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE,
+            pub_key, &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0);
       })) {
     fprintf(stderr, "TRUST_TOKEN_generate_key failed.\n");
     return false;
   }
   results.Print(name + " generate_key");
 
-  bssl::UniquePtr<TRUST_TOKEN_CLIENT> client(TRUST_TOKEN_CLIENT_new(batchsize));
-  bssl::UniquePtr<TRUST_TOKEN_ISSUER> issuer(TRUST_TOKEN_ISSUER_new(batchsize));
+  bssl::UniquePtr<TRUST_TOKEN_CLIENT> client(
+      TRUST_TOKEN_CLIENT_new(method, batchsize));
+  bssl::UniquePtr<TRUST_TOKEN_ISSUER> issuer(
+      TRUST_TOKEN_ISSUER_new(method, batchsize));
   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;
-  if (!client ||
-      !issuer ||
+  if (!client || !issuer ||
       !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, 0) ||
+          method, priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE,
+          pub_key, &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0) ||
       !TRUST_TOKEN_CLIENT_add_key(client.get(), &key_index, pub_key,
                                   pub_key_len) ||
       !TRUST_TOKEN_ISSUER_add_key(issuer.get(), priv_key, priv_key_len)) {
@@ -1374,8 +1375,10 @@
       !SpeedRSAKeyGen(selected) ||
       !SpeedHRSS(selected) ||
       !SpeedHashToCurve(selected) ||
-      !SpeedTrustToken("TrustToken-Batch1", 1, selected) ||
-      !SpeedTrustToken("TrustToken-Batch10", 10, selected)) {
+      !SpeedTrustToken("TrustToken-Exp0-Batch1", TRUST_TOKEN_experiment_v0(), 1,
+                       selected) ||
+      !SpeedTrustToken("TrustToken-Exp0-Batch10", TRUST_TOKEN_experiment_v0(),
+                       10, selected)) {
     return false;
   }
   if (g_print_json) {