• 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 "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