Skip to content
Permalink
Browse files
[WebAuthn] Prefer internal user verification if available over pin en…
…try.

https://bugs.webkit.org/show_bug.cgi?id=213903
<rdar://problem/65359269>

Patch by John Pascoe <j_pascoe@apple.com> on 2021-10-04
Reviewed by Brent Fulgham.

Source/WebCore:

This adds an option to mock internal user verification in tests.

* testing/MockWebAuthenticationConfiguration.h:
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::encode const):
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::decode):
* testing/MockWebAuthenticationConfiguration.idl:

Source/WebKit:

* UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
(WebKit::MockHidConnection::feedReports):
* UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
(WebKit::CtapAuthenticator::makeCredential):
(WebKit::CtapAuthenticator::getAssertion):
(WebKit::fido::toStatus): Deleted.
(WebKit::fido::isPinError): Deleted.

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-internal-uv.html: Added.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-internal-uv.html: Added.

Canonical link: https://commits.webkit.org/242483@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@283515 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
pascoej authored and webkit-commit-queue committed Oct 4, 2021
1 parent a16a3ec commit 6abf9728aa39e1729ff9da1dc35773398d68020d
Showing 11 changed files with 201 additions and 12 deletions.
@@ -1,3 +1,18 @@
2021-10-04 John Pascoe <j_pascoe@apple.com>

[WebAuthn] Prefer internal user verification if available over pin entry.
https://bugs.webkit.org/show_bug.cgi?id=213903
<rdar://problem/65359269>

Reviewed by Brent Fulgham.

This adds an option to mock internal user verification in tests.

* testing/MockWebAuthenticationConfiguration.h:
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::encode const):
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::decode):
* testing/MockWebAuthenticationConfiguration.idl:

2021-10-04 Sam Weinig <weinig@apple.com>

Split WebXR extension module IDLs into their own files and settings
@@ -92,6 +92,7 @@ struct MockWebAuthenticationConfiguration {
bool canDowngrade { false };
bool expectCancel { false };
bool supportClientPin { false };
bool supportInternalUV { false };

template<class Encoder> void encode(Encoder&) const;
template<class Decoder> static std::optional<HidConfiguration> decode(Decoder&);
@@ -169,7 +170,7 @@ std::optional<MockWebAuthenticationConfiguration::LocalConfiguration> MockWebAut
template<class Encoder>
void MockWebAuthenticationConfiguration::HidConfiguration::encode(Encoder& encoder) const
{
encoder << payloadBase64 << stage << subStage << error << isU2f << keepAlive << fastDataArrival << continueAfterErrorData << canDowngrade << expectCancel << supportClientPin;
encoder << payloadBase64 << stage << subStage << error << isU2f << keepAlive << fastDataArrival << continueAfterErrorData << canDowngrade << expectCancel << supportClientPin << supportInternalUV;
}

template<class Decoder>
@@ -198,6 +199,8 @@ std::optional<MockWebAuthenticationConfiguration::HidConfiguration> MockWebAuthe
return std::nullopt;
if (!decoder.decode(result.supportClientPin))
return std::nullopt;
if (!decoder.decode(result.supportInternalUV))
return std::nullopt;
return result;
}

@@ -102,6 +102,7 @@
boolean canDowngrade = false;
boolean expectCancel = false;
boolean supportClientPin = false;
boolean supportInternalUV = false;
};

