Commit 292cf067 authored by Martin Kreichgauer's avatar Martin Kreichgauer Committed by Commit Bot

fido/mac: flatten the Operation class hierarchy

Eliminate the middle level of the Operation <- OperationBase <-
{MakeCredential,GetAssertion}Operation class hierarchy. It was supposed
to allow the two concrete implementations share common behavior. But the
actual shared behavior was trivial, and so it just made the code more
annoying to read.

No functional changes intended.

Change-Id: I4de1107d17d702f5dfb1407127e09b1a2efeb75c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1965669Reviewed-by: default avatarAdam Langley <agl@chromium.org>
Commit-Queue: Martin Kreichgauer <martinkr@google.com>
Cr-Commit-Position: refs/heads/master@{#727548}
parent 77b0d67a
...@@ -202,7 +202,6 @@ component("fido") { ...@@ -202,7 +202,6 @@ component("fido") {
"mac/make_credential_operation.h", "mac/make_credential_operation.h",
"mac/make_credential_operation.mm", "mac/make_credential_operation.mm",
"mac/operation.h", "mac/operation.h",
"mac/operation_base.h",
"mac/touch_id_context.h", "mac/touch_id_context.h",
"mac/touch_id_context.mm", "mac/touch_id_context.mm",
"mac/util.h", "mac/util.h",
......
...@@ -5,13 +5,15 @@ ...@@ -5,13 +5,15 @@
#ifndef DEVICE_FIDO_MAC_GET_ASSERTION_OPERATION_H_ #ifndef DEVICE_FIDO_MAC_GET_ASSERTION_OPERATION_H_
#define DEVICE_FIDO_MAC_GET_ASSERTION_OPERATION_H_ #define DEVICE_FIDO_MAC_GET_ASSERTION_OPERATION_H_
#include "base/callback.h"
#include "base/component_export.h" #include "base/component_export.h"
#include "base/mac/availability.h" #include "base/mac/availability.h"
#include "base/macros.h" #include "base/macros.h"
#include "device/fido/authenticator_get_assertion_response.h" #include "device/fido/authenticator_get_assertion_response.h"
#include "device/fido/ctap_get_assertion_request.h" #include "device/fido/ctap_get_assertion_request.h"
#include "device/fido/mac/keychain.h" #include "device/fido/mac/keychain.h"
#include "device/fido/mac/operation_base.h" #include "device/fido/mac/operation.h"
#include "device/fido/mac/touch_id_context.h"
namespace device { namespace device {
namespace fido { namespace fido {
...@@ -27,17 +29,19 @@ namespace mac { ...@@ -27,17 +29,19 @@ namespace mac {
// For documentation on the keychain item metadata, see // For documentation on the keychain item metadata, see
// |MakeCredentialOperation|. // |MakeCredentialOperation|.
class API_AVAILABLE(macosx(10.12.2)) class API_AVAILABLE(macosx(10.12.2))
COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionOperation COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionOperation : public Operation {
: public OperationBase<CtapGetAssertionRequest,
AuthenticatorGetAssertionResponse> {
public: public:
using Callback = base::OnceCallback<void(
CtapDeviceResponseCode,
base::Optional<AuthenticatorGetAssertionResponse>)>;
GetAssertionOperation(CtapGetAssertionRequest request, GetAssertionOperation(CtapGetAssertionRequest request,
std::string metadata_secret, std::string metadata_secret,
std::string keychain_access_group, std::string keychain_access_group,
Callback callback); Callback callback);
~GetAssertionOperation() override; ~GetAssertionOperation() override;
// OperationBase: // Operation:
void Run() override; void Run() override;
// GetNextAssertion() may be called for a request with an empty allowList // GetNextAssertion() may be called for a request with an empty allowList
...@@ -45,11 +49,20 @@ class API_AVAILABLE(macosx(10.12.2)) ...@@ -45,11 +49,20 @@ class API_AVAILABLE(macosx(10.12.2))
void GetNextAssertion(Callback callback); void GetNextAssertion(Callback callback);
private: private:
const std::string& RpId() const override; void PromptTouchIdDone(bool success);
void PromptTouchIdDone(bool success) override;
base::Optional<AuthenticatorGetAssertionResponse> ResponseForCredential( base::Optional<AuthenticatorGetAssertionResponse> ResponseForCredential(
const Credential& credential); const Credential& credential);
// The secret parameter passed to |CredentialMetadata| operations to encrypt
// or encode credential metadata for storage in the macOS keychain.
const std::string metadata_secret_;
const std::string keychain_access_group_;
const std::unique_ptr<TouchIdContext> touch_id_context_ =
TouchIdContext::Create();
const CtapGetAssertionRequest request_;
Callback callback_;
std::list<Credential> matching_credentials_; std::list<Credential> matching_credentials_;
DISALLOW_COPY_AND_ASSIGN(GetAssertionOperation); DISALLOW_COPY_AND_ASSIGN(GetAssertionOperation);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include "base/bind.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/mac/mac_logging.h" #include "base/mac/mac_logging.h"
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/device_event_log/device_event_log.h" #include "components/device_event_log/device_event_log.h"
#include "device/fido/fido_constants.h" #include "device/fido/fido_constants.h"
#include "device/fido/mac/credential_metadata.h"
#include "device/fido/mac/keychain.h" #include "device/fido/mac/keychain.h"
#include "device/fido/mac/util.h" #include "device/fido/mac/util.h"
#include "device/fido/public_key_credential_descriptor.h" #include "device/fido/public_key_credential_descriptor.h"
...@@ -34,65 +36,64 @@ GetAssertionOperation::GetAssertionOperation(CtapGetAssertionRequest request, ...@@ -34,65 +36,64 @@ GetAssertionOperation::GetAssertionOperation(CtapGetAssertionRequest request,
std::string metadata_secret, std::string metadata_secret,
std::string keychain_access_group, std::string keychain_access_group,
Callback callback) Callback callback)
: OperationBase<CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>( : metadata_secret_(std::move(metadata_secret)),
std::move(request), keychain_access_group_(std::move(keychain_access_group)),
std::move(metadata_secret), request_(std::move(request)),
std::move(keychain_access_group), callback_(std::move(callback)) {}
std::move(callback)) {}
GetAssertionOperation::~GetAssertionOperation() = default; GetAssertionOperation::~GetAssertionOperation() = default;
const std::string& GetAssertionOperation::RpId() const {
return request().rp_id;
}
void GetAssertionOperation::Run() { void GetAssertionOperation::Run() {
Init();
// Display the macOS Touch ID prompt. // Display the macOS Touch ID prompt.
PromptTouchId(l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON, touch_id_context_->PromptTouchId(
base::UTF8ToUTF16(RpId()))); l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON,
base::UTF8ToUTF16(request_.rp_id)),
base::BindOnce(&GetAssertionOperation::PromptTouchIdDone,
base::Unretained(this)));
} }
void GetAssertionOperation::PromptTouchIdDone(bool success) { void GetAssertionOperation::PromptTouchIdDone(bool success) {
if (!success) { if (!success) {
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied,
.Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied, base::nullopt); base::nullopt);
return; return;
} }
std::set<std::vector<uint8_t>> allowed_credential_ids = std::set<std::vector<uint8_t>> allowed_credential_ids =
FilterInapplicableEntriesFromAllowList(request()); FilterInapplicableEntriesFromAllowList(request_);
if (allowed_credential_ids.empty() && !request().allow_list.empty()) { if (allowed_credential_ids.empty() && !request_.allow_list.empty()) {
// The caller checking // The caller checking
// TouchIdAuthenticator::HasCredentialForGetAssertionRequest() should have // TouchIdAuthenticator::HasCredentialForGetAssertionRequest() should have
// caught this. // caught this.
NOTREACHED(); NOTREACHED();
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
.Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt); base::nullopt);
return; return;
} }
const bool empty_allow_list = request().allow_list.empty(); const bool empty_allow_list = request_.allow_list.empty();
std::list<Credential> credentials = std::list<Credential> credentials =
empty_allow_list ? FindResidentCredentialsInKeychain( empty_allow_list
keychain_access_group(), metadata_secret(), RpId(), ? FindResidentCredentialsInKeychain(
authentication_context()) keychain_access_group_, metadata_secret_, request_.rp_id,
touch_id_context_->authentication_context())
: FindCredentialsInKeychain( : FindCredentialsInKeychain(
keychain_access_group(), metadata_secret(), RpId(), keychain_access_group_, metadata_secret_, request_.rp_id,
allowed_credential_ids, authentication_context()); allowed_credential_ids,
touch_id_context_->authentication_context());
if (credentials.empty()) { if (credentials.empty()) {
// TouchIdAuthenticator::HasCredentialForGetAssertionRequest() is invoked // TouchIdAuthenticator::HasCredentialForGetAssertionRequest() is
// first to ensure this doesn't occur. // invoked first to ensure this doesn't occur.
NOTREACHED(); NOTREACHED();
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
.Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt); base::nullopt);
return; return;
} }
base::Optional<AuthenticatorGetAssertionResponse> response = base::Optional<AuthenticatorGetAssertionResponse> response =
ResponseForCredential(credentials.front()); ResponseForCredential(credentials.front());
if (!response) { if (!response) {
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
.Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, base::nullopt); base::nullopt);
return; return;
} }
...@@ -102,8 +103,8 @@ void GetAssertionOperation::PromptTouchIdDone(bool success) { ...@@ -102,8 +103,8 @@ void GetAssertionOperation::PromptTouchIdDone(bool success) {
matching_credentials_ = std::move(credentials); matching_credentials_ = std::move(credentials);
} }
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kSuccess,
.Run(CtapDeviceResponseCode::kSuccess, std::move(*response)); std::move(*response));
} }
void GetAssertionOperation::GetNextAssertion(Callback callback) { void GetAssertionOperation::GetNextAssertion(Callback callback) {
...@@ -123,8 +124,8 @@ void GetAssertionOperation::GetNextAssertion(Callback callback) { ...@@ -123,8 +124,8 @@ void GetAssertionOperation::GetNextAssertion(Callback callback) {
base::Optional<AuthenticatorGetAssertionResponse> base::Optional<AuthenticatorGetAssertionResponse>
GetAssertionOperation::ResponseForCredential(const Credential& credential) { GetAssertionOperation::ResponseForCredential(const Credential& credential) {
base::Optional<CredentialMetadata> metadata = base::Optional<CredentialMetadata> metadata = UnsealCredentialId(
UnsealCredentialId(metadata_secret(), RpId(), credential.credential_id); metadata_secret_, request_.rp_id, credential.credential_id);
if (!metadata) { if (!metadata) {
// The keychain query already filtered for the RP ID encoded under this // The keychain query already filtered for the RP ID encoded under this
// operation's metadata secret, so the credential id really should have // operation's metadata secret, so the credential id really should have
...@@ -133,10 +134,10 @@ GetAssertionOperation::ResponseForCredential(const Credential& credential) { ...@@ -133,10 +134,10 @@ GetAssertionOperation::ResponseForCredential(const Credential& credential) {
return base::nullopt; return base::nullopt;
} }
AuthenticatorData authenticator_data = AuthenticatorData authenticator_data = MakeAuthenticatorData(
MakeAuthenticatorData(RpId(), /*attested_credential_data=*/base::nullopt); request_.rp_id, /*attested_credential_data=*/base::nullopt);
base::Optional<std::vector<uint8_t>> signature = GenerateSignature( base::Optional<std::vector<uint8_t>> signature = GenerateSignature(
authenticator_data, request().client_data_hash, credential.private_key); authenticator_data, request_.client_data_hash, credential.private_key);
if (!signature) { if (!signature) {
FIDO_LOG(ERROR) << "GenerateSignature failed"; FIDO_LOG(ERROR) << "GenerateSignature failed";
return base::nullopt; return base::nullopt;
......
...@@ -5,12 +5,14 @@ ...@@ -5,12 +5,14 @@
#ifndef DEVICE_FIDO_MAC_MAKE_CREDENTIAL_OPERATION_H_ #ifndef DEVICE_FIDO_MAC_MAKE_CREDENTIAL_OPERATION_H_
#define DEVICE_FIDO_MAC_MAKE_CREDENTIAL_OPERATION_H_ #define DEVICE_FIDO_MAC_MAKE_CREDENTIAL_OPERATION_H_
#include "base/callback.h"
#include "base/component_export.h" #include "base/component_export.h"
#include "base/mac/availability.h" #include "base/mac/availability.h"
#include "base/macros.h" #include "base/macros.h"
#include "device/fido/authenticator_make_credential_response.h" #include "device/fido/authenticator_make_credential_response.h"
#include "device/fido/ctap_make_credential_request.h" #include "device/fido/ctap_make_credential_request.h"
#include "device/fido/mac/operation_base.h" #include "device/fido/mac/operation.h"
#include "device/fido/mac/touch_id_context.h"
namespace device { namespace device {
namespace fido { namespace fido {
...@@ -43,22 +45,42 @@ namespace mac { ...@@ -43,22 +45,42 @@ namespace mac {
// separate from any other data that Chrome may store in the keychain in // separate from any other data that Chrome may store in the keychain in
// the future. // the future.
class API_AVAILABLE(macosx(10.12.2)) class API_AVAILABLE(macosx(10.12.2))
COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialOperation COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialOperation : public Operation {
: public OperationBase<CtapMakeCredentialRequest,
AuthenticatorMakeCredentialResponse> {
public: public:
using Callback = base::OnceCallback<void(
CtapDeviceResponseCode,
base::Optional<AuthenticatorMakeCredentialResponse>)>;
MakeCredentialOperation(CtapMakeCredentialRequest request, MakeCredentialOperation(CtapMakeCredentialRequest request,
std::string profile_id, std::string profile_id,
std::string keychain_access_group, std::string keychain_access_group,
Callback callback); Callback callback);
~MakeCredentialOperation() override; ~MakeCredentialOperation() override;
// Operation:
void Run() override; void Run() override;
private: private:
// OperationBase: void PromptTouchIdDone(bool success);
const std::string& RpId() const override;
void PromptTouchIdDone(bool success) override; // DefaultKeychainQuery returns a default keychain query dictionary that has
// the keychain item class, keychain access group and RP ID filled out (but
// not the credential ID). More fields can be set on the return value to
// refine the query.
base::ScopedCFTypeRef<CFMutableDictionaryRef> DefaultKeychainQuery() const;
// The secret parameter passed to |CredentialMetadata| operations to encrypt
// or encode credential metadata for storage in the macOS keychain.
const std::string metadata_secret_;
const std::string keychain_access_group_;
const std::unique_ptr<TouchIdContext> touch_id_context_ =
TouchIdContext::Create();
const CtapMakeCredentialRequest request_;
Callback callback_;
DISALLOW_COPY_AND_ASSIGN(MakeCredentialOperation);
}; };
} // namespace mac } // namespace mac
......
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include "base/bind.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/mac/mac_logging.h" #include "base/mac/mac_logging.h"
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "components/device_event_log/device_event_log.h" #include "components/device_event_log/device_event_log.h"
#include "device/fido/attestation_statement_formats.h" #include "device/fido/attestation_statement_formats.h"
...@@ -36,20 +38,13 @@ MakeCredentialOperation::MakeCredentialOperation( ...@@ -36,20 +38,13 @@ MakeCredentialOperation::MakeCredentialOperation(
std::string metadata_secret, std::string metadata_secret,
std::string keychain_access_group, std::string keychain_access_group,
Callback callback) Callback callback)
: OperationBase<CtapMakeCredentialRequest, : metadata_secret_(std::move(metadata_secret)),
AuthenticatorMakeCredentialResponse>( keychain_access_group_(std::move(keychain_access_group)),
std::move(request), request_(std::move(request)),
std::move(metadata_secret), callback_(std::move(callback)) {}
std::move(keychain_access_group),
std::move(callback)) {}
MakeCredentialOperation::~MakeCredentialOperation() = default; MakeCredentialOperation::~MakeCredentialOperation() = default;
const std::string& MakeCredentialOperation::RpId() const {
return request().rp.id;
}
void MakeCredentialOperation::Run() { void MakeCredentialOperation::Run() {
Init();
// Verify pubKeyCredParams contains ES-256, which is the only algorithm we // Verify pubKeyCredParams contains ES-256, which is the only algorithm we
// support. // support.
auto is_es256 = auto is_es256 =
...@@ -58,30 +53,32 @@ void MakeCredentialOperation::Run() { ...@@ -58,30 +53,32 @@ void MakeCredentialOperation::Run() {
static_cast<int>(CoseAlgorithmIdentifier::kCoseEs256); static_cast<int>(CoseAlgorithmIdentifier::kCoseEs256);
}; };
const auto& key_params = const auto& key_params =
request().public_key_credential_params.public_key_credential_params(); request_.public_key_credential_params.public_key_credential_params();
if (!std::any_of(key_params.begin(), key_params.end(), is_es256)) { if (!std::any_of(key_params.begin(), key_params.end(), is_es256)) {
DVLOG(1) << "No supported algorithm found."; DVLOG(1) << "No supported algorithm found.";
std::move(callback()) std::move(callback_).Run(
.Run(CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm, CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm, base::nullopt);
base::nullopt);
return; return;
} }
// Display the macOS Touch ID prompt. // Display the macOS Touch ID prompt.
PromptTouchId(l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON, touch_id_context_->PromptTouchId(
base::UTF8ToUTF16(RpId()))); l10n_util::GetStringFUTF16(IDS_WEBAUTHN_TOUCH_ID_PROMPT_REASON,
base::UTF8ToUTF16(request_.rp.id)),
base::BindOnce(&MakeCredentialOperation::PromptTouchIdDone,
base::Unretained(this)));
} }
void MakeCredentialOperation::PromptTouchIdDone(bool success) { void MakeCredentialOperation::PromptTouchIdDone(bool success) {
if (!success) { if (!success) {
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied,
.Run(CtapDeviceResponseCode::kCtap2ErrOperationDenied, base::nullopt); base::nullopt);
return; return;
} }
// Evaluate that excludeList does not contain any credentials stored by this // Evaluate that excludeList does not contain any credentials stored by this
// authenticator. // authenticator.
for (auto& credential : request().exclude_list) { for (auto& credential : request_.exclude_list) {
ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery(); ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery();
CFDictionarySetValue(query, kSecAttrApplicationLabel, CFDictionarySetValue(query, kSecAttrApplicationLabel,
[NSData dataWithBytes:credential.id().data() [NSData dataWithBytes:credential.id().data()
...@@ -90,16 +87,15 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -90,16 +87,15 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
if (status == errSecSuccess) { if (status == errSecSuccess) {
// Excluded item found. // Excluded item found.
DVLOG(1) << "credential from excludeList found"; DVLOG(1) << "credential from excludeList found";
std::move(callback()) std::move(callback_).Run(
.Run(CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, CtapDeviceResponseCode::kCtap2ErrCredentialExcluded, base::nullopt);
base::nullopt);
return; return;
} }
if (status != errSecItemNotFound) { if (status != errSecItemNotFound) {
// Unexpected keychain error. // Unexpected keychain error.
OSSTATUS_DLOG(ERROR, status) << "failed to check for excluded credential"; OSSTATUS_DLOG(ERROR, status) << "failed to check for excluded credential";
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt); base::nullopt);
return; return;
} }
} }
...@@ -109,7 +105,7 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -109,7 +105,7 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
// Note that because the rk bit is not encoded here, a resident credential // Note that because the rk bit is not encoded here, a resident credential
// may overwrite a non-resident credential and vice versa. // may overwrite a non-resident credential and vice versa.
const std::string encoded_rp_id_user_id = const std::string encoded_rp_id_user_id =
EncodeRpIdAndUserId(metadata_secret(), RpId(), request().user.id); EncodeRpIdAndUserId(metadata_secret_, request_.rp.id, request_.user.id);
{ {
ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery(); ScopedCFTypeRef<CFMutableDictionaryRef> query = DefaultKeychainQuery();
CFDictionarySetValue(query, kSecAttrApplicationTag, CFDictionarySetValue(query, kSecAttrApplicationTag,
...@@ -117,17 +113,17 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -117,17 +113,17 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
OSStatus status = Keychain::GetInstance().ItemDelete(query); OSStatus status = Keychain::GetInstance().ItemDelete(query);
if (status != errSecSuccess && status != errSecItemNotFound) { if (status != errSecSuccess && status != errSecItemNotFound) {
OSSTATUS_DLOG(ERROR, status) << "SecItemDelete failed"; OSSTATUS_DLOG(ERROR, status) << "SecItemDelete failed";
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt); base::nullopt);
return; return;
} }
} }
// Generate the new key pair. // Generate the new key pair.
const std::vector<uint8_t> credential_id = const std::vector<uint8_t> credential_id =
SealCredentialId(metadata_secret(), RpId(), SealCredentialId(metadata_secret_, request_.rp.id,
CredentialMetadata::FromPublicKeyCredentialUserEntity( CredentialMetadata::FromPublicKeyCredentialUserEntity(
request().user, request().resident_key_required)); request_.user, request_.resident_key_required));
ScopedCFTypeRef<CFMutableDictionaryRef> params( ScopedCFTypeRef<CFMutableDictionaryRef> params(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr)); CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
...@@ -142,9 +138,9 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -142,9 +138,9 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
CFDictionarySetValue(params, kSecPrivateKeyAttrs, private_key_params); CFDictionarySetValue(params, kSecPrivateKeyAttrs, private_key_params);
CFDictionarySetValue(private_key_params, kSecAttrIsPermanent, @YES); CFDictionarySetValue(private_key_params, kSecAttrIsPermanent, @YES);
CFDictionarySetValue(private_key_params, kSecAttrAccessControl, CFDictionarySetValue(private_key_params, kSecAttrAccessControl,
access_control()); touch_id_context_->access_control());
CFDictionarySetValue(private_key_params, kSecUseAuthenticationContext, CFDictionarySetValue(private_key_params, kSecUseAuthenticationContext,
authentication_context()); touch_id_context_->authentication_context());
CFDictionarySetValue(private_key_params, kSecAttrApplicationTag, CFDictionarySetValue(private_key_params, kSecAttrApplicationTag,
base::SysUTF8ToNSString(encoded_rp_id_user_id)); base::SysUTF8ToNSString(encoded_rp_id_user_id));
CFDictionarySetValue(private_key_params, kSecAttrApplicationLabel, CFDictionarySetValue(private_key_params, kSecAttrApplicationLabel,
...@@ -157,16 +153,16 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -157,16 +153,16 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
cferr.InitializeInto())); cferr.InitializeInto()));
if (!private_key) { if (!private_key) {
FIDO_LOG(ERROR) << "SecKeyCreateRandomKey failed: " << cferr; FIDO_LOG(ERROR) << "SecKeyCreateRandomKey failed: " << cferr;
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt); base::nullopt);
return; return;
} }
ScopedCFTypeRef<SecKeyRef> public_key( ScopedCFTypeRef<SecKeyRef> public_key(
Keychain::GetInstance().KeyCopyPublicKey(private_key)); Keychain::GetInstance().KeyCopyPublicKey(private_key));
if (!public_key) { if (!public_key) {
FIDO_LOG(ERROR) << "SecKeyCopyPublicKey failed"; FIDO_LOG(ERROR) << "SecKeyCopyPublicKey failed";
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt); base::nullopt);
return; return;
} }
...@@ -177,18 +173,18 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -177,18 +173,18 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
SecKeyRefToECPublicKey(public_key)); SecKeyRefToECPublicKey(public_key));
if (!attested_credential_data) { if (!attested_credential_data) {
FIDO_LOG(ERROR) << "MakeAttestedCredentialData failed"; FIDO_LOG(ERROR) << "MakeAttestedCredentialData failed";
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt); base::nullopt);
return; return;
} }
AuthenticatorData authenticator_data = AuthenticatorData authenticator_data = MakeAuthenticatorData(
MakeAuthenticatorData(RpId(), std::move(*attested_credential_data)); request_.rp.id, std::move(*attested_credential_data));
base::Optional<std::vector<uint8_t>> signature = GenerateSignature( base::Optional<std::vector<uint8_t>> signature = GenerateSignature(
authenticator_data, request().client_data_hash, private_key); authenticator_data, request_.client_data_hash, private_key);
if (!signature) { if (!signature) {
FIDO_LOG(ERROR) << "MakeSignature failed"; FIDO_LOG(ERROR) << "MakeSignature failed";
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
.Run(CtapDeviceResponseCode::kCtap2ErrOther, base::nullopt); base::nullopt);
return; return;
} }
AuthenticatorMakeCredentialResponse response( AuthenticatorMakeCredentialResponse response(
...@@ -198,10 +194,22 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) { ...@@ -198,10 +194,22 @@ void MakeCredentialOperation::PromptTouchIdDone(bool success) {
std::make_unique<PackedAttestationStatement>( std::make_unique<PackedAttestationStatement>(
CoseAlgorithmIdentifier::kCoseEs256, std::move(*signature), CoseAlgorithmIdentifier::kCoseEs256, std::move(*signature),
/*x509_certificates=*/std::vector<std::vector<uint8_t>>()))); /*x509_certificates=*/std::vector<std::vector<uint8_t>>())));
std::move(callback()) std::move(callback_).Run(CtapDeviceResponseCode::kSuccess,
.Run(CtapDeviceResponseCode::kSuccess, std::move(response)); std::move(response));
} }
base::ScopedCFTypeRef<CFMutableDictionaryRef>
MakeCredentialOperation::DefaultKeychainQuery() const {
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(EncodeRpId(metadata_secret_, request_.rp.id)));
return query;
}
} // namespace mac } // namespace mac
} // namespace fido } // namespace fido
} // namespace device } // namespace device
...@@ -11,15 +11,12 @@ namespace device { ...@@ -11,15 +11,12 @@ namespace device {
namespace fido { namespace fido {
namespace mac { namespace mac {
// Operation is the interface to OperationBase.
class Operation { class Operation {
public: public:
Operation() = default;
virtual ~Operation() = default; virtual ~Operation() = default;
virtual void Run() = 0; virtual void Run() = 0;
protected:
Operation() = default;
private: private:
DISALLOW_COPY_AND_ASSIGN(Operation); DISALLOW_COPY_AND_ASSIGN(Operation);
}; };
......
// 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.
#ifndef DEVICE_FIDO_MAC_OPERATION_BASE_H_
#define DEVICE_FIDO_MAC_OPERATION_BASE_H_
#import <Foundation/Foundation.h>
#import <Security/Security.h>
#include "base/bind.h"
#include "base/callback.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/macros.h"
#include "base/strings/sys_string_conversions.h"
#include "device/fido/mac/credential_metadata.h"
#include "device/fido/mac/operation.h"
#include "device/fido/mac/touch_id_context.h"
namespace device {
namespace fido {
namespace mac {
// OperationBase abstracts behavior common to both concrete Operations,
// |MakeCredentialOperation| and |GetAssertionOperation|.
template <class Request, class Response>
class API_AVAILABLE(macosx(10.12.2)) OperationBase : public Operation {
public:
using Callback = base::OnceCallback<void(CtapDeviceResponseCode,
base::Optional<Response>)>;
OperationBase(Request request,
std::string metadata_secret,
std::string keychain_access_group,
Callback callback)
: request_(std::move(request)),
metadata_secret_(std::move(metadata_secret)),
keychain_access_group_(std::move(keychain_access_group)),
callback_(std::move(callback)),
touch_id_context_(TouchIdContext::Create()) {}
~OperationBase() override = default;
protected:
// Subclasses must call Init() at the beginning of Run().
void Init() { encoded_rp_id_ = EncodeRpId(metadata_secret(), RpId()); }
// PromptTouchId triggers a Touch ID consent dialog with the given reason
// string. Subclasses implement the PromptTouchIdDone callback to receive the
// result.
void PromptTouchId(const base::string16& reason) {
// The callback passed to TouchIdContext::Prompt will not fire if the
// TouchIdContext itself has been deleted. Since that it is owned by this
// class, there is no need to bind the callback to a weak ref here.
touch_id_context_->PromptTouchId(
reason, base::BindOnce(&OperationBase::PromptTouchIdDone,
base::Unretained(this)));
}
// Callback for |PromptTouchId|.
virtual void PromptTouchIdDone(bool success) = 0;
// Subclasses override RpId to return the RP ID from the type-specific
// request.
virtual const std::string& RpId() const = 0;
LAContext* authentication_context() const {
return touch_id_context_->authentication_context();
}
SecAccessControlRef access_control() const {
return touch_id_context_->access_control();
}
// DefaultKeychainQuery returns a default keychain query dictionary that has
// the keychain item class, keychain access group and RP ID filled out (but
// not the credential ID). More fields can be set on the return value to
// refine the query.
base::ScopedCFTypeRef<CFMutableDictionaryRef> DefaultKeychainQuery() const {
base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr));
CFDictionarySetValue(query, kSecClass, kSecClassKey);
CFDictionarySetValue(query, kSecAttrAccessGroup,
base::SysUTF8ToNSString(keychain_access_group_));
DCHECK(!encoded_rp_id_.empty());
CFDictionarySetValue(query, kSecAttrLabel,
base::SysUTF8ToNSString(encoded_rp_id_));
return query;
}
const std::string& metadata_secret() const { return metadata_secret_; }
const std::string& keychain_access_group() const {
return keychain_access_group_;
}
const Request& request() const { return request_; }
Callback& callback() { return callback_; }
private:
Request request_;
// The secret parameter passed to |CredentialMetadata| operations to encrypt
// or encode credential metadata for storage in the macOS keychain.
std::string metadata_secret_;
std::string keychain_access_group_;
std::string encoded_rp_id_ = "";
Callback callback_;
std::unique_ptr<TouchIdContext> touch_id_context_;
};
} // namespace mac
} // namespace fido
} // namespace device
#endif // DEVICE_FIDO_MAC_OPERATION_BASE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment