Skip to content

Commit

Permalink
Add plumbing for PRF
Browse files Browse the repository at this point in the history
rdar://121687418 (Adopt PRF extension in WebKit)

Reviewed by Pascoe.

Update the IDLs/serializations for PRF and add some plumbing.

* Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientInputs.h:
* Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientInputs.idl:
* Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientOutputs.h:
* Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientOutputs.idl:
* Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp:
(WebCore::AuthenticatorCoordinator::create):
* Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in:
* Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticatorCoordinatorProxy.mm:
(WebKit::toNSData):
(WebKit::WebAuthenticatorCoordinatorProxy::requestsForAssertion):
(WebKit::WebAuthenticatorCoordinatorProxy::performRequest):

Canonical link: https://commits.webkit.org/274930@main
  • Loading branch information
Garrett Davidson authored and achristensen07 committed Feb 17, 2024
1 parent 5b92efd commit d0abae2
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#if ENABLE(WEB_AUTHN)

#include "BufferSource.h"
#include <wtf/KeyValuePair.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>

namespace WebCore {
Expand All @@ -39,9 +41,20 @@ struct AuthenticationExtensionsClientInputs {
std::optional<BufferSource> write;
};

struct PRFValues {
BufferSource first;
std::optional<BufferSource> second;
};

struct PRFInputs {
std::optional<AuthenticationExtensionsClientInputs::PRFValues> eval;
std::optional<Vector<KeyValuePair<String, AuthenticationExtensionsClientInputs::PRFValues>>> evalByCredential;
};

String appid;
bool credProps; // Not serialized but probably should be. Don't re-introduce rdar://101057340 though.
std::optional<AuthenticationExtensionsClientInputs::LargeBlobInputs> largeBlob;
std::optional<AuthenticationExtensionsClientInputs::PRFInputs> prf;

WEBCORE_EXPORT Vector<uint8_t> toCBOR() const;
WEBCORE_EXPORT static std::optional<AuthenticationExtensionsClientInputs> fromCBOR(std::span<const uint8_t>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,26 @@
BufferSource write;
};

[
Conditional=WEB_AUTHN,
] dictionary AuthenticationExtensionsPRFValues {
required BufferSource first;
BufferSource second;
};

[
Conditional=WEB_AUTHN,
] dictionary AuthenticationExtensionsPRFInputs {
AuthenticationExtensionsPRFValues eval;
record<USVString, AuthenticationExtensionsPRFValues> evalByCredential;
};

[
Conditional=WEB_AUTHN,
] dictionary AuthenticationExtensionsClientInputs {
USVString appid;
// For Google to turn off the legacy AppID support.
boolean credProps;
AuthenticationExtensionsLargeBlobInputs largeBlob;
AuthenticationExtensionsPRFInputs prf;
};
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,20 @@ struct AuthenticationExtensionsClientOutputs {
std::optional<bool> written;
};

struct PRFValues {
RefPtr<ArrayBuffer> first;
RefPtr<ArrayBuffer> second;
};

struct PRFOutputs {
std::optional<bool> enabled;
std::optional<AuthenticationExtensionsClientOutputs::PRFValues> results;
};

std::optional<bool> appid;
std::optional<CredentialPropertiesOutput> credProps;
std::optional<LargeBlobOutputs> largeBlob;
std::optional<PRFOutputs> prf;

WEBCORE_EXPORT Vector<uint8_t> toCBOR() const;
WEBCORE_EXPORT static std::optional<AuthenticationExtensionsClientOutputs> fromCBOR(const Vector<uint8_t>&);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,28 @@
boolean written;
};

[
Conditional=WEB_AUTHN,
JSGenerateToJSObject,
] dictionary AuthenticationExtensionsPRFValues {
required ArrayBuffer first;
ArrayBuffer second;
};

[
Conditional=WEB_AUTHN,
JSGenerateToJSObject,
] dictionary AuthenticationExtensionsPRFOutputs {
boolean enabled;
AuthenticationExtensionsPRFValues results;
};

