blob: ea22515f159dab72157962d1799fb340032aa334 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/mac_key_persistence_delegate.h"
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#include <string>
#include <utility>
#include "base/containers/span.h"
#include "base/mac/bridging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/mock_secure_enclave_client.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/mac/secure_enclave_signing_key.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/shared_command_constants.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/signing_key_pair.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using BPKUR = enterprise_management::BrowserPublicKeyUploadRequest;
using ::testing::_;
using ::testing::InSequence;
namespace enterprise_connectors {
using test::MockSecureEnclaveClient;
class MacKeyPersistenceDelegateTest : public testing::Test {
public:
MacKeyPersistenceDelegateTest() {
SetMockClient();
persistence_delegate_ = std::make_unique<MacKeyPersistenceDelegate>();
}
protected:
void SetMockClient() {
auto mock_secure_enclave_client =
std::make_unique<MockSecureEnclaveClient>();
mock_secure_enclave_client_ = mock_secure_enclave_client.get();
SecureEnclaveClient::SetInstanceForTesting(
std::move(mock_secure_enclave_client));
}
std::vector<uint8_t> CreateWrappedKeyLabel(std::string label) {
std::vector<uint8_t> output;
output.assign(label.begin(), label.end());
return output;
}
// Creates a test key.
base::ScopedCFTypeRef<SecKeyRef> CreateTestKey() {
base::ScopedCFTypeRef<CFMutableDictionaryRef> test_attributes(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
CFDictionarySetValue(test_attributes, kSecAttrLabel, CFSTR("fake-label"));
CFDictionarySetValue(test_attributes, kSecAttrKeyType,
kSecAttrKeyTypeECSECPrimeRandom);
CFDictionarySetValue(test_attributes, kSecAttrKeySizeInBits,
base::mac::NSToCFPtrCast(@256));
base::ScopedCFTypeRef<CFMutableDictionaryRef> private_key_params(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
CFDictionarySetValue(private_key_params, kSecAttrIsPermanent,
kCFBooleanFalse);
CFDictionarySetValue(test_attributes, kSecPrivateKeyAttrs,
private_key_params);
return base::ScopedCFTypeRef<SecKeyRef>(
SecKeyCreateRandomKey(test_attributes, nullptr));
}
std::unique_ptr<MacKeyPersistenceDelegate> persistence_delegate_;
MockSecureEnclaveClient* mock_secure_enclave_client_ = nullptr;
};
// Tests that storing a key with an OS key trust level invokes the clients'
// UpdateStoredKeyLabel method when the wrapped data is correct.
TEST_F(MacKeyPersistenceDelegateTest, StoreKeyPair_OSKey_Success) {
InSequence s;
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.Times(0);
EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
BPKUR::CHROME_BROWSER_OS_KEY,
CreateWrappedKeyLabel(constants::kDeviceTrustSigningKeyLabel)));
// Correct wrapped data consists of the wrapped temporary key label.
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.WillOnce([](SecureEnclaveClient::KeyType current_key_type,
SecureEnclaveClient::KeyType new_key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
return true;
});
EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
BPKUR::CHROME_BROWSER_OS_KEY,
CreateWrappedKeyLabel(constants::kTemporaryDeviceTrustSigningKeyLabel)));
}
// Tests that storing a key with an OS key trust level fails when the clients'
// UpdateStoredKeyLabel method returns false.
TEST_F(MacKeyPersistenceDelegateTest, StoreKeyPair_OSKey_Failure) {
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.WillOnce([](SecureEnclaveClient::KeyType current_key_type,
SecureEnclaveClient::KeyType new_key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
return false;
});
EXPECT_FALSE(persistence_delegate_->StoreKeyPair(
BPKUR::CHROME_BROWSER_OS_KEY,
CreateWrappedKeyLabel(constants::kTemporaryDeviceTrustSigningKeyLabel)));
}
// Tests that storing a key with an unspecified trust level invokes the clients'
// DeleteKey method with the correct key type.
TEST_F(MacKeyPersistenceDelegateTest, StoreKeyPair_UnspecifiedKey_Success) {
InSequence s;
EXPECT_CALL(*mock_secure_enclave_client_, DeleteKey(_))
.WillOnce([](SecureEnclaveClient::KeyType current_key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, current_key_type);
return true;
});
EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
BPKUR::KEY_TRUST_LEVEL_UNSPECIFIED, std::vector<uint8_t>()));
}
// Tests that storing a hardware generated key invokes the clients'
// UpdateStoredKeyLabel method when the wrapped data is correct.
TEST_F(MacKeyPersistenceDelegateTest, StoreKeyPair_HardwareKey_Success) {
InSequence s;
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.Times(0);
EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
BPKUR::CHROME_BROWSER_HW_KEY,
CreateWrappedKeyLabel(constants::kDeviceTrustSigningKeyLabel)));
// Correct wrapped data consists of the wrapped temporary key label.
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.WillOnce([](SecureEnclaveClient::KeyType current_key_type,
SecureEnclaveClient::KeyType new_key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
return true;
});
EXPECT_TRUE(persistence_delegate_->StoreKeyPair(
BPKUR::CHROME_BROWSER_HW_KEY,
CreateWrappedKeyLabel(constants::kTemporaryDeviceTrustSigningKeyLabel)));
}
// Tests that storing a hardware generated key fails when the clients'
// UpdateStoredKeyLabel method returns false.
TEST_F(MacKeyPersistenceDelegateTest, StoreKeyPair_HardwareKey_Failure) {
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.WillOnce([](SecureEnclaveClient::KeyType current_key_type,
SecureEnclaveClient::KeyType new_key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, current_key_type);
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, new_key_type);
return false;
});
EXPECT_FALSE(persistence_delegate_->StoreKeyPair(
BPKUR::CHROME_BROWSER_HW_KEY,
CreateWrappedKeyLabel(constants::kTemporaryDeviceTrustSigningKeyLabel)));
}
// Tests loading a key pair when no key previously existed.
TEST_F(MacKeyPersistenceDelegateTest, LoadKeyPair_NoKey) {
InSequence s;
EXPECT_CALL(*mock_secure_enclave_client_, GetStoredKeyLabel(_, _))
.WillOnce(
[](SecureEnclaveClient::KeyType type, std::vector<uint8_t>& output) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, type);
return false;
});
EXPECT_FALSE(persistence_delegate_->LoadKeyPair());
EXPECT_CALL(*mock_secure_enclave_client_, GetStoredKeyLabel(_, _))
.WillOnce(
[](SecureEnclaveClient::KeyType type, std::vector<uint8_t>& output) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, type);
return true;
});
EXPECT_FALSE(persistence_delegate_->LoadKeyPair());
}
// Tests loading a key pair when a key previously existed.
TEST_F(MacKeyPersistenceDelegateTest, LoadKeyPair_Key) {
EXPECT_CALL(*mock_secure_enclave_client_, GetStoredKeyLabel(_, _))
.WillOnce(
[](SecureEnclaveClient::KeyType type, std::vector<uint8_t>& output) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, type);
std::string label = constants::kDeviceTrustSigningKeyLabel;
output.assign(label.begin(), label.end());
return true;
});
SetMockClient();
EXPECT_CALL(*mock_secure_enclave_client_, CopyStoredKey(_))
.WillOnce([this](SecureEnclaveClient::KeyType type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, type);
return CreateTestKey();
});
auto key_pair = persistence_delegate_->LoadKeyPair();
EXPECT_EQ(BPKUR::CHROME_BROWSER_HW_KEY, key_pair->trust_level());
EXPECT_TRUE(key_pair->key());
}
// Tests a failure to create a new key pair.
TEST_F(MacKeyPersistenceDelegateTest, CreateKeyPair__EmptySigningKey) {
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.WillOnce([](SecureEnclaveClient::KeyType current_key_type,
SecureEnclaveClient::KeyType new_key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, current_key_type);
EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, new_key_type);
return true;
});
SetMockClient();
EXPECT_CALL(*mock_secure_enclave_client_, CreatePermanentKey())
.WillOnce([]() { return base::ScopedCFTypeRef<SecKeyRef>(); });
EXPECT_FALSE(persistence_delegate_->CreateKeyPair());
}
// Tests a successful call to create a new key pair.
TEST_F(MacKeyPersistenceDelegateTest, CreateKeyPair_Success) {
EXPECT_CALL(*mock_secure_enclave_client_, UpdateStoredKeyLabel(_, _))
.WillOnce([](SecureEnclaveClient::KeyType current_key_type,
SecureEnclaveClient::KeyType new_key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kPermanent, current_key_type);
EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, new_key_type);
return true;
});
SetMockClient();
EXPECT_CALL(*mock_secure_enclave_client_, CreatePermanentKey())
.WillOnce([this]() { return CreateTestKey(); });
auto key_pair = persistence_delegate_->CreateKeyPair();
EXPECT_EQ(BPKUR::CHROME_BROWSER_HW_KEY, key_pair->trust_level());
EXPECT_TRUE(key_pair->key());
}
// Tests that the CleanupTemporaryKeyData correctly invokes the clients'
// DeleteKey method with the correct key type.
TEST_F(MacKeyPersistenceDelegateTest, CleanupTemporaryKeyData) {
EXPECT_CALL(*mock_secure_enclave_client_, DeleteKey(_))
.WillOnce([](SecureEnclaveClient::KeyType key_type) {
EXPECT_EQ(SecureEnclaveClient::KeyType::kTemporary, key_type);
return true;
});
persistence_delegate_->CleanupTemporaryKeyData();
}
} // namespace enterprise_connectors