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

Trust Tokens: Move SRR serialization into its own file

A subsequent CL (crrev.com/c/2176796, adding an expiry checker for SRRs)
requires deserializing Trust Tokens signed redemption records (SRRs)
outside of tests. To prepare for this, this CL moves the currently
test-only SRR deserialization code to its own non-test file and add
tests covering the logic. It also moves the SRR serialization code out
of its current anonymous namespace to the same new standalone file.

R=csharrison

Bug: 1077060
Change-Id: I3803effa1104839098982d18cbb6176df803add8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2183256
Commit-Queue: David Van Cleve <davidvc@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Auto-Submit: David Van Cleve <davidvc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#766434}
parent e0704819
...@@ -31,6 +31,8 @@ source_set("trust_tokens") { ...@@ -31,6 +31,8 @@ source_set("trust_tokens") {
"pending_trust_token_store.cc", "pending_trust_token_store.cc",
"pending_trust_token_store.h", "pending_trust_token_store.h",
"scoped_boringssl_bytes.h", "scoped_boringssl_bytes.h",
"signed_redemption_record_serialization.cc",
"signed_redemption_record_serialization.h",
"sqlite_trust_token_persister.cc", "sqlite_trust_token_persister.cc",
"sqlite_trust_token_persister.h", "sqlite_trust_token_persister.h",
"suitable_trust_token_origin.cc", "suitable_trust_token_origin.cc",
...@@ -124,6 +126,7 @@ source_set("tests") { ...@@ -124,6 +126,7 @@ source_set("tests") {
"ed25519_trust_token_request_signer_unittest.cc", "ed25519_trust_token_request_signer_unittest.cc",
"has_trust_tokens_answerer_unittest.cc", "has_trust_tokens_answerer_unittest.cc",
"pending_trust_token_store_unittest.cc", "pending_trust_token_store_unittest.cc",
"signed_redemption_record_serialization_unittest.cc",
"sqlite_trust_token_persister_unittest.cc", "sqlite_trust_token_persister_unittest.cc",
"suitable_trust_token_origin_unittest.cc", "suitable_trust_token_origin_unittest.cc",
"trust_token_client_data_canonicalization_unittest.cc", "trust_token_client_data_canonicalization_unittest.cc",
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "net/http/structured_headers.h" #include "net/http/structured_headers.h"
#include "services/network/trust_tokens/scoped_boringssl_bytes.h" #include "services/network/trust_tokens/scoped_boringssl_bytes.h"
#include "services/network/trust_tokens/signed_redemption_record_serialization.h"
#include "services/network/trust_tokens/trust_token_client_data_canonicalization.h" #include "services/network/trust_tokens/trust_token_client_data_canonicalization.h"
#include "services/network/trust_tokens/trust_token_parameterization.h" #include "services/network/trust_tokens/trust_token_parameterization.h"
#include "third_party/boringssl/src/include/openssl/base.h" #include "third_party/boringssl/src/include/openssl/base.h"
...@@ -17,46 +18,6 @@ ...@@ -17,46 +18,6 @@
namespace network { namespace network {
namespace {
const char kSignedRedemptionRecordBodyKey[] = "body";
const char kSignedRedemptionRecordSignatureKey[] = "signature";
// The Trust Tokens design doc [1] defines a signed redemption record (SRR) as a
// Structured Headers Draft 15 dictionary with two "byte sequence"-typed fields,
// a body and a signature. This method constructs such a dictionary, given the
// body and signature's contents as bytestrings.
//
// Returns nullopt on internal serialization error in the Structured Headers
// library.
//
// [1]
// https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#heading=h.7mkzvhpqb8l5
base::Optional<std::string> ConstructSignedRedemptionRecord(
base::span<const uint8_t> body,
base::span<const uint8_t> signature) {
net::structured_headers::Dictionary dictionary;
// Stunningly, this is the easiest way to add a byte array-typed value to a
// net::structured_headers::Dictionary.
auto make_value_for_dict = [](base::span<const uint8_t> value) {
return net::structured_headers::ParameterizedMember(
net::structured_headers::Item(
std::string(reinterpret_cast<const char*>(value.data()),
value.size()),
net::structured_headers::Item::kByteSequenceType),
net::structured_headers::Parameters{});
};
dictionary[kSignedRedemptionRecordBodyKey] = make_value_for_dict(body);
dictionary[kSignedRedemptionRecordSignatureKey] =
make_value_for_dict(signature);
return net::structured_headers::SerializeDictionary(dictionary);
}
} // namespace
BoringsslTrustTokenRedemptionCryptographer:: BoringsslTrustTokenRedemptionCryptographer::
BoringsslTrustTokenRedemptionCryptographer() = default; BoringsslTrustTokenRedemptionCryptographer() = default;
......
// 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/signed_redemption_record_serialization.h"
#include "components/cbor/reader.h"
#include "components/cbor/values.h"
#include "net/http/structured_headers.h"
namespace network {
namespace {
const char kSignedRedemptionRecordBodyKey[] = "body";
const char kSignedRedemptionRecordSignatureKey[] = "signature";
} // namespace
base::Optional<std::string> ConstructSignedRedemptionRecord(
base::span<const uint8_t> body,
base::span<const uint8_t> signature) {
net::structured_headers::Dictionary dictionary;
// Stunningly, this is the easiest way to add a byte array-typed value to a
// net::structured_headers::Dictionary.
auto make_value_for_dict = [](base::span<const uint8_t> value) {
return net::structured_headers::ParameterizedMember(
net::structured_headers::Item(
std::string(reinterpret_cast<const char*>(value.data()),
value.size()),
net::structured_headers::Item::kByteSequenceType),
net::structured_headers::Parameters{});
};
dictionary[kSignedRedemptionRecordBodyKey] = make_value_for_dict(body);
dictionary[kSignedRedemptionRecordSignatureKey] =
make_value_for_dict(signature);
return net::structured_headers::SerializeDictionary(dictionary);
}
bool ParseTrustTokenSignedRedemptionRecord(base::StringPiece record,
std::string* body_out,
std::string* signature_out) {
base::Optional<net::structured_headers::Dictionary> maybe_dictionary =
net::structured_headers::ParseDictionary(record);
if (!maybe_dictionary)
return false;
if (maybe_dictionary->size() != 2u)
return false;
if (!maybe_dictionary->contains("body") ||
!maybe_dictionary->contains("signature")) {
return false;
}
net::structured_headers::Item& body_item =
maybe_dictionary->at("body").member.front().item;
if (!body_item.is_byte_sequence())
return false;
if (body_out)
*body_out = body_item.GetString(); // GetString gets a byte sequence, too.
net::structured_headers::Item& signature_item =
maybe_dictionary->at("signature").member.front().item;
if (!signature_item.is_byte_sequence())
return false;
if (signature_out)
*signature_out = signature_item.GetString();
return true;
}
} // 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_SIGNED_REDEMPTION_RECORD_SERIALIZATION_H_
#define SERVICES_NETWORK_TRUST_TOKENS_SIGNED_REDEMPTION_RECORD_SERIALIZATION_H_
#include <string>
#include "base/containers/span.h"
#include "base/optional.h"
#include "base/time/time.h"
namespace network {
// The Trust Tokens design doc [1] defines a signed redemption record (SRR) as a
// Structured Headers Draft 15 dictionary with two "byte sequence"-typed fields,
// a body and a signature. This method constructs such a dictionary, given the
// body and signature's contents as bytestrings.
//
// Returns nullopt on internal serialization error in the Structured Headers
// library.
//
// [1]
// https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#heading=h.7mkzvhpqb8l5
base::Optional<std::string> ConstructSignedRedemptionRecord(
base::span<const uint8_t> body,
base::span<const uint8_t> signature);
// Parses a Trust Tokens Signed Redemption Record (SRR), a Structured Headers
// Draft 15 dictionary, into its constituent "body" and "signature" elements,
// placing them in the output parameters.
//
// Each output argument may be nullptr, denoting a lack of interest in the
// corresponding field. (The entire record might still be parsed.)
//
// Returns true on parse success and false on parse error.
bool ParseTrustTokenSignedRedemptionRecord(
base::StringPiece record,
std::string* body_out = nullptr,
std::string* signature_out = nullptr);
} // namespace network
#endif // SERVICES_NETWORK_TRUST_TOKENS_SIGNED_REDEMPTION_RECORD_SERIALIZATION_H_
// 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/signed_redemption_record_serialization.h"
#include <string>
#include "base/containers/span.h"
#include "base/strings/string_piece.h"
#include "base/traits_bag.h"
#include "net/http/structured_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
enum class WithBody { kValid, kInvalid, kAbsent };
enum class WithSignature { kValid, kInvalid, kAbsent };
enum class WithAdditionalMember { kYes, kNo };
// Returns a minimal signed redemption record-like Structured Headers dictionary
// to help with error handling in the SRR deserialization code.
//
// |with_body| specifies whether to include a valid "body" field, a type-unsafe
// one, or none at all; |with_signature| does the same for the "signature"
// field.
//
// If |with_additional_member| is kYes, the dictionary will contain an
// additional member beyond whichever of the body and signature are included.
std::string CreateSerializedDictionary(
WithBody with_body,
WithSignature with_signature,
WithAdditionalMember with_additional_member) {
net::structured_headers::Dictionary dict;
switch (with_signature) {
case WithSignature::kValid:
dict["signature"] = net::structured_headers::ParameterizedMember(
net::structured_headers::Item(
"example signature",
net::structured_headers::Item::kByteSequenceType),
{});
break;
case WithSignature::kInvalid:
// This isn't a byte sequence, so it's not a valid value corresponding to
// the "signature" key.
dict["signature"] = net::structured_headers::ParameterizedMember(
net::structured_headers::Item(int64_t{5}), {});
break;
case WithSignature::kAbsent:
break;
}
switch (with_body) {
case WithBody::kValid:
dict["body"] = net::structured_headers::ParameterizedMember(
net::structured_headers::Item(
"example body", net::structured_headers::Item::kByteSequenceType),
{});
break;
case WithBody::kInvalid:
// This isn't a byte sequence, so it's not a valid value corresponding to
// the "body" key.
dict["body"] = net::structured_headers::ParameterizedMember(
net::structured_headers::Item(int64_t{5}), {});
break;
case WithBody::kAbsent:
break;
}
if (with_additional_member == WithAdditionalMember::kYes) {
dict["additional"] = net::structured_headers::ParameterizedMember(
net::structured_headers::Item(int64_t{5}), {});
}
return *net::structured_headers::SerializeDictionary(dict);
}
} // namespace
TEST(SignedRedemptionRecordSerialization, SerializeAndParse) {
std::string body = "body";
std::string signature = "example signature";
base::Optional<std::string> maybe_serialized =
ConstructSignedRedemptionRecord(
base::as_bytes(base::make_span(body)),
base::as_bytes(base::make_span(signature)));
ASSERT_TRUE(maybe_serialized);
std::string obtained_body;
std::string obtained_signature;
EXPECT_TRUE(ParseTrustTokenSignedRedemptionRecord(
*maybe_serialized, &obtained_body, &obtained_signature));
EXPECT_EQ(obtained_body, body);
EXPECT_EQ(obtained_signature, signature);
}
TEST(SignedRedemptionRecordSerialization, SerializeAndParseNullptrParams) {
// Make sure ParseTrustTokenSignedRedemptionRecord doesn't blow up (i.e.,
// dereference a null pointer) when its optional params aren't provided.
std::string body = "example body";
std::string signature = "example signature";
base::Optional<std::string> maybe_serialized =
ConstructSignedRedemptionRecord(
base::as_bytes(base::make_span(body)),
base::as_bytes(base::make_span(signature)));
ASSERT_TRUE(maybe_serialized);
EXPECT_TRUE(ParseTrustTokenSignedRedemptionRecord(*maybe_serialized, nullptr,
nullptr));
}
TEST(SignedRedemptionRecordSerialization, ParseNotDictionary) {
// Parse should reject objects that aren't Structured Headers dictionaries.
EXPECT_FALSE(ParseTrustTokenSignedRedemptionRecord(
"Not a Structured Headers dictionary", nullptr, nullptr));
}
TEST(SignedRedemptionRecordSerialization, ParseTooSmallDictionary) {
// Parse should reject Structured Headers dictionaries that aren't size 2.
EXPECT_FALSE(ParseTrustTokenSignedRedemptionRecord(
CreateSerializedDictionary(WithBody::kAbsent, WithSignature::kAbsent,
WithAdditionalMember::kNo),
nullptr, nullptr));
}
TEST(SignedRedemptionRecordSerialization,
ParseDictionaryWithTypeUnsafeSignature) {
// Parse should reject size 2 structured headers dictionaries with members of
// the wrong type.
EXPECT_FALSE(ParseTrustTokenSignedRedemptionRecord(
CreateSerializedDictionary(WithBody::kValid, WithSignature::kInvalid,
WithAdditionalMember::kNo),
nullptr, nullptr));
}
TEST(SignedRedemptionRecordSerialization, ParseDictionaryWithTypeUnsafeBody) {
// Parse should reject size 2 structured headers dictionaries with members of
// the wrong type.
EXPECT_FALSE(ParseTrustTokenSignedRedemptionRecord(
CreateSerializedDictionary(WithBody::kInvalid, WithSignature::kValid,
WithAdditionalMember::kNo),
nullptr, nullptr));
}
TEST(SignedRedemptionRecordSerialization, ParseDictionaryWithExtraMembers) {
// Parse should reject size >2 structured headers dictionaries.
EXPECT_FALSE(ParseTrustTokenSignedRedemptionRecord(
CreateSerializedDictionary(WithBody::kValid, WithSignature::kValid,
WithAdditionalMember::kYes),
nullptr, nullptr));
}
} // namespace network
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "net/http/http_request_headers.h" #include "net/http/http_request_headers.h"
#include "net/http/structured_headers.h" #include "net/http/structured_headers.h"
#include "services/network/trust_tokens/ed25519_trust_token_request_signer.h" #include "services/network/trust_tokens/ed25519_trust_token_request_signer.h"
#include "services/network/trust_tokens/signed_redemption_record_serialization.h"
#include "services/network/trust_tokens/trust_token_http_headers.h" #include "services/network/trust_tokens/trust_token_http_headers.h"
#include "services/network/trust_tokens/trust_token_parameterization.h" #include "services/network/trust_tokens/trust_token_parameterization.h"
#include "services/network/trust_tokens/trust_token_request_canonicalizer.h" #include "services/network/trust_tokens/trust_token_request_canonicalizer.h"
...@@ -54,32 +55,9 @@ SrrVerificationStatus VerifyTrustTokenSignedRedemptionRecord( ...@@ -54,32 +55,9 @@ SrrVerificationStatus VerifyTrustTokenSignedRedemptionRecord(
base::StringPiece record, base::StringPiece record,
base::StringPiece verification_key, base::StringPiece verification_key,
std::string* srr_body_out) { std::string* srr_body_out) {
base::Optional<net::structured_headers::Dictionary> maybe_dictionary = std::string body, signature;
net::structured_headers::ParseDictionary(record); if (!ParseTrustTokenSignedRedemptionRecord(record, &body, &signature))
if (!maybe_dictionary)
return SrrVerificationStatus::kParseError;
if (maybe_dictionary->size() != 2u)
return SrrVerificationStatus::kParseError;
if (!maybe_dictionary->contains("body") ||
!maybe_dictionary->contains("signature")) {
return SrrVerificationStatus::kParseError;
}
net::structured_headers::Item& body_item =
maybe_dictionary->at("body").member.front().item;
if (!body_item.is_byte_sequence())
return SrrVerificationStatus::kParseError;
std::string body =
body_item.GetString(); // GetString gets a byte sequence, too.
net::structured_headers::Item& signature_item =
maybe_dictionary->at("signature").member.front().item;
if (!signature_item.is_byte_sequence())
return SrrVerificationStatus::kParseError; return SrrVerificationStatus::kParseError;
std::string signature =
signature_item.GetString(); // GetString gets a byte sequence, too.
if (verification_key.size() != ED25519_PUBLIC_KEY_LEN) if (verification_key.size() != ED25519_PUBLIC_KEY_LEN)
return SrrVerificationStatus::kSignatureVerificationError; return SrrVerificationStatus::kSignatureVerificationError;
......
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