[
@@ -1,3 +1,19 @@
2021-10-04 John Pascoe <j_pascoe@apple.com>

[WebAuthn] Prefer internal user verification if available over pin entry.
https://bugs.webkit.org/show_bug.cgi?id=213903
<rdar://problem/65359269>

Reviewed by Brent Fulgham.

* UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
(WebKit::MockHidConnection::feedReports):
* UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
(WebKit::CtapAuthenticator::makeCredential):
(WebKit::CtapAuthenticator::getAssertion):
(WebKit::fido::toStatus): Deleted.
(WebKit::fido::isPinError): Deleted.

2021-10-04 John Pascoe <j_pascoe@apple.com>

[WebAuthn] Add SPI to change username of credential
@@ -228,15 +228,18 @@ void MockHidConnection::feedReports()
Vector<uint8_t> infoData;
if (m_configuration.hid->canDowngrade)
infoData = encodeAsCBOR(AuthenticatorGetInfoResponse({ ProtocolVersion::kCtap, ProtocolVersion::kU2f }, Vector<uint8_t>(aaguidLength, 0u)));
else if (m_configuration.hid->supportClientPin) {
else {
AuthenticatorGetInfoResponse infoResponse({ ProtocolVersion::kCtap }, Vector<uint8_t>(aaguidLength, 0u));
infoResponse.setPinProtocols({ pin::kProtocolVersion });
AuthenticatorSupportedOptions options;
options.setClientPinAvailability(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet);
if (m_configuration.hid->supportClientPin) {
infoResponse.setPinProtocols({ pin::kProtocolVersion });
options.setClientPinAvailability(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet);
}
if (m_configuration.hid->supportInternalUV)
options.setUserVerificationAvailability(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured);
infoResponse.setOptions(WTFMove(options));
infoData = encodeAsCBOR(infoResponse);
} else
infoData = encodeAsCBOR(AuthenticatorGetInfoResponse({ ProtocolVersion::kCtap }, Vector<uint8_t>(aaguidLength, 0u)));
}
infoData.insert(0, static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)); // Prepend status code.
if (stagesMatch() && m_configuration.hid->error == Mock::HidError::WrongChannelId)
message = FidoHidMessage::create(m_currentChannel - 1, FidoHidDeviceCommand::kCbor, infoData);
@@ -47,6 +47,8 @@ namespace WebKit {
using namespace WebCore;
using namespace fido;

using UVAvailability = AuthenticatorSupportedOptions::UserVerificationAvailability;

namespace {
WebAuthenticationStatus toStatus(const CtapDeviceResponseCode& error)
{
@@ -92,10 +94,14 @@ void CtapAuthenticator::makeCredential()
return;
Vector<uint8_t> cborCmd;
auto& options = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
if (m_info.options().clientPinAvailability() == AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet)
cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, options, m_info.options().userVerificationAvailability(), PinParameters { pin::kProtocolVersion, m_pinAuth });
auto internalUVAvailability = m_info.options().userVerificationAvailability();
// If UV is required, then either built-in uv or a pin will work.
if (internalUVAvailability == UVAvailability::kSupportedAndConfigured && (!options.authenticatorSelection || options.authenticatorSelection->userVerification != UserVerificationRequirement::Discouraged))
cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, options, internalUVAvailability);
else if (m_info.options().clientPinAvailability() == AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet)
cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, options, internalUVAvailability, PinParameters { pin::kProtocolVersion, m_pinAuth });
else
cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, options, m_info.options().userVerificationAvailability());
cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, options, internalUVAvailability);
driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
ASSERT(RunLoop::isMain());
if (!weakThis)
@@ -133,10 +139,14 @@ void CtapAuthenticator::getAssertion()
ASSERT(!m_isDowngraded);
Vector<uint8_t> cborCmd;
auto& options = WTF::get<PublicKeyCredentialRequestOptions>(requestData().options);
if (m_info.options().clientPinAvailability() == AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet && options.userVerification != UserVerificationRequirement::Discouraged)
cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, options, m_info.options().userVerificationAvailability(), PinParameters { pin::kProtocolVersion, m_pinAuth });
auto internalUVAvailability = m_info.options().userVerificationAvailability();
// If UV is required, then either built-in uv or a pin will work.
if (internalUVAvailability == UVAvailability::kSupportedAndConfigured && options.userVerification != UserVerificationRequirement::Discouraged)
cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, options, internalUVAvailability);
else if (m_info.options().clientPinAvailability() == AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet && options.userVerification != UserVerificationRequirement::Discouraged)
cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, options, internalUVAvailability, PinParameters { pin::kProtocolVersion, m_pinAuth });
else
cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, options, m_info.options().userVerificationAvailability());
cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, options, internalUVAvailability);
driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
ASSERT(RunLoop::isMain());
if (!weakThis)
@@ -1,3 +1,17 @@
2021-10-04 John Pascoe <j_pascoe@apple.com>

