• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/device_bound_sessions/session_binding_utils.h"
6 
7 #include <optional>
8 #include <string_view>
9 
10 #include "base/base64url.h"
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/strings/strcat.h"
14 #include "base/strings/string_split.h"
15 #include "base/time/time.h"
16 #include "base/value_iterators.h"
17 #include "base/values.h"
18 #include "crypto/signature_verifier.h"
19 #include "net/device_bound_sessions/test_support.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "url/gurl.h"
22 
23 namespace net::device_bound_sessions {
24 
25 namespace {
26 
Base64UrlEncodedJsonToValue(std::string_view input)27 base::Value Base64UrlEncodedJsonToValue(std::string_view input) {
28   std::string json;
29   EXPECT_TRUE(base::Base64UrlDecode(
30       input, base::Base64UrlDecodePolicy::DISALLOW_PADDING, &json));
31   std::optional<base::Value> result = base::JSONReader::Read(json);
32   EXPECT_TRUE(result.has_value());
33   return std::move(*result);
34 }
35 
36 }  // namespace
37 
TEST(SessionBindingUtilsTest,CreateKeyRegistrationHeaderAndPayload)38 TEST(SessionBindingUtilsTest, CreateKeyRegistrationHeaderAndPayload) {
39   auto [spki, jwk] = GetRS256SpkiAndJwkForTesting();
40 
41   std::optional<std::string> result = CreateKeyRegistrationHeaderAndPayload(
42       "test_challenge", GURL("https://accounts.example.test/RegisterKey"),
43       crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256, spki,
44       base::Time::UnixEpoch() + base::Days(200) + base::Milliseconds(123), "");
45   ASSERT_TRUE(result.has_value());
46 
47   std::vector<std::string_view> header_and_payload = base::SplitStringPiece(
48       *result, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
49   ASSERT_EQ(header_and_payload.size(), 2U);
50   base::Value actual_header =
51       Base64UrlEncodedJsonToValue(header_and_payload[0]);
52   base::Value actual_payload =
53       Base64UrlEncodedJsonToValue(header_and_payload[1]);
54 
55   base::Value::Dict expected_header =
56       base::Value::Dict().Set("alg", "RS256").Set("typ", "jwt");
57   base::Value::Dict expected_payload =
58       base::Value::Dict()
59           .Set("aud", "https://accounts.example.test/RegisterKey")
60           .Set("jti", "test_challenge")
61           .Set("iat", 17280000)
62           .Set("key", base::JSONReader::Read(jwk).value())
63           .Set("authorization", "");
64 
65   EXPECT_EQ(actual_header, expected_header);
66   EXPECT_EQ(actual_payload, expected_payload);
67 }
68 
TEST(SessionBindingUtilsTest,CreateKeyRegistrationHeaderAndPayloadWithNullAuth)69 TEST(SessionBindingUtilsTest,
70      CreateKeyRegistrationHeaderAndPayloadWithNullAuth) {
71   auto [spki, jwk] = GetRS256SpkiAndJwkForTesting();
72 
73   std::optional<std::string> result = CreateKeyRegistrationHeaderAndPayload(
74       "test_challenge", GURL("https://accounts.example.test/RegisterKey"),
75       crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256, spki,
76       base::Time::UnixEpoch() + base::Days(200) + base::Milliseconds(123),
77       /*authorization=*/std::nullopt);
78   ASSERT_TRUE(result.has_value());
79 
80   std::vector<std::string_view> header_and_payload = base::SplitStringPiece(
81       *result, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
82   ASSERT_EQ(header_and_payload.size(), 2U);
83   base::Value actual_header =
84       Base64UrlEncodedJsonToValue(header_and_payload[0]);
85   base::Value actual_payload =
86       Base64UrlEncodedJsonToValue(header_and_payload[1]);
87 
88   base::Value::Dict expected_header =
89       base::Value::Dict().Set("alg", "RS256").Set("typ", "jwt");
90   base::Value::Dict expected_payload =
91       base::Value::Dict()
92           .Set("aud", "https://accounts.example.test/RegisterKey")
93           .Set("jti", "test_challenge")
94           .Set("iat", 17280000)
95           .Set("key", base::JSONReader::Read(jwk).value());
96 
97   EXPECT_EQ(actual_header, expected_header);
98   EXPECT_EQ(actual_payload, expected_payload);
99 }
100 
TEST(SessionBindingUtilsTest,AppendSignatureToHeaderAndPayload)101 TEST(SessionBindingUtilsTest, AppendSignatureToHeaderAndPayload) {
102   std::optional<std::string> result = AppendSignatureToHeaderAndPayload(
103       "abc.efg",
104       crypto::SignatureVerifier::SignatureAlgorithm::RSA_PKCS1_SHA256,
105       std::vector<uint8_t>({1, 2, 3}));
106   EXPECT_EQ(result, "abc.efg.AQID");
107 }
108 
TEST(SessionBindingUtilsTest,AppendSignatureToHeaderAndPayloadValidECDSASignature)109 TEST(SessionBindingUtilsTest,
110      AppendSignatureToHeaderAndPayloadValidECDSASignature) {
111   const std::vector<uint8_t> kDerSignature = {
112       0x30, 0x45, 0x02, 0x20, 0x74, 0xa0, 0x6f, 0x6b, 0x2b, 0x0e, 0x82, 0x0e,
113       0x03, 0x3b, 0x6e, 0x98, 0xfc, 0x89, 0x9c, 0xf3, 0x30, 0xb5, 0x56, 0xd3,
114       0x29, 0x89, 0xb5, 0x82, 0x33, 0x5f, 0x9d, 0x97, 0xfb, 0x65, 0x64, 0x90,
115       0x02, 0x21, 0x00, 0xbc, 0xb5, 0xee, 0x42, 0xe2, 0x5a, 0x87, 0xae, 0x21,
116       0x18, 0xda, 0x7e, 0x68, 0x65, 0x30, 0xbe, 0xe5, 0x69, 0x3d, 0xc5, 0x5f,
117       0xd5, 0x62, 0x45, 0x3e, 0x8d, 0x0b, 0x05, 0x1a, 0x33, 0x79, 0x8d};
118   constexpr std::string_view kRawSignatureBase64UrlEncoded =
119       "dKBvaysOgg4DO26Y_Imc8zC1VtMpibWCM1-dl_tlZJC8te5C4lqHriEY2n5oZTC-5Wk9xV_"
120       "VYkU-jQsFGjN5jQ";
121 
122   std::optional<std::string> result = AppendSignatureToHeaderAndPayload(
123       "abc.efg", crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256,
124       kDerSignature);
125   EXPECT_EQ(result, base::StrCat({"abc.efg.", kRawSignatureBase64UrlEncoded}));
126 }
127 
TEST(SessionBindingUtilsTest,AppendSignatureToHeaderAndPayloadInvalidECDSASignature)128 TEST(SessionBindingUtilsTest,
129      AppendSignatureToHeaderAndPayloadInvalidECDSASignature) {
130   std::optional<std::string> result = AppendSignatureToHeaderAndPayload(
131       "abc.efg", crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256,
132       std::vector<uint8_t>({1, 2, 3}));
133   EXPECT_EQ(result, std::nullopt);
134 }
135 
136 }  // namespace net::device_bound_sessions
137