blob: 90a5f7b3bb11f40f9555882c0b8bf2a10a2e938f [file] [log] [blame]
/*
* 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)