Add raw redeem API.

Change-Id: I70225ad7f95fa1dbaeecb830b17e4cde34d1bd0a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43444
Commit-Queue: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/trust_token/trust_token.c b/crypto/trust_token/trust_token.c
index a4891d8..3334fba 100644
--- a/crypto/trust_token/trust_token.c
+++ b/crypto/trust_token/trust_token.c
@@ -303,7 +303,7 @@
       !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) ||
+      (ctx->method->has_srr && !CBB_add_u64(&request, time)) ||
       !CBB_finish(&request, out, out_len)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
     CBB_cleanup(&request);
@@ -518,6 +518,72 @@
   return ret;
 }
 
+
+int TRUST_TOKEN_ISSUER_redeem_raw(const TRUST_TOKEN_ISSUER *ctx,
+                                  uint32_t *out_public, uint8_t *out_private,
+                                  TRUST_TOKEN **out_token,
+                                  uint8_t **out_client_data,
+                                  size_t *out_client_data_len,
+                                  const uint8_t *request, size_t request_len) {
+  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.
+  if (!CBS_get_u32(&token_cbs, &public_metadata)) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_TOKEN);
+    return 0;
+  }
+
+  const struct trust_token_issuer_key_st *key =
+      trust_token_issuer_get_key(ctx, public_metadata);
+  uint8_t nonce[TRUST_TOKEN_NONCE_SIZE];
+  if (key == NULL ||
+      !ctx->method->read(&key->key, nonce, &private_metadata,
+                         CBS_data(&token_cbs), CBS_len(&token_cbs))) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_INVALID_TOKEN);
+    return 0;
+  }
+
+  CBS client_data;
+  if (!CBS_get_u16_length_prefixed(&request_cbs, &client_data) ||
+      (ctx->method->has_srr && !CBS_skip(&request_cbs, 8)) ||
+      CBS_len(&request_cbs) != 0) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_ERROR);
+    return 0;
+  }
+
+  uint8_t *client_data_buf = NULL;
+  size_t client_data_len = 0;
+  if (!CBS_stow(&client_data, &client_data_buf, &client_data_len)) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  TRUST_TOKEN *token = TRUST_TOKEN_new(nonce, TRUST_TOKEN_NONCE_SIZE);
+  if (token == NULL) {
+    OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  *out_public = public_metadata;
+  *out_private = private_metadata;
+  *out_token = token;
+  *out_client_data = client_data_buf;
+  *out_client_data_len = client_data_len;
+
+  return 1;
+
+err:
+  OPENSSL_free(client_data_buf);
+  return 0;
+}
+
 // 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) {
@@ -622,9 +688,9 @@
   }
 
   CBS client_data;
-  uint64_t redemption_time;
+  uint64_t redemption_time = 0;
   if (!CBS_get_u16_length_prefixed(&request_cbs, &client_data) ||
-      !CBS_get_u64(&request_cbs, &redemption_time)) {
+      (ctx->method->has_srr && !CBS_get_u64(&request_cbs, &redemption_time))) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_DECODE_ERROR);
     goto err;
   }
@@ -643,6 +709,19 @@
   // The SRR is constructed as per the format described in
   // https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#heading=h.7mkzvhpqb8l5
 
+  // The V2 protocol is intended to be used with
+  // |TRUST_TOKEN_ISSUER_redeem_raw|. However, we temporarily support it with
+  // |TRUST_TOKEN_ISSUER_redeem| to ease the transition for existing issuer
+  // callers. Those callers' consumers currently expect an expiry-timestamp
+  // field, so we fill in a placeholder value.
+  //
+  // TODO(svaldez): After the existing issues have migrated to
+  // |TRUST_TOKEN_ISSUER_redeem_raw| remove this logic.
+  uint64_t expiry_time = 0;
+  if (ctx->method->has_srr) {
+    expiry_time = redemption_time + lifetime;
+  }
+
   static const char kClientDataLabel[] = "client-data";
   static const char kExpiryTimestampLabel[] = "expiry-timestamp";
   static const char kMetadataLabel[] = "metadata";
