Changeset 243193 in webkit


Ignore:
Timestamp:
Mar 19, 2019 11:11:57 PM (5 years ago)
Author:
jiewen_tan@apple.com
Message:

[WebAuthN] Implement FIDO AppID extension
https://bugs.webkit.org/show_bug.cgi?id=143491
<rdar://problem/48298273>

Reviewed by Brent Fulgham.

Source/WebCore:

This patch adds support for FIDO AppID extension: https://www.w3.org/TR/webauthn/#sctn-appid-extension.
To be noticed, this implementation follows what spec suggested in the 'Note' session and what Chrome/Firefox
do in practice to avoid some unnecessary steps of
https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html#determining-if-a-caller-s-facetid-is-authorized-for-an-appid.

In fido::convertToU2fSignCommand, the checkOnly flag is deleted as it is never used.

Covered by new tests in existing files.

  • CMakeLists.txt:
  • DerivedSources-input.xcfilelist:
  • DerivedSources-output.xcfilelist:
  • DerivedSources.make:
  • Modules/webauthn/AuthenticationExtensionsClientInputs.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredential.idl.

(WebCore::AuthenticationExtensionsClientInputs::encode const):
(WebCore::AuthenticationExtensionsClientInputs::decode):

  • Modules/webauthn/AuthenticationExtensionsClientInputs.idl: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl.
  • Modules/webauthn/AuthenticatorCoordinator.cpp:

(WebCore::AuthenticatorCoordinatorInternal::processAppIdExtension):
(WebCore::AuthenticatorCoordinator::create const):
(WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):

  • Modules/webauthn/PublicKeyCredential.cpp:

(WebCore::PublicKeyCredential::tryCreate):
(WebCore::PublicKeyCredential::PublicKeyCredential):
(WebCore::PublicKeyCredential::getClientExtensionResults const):
(WebCore::PublicKeyCredential::create): Deleted.

  • Modules/webauthn/PublicKeyCredential.h:
  • Modules/webauthn/PublicKeyCredential.idl:
  • Modules/webauthn/PublicKeyCredentialCreationOptions.h:
  • Modules/webauthn/PublicKeyCredentialCreationOptions.idl:
  • Modules/webauthn/PublicKeyCredentialData.h:

(WebCore::PublicKeyCredentialData::encode const):
(WebCore::PublicKeyCredentialData::decode):

  • Modules/webauthn/PublicKeyCredentialRequestOptions.h:

(WebCore::PublicKeyCredentialRequestOptions::encode const):
(WebCore::PublicKeyCredentialRequestOptions::decode):

  • Modules/webauthn/PublicKeyCredentialRequestOptions.idl:
  • Modules/webauthn/fido/DeviceResponseConverter.cpp:

(fido::readCTAPMakeCredentialResponse):
(fido::readCTAPGetAssertionResponse):

  • Modules/webauthn/fido/U2fCommandConstructor.cpp:

(fido::convertToU2fSignCommand):

  • Modules/webauthn/fido/U2fCommandConstructor.h:
  • Modules/webauthn/fido/U2fResponseConverter.cpp:

(fido::readU2fRegisterResponse):
(fido::readU2fSignResponse):

  • Sources.txt:
  • WebCore.xcodeproj/project.pbxproj:

Source/WebKit:

In U2fHidAuthenticator::continueSignCommandAfterResponseReceived, it will retry the current command
with the AppID if it exists when SW_WRONG_DATA is received from devices. Noted, it will not set
the AuthenticationExtensionsClientOutputs::appid to false in any circumstances. In other words, the
field will be empty if AppID is supplied in AuthenticationExtensionsClientInputs and not used.

  • UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:

(WebKit::LocalAuthenticator::continueMakeCredentialAfterAttested):
(WebKit::LocalAuthenticator::continueGetAssertionAfterUserConsented):

  • UIProcess/WebAuthentication/fido/U2fHidAuthenticator.cpp:

(WebKit::U2fHidAuthenticator::issueSignCommand):
(WebKit::U2fHidAuthenticator::continueSignCommandAfterResponseReceived):

  • UIProcess/WebAuthentication/fido/U2fHidAuthenticator.h:

Tools:

Add a test that covers the new flag of convertToU2fSignCommand.

  • TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp:

(TestWebKitAPI::TEST):

  • TestWebKitAPI/Tests/WebCore/FidoTestData.h:
  • TestWebKitAPI/Tests/WebCore/U2fCommandConstructorTest.cpp:

(TestWebKitAPI::TEST):

LayoutTests:

  • http/wpt/webauthn/public-key-credential-create-success-hid.https.html:
  • http/wpt/webauthn/public-key-credential-create-success-local.https.html:
  • http/wpt/webauthn/public-key-credential-create-success-u2f.https.html:
  • http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt:
  • http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html:
  • http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt:
  • http/wpt/webauthn/public-key-credential-get-failure.https.html:
  • http/wpt/webauthn/public-key-credential-get-success-hid.https.html:
  • http/wpt/webauthn/public-key-credential-get-success-local.https.html:
  • http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt:
  • http/wpt/webauthn/public-key-credential-get-success-u2f.https.html:
Location:
trunk
Files:
40 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r243182 r243193  
     12019-03-19  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthN] Implement FIDO AppID extension
     4        https://bugs.webkit.org/show_bug.cgi?id=143491
     5        <rdar://problem/48298273>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        * http/wpt/webauthn/public-key-credential-create-success-hid.https.html:
     10        * http/wpt/webauthn/public-key-credential-create-success-local.https.html:
     11        * http/wpt/webauthn/public-key-credential-create-success-u2f.https.html:
     12        * http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt:
     13        * http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html:
     14        * http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt:
     15        * http/wpt/webauthn/public-key-credential-get-failure.https.html:
     16        * http/wpt/webauthn/public-key-credential-get-success-hid.https.html:
     17        * http/wpt/webauthn/public-key-credential-get-success-local.https.html:
     18        * http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt:
     19        * http/wpt/webauthn/public-key-credential-get-success-u2f.https.html:
     20
    1212019-03-19  Ryosuke Niwa  <rniwa@webkit.org>
    222
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html

    r239958 r243193  
    1717        assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testHidCredentialIdBase64));
    1818        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
    19         assert_throws("NotSupportedError", () => { credential.getClientExtensionResults() });
     19        assert_not_exists(credential.getClientExtensionResults(), "appid");
    2020
    2121        // Check attestation
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html

    r239471 r243193  
    3131        assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testCredentialIdBase64));
    3232        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
    33         assert_throws("NotSupportedError", () => { credential.getClientExtensionResults() });
     33        assert_not_exists(credential.getClientExtensionResults(), "appid");
    3434
    3535        // Check attestation
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https.html

    r239958 r243193  
    1313        assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testU2fCredentialIdBase64));
    1414        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
    15         assert_throws("NotSupportedError", () => { credential.getClientExtensionResults() });
     15        assert_not_exists(credential.getClientExtensionResults(), "appid");
    1616
    1717        // Check attestation
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt

    r239752 r243193  
    33PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator.
    44PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2
     5PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. (AppID)
     6PASS PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2 (AppID)
    57
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html

    r239752 r243193  
    4343        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No credentials from the allowCredentials list is found in the authenticator.");
    4444    }, "PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2");
     45
     46    // With AppID extension
     47    promise_test(function(t) {
     48        const options = {
     49            publicKey: {
     50                challenge: asciiToUint8Array("123456"),
     51                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }],
     52                extensions: { appid: "" }
     53            }
     54        };
     55
     56        if (window.testRunner)
     57            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64] } });
     58        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No credentials from the allowCredentials list is found in the authenticator.");
     59    }, "PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. (AppID)");
     60
     61    promise_test(function(t) {
     62        const options = {
     63            publicKey: {
     64                challenge: asciiToUint8Array("123456"),
     65                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }],
     66                extensions: { appid: "" }
     67            }
     68        };
     69
     70        if (window.testRunner)
     71            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64] } });
     72        return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "No credentials from the allowCredentials list is found in the authenticator.");
     73    }, "PublicKeyCredential's [[get]] with no matched allow credentials in a mock hid authenticator. 2 (AppID)");
    4574</script>
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt

    r236842 r243193  
    22PASS PublicKeyCredential's [[get]] with timeout
    33PASS PublicKeyCredential's [[get]] with a mismatched RP ID
     4PASS PublicKeyCredential's [[get]] with a mismatched APP ID (invalid URLs)
     5PASS PublicKeyCredential's [[get]] with a mismatched APP ID (different protocols)
     6PASS PublicKeyCredential's [[get]] with a mismatched APP ID (different sites 1)
     7PASS PublicKeyCredential's [[get]] with a mismatched APP ID (different sites 2)
    48
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https.html

    r236842 r243193  
    3232            navigator.credentials.get(options), "The origin of the document is not a registrable domain suffix of the provided RP ID.");
    3333    }, "PublicKeyCredential's [[get]] with a mismatched RP ID");
     34
     35    promise_test(t => {
     36        const options = {
     37            publicKey: {
     38                challenge: asciiToUint8Array("123456"),
     39                extensions: { appid: "abc" }
     40            }
     41        };
     42
     43        return promiseRejects(t, "SecurityError",
     44            navigator.credentials.get(options), "The origin of the document is not authorized for the provided App ID.");
     45    }, "PublicKeyCredential's [[get]] with a mismatched APP ID (invalid URLs)");
     46
     47    promise_test(t => {
     48        const options = {
     49            publicKey: {
     50                challenge: asciiToUint8Array("123456"),
     51                extensions: { appid: "ftp://localhost" }
     52            }
     53        };
     54
     55        return promiseRejects(t, "SecurityError",
     56            navigator.credentials.get(options), "The origin of the document is not authorized for the provided App ID.");
     57    }, "PublicKeyCredential's [[get]] with a mismatched APP ID (different protocols)");
     58
     59    promise_test(t => {
     60        const options = {
     61            publicKey: {
     62                challenge: asciiToUint8Array("123456"),
     63                extensions: { appid: "https://127.0.0.1" }
     64            }
     65        };
     66
     67        return promiseRejects(t, "SecurityError",
     68            navigator.credentials.get(options), "The origin of the document is not authorized for the provided App ID.");
     69    }, "PublicKeyCredential's [[get]] with a mismatched APP ID (different sites 1)");
     70
     71    promise_test(t => {
     72        const options = {
     73            publicKey: {
     74                challenge: asciiToUint8Array("123456"),
     75                extensions: { appid: "https://haha.localhost" }
     76            }
     77        };
     78
     79        return promiseRejects(t, "SecurityError",
     80            navigator.credentials.get(options), "The origin of the document is not authorized for the provided App ID.");
     81    }, "PublicKeyCredential's [[get]] with a mismatched APP ID (different sites 2)");
    3482</script>
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html

    r239958 r243193  
    1717        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
    1818        assert_equals(credential.response.userHandle, null);
     19        assert_not_exists(credential.getClientExtensionResults(), "appid");
    1920
    2021        // Check authData
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-local.https.html

    r239471 r243193  
    2020        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
    2121        assert_equals(bytesToHexString(credential.response.userHandle), "00010203040506070809");
     22        assert_not_exists(credential.getClientExtensionResults(), "appid");
    2223
    2324        // Check authData
     
    3334                return crypto.subtle.verify({name: "ECDSA", hash: "SHA-256"}, publicKey, extractRawSignature(credential.response.signature), concatenateBuffers(credential.response.authenticatorData, hash)).then( verified => {
    3435                    assert_true(verified);
    35                     assert_throws("NotSupportedError", () => { credential.getClientExtensionResults() });
     36                    assert_not_exists(credential.getClientExtensionResults(), "appid");
    3637                });
    3738            });
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt

    r239752 r243193  
    33PASS PublicKeyCredential's [[get]] with more allow credentials in a mock hid authenticator.
    44PASS PublicKeyCredential's [[get]] with test of user presence in a mock hid authenticator.
     5PASS PublicKeyCredential's [[get]] with empty extensions in a mock hid authenticator.
     6PASS PublicKeyCredential's [[get]] with same site AppID but not used in a mock hid authenticator.
     7PASS PublicKeyCredential's [[get]] with empty AppID in a mock hid authenticator.
     8PASS PublicKeyCredential's [[get]] with an AppID in a mock hid authenticator.
     9PASS PublicKeyCredential's [[get]] with multiple credentials and AppID is not used in a mock hid authenticator.
     10PASS PublicKeyCredential's [[get]] with multiple credentials and AppID is used in a mock hid authenticator.
    511
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html

    r239958 r243193  
    55<script src="./resources/util.js"></script>
    66<script>
    7     function checkResult(credential)
     7    const defaultAppIDHash = "c2671b6eb9233197d5f2b1288a55ba4f0860f96f7199bba32fe6da7c3f0f31e5";
     8
     9    function checkResult(credential, isAppID = false, appIDHash = defaultAppIDHash)
    810    {
    911        // Check respond
     
    1315        assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
    1416        assert_equals(credential.response.userHandle, null);
     17        if (!isAppID)
     18            assert_not_exists(credential.getClientExtensionResults(), "appid");
     19        else
     20            assert_true(credential.getClientExtensionResults().appid);
    1521
    1622        // Check authData
    1723        const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));
    18         assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
     24        if (!isAppID)
     25            assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
     26        else
     27            assert_equals(bytesToHexString(authData.rpIdHash), appIDHash);
    1928        assert_equals(authData.flags, 1);
    2029        assert_equals(authData.counter, 59);
     
    6877        });
    6978    }, "PublicKeyCredential's [[get]] with test of user presence in a mock hid authenticator.");
     79
     80    // With AppID extension
     81    promise_test(t => {
     82        const options = {
     83            publicKey: {
     84                challenge: Base64URL.parse("MTIzNDU2"),
     85                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
     86                timeout: 100,
     87                extensions: { }
     88            }
     89        };
     90
     91        if (window.testRunner)
     92            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fSignResponse] } });
     93        return navigator.credentials.get(options).then(credential => {
     94            return checkResult(credential);
     95        });
     96    }, "PublicKeyCredential's [[get]] with empty extensions in a mock hid authenticator.");
     97
     98    promise_test(t => {
     99        const options = {
     100            publicKey: {
     101                challenge: Base64URL.parse("MTIzNDU2"),
     102                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
     103                timeout: 100,
     104                extensions: { appid: "https://localhost:666/appid" }
     105            }
     106        };
     107
     108        if (window.testRunner)
     109            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fSignResponse] } });
     110        return navigator.credentials.get(options).then(credential => {
     111            return checkResult(credential);
     112        });
     113    }, "PublicKeyCredential's [[get]] with same site AppID but not used in a mock hid authenticator.");
     114
     115    promise_test(t => {
     116        const options = {
     117            publicKey: {
     118                challenge: Base64URL.parse("MTIzNDU2"),
     119                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
     120                timeout: 100,
     121                extensions: { appid: "" }
     122            }
     123        };
     124
     125        if (window.testRunner)
     126            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fSignResponse] } });
     127        return navigator.credentials.get(options).then(credential => {
     128            return checkResult(credential, true);
     129        });
     130    }, "PublicKeyCredential's [[get]] with empty AppID in a mock hid authenticator.");
     131
     132    // FIXME: Sub domains need to be tested as well. However, localhost has no sub domains.
     133    promise_test(t => {
     134        const options = {
     135            publicKey: {
     136                challenge: Base64URL.parse("MTIzNDU2"),
     137                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
     138                timeout: 100,
     139                extensions: { appid: "https://localhost:666/appid" }
     140            }
     141        };
     142
     143        if (window.testRunner)
     144            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fSignResponse] } });
     145        return navigator.credentials.get(options).then(credential => {
     146            return checkResult(credential, true, "7eabc5cc3251bdc59115ef87b5f7ee74cb03747e39ba8341748565cc129c0719");
     147        });
     148    }, "PublicKeyCredential's [[get]] with an AppID in a mock hid authenticator.");
     149
     150    promise_test(t => {
     151        const options = {
     152            publicKey: {
     153                challenge: Base64URL.parse("MTIzNDU2"),
     154                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
     155                timeout: 100,
     156                extensions: { appid: "" }
     157            }
     158        };
     159
     160        if (window.testRunner)
     161            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64, testU2fSignResponse] } });
     162        return navigator.credentials.get(options).then(credential => {
     163            return checkResult(credential);
     164        });
     165    }, "PublicKeyCredential's [[get]] with multiple credentials and AppID is not used in a mock hid authenticator.");
     166
     167    promise_test(t => {
     168        const options = {
     169            publicKey: {
     170                challenge: Base64URL.parse("MTIzNDU2"),
     171                allowCredentials: [{ type: "public-key", id: Base64URL.parse(testCredentialIdBase64) }, { type: "public-key", id: Base64URL.parse(testU2fCredentialIdBase64) }],
     172                timeout: 100,
     173                extensions: { appid: "" }
     174            }
     175        };
     176
     177        if (window.testRunner)
     178            testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "success", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64, testU2fSignResponse] } });
     179        return navigator.credentials.get(options).then(credential => {
     180            return checkResult(credential, true);
     181        });
     182    }, "PublicKeyCredential's [[get]] with multiple credentials and AppID is used in a mock hid authenticator.");
     183
    70184</script>
  • trunk/Source/WebCore/CMakeLists.txt

    r243014 r243193  
    439439    Modules/webaudio/WaveShaperNode.idl
    440440
     441    Modules/webauthn/AuthenticationExtensionsClientInputs.idl
    441442    Modules/webauthn/AuthenticatorAssertionResponse.idl
    442443    Modules/webauthn/AuthenticatorAttestationResponse.idl
  • trunk/Source/WebCore/ChangeLog

    r243192 r243193  
     12019-03-19  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthN] Implement FIDO AppID extension
     4        https://bugs.webkit.org/show_bug.cgi?id=143491
     5        <rdar://problem/48298273>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        This patch adds support for FIDO AppID extension: https://www.w3.org/TR/webauthn/#sctn-appid-extension.
     10        To be noticed, this implementation follows what spec suggested in the 'Note' session and what Chrome/Firefox
     11        do in practice to avoid some unnecessary steps of
     12        https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html#determining-if-a-caller-s-facetid-is-authorized-for-an-appid.
     13
     14        In fido::convertToU2fSignCommand, the checkOnly flag is deleted as it is never used.
     15
     16        Covered by new tests in existing files.
     17
     18        * CMakeLists.txt:
     19        * DerivedSources-input.xcfilelist:
     20        * DerivedSources-output.xcfilelist:
     21        * DerivedSources.make:
     22        * Modules/webauthn/AuthenticationExtensionsClientInputs.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredential.idl.
     23        (WebCore::AuthenticationExtensionsClientInputs::encode const):
     24        (WebCore::AuthenticationExtensionsClientInputs::decode):
     25        * Modules/webauthn/AuthenticationExtensionsClientInputs.idl: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl.
     26        * Modules/webauthn/AuthenticatorCoordinator.cpp:
     27        (WebCore::AuthenticatorCoordinatorInternal::processAppIdExtension):
     28        (WebCore::AuthenticatorCoordinator::create const):
     29        (WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):
     30        * Modules/webauthn/PublicKeyCredential.cpp:
     31        (WebCore::PublicKeyCredential::tryCreate):
     32        (WebCore::PublicKeyCredential::PublicKeyCredential):
     33        (WebCore::PublicKeyCredential::getClientExtensionResults const):
     34        (WebCore::PublicKeyCredential::create): Deleted.
     35        * Modules/webauthn/PublicKeyCredential.h:
     36        * Modules/webauthn/PublicKeyCredential.idl:
     37        * Modules/webauthn/PublicKeyCredentialCreationOptions.h:
     38        * Modules/webauthn/PublicKeyCredentialCreationOptions.idl:
     39        * Modules/webauthn/PublicKeyCredentialData.h:
     40        (WebCore::PublicKeyCredentialData::encode const):
     41        (WebCore::PublicKeyCredentialData::decode):
     42        * Modules/webauthn/PublicKeyCredentialRequestOptions.h:
     43        (WebCore::PublicKeyCredentialRequestOptions::encode const):
     44        (WebCore::PublicKeyCredentialRequestOptions::decode):
     45        * Modules/webauthn/PublicKeyCredentialRequestOptions.idl:
     46        * Modules/webauthn/fido/DeviceResponseConverter.cpp:
     47        (fido::readCTAPMakeCredentialResponse):
     48        (fido::readCTAPGetAssertionResponse):
     49        * Modules/webauthn/fido/U2fCommandConstructor.cpp:
     50        (fido::convertToU2fSignCommand):
     51        * Modules/webauthn/fido/U2fCommandConstructor.h:
     52        * Modules/webauthn/fido/U2fResponseConverter.cpp:
     53        (fido::readU2fRegisterResponse):
     54        (fido::readU2fSignResponse):
     55        * Sources.txt:
     56        * WebCore.xcodeproj/project.pbxproj:
     57
    1582019-03-19  Devin Rousso  <drousso@apple.com>
    259
  • trunk/Source/WebCore/DerivedSources-input.xcfilelist

    r243014 r243193  
    305305$(PROJECT_DIR)/Modules/webaudio/ScriptProcessorNode.idl
    306306$(PROJECT_DIR)/Modules/webaudio/WaveShaperNode.idl
     307$(PROJECT_DIR)/Modules/webauthn/AuthenticationExtensionsClientInputs.idl
    307308$(PROJECT_DIR)/Modules/webauthn/AuthenticatorAssertionResponse.idl
    308309$(PROJECT_DIR)/Modules/webauthn/AuthenticatorAttestationResponse.idl
  • trunk/Source/WebCore/DerivedSources-output.xcfilelist

    r243014 r243193  
    155155$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAudioTrackMediaSource.cpp
    156156$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAudioTrackMediaSource.h
     157$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAuthenticationExtensionsClientInputs.cpp
     158$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAuthenticationExtensionsClientInputs.h
    157159$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAuthenticatorAssertionResponse.cpp
    158160$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAuthenticatorAssertionResponse.h
  • trunk/Source/WebCore/DerivedSources.make

    r243169 r243193  
    352352    $(WebCore)/Modules/webaudio/ScriptProcessorNode.idl \
    353353    $(WebCore)/Modules/webaudio/WaveShaperNode.idl \
     354    $(WebCore)/Modules/webauthn/AuthenticationExtensionsClientInputs.idl \
    354355    $(WebCore)/Modules/webauthn/AuthenticatorAssertionResponse.idl \
    355356    $(WebCore)/Modules/webauthn/AuthenticatorAttestationResponse.idl \
  • trunk/Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientInputs.h

    r243192 r243193  
    11/*
    2  * Copyright (C) 2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2424 */
    2525
    26 typedef boolean AuthenticationExtensions;
     26#pragma once
    2727
    28 [
    29     Conditional=WEB_AUTHN,
    30     EnabledAtRuntime=WebAuthentication,
    31     Exposed=Window,
    32     SecureContext,
    33 ] interface PublicKeyCredential : BasicCredential {
    34     [SameObject] readonly attribute ArrayBuffer rawId;
    35     [SameObject] readonly attribute AuthenticatorResponse response;
    36     [MayThrowException] AuthenticationExtensions getClientExtensionResults();
     28#if ENABLE(WEB_AUTHN)
    3729
    38     [CallWith=Document] static Promise<boolean> isUserVerifyingPlatformAuthenticatorAvailable();
     30#include <wtf/text/WTFString.h>
     31
     32namespace WebCore {
     33
     34struct AuthenticationExtensionsClientInputs {
     35    String appid;
     36
     37    template<class Encoder> void encode(Encoder&) const;
     38    template<class Decoder> static Optional<AuthenticationExtensionsClientInputs> decode(Decoder&);
    3939};
     40
     41template<class Encoder>
     42void AuthenticationExtensionsClientInputs::encode(Encoder& encoder) const
     43{
     44    encoder << appid;
     45}
     46
     47template<class Decoder>
     48Optional<AuthenticationExtensionsClientInputs> AuthenticationExtensionsClientInputs::decode(Decoder& decoder)
     49{
     50    AuthenticationExtensionsClientInputs result;
     51
     52    Optional<String> appid;
     53    decoder >> appid;
     54    if (!appid)
     55        return WTF::nullopt;
     56    result.appid = WTFMove(*appid);
     57
     58    return result;
     59}
     60
     61} // namespace WebCore
     62
     63#endif // ENABLE(WEB_AUTHN)
  • trunk/Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientInputs.idl

    r243192 r243193  
    11/*
    2  * Copyright (C) 2018 Apple Inc. All rights reserved.
     2 * Copyright (C) 2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2626[
    2727    Conditional=WEB_AUTHN,
    28 ] dictionary PublicKeyCredentialRequestOptions {
    29     required BufferSource challenge;
    30     unsigned long timeout;
    31     USVString rpId;
    32     sequence<PublicKeyCredentialDescriptor> allowCredentials = [];
    33     UserVerificationRequirement userVerification = "preferred";
    34     // Not support yet.
    35     // AuthenticationExtensions extensions;
     28] dictionary AuthenticationExtensionsClientInputs {
     29    USVString appid;
    3630};
  • trunk/Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp

    r239665 r243193  
    3838#include "PublicKeyCredentialData.h"
    3939#include "PublicKeyCredentialRequestOptions.h"
     40#include "RegistrableDomain.h"
     41#include "SchemeRegistry.h"
    4042#include "SecurityOrigin.h"
    4143#include <pal/crypto/CryptoDigest.h>
     
    7981}
    8082
     83// The following roughly implements Step 1-3 of the spec to avoid the complexity of making unnecessary network requests:
     84// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html#determining-if-a-caller-s-facetid-is-authorized-for-an-appid
     85// It follows what Chrome and Firefox do, see:
     86// https://bugzilla.mozilla.org/show_bug.cgi?id=1244959#c8
     87// https://bugs.chromium.org/p/chromium/issues/detail?id=818303
     88static String processAppIdExtension(const SecurityOrigin& facetId, const String& appId)
     89{
     90    // Step 1. Skipped since facetId should always be secure origins.
     91    ASSERT(SchemeRegistry::shouldTreatURLSchemeAsSecure(facetId.protocol()));
     92
     93    // Step 2. Follow Chrome and Firefox to use the origin directly without adding a trailing slash.
     94    if (appId.isEmpty())
     95        return facetId.toString();
     96
     97    // Step 3. Relax the comparison to same site.
     98    URL appIdURL(URL(), appId);
     99    if (!appIdURL.isValid() || facetId.protocol() != appIdURL.protocol() || RegistrableDomain(appIdURL) != RegistrableDomain::uncheckedCreateFromHost(facetId.host()))
     100        return String();
     101    return appId;
     102}
     103
    81104} // namespace AuthenticatorCoordinatorInternal
    82105
     
    96119
    97120    // The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
    98     // FIXME: Extensions are not supported yet. Skip Step 11-12.
     121    // Extensions are not supported. Skip Step 11-12.
    99122    // Step 1, 3, 16 are handled by the caller.
    100123    // Step 2.
     
    159182
    160183    // The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
    161     // FIXME: Extensions are not supported yet. Skip Step 8-9.
    162184    // Step 1, 3, 13 are handled by the caller.
    163185    // Step 2.
     
    177199    if (options.rpId.isEmpty())
    178200        options.rpId = callerOrigin.host();
     201
     202    // Step 8-9.
     203    // Only FIDO AppID Extension is supported.
     204    if (options.extensions && !options.extensions->appid.isNull()) {
     205        // The following implements https://www.w3.org/TR/webauthn/#sctn-appid-extension as of 4 March 2019.
     206        auto appid = processAppIdExtension(callerOrigin, options.extensions->appid);
     207        if (!appid) {
     208            promise.reject(Exception { SecurityError, "The origin of the document is not authorized for the provided App ID."_s });
     209            return;
     210        }
     211        options.extensions->appid = appid;
     212    }
    179213
    180214    // Step 10-12.
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredential.cpp

    r238238 r243193  
    4141namespace WebCore {
    4242
    43 Ref<PublicKeyCredential> PublicKeyCredential::create(Ref<ArrayBuffer>&& id, Ref<AuthenticatorResponse>&& response)
    44 {
    45     return adoptRef(*new PublicKeyCredential(WTFMove(id), WTFMove(response)));
    46 }
    47 
    4843RefPtr<PublicKeyCredential> PublicKeyCredential::tryCreate(const PublicKeyCredentialData& data)
    4944{
     
    5550            return nullptr;
    5651
    57         return adoptRef(*new PublicKeyCredential(data.rawId.releaseNonNull(), AuthenticatorAttestationResponse::create(data.clientDataJSON.releaseNonNull(), data.attestationObject.releaseNonNull())));
     52        return adoptRef(*new PublicKeyCredential(data.rawId.releaseNonNull(), AuthenticatorAttestationResponse::create(data.clientDataJSON.releaseNonNull(), data.attestationObject.releaseNonNull()), { data.appid }));
    5853    }
    5954
     
    6156        return nullptr;
    6257
    63     return adoptRef(*new PublicKeyCredential(data.rawId.releaseNonNull(), AuthenticatorAssertionResponse::create(data.clientDataJSON.releaseNonNull(), data.authenticatorData.releaseNonNull(), data.signature.releaseNonNull(), WTFMove(data.userHandle))));
     58    return adoptRef(*new PublicKeyCredential(data.rawId.releaseNonNull(), AuthenticatorAssertionResponse::create(data.clientDataJSON.releaseNonNull(), data.authenticatorData.releaseNonNull(), data.signature.releaseNonNull(), WTFMove(data.userHandle)), { data.appid }));
    6459}
    6560
    66 PublicKeyCredential::PublicKeyCredential(Ref<ArrayBuffer>&& id, Ref<AuthenticatorResponse>&& response)
     61PublicKeyCredential::PublicKeyCredential(Ref<ArrayBuffer>&& id, Ref<AuthenticatorResponse>&& response, AuthenticationExtensionsClientOutputs&& extensions)
    6762    : BasicCredential(WTF::base64URLEncode(id->data(), id->byteLength()), Type::PublicKey, Discovery::Remote)
    6863    , m_rawId(WTFMove(id))
    6964    , m_response(WTFMove(response))
     65    , m_extensions(WTFMove(extensions))
    7066{
    7167}
    7268
    73 ExceptionOr<bool> PublicKeyCredential::getClientExtensionResults() const
     69PublicKeyCredential::AuthenticationExtensionsClientOutputs PublicKeyCredential::getClientExtensionResults() const
    7470{
    75     return Exception { NotSupportedError };
     71    return m_extensions;
    7672}
    7773
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredential.h

    r235888 r243193  
    4343class PublicKeyCredential final : public BasicCredential {
    4444public:
    45     static Ref<PublicKeyCredential> create(Ref<ArrayBuffer>&& id, Ref<AuthenticatorResponse>&&);
     45    struct AuthenticationExtensionsClientOutputs {
     46        Optional<bool> appid;
     47    };
     48
    4649    static RefPtr<PublicKeyCredential> tryCreate(const PublicKeyCredentialData&);
    4750
    4851    ArrayBuffer* rawId() const { return m_rawId.ptr(); }
    4952    AuthenticatorResponse* response() const { return m_response.ptr(); }
    50     // Not support yet. Always throws.
    51     ExceptionOr<bool> getClientExtensionResults() const;
     53    AuthenticationExtensionsClientOutputs getClientExtensionResults() const;
    5254
    5355    static void isUserVerifyingPlatformAuthenticatorAvailable(Document&, DOMPromiseDeferred<IDLBoolean>&&);
    5456
    5557private:
    56     PublicKeyCredential(Ref<ArrayBuffer>&& id, Ref<AuthenticatorResponse>&&);
     58    PublicKeyCredential(Ref<ArrayBuffer>&& id, Ref<AuthenticatorResponse>&&, AuthenticationExtensionsClientOutputs&&);
    5759
    5860    Type credentialType() const final { return Type::PublicKey; }
     
    6062    Ref<ArrayBuffer> m_rawId;
    6163    Ref<AuthenticatorResponse> m_response;
     64    AuthenticationExtensionsClientOutputs m_extensions;
    6265};
    6366
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredential.idl

    r235888 r243193  
    2424 */
    2525
    26 typedef boolean AuthenticationExtensions;
    27 
    2826[
    2927    Conditional=WEB_AUTHN,
     
    3432    [SameObject] readonly attribute ArrayBuffer rawId;
    3533    [SameObject] readonly attribute AuthenticatorResponse response;
    36     [MayThrowException] AuthenticationExtensions getClientExtensionResults();
     34    AuthenticationExtensionsClientOutputs getClientExtensionResults();
    3735
    3836    [CallWith=Document] static Promise<boolean> isUserVerifyingPlatformAuthenticatorAvailable();
    3937};
     38
     39[
     40    Conditional=WEB_AUTHN,
     41    JSGenerateToJSObject,
     42] dictionary AuthenticationExtensionsClientOutputs {
     43    boolean appid;
     44};
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.h

    r239427 r243193  
    2828#if ENABLE(WEB_AUTHN)
    2929
     30#include "AuthenticationExtensionsClientInputs.h"
    3031#include "BufferSource.h"
    3132#include "PublicKeyCredentialDescriptor.h"
     
    8485    Vector<PublicKeyCredentialDescriptor> excludeCredentials;
    8586    Optional<AuthenticatorSelectionCriteria> authenticatorSelection;
     87    Optional<AuthenticationExtensionsClientInputs> extensions; // A place holder, but never used.
    8688
    8789    template<class Encoder> void encode(Encoder&) const;
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.idl

    r237983 r243193  
    4040    // Always "direct" for us.
    4141    // AttestationConveyancePreference attestation = "none";
    42     // Not support yet.
    43     // AuthenticationExtensions extensions;
     42    AuthenticationExtensionsClientInputs extensions;
    4443};
    4544
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredentialData.h

    r239427 r243193  
    5050    mutable RefPtr<ArrayBuffer> userHandle;
    5151
     52    // Extensions
     53    Optional<bool> appid;
     54
    5255    template<class Encoder> void encode(Encoder&) const;
    5356    template<class Decoder> static Optional<PublicKeyCredentialData> decode(Decoder&);
     
    8184    encoder << static_cast<uint64_t>(signature->byteLength());
    8285    encoder.encodeFixedLengthData(reinterpret_cast<const uint8_t*>(signature->data()), signature->byteLength(), 1);
     86
     87    // Encode AppID before user handle to avoid the userHandle flag.
     88    encoder << appid;
    8389
    8490    if (!userHandle) {
     
    149155        return WTF::nullopt;
    150156
     157    Optional<Optional<bool>> appid;
     158    decoder >> appid;
     159    if (!appid)
     160        return WTF::nullopt;
     161    result.appid = WTFMove(*appid);
     162
    151163    Optional<bool> hasUserHandle;
    152164    decoder >> hasUserHandle;
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.h

    r239427 r243193  
    2828#if ENABLE(WEB_AUTHN)
    2929
     30#include "AuthenticationExtensionsClientInputs.h"
    3031#include "BufferSource.h"
    3132#include "PublicKeyCredentialDescriptor.h"
     
    4142    Vector<PublicKeyCredentialDescriptor> allowCredentials;
    4243    UserVerificationRequirement userVerification { UserVerificationRequirement::Preferred };
     44    mutable Optional<AuthenticationExtensionsClientInputs> extensions;
    4345
    4446    template<class Encoder> void encode(Encoder&) const;
     
    5052void PublicKeyCredentialRequestOptions::encode(Encoder& encoder) const
    5153{
    52     encoder << timeout << rpId << allowCredentials << userVerification;
     54    encoder << timeout << rpId << allowCredentials << userVerification << extensions;
    5355}
    5456
     
    7577    result.userVerification = WTFMove(*userVerification);
    7678
     79    Optional<Optional<AuthenticationExtensionsClientInputs>> extensions;
     80    decoder >> extensions;
     81    if (!extensions)
     82        return WTF::nullopt;
     83    result.extensions = WTFMove(*extensions);
     84
    7785    return result;
    7886}
  • trunk/Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl

    r237983 r243193  
    3232    sequence<PublicKeyCredentialDescriptor> allowCredentials = [];
    3333    UserVerificationRequirement userVerification = "preferred";
    34     // Not support yet.
    35     // AuthenticationExtensions extensions;
     34    AuthenticationExtensionsClientInputs extensions;
    3635};
  • trunk/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp

    r239665 r243193  
    122122    auto attestationObject = cbor::CBORWriter::write(CBOR(WTFMove(attestationObjectMap)));
    123123
    124     return PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.value().data(), attestationObject.value().size()), nullptr, nullptr, nullptr };
     124    return PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.value().data(), attestationObject.value().size()), nullptr, nullptr, nullptr, WTF::nullopt };
    125125}
    126126
     
    171171    }
    172172
    173     return PublicKeyCredentialData { WTFMove(credentialId), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(signature.data(), signature.size()), WTFMove(userHandle) };
     173    return PublicKeyCredentialData { WTFMove(credentialId), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(signature.data(), signature.size()), WTFMove(userHandle), WTF::nullopt };
    174174}
    175175
  • trunk/Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.cpp

    r242776 r243193  
    115115}
    116116
    117 Optional<Vector<uint8_t>> convertToU2fSignCommand(const Vector<uint8_t>& clientDataHash, const PublicKeyCredentialRequestOptions& request, const Vector<uint8_t>& keyHandle, bool checkOnly)
     117Optional<Vector<uint8_t>> convertToU2fSignCommand(const Vector<uint8_t>& clientDataHash, const PublicKeyCredentialRequestOptions& request, const Vector<uint8_t>& keyHandle, bool isAppId)
    118118{
    119119    if (!isConvertibleToU2fSignCommand(request))
    120120        return WTF::nullopt;
    121121
    122     return constructU2fSignCommand(produceRpIdHash(request.rpId), clientDataHash, keyHandle, checkOnly);
     122    if (!isAppId)
     123        return constructU2fSignCommand(produceRpIdHash(request.rpId), clientDataHash, keyHandle, false);
     124    ASSERT(request.extensions && !request.extensions->appid.isNull());
     125    return constructU2fSignCommand(produceRpIdHash(request.extensions->appid), clientDataHash, keyHandle, false);
    123126}
    124127
  • trunk/Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.h

    r242776 r243193  
    6363
    6464// Extracts APDU encoded U2F sign command from PublicKeyCredentialRequestOptions.
    65 WEBCORE_EXPORT Optional<Vector<uint8_t>> convertToU2fSignCommand(const Vector<uint8_t>& clientDataHash, const WebCore::PublicKeyCredentialRequestOptions&, const Vector<uint8_t>& keyHandle, bool checkOnly = false);
     65WEBCORE_EXPORT Optional<Vector<uint8_t>> convertToU2fSignCommand(const Vector<uint8_t>& clientDataHash, const WebCore::PublicKeyCredentialRequestOptions&, const Vector<uint8_t>& keyHandle, bool isAppId = false);
    6666
    6767WEBCORE_EXPORT Vector<uint8_t> constructBogusU2fRegistrationCommand();
  • trunk/Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.cpp

    r239752 r243193  
    171171    auto attestationObject = buildAttestationObject(WTFMove(authData), "fido-u2f", WTFMove(fidoAttestationStatement));
    172172
    173     return PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.data(), attestationObject.size()), nullptr, nullptr, nullptr };
     173    return PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.data(), attestationObject.size()), nullptr, nullptr, nullptr, WTF::nullopt };
    174174}
    175175
     
    187187    auto authData = buildAuthData(rpId, flags, counter, { });
    188188
    189     return PublicKeyCredentialData { ArrayBuffer::create(keyHandle.data(), keyHandle.size()), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(u2fData.data() + signatureIndex, u2fData.size() - signatureIndex), nullptr };
     189    return PublicKeyCredentialData { ArrayBuffer::create(keyHandle.data(), keyHandle.size()), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(u2fData.data() + signatureIndex, u2fData.size() - signatureIndex), nullptr, WTF::nullopt };
    190190}
    191191
  • trunk/Source/WebCore/Sources.txt

    r243185 r243193  
    25682568JSAudioParam.cpp
    25692569JSAudioProcessingEvent.cpp
     2570JSAuthenticationExtensionsClientInputs.cpp
    25702571JSAuthenticatorAssertionResponse.cpp
    25712572JSAuthenticatorAttestationResponse.cpp
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r243185 r243193  
    18651865                57D8462E1FEAF69900CA3682 /* PublicKeyCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 57D8462B1FEAF68F00CA3682 /* PublicKeyCredential.h */; settings = {ATTRIBUTES = (Private, ); }; };
    18661866                57D846351FEAFCD300CA3682 /* JSPublicKeyCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 57D846301FEAFC2F00CA3682 /* JSPublicKeyCredential.h */; };
     1867                57DA47B0224034E4002A4612 /* AuthenticationExtensionsClientInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DA47A522401E0F002A4612 /* AuthenticationExtensionsClientInputs.h */; settings = {ATTRIBUTES = (Private, ); }; };
    18671868                57DCED74214305F00016B847 /* PublicKeyCredentialData.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED72214305F00016B847 /* PublicKeyCredentialData.h */; settings = {ATTRIBUTES = (Private, ); }; };
    18681869                57DCED9021487FF70016B847 /* AuthenticatorTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED8C21487EDB0016B847 /* AuthenticatorTransport.h */; settings = {ATTRIBUTES = (Private, ); }; };
     
    88668867                57D846301FEAFC2F00CA3682 /* JSPublicKeyCredential.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPublicKeyCredential.h; sourceTree = "<group>"; };
    88678868                57D846311FEAFC2F00CA3682 /* JSPublicKeyCredential.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPublicKeyCredential.cpp; sourceTree = "<group>"; };
     8869                57DA47A522401E0F002A4612 /* AuthenticationExtensionsClientInputs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthenticationExtensionsClientInputs.h; sourceTree = "<group>"; };
     8870                57DA47A722401E0F002A4612 /* AuthenticationExtensionsClientInputs.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = AuthenticationExtensionsClientInputs.idl; sourceTree = "<group>"; };
     8871                57DA47AC224032DC002A4612 /* JSAuthenticationExtensionsClientInputs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSAuthenticationExtensionsClientInputs.cpp; sourceTree = "<group>"; };
     8872                57DA47AD224032DD002A4612 /* JSAuthenticationExtensionsClientInputs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSAuthenticationExtensionsClientInputs.h; sourceTree = "<group>"; };
    88688873                57DCED72214305F00016B847 /* PublicKeyCredentialData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PublicKeyCredentialData.h; sourceTree = "<group>"; };
    88698874                57DCED8C21487EDB0016B847 /* AuthenticatorTransport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthenticatorTransport.h; sourceTree = "<group>"; };
     
    1988019885                                57303BB32006C6ED00355965 /* cbor */,
    1988119886                                578A4BFA2166AE0000D08F34 /* fido */,
     19887                                57DA47A522401E0F002A4612 /* AuthenticationExtensionsClientInputs.h */,
     19888                                57DA47A722401E0F002A4612 /* AuthenticationExtensionsClientInputs.idl */,
    1988219889                                57303C272009B2FC00355965 /* AuthenticatorAssertionResponse.h */,
    1988319890                                57303C292009B2FC00355965 /* AuthenticatorAssertionResponse.idl */,
     
    1991619923                        isa = PBXGroup;
    1991719924                        children = (
     19925                                57DA47AC224032DC002A4612 /* JSAuthenticationExtensionsClientInputs.cpp */,
     19926                                57DA47AD224032DD002A4612 /* JSAuthenticationExtensionsClientInputs.h */,
    1991819927                                57303C2E2009B7DA00355965 /* JSAuthenticatorAssertionResponse.cpp */,
    1991919928                                57303C2D2009B7D900355965 /* JSAuthenticatorAssertionResponse.h */,
     
    2860928618                                934F713A0D5A6F1000018D69 /* AuthenticationChallengeBase.h in Headers */,
    2861028619                                E124748410AA161D00B79493 /* AuthenticationClient.h in Headers */,
     28620                                57DA47B0224034E4002A4612 /* AuthenticationExtensionsClientInputs.h in Headers */,
    2861128621                                514C764C0CE9234E007EF3CD /* AuthenticationMac.h in Headers */,
    2861228622                                57303C2C2009B4A800355965 /* AuthenticatorAssertionResponse.h in Headers */,
  • trunk/Source/WebKit/ChangeLog

    r243186 r243193  
     12019-03-19  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthN] Implement FIDO AppID extension
     4        https://bugs.webkit.org/show_bug.cgi?id=143491
     5        <rdar://problem/48298273>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        In U2fHidAuthenticator::continueSignCommandAfterResponseReceived, it will retry the current command
     10        with the AppID if it exists when SW_WRONG_DATA is received from devices. Noted, it will not set
     11        the AuthenticationExtensionsClientOutputs::appid to false in any circumstances. In other words, the
     12        field will be empty if AppID is supplied in AuthenticationExtensionsClientInputs and not used.
     13
     14        * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
     15        (WebKit::LocalAuthenticator::continueMakeCredentialAfterAttested):
     16        (WebKit::LocalAuthenticator::continueGetAssertionAfterUserConsented):
     17        * UIProcess/WebAuthentication/fido/U2fHidAuthenticator.cpp:
     18        (WebKit::U2fHidAuthenticator::issueSignCommand):
     19        (WebKit::U2fHidAuthenticator::continueSignCommandAfterResponseReceived):
     20        * UIProcess/WebAuthentication/fido/U2fHidAuthenticator.h:
     21
    1222019-03-19  Ross Kirsling  <ross.kirsling@sony.com>
    223
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm

    r242308 r243193  
    327327    auto attestationObject = buildAttestationObject(WTFMove(authData), "Apple", WTFMove(attestationStatementMap));
    328328
    329     receiveRespond(PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.data(), attestationObject.size()), nullptr, nullptr, nullptr });
     329    receiveRespond(PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.data(), attestationObject.size()), nullptr, nullptr, nullptr, WTF::nullopt });
    330330#endif // !PLATFORM(IOS_FAMILY)
    331331}
     
    464464
    465465    // Step 13.
    466     receiveRespond(PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(signature.data(), signature.size()), ArrayBuffer::create(userhandle.data(), userhandle.size()) });
     466    receiveRespond(PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(signature.data(), signature.size()), ArrayBuffer::create(userhandle.data(), userhandle.size()), WTF::nullopt });
    467467#endif // !PLATFORM(IOS_FAMILY)
    468468}
  • trunk/Source/WebKit/UIProcess/WebAuthentication/fido/U2fHidAuthenticator.cpp

    r239752 r243193  
    102102        return;
    103103    }
    104     auto u2fCmd = convertToU2fSignCommand(requestData().hash, requestData().requestOptions, requestData().requestOptions.allowCredentials[index].idVector);
     104    auto u2fCmd = convertToU2fSignCommand(requestData().hash, requestData().requestOptions, requestData().requestOptions.allowCredentials[index].idVector, m_isAppId);
    105105    ASSERT(u2fCmd);
    106106    issueNewCommand(WTFMove(*u2fCmd), CommandType::SignCommand);
     
    201201    switch (apduResponse.status()) {
    202202    case ApduResponse::Status::SW_NO_ERROR: {
    203         auto response = readU2fSignResponse(requestData().requestOptions.rpId, requestData().requestOptions.allowCredentials[m_nextListIndex - 1].idVector, apduResponse.data());
     203        Optional<PublicKeyCredentialData> response;
     204        if (m_isAppId) {
     205            ASSERT(requestData().requestOptions.extensions && !requestData().requestOptions.extensions->appid.isNull());
     206            response = readU2fSignResponse(requestData().requestOptions.extensions->appid, requestData().requestOptions.allowCredentials[m_nextListIndex - 1].idVector, apduResponse.data());
     207        } else
     208            response = readU2fSignResponse(requestData().requestOptions.rpId, requestData().requestOptions.allowCredentials[m_nextListIndex - 1].idVector, apduResponse.data());
    204209        if (!response) {
    205210            receiveRespond(ExceptionData { UnknownError, "Couldn't parse the U2F sign response."_s });
    206211            return;
    207212        }
     213        if (m_isAppId)
     214            response->appid = m_isAppId;
     215
    208216        receiveRespond(WTFMove(*response));
    209217        return;
     
    213221        m_retryTimer.startOneShot(Seconds::fromMilliseconds(retryTimeOutValueMs));
    214222        return;
    215     default:
     223    case ApduResponse::Status::SW_WRONG_DATA:
     224        if (requestData().requestOptions.extensions && !requestData().requestOptions.extensions->appid.isNull()) {
     225            if (!m_isAppId) {
     226                m_isAppId = true;
     227                issueSignCommand(m_nextListIndex - 1);
     228                return;
     229            }
     230            m_isAppId = false;
     231        }
    216232        issueSignCommand(m_nextListIndex++);
     233        return;
     234    default:
     235        issueSignCommand(m_nextListIndex++);
    217236    }
    218237}
  • trunk/Source/WebKit/UIProcess/WebAuthentication/fido/U2fHidAuthenticator.h

    r239752 r243193  
    7575    CommandType m_lastCommandType;
    7676    size_t m_nextListIndex { 0 };
     77    bool m_isAppId { false };
    7778};
    7879
  • trunk/Tools/ChangeLog

    r243188 r243193  
     12019-03-19  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthN] Implement FIDO AppID extension
     4        https://bugs.webkit.org/show_bug.cgi?id=143491
     5        <rdar://problem/48298273>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        Add a test that covers the new flag of convertToU2fSignCommand.
     10
     11        * TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp:
     12        (TestWebKitAPI::TEST):
     13        * TestWebKitAPI/Tests/WebCore/FidoTestData.h:
     14        * TestWebKitAPI/Tests/WebCore/U2fCommandConstructorTest.cpp:
     15        (TestWebKitAPI::TEST):
     16
    1172019-03-19  Keith Rollin  <krollin@apple.com>
    218
  • trunk/Tools/TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp

    r239427 r243193  
    6060    PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria selection { PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform, true, UserVerificationRequirement::Preferred };
    6161
    62     PublicKeyCredentialCreationOptions options { rp, user, { }, params, WTF::nullopt, { }, selection };
     62    PublicKeyCredentialCreationOptions options { rp, user, { }, params, WTF::nullopt, { }, selection, WTF::nullopt };
    6363    Vector<uint8_t> hash;
    6464    hash.append(TestData::kClientDataHash, sizeof(TestData::kClientDataHash));
  • trunk/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h

    r239752 r243193  
    101101    0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF,
    102102    0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE,
     103    // Key handle length
     104    0x40,
     105    // Key handle
     106    0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26,
     107    0x35, 0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3,
     108    0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94,
     109    0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64,
     110    0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08,
     111    0xFE, 0x42, 0x00, 0x38,
     112    // Max response length
     113    0x00, 0x00,
     114};
     115
     116constexpr uint8_t kU2fAppIDSignCommandApdu[] = {
     117    // CLA, INS, P1, P2 APDU instruction parameters
     118    0x00, 0x02, 0x03, 0x00,
     119    // Data Length (3 bytes in big endian order)
     120    0x00, 0x00, 0x81,
     121    // Challenge parameter -- see kClientDataHash
     122    0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, 0x42,
     123    0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05,
     124    0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41,
     125    // Application parameter
     126    0xc9, 0x34, 0x02, 0x87, 0x08, 0x3d, 0x64, 0xde, 0xed, 0x17, 0x1b, 0xbb,
     127    0xd7, 0x60, 0x10, 0xae, 0xc5, 0x65, 0x3e, 0x78, 0xfc, 0xd0, 0x31, 0x88,
     128    0xd0, 0xbf, 0x70, 0x16, 0x9a, 0x46, 0x91, 0xda,
    103129    // Key handle length
    104130    0x40,
  • trunk/Tools/TestWebKitAPI/Tests/WebCore/U2fCommandConstructorTest.cpp

    r239665 r243193  
    177177}
    178178
     179TEST(U2fCommandConstructorTest, TestConvertCtapGetAssertionWithAppIDToU2fSignRequest)
     180{
     181    auto getAssertionReq = constructGetAssertionRequest();
     182    PublicKeyCredentialDescriptor credentialDescriptor;
     183    credentialDescriptor.type = PublicKeyCredentialType::PublicKey;
     184    credentialDescriptor.idVector = convertBytesToVector(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle));
     185    Vector<PublicKeyCredentialDescriptor> allowedList;
     186    allowedList.append(WTFMove(credentialDescriptor));
     187    getAssertionReq.allowCredentials = WTFMove(allowedList);
     188    EXPECT_TRUE(isConvertibleToU2fSignCommand(getAssertionReq));
     189
     190    // AppID
     191    WebCore::AuthenticationExtensionsClientInputs extensions;
     192    extensions.appid = "https://www.example.com/appid";
     193    getAssertionReq.extensions = WTFMove(extensions);
     194
     195    const auto u2fSignCommand = convertToU2fSignCommand(convertBytesToVector(TestData::kClientDataHash, sizeof(TestData::kClientDataHash)), getAssertionReq, convertBytesToVector(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle)), true);
     196    ASSERT_TRUE(u2fSignCommand);
     197    EXPECT_EQ(*u2fSignCommand, convertBytesToVector(TestData::kU2fAppIDSignCommandApdu, sizeof(TestData::kU2fAppIDSignCommandApdu)));
     198}
     199
    179200TEST(U2fCommandConstructorTest, TestU2fSignAllowListRequirement)
    180201{
Note: See TracChangeset for help on using the changeset viewer.