1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/webid/jwt_signer.h"
#include "base/functional/callback.h"
#include "content/browser/webid/sd_jwt.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "crypto/ec_private_key.h"
#include "crypto/sha2.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content::sdjwt {
namespace {
std::vector<std::uint8_t> Sha256(std::string_view data) {
std::string str = crypto::SHA256HashString(data);
std::vector<uint8_t> result(str.begin(), str.end());
return result;
}
} // namespace
class JwtSignerBrowserTest : public ContentBrowserTest {
protected:
JwtSignerBrowserTest() = default;
~JwtSignerBrowserTest() override = default;
};
std::pair<SdJwtKb, Jwk> CreateTestSdJwtKb(const std::string aud,
const std::string nonce,
int iat) {
auto holder_private_key = crypto::ECPrivateKey::Create();
auto jwk = ExportPublicKey(*holder_private_key);
auto issuer_private_key = crypto::ECPrivateKey::Create();
Header header;
header.typ = "jwt";
header.alg = "ES256";
Payload payload;
payload.iss = "https://issuer.example";
Disclosure name;
name.salt = Disclosure::CreateSalt();
name.name = "name";
name.value = "Sam";
payload._sd = {*name.Digest(base::BindRepeating(Sha256))};
ConfirmationKey confirmation;
confirmation.jwk = *jwk;
payload.cnf = confirmation;
Jwt issued;
issued.header = *header.ToJson();
issued.payload = *payload.ToJson();
auto issuer_jwk = ExportPublicKey(*issuer_private_key);
issued.Sign(CreateJwtSigner(std::move(issuer_private_key)));
auto disclosures = SdJwt::Disclose({{name.name, *name.ToJson()}}, {"name"});
EXPECT_TRUE(disclosures);
SdJwt presentation;
presentation.jwt = issued;
presentation.disclosures = *disclosures;
std::optional<SdJwtKb> sd_jwt_kb =
SdJwtKb::Create(presentation, aud, nonce, base::Time::FromTimeT(iat),
base::BindRepeating(Sha256),
CreateJwtSigner(std::move(holder_private_key)));
return std::make_pair(*sd_jwt_kb, *issuer_jwk);
}
IN_PROC_BROWSER_TEST_F(JwtSignerBrowserTest, VerifyWithWebCrypto) {
EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(".", "fedcm/sd_jwt.html")));
// Generate a test SD-JWT+KB presentation.
auto token =
CreateTestSdJwtKb("https://verifier.example", "__fake_nonce__", 1234);
const std::string sdjwtkb = token.first.Serialize();
const std::string key = *token.second.Serialize();
// Load the token into a string
ASSERT_TRUE(ExecJs(shell(), "var token = '" + sdjwtkb + "';"));
// Load the key into an object
ASSERT_TRUE(ExecJs(shell(), "var key = " + key + ";"));
std::string verify = R"(
main(
token,
key,
'https://verifier.example',
'__fake_nonce__'
)
)";
// Verify the SD-JWT+KB.
EXPECT_THAT(EvalJs(shell(), verify).ExtractList(),
testing::UnorderedElementsAre("Sam"));
}
} // namespace content::sdjwt
|