blob: 9a756dc5072d4a7bdc2c280f37e312849024801b [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 AuthenticationServices
import CryptoKit
import Foundation
@objcMembers public class PRFInputValues: NSObject {
public var saltInput1: Data
public var saltInput2: Data?
@available(iOS 18.0, *) init(
inputValues: ASAuthorizationPublicKeyCredentialPRFAssertionInput.InputValues
) {
saltInput1 = inputValues.saltInput1
saltInput2 = inputValues.saltInput2
}
}
@objcMembers public class PRFOutputValues: NSObject {
public var saltOutput1: Data
public var saltOutput2: Data?
@available(iOS 18.0, *) @objc public static func fromValues(
_ outputValues: [Data]
)
-> PRFOutputValues?
{
return outputValues.isEmpty ? nil : PRFOutputValues(outputValues: outputValues)
}
@available(iOS 18.0, *) init(
outputValues: [Data]
) {
saltOutput1 = outputValues[0]
saltOutput2 = (outputValues.count > 1) ? outputValues[1] : nil
}
@available(iOS 18.0, *) func key1() -> CryptoKit.SymmetricKey {
return CryptoKit.SymmetricKey(data: saltOutput1)
}
@available(iOS 18.0, *) func key2() -> CryptoKit.SymmetricKey? {
guard let saltInput = saltOutput2 else { return nil }
return CryptoKit.SymmetricKey(data: saltInput)
}
@available(iOS 18.0, *) func toAssertionOutput()
-> ASAuthorizationPublicKeyCredentialPRFAssertionOutput?
{
return ASAuthorizationPublicKeyCredentialPRFAssertionOutput(
first: key1(), second: key2())
}
@available(iOS 18.0, *) func toRegistrationOutput()
-> ASAuthorizationPublicKeyCredentialPRFRegistrationOutput?
{
return ASAuthorizationPublicKeyCredentialPRFRegistrationOutput(
first: key1(), second: key2())
}
}
@objcMembers public class PRFData: NSObject {
public var inputValues: PRFInputValues?
public var perCredentialInputValues: [Data: PRFInputValues] = [:]
public var checkForSupport: Bool
@available(iOS 18.0, *) init(
input: ASAuthorizationPublicKeyCredentialPRFAssertionInput
) {
checkForSupport = false
super.init()
setInputValues(input.inputValues)
for (credentialID, inputValues) in input.perCredentialInputValues ?? [:] {
perCredentialInputValues[credentialID] =
PRFInputValues(inputValues: inputValues)
}
}
@available(iOS 18.0, *) init(
input: ASAuthorizationPublicKeyCredentialPRFRegistrationInput
) {
checkForSupport = input.shouldCheckForSupport
super.init()
setInputValues(input.inputValues)
}
@available(iOS 18.0, *) func setInputValues(
_ values: ASAuthorizationPublicKeyCredentialPRFAssertionInput.InputValues?
) {
guard let prfInputValues = values else { return }
inputValues = PRFInputValues(inputValues: prfInputValues)
}
@available(iOS 18.0, *) @objc public static func fromParameters(
_ parameters: ASPasskeyCredentialRequestParameters
)
-> PRFData?
{
guard let prf = parameters.extensionInput?.prf else { return nil }
return PRFData(input: prf)
}
@available(iOS 18.0, *) @objc public static func fromRequest(
_ request: ASPasskeyCredentialRequest
)
-> PRFData?
{
switch request.extensionInput {
case .assertion(let input):
guard let prf = input.prf else { return nil }
return PRFData(input: prf)
case .registration(let input):
guard let prf = input.prf else { return nil }
return PRFData(input: prf)
case .none:
return nil
@unknown default:
return nil
}
}
}
@available(iOS 18.0, *) @objc extension ASPasskeyAssertionCredential {
@objc public func setPRF(fromOutputValues outputValues: PRFOutputValues) {
guard let prf = outputValues.toAssertionOutput() else { return }
extensionOutput = ASPasskeyAssertionCredentialExtensionOutput(
largeBlob: extensionOutput?.largeBlob, prf: prf)
}
}
@available(iOS 18.0, *) @objc extension ASPasskeyRegistrationCredential {
@objc public func setPRF(fromOutputValues outputValues: PRFOutputValues) {
guard let prf = outputValues.toRegistrationOutput() else { return }
extensionOutput = ASPasskeyRegistrationCredentialExtensionOutput(
largeBlob: extensionOutput?.largeBlob, prf: prf)
}
@objc public func setPRFIsSupported() {
extensionOutput = ASPasskeyRegistrationCredentialExtensionOutput(
largeBlob: extensionOutput?.largeBlob,
prf: ASAuthorizationPublicKeyCredentialPRFRegistrationOutput.supported)
}
@objc public func setLargeBlobIsSupported() {
extensionOutput = ASPasskeyRegistrationCredentialExtensionOutput(
largeBlob: ASAuthorizationPublicKeyCredentialLargeBlobRegistrationOutput.supported,
prf: extensionOutput?.prf
)
}
}