Commit d43ac644 authored by David Van Cleve's avatar David Van Cleve Committed by Commit Bot

Trust Tokens: Expand key commitment parsing to handle multiple issuers

This CL makes a couple key commitment parsing and storage changes in
preparation for receiving JSON-encoded keys (rather than Mojo structs)
from the browser:

1. It modifies the NetworkService SetTrustTokenKeyCommitments interface
to take a string rather than a map<issuer, key commitment result>;
2. It expands the key commitment parsing logic in the network service
with a new method capable of parsing a collection of (issuer -> key
commitment result) entries.
3. It adds a new TrustTokenKeyCommitments setter taking a raw string and
passing it to the new parsing interface, rather than taking a map.

R=csharrison

Bug: 1068651
Change-Id: I930e797c39c4adac0da3919cbc462283c89d6f3f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2139416
Commit-Queue: Matthew Denton <mpdenton@chromium.org>
Reviewed-by: default avatarMatthew Denton <mpdenton@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759185}
parent bd8cd491
...@@ -712,9 +712,10 @@ void NetworkService::SetEnvironment( ...@@ -712,9 +712,10 @@ void NetworkService::SetEnvironment(
} }
void NetworkService::SetTrustTokenKeyCommitments( void NetworkService::SetTrustTokenKeyCommitments(
base::flat_map<url::Origin, mojom::TrustTokenKeyCommitmentResultPtr> const std::string& raw_commitments,
commitments) { base::OnceClosure done) {
trust_token_key_commitments_->Set(std::move(commitments)); trust_token_key_commitments_->ParseAndSet(raw_commitments);
std::move(done).Run();
} }
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
......
...@@ -175,9 +175,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService ...@@ -175,9 +175,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService
#endif #endif
void SetEnvironment( void SetEnvironment(
std::vector<mojom::EnvironmentVariablePtr> environment) override; std::vector<mojom::EnvironmentVariablePtr> environment) override;
void SetTrustTokenKeyCommitments( void SetTrustTokenKeyCommitments(const std::string& raw_commitments,
base::flat_map<url::Origin, mojom::TrustTokenKeyCommitmentResultPtr> base::OnceClosure done) override;
commitments) override;
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
void DumpWithoutCrashing(base::Time dump_request_time) override; void DumpWithoutCrashing(base::Time dump_request_time) override;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "base/base64.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/containers/span.h" #include "base/containers/span.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
...@@ -1230,25 +1231,25 @@ TEST_F(NetworkServiceTestWithService, SetsTrustTokenKeyCommitments) { ...@@ -1230,25 +1231,25 @@ TEST_F(NetworkServiceTestWithService, SetsTrustTokenKeyCommitments) {
ASSERT_TRUE(service_->trust_token_key_commitments()); ASSERT_TRUE(service_->trust_token_key_commitments());
auto expectation = mojom::TrustTokenKeyCommitmentResult::New(); auto expectation = mojom::TrustTokenKeyCommitmentResult::New();
expectation->batch_size = mojom::TrustTokenKeyCommitmentBatchSize::New(5); ASSERT_TRUE(base::Base64Decode(
"aaaa", &expectation->signed_redemption_record_verification_key));
url::Origin issuer_origin = base::RunLoop run_loop;
url::Origin::Create(GURL("https://issuer.example")); network_service_->SetTrustTokenKeyCommitments(
R"( { "https://issuer.example": { "srrkey": "aaaa" } } )",
base::flat_map<url::Origin, mojom::TrustTokenKeyCommitmentResultPtr> to_set; run_loop.QuitClosure());
to_set.insert_or_assign(issuer_origin, expectation.Clone()); run_loop.Run();
network_service_->SetTrustTokenKeyCommitments(std::move(to_set));
network_service_.FlushForTesting();
mojom::TrustTokenKeyCommitmentResultPtr result; mojom::TrustTokenKeyCommitmentResultPtr result;
bool ran = false; bool ran = false;
service_->trust_token_key_commitments()->Get( service_->trust_token_key_commitments()->Get(
issuer_origin, base::BindLambdaForTesting( *SuitableTrustTokenOrigin::Create(GURL("https://issuer.example")),
[&](mojom::TrustTokenKeyCommitmentResultPtr ptr) { base::BindLambdaForTesting(
result = std::move(ptr); [&](mojom::TrustTokenKeyCommitmentResultPtr ptr) {
ran = true; result = std::move(ptr);
})); ran = true;
}));
ASSERT_TRUE(ran); ASSERT_TRUE(ran);
......
...@@ -387,11 +387,12 @@ interface NetworkService { ...@@ -387,11 +387,12 @@ interface NetworkService {
// |environment| array. // |environment| array.
SetEnvironment(array<EnvironmentVariable> environment); SetEnvironment(array<EnvironmentVariable> environment);
// Sets Trust Tokens key commitment state. |commitments| is a map from issuer // Sets Trust Tokens key commitment state. |raw_commitments| is the
// origins to key commitment results (each result contains a collection of // JSON-encoded string representation of a collection of issuers' key
// keys and some associated metadata). // commitments according to the format specified, for now, in the Trust
SetTrustTokenKeyCommitments( // Tokens design doc:
map<url.mojom.Origin, TrustTokenKeyCommitmentResult> commitments); // https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#heading=h.z52drgpfgulz.
SetTrustTokenKeyCommitments(string raw_commitments) => ();
// Calls base::debug::DumpWithoutCrashing for the network process. // Calls base::debug::DumpWithoutCrashing for the network process.
// TODO(http://crbug.com/934317): Remove this once done debugging renderer // TODO(http://crbug.com/934317): Remove this once done debugging renderer
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//services/network/public/cpp/features.gni") import("//services/network/public/cpp/features.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//third_party/protobuf/proto_library.gni") import("//third_party/protobuf/proto_library.gni")
source_set("trust_tokens") { source_set("trust_tokens") {
...@@ -158,3 +159,14 @@ proto_library("storage_proto") { ...@@ -158,3 +159,14 @@ proto_library("storage_proto") {
extra_configs = [ "//build/config/compiler:wexit_time_destructors" ] extra_configs = [ "//build/config/compiler:wexit_time_destructors" ]
} }
fuzzer_test("trust_token_key_commitment_parser_fuzzer") {
sources = [ "test/trust_token_key_commitment_parser_fuzzer.cc" ]
deps = [
":trust_tokens",
"//base",
"//services/network/public/mojom",
]
dict = "test/trust_token_key_commitment_parser_fuzzer.dict"
seed_corpus = "//third_party/grpc/src/test/core/json/corpus/"
}
// 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 <memory>
#include "services/network/public/mojom/trust_tokens.mojom.h"
namespace network {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
base::StringPiece string_input(reinterpret_cast<const char*>(data), size);
TrustTokenKeyCommitmentParser().Parse(string_input);
TrustTokenKeyCommitmentParser().ParseMultipleIssuers(string_input);
return 0;
}
} // 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.
"{"
"}"
"["
"]"
"\""
"'"
"\\"
"//"
":"
","
" "
"\\n"
"\\r"
"/*"
"*/"
"true"
"false"
"null"
"\\u"
"\\b"
"\\f"
"\\t"
"."
"e"
"e+"
"e-"
"E"
"E+"
"E-"
"srrkey"
"batchsize"
"Y"
"expiry"
"https"
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/values.h" #include "base/values.h"
#include "services/network/public/mojom/trust_tokens.mojom-forward.h"
#include "services/network/public/mojom/trust_tokens.mojom.h" #include "services/network/public/mojom/trust_tokens.mojom.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h"
namespace network { namespace network {
...@@ -70,56 +72,27 @@ ParseKeyResult ParseSingleKeyExceptLabel( ...@@ -70,56 +72,27 @@ ParseKeyResult ParseSingleKeyExceptLabel(
return ParseKeyResult::kSucceed; return ParseKeyResult::kSucceed;
} }
} // namespace mojom::TrustTokenKeyCommitmentResultPtr ParseSingleIssuer(
const base::Value& value) {
const char kTrustTokenKeyCommitmentBatchsizeField[] = "batchsize"; if (!value.is_dict())
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" : ...,
// }
// }
mojom::TrustTokenKeyCommitmentResultPtr 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; return nullptr;
auto result = mojom::TrustTokenKeyCommitmentResult::New(); auto result = mojom::TrustTokenKeyCommitmentResult::New();
// Confirm that the batchsize field is type-safe, if it's present. // Confirm that the batchsize field is type-safe, if it's present.
if (maybe_value->FindKey(kTrustTokenKeyCommitmentBatchsizeField) && if (value.FindKey(kTrustTokenKeyCommitmentBatchsizeField) &&
!maybe_value->FindIntKey(kTrustTokenKeyCommitmentBatchsizeField)) { !value.FindIntKey(kTrustTokenKeyCommitmentBatchsizeField)) {
return nullptr; return nullptr;
} }
if (base::Optional<int> maybe_batch_size = if (base::Optional<int> maybe_batch_size =
maybe_value->FindIntKey(kTrustTokenKeyCommitmentBatchsizeField)) { value.FindIntKey(kTrustTokenKeyCommitmentBatchsizeField)) {
result->batch_size = result->batch_size =
mojom::TrustTokenKeyCommitmentBatchSize::New(*maybe_batch_size); mojom::TrustTokenKeyCommitmentBatchSize::New(*maybe_batch_size);
} }
// Confirm that the srrkey field is present and base64-encoded. // Confirm that the srrkey field is present and base64-encoded.
const std::string* maybe_srrkey = const std::string* maybe_srrkey =
maybe_value->FindStringKey(kTrustTokenKeyCommitmentSrrkeyField); value.FindStringKey(kTrustTokenKeyCommitmentSrrkeyField);
if (!maybe_srrkey) if (!maybe_srrkey)
return nullptr; return nullptr;
if (!base::Base64Decode(*maybe_srrkey, if (!base::Base64Decode(*maybe_srrkey,
...@@ -129,7 +102,7 @@ mojom::TrustTokenKeyCommitmentResultPtr TrustTokenKeyCommitmentParser::Parse( ...@@ -129,7 +102,7 @@ mojom::TrustTokenKeyCommitmentResultPtr TrustTokenKeyCommitmentParser::Parse(
// Parse the key commitments in the result (these are exactly the // Parse the key commitments in the result (these are exactly the
// key-value pairs in the dictionary with dictionary-typed values). // key-value pairs in the dictionary with dictionary-typed values).
for (const auto& kv : maybe_value->DictItems()) { for (const auto& kv : value.DictItems()) {
const base::Value& item = kv.second; const base::Value& item = kv.second;
if (!item.is_dict()) if (!item.is_dict())
continue; continue;
...@@ -152,4 +125,117 @@ mojom::TrustTokenKeyCommitmentResultPtr TrustTokenKeyCommitmentParser::Parse( ...@@ -152,4 +125,117 @@ mojom::TrustTokenKeyCommitmentResultPtr TrustTokenKeyCommitmentParser::Parse(
return result; return result;
} }
// Entry is a convenience struct used as an intermediate representation when
// parsing multiple issuers. In addition to a parsed canonicalized issuer, it
// preserves the raw JSON string key (the second entry) in order
// deterministically to deduplicate entries with keys canonicalizing to the same
// issuer.
using Entry = std::tuple<SuitableTrustTokenOrigin, // canonicalized issuer
std::string, // raw key from the JSON
mojom::TrustTokenKeyCommitmentResultPtr>;
SuitableTrustTokenOrigin& canonicalized_issuer(Entry& e) {
return std::get<0>(e);
}
mojom::TrustTokenKeyCommitmentResultPtr& commitment(Entry& e) {
return std::get<2>(e);
}
} // 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" : ...,
// }
// }
mojom::TrustTokenKeyCommitmentResultPtr TrustTokenKeyCommitmentParser::Parse(
base::StringPiece response_body) {
base::Optional<base::Value> maybe_value =
base::JSONReader::Read(response_body);
if (!maybe_value)
return nullptr;
return ParseSingleIssuer(std::move(*maybe_value));
}
std::unique_ptr<base::flat_map<SuitableTrustTokenOrigin,
mojom::TrustTokenKeyCommitmentResultPtr>>
TrustTokenKeyCommitmentParser::ParseMultipleIssuers(
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;
// The configuration might contain conflicting lists of keys for issuers with
// the same canonicalized URLs but different string representations provided
// by the server. In order to handle these deterministically, first transfer
// the entries to intermediate storage while maintaining the initial JSON
// keys; then deduplicate based on identical entries' JSON keys' lexicographic
// value.
std::vector<Entry> parsed_entries;
for (const auto& kv : maybe_value->DictItems()) {
const std::string& raw_key_from_json = kv.first;
base::Optional<SuitableTrustTokenOrigin> maybe_issuer =
SuitableTrustTokenOrigin::Create(GURL(raw_key_from_json));
if (!maybe_issuer)
continue;
mojom::TrustTokenKeyCommitmentResultPtr commitment_result =
ParseSingleIssuer(kv.second);
if (!commitment_result)
continue;
parsed_entries.emplace_back(Entry(std::move(*maybe_issuer),
raw_key_from_json,
std::move(commitment_result)));
}
// Deterministically deduplicate entries corresponding to the same issuer,
// with the largest JSON key lexicographically winning.
std::sort(parsed_entries.begin(), parsed_entries.end(), std::greater<>());
parsed_entries.erase(std::unique(parsed_entries.begin(), parsed_entries.end(),
[](Entry& lhs, Entry& rhs) -> bool {
return canonicalized_issuer(lhs) ==
canonicalized_issuer(rhs);
}),
parsed_entries.end());
// Finally, discard the raw JSON strings and construct a map to return.
std::vector<std::pair<SuitableTrustTokenOrigin,
mojom::TrustTokenKeyCommitmentResultPtr>>
map_storage;
map_storage.reserve(parsed_entries.size());
for (Entry& e : parsed_entries) {
map_storage.emplace_back(std::move(canonicalized_issuer(e)),
std::move(commitment(e)));
}
return std::make_unique<base::flat_map<
SuitableTrustTokenOrigin, mojom::TrustTokenKeyCommitmentResultPtr>>(
std::move(map_storage));
}
} // namespace network } // namespace network
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/strings/string_piece_forward.h" #include "base/strings/string_piece_forward.h"
#include "services/network/public/mojom/trust_tokens.mojom-forward.h" #include "services/network/public/mojom/trust_tokens.mojom-forward.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h"
#include "services/network/trust_tokens/trust_token_key_commitment_controller.h" #include "services/network/trust_tokens/trust_token_key_commitment_controller.h"
namespace network { namespace network {
...@@ -46,6 +47,30 @@ class TrustTokenKeyCommitmentParser ...@@ -46,6 +47,30 @@ class TrustTokenKeyCommitmentParser
// epoch) storing a time in the future. // epoch) storing a time in the future.
mojom::TrustTokenKeyCommitmentResultPtr Parse( mojom::TrustTokenKeyCommitmentResultPtr Parse(
base::StringPiece response_body) override; base::StringPiece response_body) override;
// Like |Parse|, except that the input is expected to be of the form
// { "https://some-issuer.example": <JSON in the form expected by |Parse|>
// "https://some-other-issuer.example":
// <JSON in the form expected by |Parse|>,
// ... }
//
// Returns nullptr if the input is not a dictionary.
//
// WARNING: If there are multiple keys that are exactly equal strings,
// deduplicates these entries arbitrarily (due to the behavior of
// base::JSONReader). For instance, if these keys are arriving through the
// component updater, you might want to guarantee that the server-side logic
// producing these structures guarantees no duplicate keys.
//
// If there are multiple keys that are not exact duplicates but correspond to
// the same issuer, drops all but the entry with the largest key
// lexicographically.
//
// Skips key-value pairs where the key is not a suitable Trust Tokens origin
// or the value fails to parse.
std::unique_ptr<base::flat_map<SuitableTrustTokenOrigin,
mojom::TrustTokenKeyCommitmentResultPtr>>
ParseMultipleIssuers(base::StringPiece response_body);
}; };
} // namespace network } // namespace network
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/optional.h" #include "base/optional.h"
#include "services/network/public/mojom/trust_tokens.mojom-forward.h" #include "services/network/public/mojom/trust_tokens.mojom-forward.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h" #include "services/network/trust_tokens/suitable_trust_token_origin.h"
#include "services/network/trust_tokens/trust_token_key_commitment_parser.h"
namespace network { namespace network {
...@@ -43,6 +44,13 @@ void TrustTokenKeyCommitments::Set( ...@@ -43,6 +44,13 @@ void TrustTokenKeyCommitments::Set(
map_.replace(std::move(filtered)); map_.replace(std::move(filtered));
} }
void TrustTokenKeyCommitments::ParseAndSet(base::StringPiece raw_commitments) {
TrustTokenKeyCommitmentParser parser;
if (auto commitments = parser.ParseMultipleIssuers(raw_commitments)) {
map_.swap(*commitments);
}
}
void TrustTokenKeyCommitments::Get( void TrustTokenKeyCommitments::Get(
const url::Origin& origin, const url::Origin& origin,
base::OnceCallback<void(mojom::TrustTokenKeyCommitmentResultPtr)> done) base::OnceCallback<void(mojom::TrustTokenKeyCommitmentResultPtr)> done)
......
...@@ -33,6 +33,13 @@ class TrustTokenKeyCommitments : public TrustTokenKeyCommitmentGetter { ...@@ -33,6 +33,13 @@ class TrustTokenKeyCommitments : public TrustTokenKeyCommitmentGetter {
void Set( void Set(
base::flat_map<url::Origin, mojom::TrustTokenKeyCommitmentResultPtr> map); base::flat_map<url::Origin, mojom::TrustTokenKeyCommitmentResultPtr> map);
// Overwrites the current issuers-to-commitments map with the values in
// |raw_commitments|, which should be the JSON-encoded string representation
// of a collection of issuers' key commitments according to the format
// specified, for now, in the Trust Tokens design doc:
// https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#heading=h.z52drgpfgulz.
void ParseAndSet(base::StringPiece raw_commitments);
// TrustTokenKeyCommitmentGetter implementation: // TrustTokenKeyCommitmentGetter implementation:
// //
// If |origin| is a suitable Trust Tokens origin (in the sense of // If |origin| is a suitable Trust Tokens origin (in the sense of
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "services/network/public/mojom/trust_tokens.mojom-forward.h" #include "services/network/public/mojom/trust_tokens.mojom-forward.h"
#include "services/network/public/mojom/trust_tokens.mojom.h" #include "services/network/public/mojom/trust_tokens.mojom.h"
#include "services/network/trust_tokens/suitable_trust_token_origin.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h" #include "url/gurl.h"
#include "url/origin.h" #include "url/origin.h"
...@@ -122,4 +123,14 @@ TEST(TrustTokenKeyCommitments, MultipleOrigins) { ...@@ -122,4 +123,14 @@ TEST(TrustTokenKeyCommitments, MultipleOrigins) {
} }
} }
TEST(TrustTokenKeyCommitments, ParseAndSet) {
TrustTokenKeyCommitments commitments;
commitments.ParseAndSet(
R"( { "https://issuer.example": { "srrkey": "aaaa" } } )");
EXPECT_TRUE(GetCommitmentForOrigin(
commitments,
*SuitableTrustTokenOrigin::Create(GURL("https://issuer.example"))));
}
} // namespace network } // namespace network
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