@@ -673,7 +752,7 @@
       !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_int(&srr, expiry_time) ||
       !CBB_finish(&srr, &srr_buf, &srr_len)) {
     OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
     goto err;
diff --git a/crypto/trust_token/trust_token_test.cc b/crypto/trust_token/trust_token_test.cc
index b6d080f..f9f183d 100644
--- a/crypto/trust_token/trust_token_test.cc
+++ b/crypto/trust_token/trust_token_test.cc
@@ -325,7 +325,7 @@
 
   for (TRUST_TOKEN *token : tokens.get()) {
     const uint8_t kClientData[] = "\x70TEST CLIENT DATA";
-    uint64_t kRedemptionTime = 13374242;
+    uint64_t kRedemptionTime = (method()->has_srr ? 13374242 : 0);
 
     uint8_t *redeem_msg = NULL, *redeem_resp = NULL;
     ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_redemption(
@@ -366,7 +366,7 @@
 
   for (TRUST_TOKEN *token : tokens.get()) {
     const uint8_t kClientData[] = "\x70TEST CLIENT DATA";
-    uint64_t kRedemptionTime = 13374242;
+    uint64_t kRedemptionTime = 0;
 
     uint8_t *redeem_msg = NULL, *redeem_resp = NULL;
     ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_redemption(
@@ -499,9 +499,9 @@
 
   for (TRUST_TOKEN *token : tokens.get()) {
     const uint8_t kClientData[] = "\x70TEST CLIENT DATA";
-    uint64_t kRedemptionTime = 13374242;
+    uint64_t kRedemptionTime = (method()->has_srr ? 13374242 : 0);
 
-    const uint8_t kExpectedSRR[] =
+    const uint8_t kExpectedSRRV1[] =
         "\xa4\x68\x6d\x65\x74\x61\x64\x61\x74\x61\xa2\x66\x70\x75\x62\x6c\x69"
         "\x63\x00\x67\x70\x72\x69\x76\x61\x74\x65\x00\x6a\x74\x6f\x6b\x65\x6e"
         "\x2d\x68\x61\x73\x68\x58\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
@@ -511,6 +511,23 @@
         "\x70\x65\x78\x70\x69\x72\x79\x2d\x74\x69\x6d\x65\x73\x74\x61\x6d\x70"
         "\x1a\x00\xcc\x15\x7a";
 
+    const uint8_t kExpectedSRRV2[] =
+        "\xa4\x68\x6d\x65\x74\x61\x64\x61\x74\x61\xa2\x66\x70\x75\x62\x6c\x69"
+        "\x63\x00\x67\x70\x72\x69\x76\x61\x74\x65\x00\x6a\x74\x6f\x6b\x65\x6e"
+        "\x2d\x68\x61\x73\x68\x58\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x6b\x63\x6c\x69\x65\x6e\x74\x2d\x64\x61\x74\x61"
+        "\x70\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"
+        "\x00";
+
+    const uint8_t *expected_srr = kExpectedSRRV1;
+    size_t expected_srr_len = sizeof(kExpectedSRRV1) - 1;
+    if (!method()->has_srr) {
+      expected_srr = kExpectedSRRV2;
+      expected_srr_len = sizeof(kExpectedSRRV2) - 1;
+    }
+
     uint8_t *redeem_msg = NULL, *redeem_resp = NULL;
     ASSERT_TRUE(TRUST_TOKEN_CLIENT_begin_redemption(
         client.get(), &redeem_msg, &msg_len, token, kClientData,
@@ -540,22 +557,21 @@
 
     if (!method()->has_srr) {
       size_t b64_len;
-      ASSERT_TRUE(EVP_EncodedLength(&b64_len, sizeof(kExpectedSRR) - 1));
+      ASSERT_TRUE(EVP_EncodedLength(&b64_len, expected_srr_len));
       b64_len -= 1;
-
       const char kSRRHeader[] = "body=:";
       ASSERT_LT(sizeof(kSRRHeader) - 1 + b64_len, srr_len);
 
       ASSERT_EQ(Bytes(kSRRHeader, sizeof(kSRRHeader) - 1),
                 Bytes(srr, sizeof(kSRRHeader) - 1));
       uint8_t *decoded_srr =
-          (uint8_t *)OPENSSL_malloc(sizeof(kExpectedSRR) + 1);
+          (uint8_t *)OPENSSL_malloc(expected_srr_len + 2);
       ASSERT_TRUE(decoded_srr);
-      ASSERT_LT(
-          int(sizeof(kExpectedSRR) - 1),
+      ASSERT_LE(
+          int(expected_srr_len),
           EVP_DecodeBlock(decoded_srr, srr + sizeof(kSRRHeader) - 1, b64_len));
       srr = decoded_srr;
-      srr_len = sizeof(kExpectedSRR) - 1;
+      srr_len = expected_srr_len;
       free_srr.reset(srr);
     }
 
@@ -584,11 +600,63 @@
     // Clear out the token hash.
     OPENSSL_memset(srr + 41, 0, sizeof(token_hash));
 
-    ASSERT_EQ(Bytes(kExpectedSRR, sizeof(kExpectedSRR) - 1),
+    ASSERT_EQ(Bytes(expected_srr, expected_srr_len),
               Bytes(srr, srr_len));
   }
 }
 
+TEST_P(TrustTokenMetadataTest, RawSetAndGetMetadata) {
+  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);
+  size_t tokens_issued;
+  bool result = TRUST_TOKEN_ISSUER_issue(
+      issuer.get(), &issue_resp, &resp_len, &tokens_issued, issue_msg, msg_len,
+      public_metadata(), private_metadata(), /*max_issuance=*/1);
+  if (!method()->has_private_metadata && private_metadata()) {
+    ASSERT_FALSE(result);
+    return;
+  }
+  ASSERT_TRUE(result);
+  bssl::UniquePtr<uint8_t> free_msg(issue_resp);
+  size_t key_index;
+  bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens(
+      TRUST_TOKEN_CLIENT_finish_issuance(client.get(), &key_index, issue_resp,
+                                         resp_len));
+  ASSERT_TRUE(tokens);
+  EXPECT_EQ(1u, sk_TRUST_TOKEN_num(tokens.get()));
+
+  for (TRUST_TOKEN *token : tokens.get()) {
+    const uint8_t kClientData[] = "\x70TEST CLIENT DATA";
+    uint64_t kRedemptionTime = (method()->has_srr ? 13374242 : 0);
+
+    uint8_t *redeem_msg = 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);
+    uint32_t public_value;
+    uint8_t private_value;
+    TRUST_TOKEN *rtoken;
+    uint8_t *client_data;
+    size_t client_data_len;
+    ASSERT_TRUE(TRUST_TOKEN_ISSUER_redeem_raw(
+        issuer.get(), &public_value, &private_value, &rtoken,
+        &client_data, &client_data_len, redeem_msg, msg_len));
+    bssl::UniquePtr<uint8_t> free_client_data(client_data);
+    bssl::UniquePtr<TRUST_TOKEN> free_rtoken(rtoken);
+
+    ASSERT_EQ(Bytes(kClientData, sizeof(kClientData) - 1),
+              Bytes(client_data, client_data_len));
+    ASSERT_EQ(public_value, static_cast<uint32_t>(public_metadata()));
+    ASSERT_EQ(private_value, private_metadata());
+  }
+}
+
 TEST_P(TrustTokenMetadataTest, TooManyRequests) {
   if (!method()->has_private_metadata && private_metadata()) {
     return;
diff --git a/include/openssl/trust_token.h b/include/openssl/trust_token.h
index 7146995..d9247f7 100644
--- a/include/openssl/trust_token.h
+++ b/include/openssl/trust_token.h
@@ -42,14 +42,10 @@
 
 // TRUST_TOKEN_experiment_v2_voprf is an experimental Trust Tokens protocol
 // using VOPRFs and P-384 with up to 6 keys, without RR verification.
-//
-// This version is incomplete and should not be used.
 OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_voprf(void);
 
 // TRUST_TOKEN_experiment_v2_pmb is an experimental Trust Tokens protocol using
 // PMBTokens and P-384 with up to 3 keys, without RR verification.
-//
-// This version is incomplete and should not be used.
 OPENSSL_EXPORT const TRUST_TOKEN_METHOD *TRUST_TOKEN_experiment_v2_pmb(void);
 
 // trust_token_st represents a single-use token for the Trust Token protocol.
@@ -150,9 +146,9 @@
 // |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.
+// verify the validity of the issuer's response in TrustTokenV1 and ignored in
+// other versions. 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,
@@ -228,16 +224,16 @@
     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
+// verifies the token. If the token is valid, a RR 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.
+// buffer and stored in |*out_client_data|. In TrustTokenV1, 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| seen globally before
 // returning the SRR to the client. If the value has been reused, the caller
@@ -249,6 +245,24 @@
     size_t *out_client_data_len, uint64_t *out_redemption_time,
     const uint8_t *request, size_t request_len, uint64_t lifetime);
 
+// TRUST_TOKEN_ISSUER_redeem_raw ingests a |request| for token redemption and
+// verifies the token. The public metadata is stored in |*out_public|. The
+// private metadata (if any) is stored in |*out_private|. 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 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| seen globally before
+// returning a response to the client. If the value has been reused, the caller
+// must report an error to the client. Returning a response with replayed values
+// allows an attacker to double-spend tokens.
+OPENSSL_EXPORT int TRUST_TOKEN_ISSUER_redeem_raw(
+    const TRUST_TOKEN_ISSUER *ctx, uint32_t *out_public, uint8_t *out_private,
+    TRUST_TOKEN **out_token, uint8_t **out_client_data,
+    size_t *out_client_data_len, const uint8_t *request, size_t request_len);
+
 // TRUST_TOKEN_decode_private_metadata decodes |encrypted_bit| using the
 // private metadata key specified by a |key| buffer of length |key_len| and the
 // nonce by a |nonce| buffer of length |nonce_len|. The nonce in