Commit 1a63f5a8 authored by David Van Cleve's avatar David Van Cleve Committed by Commit Bot

Parse Trust Tokens key commitments.

Trust Tokens key commitment responses are served during the
issuance and redemption steps of the Trust Tokens (Privacy Pass)
protocol. As part of implementing Trust Tokens, we need to fetch
and parse these responses during the "issuance" and "redemption"
protocol steps, triggered by requests' having been tagged with
the corresponding fetch flags. For the preliminary version, these key
commitment responses' contents are specified in the design doc:
https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#bookmark=id.6wh9crbxdizi

This CL adds parsing code; a concurrent change adds a flow
for requesting these records from issuers' key commitment endpoints.

New code:
- KeyCommitmentResult describes the information we get from
a key commitment response;
- key_commitment_parsing.* implement logic and tests for parsing
the commitments. We try to err on the side of being very strict,
rejecting outright many messages that have any part malformed
(for instance, if a record contains three keys and one is bad,
the entire message gets thrown out).

Bug: 1042962
Change-Id: I3b3d9b0b71bc74f80839b0f14309b990bcbe5a09
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2029292
Commit-Queue: David Van Cleve <davidvc@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748677}
parent 0c01aedd
...@@ -21,6 +21,8 @@ source_set("trust_tokens") { ...@@ -21,6 +21,8 @@ source_set("trust_tokens") {
"trust_token_http_headers.h", "trust_token_http_headers.h",
"trust_token_key_commitment_controller.cc", "trust_token_key_commitment_controller.cc",
"trust_token_key_commitment_controller.h", "trust_token_key_commitment_controller.h",
"trust_token_key_commitment_parser.cc",
"trust_token_key_commitment_parser.h",
"trust_token_key_commitment_result.cc", "trust_token_key_commitment_result.cc",
"trust_token_key_commitment_result.h", "trust_token_key_commitment_result.h",
"trust_token_operation_status.h", "trust_token_operation_status.h",
...@@ -78,6 +80,7 @@ source_set("tests") { ...@@ -78,6 +80,7 @@ source_set("tests") {
"sqlite_trust_token_persister_unittest.cc", "sqlite_trust_token_persister_unittest.cc",
"trust_token_database_owner_unittest.cc", "trust_token_database_owner_unittest.cc",
"trust_token_key_commitment_controller_unittest.cc", "trust_token_key_commitment_controller_unittest.cc",
"trust_token_key_commitment_parser_unittest.cc",
"trust_token_persister_unittest.cc", "trust_token_persister_unittest.cc",
"trust_token_request_canonicalizer_unittest.cc", "trust_token_request_canonicalizer_unittest.cc",
"trust_token_request_signing_helper_unittest.cc", "trust_token_request_signing_helper_unittest.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/trust_tokens/trust_token_key_commitment_parser.h"
#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
#include "services/network/trust_tokens/trust_token_key_commitment_result.h"
namespace network {
namespace {
// Parses a single key label. If |in| is the string representation of an integer
// in in the representable range of uint32_t, sets |*out| to that integer value
// and returns true. Otherwise, returns false.
bool ParseSingleKeyLabel(base::StringPiece in, uint32_t* out) {
uint64_t key_label_in_uint64;
if (!base::StringToUint64(in, &key_label_in_uint64))
return false;
if (!base::IsValueInRangeForNumericType<uint32_t>(key_label_in_uint64))
return false;
*out = base::checked_cast<uint32_t>(key_label_in_uint64);
return true;
}
enum class ParseKeyResult {
// Continue as if the key didn't exist.
kIgnore,
// Fail parsing totally.
kFail,
// Parsing the key succeeded.
kSucceed
};
// Parses a single key, consisting of a body (the key material) and an expiry
// timestamp. Fails the parse if either field is missing or malformed. If the
// key has expired but is otherwise valid, ignores the key rather than failing
// the prase.
ParseKeyResult ParseSingleKeyExceptLabel(
const base::Value& in,
TrustTokenKeyCommitmentResult::Key* out) {
CHECK(in.is_dict());
const std::string* expiry =
in.FindStringKey(kTrustTokenKeyCommitmentExpiryField);
const std::string* key_body =
in.FindStringKey(kTrustTokenKeyCommitmentKeyField);
if (!expiry || !key_body)
return ParseKeyResult::kFail;
uint64_t expiry_microseconds_since_unix_epoch;
if (!base::StringToUint64(*expiry, &expiry_microseconds_since_unix_epoch))
return ParseKeyResult::kFail;
if (!base::Base64Decode(*key_body, &out->body))
return ParseKeyResult::kFail;
out->expiry =
base::Time::UnixEpoch() +
base::TimeDelta::FromMicroseconds(expiry_microseconds_since_unix_epoch);
if (out->expiry <= base::Time::Now())
return ParseKeyResult::kIgnore;
return ParseKeyResult::kSucceed;
}
} // namespace
const char kTrustTokenKeyCommitmentBatchsizeField[] = "batchsize";
const char kTrustTokenKeyCommitmentSrrkeyField[] = "srrkey";
const char kTrustTokenKeyCommitmentExpiryField[] = "expiry";
const char kTrustTokenKeyCommitmentKeyField[] = "Y";
// https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#bookmark=id.6wh9crbxdizi
// {
// "batchsize" : ..., // Optional batch size; value of type int.
// "srrkey" : ..., // Required Signed Redemption Record (SRR)
// // verification key, in base64.
//
// "1" : { // Key label, a number in uint32_t range.
// "Y" : ..., // Required token issuance verification key, in
// // base64.
// "expiry" : ..., // Required token issuance key expiry time, in
// // microseconds since the Unix epoch.
// },
// "17" : { // No guarantee that key labels (1, 17) are dense.
// "Y" : ...,
// "expiry" : ...,
// }
// }
std::unique_ptr<TrustTokenKeyCommitmentResult>
TrustTokenKeyCommitmentParser::Parse(base::StringPiece response_body) {
base::Optional<base::Value> maybe_value =
base::JSONReader::Read(response_body);
if (!maybe_value)
return nullptr;
if (!maybe_value->is_dict())
return nullptr;
auto result = std::make_unique<TrustTokenKeyCommitmentResult>();
// Confirm that the batchsize field is type-safe, if it's present.
if (maybe_value->FindKey(kTrustTokenKeyCommitmentBatchsizeField) &&
!maybe_value->FindIntKey(kTrustTokenKeyCommitmentBatchsizeField)) {
return nullptr;
}
result->batch_size =
maybe_value->FindIntKey(kTrustTokenKeyCommitmentBatchsizeField);
// Confirm that the srrkey field is present and base64-encoded.
const std::string* maybe_srrkey =
maybe_value->FindStringKey(kTrustTokenKeyCommitmentSrrkeyField);
if (!maybe_srrkey)
return nullptr;
if (!base::Base64Decode(*maybe_srrkey,
&result->signed_redemption_record_verification_key)) {
return nullptr;
}
// Parse the key commitments in the result (these are exactly the
// key-value pairs in the dictionary with dictionary-typed values).
for (const auto& kv : maybe_value->DictItems()) {
const base::Value& item = kv.second;
if (!item.is_dict())
continue;
TrustTokenKeyCommitmentResult::Key key;
if (!ParseSingleKeyLabel(kv.first, &key.label))
return nullptr;
switch (ParseSingleKeyExceptLabel(item, &key)) {
case ParseKeyResult::kFail:
return nullptr;
case ParseKeyResult::kIgnore:
continue;
case ParseKeyResult::kSucceed:
result->keys.push_back(std::move(key));
}
}
return result;
}
} // namespace network
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SERVICES_NETWORK_TRUST_TOKENS_TRUST_TOKEN_KEY_COMMITMENT_PARSER_H_
#define SERVICES_NETWORK_TRUST_TOKENS_TRUST_TOKEN_KEY_COMMITMENT_PARSER_H_
#include <memory>
#include "base/strings/string_piece_forward.h"
#include "services/network/trust_tokens/trust_token_key_commitment_controller.h"
namespace network {
struct TrustTokenKeyCommitmentResult;
// Field names from the key commitment JSON format specified in the Trust Tokens
// design doc
// (https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#bookmark=id.6wh9crbxdizi):
// - "batch size" (number of blinded tokens to provide per issuance request)
extern const char kTrustTokenKeyCommitmentBatchsizeField[];
// - verification key for the signatures the issuer provides over its Signed
// Redemption Records (SRRs)
extern const char kTrustTokenKeyCommitmentSrrkeyField[];
// - each issuance key's expiry timestamp
extern const char kTrustTokenKeyCommitmentExpiryField[];
// - each issuance key's key material
extern const char kTrustTokenKeyCommitmentKeyField[];
class TrustTokenKeyCommitmentParser
: public TrustTokenKeyCommitmentController::Parser {
public:
TrustTokenKeyCommitmentParser() = default;
~TrustTokenKeyCommitmentParser() override = default;
// Parses a JSON key commitment response.
//
// This method returns nullptr unless:
// - the input is valid JSON; and
// - the JSON represents a nonempty dictionary; and
// - within this inner dictionary (which stores metadata like batch size, as
// well as more dictionaries denoting keys' information):
// - every dictionary-type value has an expiry field
// (|kTrustTokenKeyCommitmentExpiryField| above) and a key body field
// (|kTrustTokenKeyCommitmentKeyField|), and
// - the expiry field is a positive integer (microseconds since the Unix
// epoch) storing a time in the future.
std::unique_ptr<TrustTokenKeyCommitmentResult> Parse(
base::StringPiece response_body) override;
};
} // namespace network
#endif // SERVICES_NETWORK_TRUST_TOKENS_TRUST_TOKEN_KEY_COMMITMENT_PARSER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment