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