PageRenderTime 27ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/services/network/trust_tokens/test/trust_token_request_handler.cc

http://github.com/chromium/chromium
C++ | 398 lines | 287 code | 68 blank | 43 comment | 36 complexity | 1c1f00fbc47969db1a222b08f458a086 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.0, BSD-2-Clause, LGPL-2.1, MPL-2.0, 0BSD, EPL-1.0, MPL-2.0-no-copyleft-exception, GPL-2.0, BitTorrent-1.0, CPL-1.0, LGPL-3.0, Unlicense, BSD-3-Clause, CC0-1.0, JSON, MIT, GPL-3.0, CC-BY-SA-3.0, AGPL-1.0
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "services/network/trust_tokens/test/trust_token_request_handler.h"
  5. #include "base/base64.h"
  6. #include "base/check.h"
  7. #include "base/containers/span.h"
  8. #include "base/json/json_string_value_serializer.h"
  9. #include "base/strings/string_number_conversions.h"
  10. #include "base/strings/string_piece.h"
  11. #include "base/strings/string_split.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/test/bind_test_util.h"
  14. #include "components/cbor/reader.h"
  15. #include "components/cbor/values.h"
  16. #include "crypto/sha2.h"
  17. #include "net/http/http_request_headers.h"
  18. #include "net/http/structured_headers.h"
  19. #include "services/network/trust_tokens/ed25519_trust_token_request_signer.h"
  20. #include "services/network/trust_tokens/scoped_boringssl_bytes.h"
  21. #include "services/network/trust_tokens/test/signed_request_verification_util.h"
  22. #include "services/network/trust_tokens/trust_token_http_headers.h"
  23. #include "services/network/trust_tokens/trust_token_request_canonicalizer.h"
  24. #include "services/network/trust_tokens/trust_token_request_signing_helper.h"
  25. #include "third_party/boringssl/src/include/openssl/curve25519.h"
  26. #include "third_party/boringssl/src/include/openssl/evp.h"
  27. #include "third_party/boringssl/src/include/openssl/trust_token.h"
  28. namespace network {
  29. namespace test {
  30. namespace {
  31. struct IssuanceKeyPair {
  32. // Token signing and verification keys:
  33. std::vector<uint8_t> signing;
  34. std::vector<uint8_t> verification;
  35. // Default to a very long expiry time, but allow this to be overridden when
  36. // specific tests want to do so.
  37. base::Time expiry = base::Time::Max();
  38. };
  39. IssuanceKeyPair GenerateIssuanceKeyPair(int id) {
  40. IssuanceKeyPair keys;
  41. keys.signing.resize(TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE);
  42. keys.verification.resize(TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE);
  43. size_t signing_key_len, verification_key_len;
  44. CHECK(TRUST_TOKEN_generate_key(
  45. TRUST_TOKEN_experiment_v0(), keys.signing.data(), &signing_key_len,
  46. keys.signing.size(), keys.verification.data(), &verification_key_len,
  47. keys.verification.size(), id));
  48. keys.signing.resize(signing_key_len);
  49. keys.verification.resize(verification_key_len);
  50. return keys;
  51. }
  52. // This convenience helper prevents forgetting whether the inequality is weak or
  53. // strict.
  54. bool HasKeyPairExpired(const IssuanceKeyPair& p) {
  55. return p.expiry <= base::Time::Now();
  56. }
  57. } // namespace
  58. struct TrustTokenRequestHandler::Rep {
  59. // Issue at most this many tokens per issuance.
  60. int batch_size;
  61. // Signed redemption record (SRR) signing and verification keys:
  62. std::vector<uint8_t> srr_signing;
  63. std::vector<uint8_t> srr_verification;
  64. std::vector<IssuanceKeyPair> issuance_keys;
  65. // Creates a BoringSSL token issuer context suitable for issuance or
  66. // redemption, using only the unexpired key pairs from |issuance_keys|.
  67. bssl::UniquePtr<TRUST_TOKEN_ISSUER> CreateIssuerContextFromUnexpiredKeys()
  68. const;
  69. // Verifies the redemption request's client datal is a valid CBOR
  70. // encoding of a structure matching the format specified in the design doc.
  71. //
  72. // If this is the case, returns true and stores the contained
  73. // browser-generated public key hash in |hashes_of_redemption_bound_key_pairs|
  74. // for comparison against subsequent signed requests. Otherwise, returns false
  75. // and, if |error| is not null, sets |error| to a human-readable explanation
  76. // of why the input was not valid.
  77. bool ConfirmClientDataIntegrityAndStoreKeyHash(
  78. base::span<const uint8_t> client_data,
  79. std::string* error = nullptr);
  80. // Maintains all key pairs bound to successful redemptions.
  81. // TODO(davidvc): This can be expanded to map per top-frame origin for
  82. // tests across multiple origins.
  83. // TODO(davidvc): We can also expand the verification logic here to confirm
  84. // the private metadata field decodes appropriately.
  85. std::set<std::string> hashes_of_redemption_bound_key_pairs;
  86. // Contains a human-readable string explaining why the most recent signed
  87. // request verification to fail failed, or nullopt if no verification has
  88. // failed.
  89. base::Optional<std::string> last_verification_error;
  90. };
  91. bssl::UniquePtr<TRUST_TOKEN_ISSUER>
  92. TrustTokenRequestHandler::Rep::CreateIssuerContextFromUnexpiredKeys() const {
  93. bssl::UniquePtr<TRUST_TOKEN_ISSUER> ret(
  94. TRUST_TOKEN_ISSUER_new(TRUST_TOKEN_experiment_v0(), batch_size));
  95. if (!ret)
  96. return nullptr;
  97. for (const IssuanceKeyPair& key_pair : issuance_keys) {
  98. if (HasKeyPairExpired(key_pair))
  99. continue;
  100. if (!TRUST_TOKEN_ISSUER_add_key(ret.get(), key_pair.signing.data(),
  101. key_pair.signing.size())) {
  102. return nullptr;
  103. }
  104. }
  105. // Copying the comment from evp.h:
  106. // The [Ed25519] RFC 8032 private key format is the 32-byte prefix of
  107. // |ED25519_sign|'s 64-byte private key.
  108. bssl::UniquePtr<EVP_PKEY> issuer_srr_key(EVP_PKEY_new_raw_private_key(
  109. EVP_PKEY_ED25519, /*unused=*/nullptr, srr_signing.data(),
  110. /*len=*/32));
  111. if (!issuer_srr_key)
  112. return nullptr;
  113. if (!TRUST_TOKEN_ISSUER_set_srr_key(ret.get(), issuer_srr_key.get()))
  114. return nullptr;
  115. return ret;
  116. }
  117. bool TrustTokenRequestHandler::Rep::ConfirmClientDataIntegrityAndStoreKeyHash(
  118. base::span<const uint8_t> client_data,
  119. std::string* error) {
  120. std::string dummy_error;
  121. if (!error)
  122. error = &dummy_error;
  123. base::Optional<cbor::Value> maybe_value = cbor::Reader::Read(client_data);
  124. if (!maybe_value) {
  125. *error = "client data was invalid CBOR";
  126. return false;
  127. }
  128. if (!maybe_value->is_map()) {
  129. *error = "client data was valid CBOR but not a map";
  130. return false;
  131. }
  132. const cbor::Value::MapValue& map = maybe_value->GetMap();
  133. if (map.size() != 3u) {
  134. *error = "Unexpected number of fields in client data";
  135. return false;
  136. }
  137. auto it = map.find(cbor::Value("key-hash", cbor::Value::Type::STRING));
  138. if (it == map.end()) {
  139. *error = "client data was missing a 'key-hash' field";
  140. return false;
  141. }
  142. if (!it->second.is_bytestring()) {
  143. *error = "client data 'key-hash' field was not a bytestring";
  144. return false;
  145. }
  146. base::StringPiece key_hash = it->second.GetBytestringAsString();
  147. // Even though we don't yet examine the remaining fields in detail, perform
  148. // some structural integrity checks to make sure all's generally well:
  149. cbor::Value redeeming_origin_key("redeeming-origin",
  150. cbor::Value::Type::STRING);
  151. if (!map.contains(redeeming_origin_key) ||
  152. !map.at(redeeming_origin_key).is_string()) {
  153. *error = "Missing or type-unsafe redeeming-origin field in client data";
  154. return false;
  155. }
  156. cbor::Value redemption_timestamp_key("redemption-timestamp",
  157. cbor::Value::Type::STRING);
  158. if (!map.contains(redemption_timestamp_key) ||
  159. !map.at(redemption_timestamp_key).is_unsigned()) {
  160. *error = "Missing or type-unsafe redemption-timestamp field in client data";
  161. return false;
  162. }
  163. hashes_of_redemption_bound_key_pairs.insert(std::string(key_hash));
  164. return true;
  165. }
  166. TrustTokenRequestHandler::TrustTokenRequestHandler(int num_keys, int batch_size)
  167. : rep_(std::make_unique<Rep>()) {
  168. rep_->batch_size = batch_size;
  169. rep_->srr_signing.resize(ED25519_PRIVATE_KEY_LEN);
  170. rep_->srr_verification.resize(ED25519_PUBLIC_KEY_LEN);
  171. ED25519_keypair(rep_->srr_verification.data(), rep_->srr_signing.data());
  172. for (int i = 0; i < num_keys; ++i) {
  173. rep_->issuance_keys.push_back(GenerateIssuanceKeyPair(i));
  174. }
  175. }
  176. TrustTokenRequestHandler::~TrustTokenRequestHandler() = default;
  177. std::string TrustTokenRequestHandler::GetKeyCommitmentRecord() const {
  178. base::AutoLock lock(mutex_);
  179. std::string ret;
  180. JSONStringValueSerializer serializer(&ret);
  181. base::Value value(base::Value::Type::DICTIONARY);
  182. value.SetStringKey(
  183. "srrkey", base::Base64Encode(base::make_span(rep_->srr_verification)));
  184. value.SetIntKey("batchsize", rep_->batch_size);
  185. for (size_t i = 0; i < rep_->issuance_keys.size(); ++i) {
  186. value.SetStringPath(base::NumberToString(i) + ".Y",
  187. base::Base64Encode(base::make_span(
  188. rep_->issuance_keys[i].verification)));
  189. value.SetStringPath(base::NumberToString(i) + ".expiry",
  190. base::NumberToString((rep_->issuance_keys[i].expiry -
  191. base::Time::UnixEpoch())
  192. .InMicroseconds()));
  193. }
  194. // It's OK to be a bit crashy in exceptional failure cases because it
  195. // indicates a serious coding error in this test-only code; we'd like to find
  196. // this out sooner rather than later.
  197. CHECK(serializer.Serialize(value));
  198. return ret;
  199. }
  200. base::Optional<std::string> TrustTokenRequestHandler::Issue(
  201. base::StringPiece issuance_request) {
  202. base::AutoLock lock(mutex_);
  203. bssl::UniquePtr<TRUST_TOKEN_ISSUER> issuer_ctx =
  204. rep_->CreateIssuerContextFromUnexpiredKeys();
  205. std::string decoded_issuance_request;
  206. if (!base::Base64Decode(issuance_request, &decoded_issuance_request))
  207. return base::nullopt;
  208. // TODO(davidvc): Perhaps make this configurable? Not a high priority, though.
  209. constexpr uint8_t kPrivateMetadata = 0;
  210. ScopedBoringsslBytes decoded_issuance_response;
  211. size_t num_tokens_issued = 0;
  212. bool ok = false;
  213. for (size_t i = 0; i < rep_->issuance_keys.size(); ++i) {
  214. if (HasKeyPairExpired(rep_->issuance_keys[i]))
  215. continue;
  216. if (TRUST_TOKEN_ISSUER_issue(
  217. issuer_ctx.get(), decoded_issuance_response.mutable_ptr(),
  218. decoded_issuance_response.mutable_len(), &num_tokens_issued,
  219. base::as_bytes(base::make_span(decoded_issuance_request)).data(),
  220. decoded_issuance_request.size(),
  221. /*public_metadata=*/static_cast<uint32_t>(i), kPrivateMetadata,
  222. rep_->batch_size)) {
  223. ok = true;
  224. break;
  225. }
  226. }
  227. if (!ok)
  228. return base::nullopt;
  229. return base::Base64Encode(decoded_issuance_response.as_span());
  230. }
  231. constexpr base::TimeDelta TrustTokenRequestHandler::kSrrLifetime =
  232. base::TimeDelta::FromDays(100);
  233. base::Optional<std::string> TrustTokenRequestHandler::Redeem(
  234. base::StringPiece redemption_request) {
  235. base::AutoLock lock(mutex_);
  236. bssl::UniquePtr<TRUST_TOKEN_ISSUER> issuer_ctx =
  237. rep_->CreateIssuerContextFromUnexpiredKeys();
  238. std::string decoded_redemption_request;
  239. if (!base::Base64Decode(redemption_request, &decoded_redemption_request))
  240. return base::nullopt;
  241. ScopedBoringsslBytes decoded_redemption_response;
  242. TRUST_TOKEN* redeemed_token;
  243. ScopedBoringsslBytes redeemed_client_data;
  244. uint64_t received_redemption_timestamp;
  245. if (!TRUST_TOKEN_ISSUER_redeem(
  246. issuer_ctx.get(), decoded_redemption_response.mutable_ptr(),
  247. decoded_redemption_response.mutable_len(), &redeemed_token,
  248. redeemed_client_data.mutable_ptr(),
  249. redeemed_client_data.mutable_len(), &received_redemption_timestamp,
  250. base::as_bytes(base::make_span(decoded_redemption_request)).data(),
  251. decoded_redemption_request.size(), kSrrLifetime.InSeconds())) {
  252. return base::nullopt;
  253. }
  254. rep_->ConfirmClientDataIntegrityAndStoreKeyHash(
  255. redeemed_client_data.as_span());
  256. // Put the issuer-receied token in a smart pointer so it will get deleted on
  257. // leaving scope.
  258. bssl::UniquePtr<TRUST_TOKEN> redeemed_token_scoper(redeemed_token);
  259. return base::Base64Encode(decoded_redemption_response.as_span());
  260. }
  261. bool TrustTokenRequestHandler::VerifySignedRequest(
  262. const GURL& destination,
  263. const net::HttpRequestHeaders& headers,
  264. std::string* error_out) {
  265. std::string dummy_error;
  266. if (!error_out)
  267. error_out = &dummy_error;
  268. // In order to avoid deadlock, this must be before VerifySignedRequest's
  269. // |lock|'s definition. This is so that |set_last_error_on_return|'s
  270. // destructor (and associated callback) are run after the function-scoped
  271. // AutoLock is destroyed (and releases the mutex).
  272. base::ScopedClosureRunner set_last_error_on_return(
  273. base::BindLambdaForTesting([error_out, this]() {
  274. base::AutoLock lock(mutex_);
  275. if (!error_out->empty())
  276. rep_->last_verification_error = *error_out;
  277. }));
  278. base::AutoLock lock(mutex_);
  279. std::string verification_key;
  280. if (!ReconstructSigningDataAndVerifySignature(destination, headers,
  281. /*verifier=*/{}, error_out,
  282. &verification_key)) {
  283. return false;
  284. }
  285. if (!base::Contains(rep_->hashes_of_redemption_bound_key_pairs,
  286. crypto::SHA256HashString(verification_key))) {
  287. if (error_out) {
  288. *error_out =
  289. "Got a request signed with a verification key whose hash was not "
  290. "previously bound to a redemption request.";
  291. }
  292. return false;
  293. }
  294. std::string sec_signed_redemption_record_header;
  295. if (!headers.GetHeader(kTrustTokensRequestHeaderSecSignedRedemptionRecord,
  296. &sec_signed_redemption_record_header)) {
  297. if (error_out)
  298. *error_out = "Request missing its SRR header";
  299. return false;
  300. }
  301. std::string srr_body;
  302. switch (VerifyTrustTokenSignedRedemptionRecord(
  303. sec_signed_redemption_record_header,
  304. base::StringPiece(
  305. reinterpret_cast<const char*>(rep_->srr_verification.data()),
  306. rep_->srr_verification.size()),
  307. &srr_body)) {
  308. case SrrVerificationStatus::kSignatureVerificationError:
  309. if (error_out) {
  310. *error_out = "Request SRR signature failed to verify";
  311. }
  312. return false;
  313. case SrrVerificationStatus::kParseError:
  314. if (error_out) {
  315. *error_out = "Request SRR header failed to parse";
  316. }
  317. return false;
  318. case SrrVerificationStatus::kSuccess:
  319. break;
  320. }
  321. if (!ConfirmSrrBodyIntegrity(srr_body, error_out))
  322. return false; // On failure, |ConfirmSrrBodyIntegrity| has set the error.
  323. return true;
  324. }
  325. base::Optional<std::string> TrustTokenRequestHandler::LastVerificationError() {
  326. base::AutoLock lock(mutex_);
  327. return rep_->last_verification_error;
  328. }
  329. } // namespace test
  330. } // namespace network