[WebAuthn] Prefer internal user verification if available over pin entry.
https://bugs.webkit.org/show_bug.cgi?id=213903
<rdar://problem/65359269>

Reviewed by Brent Fulgham.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-internal-uv.html: Added.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-internal-uv.html: Added.

2021-10-04 John Pascoe <j_pascoe@apple.com>

[WebAuthn] Add SPI to change username of credential
@@ -395,6 +395,8 @@
524BBC9E19DF72C0002F1AF1 /* file-with-video.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 524BBC9B19DF3714002F1AF1 /* file-with-video.html */; };
524BBCA119E30C77002F1AF1 /* test.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = 524BBCA019E30C63002F1AF1 /* test.mp4 */; };
52B8CF9815868D9100281053 /* SetDocumentURI.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 52B8CF9415868CF000281053 /* SetDocumentURI.html */; };
52C8C1382706437C00BDF3B7 /* web-authentication-make-credential-hid-internal-uv.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 52C8C1372706437C00BDF3B7 /* web-authentication-make-credential-hid-internal-uv.html */; };
52C8C13A2706439000BDF3B7 /* web-authentication-get-assertion-hid-internal-uv.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 52C8C1392706439000BDF3B7 /* web-authentication-get-assertion-hid-internal-uv.html */; };
52D5D6C021B9F1B30046ABA6 /* RenderingProgress.mm in Sources */ = {isa = PBXBuildFile; fileRef = 52D5D6BD21B9F1B20046ABA6 /* RenderingProgress.mm */; };
52D673EE1AFB127300FA19FE /* WKPageCopySessionStateWithFiltering.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52D673EC1AFB126800FA19FE /* WKPageCopySessionStateWithFiltering.cpp */; };
52E5CE4914D21EAB003B2BD8 /* ParentFrame_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52E5CE4814D21EAB003B2BD8 /* ParentFrame_Bundle.cpp */; };
@@ -1752,6 +1754,7 @@
2EBD9D0A2134730D002DA758 /* video.html in Copy Resources */,
CD577799211CE0E4001B371E /* web-audio-only.html in Copy Resources */,
57663DF32357E48900E85E09 /* web-authentication-get-assertion-hid-cancel.html in Copy Resources */,
52C8C13A2706439000BDF3B7 /* web-authentication-get-assertion-hid-internal-uv.html in Copy Resources */,
579F1C0123C93AF500C7D4B4 /* web-authentication-get-assertion-hid-multiple-accounts.html in Copy Resources */,
577454D02359B378008E1ED7 /* web-authentication-get-assertion-hid-no-credentials.html in Copy Resources */,
578DA44C23ECD23000246010 /* web-authentication-get-assertion-hid-pin-auth-blocked-error.html in Copy Resources */,
@@ -1765,6 +1768,7 @@
57663DEA234EA66D00E85E09 /* web-authentication-get-assertion-nfc.html in Copy Resources */,
577454D22359BB01008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html in Copy Resources */,
57C624502346C21E00383FE7 /* web-authentication-get-assertion.html in Copy Resources */,
52C8C1382706437C00BDF3B7 /* web-authentication-make-credential-hid-internal-uv.html in Copy Resources */,
578DA44823ECD09B00246010 /* web-authentication-make-credential-hid-pin-auth-blocked-error.html in Copy Resources */,
570D26F423C3CA6A00D5CF67 /* web-authentication-make-credential-hid-pin-get-key-agreement-error.html in Copy Resources */,
5751B28A249D5BC500664C2A /* web-authentication-make-credential-hid-pin-get-pin-token-fake-pin-invalid-error-retry.html in Copy Resources */,
@@ -2257,6 +2261,8 @@
524BBCA019E30C63002F1AF1 /* test.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.mp4; sourceTree = "<group>"; };
52B8CF9415868CF000281053 /* SetDocumentURI.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = SetDocumentURI.html; sourceTree = "<group>"; };
52B8CF9515868CF000281053 /* SetDocumentURI.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SetDocumentURI.mm; sourceTree = "<group>"; };
52C8C1372706437C00BDF3B7 /* web-authentication-make-credential-hid-internal-uv.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "web-authentication-make-credential-hid-internal-uv.html"; path = "Tests/WebKitCocoa/web-authentication-make-credential-hid-internal-uv.html"; sourceTree = "<group>"; };
52C8C1392706439000BDF3B7 /* web-authentication-get-assertion-hid-internal-uv.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "web-authentication-get-assertion-hid-internal-uv.html"; path = "Tests/WebKitCocoa/web-authentication-get-assertion-hid-internal-uv.html"; sourceTree = "<group>"; };
52CB47401448FB9300873995 /* LoadAlternateHTMLStringWithNonDirectoryURL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LoadAlternateHTMLStringWithNonDirectoryURL.cpp; sourceTree = "<group>"; };
52D5D6BD21B9F1B20046ABA6 /* RenderingProgress.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RenderingProgress.mm; sourceTree = "<group>"; };
52D5D6BE21B9F1B20046ABA6 /* RenderingProgressPlugIn.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RenderingProgressPlugIn.mm; sourceTree = "<group>"; };
@@ -3254,6 +3260,8 @@
08FB7794FE84155DC02AAC07 /* TestWebKitAPI */ = {
isa = PBXGroup;
children = (
52C8C1392706439000BDF3B7 /* web-authentication-get-assertion-hid-internal-uv.html */,
52C8C1372706437C00BDF3B7 /* web-authentication-make-credential-hid-internal-uv.html */,
49AEEF682407276F00C87E4C /* Info.plist */,
5C9D922622D7DD7B008E9266 /* Derived Sources */,
5C9D921D22D7DBF7008E9266 /* Sources.txt */,
@@ -1123,6 +1123,24 @@ TCPServer server([parentFrame = String(parentFrame), subFrame = String(subFrame)
EXPECT_TRUE(webAuthenticationPanelUpdatePINInvalid);
}

TEST(WebAuthenticationPanel, MakeCredentialInternalUV)
{
reset();
RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-internal-uv" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];

auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
[[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
[[configuration preferences] _setEnabled:NO forExperimentalFeature:webAuthenticationModernExperimentalFeature()];

auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
[webView setUIDelegate:delegate.get()];
[webView focus];

[webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
[webView waitForMessage:@"Succeeded!"];
}

TEST(WebAuthenticationPanel, MakeCredentialPin)
{
reset();
@@ -1202,6 +1220,24 @@ TCPServer server([parentFrame = String(parentFrame), subFrame = String(subFrame)
[webView waitForMessage:@"Succeeded!"];
}

TEST(WebAuthenticationPanel, GetAssertionInternalUV)
{
reset();
RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-hid-internal-uv" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];

auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
[[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
[[configuration preferences] _setEnabled:NO forExperimentalFeature:webAuthenticationModernExperimentalFeature()];

auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
[webView setUIDelegate:delegate.get()];
[webView focus];

[webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
[webView waitForMessage:@"Succeeded!"];
}

TEST(WebAuthenticationPanel, GetAssertionPinAuthBlockedError)
{
reset();
@@ -0,0 +1,30 @@
<input type="text" id="input">
<script>
const testAssertionMessageBase64 =
"AKMBomJpZFhAKAitzuj+Tslzelf3/vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2" +
"w/mooa7tQtGgDdwZIjOhjcuZ0pQ1ajoE4GR0eXBlanB1YmxpYy1rZXkCWCVGzH+5" +
"Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
"4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
"QoJ1L7Fe64G9uBc=";

if (window.internals) {
internals.setMockWebAuthenticationConfiguration({ hid: { supportClientPin: true, supportInternalUV: true, payloadBase64: [testAssertionMessageBase64] } });
internals.withUserGesture(() => { input.focus(); });
}

const options = {
publicKey: {
challenge: new Uint8Array(16),
timeout: 100
},
authenticatorSelection: { userVerification: "required" },
};

navigator.credentials.get(options).then(credential => {
// console.log("Succeeded!");
window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
}, error => {
// console.log(error.message);
window.webkit.messageHandlers.testHandler.postMessage(error.message);
});
</script>

0 comments on commit 6abf972

Please sign in to comment.