| /* |
| * Copyright (C) 2018 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| #import "LocalConnection.h" |
| |
| #if ENABLE(WEB_AUTHN) |
| |
| #import "DeviceIdentitySPI.h" |
| #import <WebCore/ExceptionData.h> |
| #import <wtf/BlockPtr.h> |
| #import <wtf/RunLoop.h> |
| |
| #import "LocalAuthenticationSoftLink.h" |
| |
| namespace WebKit { |
| |
| void LocalConnection::getUserConsent(const String& reason, UserConsentCallback&& completionHandler) const |
| { |
| auto context = adoptNS([allocLAContextInstance() init]); |
| auto reply = makeBlockPtr([completionHandler = WTFMove(completionHandler)] (BOOL success, NSError *error) mutable { |
| ASSERT(!RunLoop::isMain()); |
| |
| UserConsent consent = UserConsent::Yes; |
| if (!success || error) { |
| LOG_ERROR("Couldn't authenticate with biometrics: %@", error); |
| consent = UserConsent::No; |
| } |
| RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), consent]() mutable { |
| completionHandler(consent); |
| }); |
| }); |
| [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:reason reply:reply.get()]; |
| } |
| |
| void LocalConnection::getUserConsent(const String& reason, SecAccessControlRef accessControl, UserConsentContextCallback&& completionHandler) const |
| { |
| auto context = adoptNS([allocLAContextInstance() init]); |
| auto reply = makeBlockPtr([context, completionHandler = WTFMove(completionHandler)] (BOOL success, NSError *error) mutable { |
| ASSERT(!RunLoop::isMain()); |
| |
| UserConsent consent = UserConsent::Yes; |
| if (!success || error) { |
| LOG_ERROR("Couldn't authenticate with biometrics: %@", error); |
| consent = UserConsent::No; |
| } |
| RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), consent, context = WTFMove(context)]() mutable { |
| completionHandler(consent, context.get()); |
| }); |
| }); |
| [context evaluateAccessControl:accessControl operation:LAAccessControlOperationUseKeySign localizedReason:reason reply:reply.get()]; |
| } |
| |
| void LocalConnection::getAttestation(const String& rpId, const String& username, const Vector<uint8_t>& hash, AttestationCallback&& completionHandler) const |
| { |
| #if HAVE(DEVICE_IDENTITY) |
| // Apple Attestation |
| ASSERT(hash.size() <= 32); |
| |
| RetainPtr<SecAccessControlRef> accessControlRef; |
| { |
| CFErrorRef errorRef = nullptr; |
| accessControlRef = adoptCF(SecAccessControlCreateWithFlags(NULL, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlPrivateKeyUsage | kSecAccessControlUserPresence, &errorRef)); |
| auto retainError = adoptCF(errorRef); |
| if (errorRef) { |
| LOG_ERROR("Couldn't create ACL: %@", (NSError *)errorRef); |
| completionHandler(NULL, NULL, [NSError errorWithDomain:@"com.apple.WebKit.WebAuthN" code:1 userInfo:nil]); |
| return; |
| } |
| } |
| |
| String label = makeString(username, "@", rpId); |
| NSDictionary *options = @{ |
| kMAOptionsBAAKeychainLabel: label, |
| // FIXME(rdar://problem/38489134): Need a formal name. |
| kMAOptionsBAAKeychainAccessGroup: @"com.apple.safari.WebAuthN.credentials", |
| kMAOptionsBAAIgnoreExistingKeychainItems: @YES, |
| // FIXME(rdar://problem/38489134): Determine a proper lifespan. |
| kMAOptionsBAAValidity: @(1440), // Last one day. |
| kMAOptionsBAASCRTAttestation: @NO, |
| kMAOptionsBAANonce: [NSData dataWithBytes:hash.data() length:hash.size()], |
| kMAOptionsBAAAccessControls: (id)accessControlRef.get(), |
| kMAOptionsBAAOIDSToInclude: @[kMAOptionsBAAOIDNonce] |
| }; |
| |
| // FIXME(183652): Reduce prompt for biometrics |
| DeviceIdentityIssueClientCertificateWithCompletion(dispatch_get_main_queue(), options, makeBlockPtr(WTFMove(completionHandler)).get()); |
| #endif // HAVE(DEVICE_IDENTITY) |
| } |
| |
| NSDictionary *LocalConnection::selectCredential(const NSArray *credentials) const |
| { |
| // FIXME(rdar://problem/35900534): We don't have an UI to prompt users for selecting intersectedCredentials, and therefore we always use the first one for now. |
| return credentials[0]; |
| } |
| |
| } // namespace WebKit |
| |
| #endif // ENABLE(WEB_AUTHN) |