Source/WebCore/ChangeLog

 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 NOBODY (OOPS!).
 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 unncessary 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 Covered by new tests in existing files.
 15
 16 * DerivedSources-input.xcfilelist:
 17 * DerivedSources-output.xcfilelist:
 18 * DerivedSources.make:
 19 * Modules/webauthn/AuthenticationExtensionsClientInputs.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredential.idl.
 20 (WebCore::AuthenticationExtensionsClientInputs::encode const):
 21 (WebCore::AuthenticationExtensionsClientInputs::decode):
 22 * Modules/webauthn/AuthenticationExtensionsClientInputs.idl: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl.
 23 * Modules/webauthn/AuthenticatorCoordinator.cpp:
 24 (WebCore::AuthenticatorCoordinatorInternal::processAppIdExtension):
 25 (WebCore::AuthenticatorCoordinator::create const):
 26 (WebCore::AuthenticatorCoordinator::discoverFromExternalSource const):
 27 * Modules/webauthn/PublicKeyCredential.cpp:
 28 (WebCore::PublicKeyCredential::tryCreate):
 29 (WebCore::PublicKeyCredential::PublicKeyCredential):
 30 (WebCore::PublicKeyCredential::getClientExtensionResults const):
 31 (WebCore::PublicKeyCredential::create): Deleted.
 32 * Modules/webauthn/PublicKeyCredential.h:
 33 * Modules/webauthn/PublicKeyCredential.idl:
 34 * Modules/webauthn/PublicKeyCredentialCreationOptions.h:
 35 * Modules/webauthn/PublicKeyCredentialCreationOptions.idl:
 36 * Modules/webauthn/PublicKeyCredentialData.h:
 37 (WebCore::PublicKeyCredentialData::encode const):
 38 (WebCore::PublicKeyCredentialData::decode):
 39 * Modules/webauthn/PublicKeyCredentialRequestOptions.h:
 40 (WebCore::PublicKeyCredentialRequestOptions::encode const):
 41 (WebCore::PublicKeyCredentialRequestOptions::decode):
 42 * Modules/webauthn/PublicKeyCredentialRequestOptions.idl:
 43 * Modules/webauthn/fido/DeviceResponseConverter.cpp:
 44 (fido::readCTAPMakeCredentialResponse):
 45 (fido::readCTAPGetAssertionResponse):
 46 * Modules/webauthn/fido/U2fCommandConstructor.cpp:
 47 (fido::convertToU2fSignCommand):
 48 * Modules/webauthn/fido/U2fCommandConstructor.h:
 49 * Modules/webauthn/fido/U2fResponseConverter.cpp:
 50 (fido::readU2fRegisterResponse):
 51 (fido::readU2fSignResponse):
 52 * Sources.txt:
 53 * WebCore.xcodeproj/project.pbxproj:
 54
1552019-03-19 Ryosuke Niwa <rniwa@webkit.org>
256
357 Reparenting during a mutation event inside appendChild could result in a circular DOM tree

Source/WebKit/ChangeLog

 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 NOBODY (OOPS!).
 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 Chris Dumez <cdumez@apple.com>
223
324 Unreviewed build fix after r243173.

Source/WebCore/DerivedSources-input.xcfilelist

@@$(PROJECT_DIR)/Modules/webaudio/PannerNode.idl
304304$(PROJECT_DIR)/Modules/webaudio/PeriodicWave.idl
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
309310$(PROJECT_DIR)/Modules/webauthn/AuthenticatorResponse.idl

Source/WebCore/DerivedSources-output.xcfilelist

@@$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAudioTrackList.cpp
154154$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAudioTrackList.h
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
159161$(BUILT_PRODUCTS_DIR)/DerivedSources/WebCore/JSAuthenticatorAttestationResponse.cpp

Source/WebCore/DerivedSources.make

@@JS_BINDING_IDLS = \
351351 $(WebCore)/Modules/webaudio/PeriodicWave.idl \
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 \
356357 $(WebCore)/Modules/webauthn/AuthenticatorResponse.idl \

Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientInputs.h

 1/*
 2 * Copyright (C) 2019 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26#pragma once
 27
 28#if ENABLE(WEB_AUTHN)
 29
 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&);
 39};
 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)

Source/WebCore/Modules/webauthn/AuthenticationExtensionsClientInputs.idl

 1/*
 2 * Copyright (C) 2019 Apple Inc. All rights reserved.
 3 *
 4 * Redistribution and use in source and binary forms, with or without
 5 * modification, are permitted provided that the following conditions
 6 * are met:
 7 * 1. Redistributions of source code must retain the above copyright
 8 * notice, this list of conditions and the following disclaimer.
 9 * 2. Redistributions in binary form must reproduce the above copyright
 10 * notice, this list of conditions and the following disclaimer in the
 11 * documentation and/or other materials provided with the distribution.
 12 *
 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 23 * THE POSSIBILITY OF SUCH DAMAGE.
 24 */
 25
 26[
 27 Conditional=WEB_AUTHN,
 28] dictionary AuthenticationExtensionsClientInputs {
 29 USVString appid;
 30};

Source/WebCore/Modules/webauthn/AuthenticatorCoordinator.cpp

3737#include "PublicKeyCredentialCreationOptions.h"
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>
4244#include <wtf/JSONValues.h>

@@static Vector<uint8_t> produceClientDataJsonHash(const ArrayBuffer& clientDataJs
7880 return crypto->computeHash();
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
83106AuthenticatorCoordinator::AuthenticatorCoordinator(std::unique_ptr<AuthenticatorCoordinatorClient>&& client)

@@void AuthenticatorCoordinator::create(const SecurityOrigin& callerOrigin, const
95118 using namespace AuthenticatorCoordinatorInternal;
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.
101124 if (!sameOriginWithAncestors) {

@@void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin&
158181 using namespace AuthenticatorCoordinatorInternal;
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.
164186 if (!sameOriginWithAncestors) {

@@void AuthenticatorCoordinator::discoverFromExternalSource(const SecurityOrigin&
177199 if (options.rpId.isEmpty())
178200 options.rpId = callerOrigin.host();
179201
 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 }
 213
180214 // Step 10-12.
181215 auto clientDataJson = produceClientDataJson(ClientDataType::Get, options.challenge, callerOrigin);
182216 auto clientDataJsonHash = produceClientDataJsonHash(clientDataJson);

Source/WebCore/Modules/webauthn/PublicKeyCredential.cpp

4040
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{
5045 if (!data.rawId || !data.clientDataJSON)

@@RefPtr<PublicKeyCredential> PublicKeyCredential::tryCreate(const PublicKeyCreden
5449 if (!data.attestationObject)
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
6055 if (!data.authenticatorData || !data.signature)
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
7874void PublicKeyCredential::isUserVerifyingPlatformAuthenticatorAvailable(Document& document, DOMPromiseDeferred<IDLBoolean>&& promise)

Source/WebCore/Modules/webauthn/PublicKeyCredential.h

@@struct PublicKeyCredentialData;
4242
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; }
5961
6062 Ref<ArrayBuffer> m_rawId;
6163 Ref<AuthenticatorResponse> m_response;
 64 AuthenticationExtensionsClientOutputs m_extensions;
6265};
6366
6467} // namespace WebCore

Source/WebCore/Modules/webauthn/PublicKeyCredential.idl

2323 * THE POSSIBILITY OF SUCH DAMAGE.
2424 */
2525
26 typedef boolean AuthenticationExtensions;
27 
2826[
2927 Conditional=WEB_AUTHN,
3028 EnabledAtRuntime=WebAuthentication,

@@typedef boolean AuthenticationExtensions;
3331] interface PublicKeyCredential : BasicCredential {
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};

Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.h

2727
2828#if ENABLE(WEB_AUTHN)
2929
 30#include "AuthenticationExtensionsClientInputs.h"
3031#include "BufferSource.h"
3132#include "PublicKeyCredentialDescriptor.h"
3233#include "PublicKeyCredentialType.h"

@@struct PublicKeyCredentialCreationOptions {
8384 Optional<unsigned> timeout;
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;
8890 template<class Decoder> static Optional<PublicKeyCredentialCreationOptions> decode(Decoder&);

Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.idl

@@typedef long COSEAlgorithmIdentifier;
3939 AuthenticatorSelectionCriteria authenticatorSelection;
4040 // Always "direct" for us.
4141 // AttestationConveyancePreference attestation = "none";
42  // Not support yet.
43  // AuthenticationExtensions extensions;
 42 AuthenticationExtensionsClientInputs extensions;
4443};
4544
4645[

Source/WebCore/Modules/webauthn/PublicKeyCredentialData.h

@@struct PublicKeyCredentialData {
4949 mutable RefPtr<ArrayBuffer> signature;
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&);
5457};

@@void PublicKeyCredentialData::encode(Encoder& encoder) const
8184 encoder << static_cast<uint64_t>(signature->byteLength());
8285 encoder.encodeFixedLengthData(reinterpret_cast<const uint8_t*>(signature->data()), signature->byteLength(), 1);
8386
 87 // Encode AppID before user handle to avoid the userHandle flag.
 88 encoder << appid;
 89
8490 if (!userHandle) {
8591 encoder << false;
8692 return;

@@Optional<PublicKeyCredentialData> PublicKeyCredentialData::decode(Decoder& decod
148154 if (!decoder.decodeFixedLengthData(reinterpret_cast<uint8_t*>(result.signature->data()), signatureLength.value(), 1))
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;
153165 if (!hasUserHandle)

Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.h

2727
2828#if ENABLE(WEB_AUTHN)
2929
 30#include "AuthenticationExtensionsClientInputs.h"
3031#include "BufferSource.h"
3132#include "PublicKeyCredentialDescriptor.h"
3233#include "UserVerificationRequirement.h"

@@struct PublicKeyCredentialRequestOptions {
4041 mutable String rpId;
4142 Vector<PublicKeyCredentialDescriptor> allowCredentials;
4243 UserVerificationRequirement userVerification { UserVerificationRequirement::Preferred };
 44 mutable Optional<AuthenticationExtensionsClientInputs> extensions;
4345
4446 template<class Encoder> void encode(Encoder&) const;
4547 template<class Decoder> static Optional<PublicKeyCredentialRequestOptions> decode(Decoder&);

@@struct PublicKeyCredentialRequestOptions {
4951template<class Encoder>
5052void PublicKeyCredentialRequestOptions::encode(Encoder& encoder) const
5153{
52  encoder << timeout << rpId << allowCredentials << userVerification;
 54 encoder << timeout << rpId << allowCredentials << userVerification << extensions;
5355}
5456
5557template<class Decoder>

@@Optional<PublicKeyCredentialRequestOptions> PublicKeyCredentialRequestOptions::d
7476 return WTF::nullopt;
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}
7987

Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl

3131 USVString rpId;
3232 sequence<PublicKeyCredentialDescriptor> allowCredentials = [];
3333 UserVerificationRequirement userVerification = "preferred";
34  // Not support yet.
35  // AuthenticationExtensions extensions;
 34 AuthenticationExtensionsClientInputs extensions;
3635};

Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp

@@Optional<PublicKeyCredentialData> readCTAPMakeCredentialResponse(const Vector<ui
121121 attestationObjectMap[CBOR("attStmt")] = WTFMove(attStmt);
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
127127Optional<PublicKeyCredentialData> readCTAPGetAssertionResponse(const Vector<uint8_t>& inBuffer)

@@Optional<PublicKeyCredentialData> readCTAPGetAssertionResponse(const Vector<uint
170170 userHandle = ArrayBuffer::create(id.data(), id.size());
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
176176Optional<AuthenticatorGetInfoResponse> readCTAPGetInfoResponse(const Vector<uint8_t>& inBuffer)

Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.cpp

@@Optional<Vector<uint8_t>> convertToU2fCheckOnlySignCommand(const Vector<uint8_t>
114114 return constructU2fSignCommand(produceRpIdHash(request.rp.id), clientDataHash, keyHandle.idVector, true /* checkOnly */);
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
125128Vector<uint8_t> constructBogusU2fRegistrationCommand()

Source/WebCore/Modules/webauthn/fido/U2fCommandConstructor.h

@@WEBCORE_EXPORT Optional<Vector<uint8_t>> convertToU2fRegisterCommand(const Vecto
6262WEBCORE_EXPORT Optional<Vector<uint8_t>> convertToU2fCheckOnlySignCommand(const Vector<uint8_t>& clientDataHash, const WebCore::PublicKeyCredentialCreationOptions&, const WebCore::PublicKeyCredentialDescriptor&);
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();
6868

Source/WebCore/Modules/webauthn/fido/U2fResponseConverter.cpp

@@Optional<PublicKeyCredentialData> readU2fRegisterResponse(const String& rpId, co
170170
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
176176Optional<PublicKeyCredentialData> readU2fSignResponse(const String& rpId, const Vector<uint8_t>& keyHandle, const Vector<uint8_t>& u2fData)

@@Optional<PublicKeyCredentialData> readU2fSignResponse(const String& rpId, const
186186 counter += u2fData[counterIndex + 3];
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
192192} // namespace fido

Source/WebCore/Sources.txt

@@JSAudioListener.cpp
25692569JSAudioNode.cpp
25702570JSAudioParam.cpp
25712571JSAudioProcessingEvent.cpp
 2572JSAuthenticationExtensionsClientInputs.cpp
25722573JSAuthenticatorAssertionResponse.cpp
25732574JSAuthenticatorAttestationResponse.cpp
25742575JSAuthenticatorResponse.cpp

Source/WebCore/WebCore.xcodeproj/project.pbxproj

18661866 57D0018D1DD5413200ED19D9 /* JSCryptoKeyUsage.h in Headers */ = {isa = PBXBuildFile; fileRef = 57D0018C1DD5413200ED19D9 /* JSCryptoKeyUsage.h */; };
18671867 57D8462E1FEAF69900CA3682 /* PublicKeyCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 57D8462B1FEAF68F00CA3682 /* PublicKeyCredential.h */; settings = {ATTRIBUTES = (Private, ); }; };
18681868 57D846351FEAFCD300CA3682 /* JSPublicKeyCredential.h in Headers */ = {isa = PBXBuildFile; fileRef = 57D846301FEAFC2F00CA3682 /* JSPublicKeyCredential.h */; };
 1869 57DA47B0224034E4002A4612 /* AuthenticationExtensionsClientInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DA47A522401E0F002A4612 /* AuthenticationExtensionsClientInputs.h */; settings = {ATTRIBUTES = (Private, ); }; };
18691870 57DCED74214305F00016B847 /* PublicKeyCredentialData.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED72214305F00016B847 /* PublicKeyCredentialData.h */; settings = {ATTRIBUTES = (Private, ); }; };
18701871 57DCED9021487FF70016B847 /* AuthenticatorTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED8C21487EDB0016B847 /* AuthenticatorTransport.h */; settings = {ATTRIBUTES = (Private, ); }; };
18711872 57DCED98214882160016B847 /* JSAuthenticatorTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCED92214880C60016B847 /* JSAuthenticatorTransport.h */; };

88718872 57D8462D1FEAF68F00CA3682 /* PublicKeyCredential.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = PublicKeyCredential.idl; sourceTree = "<group>"; };
88728873 57D846301FEAFC2F00CA3682 /* JSPublicKeyCredential.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPublicKeyCredential.h; sourceTree = "<group>"; };
88738874 57D846311FEAFC2F00CA3682 /* JSPublicKeyCredential.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPublicKeyCredential.cpp; sourceTree = "<group>"; };
 8875 57DA47A522401E0F002A4612 /* AuthenticationExtensionsClientInputs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthenticationExtensionsClientInputs.h; sourceTree = "<group>"; };
 8876 57DA47A722401E0F002A4612 /* AuthenticationExtensionsClientInputs.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = AuthenticationExtensionsClientInputs.idl; sourceTree = "<group>"; };
 8877 57DA47AC224032DC002A4612 /* JSAuthenticationExtensionsClientInputs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSAuthenticationExtensionsClientInputs.cpp; sourceTree = "<group>"; };
 8878 57DA47AD224032DD002A4612 /* JSAuthenticationExtensionsClientInputs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSAuthenticationExtensionsClientInputs.h; sourceTree = "<group>"; };
88748879 57DCED72214305F00016B847 /* PublicKeyCredentialData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PublicKeyCredentialData.h; sourceTree = "<group>"; };
88758880 57DCED8C21487EDB0016B847 /* AuthenticatorTransport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthenticatorTransport.h; sourceTree = "<group>"; };
88768881 57DCED8E21487EDB0016B847 /* AuthenticatorTransport.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = AuthenticatorTransport.idl; sourceTree = "<group>"; };

1988119886 57152B5321CB2CE3000C37CA /* apdu */,
1988219887 57303BB32006C6ED00355965 /* cbor */,
1988319888 578A4BFA2166AE0000D08F34 /* fido */,
 19889 57DA47A522401E0F002A4612 /* AuthenticationExtensionsClientInputs.h */,
 19890 57DA47A722401E0F002A4612 /* AuthenticationExtensionsClientInputs.idl */,
1988419891 57303C272009B2FC00355965 /* AuthenticatorAssertionResponse.h */,
1988519892 57303C292009B2FC00355965 /* AuthenticatorAssertionResponse.idl */,
1988619893 57303C1B2009A98600355965 /* AuthenticatorAttestationResponse.h */,

1991719924 57D8462F1FEAFB0500CA3682 /* WebAuthN */ = {
1991819925 isa = PBXGroup;
1991919926 children = (
 19927 57DA47AC224032DC002A4612 /* JSAuthenticationExtensionsClientInputs.cpp */,
 19928 57DA47AD224032DD002A4612 /* JSAuthenticationExtensionsClientInputs.h */,
1992019929 57303C2E2009B7DA00355965 /* JSAuthenticatorAssertionResponse.cpp */,
1992119930 57303C2D2009B7D900355965 /* JSAuthenticatorAssertionResponse.h */,
1992219931 57303C202009AEF500355965 /* JSAuthenticatorAttestationResponse.cpp */,

2861428623 7EE6846112D26E3800E79415 /* AuthenticationChallenge.h in Headers */,
2861528624 934F713A0D5A6F1000018D69 /* AuthenticationChallengeBase.h in Headers */,
2861628625 E124748410AA161D00B79493 /* AuthenticationClient.h in Headers */,
 28626 57DA47B0224034E4002A4612 /* AuthenticationExtensionsClientInputs.h in Headers */,
2861728627 514C764C0CE9234E007EF3CD /* AuthenticationMac.h in Headers */,
2861828628 57303C2C2009B4A800355965 /* AuthenticatorAssertionResponse.h in Headers */,
2861928629 57303C1F2009AB4200355965 /* AuthenticatorAttestationResponse.h in Headers */,

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm

@@void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKe
326326 }
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}
332332

@@void LocalAuthenticator::continueGetAssertionAfterUserConsented(LocalConnection:
463463 }
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}
469469

Source/WebKit/UIProcess/WebAuthentication/fido/U2fHidAuthenticator.cpp

@@void U2fHidAuthenticator::issueSignCommand(size_t index)
101101 receiveRespond(ExceptionData { NotAllowedError, "No credentials from the allowCredentials list is found in the authenticator."_s });
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);
107107}

@@void U2fHidAuthenticator::continueSignCommandAfterResponseReceived(ApduResponse&
200200{
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 = true;
 215
208216 receiveRespond(WTFMove(*response));
209217 return;
210218 }

@@void U2fHidAuthenticator::continueSignCommandAfterResponseReceived(ApduResponse&
212220 // Polling is required during test of user presence.
213221 m_retryTimer.startOneShot(Seconds::fromMilliseconds(retryTimeOutValueMs));
214222 return;
 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 }
 232 issueSignCommand(m_nextListIndex++);
 233 return;
215234 default:
216235 issueSignCommand(m_nextListIndex++);
217236 }

Source/WebKit/UIProcess/WebAuthentication/fido/U2fHidAuthenticator.h

@@private:
7474 Vector<uint8_t> m_lastCommand;
7575 CommandType m_lastCommandType;
7676 size_t m_nextListIndex { 0 };
 77 bool m_isAppId { false };
7778};
7879
7980} // namespace WebKit

Tools/ChangeLog

 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 NOBODY (OOPS!).
 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 Aakash Jain <aakash_jain@apple.com>
218
319 [ews-build] Improve summary for PrintConfiguration step

Tools/TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp

@@TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam)
5959 Vector<PublicKeyCredentialCreationOptions::Parameters> params { { PublicKeyCredentialType::PublicKey, 7 }, { PublicKeyCredentialType::PublicKey, 257 } };
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));
6565 auto serializedData = encodeMakeCredenitalRequestAsCBOR(hash, options, AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedButNotConfigured);

Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h

@@constexpr uint8_t kU2fSignCommandApdu[] = {
113113 0x00, 0x00,
114114};
115115
 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,
 129 // Key handle length
 130 0x40,
 131 // Key handle
 132 0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C, 0x26,
 133 0x35, 0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17, 0x36, 0xC3,
 134 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54, 0xD7, 0xFF, 0x94,
 135 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B, 0xDD, 0x39, 0x6B, 0x64,
 136 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00, 0xCC, 0xD4, 0x15, 0xCD, 0x08,
 137 0xFE, 0x42, 0x00, 0x38,
 138 // Max response length
 139 0x00, 0x00,
 140};
 141
116142constexpr uint8_t kU2fCheckOnlySignCommandApdu[] = {
117143 // CLA, INS, P1, P2 APDU instruction parameters
118144 0x00, 0x02, 0x07, 0x00,

Tools/TestWebKitAPI/Tests/WebCore/U2fCommandConstructorTest.cpp

@@TEST(U2fCommandConstructorTest, TestConvertCtapGetAssertionToU2fSignRequest)
176176 EXPECT_EQ(*u2fSignCommand, convertBytesToVector(TestData::kU2fSignCommandApdu, sizeof(TestData::kU2fSignCommandApdu)));
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{
181202 auto getAssertionReq = constructGetAssertionRequest();

LayoutTests/ChangeLog

 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 NOBODY (OOPS!).
 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
323 Reparenting during a mutation event inside appendChild could result in a circular DOM tree

LayoutTests/http/wpt/webauthn/public-key-credential-create-success-hid.https.html

1616 assert_equals(credential.type, 'public-key');
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());
2020
2121 // Check attestation
2222 const attestationObject = CBOR.decode(credential.response.attestationObject);

LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html

3030 assert_equals(credential.type, 'public-key');
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());
3434
3535 // Check attestation
3636 const attestationObject = CBOR.decode(credential.response.attestationObject);

