blob: e6ef77773fcddd9640f3d4a8628ff46d71db4ef9 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/credential_provider_extension/passkey_request_details.h"
#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "components/webauthn/core/browser/passkey_model_utils.h"
#import "ios/chrome/common/credential_provider/credential.h"
#import "ios/chrome/credential_provider_extension/passkey_util.h"
#import "ios/chrome/credential_provider_extension/passkey_util_swift.h"
#import "ios/chrome/credential_provider_extension/ui/feature_flags.h"
@interface PasskeyRequestDetails ()
// Hash of client data for credential provider to sign as part of the operation.
@property(strong, nonatomic, readwrite) NSData* clientDataHash;
// A preference for whether the authenticator should attempt to verify that it
// is being used by its owner.
@property(nonatomic, readwrite) BOOL userVerificationRequired;
// The relying party identifier for this request.
@property(strong, nonatomic, readwrite) NSString* relyingPartyIdentifier;
// A list of allowed credential IDs for this request. An empty list means all
// credentials are allowed.
@property(strong, nonatomic, readwrite) NSArray<NSData*>* allowedCredentials;
// Whether at least one signing algorithm is supported by the relying party.
// Unused by assertion requests.
@property(nonatomic, readwrite) BOOL algorithmIsSupported;
// The user name of the passkey credential.
@property(strong, nonatomic, readwrite) NSString* userName;
// The user handle of the passkey credential.
@property(strong, nonatomic, readwrite) NSData* userHandle;
@end
@implementation PasskeyRequestDetails {
PRFData* _prf;
}
- (instancetype)initWithParameters:(ASPasskeyCredentialRequestParameters*)
passkeyCredentialRequestParameters
isBiometricAuthenticationEnabled:(BOOL)isBiometricAuthenticationEnabled
API_AVAILABLE(ios(17.0)) {
CHECK(passkeyCredentialRequestParameters);
self = [super init];
if (self) {
self.clientDataHash = passkeyCredentialRequestParameters.clientDataHash;
self.userVerificationRequired = ShouldPerformUserVerificationForPreference(
passkeyCredentialRequestParameters.userVerificationPreference,
isBiometricAuthenticationEnabled);
self.relyingPartyIdentifier =
passkeyCredentialRequestParameters.relyingPartyIdentifier;
self.allowedCredentials =
passkeyCredentialRequestParameters.allowedCredentials;
self.algorithmIsSupported = NO;
self.userName = nil;
self.userHandle = nil;
if (@available(iOS 18.0, *)) {
if (IsPasskeyPRFEnabled()) {
_prf = [PRFData fromParameters:passkeyCredentialRequestParameters];
}
}
}
return self;
}
- (instancetype)initWithRequest:(id<ASCredentialRequest>)credentialRequest
isBiometricAuthenticationEnabled:(BOOL)isBiometricAuthenticationEnabled
API_AVAILABLE(ios(17.0)) {
CHECK(credentialRequest);
self = [super init];
if (self) {
ASPasskeyCredentialRequest* passkeyCredentialRequest =
base::apple::ObjCCastStrict<ASPasskeyCredentialRequest>(
credentialRequest);
self.clientDataHash = passkeyCredentialRequest.clientDataHash;
self.userVerificationRequired = ShouldPerformUserVerificationForPreference(
passkeyCredentialRequest.userVerificationPreference,
isBiometricAuthenticationEnabled);
NSArray<NSNumber*>* supportedAlgorithms = [passkeyCredentialRequest
.supportedAlgorithms
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
NSNumber* algorithm,
NSDictionary* bindings) {
return webauthn::passkey_model_utils::IsSupportedAlgorithm(
algorithm.intValue);
}]];
self.algorithmIsSupported = supportedAlgorithms.count > 0;
ASPasskeyCredentialIdentity* identity =
base::apple::ObjCCastStrict<ASPasskeyCredentialIdentity>(
passkeyCredentialRequest.credentialIdentity);
self.relyingPartyIdentifier = identity.relyingPartyIdentifier;
self.userName = identity.userName;
self.userHandle = identity.userHandle;
self.allowedCredentials = nil;
if (@available(iOS 18.0, *)) {
if (IsPasskeyPRFEnabled()) {
_prf = [PRFData fromRequest:passkeyCredentialRequest];
}
}
}
return self;
}
- (ASPasskeyRegistrationCredential*)createPasskeyForGaia:(NSString*)gaia
securityDomainSecrets:
(NSArray<NSData*>*)securityDomainSecrets
API_AVAILABLE(ios(17.0)) {
NSArray<NSData*>* prfInputs = nil;
if (@available(iOS 18.0, *)) {
if (_prf.inputValues) {
prfInputs = [NSArray arrayWithObjects:_prf.inputValues.saltInput1,
_prf.inputValues.saltInput2, nil];
} else if (_prf.checkForSupport) {
// Initialize prfInputs with a non nil empty array to check for support.
prfInputs = [NSArray array];
}
}
PasskeyCreationOutput passkeyCreationOutput = PerformPasskeyCreation(
self.clientDataHash, self.relyingPartyIdentifier, self.userName,
self.userHandle, gaia, securityDomainSecrets, prfInputs);
if (@available(iOS 18.0, *)) {
if (passkeyCreationOutput.credential) {
if ([passkeyCreationOutput.prf_outputs count]) {
PRFOutputValues* prfOutputValues =
[PRFOutputValues fromValues:passkeyCreationOutput.prf_outputs];
[passkeyCreationOutput.credential
setPRFFromOutputValues:prfOutputValues];
} else if (_prf.checkForSupport) {
[passkeyCreationOutput.credential setPRFIsSupported];
}
}
}
return passkeyCreationOutput.credential;
}
- (ASPasskeyAssertionCredential*)
assertPasskeyCredential:(id<Credential>)credential
securityDomainSecrets:(NSArray<NSData*>*)securityDomainSecrets
API_AVAILABLE(ios(17.0)) {
NSArray<NSData*>* prfInputs = nil;
PRFInputValues* inputValues = nil;
if (@available(iOS 18.0, *)) {
if (_prf) {
// Check if there's per credential values available.
inputValues = _prf.perCredentialInputValues[credential.credentialId];
if (!inputValues) {
// If there are no per credential values, use the generic values.
inputValues = _prf.inputValues;
}
if (inputValues) {
prfInputs = [NSArray arrayWithObjects:inputValues.saltInput1,
inputValues.saltInput2, nil];
}
}
}
PasskeyAssertionOutput passkeyAssertionOutput = PerformPasskeyAssertion(
credential, self.clientDataHash, self.allowedCredentials,
securityDomainSecrets, prfInputs);
if (@available(iOS 18.0, *)) {
if (passkeyAssertionOutput.credential &&
[passkeyAssertionOutput.prf_outputs count]) {
PRFOutputValues* prfOutputValues =
[PRFOutputValues fromValues:passkeyAssertionOutput.prf_outputs];
[passkeyAssertionOutput.credential
setPRFFromOutputValues:prfOutputValues];
}
}
return passkeyAssertionOutput.credential;
}
- (BOOL)hasMatchingPassword:(NSArray<id<Credential>>*)credentials {
NSUInteger credentialIndex = [credentials indexOfObjectPassingTest:^BOOL(
id<Credential> credential,
NSUInteger idx, BOOL* stop) {
return !credential.isPasskey &&
[credential.username isEqualToString:self.userName] &&
[credential.serviceName isEqualToString:self.relyingPartyIdentifier];
}];
return credentialIndex != NSNotFound;
}
@end