xref: /aosp_15_r20/external/cronet/crypto/user_verifying_key_mac_unittest.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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