blob: 33f542b004e5d6004971673cc5587311b1a7c867 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/fido/mac/keychain.h"
#import <Foundation/Foundation.h>
#include "base/mac/foundation_util.h"
#include "base/mac/mac_logging.h"
#include "base/strings/sys_string_conversions.h"
#include "device/fido/mac/credential_metadata.h"
namespace device {
namespace fido {
namespace mac {
static API_AVAILABLE(macos(10.12.2)) Keychain* g_keychain_instance_override =
nullptr;
// static
Keychain& Keychain::GetInstance() {
if (g_keychain_instance_override) {
return *g_keychain_instance_override;
}
static base::NoDestructor<Keychain> k;
return *k;
}
// static
void Keychain::SetInstanceOverride(Keychain* keychain) {
CHECK(!g_keychain_instance_override);
g_keychain_instance_override = keychain;
}
// static
void Keychain::ClearInstanceOverride() {
CHECK(g_keychain_instance_override);
g_keychain_instance_override = nullptr;
}
Keychain::Keychain() = default;
Keychain::~Keychain() = default;
base::ScopedCFTypeRef<SecKeyRef> Keychain::KeyCreateRandomKey(
CFDictionaryRef params,
CFErrorRef* error) {
return base::ScopedCFTypeRef<SecKeyRef>(SecKeyCreateRandomKey(params, error));
}
base::ScopedCFTypeRef<CFDataRef> Keychain::KeyCreateSignature(
SecKeyRef key,
SecKeyAlgorithm algorithm,
CFDataRef data,
CFErrorRef* error) {
return base::ScopedCFTypeRef<CFDataRef>(
SecKeyCreateSignature(key, algorithm, data, error));
}
base::ScopedCFTypeRef<SecKeyRef> Keychain::KeyCopyPublicKey(SecKeyRef key) {
return base::ScopedCFTypeRef<SecKeyRef>(SecKeyCopyPublicKey(key));
}
OSStatus Keychain::ItemCopyMatching(CFDictionaryRef query, CFTypeRef* result) {
return SecItemCopyMatching(query, result);
}
OSStatus Keychain::ItemDelete(CFDictionaryRef query) {
return SecItemDelete(query);
}
Credential::Credential(base::ScopedCFTypeRef<SecKeyRef> private_key_,
std::vector<uint8_t> credential_id_)
: private_key(std::move(private_key_)),
credential_id(std::move(credential_id_)) {}
Credential::~Credential() = default;
Credential::Credential(Credential&& other) = default;
Credential& Credential::operator=(Credential&& other) = default;
base::Optional<Credential> FindCredentialInKeychain(
const std::string& keychain_access_group,
const std::string& metadata_secret,
const std::string& rp_id,
const std::set<std::vector<uint8_t>>& credential_id_filter,
LAContext* authentication_context) {
base::Optional<std::string> encoded_rp_id =
CredentialMetadata::EncodeRpId(metadata_secret, rp_id);
if (!encoded_rp_id)
return base::nullopt;
base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
CFDictionarySetValue(query, kSecClass, kSecClassKey);
CFDictionarySetValue(query, kSecAttrAccessGroup,
base::SysUTF8ToNSString(keychain_access_group));
CFDictionarySetValue(query, kSecAttrLabel,
base::SysUTF8ToNSString(*encoded_rp_id));
if (authentication_context) {
CFDictionarySetValue(query, kSecUseAuthenticationContext,
authentication_context);
}
CFDictionarySetValue(query, kSecReturnRef, @YES);
CFDictionarySetValue(query, kSecReturnAttributes, @YES);
CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
base::ScopedCFTypeRef<CFArrayRef> keychain_items;
OSStatus status = Keychain::GetInstance().ItemCopyMatching(
query, reinterpret_cast<CFTypeRef*>(keychain_items.InitializeInto()));
if (status == errSecItemNotFound) {
// No credentials for the RP.
return base::nullopt;
}
if (status != errSecSuccess) {
OSSTATUS_DLOG(ERROR, status) << "SecItemCopyMatching failed";
return base::nullopt;
}
// Credentials for the RP exist. Find a match.
std::vector<uint8_t> credential_id;
base::ScopedCFTypeRef<SecKeyRef> private_key(nil);
for (CFIndex i = 0; i < CFArrayGetCount(keychain_items); ++i) {
CFDictionaryRef attributes = base::mac::CFCast<CFDictionaryRef>(
CFArrayGetValueAtIndex(keychain_items, i));
CFDataRef application_label = base::mac::GetValueFromDictionary<CFDataRef>(
attributes, kSecAttrApplicationLabel);
SecKeyRef key =
base::mac::GetValueFromDictionary<SecKeyRef>(attributes, kSecValueRef);
if (!application_label || !key) {
// Corrupted keychain?
DLOG(ERROR) << "could not find application label or key ref: "
<< attributes;
continue;
}
std::vector<uint8_t> cid(CFDataGetBytePtr(application_label),
CFDataGetBytePtr(application_label) +
CFDataGetLength(application_label));
if (credential_id_filter.empty() ||
credential_id_filter.find(cid) != credential_id_filter.end()) {
private_key.reset(key, base::scoped_policy::RETAIN);
credential_id = std::move(cid);
break;
}
}
if (private_key == nil) {
DVLOG(1) << "no allowed credential found";
return base::nullopt;
}
return Credential(std::move(private_key), std::move(credential_id));
}
} // namespace mac
} // namespace fido
} // namespace device