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 "crypto/user_verifying_key.h" 6 7#include <iterator> 8#include <memory> 9 10#include <LocalAuthentication/LocalAuthentication.h> 11 12#include "base/functional/bind.h" 13#include "base/run_loop.h" 14#include "base/task/single_thread_task_runner.h" 15#include "base/test/bind.h" 16#include "base/test/task_environment.h" 17#include "crypto/fake_apple_keychain_v2.h" 18#include "crypto/scoped_fake_apple_keychain_v2.h" 19#include "crypto/scoped_lacontext.h" 20#include "testing/gtest/include/gtest/gtest.h" 21 22namespace crypto { 23 24namespace { 25 26constexpr char kTestKeychainAccessGroup[] = "test-keychain-access-group"; 27constexpr SignatureVerifier::SignatureAlgorithm kAcceptableAlgos[] = { 28 SignatureVerifier::ECDSA_SHA256}; 29 30UserVerifyingKeyProvider::Config MakeConfig() { 31 UserVerifyingKeyProvider::Config config; 32 config.keychain_access_group = kTestKeychainAccessGroup; 33 return config; 34} 35 36class UserVerifyingKeyMacTest : public testing::Test { 37 public: 38 std::unique_ptr<UserVerifyingSigningKey> GenerateUserVerifyingSigningKey() { 39 std::unique_ptr<UserVerifyingSigningKey> key; 40 base::RunLoop run_loop; 41 provider_->GenerateUserVerifyingSigningKey( 42 kAcceptableAlgos, 43 base::BindLambdaForTesting( 44 [&](base::expected<std::unique_ptr<UserVerifyingSigningKey>, 45 UserVerifyingKeyCreationError> result) { 46 if (result.has_value()) { 47 key = std::move(result.value()); 48 } 49 run_loop.Quit(); 50 })); 51 run_loop.Run(); 52 return key; 53 } 54 55 std::unique_ptr<UserVerifyingSigningKey> GetUserVerifyingSigningKey( 56 std::string key_label) { 57 std::unique_ptr<UserVerifyingSigningKey> key; 58 base::RunLoop run_loop; 59 provider_->GetUserVerifyingSigningKey( 60 key_label, 61 base::BindLambdaForTesting( 62 [&](base::expected<std::unique_ptr<UserVerifyingSigningKey>, 63 UserVerifyingKeyCreationError> result) { 64 if (result.has_value()) { 65 key = std::move(result.value()); 66 } 67 run_loop.Quit(); 68 })); 69 run_loop.Run(); 70 return key; 71 } 72 73 bool DeleteUserVerifyingKey(std::string key_label) { 74 std::optional<bool> deleted; 75 base::RunLoop run_loop; 76 provider_->DeleteUserVerifyingKey( 77 key_label, base::BindLambdaForTesting([&](bool result) { 78 deleted = result; 79 run_loop.Quit(); 80 })); 81 run_loop.Run(); 82 return *deleted; 83 } 84 85 std::optional<std::vector<uint8_t>> Sign(UserVerifyingSigningKey* key, 86 base::span<const uint8_t> message) { 87 std::optional<std::vector<uint8_t>> signature; 88 base::RunLoop run_loop; 89 key->Sign(message, 90 base::BindLambdaForTesting( 91 [&](base::expected<std::vector<uint8_t>, 92 UserVerifyingKeySigningError> result) { 93 if (result.has_value()) { 94 signature = std::move(result.value()); 95 } 96 run_loop.Quit(); 97 })); 98 run_loop.Run(); 99 return signature; 100 } 101 102 protected: 103 ScopedFakeAppleKeychainV2 scoped_fake_apple_keychain_{ 104 kTestKeychainAccessGroup}; 105 106 base::test::TaskEnvironment task_environment_; 107 108 std::unique_ptr<UserVerifyingKeyProvider> provider_ = 109 crypto::GetUserVerifyingKeyProvider(MakeConfig()); 110}; 111 112TEST_F(UserVerifyingKeyMacTest, RoundTrip) { 113 for (bool use_lacontext : {false, true}) { 114 SCOPED_TRACE(use_lacontext); 115 UserVerifyingKeyProvider::Config config = MakeConfig(); 116 if (use_lacontext) { 117 config.lacontext = ScopedLAContext([[LAContext alloc] init]); 118 } 119 provider_ = crypto::GetUserVerifyingKeyProvider(std::move(config)); 120 121 std::unique_ptr<UserVerifyingSigningKey> key = 122 GenerateUserVerifyingSigningKey(); 123 ASSERT_TRUE(key); 124 ASSERT_TRUE(!key->GetKeyLabel().empty()); 125 126 const std::vector<uint8_t> spki = key->GetPublicKey(); 127 const uint8_t message[] = {1, 2, 3, 4}; 128 std::optional<std::vector<uint8_t>> signature = Sign(key.get(), message); 129 ASSERT_TRUE(signature); 130 131 crypto::SignatureVerifier verifier; 132 ASSERT_TRUE(verifier.VerifyInit(kAcceptableAlgos[0], *signature, spki)); 133 verifier.VerifyUpdate(message); 134 ASSERT_TRUE(verifier.VerifyFinal()); 135 136 std::unique_ptr<UserVerifyingSigningKey> key2 = 137 GetUserVerifyingSigningKey(key->GetKeyLabel()); 138 ASSERT_TRUE(key2); 139 140 std::optional<std::vector<uint8_t>> signature2 = Sign(key.get(), message); 141 ASSERT_TRUE(signature2); 142 143 crypto::SignatureVerifier verifier2; 144 ASSERT_TRUE(verifier2.VerifyInit(kAcceptableAlgos[0], *signature2, spki)); 145 verifier2.VerifyUpdate(message); 146 ASSERT_TRUE(verifier2.VerifyFinal()); 147 } 148} 149 150TEST_F(UserVerifyingKeyMacTest, SecureEnclaveAvailability) { 151 using UVMethod = FakeAppleKeychainV2::UVMethod; 152 struct { 153 bool enclave_available; 154 UVMethod uv_method; 155 bool expected_uvk_available; 156 } kTests[] = { 157 {false, UVMethod::kNone, false}, 158 {false, UVMethod::kPasswordOnly, false}, 159 {false, UVMethod::kBiometrics, false}, 160 {true, UVMethod::kNone, false}, 161 {true, UVMethod::kPasswordOnly, true}, 162 {true, UVMethod::kBiometrics, true}, 163 }; 164 for (auto test : kTests) { 165 SCOPED_TRACE(test.enclave_available); 166 SCOPED_TRACE(static_cast<int>(test.uv_method)); 167 scoped_fake_apple_keychain_.keychain()->set_secure_enclave_available( 168 test.enclave_available); 169 scoped_fake_apple_keychain_.keychain()->set_uv_method(test.uv_method); 170 std::optional<bool> result; 171 base::RunLoop run_loop; 172 AreUserVerifyingKeysSupported(MakeConfig(), 173 base::BindLambdaForTesting([&](bool ret) { 174 result = ret; 175 run_loop.Quit(); 176 })); 177 run_loop.Run(); 178 EXPECT_EQ(result.value(), test.expected_uvk_available); 179 } 180} 181 182TEST_F(UserVerifyingKeyMacTest, DeleteSigningKey) { 183 std::unique_ptr<UserVerifyingSigningKey> key = 184 GenerateUserVerifyingSigningKey(); 185 ASSERT_TRUE(key); 186 187 EXPECT_TRUE(DeleteUserVerifyingKey(key->GetKeyLabel())); 188 EXPECT_FALSE(GetUserVerifyingSigningKey(key->GetKeyLabel())); 189 EXPECT_FALSE(DeleteUserVerifyingKey(key->GetKeyLabel())); 190} 191 192} // namespace 193 194} // namespace crypto 195