LayoutTests/http/wpt/webauthn/public-key-credential-create-success-u2f.https.html

1212 assert_equals(credential.type, 'public-key');
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());
1616
1717 // Check attestation
1818 const attestationObject = CBOR.decode(credential.response.attestationObject);

LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https-expected.txt

22PASS PublicKeyCredential's [[get]] with malformed sign response in a mock hid authenticator.
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

LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-u2f.https.html

4242 testRunner.setWebAuthenticationMockConfiguration({ hid: { stage: "request", subStage: "msg", error: "malicious-payload", isU2f: true, payloadBase64: [testU2fApduWrongDataOnlyResponseBase64, testU2fApduWrongDataOnlyResponseBase64] } });
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>

LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt

11
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

LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https.html

3131 return promiseRejects(t, "SecurityError",
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>

LayoutTests/http/wpt/webauthn/public-key-credential-get-success-hid.https.html

1616 assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testHidCredentialIdBase64));
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());
1920
2021 // Check authData
2122 const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));

LayoutTests/http/wpt/webauthn/public-key-credential-get-success-local.https.html

1919 assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testCredentialIdBase64));
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());
2223
2324 // Check authData
2425 const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));

3233 // credential.response.signature is in ASN.1 and WebCrypto expect signatures provides in r|s.
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());
3637 });
3738 });
3839 });

LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https-expected.txt

22PASS PublicKeyCredential's [[get]] with minimum options in a mock hid authenticator.
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

LayoutTests/http/wpt/webauthn/public-key-credential-get-success-u2f.https.html

44<script src="/resources/testharnessreport.js"></script>
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
1012 assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testU2fCredentialIdBase64));

1214 assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testU2fCredentialIdBase64));
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());
 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);
2130 }

6776 return checkResult(credential);
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>