blob: d196ea59c6fa183387df7b06c3c9f0f1d0c6ecb9 [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/touch_id_context.h"
#import <Foundation/Foundation.h>
#include "base/bind.h"
#include "base/logging.h"
#include "base/mac/foundation_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() {
return base::ScopedCFTypeRef<SecAccessControlRef>(
SecAccessControlCreateWithFlags(
kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAccessControlPrivateKeyUsage | kSecAccessControlTouchIDAny,
nullptr));
}
} // namespace
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(std::string 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::SysUTF8ToNSString(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