[
Conditional=WEB_AUTHN,
JSGenerateToJSObject,
] dictionary AuthenticationExtensionsClientOutputs {
boolean appid;
CredentialPropertiesOutput credProps;
AuthenticationExtensionsLargeBlobOutputs largeBlob;
AuthenticationExtensionsPRFOutputs prf;
};
2 changes: 2 additions & 0 deletions Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,14 @@ void AuthenticatorCoordinator::create(const Document& document, CredentialCreati
AuthenticationExtensionsClientInputs extensionInputs = {
String(),
false,
std::nullopt,
std::nullopt
};

if (auto extensions = options.extensions) {
extensionInputs.credProps = extensions->credProps;
extensionInputs.largeBlob = extensions->largeBlob;
extensionInputs.prf = extensions->prf;
}

options.extensions = extensionInputs;
Expand Down
24 changes: 23 additions & 1 deletion Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in
Original file line number Diff line number Diff line change
Expand Up @@ -1495,12 +1495,23 @@ struct WebCore::AuthenticatorResponseData {
String support;
std::optional<bool> read;
std::optional<WebCore::BufferSource> write;
}
};

[Nested] struct WebCore::AuthenticationExtensionsClientInputs::PRFValues {
WebCore::BufferSource first;
std::optional<WebCore::BufferSource> second;
};

[Nested] struct WebCore::AuthenticationExtensionsClientInputs::PRFInputs {
std::optional<WebCore::AuthenticationExtensionsClientInputs::PRFValues> eval;
std::optional<Vector<KeyValuePair<String, WebCore::AuthenticationExtensionsClientInputs::PRFValues>>> evalByCredential;
};

[LegacyPopulateFromEmptyConstructor] struct WebCore::AuthenticationExtensionsClientInputs {
String appid;
[NotSerialized] bool credProps;
std::optional<WebCore::AuthenticationExtensionsClientInputs::LargeBlobInputs> largeBlob;
std::optional<WebCore::AuthenticationExtensionsClientInputs::PRFInputs> prf;
}

[Nested] struct WebCore::AuthenticationExtensionsClientOutputs::CredentialPropertiesOutput {
Expand All @@ -1513,10 +1524,21 @@ struct WebCore::AuthenticatorResponseData {
std::optional<bool> written;
};

[Nested] struct WebCore::AuthenticationExtensionsClientOutputs::PRFValues {
RefPtr<JSC::ArrayBuffer> first;
RefPtr<JSC::ArrayBuffer> second;
};

[Nested] struct WebCore::AuthenticationExtensionsClientOutputs::PRFOutputs {
std::optional<bool> enabled;
std::optional<WebCore::AuthenticationExtensionsClientOutputs::PRFValues> results;
};

struct WebCore::AuthenticationExtensionsClientOutputs {
std::optional<bool> appid;
std::optional<WebCore::AuthenticationExtensionsClientOutputs::CredentialPropertiesOutput> credProps;
std::optional<WebCore::AuthenticationExtensionsClientOutputs::LargeBlobOutputs> largeBlob;
std::optional<WebCore::AuthenticationExtensionsClientOutputs::PRFOutputs> prf;
}

[Nested] struct WebCore::PublicKeyCredentialCreationOptions::Parameters {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@
#import <wtf/EnumTraits.h>
#import <wtf/StdLibExtras.h>
#import <wtf/cocoa/VectorCocoa.h>
#import <wtf/text/Base64.h>

#if USE(APPLE_INTERNAL_SDK) && __has_include(<WebKitAdditions/WebAuthenticatorCoordinatorProxyAdditions.h>)
#import <WebKitAdditions/WebAuthenticatorCoordinatorProxyAdditions.h>
#else
#define AUTHENTICATOR_COORDINATOR_ADDITIONS
#define AUTHENTICATOR_COORDINATOR_ASSERTION_REQUEST_ADDITIONS
#define AUTHENTICATOR_COORDINATOR_ASSERTION_RESPONSE_ADDITIONS
#define AUTHENTICATOR_COORDINATOR_REGISTRATION_RESPONSE_ADDITIONS
#endif

#import "AuthenticationServicesCoreSoftLink.h"
Expand Down Expand Up @@ -111,6 +115,11 @@ - (void)authorizationController:(ASAuthorizationController *)controller didCompl
return ArrayBuffer::create(reinterpret_cast<const uint8_t *>(data.bytes), data.length);
}

static inline RetainPtr<NSData> toNSData(const Vector<uint8_t>& data)
{
return adoptNS([[NSData alloc] initWithBytes:data.data() length:data.size()]);
}

#if HAVE(WEB_AUTHN_AS_MODERN)

static inline ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement toASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement(const String& requirement)
Expand Down Expand Up @@ -288,6 +297,9 @@ static inline ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement toAS
if (largeBlob->write)
request.get().largeBlob.dataToWrite = WebCore::toNSData(*largeBlob->write).get();
}

