| // 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/touch_id_context.h" |
| |
| #import <Foundation/Foundation.h> |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| |
| namespace device { |
| namespace fido { |
| namespace mac { |
| |
| namespace { |
| API_AVAILABLE(macosx(10.12.2)) |
| base::ScopedCFTypeRef<SecAccessControlRef> DefaultAccessControl() { |
| // The default access control policy used for WebAuthn credentials stored by |
| // the Touch ID platform authenticator. |
| return base::ScopedCFTypeRef<SecAccessControlRef>( |
| SecAccessControlCreateWithFlags( |
| kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, |
| kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence, |
| nullptr)); |
| } |
| } // namespace |
| |
| // static |
| std::unique_ptr<TouchIdContext> TouchIdContext::CreateImpl() { |
| return base::WrapUnique(new TouchIdContext()); |
| } |
| |
| // static |
| TouchIdContext::CreateFuncPtr TouchIdContext::g_create_ = |
| &TouchIdContext::CreateImpl; |
| |
| // static |
| std::unique_ptr<TouchIdContext> TouchIdContext::Create() { |
| // Testing seam to allow faking Touch ID in tests. |
| return (*g_create_)(); |
| } |
| |
| // static |
| bool TouchIdContext::TouchIdAvailableImpl() { |
| base::scoped_nsobject<LAContext> context([[LAContext alloc] init]); |
| return |
| [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics |
| error:nil]; |
| } |
| |
| // static |
| TouchIdContext::TouchIdAvailableFuncPtr TouchIdContext::g_touch_id_available_ = |
| &TouchIdContext::TouchIdAvailableImpl; |
| |
| // static |
| bool TouchIdContext::TouchIdAvailable() { |
| // Testing seam to allow faking Touch ID in tests. |
| return (*g_touch_id_available_)(); |
| } |
| |
| TouchIdContext::TouchIdContext() |
| : context_([[LAContext alloc] init]), |
| access_control_(DefaultAccessControl()), |
| callback_(), |
| weak_ptr_factory_(this) {} |
| |
| TouchIdContext::~TouchIdContext() { |
| // Invalidating the LAContext will dismiss any pending UI dialog (e.g. if the |
| // transaction was cancelled while we are waiting for a fingerprint). Simply |
| // releasing the LAContext does not have the same effect. |
| [context_ invalidate]; |
| } |
| |
| void TouchIdContext::PromptTouchId(const base::string16& reason, |
| Callback callback) { |
| callback_ = std::move(callback); |
| scoped_refptr<base::SequencedTaskRunner> runner = |
| base::SequencedTaskRunnerHandle::Get(); |
| auto weak_self = weak_ptr_factory_.GetWeakPtr(); |
| // If evaluation succeeds (i.e. user provides a fingerprint), |context_| can |
| // be used for one signing operation. N.B. even in |MakeCredentialOperation|, |
| // we need to perform a signature for the attestation statement, so we need |
| // the sign bit there. |
| [context_ evaluateAccessControl:access_control_ |
| operation:LAAccessControlOperationUseKeySign |
| localizedReason:base::SysUTF16ToNSString(reason) |
| reply:^(BOOL success, NSError* error) { |
| // The reply block is invoked in a separate |
| // thread. We want to invoke the callback in the |
| // original thread, so we post it onto the |
| // originating runner. The weak_self and runner |
| // variables inside the block are const-copies of |
| // the ones in the enclosing scope (c.f. |
| // http://clang.llvm.org/docs/Block-ABI-Apple.html#imported-variables). |
| if (!success) { |
| // |error| is autoreleased in this block. It |
| // is not currently passed onto the other |
| // thread running the callback; but if it |
| // were, it would have to be retained first |
| // (and probably captured in a |
| // scoped_nsobject<NSError>). |
| DCHECK(error != nil); |
| DVLOG(1) << "Touch ID prompt failed: " |
| << base::mac::NSToCFCast(error); |
| } |
| runner->PostTask( |
| FROM_HERE, |
| base::BindOnce(&TouchIdContext::RunCallback, |
| weak_self, success)); |
| }]; |
| } |
| |
| void TouchIdContext::RunCallback(bool success) { |
| std::move(callback_).Run(success); |
| } |
| |
| } // namespace mac |
| } // namespace fido |
| } // namespace device |