blob: d96fdc2f6fa0f30e70b1d3db2125f00d9a329012 [file] [log] [blame]
/*
* Copyright (C) 2019-2021 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 "_WKWebAuthenticationPanelInternal.h"
#import "LocalAuthenticator.h"
#import "LocalService.h"
#import "WKError.h"
#import "WebAuthenticationPanelClient.h"
#import "_WKAuthenticationExtensionsClientInputs.h"
#import "_WKAuthenticationExtensionsClientOutputsInternal.h"
#import "_WKAuthenticatorAssertionResponseInternal.h"
#import "_WKAuthenticatorAttestationResponseInternal.h"
#import "_WKAuthenticatorSelectionCriteria.h"
#import "_WKPublicKeyCredentialCreationOptions.h"
#import "_WKPublicKeyCredentialDescriptor.h"
#import "_WKPublicKeyCredentialParameters.h"
#import "_WKPublicKeyCredentialRequestOptions.h"
#import "_WKPublicKeyCredentialRelyingPartyEntity.h"
#import "_WKPublicKeyCredentialUserEntity.h"
#import <WebCore/AuthenticatorAttachment.h>
#import <WebCore/AuthenticatorResponse.h>
#import <WebCore/AuthenticatorResponseData.h>
#import <WebCore/BufferSource.h>
#import <WebCore/CBORReader.h>
#import <WebCore/CBORWriter.h>
#import <WebCore/DeviceRequestConverter.h>
#import <WebCore/FidoConstants.h>
#import <WebCore/MockWebAuthenticationConfiguration.h>
#import <WebCore/PublicKeyCredentialCreationOptions.h>
#import <WebCore/PublicKeyCredentialRequestOptions.h>
#import <WebCore/WebAuthenticationConstants.h>
#import <WebCore/WebAuthenticationUtils.h>
#import <objc/runtime.h>
#import <pal/crypto/CryptoDigest.h>
#import <wtf/BlockPtr.h>
#import <wtf/RetainPtr.h>
#import <wtf/cocoa/TypeCastsCocoa.h>
#import <wtf/cocoa/VectorCocoa.h>
#import <wtf/text/Base64.h>
#if ENABLE(WEB_AUTHN)
#if USE(APPLE_INTERNAL_SDK)
#import <WebKitAdditions/LocalAuthenticatorAdditions.h>
#else
static void updateQueryIfNecessary(NSMutableDictionary *)
{
}
#endif
static RetainPtr<NSData> produceClientDataJson(_WKWebAuthenticationType type, NSData *challenge, NSString *origin)
{
WebCore::ClientDataType clientDataType;
switch (type) {
case _WKWebAuthenticationTypeCreate:
clientDataType = WebCore::ClientDataType::Create;
break;
case _WKWebAuthenticationTypeGet:
clientDataType = WebCore::ClientDataType::Get;
break;
}
auto challengeBuffer = ArrayBuffer::tryCreate(reinterpret_cast<const uint8_t*>(challenge.bytes), challenge.length);
auto securityOrigin = WebCore::SecurityOrigin::createFromString(origin);
auto clientDataJson = buildClientDataJson(clientDataType, WebCore::BufferSource(challengeBuffer), securityOrigin, WebAuthn::Scope::SameOrigin);
return adoptNS([[NSData alloc] initWithBytes:clientDataJson->data() length:clientDataJson->byteLength()]);
}
static Vector<uint8_t> produceClientDataJsonHash(NSData *clientDataJson)
{
auto crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
crypto->addBytes(clientDataJson.bytes, clientDataJson.length);
return crypto->computeHash();
}
#endif
NSString * const _WKLocalAuthenticatorCredentialNameKey = @"_WKLocalAuthenticatorCredentialNameKey";
NSString * const _WKLocalAuthenticatorCredentialDisplayNameKey = @"_WKLocalAuthenticatorCredentialDisplayNameKey";
NSString * const _WKLocalAuthenticatorCredentialIDKey = @"_WKLocalAuthenticatorCredentialIDKey";
NSString * const _WKLocalAuthenticatorCredentialRelyingPartyIDKey = @"_WKLocalAuthenticatorCredentialRelyingPartyIDKey";
NSString * const _WKLocalAuthenticatorCredentialLastModificationDateKey = @"_WKLocalAuthenticatorCredentialLastModificationDateKey";
NSString * const _WKLocalAuthenticatorCredentialCreationDateKey = @"_WKLocalAuthenticatorCredentialCreationDateKey";
@implementation _WKWebAuthenticationPanel {
#if ENABLE(WEB_AUTHN)
WeakPtr<WebKit::WebAuthenticationPanelClient> _client;
RetainPtr<NSMutableSet> _transports;
#endif
}
- (instancetype)init
{
if (!(self = [super init]))
return nil;
#if ENABLE(WEB_AUTHN)
API::Object::constructInWrapper<API::WebAuthenticationPanel>(self);
#endif
return self;
}
#if ENABLE(WEB_AUTHN)
- (void)dealloc
{
if (WebCoreObjCScheduleDeallocateOnMainRunLoop(_WKWebAuthenticationPanel.class, self))
return;
_panel->~WebAuthenticationPanel();
[super dealloc];
}
- (id <_WKWebAuthenticationPanelDelegate>)delegate
{
if (!_client)
return nil;
return _client->delegate().autorelease();
}
- (void)setDelegate:(id<_WKWebAuthenticationPanelDelegate>)delegate
{
auto client = WTF::makeUniqueRef<WebKit::WebAuthenticationPanelClient>(self, delegate);
_client = client.get();
_panel->setClient(WTFMove(client));
}
- (NSString *)relyingPartyID
{
return _panel->rpId();
}
static _WKWebAuthenticationTransport wkWebAuthenticationTransport(WebCore::AuthenticatorTransport transport)
{
switch (transport) {
case WebCore::AuthenticatorTransport::Usb:
return _WKWebAuthenticationTransportUSB;
case WebCore::AuthenticatorTransport::Nfc:
return _WKWebAuthenticationTransportNFC;
case WebCore::AuthenticatorTransport::Internal:
return _WKWebAuthenticationTransportInternal;
default:
ASSERT_NOT_REACHED();
return _WKWebAuthenticationTransportUSB;
}
}
- (NSSet *)transports
{
if (_transports)
return _transports.get();
auto& transports = _panel->transports();
_transports = [[NSMutableSet alloc] initWithCapacity:transports.size()];
for (auto& transport : transports)
[_transports addObject:adoptNS([[NSNumber alloc] initWithInt:wkWebAuthenticationTransport(transport)]).get()];
return _transports.get();
}
static _WKWebAuthenticationType wkWebAuthenticationType(WebCore::ClientDataType type)
{
switch (type) {
case WebCore::ClientDataType::Create:
return _WKWebAuthenticationTypeCreate;
case WebCore::ClientDataType::Get:
return _WKWebAuthenticationTypeGet;
default:
ASSERT_NOT_REACHED();
return _WKWebAuthenticationTypeCreate;
}
}
static fido::AuthenticatorSupportedOptions::UserVerificationAvailability coreUserVerificationAvailability(_WKWebAuthenticationUserVerificationAvailability wkAvailability)
{
switch (wkAvailability) {
case _WKWebAuthenticationUserVerificationAvailabilitySupportedAndConfigured:
return fido::AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured;
case _WKWebAuthenticationUserVerificationAvailabilitySupportedButNotConfigured:
return fido::AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedButNotConfigured;
case _WKWebAuthenticationUserVerificationAvailabilityNotSupported:
return fido::AuthenticatorSupportedOptions::UserVerificationAvailability::kNotSupported;
}
ASSERT_NOT_REACHED();
return fido::AuthenticatorSupportedOptions::UserVerificationAvailability::kNotSupported;
}
- (_WKWebAuthenticationType)type
{
return wkWebAuthenticationType(_panel->clientDataType());
}
- (NSString *)userName
{
return _panel->userName();
}
#else // ENABLE(WEB_AUTHN)
- (id <_WKWebAuthenticationPanelDelegate>)delegate
{
return nil;
}
- (void)setDelegate:(id<_WKWebAuthenticationPanelDelegate>)delegate
{
}
#endif // ENABLE(WEB_AUTHN)
#if ENABLE(WEB_AUTHN)
static RetainPtr<NSArray> getAllLocalAuthenticatorCredentialsImpl(NSString *accessGroup)
{
auto query = adoptNS([[NSMutableDictionary alloc] init]);
[query setDictionary:@{
(__bridge id)kSecClass: bridge_id_cast(kSecClassKey),
(__bridge id)kSecAttrKeyClass: bridge_id_cast(kSecAttrKeyClassPrivate),
(__bridge id)kSecAttrAccessGroup: accessGroup,
(__bridge id)kSecReturnAttributes: @YES,
(__bridge id)kSecMatchLimit: bridge_id_cast(kSecMatchLimitAll),
(__bridge id)kSecUseDataProtectionKeychain: @YES
}];
updateQueryIfNecessary(query.get());
CFTypeRef attributesArrayRef = nullptr;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query.get(), &attributesArrayRef);
if (status && status != errSecItemNotFound)
return nullptr;
auto retainAttributesArray = adoptCF(attributesArrayRef);
auto result = adoptNS([[NSMutableArray alloc] init]);
for (NSDictionary *attributes in (NSArray *)attributesArrayRef) {
auto decodedResponse = cbor::CBORReader::read(vectorFromNSData(attributes[bridge_id_cast(kSecAttrApplicationTag)]));
if (!decodedResponse || !decodedResponse->isMap()) {
ASSERT_NOT_REACHED();
return nullptr;
}
auto& responseMap = decodedResponse->getMap();
auto it = responseMap.find(cbor::CBORValue(fido::kEntityNameMapKey));
if (it == responseMap.end() || !it->second.isString()) {
ASSERT_NOT_REACHED();
return nullptr;
}
auto& username = it->second.getString();
auto credential = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys:
username, _WKLocalAuthenticatorCredentialNameKey,
attributes[bridge_cast(kSecAttrApplicationLabel)], _WKLocalAuthenticatorCredentialIDKey,
attributes[bridge_cast(kSecAttrLabel)], _WKLocalAuthenticatorCredentialRelyingPartyIDKey,
attributes[bridge_cast(kSecAttrModificationDate)], _WKLocalAuthenticatorCredentialLastModificationDateKey,
attributes[bridge_cast(kSecAttrCreationDate)], _WKLocalAuthenticatorCredentialCreationDateKey,
nil
]);
it = responseMap.find(cbor::CBORValue(fido::kDisplayNameMapKey));
if (it != responseMap.end() && it->second.isString())
[credential setObject:it->second.getString() forKey:_WKLocalAuthenticatorCredentialDisplayNameKey];
[result addObject:credential.get()];
}
return result;
}
#endif
+ (NSArray<NSDictionary *> *)getAllLocalAuthenticatorCredentials
{
#if ENABLE(WEB_AUTHN)
return getAllLocalAuthenticatorCredentialsImpl(String(WebCore::LocalAuthenticatiorAccessGroup)).autorelease();
#else
return nullptr;
#endif
}
+ (NSArray<NSDictionary *> *)getAllLocalAuthenticatorCredentialsWithAccessGroup:(NSString *)accessGroup
{
#if ENABLE(WEB_AUTHN)
return getAllLocalAuthenticatorCredentialsImpl(accessGroup).autorelease();
#else
return nullptr;
#endif
}
+ (void)deleteLocalAuthenticatorCredentialWithID:(NSData *)credentialID
{
#if ENABLE(WEB_AUTHN)
auto deleteQuery = adoptNS([[NSMutableDictionary alloc] init]);
[deleteQuery setDictionary:@{
(__bridge id)kSecClass: bridge_id_cast(kSecClassKey),
(__bridge id)kSecAttrApplicationLabel: credentialID,
(__bridge id)kSecUseDataProtectionKeychain: @YES
}];
updateQueryIfNecessary(deleteQuery.get());
SecItemDelete((__bridge CFDictionaryRef)deleteQuery.get());
#endif
}
+ (void)clearAllLocalAuthenticatorCredentials
{
#if ENABLE(WEB_AUTHN)
WebKit::LocalAuthenticator::clearAllCredentials();
#endif
}
+ (void)setUsernameForLocalCredentialWithID:(NSData *)credentialID username: (NSString *)username
{
#if ENABLE(WEB_AUTHN)
auto query = adoptNS([[NSMutableDictionary alloc] init]);
[query setDictionary:@{
(__bridge id)kSecClass: bridge_id_cast(kSecClassKey),
(__bridge id)kSecReturnAttributes: @YES,
(__bridge id)kSecAttrApplicationLabel: credentialID,
(__bridge id)kSecReturnPersistentRef : bridge_id_cast(kCFBooleanTrue),
(__bridge id)kSecUseDataProtectionKeychain: @YES
}];
updateQueryIfNecessary(query.get());
CFTypeRef attributesArrayRef = nullptr;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query.get(), &attributesArrayRef);
if (status && status != errSecItemNotFound) {
ASSERT_NOT_REACHED();
return;
}
NSDictionary *attributes = (__bridge NSDictionary *)attributesArrayRef;
auto decodedResponse = cbor::CBORReader::read(vectorFromNSData(attributes[bridge_id_cast(kSecAttrApplicationTag)]));
if (!decodedResponse || !decodedResponse->isMap()) {
ASSERT_NOT_REACHED();
return;
}
auto& previousUserMap = decodedResponse->getMap();
bool usernameSet = false;
cbor::CBORValue::MapValue updatedUserMap;
for (auto it = previousUserMap.begin(); it != previousUserMap.end(); ++it) {
if (it->first.isString() && it->first.getString() == fido::kEntityNameMapKey) {
updatedUserMap[it->first.clone()] = cbor::CBORValue(String(username));
usernameSet = true;
} else
updatedUserMap[it->first.clone()] = it->second.clone();
}
if (!usernameSet) {
ASSERT_NOT_REACHED();
return;
}
auto updatedTag = cbor::CBORWriter::write(cbor::CBORValue(WTFMove(updatedUserMap)));
auto secAttrApplicationTag = adoptNS([[NSData alloc] initWithBytes:updatedTag->data() length:updatedTag->size()]);
NSDictionary *updateParams = @{
(__bridge id)kSecAttrApplicationTag: secAttrApplicationTag.get(),
};
[query setDictionary:@{
(__bridge id)kSecValuePersistentRef: [attributes objectForKey:bridge_id_cast(kSecValuePersistentRef)],
(__bridge id)kSecClass: bridge_id_cast(kSecClassKey),
}];
updateQueryIfNecessary(query.get());
status = SecItemUpdate((__bridge CFDictionaryRef)query.get(), (__bridge CFDictionaryRef)updateParams);
if (status && status != errSecItemNotFound) {
ASSERT_NOT_REACHED();
return;
}
#endif
}
- (void)cancel
{
#if ENABLE(WEB_AUTHN)
_panel->cancel();
#endif
}
#if ENABLE(WEB_AUTHN)
static WebCore::PublicKeyCredentialCreationOptions::RpEntity publicKeyCredentialRpEntity(_WKPublicKeyCredentialRelyingPartyEntity *rpEntity)
{
WebCore::PublicKeyCredentialCreationOptions::RpEntity result;
result.name = rpEntity.name;
result.icon = rpEntity.icon;
result.id = rpEntity.identifier;
return result;
}
static WebCore::PublicKeyCredentialCreationOptions::UserEntity publicKeyCredentialUserEntity(_WKPublicKeyCredentialUserEntity *userEntity)
{
WebCore::PublicKeyCredentialCreationOptions::UserEntity result;
result.name = userEntity.name;
result.icon = userEntity.icon;
result.id = WebCore::toBufferSource(userEntity.identifier);
result.displayName = userEntity.displayName;
return result;
}
static Vector<WebCore::PublicKeyCredentialCreationOptions::Parameters> publicKeyCredentialParameters(NSArray<_WKPublicKeyCredentialParameters *> *publicKeyCredentialParamaters)
{
Vector<WebCore::PublicKeyCredentialCreationOptions::Parameters> result;
result.reserveInitialCapacity(publicKeyCredentialParamaters.count);
for (_WKPublicKeyCredentialParameters *param : publicKeyCredentialParamaters)
result.uncheckedAppend({ WebCore::PublicKeyCredentialType::PublicKey, param.algorithm.longLongValue });
return result;
}
static WebCore::AuthenticatorTransport authenticatorTransport(_WKWebAuthenticationTransport transport)
{
switch (transport) {
case _WKWebAuthenticationTransportUSB:
return WebCore::AuthenticatorTransport::Usb;
case _WKWebAuthenticationTransportNFC:
return WebCore::AuthenticatorTransport::Nfc;
case _WKWebAuthenticationTransportInternal:
return WebCore::AuthenticatorTransport::Internal;
default:
ASSERT_NOT_REACHED();
return WebCore::AuthenticatorTransport::Usb;
}
}
static Vector<WebCore::AuthenticatorTransport> authenticatorTransports(NSArray<NSNumber *> *transports)
{
Vector<WebCore::AuthenticatorTransport> result;
result.reserveInitialCapacity(transports.count);
for (NSNumber *transport : transports)
result.uncheckedAppend(authenticatorTransport((_WKWebAuthenticationTransport)transport.intValue));
return result;
}
static Vector<WebCore::PublicKeyCredentialDescriptor> publicKeyCredentialDescriptors(NSArray<_WKPublicKeyCredentialDescriptor *> *credentials)
{
Vector<WebCore::PublicKeyCredentialDescriptor> result;
result.reserveInitialCapacity(credentials.count);
for (_WKPublicKeyCredentialDescriptor *credential : credentials)
result.uncheckedAppend({ WebCore::PublicKeyCredentialType::PublicKey, WebCore::toBufferSource(credential.identifier), authenticatorTransports(credential.transports) });
return result;
}
static std::optional<WebCore::AuthenticatorAttachment> authenticatorAttachment(_WKAuthenticatorAttachment attachment)
{
switch (attachment) {
case _WKAuthenticatorAttachmentAll:
return std::nullopt;
case _WKAuthenticatorAttachmentPlatform:
return WebCore::AuthenticatorAttachment::Platform;
case _WKAuthenticatorAttachmentCrossPlatform:
return WebCore::AuthenticatorAttachment::CrossPlatform;
default:
ASSERT_NOT_REACHED();
return std::nullopt;
}
}
static WebCore::UserVerificationRequirement userVerification(_WKUserVerificationRequirement uv)
{
switch (uv) {
case _WKUserVerificationRequirementRequired:
return WebCore::UserVerificationRequirement::Required;
case _WKUserVerificationRequirementPreferred:
return WebCore::UserVerificationRequirement::Preferred;
case _WKUserVerificationRequirementDiscouraged:
return WebCore::UserVerificationRequirement::Discouraged;
default:
ASSERT_NOT_REACHED();
return WebCore::UserVerificationRequirement::Preferred;
}
}
static WebCore::PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria authenticatorSelectionCriteria(_WKAuthenticatorSelectionCriteria *authenticatorSelection)
{
WebCore::PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria result;
result.authenticatorAttachment = authenticatorAttachment(authenticatorSelection.authenticatorAttachment);
result.requireResidentKey = authenticatorSelection.requireResidentKey;
result.userVerification = userVerification(authenticatorSelection.userVerification);
return result;
}
static WebCore::AttestationConveyancePreference attestationConveyancePreference(_WKAttestationConveyancePreference attestation)
{
switch (attestation) {
case _WKAttestationConveyancePreferenceNone:
return WebCore::AttestationConveyancePreference::None;
case _WKAttestationConveyancePreferenceIndirect:
return WebCore::AttestationConveyancePreference::Indirect;
case _WKAttestationConveyancePreferenceDirect:
return WebCore::AttestationConveyancePreference::Direct;
default:
ASSERT_NOT_REACHED();
return WebCore::AttestationConveyancePreference::None;
}
}
static WebCore::AuthenticationExtensionsClientInputs authenticationExtensionsClientInputs(_WKAuthenticationExtensionsClientInputs *extensions)
{
WebCore::AuthenticationExtensionsClientInputs result;
result.appid = extensions.appid;
result.googleLegacyAppidSupport = extensions.googleLegacyAppidSupport;
return result;
}
#endif
+ (WebCore::PublicKeyCredentialCreationOptions)convertToCoreCreationOptionsWithOptions:(_WKPublicKeyCredentialCreationOptions *)options
{
WebCore::PublicKeyCredentialCreationOptions result;
#if ENABLE(WEB_AUTHN)
result.rp = publicKeyCredentialRpEntity(options.relyingParty);
result.user = publicKeyCredentialUserEntity(options.user);
result.pubKeyCredParams = publicKeyCredentialParameters(options.publicKeyCredentialParamaters);
if (options.timeout)
result.timeout = options.timeout.unsignedIntValue;
if (options.excludeCredentials)
result.excludeCredentials = publicKeyCredentialDescriptors(options.excludeCredentials);
if (options.authenticatorSelection)
result.authenticatorSelection = authenticatorSelectionCriteria(options.authenticatorSelection);
result.attestation = attestationConveyancePreference(options.attestation);
result.extensions = authenticationExtensionsClientInputs(options.extensions);
#endif
return result;
}
#if ENABLE(WEB_AUTHN)
static _WKAuthenticatorAttachment authenticatorAttachmentToWKAuthenticatorAttachment(WebCore::AuthenticatorAttachment attachment)
{
switch (attachment) {
case WebCore::AuthenticatorAttachment::Platform:
return _WKAuthenticatorAttachmentPlatform;
case WebCore::AuthenticatorAttachment::CrossPlatform:
return _WKAuthenticatorAttachmentCrossPlatform;
}
}
static RetainPtr<_WKAuthenticatorAttestationResponse> wkAuthenticatorAttestationResponse(const WebCore::AuthenticatorResponseData& data, NSData *clientDataJSON, WebCore::AuthenticatorAttachment attachment)
{
return adoptNS([[_WKAuthenticatorAttestationResponse alloc] initWithClientDataJSON:clientDataJSON rawId:[NSData dataWithBytes:data.rawId->data() length:data.rawId->byteLength()] extensions:nil attestationObject:[NSData dataWithBytes:data.attestationObject->data() length:data.attestationObject->byteLength()] attachment: authenticatorAttachmentToWKAuthenticatorAttachment(attachment)]);
}
#endif
- (void)makeCredentialWithChallenge:(NSData *)challenge origin:(NSString *)origin options:(_WKPublicKeyCredentialCreationOptions *)options completionHandler:(void (^)(_WKAuthenticatorAttestationResponse *, NSError *))handler
{
#if ENABLE(WEB_AUTHN)
auto clientDataJSON = produceClientDataJson(_WKWebAuthenticationTypeCreate, challenge, origin);
auto hash = produceClientDataJsonHash(clientDataJSON.get());
auto callback = [handler = makeBlockPtr(handler), clientDataJSON = WTFMove(clientDataJSON)] (std::variant<Ref<WebCore::AuthenticatorResponse>, WebCore::ExceptionData>&& result) mutable {
WTF::switchOn(result, [&](const Ref<WebCore::AuthenticatorResponse>& response) {
handler(wkAuthenticatorAttestationResponse(response->data(), clientDataJSON.get(), response->attachment()).get(), nil);
}, [&](const WebCore::ExceptionData& exception) {
handler(nil, [NSError errorWithDomain:WKErrorDomain code:exception.code userInfo:@{ NSLocalizedDescriptionKey: exception.message }]);
});
};
_panel->handleRequest({ WTFMove(hash), [_WKWebAuthenticationPanel convertToCoreCreationOptionsWithOptions:options], nullptr, WebKit::WebAuthenticationPanelResult::Unavailable, nullptr, std::nullopt, { }, true, String(), nullptr }, WTFMove(callback));
#endif
}
- (void)makeCredentialWithClientDataHash:(NSData *)clientDataHash options:(_WKPublicKeyCredentialCreationOptions *)options completionHandler:(void (^)(_WKAuthenticatorAttestationResponse *, NSError *))handler
{
#if ENABLE(WEB_AUTHN)
auto callback = [handler = makeBlockPtr(handler)] (std::variant<Ref<WebCore::AuthenticatorResponse>, WebCore::ExceptionData>&& result) mutable {
WTF::switchOn(result, [&](const Ref<WebCore::AuthenticatorResponse>& response) {
handler(wkAuthenticatorAttestationResponse(response->data(), nullptr, response->attachment()).get(), nil);
}, [&](const WebCore::ExceptionData& exception) {
handler(nil, [NSError errorWithDomain:WKErrorDomain code:exception.code userInfo:@{ NSLocalizedDescriptionKey: exception.message }]);
});
};
_panel->handleRequest({ vectorFromNSData(clientDataHash), [_WKWebAuthenticationPanel convertToCoreCreationOptionsWithOptions:options], nullptr, WebKit::WebAuthenticationPanelResult::Unavailable, nullptr, std::nullopt, { }, true, String(), nullptr }, WTFMove(callback));
#endif
}
+ (WebCore::PublicKeyCredentialRequestOptions)convertToCoreRequestOptionsWithOptions:(_WKPublicKeyCredentialRequestOptions *)options
{
WebCore::PublicKeyCredentialRequestOptions result;
#if ENABLE(WEB_AUTHN)
if (options.timeout)
result.timeout = options.timeout.unsignedIntValue;
if (options.relyingPartyIdentifier)
result.rpId = options.relyingPartyIdentifier;
if (options.allowCredentials)
result.allowCredentials = publicKeyCredentialDescriptors(options.allowCredentials);
result.userVerification = userVerification(options.userVerification);
result.authenticatorAttachment = authenticatorAttachment(options.authenticatorAttachment);
result.extensions = authenticationExtensionsClientInputs(options.extensions);
#endif
return result;
}
#if ENABLE(WEB_AUTHN)
static RetainPtr<_WKAuthenticatorAssertionResponse> wkAuthenticatorAssertionResponse(const WebCore::AuthenticatorResponseData& data, NSData *clientDataJSON, WebCore::AuthenticatorAttachment attachment)
{
RetainPtr<_WKAuthenticationExtensionsClientOutputs> extensions;
if (data.appid)
extensions = adoptNS([[_WKAuthenticationExtensionsClientOutputs alloc] initWithAppid:data.appid.value()]);
NSData *userHandle = nil;
if (data.userHandle)
userHandle = [NSData dataWithBytes:data.userHandle->data() length:data.userHandle->byteLength()];
return adoptNS([[_WKAuthenticatorAssertionResponse alloc] initWithClientDataJSON:clientDataJSON rawId:[NSData dataWithBytes:data.rawId->data() length:data.rawId->byteLength()] extensions:WTFMove(extensions) authenticatorData:[NSData dataWithBytes:data.authenticatorData->data() length:data.authenticatorData->byteLength()] signature:[NSData dataWithBytes:data.signature->data() length:data.signature->byteLength()] userHandle:userHandle attachment:authenticatorAttachmentToWKAuthenticatorAttachment(attachment)]);
}
#endif
- (void)getAssertionWithChallenge:(NSData *)challenge origin:(NSString *)origin options:(_WKPublicKeyCredentialRequestOptions *)options completionHandler:(void (^)(_WKAuthenticatorAssertionResponse *, NSError *))handler
{
#if ENABLE(WEB_AUTHN)
auto clientDataJSON = produceClientDataJson(_WKWebAuthenticationTypeGet, challenge, origin);
auto hash = produceClientDataJsonHash(clientDataJSON.get());
auto callback = [handler = makeBlockPtr(handler), clientDataJSON = WTFMove(clientDataJSON)] (std::variant<Ref<WebCore::AuthenticatorResponse>, WebCore::ExceptionData>&& result) mutable {
WTF::switchOn(result, [&](const Ref<WebCore::AuthenticatorResponse>& response) {
handler(wkAuthenticatorAssertionResponse(response->data(), clientDataJSON.get(), response->attachment()).get(), nil);
}, [&](const WebCore::ExceptionData& exception) {
handler(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]);
});
};
_panel->handleRequest({ WTFMove(hash), [_WKWebAuthenticationPanel convertToCoreRequestOptionsWithOptions:options], nullptr, WebKit::WebAuthenticationPanelResult::Unavailable, nullptr, std::nullopt, { }, true, String(), nullptr }, WTFMove(callback));
#endif
}
- (void)getAssertionWithClientDataHash:(NSData *)clientDataHash options:(_WKPublicKeyCredentialRequestOptions *)options completionHandler:(void (^)(_WKAuthenticatorAssertionResponse *, NSError *))handler
{
#if ENABLE(WEB_AUTHN)
auto callback = [handler = makeBlockPtr(handler)] (std::variant<Ref<WebCore::AuthenticatorResponse>, WebCore::ExceptionData>&& result) mutable {
WTF::switchOn(result, [&](const Ref<WebCore::AuthenticatorResponse>& response) {
handler(wkAuthenticatorAssertionResponse(response->data(), nullptr, response->attachment()).get(), nil);
}, [&](const WebCore::ExceptionData& exception) {
handler(nil, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nil]);
});
};
_panel->handleRequest({ vectorFromNSData(clientDataHash), [_WKWebAuthenticationPanel convertToCoreRequestOptionsWithOptions:options], nullptr, WebKit::WebAuthenticationPanelResult::Unavailable, nullptr, std::nullopt, { }, true, String(), nullptr }, WTFMove(callback));
#endif
}
+ (BOOL)isUserVerifyingPlatformAuthenticatorAvailable
{
#if ENABLE(WEB_AUTHN)
return WebKit::LocalService::isAvailable();
#else
return NO;
#endif
}
+ (NSData *)getClientDataJSONForAuthenticationType:(_WKWebAuthenticationType)type challenge:(NSData *)challenge origin:(NSString *)origin
{
RetainPtr<NSData> clientDataJSON;
#if ENABLE(WEB_AUTHN)
clientDataJSON = produceClientDataJson(type, challenge, origin);
#endif
return clientDataJSON.autorelease();
}
+ (NSData *)encodeMakeCredentialCommandWithClientDataJSON:(NSData *)clientDataJSON options:(_WKPublicKeyCredentialCreationOptions *)options userVerificationAvailability:(_WKWebAuthenticationUserVerificationAvailability)userVerificationAvailability
{
RetainPtr<NSData> encodedCommand;
#if ENABLE(WEB_AUTHN)
auto hash = produceClientDataJsonHash(clientDataJSON);
auto encodedVector = fido::encodeMakeCredenitalRequestAsCBOR(hash, [_WKWebAuthenticationPanel convertToCoreCreationOptionsWithOptions:options], coreUserVerificationAvailability(userVerificationAvailability), std::nullopt);
encodedCommand = adoptNS([[NSData alloc] initWithBytes:encodedVector.data() length:encodedVector.size()]);
#endif
return encodedCommand.autorelease();
}
+ (NSData *)encodeGetAssertionCommandWithClientDataJSON:(NSData *)clientDataJSON options:(_WKPublicKeyCredentialRequestOptions *)options userVerificationAvailability:(_WKWebAuthenticationUserVerificationAvailability)userVerificationAvailability
{
RetainPtr<NSData> encodedCommand;
#if ENABLE(WEB_AUTHN)
auto hash = produceClientDataJsonHash(clientDataJSON);
auto encodedVector = fido::encodeGetAssertionRequestAsCBOR(hash, [_WKWebAuthenticationPanel convertToCoreRequestOptionsWithOptions:options], coreUserVerificationAvailability(userVerificationAvailability), std::nullopt);
encodedCommand = adoptNS([[NSData alloc] initWithBytes:encodedVector.data() length:encodedVector.size()]);
#endif
return encodedCommand.autorelease();
}
+ (NSData *)encodeMakeCredentialCommandWithClientDataHash:(NSData *)clientDataHash options:(_WKPublicKeyCredentialCreationOptions *)options userVerificationAvailability:(_WKWebAuthenticationUserVerificationAvailability)userVerificationAvailability
{
RetainPtr<NSData> encodedCommand;
#if ENABLE(WEB_AUTHN)
auto encodedVector = fido::encodeMakeCredenitalRequestAsCBOR(vectorFromNSData(clientDataHash), [_WKWebAuthenticationPanel convertToCoreCreationOptionsWithOptions:options], coreUserVerificationAvailability(userVerificationAvailability), std::nullopt);
encodedCommand = adoptNS([[NSData alloc] initWithBytes:encodedVector.data() length:encodedVector.size()]);
#endif
return encodedCommand.autorelease();
}
+ (NSData *)encodeGetAssertionCommandWithClientDataHash:(NSData *)clientDataHash options:(_WKPublicKeyCredentialRequestOptions *)options userVerificationAvailability:(_WKWebAuthenticationUserVerificationAvailability)userVerificationAvailability
{
RetainPtr<NSData> encodedCommand;
#if ENABLE(WEB_AUTHN)
auto encodedVector = fido::encodeGetAssertionRequestAsCBOR(vectorFromNSData(clientDataHash), [_WKWebAuthenticationPanel convertToCoreRequestOptionsWithOptions:options], coreUserVerificationAvailability(userVerificationAvailability), std::nullopt);
encodedCommand = adoptNS([[NSData alloc] initWithBytes:encodedVector.data() length:encodedVector.size()]);
#endif
return encodedCommand.autorelease();
}
- (void)setMockConfiguration:(NSDictionary *)configuration
{
#if ENABLE(WEB_AUTHN)
WebCore::MockWebAuthenticationConfiguration::LocalConfiguration localConfiguration;
localConfiguration.userVerification = WebCore::MockWebAuthenticationConfiguration::UserVerification::Yes;
if (configuration[@"privateKeyBase64"])
localConfiguration.privateKeyBase64 = configuration[@"privateKeyBase64"];
WebCore::MockWebAuthenticationConfiguration mockConfiguration;
mockConfiguration.local = WTFMove(localConfiguration);
_panel->setMockConfiguration(WTFMove(mockConfiguration));
#endif
}
#if ENABLE(WEB_AUTHN)
#pragma mark WKObject protocol implementation
- (API::Object&)_apiObject
{
return *_panel;
}
#endif
@end