AUTHENTICATOR_COORDINATOR_ASSERTION_REQUEST_ADDITIONS

[requests addObject:request.leakRef()];
}

Expand Down Expand Up @@ -367,21 +379,40 @@ static inline ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement toAS
response.attestationObject = toArrayBuffer(credential.get().rawAttestationObject);
response.transports = { };
response.clientDataJSON = toArrayBuffer(credential.get().rawClientDataJSON);
if (credential.get().largeBlob)
response.extensionOutputs = { { std::nullopt, std::nullopt, { { credential.get().largeBlob.isSupported, nullptr, std::nullopt } } } };

bool hasExtensionOutput = false;
AuthenticationExtensionsClientOutputs extensionOutputs;
if (credential.get().largeBlob) {
hasExtensionOutput = true;
extensionOutputs.largeBlob = { credential.get().largeBlob.isSupported, nullptr, std::nullopt };
}

AUTHENTICATOR_COORDINATOR_REGISTRATION_RESPONSE_ADDITIONS

if (hasExtensionOutput)
response.extensionOutputs = extensionOutputs;
} else if ([auth.get().credential isKindOfClass:getASAuthorizationPlatformPublicKeyCredentialAssertionClass()]) {
auto credential = retainPtr((ASAuthorizationPlatformPublicKeyCredentialAssertion *)auth.get().credential);
response.rawId = toArrayBuffer(credential.get().credentialID);
response.authenticatorData = toArrayBuffer(credential.get().rawAuthenticatorData);
response.signature = toArrayBuffer(credential.get().signature);
response.userHandle = toArrayBuffer(credential.get().userID);
response.clientDataJSON = toArrayBuffer(credential.get().rawClientDataJSON);

bool hasExtensionOutput = false;
AuthenticationExtensionsClientOutputs extensionOutputs;
if (credential.get().largeBlob) {
hasExtensionOutput = true;
RefPtr<ArrayBuffer> protector = nullptr;
if (credential.get().largeBlob.readData)
protector = toArrayBuffer(credential.get().largeBlob.readData);
response.extensionOutputs = { { std::nullopt, std::nullopt, { { std::nullopt, protector, credential.get().largeBlob.didWrite } } } };
extensionOutputs.largeBlob = { std::nullopt, protector, credential.get().largeBlob.didWrite };
}

AUTHENTICATOR_COORDINATOR_ASSERTION_RESPONSE_ADDITIONS

if (hasExtensionOutput)
response.extensionOutputs = extensionOutputs;
} else if ([auth.get().credential isKindOfClass:getASAuthorizationSecurityKeyPublicKeyCredentialRegistrationClass()]) {
auto credential = retainPtr((ASAuthorizationSecurityKeyPublicKeyCredentialRegistration *)auth.get().credential);
response.isAuthenticatorAttestationResponse = true;
Expand Down Expand Up @@ -421,11 +452,6 @@ static inline ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement toAS
#endif
}

static inline RetainPtr<NSData> toNSData(const Vector<uint8_t>& data)
{
return adoptNS([[NSData alloc] initWithBytes:data.data() length:data.size()]);
}

static inline RetainPtr<NSString> toNSString(UserVerificationRequirement userVerificationRequirement)
{
switch (userVerificationRequirement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ void U2fAuthenticator::continueSignCommandAfterResponseReceived(ApduResponse&& a
return;
}
if (m_isAppId)
response->setExtensions({ m_isAppId, std::nullopt, std::nullopt });
response->setExtensions({ m_isAppId, std::nullopt, std::nullopt, std::nullopt });

receiveRespond(response.releaseNonNull());
return;
Expand Down

0 comments on commit d0abae2

Please sign in to comment.