[WebAuthN] Import CTAP device request/response converters from Chromium
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 10:29:58 +0000 (10:29 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 10:29:58 +0000 (10:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190784
<rdar://problem/45460333>

Reviewed by Brent Fulgham.

Source/WebCore:

This patch imports Chromium's CTAP device request/response converters:
https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#message-encoding
1. It directly imports the following files and suit them to WebKit's coding style:
https://cs.chromium.org/chromium/src/device/fido/device_response_converter.cc?l=20&rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/device_response_converter.h?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/authenticator_get_info_response.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/authenticator_get_info_response.h?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/authenticator_supported_options.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/authenticator_supported_options.h?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/ctap_request_unittest.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/ctap_response_unittest.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
https://cs.chromium.org/chromium/src/device/fido/fido_test_data.h?l=1&rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
2. It gathers the following methods into DeviceRequestConverter:
CtapGetAssertionRequest::EncodeAsCBOR()
CtapMakeCredentialRequest::EncodeAsCBOR()
PublicKeyCredentialDescriptor::ConvertToCBOR()
PublicKeyCredentialParams::ConvertToCBOR()
PublicKeyCredentialRpEntity::ConvertToCBOR()
PublicKeyCredentialUserEntity::ConvertToCBOR()
3. It also apply a patch from Chromium to CBORValue:
https://chromium.googlesource.com/chromium/src/+/7b6fbff35cd8e4d508f08e1cd93b4aa0a0dc402c%5E%21/

Besides importing things from Chromium, it also implements UserVerificationRequirement for both
PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions such that both options
can specify more dimensions of a desired authenticator.

Covered by API tests.

* CMakeLists.txt:
* DerivedSources.make:
* Modules/webauthn/PublicKeyCredentialCreationOptions.h:
(WebCore::PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria::encode const):
(WebCore::PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria::decode):
* Modules/webauthn/PublicKeyCredentialCreationOptions.idl:
* Modules/webauthn/PublicKeyCredentialRequestOptions.h:
(WebCore::PublicKeyCredentialRequestOptions::encode const):
(WebCore::PublicKeyCredentialRequestOptions::decode):
* Modules/webauthn/PublicKeyCredentialRequestOptions.idl:
* Modules/webauthn/UserVerificationRequirement.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl.
* Modules/webauthn/UserVerificationRequirement.idl: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl.
* Modules/webauthn/cbor/CBORValue.cpp:
(cbor::CBORValue::CBORValue):
(cbor::CBORValue::getBool const):
* Modules/webauthn/cbor/CBORValue.h:
* Modules/webauthn/fido/AuthenticatorGetInfoResponse.cpp: Added.
(fido::toArrayValue):
(fido::AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse):
(fido::AuthenticatorGetInfoResponse::setMaxMsgSize):
(fido::AuthenticatorGetInfoResponse::setPinProtocols):
(fido::AuthenticatorGetInfoResponse::setExtensions):
(fido::AuthenticatorGetInfoResponse::setOptions):
(fido::encodeAsCBOR):
* Modules/webauthn/fido/AuthenticatorGetInfoResponse.h: Added.
* Modules/webauthn/fido/AuthenticatorSupportedOptions.cpp: Added.
(fido::AuthenticatorSupportedOptions::setSupportsResidentKey):
(fido::AuthenticatorSupportedOptions::setUserVerificationAvailability):
(fido::AuthenticatorSupportedOptions::setUserPresenceRequired):
(fido::AuthenticatorSupportedOptions::setClientPinAvailability):
(fido::AuthenticatorSupportedOptions::setIsPlatformDevice):
(fido::convertToCBOR):
* Modules/webauthn/fido/AuthenticatorSupportedOptions.h: Added.
* Modules/webauthn/fido/DeviceRequestConverter.cpp: Added.
(fido::convertRpEntityToCBOR):
(fido::convertUserEntityToCBOR):
(fido::convertParametersToCBOR):
(fido::convertDescriptorToCBOR):
(fido::encodeMakeCredenitalRequestAsCBOR):
(fido::encodeGetAssertionRequestAsCBOR):
(fido::encodeEmptyAuthenticatorRequest):
* Modules/webauthn/fido/DeviceRequestConverter.h: Copied from Source/WebCore/Modules/webauthn/fido/FidoConstants.h.
* Modules/webauthn/fido/DeviceResponseConverter.cpp: Added.
(fido::convertStringToProtocolVersion):
(fido::getResponseCode):
(fido::getCredentialId):
(fido::readCTAPMakeCredentialResponse):
(fido::readCTAPGetAssertionResponse):
(fido::readCTAPGetInfoResponse):
* Modules/webauthn/fido/DeviceResponseConverter.h: Copied from Source/WebCore/Modules/webauthn/fido/FidoConstants.cpp.
* Modules/webauthn/fido/FidoConstants.cpp:
(fido::isCtapDeviceResponseCode):
(fido::publicKeyCredentialTypeToString):
* Modules/webauthn/fido/FidoConstants.h:
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebCore/CBORValueTest.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp: Added.
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp: Added.
(TestWebKitAPI::getTestCorruptedSignResponse):
(TestWebKitAPI::getTestCredentialRawIdBytes):
(TestWebKitAPI::convertToVector):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebCore/FidoTestData.h: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@237983 268f45cc-cd09-0410-ab3c-d52691b4dbfc

29 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.make
Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.h
Source/WebCore/Modules/webauthn/PublicKeyCredentialCreationOptions.idl
Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.h
Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl
Source/WebCore/Modules/webauthn/UserVerificationRequirement.h [new file with mode: 0644]
Source/WebCore/Modules/webauthn/UserVerificationRequirement.idl [new file with mode: 0644]
Source/WebCore/Modules/webauthn/cbor/CBORValue.cpp
Source/WebCore/Modules/webauthn/cbor/CBORValue.h
Source/WebCore/Modules/webauthn/fido/AuthenticatorGetInfoResponse.cpp [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/AuthenticatorGetInfoResponse.h [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/AuthenticatorSupportedOptions.cpp [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/AuthenticatorSupportedOptions.h [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.cpp [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.h [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.h [new file with mode: 0644]
Source/WebCore/Modules/webauthn/fido/FidoConstants.cpp
Source/WebCore/Modules/webauthn/fido/FidoConstants.h
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebCore/CBORValueTest.cpp
Tools/TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h [new file with mode: 0644]

index 246f9ba..83caef8 100644 (file)
@@ -438,6 +438,7 @@ set(WebCore_NON_SVG_IDL_FILES
     Modules/webauthn/PublicKeyCredentialDescriptor.idl
     Modules/webauthn/PublicKeyCredentialRequestOptions.idl
     Modules/webauthn/PublicKeyCredentialType.idl
+    Modules/webauthn/UserVerificationRequirement.idl
 
     Modules/webdatabase/DOMWindowWebDatabase.idl
     Modules/webdatabase/Database.idl
index f4320ee..2d48e94 100644 (file)
@@ -1,3 +1,96 @@
+2018-11-08  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Import CTAP device request/response converters from Chromium
+        https://bugs.webkit.org/show_bug.cgi?id=190784
+        <rdar://problem/45460333>
+
+        Reviewed by Brent Fulgham.
+
+        This patch imports Chromium's CTAP device request/response converters:
+        https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#message-encoding
+        1. It directly imports the following files and suit them to WebKit's coding style:
+        https://cs.chromium.org/chromium/src/device/fido/device_response_converter.cc?l=20&rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/device_response_converter.h?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/authenticator_get_info_response.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/authenticator_get_info_response.h?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/authenticator_supported_options.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/authenticator_supported_options.h?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/ctap_request_unittest.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/ctap_response_unittest.cc?rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        https://cs.chromium.org/chromium/src/device/fido/fido_test_data.h?l=1&rcl=098dfd90850ffa84c27a884ab75edd2d99c4ec45
+        2. It gathers the following methods into DeviceRequestConverter:
+        CtapGetAssertionRequest::EncodeAsCBOR()
+        CtapMakeCredentialRequest::EncodeAsCBOR()
+        PublicKeyCredentialDescriptor::ConvertToCBOR()
+        PublicKeyCredentialParams::ConvertToCBOR()
+        PublicKeyCredentialRpEntity::ConvertToCBOR()
+        PublicKeyCredentialUserEntity::ConvertToCBOR()
+        3. It also apply a patch from Chromium to CBORValue:
+        https://chromium.googlesource.com/chromium/src/+/7b6fbff35cd8e4d508f08e1cd93b4aa0a0dc402c%5E%21/
+
+        Besides importing things from Chromium, it also implements UserVerificationRequirement for both
+        PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions such that both options
+        can specify more dimensions of a desired authenticator.
+
+        Covered by API tests.
+
+        * CMakeLists.txt:
+        * DerivedSources.make:
+        * Modules/webauthn/PublicKeyCredentialCreationOptions.h:
+        (WebCore::PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria::encode const):
+        (WebCore::PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria::decode):
+        * Modules/webauthn/PublicKeyCredentialCreationOptions.idl:
+        * Modules/webauthn/PublicKeyCredentialRequestOptions.h:
+        (WebCore::PublicKeyCredentialRequestOptions::encode const):
+        (WebCore::PublicKeyCredentialRequestOptions::decode):
+        * Modules/webauthn/PublicKeyCredentialRequestOptions.idl:
+        * Modules/webauthn/UserVerificationRequirement.h: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl.
+        * Modules/webauthn/UserVerificationRequirement.idl: Copied from Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.idl.
+        * Modules/webauthn/cbor/CBORValue.cpp:
+        (cbor::CBORValue::CBORValue):
+        (cbor::CBORValue::getBool const):
+        * Modules/webauthn/cbor/CBORValue.h:
+        * Modules/webauthn/fido/AuthenticatorGetInfoResponse.cpp: Added.
+        (fido::toArrayValue):
+        (fido::AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse):
+        (fido::AuthenticatorGetInfoResponse::setMaxMsgSize):
+        (fido::AuthenticatorGetInfoResponse::setPinProtocols):
+        (fido::AuthenticatorGetInfoResponse::setExtensions):
+        (fido::AuthenticatorGetInfoResponse::setOptions):
+        (fido::encodeAsCBOR):
+        * Modules/webauthn/fido/AuthenticatorGetInfoResponse.h: Added.
+        * Modules/webauthn/fido/AuthenticatorSupportedOptions.cpp: Added.
+        (fido::AuthenticatorSupportedOptions::setSupportsResidentKey):
+        (fido::AuthenticatorSupportedOptions::setUserVerificationAvailability):
+        (fido::AuthenticatorSupportedOptions::setUserPresenceRequired):
+        (fido::AuthenticatorSupportedOptions::setClientPinAvailability):
+        (fido::AuthenticatorSupportedOptions::setIsPlatformDevice):
+        (fido::convertToCBOR):
+        * Modules/webauthn/fido/AuthenticatorSupportedOptions.h: Added.
+        * Modules/webauthn/fido/DeviceRequestConverter.cpp: Added.
+        (fido::convertRpEntityToCBOR):
+        (fido::convertUserEntityToCBOR):
+        (fido::convertParametersToCBOR):
+        (fido::convertDescriptorToCBOR):
+        (fido::encodeMakeCredenitalRequestAsCBOR):
+        (fido::encodeGetAssertionRequestAsCBOR):
+        (fido::encodeEmptyAuthenticatorRequest):
+        * Modules/webauthn/fido/DeviceRequestConverter.h: Copied from Source/WebCore/Modules/webauthn/fido/FidoConstants.h.
+        * Modules/webauthn/fido/DeviceResponseConverter.cpp: Added.
+        (fido::convertStringToProtocolVersion):
+        (fido::getResponseCode):
+        (fido::getCredentialId):
+        (fido::readCTAPMakeCredentialResponse):
+        (fido::readCTAPGetAssertionResponse):
+        (fido::readCTAPGetInfoResponse):
+        * Modules/webauthn/fido/DeviceResponseConverter.h: Copied from Source/WebCore/Modules/webauthn/fido/FidoConstants.cpp.
+        * Modules/webauthn/fido/FidoConstants.cpp:
+        (fido::isCtapDeviceResponseCode):
+        (fido::publicKeyCredentialTypeToString):
+        * Modules/webauthn/fido/FidoConstants.h:
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+
 2018-11-07  Justin Michaud  <justin_michaud@apple.com>
 
         CSS Painting API should pass size, arguments and input properties to paint callback
index e97808f..f6af04f 100644 (file)
@@ -358,6 +358,7 @@ JS_BINDING_IDLS = \
     $(WebCore)/Modules/webauthn/PublicKeyCredentialDescriptor.idl \
     $(WebCore)/Modules/webauthn/PublicKeyCredentialRequestOptions.idl \
     $(WebCore)/Modules/webauthn/PublicKeyCredentialType.idl \
+    $(WebCore)/Modules/webauthn/UserVerificationRequirement.idl \
     $(WebCore)/Modules/webdatabase/DOMWindowWebDatabase.idl \
     $(WebCore)/Modules/webdatabase/Database.idl \
     $(WebCore)/Modules/webdatabase/DatabaseCallback.idl \
index 8afb4dd..ba68df9 100644 (file)
@@ -30,6 +30,7 @@
 #include "BufferSource.h"
 #include "PublicKeyCredentialDescriptor.h"
 #include "PublicKeyCredentialType.h"
+#include "UserVerificationRequirement.h"
 #include <wtf/CrossThreadCopier.h>
 #include <wtf/Forward.h>
 
@@ -65,7 +66,9 @@ struct PublicKeyCredentialCreationOptions {
     };
 
     struct AuthenticatorSelectionCriteria {
-        AuthenticatorAttachment authenticatorAttachment;
+        std::optional<AuthenticatorAttachment> authenticatorAttachment;
+        bool requireResidentKey { false };
+        UserVerificationRequirement userVerification { UserVerificationRequirement::Preferred };
 
         template<class Encoder> void encode(Encoder&) const;
         template<class Decoder> static std::optional<AuthenticatorSelectionCriteria> decode(Decoder&);
@@ -105,14 +108,27 @@ std::optional<PublicKeyCredentialCreationOptions::Parameters> PublicKeyCredentia
 template<class Encoder>
 void PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria::encode(Encoder& encoder) const
 {
-    encoder << authenticatorAttachment;
+    encoder << authenticatorAttachment << requireResidentKey << userVerification;
 }
 
 template<class Decoder>
 std::optional<PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria> PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria::decode(Decoder& decoder)
 {
     PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria result;
-    if (!decoder.decodeEnum(result.authenticatorAttachment))
+
+    std::optional<std::optional<AuthenticatorAttachment>> authenticatorAttachment;
+    decoder >> authenticatorAttachment;
+    if (!authenticatorAttachment)
+        return std::nullopt;
+    result.authenticatorAttachment = WTFMove(*authenticatorAttachment);
+
+    std::optional<bool> requireResidentKey;
+    decoder >> requireResidentKey;
+    if (!requireResidentKey)
+        return std::nullopt;
+    result.requireResidentKey = *requireResidentKey;
+
+    if (!decoder.decodeEnum(result.userVerification))
         return std::nullopt;
     return result;
 }
index a31525f..36e180b 100644 (file)
@@ -74,10 +74,8 @@ typedef long COSEAlgorithmIdentifier;
     Conditional=WEB_AUTHN,
 ] dictionary AuthenticatorSelectionCriteria {
     AuthenticatorAttachment      authenticatorAttachment;
-    // We don't allow RPs to set the following values at this stage.
-    // Hence, those options are always { true, "required" }.
-    // boolean                      requireResidentKey = false;
-    // UserVerificationRequirement  userVerification = "preferred";
+    boolean                      requireResidentKey = false;
+    UserVerificationRequirement  userVerification = "preferred";
 };
 
 [
index 274476e..e2b0e33 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "BufferSource.h"
 #include "PublicKeyCredentialDescriptor.h"
+#include "UserVerificationRequirement.h"
 #include <wtf/Forward.h>
 
 namespace WebCore {
@@ -38,6 +39,7 @@ struct PublicKeyCredentialRequestOptions {
     std::optional<unsigned> timeout;
     mutable String rpId;
     Vector<PublicKeyCredentialDescriptor> allowCredentials;
+    UserVerificationRequirement userVerification { UserVerificationRequirement::Preferred };
 
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static std::optional<PublicKeyCredentialRequestOptions> decode(Decoder&);
@@ -47,7 +49,7 @@ struct PublicKeyCredentialRequestOptions {
 template<class Encoder>
 void PublicKeyCredentialRequestOptions::encode(Encoder& encoder) const
 {
-    encoder << timeout << rpId << allowCredentials;
+    encoder << timeout << rpId << allowCredentials << userVerification;
 }
 
 template<class Decoder>
@@ -65,6 +67,13 @@ std::optional<PublicKeyCredentialRequestOptions> PublicKeyCredentialRequestOptio
         return std::nullopt;
     if (!decoder.decode(result.allowCredentials))
         return std::nullopt;
+
+    std::optional<UserVerificationRequirement> userVerification;
+    decoder >> userVerification;
+    if (!userVerification)
+        return std::nullopt;
+    result.userVerification = WTFMove(*userVerification);
+
     return result;
 }
 
index 89a206d..d651db2 100644 (file)
@@ -30,8 +30,7 @@
     unsigned long timeout;
     USVString rpId;
     sequence<PublicKeyCredentialDescriptor> allowCredentials = [];
-    // Always "required" at this stage
-    // UserVerificationRequirement userVerification = "preferred";
+    UserVerificationRequirement userVerification = "preferred";
     // Not support yet.
     // AuthenticationExtensions extensions;
 };
diff --git a/Source/WebCore/Modules/webauthn/UserVerificationRequirement.h b/Source/WebCore/Modules/webauthn/UserVerificationRequirement.h
new file mode 100644 (file)
index 0000000..8314d02
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#include <wtf/EnumTraits.h>
+
+namespace WebCore {
+
+enum class UserVerificationRequirement {
+    Required,
+    Preferred,
+    Discouraged
+};
+
+} // namespace WebCore
+
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::UserVerificationRequirement> {
+    using values = EnumValues<
+        WebCore::UserVerificationRequirement,
+        WebCore::UserVerificationRequirement::Required,
+        WebCore::UserVerificationRequirement::Preferred,
+        WebCore::UserVerificationRequirement::Discouraged
+    >;
+};
+
+} // namespace WTF
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/UserVerificationRequirement.idl b/Source/WebCore/Modules/webauthn/UserVerificationRequirement.idl
new file mode 100644 (file)
index 0000000..9bf4fb9
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+    Conditional=WEB_AUTHN,
+    ImplementedAs=UserVerificationRequirement
+] enum UserVerificationRequirement {
+    "required",
+    "preferred",
+    "discouraged"
+};
index 25ed98b..4c8ffad 100644 (file)
@@ -153,6 +153,12 @@ CBORValue::CBORValue(SimpleValue inSimple)
     ASSERT(static_cast<int>(inSimple) >= 20 && static_cast<int>(inSimple) <= 23);
 }
 
+CBORValue::CBORValue(bool inBool)
+    : m_type(Type::SimpleValue)
+{
+    m_simpleValue = inBool ? CBORValue::SimpleValue::TrueValue : CBORValue::SimpleValue::FalseValue;
+}
+
 CBORValue& CBORValue::operator=(CBORValue&& that)
 {
     internalCleanup();
@@ -240,6 +246,12 @@ CBORValue::SimpleValue CBORValue::getSimpleValue() const
     return m_simpleValue;
 }
 
+bool CBORValue::getBool() const
+{
+    ASSERT(isBool());
+    return m_simpleValue == SimpleValue::TrueValue;
+}
+
 void CBORValue::internalMoveConstructFrom(CBORValue&& that)
 {
     m_type = that.m_type;
index e2db0e1..6a4c83a 100644 (file)
@@ -138,6 +138,7 @@ public:
     explicit CBORValue(MapValue&&);
 
     explicit CBORValue(SimpleValue);
+    explicit CBORValue(bool);
 
     CBORValue& operator=(CBORValue&&);
 
@@ -161,9 +162,11 @@ public:
     bool isArray() const { return type() == Type::Array; }
     bool isMap() const { return type() == Type::Map; }
     bool isSimple() const { return type() == Type::SimpleValue; }
+    bool isBool() const { return isSimple() && (m_simpleValue == SimpleValue::TrueValue || m_simpleValue == SimpleValue::FalseValue); }
 
     // These will all fatally assert if the type doesn't match.
     SimpleValue getSimpleValue() const;
+    bool getBool() const;
     const int64_t& getInteger() const;
     const int64_t& getUnsigned() const;
     const int64_t& getNegative() const;
diff --git a/Source/WebCore/Modules/webauthn/fido/AuthenticatorGetInfoResponse.cpp b/Source/WebCore/Modules/webauthn/fido/AuthenticatorGetInfoResponse.cpp
new file mode 100644 (file)
index 0000000..309ad76
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "AuthenticatorGetInfoResponse.h"
+
+#if ENABLE(WEB_AUTHN)
+
+#include "CBORValue.h"
+#include "CBORWriter.h"
+
+namespace fido {
+
+template <typename Container>
+cbor::CBORValue toArrayValue(const Container& container)
+{
+    cbor::CBORValue::ArrayValue value;
+    value.reserveInitialCapacity(container.size());
+    for (const auto& item : container)
+        value.append(cbor::CBORValue(item));
+    return cbor::CBORValue(WTFMove(value));
+}
+
+AuthenticatorGetInfoResponse::AuthenticatorGetInfoResponse(StdSet<ProtocolVersion>&& versions, Vector<uint8_t>&& aaguid)
+    : m_versions(WTFMove(versions))
+    , m_aaguid(WTFMove(aaguid))
+{
+}
+
+AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::setMaxMsgSize(uint32_t maxMsgSize)
+{
+    m_maxMsgSize = maxMsgSize;
+    return *this;
+}
+
+AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::setPinProtocols(Vector<uint8_t>&& pinProtocols)
+{
+    m_pinProtocols = WTFMove(pinProtocols);
+    return *this;
+}
+
+AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::setExtensions(Vector<String>&& extensions)
+{
+    m_extensions = WTFMove(extensions);
+    return *this;
+}
+
+AuthenticatorGetInfoResponse& AuthenticatorGetInfoResponse::setOptions(AuthenticatorSupportedOptions&& options)
+{
+    m_options = WTFMove(options);
+    return *this;
+}
+
+Vector<uint8_t> encodeAsCBOR(const AuthenticatorGetInfoResponse& response)
+{
+    using namespace cbor;
+
+    CBORValue::MapValue deviceInfoMap;
+
+    CBORValue::ArrayValue versionArray;
+    for (const auto& version : response.versions())
+        versionArray.append(version == ProtocolVersion::kCtap ? kCtap2Version : kU2fVersion);
+    deviceInfoMap.emplace(CBORValue(1), CBORValue(WTFMove(versionArray)));
+
+    if (response.extensions())
+        deviceInfoMap.emplace(CBORValue(2), toArrayValue(*response.extensions()));
+
+    deviceInfoMap.emplace(CBORValue(3), CBORValue(response.aaguid()));
+    deviceInfoMap.emplace(CBORValue(4), convertToCBOR(response.options()));
+
+    if (response.maxMsgSize())
+        deviceInfoMap.emplace(CBORValue(5), CBORValue(static_cast<int64_t>(*response.maxMsgSize())));
+
+    if (response.pinProtocol())
+        deviceInfoMap.emplace(CBORValue(6), toArrayValue(*response.pinProtocol()));
+
+    auto encodedBytes = CBORWriter::write(CBORValue(WTFMove(deviceInfoMap)));
+    ASSERT(encodedBytes);
+    return *encodedBytes;
+}
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/fido/AuthenticatorGetInfoResponse.h b/Source/WebCore/Modules/webauthn/fido/AuthenticatorGetInfoResponse.h
new file mode 100644 (file)
index 0000000..c063c4f
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#include "AuthenticatorSupportedOptions.h"
+#include "FidoConstants.h"
+#include <wtf/StdSet.h>
+
+namespace fido {
+
+// Authenticator response for AuthenticatorGetInfo request that encapsulates
+// versions, options, AAGUID(Authenticator Attestation GUID), other
+// authenticator device information.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#authenticatorgetinfo-0x04
+class WEBCORE_EXPORT AuthenticatorGetInfoResponse {
+    WTF_MAKE_NONCOPYABLE(AuthenticatorGetInfoResponse);
+public:
+    AuthenticatorGetInfoResponse(StdSet<ProtocolVersion>&& versions, Vector<uint8_t>&& aaguid);
+    AuthenticatorGetInfoResponse(AuthenticatorGetInfoResponse&& that) = default;
+    AuthenticatorGetInfoResponse& operator=(AuthenticatorGetInfoResponse&& other) = default;
+
+    AuthenticatorGetInfoResponse& setMaxMsgSize(uint32_t);
+    AuthenticatorGetInfoResponse& setPinProtocols(Vector<uint8_t>&&);
+    AuthenticatorGetInfoResponse& setExtensions(Vector<String>&&);
+    AuthenticatorGetInfoResponse& setOptions(AuthenticatorSupportedOptions&&);
+
+    const StdSet<ProtocolVersion>& versions() const { return m_versions; }
+    const Vector<uint8_t>& aaguid() const { return m_aaguid; }
+    const std::optional<uint32_t>& maxMsgSize() const { return m_maxMsgSize; }
+    const std::optional<Vector<uint8_t>>& pinProtocol() const { return m_pinProtocols; }
+    const std::optional<Vector<String>>& extensions() const { return m_extensions; }
+    const AuthenticatorSupportedOptions& options() const { return m_options; }
+
+private:
+    StdSet<ProtocolVersion> m_versions;
+    Vector<uint8_t> m_aaguid;
+    std::optional<uint32_t> m_maxMsgSize;
+    std::optional<Vector<uint8_t>> m_pinProtocols;
+    std::optional<Vector<String>> m_extensions;
+    AuthenticatorSupportedOptions m_options;
+};
+
+WEBCORE_EXPORT Vector<uint8_t> encodeAsCBOR(const AuthenticatorGetInfoResponse&);
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/fido/AuthenticatorSupportedOptions.cpp b/Source/WebCore/Modules/webauthn/fido/AuthenticatorSupportedOptions.cpp
new file mode 100644 (file)
index 0000000..bdcf8a4
--- /dev/null
@@ -0,0 +1,109 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "AuthenticatorSupportedOptions.h"
+
+#if ENABLE(WEB_AUTHN)
+
+#include "FidoConstants.h"
+
+namespace fido {
+
+AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::setSupportsResidentKey(bool supportsResidentKey)
+{
+    m_supportsResidentKey = supportsResidentKey;
+    return *this;
+}
+
+AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::setUserVerificationAvailability(UserVerificationAvailability userVerificationAvailability)
+{
+    m_userVerificationAvailability = userVerificationAvailability;
+    return *this;
+}
+
+AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::setUserPresenceRequired(bool userPresenceRequired)
+{
+    m_userPresenceRequired = userPresenceRequired;
+    return *this;
+}
+
+AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::setClientPinAvailability(ClientPinAvailability clientPinAvailability)
+{
+    m_clientPinAvailability = clientPinAvailability;
+    return *this;
+}
+
+AuthenticatorSupportedOptions& AuthenticatorSupportedOptions::setIsPlatformDevice(bool isPlatformDevice)
+{
+    m_isPlatformDevice = isPlatformDevice;
+    return *this;
+}
+
+cbor::CBORValue convertToCBOR(const AuthenticatorSupportedOptions& options)
+{
+    using namespace cbor;
+
+    CBORValue::MapValue optionMap;
+    optionMap.emplace(CBORValue(kResidentKeyMapKey), CBORValue(options.supportsResidentKey()));
+    optionMap.emplace(CBORValue(kUserPresenceMapKey), CBORValue(options.userPresenceRequired()));
+    optionMap.emplace(CBORValue(kPlatformDeviceMapKey), CBORValue(options.isPlatformDevice()));
+
+    using UvAvailability = AuthenticatorSupportedOptions::UserVerificationAvailability;
+
+    switch (options.userVerificationAvailability()) {
+    case UvAvailability::kSupportedAndConfigured:
+        optionMap.emplace(CBORValue(kUserVerificationMapKey), CBORValue(true));
+        break;
+    case UvAvailability::kSupportedButNotConfigured:
+        optionMap.emplace(CBORValue(kUserVerificationMapKey), CBORValue(false));
+        break;
+    case UvAvailability::kNotSupported:
+        break;
+    }
+
+    using ClientPinAvailability = AuthenticatorSupportedOptions::ClientPinAvailability;
+
+    switch (options.clientPinAvailability()) {
+    case ClientPinAvailability::kSupportedAndPinSet:
+        optionMap.emplace(CBORValue(kClientPinMapKey), CBORValue(true));
+        break;
+    case ClientPinAvailability::kSupportedButPinNotSet:
+        optionMap.emplace(CBORValue(kClientPinMapKey), CBORValue(false));
+        break;
+    case ClientPinAvailability::kNotSupported:
+        break;
+    }
+
+    return CBORValue(WTFMove(optionMap));
+}
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/fido/AuthenticatorSupportedOptions.h b/Source/WebCore/Modules/webauthn/fido/AuthenticatorSupportedOptions.h
new file mode 100644 (file)
index 0000000..ec40ae4
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#include "CBORValue.h"
+#include <wtf/Forward.h>
+
+namespace fido {
+
+// Represents CTAP device properties and capabilities received as a response to
+// AuthenticatorGetInfo command.
+class WEBCORE_EXPORT AuthenticatorSupportedOptions {
+    WTF_MAKE_NONCOPYABLE(AuthenticatorSupportedOptions);
+public:
+    enum class UserVerificationAvailability {
+        // e.g. Authenticator with finger print sensor and user's fingerprint is
+        // registered to the device.
+        kSupportedAndConfigured,
+        // e.g. Authenticator with fingerprint sensor without user's fingerprint
+        // registered.
+        kSupportedButNotConfigured,
+        kNotSupported
+    };
+
+    enum class ClientPinAvailability {
+        kSupportedAndPinSet,
+        kSupportedButPinNotSet,
+        kNotSupported,
+    };
+
+    AuthenticatorSupportedOptions() = default;
+    AuthenticatorSupportedOptions(AuthenticatorSupportedOptions&&) = default;
+    AuthenticatorSupportedOptions& operator=(AuthenticatorSupportedOptions&&) = default;
+
+    AuthenticatorSupportedOptions& setIsPlatformDevice(bool);
+    AuthenticatorSupportedOptions& setSupportsResidentKey(bool);
+    AuthenticatorSupportedOptions& setUserVerificationAvailability(UserVerificationAvailability);
+    AuthenticatorSupportedOptions& setUserPresenceRequired(bool);
+    AuthenticatorSupportedOptions& setClientPinAvailability(ClientPinAvailability);
+
+    bool isPlatformDevice() const { return m_isPlatformDevice; }
+    bool supportsResidentKey() const { return m_supportsResidentKey; }
+    UserVerificationAvailability userVerificationAvailability() const { return m_userVerificationAvailability; }
+    bool userPresenceRequired() const { return m_userPresenceRequired; }
+    ClientPinAvailability clientPinAvailability() const { return m_clientPinAvailability; }
+
+private:
+    // Indicates that the device is attached to the client and therefore can't be
+    // removed and used on another client.
+    bool m_isPlatformDevice { false };
+    // Indicates that the device is capable of storing keys on the device itself
+    // and therefore can satisfy the authenticatorGetAssertion request with
+    // allowList parameter not specified or empty.
+    bool m_supportsResidentKey { false };
+    // Indicates whether the device is capable of verifying the user on its own.
+    UserVerificationAvailability m_userVerificationAvailability { UserVerificationAvailability::kNotSupported };
+    bool m_userPresenceRequired { true };
+    // Represents whether client pin in set and stored in device. Set as null
+    // optional if client pin capability is not supported by the authenticator.
+    ClientPinAvailability m_clientPinAvailability { ClientPinAvailability::kNotSupported };
+};
+
+WEBCORE_EXPORT cbor::CBORValue convertToCBOR(const AuthenticatorSupportedOptions&);
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.cpp b/Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.cpp
new file mode 100644 (file)
index 0000000..2d2d922
--- /dev/null
@@ -0,0 +1,182 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "DeviceRequestConverter.h"
+
+#if ENABLE(WEB_AUTHN)
+
+#include "CBORWriter.h"
+#include "PublicKeyCredentialCreationOptions.h"
+#include "PublicKeyCredentialRequestOptions.h"
+#include <wtf/Vector.h>
+
+namespace fido {
+using namespace WebCore;
+using namespace cbor;
+
+using UVAvailability = AuthenticatorSupportedOptions::UserVerificationAvailability;
+
+static CBORValue convertRpEntityToCBOR(const PublicKeyCredentialCreationOptions::RpEntity& rpEntity)
+{
+    CBORValue::MapValue rpMap;
+    rpMap.emplace(CBORValue(kEntityNameMapKey), CBORValue(rpEntity.name));
+    if (!rpEntity.icon.isEmpty())
+        rpMap.emplace(CBORValue(kIconUrlMapKey), CBORValue(rpEntity.icon));
+    if (!rpEntity.id.isEmpty())
+        rpMap.emplace(CBORValue(kEntityIdMapKey), CBORValue(rpEntity.id));
+
+    return CBORValue(WTFMove(rpMap));
+}
+
+static CBORValue convertUserEntityToCBOR(const PublicKeyCredentialCreationOptions::UserEntity& userEntity)
+{
+    CBORValue::MapValue userMap;
+    userMap.emplace(CBORValue(kEntityNameMapKey), CBORValue(userEntity.name));
+    if (!userEntity.icon.isEmpty())
+        userMap.emplace(CBORValue(kIconUrlMapKey), CBORValue(userEntity.icon));
+    userMap.emplace(CBORValue(kEntityIdMapKey), CBORValue(userEntity.idVector));
+    userMap.emplace(CBORValue(kDisplayNameMapKey), CBORValue(userEntity.displayName));
+    return CBORValue(WTFMove(userMap));
+}
+
+static CBORValue convertParametersToCBOR(const Vector<PublicKeyCredentialCreationOptions::Parameters>& parameters)
+{
+    CBORValue::ArrayValue credentialParamArray;
+    credentialParamArray.reserveInitialCapacity(parameters.size());
+    for (const auto& credential : parameters) {
+        CBORValue::MapValue cborCredentialMap;
+        cborCredentialMap.emplace(CBORValue(kCredentialTypeMapKey), CBORValue(publicKeyCredentialTypeToString(credential.type)));
+        cborCredentialMap.emplace(CBORValue(kCredentialAlgorithmMapKey), CBORValue(credential.alg));
+        credentialParamArray.append(WTFMove(cborCredentialMap));
+    }
+    return CBORValue(WTFMove(credentialParamArray));
+}
+
+static CBORValue convertDescriptorToCBOR(const PublicKeyCredentialDescriptor& descriptor)
+{
+    CBORValue::MapValue cborDescriptorMap;
+    cborDescriptorMap[CBORValue(kCredentialTypeKey)] = CBORValue(publicKeyCredentialTypeToString(descriptor.type));
+    cborDescriptorMap[CBORValue(kCredentialIdKey)] = CBORValue(descriptor.idVector);
+    return CBORValue(WTFMove(cborDescriptorMap));
+}
+
+Vector<uint8_t> encodeMakeCredenitalRequestAsCBOR(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions& options, UVAvailability uvCapability)
+{
+    CBORValue::MapValue cborMap;
+    cborMap[CBORValue(1)] = CBORValue(hash);
+    cborMap[CBORValue(2)] = convertRpEntityToCBOR(options.rp);
+    cborMap[CBORValue(3)] = convertUserEntityToCBOR(options.user);
+    cborMap[CBORValue(4)] = convertParametersToCBOR(options.pubKeyCredParams);
+    if (!options.excludeCredentials.isEmpty()) {
+        CBORValue::ArrayValue excludeListArray;
+        for (const auto& descriptor : options.excludeCredentials)
+            excludeListArray.append(convertDescriptorToCBOR(descriptor));
+        cborMap[CBORValue(5)] = CBORValue(WTFMove(excludeListArray));
+    }
+
+    CBORValue::MapValue optionMap;
+    if (options.authenticatorSelection) {
+        // Resident keys are not supported by default.
+        if (options.authenticatorSelection->requireResidentKey)
+            optionMap[CBORValue(kResidentKeyMapKey)] = CBORValue(true);
+
+        // User verification is not required by default.
+        bool requireUserVerification = false;
+        switch (options.authenticatorSelection->userVerification) {
+        case UserVerificationRequirement::Required:
+            requireUserVerification = true;
+            break;
+        case UserVerificationRequirement::Preferred:
+            requireUserVerification = uvCapability == UVAvailability::kNotSupported ? false : true;
+            break;
+        case UserVerificationRequirement::Discouraged:
+            requireUserVerification = false;
+        }
+        optionMap[CBORValue(kUserVerificationMapKey)] = CBORValue(requireUserVerification);
+    }
+    if (!optionMap.empty())
+        cborMap[CBORValue(7)] = CBORValue(WTFMove(optionMap));
+
+    auto serializedParam = CBORWriter::write(CBORValue(WTFMove(cborMap)));
+    ASSERT(serializedParam);
+
+    Vector<uint8_t> cborRequest({ static_cast<uint8_t>(CtapRequestCommand::kAuthenticatorMakeCredential) });
+    cborRequest.appendVector(*serializedParam);
+    return cborRequest;
+}
+
+Vector<uint8_t> encodeGetAssertionRequestAsCBOR(const Vector<uint8_t>& hash, const PublicKeyCredentialRequestOptions& options, UVAvailability uvCapability)
+{
+    CBORValue::MapValue cborMap;
+    cborMap[CBORValue(1)] = CBORValue(options.rpId);
+    cborMap[CBORValue(2)] = CBORValue(hash);
+
+    if (!options.allowCredentials.isEmpty()) {
+        CBORValue::ArrayValue allowListArray;
+        for (const auto& descriptor : options.allowCredentials)
+            allowListArray.append(convertDescriptorToCBOR(descriptor));
+        cborMap[CBORValue(3)] = CBORValue(WTFMove(allowListArray));
+    }
+
+    CBORValue::MapValue optionMap;
+    // User verification is not required by default.
+    bool requireUserVerification = false;
+    switch (options.userVerification) {
+    case UserVerificationRequirement::Required:
+        requireUserVerification = true;
+        break;
+    case UserVerificationRequirement::Preferred:
+        requireUserVerification = uvCapability == UVAvailability::kNotSupported ? false : true;
+        break;
+    case UserVerificationRequirement::Discouraged:
+        requireUserVerification = false;
+    }
+    optionMap[CBORValue(kUserVerificationMapKey)] = CBORValue(requireUserVerification);
+    optionMap[CBORValue(kUserPresenceMapKey)] = CBORValue(!requireUserVerification);
+
+    if (!optionMap.empty())
+        cborMap[CBORValue(5)] = CBORValue(WTFMove(optionMap));
+
+    auto serializedParam = CBORWriter::write(CBORValue(WTFMove(cborMap)));
+    ASSERT(serializedParam);
+
+    Vector<uint8_t> cborRequest({ static_cast<uint8_t>(CtapRequestCommand::kAuthenticatorGetAssertion) });
+    cborRequest.appendVector(*serializedParam);
+    return cborRequest;
+}
+
+Vector<uint8_t> encodeEmptyAuthenticatorRequest(CtapRequestCommand cmd)
+{
+    return { static_cast<uint8_t>(cmd) };
+}
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.h b/Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.h
new file mode 100644 (file)
index 0000000..4acc3ff
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#include "AuthenticatorSupportedOptions.h"
+#include "FidoConstants.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+struct PublicKeyCredentialCreationOptions;
+struct PublicKeyCredentialRequestOptions;
+}
+
+namespace fido {
+
+// Serializes MakeCredential request parameter into CBOR encoded map with
+// integer keys and CBOR encoded values as defined by the CTAP spec.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#authenticatorMakeCredential
+WEBCORE_EXPORT Vector<uint8_t> encodeMakeCredenitalRequestAsCBOR(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialCreationOptions&, AuthenticatorSupportedOptions::UserVerificationAvailability);
+
+// Serializes GetAssertion request parameter into CBOR encoded map with
+// integer keys and CBOR encoded values as defined by the CTAP spec.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#authenticatorGetAssertion
+WEBCORE_EXPORT Vector<uint8_t> encodeGetAssertionRequestAsCBOR(const Vector<uint8_t>& hash, const WebCore::PublicKeyCredentialRequestOptions&, AuthenticatorSupportedOptions::UserVerificationAvailability);
+
+// Represents CTAP requests with empty parameters, including
+// AuthenticatorGetInfo, AuthenticatorCancel, AuthenticatorReset and
+// AuthenticatorGetNextAssertion commands.
+WEBCORE_EXPORT Vector<uint8_t> encodeEmptyAuthenticatorRequest(CtapRequestCommand);
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp b/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.cpp
new file mode 100644 (file)
index 0000000..3f6a959
--- /dev/null
@@ -0,0 +1,312 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "DeviceResponseConverter.h"
+
+#if ENABLE(WEB_AUTHN)
+
+#include "AuthenticatorSupportedOptions.h"
+#include "CBORReader.h"
+#include "CBORWriter.h"
+#include <wtf/StdSet.h>
+#include <wtf/Vector.h>
+
+namespace fido {
+using namespace WebCore;
+using CBOR = cbor::CBORValue;
+
+constexpr size_t kResponseCodeLength = 1;
+
+static ProtocolVersion convertStringToProtocolVersion(const String& version)
+{
+    if (version == kCtap2Version)
+        return ProtocolVersion::kCtap;
+    if (version == kU2fVersion)
+        return ProtocolVersion::kU2f;
+
+    return ProtocolVersion::kUnknown;
+}
+
+CtapDeviceResponseCode getResponseCode(const Vector<uint8_t>& buffer)
+{
+    if (buffer.isEmpty())
+        return CtapDeviceResponseCode::kCtap2ErrInvalidCBOR;
+
+    auto code = static_cast<CtapDeviceResponseCode>(buffer[0]);
+    return isCtapDeviceResponseCode(code) ? code : CtapDeviceResponseCode::kCtap2ErrInvalidCBOR;
+}
+
+static Vector<uint8_t> getCredentialId(const Vector<uint8_t>& authenticatorData)
+{
+    const size_t credentialIdLengthOffset = kRpIdHashLength + kFlagsLength + kSignCounterLength + kAaguidLength;
+
+    if (authenticatorData.size() < credentialIdLengthOffset + kCredentialIdLengthLength)
+        return { };
+    size_t credentialIdLength = (static_cast<size_t>(authenticatorData[credentialIdLengthOffset]) << 8) | static_cast<size_t>(authenticatorData[credentialIdLengthOffset + 1]);
+
+    if (authenticatorData.size() < credentialIdLengthOffset + kCredentialIdLengthLength + credentialIdLength)
+        return { };
+    Vector<uint8_t> credentialId;
+    credentialId.reserveInitialCapacity(credentialIdLength);
+    auto beginIt = authenticatorData.begin() + credentialIdLengthOffset + kCredentialIdLengthLength;
+    credentialId.appendRange(beginIt, beginIt + credentialIdLength);
+    return credentialId;
+}
+
+
+// Decodes byte array response from authenticator to CBOR value object and
+// checks for correct encoding format.
+std::optional<PublicKeyCredentialData> readCTAPMakeCredentialResponse(const Vector<uint8_t>& inBuffer)
+{
+    if (inBuffer.size() <= kResponseCodeLength)
+        return std::nullopt;
+
+    Vector<uint8_t> buffer;
+    buffer.append(inBuffer.data() + 1, inBuffer.size() - 1);
+    std::optional<CBOR> decodedResponse = cbor::CBORReader::read(buffer);
+    if (!decodedResponse || !decodedResponse->isMap())
+        return std::nullopt;
+    const auto& decodedMap = decodedResponse->getMap();
+
+    auto it = decodedMap.find(CBOR(1));
+    if (it == decodedMap.end() || !it->second.isString())
+        return std::nullopt;
+    auto format = it->second.clone();
+
+    it = decodedMap.find(CBOR(2));
+    if (it == decodedMap.end() || !it->second.isByteString())
+        return std::nullopt;
+    auto authenticatorData = it->second.clone();
+
+    auto credentialId = getCredentialId(authenticatorData.getByteString());
+    if (credentialId.isEmpty())
+        return std::nullopt;
+
+    it = decodedMap.find(CBOR(3));
+    if (it == decodedMap.end() || !it->second.isMap())
+        return std::nullopt;
+    auto attStmt = it->second.clone();
+
+    CBOR::MapValue attestationObjectMap;
+    attestationObjectMap[CBOR("authData")] = WTFMove(authenticatorData);
+    attestationObjectMap[CBOR("fmt")] = WTFMove(format);
+    attestationObjectMap[CBOR("attStmt")] = WTFMove(attStmt);
+    auto attestationObject = cbor::CBORWriter::write(CBOR(WTFMove(attestationObjectMap)));
+
+    return PublicKeyCredentialData { ArrayBuffer::create(credentialId.data(), credentialId.size()), true, nullptr, ArrayBuffer::create(attestationObject.value().data(), attestationObject.value().size()), nullptr, nullptr, nullptr };
+}
+
+std::optional<PublicKeyCredentialData> readCTAPGetAssertionResponse(const Vector<uint8_t>& inBuffer)
+{
+    if (inBuffer.size() <= kResponseCodeLength)
+        return std::nullopt;
+
+    Vector<uint8_t> buffer;
+    buffer.append(inBuffer.data() + 1, inBuffer.size() - 1);
+    std::optional<CBOR> decodedResponse = cbor::CBORReader::read(buffer);
+
+    if (!decodedResponse || !decodedResponse->isMap())
+        return std::nullopt;
+
+    auto& responseMap = decodedResponse->getMap();
+
+    RefPtr<ArrayBuffer> credentialId;
+    auto it = responseMap.find(CBOR(1));
+    if (it != responseMap.end() && it->second.isMap()) {
+        auto& credential = it->second.getMap();
+        auto itr = credential.find(CBOR(kCredentialIdKey));
+        if (itr == credential.end() || !itr->second.isByteString())
+            return std::nullopt;
+        auto& id = itr->second.getByteString();
+        credentialId = ArrayBuffer::create(id.data(), id.size());
+    }
+
+    it = responseMap.find(CBOR(2));
+    if (it == responseMap.end() || !it->second.isByteString())
+        return std::nullopt;
+    auto& authData = it->second.getByteString();
+
+    it = responseMap.find(CBOR(3));
+    if (it == responseMap.end() || !it->second.isByteString())
+        return std::nullopt;
+    auto& signature = it->second.getByteString();
+
+    RefPtr<ArrayBuffer> userHandle;
+    {
+        it = responseMap.find(CBOR(4));
+        if (it == responseMap.end() || !it->second.isMap())
+            return std::nullopt;
+        auto& user = it->second.getMap();
+        auto itr = user.find(CBOR(kEntityIdMapKey));
+        if (itr == user.end() || !itr->second.isByteString())
+            return std::nullopt;
+        auto& id = itr->second.getByteString();
+        userHandle = ArrayBuffer::create(id.data(), id.size());
+    }
+
+    return PublicKeyCredentialData { WTFMove(credentialId), false, nullptr, nullptr, ArrayBuffer::create(authData.data(), authData.size()), ArrayBuffer::create(signature.data(), signature.size()), WTFMove(userHandle) };
+}
+
+std::optional<AuthenticatorGetInfoResponse> readCTAPGetInfoResponse(const Vector<uint8_t>& inBuffer)
+{
+    if (inBuffer.size() <= kResponseCodeLength || getResponseCode(inBuffer) != CtapDeviceResponseCode::kSuccess)
+        return std::nullopt;
+
+    Vector<uint8_t> buffer;
+    buffer.append(inBuffer.data() + 1, inBuffer.size() - 1);
+    std::optional<CBOR> decodedResponse = cbor::CBORReader::read(buffer);
+    if (!decodedResponse || !decodedResponse->isMap())
+        return std::nullopt;
+    const auto& responseMap = decodedResponse->getMap();
+
+    auto it = responseMap.find(CBOR(1));
+    if (it == responseMap.end() || !it->second.isArray() || it->second.getArray().size() > 2)
+        return std::nullopt;
+    StdSet<ProtocolVersion> protocolVersions;
+    for (const auto& version : it->second.getArray()) {
+        if (!version.isString())
+            return std::nullopt;
+
+        auto protocol = convertStringToProtocolVersion(version.getString());
+        if (protocol == ProtocolVersion::kUnknown) {
+            LOG_ERROR("Unexpected protocol version received.");
+            continue;
+        }
+
+        if (!protocolVersions.insert(protocol).second)
+            return std::nullopt;
+    }
+    if (protocolVersions.empty())
+        return std::nullopt;
+
+    it = responseMap.find(CBOR(3));
+    if (it == responseMap.end() || !it->second.isByteString() || it->second.getByteString().size() != kAaguidLength)
+        return std::nullopt;
+
+    AuthenticatorGetInfoResponse response(WTFMove(protocolVersions), Vector<uint8_t>(it->second.getByteString()));
+
+    it = responseMap.find(CBOR(2));
+    if (it != responseMap.end()) {
+        if (!it->second.isArray())
+            return std::nullopt;
+
+        Vector<String> extensions;
+        for (const auto& extension : it->second.getArray()) {
+            if (!extension.isString())
+                return std::nullopt;
+
+            extensions.append(extension.getString());
+        }
+        response.setExtensions(WTFMove(extensions));
+    }
+
+    AuthenticatorSupportedOptions options;
+    it = responseMap.find(CBOR(4));
+    if (it != responseMap.end()) {
+        if (!it->second.isMap())
+            return std::nullopt;
+        const auto& optionMap = it->second.getMap();
+        auto optionMapIt = optionMap.find(CBOR(kPlatformDeviceMapKey));
+        if (optionMapIt != optionMap.end()) {
+            if (!optionMapIt->second.isBool())
+                return std::nullopt;
+
+            options.setIsPlatformDevice(optionMapIt->second.getBool());
+        }
+
+        optionMapIt = optionMap.find(CBOR(kResidentKeyMapKey));
+        if (optionMapIt != optionMap.end()) {
+            if (!optionMapIt->second.isBool())
+                return std::nullopt;
+
+            options.setSupportsResidentKey(optionMapIt->second.getBool());
+        }
+
+        optionMapIt = optionMap.find(CBOR(kUserPresenceMapKey));
+        if (optionMapIt != optionMap.end()) {
+            if (!optionMapIt->second.isBool())
+                return std::nullopt;
+
+            options.setUserPresenceRequired(optionMapIt->second.getBool());
+        }
+
+        optionMapIt = optionMap.find(CBOR(kUserVerificationMapKey));
+        if (optionMapIt != optionMap.end()) {
+            if (!optionMapIt->second.isBool())
+                return std::nullopt;
+
+            if (optionMapIt->second.getBool())
+                options.setUserVerificationAvailability(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured);
+            else
+                options.setUserVerificationAvailability(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedButNotConfigured);
+        }
+
+        optionMapIt = optionMap.find(CBOR(kClientPinMapKey));
+        if (optionMapIt != optionMap.end()) {
+            if (!optionMapIt->second.isBool())
+                return std::nullopt;
+
+            if (optionMapIt->second.getBool())
+                options.setClientPinAvailability(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet);
+            else
+                options.setClientPinAvailability(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedButPinNotSet);
+        }
+        response.setOptions(WTFMove(options));
+    }
+
+    it = responseMap.find(CBOR(5));
+    if (it != responseMap.end()) {
+        if (!it->second.isUnsigned())
+            return std::nullopt;
+
+        response.setMaxMsgSize(it->second.getUnsigned());
+    }
+
+    it = responseMap.find(CBOR(6));
+    if (it != responseMap.end()) {
+        if (!it->second.isArray())
+            return std::nullopt;
+
+        Vector<uint8_t> supportedPinProtocols;
+        for (const auto& protocol : it->second.getArray()) {
+            if (!protocol.isUnsigned())
+                return std::nullopt;
+
+            supportedPinProtocols.append(protocol.getUnsigned());
+        }
+        response.setPinProtocols(WTFMove(supportedPinProtocols));
+    }
+
+    return WTFMove(response);
+}
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.h b/Source/WebCore/Modules/webauthn/fido/DeviceResponseConverter.h
new file mode 100644 (file)
index 0000000..8196335
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+#include "AuthenticatorGetInfoResponse.h"
+#include "FidoConstants.h"
+#include "PublicKeyCredentialData.h"
+
+// Converts response from authenticators to CTAPResponse objects. If the
+// response of the authenticator does not conform to format specified by the
+// CTAP protocol, null optional is returned.
+namespace fido {
+
+// Parses response code from response received from the authenticator. If
+// unknown response code value is received, then CTAP2_ERR_OTHER is returned.
+WEBCORE_EXPORT CtapDeviceResponseCode getResponseCode(const Vector<uint8_t>&);
+
+// De-serializes CBOR encoded response, checks for valid CBOR map formatting,
+// and converts response to AuthenticatorMakeCredentialResponse object with
+// CBOR map keys that conform to format of attestation object defined by the
+// WebAuthN spec : https://w3c.github.io/webauthn/#fig-attStructs
+WEBCORE_EXPORT std::optional<WebCore::PublicKeyCredentialData> readCTAPMakeCredentialResponse(const Vector<uint8_t>&);
+
+// De-serializes CBOR encoded response to AuthenticatorGetAssertion /
+// AuthenticatorGetNextAssertion request to AuthenticatorGetAssertionResponse
+// object.
+// FIXME(190783): Probably need to remake AuthenticatorResponse to include more fields like numberOfCredentials,
+// and use it here instead of PublicKeyCredentialData.
+WEBCORE_EXPORT std::optional<WebCore::PublicKeyCredentialData> readCTAPGetAssertionResponse(const Vector<uint8_t>&);
+
+// De-serializes CBOR encoded response to AuthenticatorGetInfo request to
+// AuthenticatorGetInfoResponse object.
+WEBCORE_EXPORT std::optional<AuthenticatorGetInfoResponse> readCTAPGetInfoResponse(const Vector<uint8_t>&);
+
+} // namespace fido
+
+#endif // ENABLE(WEB_AUTHN)
index 3881fd2..81725ce 100644 (file)
 #if ENABLE(WEB_AUTHN)
 
 namespace fido {
+using namespace WebCore;
+
+bool isCtapDeviceResponseCode(CtapDeviceResponseCode code)
+{
+    switch (code) {
+    case CtapDeviceResponseCode::kSuccess:
+    case CtapDeviceResponseCode::kCtap1ErrInvalidCommand:
+    case CtapDeviceResponseCode::kCtap1ErrInvalidParameter:
+    case CtapDeviceResponseCode::kCtap1ErrInvalidLength:
+    case CtapDeviceResponseCode::kCtap1ErrInvalidSeq:
+    case CtapDeviceResponseCode::kCtap1ErrTimeout:
+    case CtapDeviceResponseCode::kCtap1ErrChannelBusy:
+    case CtapDeviceResponseCode::kCtap1ErrLockRequired:
+    case CtapDeviceResponseCode::kCtap1ErrInvalidChannel:
+    case CtapDeviceResponseCode::kCtap2ErrCBORParsing:
+    case CtapDeviceResponseCode::kCtap2ErrUnexpectedType:
+    case CtapDeviceResponseCode::kCtap2ErrInvalidCBOR:
+    case CtapDeviceResponseCode::kCtap2ErrInvalidCBORType:
+    case CtapDeviceResponseCode::kCtap2ErrMissingParameter:
+    case CtapDeviceResponseCode::kCtap2ErrLimitExceeded:
+    case CtapDeviceResponseCode::kCtap2ErrUnsupportedExtension:
+    case CtapDeviceResponseCode::kCtap2ErrTooManyElements:
+    case CtapDeviceResponseCode::kCtap2ErrExtensionNotSupported:
+    case CtapDeviceResponseCode::kCtap2ErrCredentialExcluded:
+    case CtapDeviceResponseCode::kCtap2ErrProcesssing:
+    case CtapDeviceResponseCode::kCtap2ErrInvalidCredential:
+    case CtapDeviceResponseCode::kCtap2ErrUserActionPending:
+    case CtapDeviceResponseCode::kCtap2ErrOperationPending:
+    case CtapDeviceResponseCode::kCtap2ErrNoOperations:
+    case CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithms:
+    case CtapDeviceResponseCode::kCtap2ErrOperationDenied:
+    case CtapDeviceResponseCode::kCtap2ErrKeyStoreFull:
+    case CtapDeviceResponseCode::kCtap2ErrNotBusy:
+    case CtapDeviceResponseCode::kCtap2ErrNoOperationPending:
+    case CtapDeviceResponseCode::kCtap2ErrUnsupportedOption:
+    case CtapDeviceResponseCode::kCtap2ErrInvalidOption:
+    case CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel:
+    case CtapDeviceResponseCode::kCtap2ErrNoCredentials:
+    case CtapDeviceResponseCode::kCtap2ErrUserActionTimeout:
+    case CtapDeviceResponseCode::kCtap2ErrNotAllowed:
+    case CtapDeviceResponseCode::kCtap2ErrPinInvalid:
+    case CtapDeviceResponseCode::kCtap2ErrPinBlocked:
+    case CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid:
+    case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked:
+    case CtapDeviceResponseCode::kCtap2ErrPinNotSet:
+    case CtapDeviceResponseCode::kCtap2ErrPinRequired:
+    case CtapDeviceResponseCode::kCtap2ErrPinPolicyViolation:
+    case CtapDeviceResponseCode::kCtap2ErrPinTokenExpired:
+    case CtapDeviceResponseCode::kCtap2ErrRequestTooLarge:
+    case CtapDeviceResponseCode::kCtap2ErrOther:
+    case CtapDeviceResponseCode::kCtap2ErrSpecLast:
+    case CtapDeviceResponseCode::kCtap2ErrExtensionFirst:
+    case CtapDeviceResponseCode::kCtap2ErrExtensionLast:
+    case CtapDeviceResponseCode::kCtap2ErrVendorFirst:
+    case CtapDeviceResponseCode::kCtap2ErrVendorLast:
+        return true;
+    default:
+        return false;
+    }
+}
 
 bool isFidoHidDeviceCommand(FidoHidDeviceCommand cmd)
 {
@@ -52,6 +112,16 @@ bool isFidoHidDeviceCommand(FidoHidDeviceCommand cmd)
     }
 }
 
+const char* publicKeyCredentialTypeToString(PublicKeyCredentialType type)
+{
+    switch (type) {
+    case PublicKeyCredentialType::PublicKey:
+        return kPublicKey;
+    }
+    ASSERT_NOT_REACHED();
+    return kPublicKey;
+}
+
 } // namespace fido
 
 #endif // ENABLE(WEB_AUTHN)
index b8690f0..5cd7ae8 100644 (file)
 
 #if ENABLE(WEB_AUTHN)
 
+#include "PublicKeyCredentialType.h"
+
 namespace fido {
 
+enum class ProtocolVersion {
+    kCtap,
+    kU2f,
+    kUnknown,
+};
+
+// Length of the SHA-256 hash of the RP ID asssociated with the credential:
+// https://www.w3.org/TR/webauthn/#sec-authenticator-data
+constexpr size_t kRpIdHashLength = 32;
+
+// Length of the flags:
+// https://www.w3.org/TR/webauthn/#sec-authenticator-data
+constexpr size_t kFlagsLength = 1;
+
+// Length of the signature counter, 32-bit unsigned big-endian integer:
+// https://www.w3.org/TR/webauthn/#sec-authenticator-data
+constexpr size_t kSignCounterLength = 4;
+
+// Length of the AAGUID of the authenticator:
+// https://www.w3.org/TR/webauthn/#sec-attested-credential-data
+constexpr size_t kAaguidLength = 16;
+
+// Length of the byte length L of Credential ID, 16-bit unsigned big-endian
+// integer: https://www.w3.org/TR/webauthn/#sec-attested-credential-data
+constexpr size_t kCredentialIdLengthLength = 2;
+
+// CTAP protocol device response code, as specified in
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#error-responses
+enum class CtapDeviceResponseCode : uint8_t {
+    kSuccess = 0x00,
+    kCtap1ErrInvalidCommand = 0x01,
+    kCtap1ErrInvalidParameter = 0x02,
+    kCtap1ErrInvalidLength = 0x03,
+    kCtap1ErrInvalidSeq = 0x04,
+    kCtap1ErrTimeout = 0x05,
+    kCtap1ErrChannelBusy = 0x06,
+    kCtap1ErrLockRequired = 0x0A,
+    kCtap1ErrInvalidChannel = 0x0B,
+    kCtap2ErrCBORParsing = 0x10,
+    kCtap2ErrUnexpectedType = 0x11,
+    kCtap2ErrInvalidCBOR = 0x12,
+    kCtap2ErrInvalidCBORType = 0x13,
+    kCtap2ErrMissingParameter = 0x14,
+    kCtap2ErrLimitExceeded = 0x15,
+    kCtap2ErrUnsupportedExtension = 0x16,
+    kCtap2ErrTooManyElements = 0x17,
+    kCtap2ErrExtensionNotSupported = 0x18,
+    kCtap2ErrCredentialExcluded = 0x19,
+    kCtap2ErrProcesssing = 0x21,
+    kCtap2ErrInvalidCredential = 0x22,
+    kCtap2ErrUserActionPending = 0x23,
+    kCtap2ErrOperationPending = 0x24,
+    kCtap2ErrNoOperations = 0x25,
+    kCtap2ErrUnsupportedAlgorithms = 0x26,
+    kCtap2ErrOperationDenied = 0x27,
+    kCtap2ErrKeyStoreFull = 0x28,
+    kCtap2ErrNotBusy = 0x29,
+    kCtap2ErrNoOperationPending = 0x2A,
+    kCtap2ErrUnsupportedOption = 0x2B,
+    kCtap2ErrInvalidOption = 0x2C,
+    kCtap2ErrKeepAliveCancel = 0x2D,
+    kCtap2ErrNoCredentials = 0x2E,
+    kCtap2ErrUserActionTimeout = 0x2F,
+    kCtap2ErrNotAllowed = 0x30,
+    kCtap2ErrPinInvalid = 0x31,
+    kCtap2ErrPinBlocked = 0x32,
+    kCtap2ErrPinAuthInvalid = 0x33,
+    kCtap2ErrPinAuthBlocked = 0x34,
+    kCtap2ErrPinNotSet = 0x35,
+    kCtap2ErrPinRequired = 0x36,
+    kCtap2ErrPinPolicyViolation = 0x37,
+    kCtap2ErrPinTokenExpired = 0x38,
+    kCtap2ErrRequestTooLarge = 0x39,
+    kCtap2ErrOther = 0x7F,
+    kCtap2ErrSpecLast = 0xDF,
+    kCtap2ErrExtensionFirst = 0xE0,
+    kCtap2ErrExtensionLast = 0xEF,
+    kCtap2ErrVendorFirst = 0xF0,
+    kCtap2ErrVendorLast = 0xFF
+};
+
+bool isCtapDeviceResponseCode(CtapDeviceResponseCode);
+
 // Commands supported by CTAPHID device as specified in
 // https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#ctaphid-commands
 enum class FidoHidDeviceCommand : uint8_t {
@@ -47,7 +132,24 @@ enum class FidoHidDeviceCommand : uint8_t {
     kLock = 0x04,
 };
 
-bool isFidoHidDeviceCommand(FidoHidDeviceCommand cmd);
+bool isFidoHidDeviceCommand(FidoHidDeviceCommand);
+
+// String key values for CTAP request optional parameters and
+// AuthenticatorGetInfo response.
+const char kResidentKeyMapKey[] = "rk";
+const char kUserVerificationMapKey[] = "uv";
+const char kUserPresenceMapKey[] = "up";
+const char kClientPinMapKey[] = "clientPin";
+const char kPlatformDeviceMapKey[] = "plat";
+const char kEntityIdMapKey[] = "id";
+const char kEntityNameMapKey[] = "name";
+const char kDisplayNameMapKey[] = "displayName";
+const char kIconUrlMapKey[] = "icon";
+const char kCredentialTypeMapKey[] = "type";
+const char kCredentialAlgorithmMapKey[] = "alg";
+// Keys for storing credential descriptor information in CBOR map.
+const char kCredentialIdKey[] = "id";
+const char kCredentialTypeKey[] = "type";
 
 // HID transport specific constants.
 const size_t kHidPacketSize = 64;
@@ -63,6 +165,34 @@ const uint8_t kHidMaxLockSeconds = 10;
 // Messages are limited to an initiation packet and 128 continuation packets.
 const size_t kHidMaxMessageSize = 7609;
 
+// Authenticator API commands supported by CTAP devices, as specified in
+// https://fidoalliance.org/specs/fido-v2.0-rd-20170927/fido-client-to-authenticator-protocol-v2.0-rd-20170927.html#authenticator-api
+enum class CtapRequestCommand : uint8_t {
+    kAuthenticatorMakeCredential = 0x01,
+    kAuthenticatorGetAssertion = 0x02,
+    kAuthenticatorGetNextAssertion = 0x08,
+    kAuthenticatorGetInfo = 0x04,
+    kAuthenticatorClientPin = 0x06,
+    kAuthenticatorReset = 0x07,
+};
+
+// String key values for attestation object as a response to MakeCredential
+// request.
+const char kFormatKey[] = "fmt";
+const char kAttestationStatementKey[] = "attStmt";
+const char kAuthDataKey[] = "authData";
+const char kNoneAttestationValue[] = "none";
+
+// String representation of public key credential enum.
+// https://w3c.github.io/webauthn/#credentialType
+const char kPublicKey[] = "public-key";
+
+const char* publicKeyCredentialTypeToString(WebCore::PublicKeyCredentialType);
+
+// FIXME: Add url to the official spec once it's standardized.
+const char kCtap2Version[] = "FIDO_2_0";
+const char kU2fVersion[] = "U2F_V2";
+
 } // namespace fido
 
 #endif // ENABLE(WEB_AUTHN)
index 60632cf..d64ab24 100644 (file)
@@ -259,6 +259,10 @@ Modules/webauthn/PublicKeyCredential.cpp
 Modules/webauthn/cbor/CBORReader.cpp
 Modules/webauthn/cbor/CBORValue.cpp
 Modules/webauthn/cbor/CBORWriter.cpp
+Modules/webauthn/fido/AuthenticatorGetInfoResponse.cpp
+Modules/webauthn/fido/AuthenticatorSupportedOptions.cpp
+Modules/webauthn/fido/DeviceRequestConverter.cpp
+Modules/webauthn/fido/DeviceResponseConverter.cpp
 Modules/webauthn/fido/FidoConstants.cpp
 Modules/webauthn/fido/FidoHidMessage.cpp
 Modules/webauthn/fido/FidoHidPacket.cpp
@@ -3179,6 +3183,7 @@ JSTreeWalker.cpp
 JSUIEvent.cpp
 JSUIEventInit.cpp
 JSURLSearchParams.cpp
+JSUserVerificationRequirement.cpp
 JSVRDisplay.cpp
 JSVRDisplayCapabilities.cpp
 JSVRDisplayEvent.cpp
index 14204a5..3f9dabc 100644 (file)
                5721A9871ECE53B10081295A /* CryptoDigestAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = 5721A9861ECE53B10081295A /* CryptoDigestAlgorithm.h */; };
                5721A98B1ECE57040081295A /* CryptoAlgorithmRsaPssParams.h in Headers */ = {isa = PBXBuildFile; fileRef = 5721A9881ECE57040081295A /* CryptoAlgorithmRsaPssParams.h */; };
                572A7F211C6E5719009C6149 /* SimulatedClick.h in Headers */ = {isa = PBXBuildFile; fileRef = 572A7F201C6E5719009C6149 /* SimulatedClick.h */; };
+               572B401F21757A64000AD43E /* DeviceRequestConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 572B401D21757A64000AD43E /* DeviceRequestConverter.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               572B402C21769020000AD43E /* JSUserVerificationRequirement.h in Headers */ = {isa = PBXBuildFile; fileRef = 572B402A2176901F000AD43E /* JSUserVerificationRequirement.h */; };
+               572B40362176A922000AD43E /* UserVerificationRequirement.h in Headers */ = {isa = PBXBuildFile; fileRef = 572B402321768D85000AD43E /* UserVerificationRequirement.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               572B403A21772581000AD43E /* DeviceResponseConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 572B40372177256C000AD43E /* DeviceResponseConverter.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               572B404021780171000AD43E /* AuthenticatorSupportedOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 572B403E21780170000AD43E /* AuthenticatorSupportedOptions.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               572B40422178114A000AD43E /* AuthenticatorGetInfoResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 572B403C2177FB61000AD43E /* AuthenticatorGetInfoResponse.h */; settings = {ATTRIBUTES = (Private, ); }; };
                57303BB92006C6EE00355965 /* CBORValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BB42006C6ED00355965 /* CBORValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
                57303BBA2006C6EE00355965 /* CBORWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BB52006C6ED00355965 /* CBORWriter.h */; settings = {ATTRIBUTES = (Private, ); }; };
                57303BBB2006C6EE00355965 /* CBORBinary.h in Headers */ = {isa = PBXBuildFile; fileRef = 57303BB62006C6ED00355965 /* CBORBinary.h */; };
                5721A98A1ECE57040081295A /* RsaPssParams.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = RsaPssParams.idl; sourceTree = "<group>"; };
                572A7F201C6E5719009C6149 /* SimulatedClick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimulatedClick.h; sourceTree = "<group>"; };
                572A7F221C6E5A66009C6149 /* SimulatedClick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SimulatedClick.cpp; sourceTree = "<group>"; };
+               572B401D21757A64000AD43E /* DeviceRequestConverter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeviceRequestConverter.h; sourceTree = "<group>"; };
+               572B401E21757A64000AD43E /* DeviceRequestConverter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DeviceRequestConverter.cpp; sourceTree = "<group>"; };
+               572B402321768D85000AD43E /* UserVerificationRequirement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserVerificationRequirement.h; sourceTree = "<group>"; };
+               572B402521768D85000AD43E /* UserVerificationRequirement.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = UserVerificationRequirement.idl; sourceTree = "<group>"; };
+               572B402A2176901F000AD43E /* JSUserVerificationRequirement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSUserVerificationRequirement.h; sourceTree = "<group>"; };
+               572B402B2176901F000AD43E /* JSUserVerificationRequirement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSUserVerificationRequirement.cpp; sourceTree = "<group>"; };
+               572B40372177256C000AD43E /* DeviceResponseConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DeviceResponseConverter.h; sourceTree = "<group>"; };
+               572B40382177256C000AD43E /* DeviceResponseConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeviceResponseConverter.cpp; sourceTree = "<group>"; };
+               572B403B2177FB60000AD43E /* AuthenticatorGetInfoResponse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AuthenticatorGetInfoResponse.cpp; sourceTree = "<group>"; };
+               572B403C2177FB61000AD43E /* AuthenticatorGetInfoResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthenticatorGetInfoResponse.h; sourceTree = "<group>"; };
+               572B403D2178016F000AD43E /* AuthenticatorSupportedOptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AuthenticatorSupportedOptions.cpp; sourceTree = "<group>"; };
+               572B403E21780170000AD43E /* AuthenticatorSupportedOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthenticatorSupportedOptions.h; sourceTree = "<group>"; };
                57303BB42006C6ED00355965 /* CBORValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBORValue.h; sourceTree = "<group>"; };
                57303BB52006C6ED00355965 /* CBORWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBORWriter.h; sourceTree = "<group>"; };
                57303BB62006C6ED00355965 /* CBORBinary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBORBinary.h; sourceTree = "<group>"; };
                578A4BFA2166AE0000D08F34 /* fido */ = {
                        isa = PBXGroup;
                        children = (
+                               572B403B2177FB60000AD43E /* AuthenticatorGetInfoResponse.cpp */,
+                               572B403C2177FB61000AD43E /* AuthenticatorGetInfoResponse.h */,
+                               572B403D2178016F000AD43E /* AuthenticatorSupportedOptions.cpp */,
+                               572B403E21780170000AD43E /* AuthenticatorSupportedOptions.h */,
+                               572B401E21757A64000AD43E /* DeviceRequestConverter.cpp */,
+                               572B401D21757A64000AD43E /* DeviceRequestConverter.h */,
+                               572B40382177256C000AD43E /* DeviceResponseConverter.cpp */,
+                               572B40372177256C000AD43E /* DeviceResponseConverter.h */,
                                57BAF2872167D2F7008E954E /* FidoConstants.cpp */,
                                578A4C0B2167D29600D08F34 /* FidoConstants.h */,
                                578A4C082167D29500D08F34 /* FidoHidMessage.cpp */,
                                57303C08200998F800355965 /* PublicKeyCredentialRequestOptions.idl */,
                                57303BEA20097F4000355965 /* PublicKeyCredentialType.h */,
                                57303BF02009846100355965 /* PublicKeyCredentialType.idl */,
+                               572B402321768D85000AD43E /* UserVerificationRequirement.h */,
+                               572B402521768D85000AD43E /* UserVerificationRequirement.idl */,
                        );
                        path = webauthn;
                        sourceTree = "<group>";
                                57303C0C20099C7500355965 /* JSPublicKeyCredentialRequestOptions.h */,
                                57303BFA2009913500355965 /* JSPublicKeyCredentialType.cpp */,
                                57303BF92009913400355965 /* JSPublicKeyCredentialType.h */,
+                               572B402B2176901F000AD43E /* JSUserVerificationRequirement.cpp */,
+                               572B402A2176901F000AD43E /* JSUserVerificationRequirement.h */,
                        );
                        name = WebAuthN;
                        sourceTree = "<group>";
                                57303C1F2009AB4200355965 /* AuthenticatorAttestationResponse.h in Headers */,
                                57303C4620105D2F00355965 /* AuthenticatorCoordinator.h in Headers */,
                                5760827220215A5500116678 /* AuthenticatorCoordinatorClient.h in Headers */,
+                               572B40422178114A000AD43E /* AuthenticatorGetInfoResponse.h in Headers */,
                                57303BD220087A8300355965 /* AuthenticatorResponse.h in Headers */,
+                               572B404021780171000AD43E /* AuthenticatorSupportedOptions.h in Headers */,
                                57DCED9021487FF70016B847 /* AuthenticatorTransport.h in Headers */,
                                A501920E132EBF2E008BFE55 /* Autocapitalize.h in Headers */,
                                A5A7AA43132F0ECC00D3A3C2 /* AutocapitalizeTypes.h in Headers */,
                                59A8F1D611A69513001AC34A /* DeviceOrientationController.h in Headers */,
                                590E1B4911E4EF4B0069F784 /* DeviceOrientationData.h in Headers */,
                                59A85EA4119D68EC00DEF1EF /* DeviceOrientationEvent.h in Headers */,
+                               572B401F21757A64000AD43E /* DeviceRequestConverter.h in Headers */,
+                               572B403A21772581000AD43E /* DeviceResponseConverter.h in Headers */,
                                267725FD1A5B3AD9003C24DD /* DFA.h in Headers */,
                                5CD9F5661AA0F73C00DA45FF /* DFABytecode.h in Headers */,
                                5CD9F5671AA0F74200DA45FF /* DFABytecodeCompiler.h in Headers */,
                                465307D01DB6EE4800E4137C /* JSUIEventInit.h in Headers */,
                                7C73FB12191EF6F4007DE061 /* JSUserMessageHandler.h in Headers */,
                                7C73FB0D191EF5A8007DE061 /* JSUserMessageHandlersNamespace.h in Headers */,
+                               572B402C21769020000AD43E /* JSUserVerificationRequirement.h in Headers */,
                                15C77093100D3CA8005BA267 /* JSValidityState.h in Headers */,
                                BE8EF04B171C9014009B48C3 /* JSVideoTrack.h in Headers */,
                                BE8EF04D171C9014009B48C3 /* JSVideoTrackList.h in Headers */,
                                BC8BF151105813BF00A40A07 /* UserStyleSheet.h in Headers */,
                                BC8BF15A1058141800A40A07 /* UserStyleSheetTypes.h in Headers */,
                                BCDF317C11F8D683003C5BF8 /* UserTypingGestureIndicator.h in Headers */,
+                               572B40362176A922000AD43E /* UserVerificationRequirement.h in Headers */,
                                55AF14E61EAAC59B0026EEAA /* UTIRegistry.h in Headers */,
                                1FAFBF1915A5FA7400083A20 /* UTIUtilities.h in Headers */,
                                83C45B8E1DC2B68A008871BA /* ValidationBubble.h in Headers */,
index 84e5b36..a3d6660 100644 (file)
@@ -1,3 +1,23 @@
+2018-11-08  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Import CTAP device request/response converters from Chromium
+        https://bugs.webkit.org/show_bug.cgi?id=190784
+        <rdar://problem/45460333>
+
+        Reviewed by Brent Fulgham.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebCore/CBORValueTest.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp: Added.
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp: Added.
+        (TestWebKitAPI::getTestCorruptedSignResponse):
+        (TestWebKitAPI::getTestCredentialRawIdBytes):
+        (TestWebKitAPI::convertToVector):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebCore/FidoTestData.h: Added.
+
 2018-11-07  Caio Lima  <ticaiolima@gmail.com>
 
         [BigInt] Add support to BigInt into ValueAdd
index 22d57b4..7ef0314 100644 (file)
                5714ECBB1CA8BFE400051AC8 /* DownloadRequestOriginalURLFrame.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */; };
                5714ECBD1CA8C22A00051AC8 /* DownloadRequestOriginalURL2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECBC1CA8C21800051AC8 /* DownloadRequestOriginalURL2.html */; };
                571F7FD01F2961FB00946648 /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-wal in Copy Resources */ = {isa = PBXBuildFile; fileRef = 571F7FCF1F2961E100946648 /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-wal */; };
+               572B403421769A88000AD43E /* CtapRequestTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 572B403321769A88000AD43E /* CtapRequestTest.cpp */; };
+               572B404421781B43000AD43E /* CtapResponseTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 572B404321781B42000AD43E /* CtapResponseTest.cpp */; };
                57303BC9200824D300355965 /* CBORValueTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57303BAC2006C56000355965 /* CBORValueTest.cpp */; };
                57303BCA20082C0100355965 /* CBORWriterTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57303BAB2006C55400355965 /* CBORWriterTest.cpp */; };
                57303BCB2008376500355965 /* CBORReaderTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 57303BC220071E2200355965 /* CBORReaderTest.cpp */; };
                5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DownloadRequestOriginalURLFrame.html; sourceTree = "<group>"; };
                5714ECBC1CA8C21800051AC8 /* DownloadRequestOriginalURL2.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DownloadRequestOriginalURL2.html; sourceTree = "<group>"; };
                571F7FCF1F2961E100946648 /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-wal */ = {isa = PBXFileReference; lastKnownFileType = file; path = "IndexedDBStructuredCloneBackwardCompatibility.sqlite3-wal"; sourceTree = "<group>"; };
+               572B403321769A88000AD43E /* CtapRequestTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CtapRequestTest.cpp; sourceTree = "<group>"; };
+               572B40352176A029000AD43E /* FidoTestData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FidoTestData.h; sourceTree = "<group>"; };
+               572B404321781B42000AD43E /* CtapResponseTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CtapResponseTest.cpp; sourceTree = "<group>"; };
                57303BAB2006C55400355965 /* CBORWriterTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CBORWriterTest.cpp; sourceTree = "<group>"; };
                57303BAC2006C56000355965 /* CBORValueTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CBORValueTest.cpp; sourceTree = "<group>"; };
                57303BC220071E2200355965 /* CBORReaderTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CBORReaderTest.cpp; sourceTree = "<group>"; };
                                1C9EB8401E380DA1005C6442 /* ComplexTextController.cpp */,
                                7CB184C41AA3F2100066EDFD /* ContentExtensions.cpp */,
                                CD5451E919E41F9D0016936F /* CSSParser.cpp */,
+                               572B403321769A88000AD43E /* CtapRequestTest.cpp */,
+                               572B404321781B42000AD43E /* CtapResponseTest.cpp */,
                                260BA5781B1D2E7B004FA07C /* DFACombiner.cpp */,
                                260BA57A1B1D2EE2004FA07C /* DFAHelpers.h */,
                                26F6E1EF1ADC749B00DE696B /* DFAMinimizer.cpp */,
                                3151180F1DB1ADD500176304 /* ExtendedColor.cpp */,
                                579651E6216BFD53006EBFE5 /* FidoHidMessageTest.cpp */,
+                               572B40352176A029000AD43E /* FidoTestData.h */,
                                7A32D7491F02151500162C44 /* FileMonitor.cpp */,
                                41973B5A1AF2286A006C7B36 /* FileSystem.cpp */,
                                7A909A701D877475007E10F8 /* FloatPoint.cpp */,
                                7CCE7EAC1A411A3400447C4C /* Counters.cpp in Sources */,
                                7AEAD47F1E20116C00416EFE /* CrossPartitionFileSchemeAccess.mm in Sources */,
                                7CCE7EDB1A411A9200447C4C /* CSSParser.cpp in Sources */,
+                               572B403421769A88000AD43E /* CtapRequestTest.cpp in Sources */,
+                               572B404421781B43000AD43E /* CtapResponseTest.cpp in Sources */,
                                7AC7B57020D9BA5B002C09A0 /* CustomBundleObject.mm in Sources */,
                                7AC7B56D20D9769F002C09A0 /* CustomBundleParameter.mm in Sources */,
                                7CCE7F291A411B1000447C4C /* CustomProtocolsInvalidScheme.mm in Sources */,
index a2d6b89..ff520dc 100644 (file)
@@ -148,6 +148,17 @@ TEST(CBORValueTest, ConstructSimpleValue)
     EXPECT_TRUE(CBORValue::SimpleValue::Undefined == undefinedValue.getSimpleValue());
 }
 
+TEST(CBORValueTest, ConstructSimpleBooleanValue)
+{
+    CBORValue trueValue(true);
+    ASSERT_EQ(CBORValue::Type::SimpleValue, trueValue.type());
+    EXPECT_TRUE(trueValue.getBool());
+
+    CBORValue falseValue(false);
+    ASSERT_EQ(CBORValue::Type::SimpleValue, falseValue.type());
+    EXPECT_FALSE(falseValue.getBool());
+}
+
 // Test copy constructors
 TEST(CBORValueTest, CopyUnsigned)
 {
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CtapRequestTest.cpp
new file mode 100644 (file)
index 0000000..f439dad
--- /dev/null
@@ -0,0 +1,128 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+
+#if ENABLE(WEB_AUTHN)
+
+#include "FidoTestData.h"
+#include <WebCore/DeviceRequestConverter.h>
+#include <WebCore/FidoConstants.h>
+#include <WebCore/PublicKeyCredentialCreationOptions.h>
+#include <WebCore/PublicKeyCredentialRequestOptions.h>
+#include <wtf/text/Base64.h>
+
+namespace TestWebKitAPI {
+using namespace WebCore;
+using namespace fido;
+
+// Leveraging example 2 of section 6.1 of the spec
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html
+TEST(CTAPRequestTest, TestConstructMakeCredentialRequestParam)
+{
+    PublicKeyCredentialCreationOptions::RpEntity rp;
+    rp.name = "Acme";
+    rp.id = "acme.com";
+
+    PublicKeyCredentialCreationOptions::UserEntity user;
+    user.name = "johnpsmith@example.com";
+    user.icon = "https://pics.acme.com/00/p/aBjjjpqPb.png";
+    user.idVector.append(TestData::kUserId, sizeof(TestData::kUserId));
+    user.displayName = "John P. Smith";
+
+    Vector<PublicKeyCredentialCreationOptions::Parameters> params { { PublicKeyCredentialType::PublicKey, 7 }, { PublicKeyCredentialType::PublicKey, 257 } };
+    PublicKeyCredentialCreationOptions::AuthenticatorSelectionCriteria selection { PublicKeyCredentialCreationOptions::AuthenticatorAttachment::Platform, true, UserVerificationRequirement::Preferred };
+
+    PublicKeyCredentialCreationOptions options { rp, user, { }, params, std::nullopt, { }, selection };
+    Vector<uint8_t> hash;
+    hash.append(TestData::kClientDataHash, sizeof(TestData::kClientDataHash));
+    auto serializedData = encodeMakeCredenitalRequestAsCBOR(hash, options, AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedButNotConfigured);
+    EXPECT_EQ(serializedData.size(), sizeof(TestData::kCtapMakeCredentialRequest));
+    EXPECT_EQ(memcmp(serializedData.data(), TestData::kCtapMakeCredentialRequest, serializedData.size()), 0);
+}
+
+TEST(CTAPRequestTest, TestConstructGetAssertionRequest)
+{
+    PublicKeyCredentialRequestOptions options;
+    options.rpId = "acme.com";
+
+    PublicKeyCredentialDescriptor descriptor1;
+    descriptor1.type = PublicKeyCredentialType::PublicKey;
+    const uint8_t id1[] = {
+        0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94,
+        0x2f, 0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b,
+        0x3d, 0xf8, 0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0,
+        0x34, 0x85, 0x8a, 0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98,
+        0x08, 0xd9, 0x4f, 0xcb, 0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77,
+        0xaf, 0x0a, 0xdc, 0xc3, 0x58, 0x52, 0xea, 0x6b, 0x9e };
+    descriptor1.idVector.append(id1, sizeof(id1));
+    options.allowCredentials.append(descriptor1);
+
+    PublicKeyCredentialDescriptor descriptor2;
+    descriptor2.type = PublicKeyCredentialType::PublicKey;
+    const uint8_t id2[] = {
+        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+        0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
+    descriptor2.idVector.append(id2, sizeof(id2));
+    options.allowCredentials.append(descriptor2);
+
+    options.userVerification = UserVerificationRequirement::Required;
+
+    Vector<uint8_t> hash;
+    hash.append(TestData::kClientDataHash, sizeof(TestData::kClientDataHash));
+    auto serializedData = encodeGetAssertionRequestAsCBOR(hash, options, AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedButNotConfigured);
+    EXPECT_EQ(serializedData.size(), sizeof(TestData::kTestComplexCtapGetAssertionRequest));
+    EXPECT_EQ(memcmp(serializedData.data(), TestData::kTestComplexCtapGetAssertionRequest, serializedData.size()), 0);
+}
+
+TEST(CTAPRequestTest, TestConstructCtapAuthenticatorRequestParam)
+{
+    static constexpr uint8_t kSerializedGetInfoCmd = 0x04;
+    static constexpr uint8_t kSerializedGetNextAssertionCmd = 0x08;
+    static constexpr uint8_t kSerializedResetCmd = 0x07;
+
+    auto serializedData1 = encodeEmptyAuthenticatorRequest(CtapRequestCommand::kAuthenticatorGetInfo);
+    EXPECT_EQ(serializedData1.size(), 1u);
+    EXPECT_EQ(memcmp(serializedData1.data(), &kSerializedGetInfoCmd, 1), 0);
+
+    auto serializedData2 = encodeEmptyAuthenticatorRequest(CtapRequestCommand::kAuthenticatorGetNextAssertion);
+    EXPECT_EQ(serializedData2.size(), 1u);
+    EXPECT_EQ(memcmp(serializedData2.data(), &kSerializedGetNextAssertionCmd, 1), 0);
+
+    auto serializedData3 = encodeEmptyAuthenticatorRequest(CtapRequestCommand::kAuthenticatorReset);
+    EXPECT_EQ(serializedData3.size(), 1u);
+    EXPECT_EQ(memcmp(serializedData3.data(), &kSerializedResetCmd, 1), 0);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CtapResponseTest.cpp
new file mode 100644 (file)
index 0000000..3b5d25e
--- /dev/null
@@ -0,0 +1,354 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+
+#if ENABLE(WEB_AUTHN)
+
+#include "FidoTestData.h"
+#include <WebCore/CBORReader.h>
+#include <WebCore/CBORValue.h>
+#include <WebCore/CBORWriter.h>
+#include <WebCore/DeviceResponseConverter.h>
+#include <WebCore/FidoConstants.h>
+#include <WebCore/PublicKeyCredential.h>
+
+namespace TestWebKitAPI {
+using namespace fido;
+
+constexpr uint8_t kTestAuthenticatorGetInfoResponseWithNoVersion[] = {
+    // Success status byte
+    0x00,
+    // Map of 6 elements
+    0xA6,
+    // Key(01) - versions
+    0x01,
+    // Array(0)
+    0x80,
+    // Key(02) - extensions
+    0x02,
+    // Array(2)
+    0x82,
+    // "uvm"
+    0x63, 0x75, 0x76, 0x6D,
+    // "hmac-secret"
+    0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
+    // Key(03) - AAGUID
+    0x03,
+    // Bytes(16)
+    0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17,
+    0x11, 0x1F, 0x9E, 0xDC, 0x7D,
+    // Key(04) - options
+    0x04,
+    // Map(05)
+    0xA5,
+    // Key - "rk"
+    0x62, 0x72, 0x6B,
+    // true
+    0xF5,
+    // Key - "up"
+    0x62, 0x75, 0x70,
+    // true
+    0xF5,
+    // Key - "uv"
+    0x62, 0x75, 0x76,
+    // true
+    0xF5,
+    // Key - "plat"
+    0x64, 0x70, 0x6C, 0x61, 0x74,
+    // true
+    0xF5,
+    // Key - "clientPin"
+    0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E,
+    // false
+    0xF4,
+    // Key(05) - Max message size
+    0x05,
+    // 1200
+    0x19, 0x04, 0xB0,
+    // Key(06) - Pin protocols
+    0x06,
+    // Array[1]
+    0x81, 0x01,
+};
+
+constexpr uint8_t kTestAuthenticatorGetInfoResponseWithDuplicateVersion[] = {
+    // Success status byte
+    0x00,
+    // Map of 6 elements
+    0xA6,
+    // Key(01) - versions
+    0x01,
+    // Array(02)
+    0x82,
+    // "U2F_V2"
+    0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32,
+    // "U2F_V2"
+    0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32,
+    // Key(02) - extensions
+    0x02,
+    // Array(2)
+    0x82,
+    // "uvm"
+    0x63, 0x75, 0x76, 0x6D,
+    // "hmac-secret"
+    0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
+    // Key(03) - AAGUID
+    0x03,
+    // Bytes(16)
+    0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17,
+    0x11, 0x1F, 0x9E, 0xDC, 0x7D,
+    // Key(04) - options
+    0x04,
+    // Map(05)
+    0xA5,
+    // Key - "rk"
+    0x62, 0x72, 0x6B,
+    // true
+    0xF5,
+    // Key - "up"
+    0x62, 0x75, 0x70,
+    // true
+    0xF5,
+    // Key - "uv"
+    0x62, 0x75, 0x76,
+    // true
+    0xF5,
+    // Key - "plat"
+    0x64, 0x70, 0x6C, 0x61, 0x74,
+    // true
+    0xF5,
+    // Key - "clientPin"
+    0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E,
+    // false
+    0xF4,
+    // Key(05) - Max message size
+    0x05,
+    // 1200
+    0x19, 0x04, 0xB0,
+    // Key(06) - Pin protocols
+    0x06,
+    // Array[1]
+    0x81, 0x01,
+};
+
+constexpr uint8_t kTestAuthenticatorGetInfoResponseWithIncorrectAaguid[] = {
+    // Success status byte
+    0x00,
+    // Map of 6 elements
+    0xA6,
+    // Key(01) - versions
+    0x01,
+    // Array(01)
+    0x81,
+    // "U2F_V2"
+    0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32,
+    // Key(02) - extensions
+    0x02,
+    // Array(2)
+    0x82,
+    // "uvm"
+    0x63, 0x75, 0x76, 0x6D,
+    // "hmac-secret"
+    0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
+    // Key(03) - AAGUID
+    0x03,
+    // Bytes(17) - FIDO2 device AAGUID must be 16 bytes long in order to be
+    // correct.
+    0x51, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17,
+    0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x00,
+    // Key(04) - options
+    0x04,
+    // Map(05)
+    0xA5,
+    // Key - "rk"
+    0x62, 0x72, 0x6B,
+    // true
+    0xF5,
+    // Key - "up"
+    0x62, 0x75, 0x70,
+    // true
+    0xF5,
+    // Key - "uv"
+    0x62, 0x75, 0x76,
+    // true
+    0xF5,
+    // Key - "plat"
+    0x64, 0x70, 0x6C, 0x61, 0x74,
+    // true
+    0xF5,
+    // Key - "clientPin"
+    0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E,
+    // false
+    0xF4,
+    // Key(05) - Max message size
+    0x05,
+    // 1200
+    0x19, 0x04, 0xB0,
+    // Key(06) - Pin protocols
+    0x06,
+    // Array[1]
+    0x81, 0x01,
+};
+
+constexpr uint8_t kTestDeviceAaguid[] = {
+    0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1F, 0x9E, 0xDC, 0x7D
+};
+
+// Get a subset of the response for testing error handling.
+Vector<uint8_t> getTestCorruptedSignResponse(size_t length)
+{
+    ASSERT(length < sizeof(TestData::kTestU2fSignResponse));
+    Vector<uint8_t> testCorruptedSignResponse;
+    testCorruptedSignResponse.reserveInitialCapacity(length);
+    testCorruptedSignResponse.append(TestData::kTestU2fSignResponse, length);
+    return testCorruptedSignResponse;
+}
+
+// Return a key handle used for GetAssertion request.
+Vector<uint8_t> getTestCredentialRawIdBytes()
+{
+    Vector<uint8_t> testCredentialRawIdBytes;
+    testCredentialRawIdBytes.reserveInitialCapacity(sizeof(TestData::kU2fSignKeyHandle));
+    testCredentialRawIdBytes.append(TestData::kU2fSignKeyHandle, sizeof(TestData::kU2fSignKeyHandle));
+    return testCredentialRawIdBytes;
+}
+
+Vector<uint8_t> convertToVector(const uint8_t byteArray[], const size_t length)
+{
+    Vector<uint8_t> result;
+    result.reserveInitialCapacity(length);
+    result.append(byteArray, length);
+    return result;
+}
+
+// Leveraging example 4 of section 6.1 of the spec
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#commands
+TEST(CTAPResponseTest, TestReadMakeCredentialResponse)
+{
+    auto makeCredentialResponse = readCTAPMakeCredentialResponse(convertToVector(TestData::kTestMakeCredentialResponse, sizeof(TestData::kTestMakeCredentialResponse)));
+    ASSERT_TRUE(makeCredentialResponse);
+    auto cborAttestationObject = cbor::CBORReader::read(convertToVector(reinterpret_cast<uint8_t*>(makeCredentialResponse->attestationObject->data()), makeCredentialResponse->attestationObject->byteLength()));
+    ASSERT_TRUE(cborAttestationObject);
+    ASSERT_TRUE(cborAttestationObject->isMap());
+
+    const auto& attestationObjectMap = cborAttestationObject->getMap();
+    auto it = attestationObjectMap.find(cbor::CBORValue(kFormatKey));
+    ASSERT_TRUE(it != attestationObjectMap.end());
+    ASSERT_TRUE(it->second.isString());
+    EXPECT_STREQ(it->second.getString().utf8().data(), "packed");
+
+    it = attestationObjectMap.find(cbor::CBORValue(kAuthDataKey));
+    ASSERT_TRUE(it != attestationObjectMap.end());
+    ASSERT_TRUE(it->second.isByteString());
+    EXPECT_EQ(it->second.getByteString(), convertToVector(TestData::kCtap2MakeCredentialAuthData, sizeof(TestData::kCtap2MakeCredentialAuthData)));
+
+    it = attestationObjectMap.find(cbor::CBORValue(kAttestationStatementKey));
+    ASSERT_TRUE(it != attestationObjectMap.end());
+    ASSERT_TRUE(it->second.isMap());
+
+    const auto& attestationStatementMap = it->second.getMap();
+    auto attStmtIt = attestationStatementMap.find(cbor::CBORValue("alg"));
+
+    ASSERT_TRUE(attStmtIt != attestationStatementMap.end());
+    ASSERT_TRUE(attStmtIt->second.isInteger());
+    EXPECT_EQ(attStmtIt->second.getInteger(), -7);
+
+    attStmtIt = attestationStatementMap.find(cbor::CBORValue("sig"));
+    ASSERT_TRUE(attStmtIt != attestationStatementMap.end());
+    ASSERT_TRUE(attStmtIt->second.isByteString());
+    EXPECT_EQ(attStmtIt->second.getByteString(), convertToVector(TestData::kCtap2MakeCredentialSignature, sizeof(TestData::kCtap2MakeCredentialSignature)));
+
+    attStmtIt = attestationStatementMap.find(cbor::CBORValue("x5c"));
+    ASSERT_TRUE(attStmtIt != attestationStatementMap.end());
+    const auto& certificate = attStmtIt->second;
+    ASSERT_TRUE(certificate.isArray());
+    ASSERT_EQ(certificate.getArray().size(), 1u);
+    ASSERT_TRUE(certificate.getArray()[0].isByteString());
+    EXPECT_EQ(certificate.getArray()[0].getByteString(), convertToVector(TestData::kCtap2MakeCredentialCertificate, sizeof(TestData::kCtap2MakeCredentialCertificate)));
+    EXPECT_EQ(makeCredentialResponse->rawId->byteLength(), sizeof(TestData::kCtap2MakeCredentialCredentialId));
+    EXPECT_EQ(memcmp(makeCredentialResponse->rawId->data(), TestData::kCtap2MakeCredentialCredentialId, sizeof(TestData::kCtap2MakeCredentialCredentialId)), 0);
+}
+
+// Leveraging example 5 of section 6.1 of the CTAP spec.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html
+TEST(CTAPResponseTest, TestReadGetAssertionResponse)
+{
+    auto getAssertionResponse = readCTAPGetAssertionResponse(convertToVector(TestData::kDeviceGetAssertionResponse, sizeof(TestData::kDeviceGetAssertionResponse)));
+    ASSERT_TRUE(getAssertionResponse);
+
+    EXPECT_EQ(getAssertionResponse->authenticatorData->byteLength(), sizeof(TestData::kCtap2GetAssertionAuthData));
+    EXPECT_EQ(memcmp(getAssertionResponse->authenticatorData->data(), TestData::kCtap2GetAssertionAuthData, sizeof(TestData::kCtap2GetAssertionAuthData)), 0);
+    EXPECT_EQ(getAssertionResponse->signature->byteLength(), sizeof(TestData::kCtap2GetAssertionSignature));
+    EXPECT_EQ(memcmp(getAssertionResponse->signature->data(), TestData::kCtap2GetAssertionSignature, sizeof(TestData::kCtap2GetAssertionSignature)), 0);
+}
+
+TEST(CTAPResponseTest, TestReadGetInfoResponse)
+{
+    auto getInfoResponse = readCTAPGetInfoResponse(convertToVector(TestData::kTestGetInfoResponsePlatformDevice, sizeof(TestData::kTestGetInfoResponsePlatformDevice)));
+    ASSERT_TRUE(getInfoResponse);
+    ASSERT_TRUE(getInfoResponse->maxMsgSize());
+    EXPECT_EQ(*getInfoResponse->maxMsgSize(), 1200u);
+    EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kCtap), getInfoResponse->versions().end());
+    EXPECT_NE(getInfoResponse->versions().find(ProtocolVersion::kU2f), getInfoResponse->versions().end());
+    EXPECT_TRUE(getInfoResponse->options().isPlatformDevice());
+    EXPECT_TRUE(getInfoResponse->options().supportsResidentKey());
+    EXPECT_TRUE(getInfoResponse->options().userPresenceRequired());
+    EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured, getInfoResponse->options().userVerificationAvailability());
+    EXPECT_EQ(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedButPinNotSet, getInfoResponse->options().clientPinAvailability());
+}
+
+TEST(CTAPResponseTest, TestReadGetInfoResponseWithIncorrectFormat)
+{
+    EXPECT_FALSE(readCTAPGetInfoResponse(convertToVector(kTestAuthenticatorGetInfoResponseWithNoVersion, sizeof(kTestAuthenticatorGetInfoResponseWithNoVersion))));
+    EXPECT_FALSE(readCTAPGetInfoResponse(convertToVector(kTestAuthenticatorGetInfoResponseWithDuplicateVersion, sizeof(kTestAuthenticatorGetInfoResponseWithDuplicateVersion))));
+    EXPECT_FALSE(readCTAPGetInfoResponse(convertToVector(kTestAuthenticatorGetInfoResponseWithIncorrectAaguid, sizeof(kTestAuthenticatorGetInfoResponseWithIncorrectAaguid))));
+}
+
+TEST(CTAPResponseTest, TestSerializeGetInfoResponse)
+{
+    AuthenticatorGetInfoResponse response({ ProtocolVersion::kCtap, ProtocolVersion::kU2f }, convertToVector(kTestDeviceAaguid, sizeof(kTestDeviceAaguid)));
+    response.setExtensions({ "uvm", "hmac-secret" });
+    AuthenticatorSupportedOptions options;
+    options.setSupportsResidentKey(true);
+    options.setIsPlatformDevice(true);
+    options.setClientPinAvailability(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedButPinNotSet);
+    options.setUserVerificationAvailability(AuthenticatorSupportedOptions::UserVerificationAvailability::kSupportedAndConfigured);
+    response.setOptions(WTFMove(options));
+    response.setMaxMsgSize(1200);
+    response.setPinProtocols({ 1 });
+
+    auto responseAsCBOR = encodeAsCBOR(response);
+    EXPECT_EQ(responseAsCBOR.size(), sizeof(TestData::kTestGetInfoResponsePlatformDevice) - 1);
+    EXPECT_EQ(memcmp(responseAsCBOR.data(), TestData::kTestGetInfoResponsePlatformDevice + 1, responseAsCBOR.size()), 0);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // ENABLE(WEB_AUTHN)
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h b/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h
new file mode 100644 (file)
index 0000000..efea65b
--- /dev/null
@@ -0,0 +1,645 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright (C) 2018 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#if ENABLE(WEB_AUTHN)
+
+namespace TestWebKitAPI {
+
+namespace TestData {
+
+// U2F request parameters ------------------------------------------------------
+
+// Sample U2F register request parameters used in example 6 of the CTAP spec.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#using-the-ctap2-authenticatormakecredential-command-with-ctap1-u2f-authenticators
+constexpr uint8_t kClientDataHash[] = {
+    0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e, 0x42,
+    0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05,
+    0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41};
+
+constexpr uint8_t kUserId[] = {0x10, 0x98, 0x23, 0x72, 0x35, 0x40, 0x98, 0x72};
+
+// Sample U2F sign request parameters used in example 7 of the CTAP spec.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#using-the-ctap2-authenticatormakecredential-command-with-ctap1-u2f-authenticators
+constexpr uint8_t kU2fSignKeyHandle[] = {
+    0x3E, 0xBD, 0x89, 0xBF, 0x77, 0xEC, 0x50, 0x97, 0x55, 0xEE, 0x9C,
+    0x26, 0x35, 0xEF, 0xAA, 0xAC, 0x7B, 0x2B, 0x9C, 0x5C, 0xEF, 0x17,
+    0x36, 0xC3, 0x71, 0x7D, 0xA4, 0x85, 0x34, 0xC8, 0xC6, 0xB6, 0x54,
+    0xD7, 0xFF, 0x94, 0x5F, 0x50, 0xB5, 0xCC, 0x4E, 0x78, 0x05, 0x5B,
+    0xDD, 0x39, 0x6B, 0x64, 0xF7, 0x8D, 0xA2, 0xC5, 0xF9, 0x62, 0x00,
+    0xCC, 0xD4, 0x15, 0xCD, 0x08, 0xFE, 0x42, 0x00, 0x38,
+};
+
+// U2F responses ---------------------------------------------------------------
+
+// EC public key encoded in COSE_Key format extracted from above
+// |kTestU2fRegisterResponse|.
+constexpr uint8_t kTestECPublicKeyCOSE[] = {
+    // Map(5)
+    0xA5,
+    // kty: EC key type
+    0x01, 0x02,
+    // alg: EC256 signature algorithm
+    0x03, 0x26,
+    // crv: P-256 curve
+    0x20, 0x01,
+    // x-coordinate
+    0x21,
+    // Bytes(32)
+    0x58, 0x20,
+    // Byte array content
+    0xE8, 0x76, 0x25, 0x89, 0x6E, 0xE4, 0xE4, 0x6D, 0xC0, 0x32, 0x76, 0x6E,
+    0x80, 0x87, 0x96, 0x2F, 0x36, 0xDF, 0x9D, 0xFE, 0x8B, 0x56, 0x7F, 0x37,
+    0x63, 0x01, 0x5B, 0x19, 0x90, 0xA6, 0x0E, 0x14,
+    // y-coordinate
+    0x22,
+    // Bytes(32)
+    0x58, 0x20,
+    // Byte array content
+    0x27, 0xDE, 0x61, 0x2D, 0x66, 0x41, 0x8B, 0xDA, 0x19, 0x50, 0x58, 0x1E,
+    0xBC, 0x5C, 0x8C, 0x1D, 0xAD, 0x71, 0x0C, 0xB1, 0x4C, 0x22, 0xF8, 0xC9,
+    0x70, 0x45, 0xF4, 0x61, 0x2F, 0xB2, 0x0C, 0x91,
+};
+
+// The attestation statement extracted from above |kTestU2fRegisterResponse|, a
+// CBOR-encoded byte array.
+// Diagnostic notation:
+// {"sig":
+// h'30450220324779C68F3380288A1197B6095F7A6EB9B1B1C127F66AE12A99FE8532EC23B9
+//   022100E39516AC4D61EE64044D50B415A6A4D4D84BA6D895CB5AB7A1AA7D081DE341FA',
+// "x5c": [h'3082024A30820132A0030201020204046C8822300D06092A864886F70D01010B0 \
+// 500302E312C302A0603550403132359756269636F2055324620526F6F742043412053657269 \
+// 616C203435373230303633313020170D3134303830313030303030305A180F3230353030393 \
+// 0343030303030305A302C312A302806035504030C2159756269636F20553246204545205365 \
+// 7269616C203234393138323332343737303059301306072A8648CE3D020106082A8648CE3D0 \
+// 30107034200043CCAB92CCB97287EE8E639437E21FCD6B6F165B2D5A3F3DB131D31C16B742B \
+// B476D8D1E99080EB546C9BBDF556E6210FD42785899E78CC589EBE310F6CDB9FF4A33B30393 \
+// 02206092B0601040182C40A020415312E332E362E312E342E312E34313438322E312E323013 \
+// 060B2B0601040182E51C020101040403020430300D06092A864886F70D01010B05000382010 \
+// 1009F9B052248BC4CF42CC5991FCAABAC9B651BBE5BDCDC8EF0AD2C1C1FFB36D18715D42E78 \
+// B249224F92C7E6E7A05C49F0E7E4C881BF2E94F45E4A21833D7456851D0F6C145A29540C874 \
+// F3092C934B43D222B8962C0F410CEF1DB75892AF116B44A96F5D35ADEA3822FC7146F600438 \
+// 5BCB69B65C99E7EB6919786703C0D8CD41E8F75CCA44AA8AB725AD8E799FF3A8696A6F1B265 \
+// 6E631B1E40183C08FDA53FA4A8F85A05693944AE179A1339D002D15CABD810090EC722EF5DE \
+// F9965A371D415D624B68A2707CAD97BCDD1785AF97E258F33DF56A031AA0356D8E8D5EBCADC \
+// 74E071636C6B110ACE5CC9B90DFEACAE640FF1BB0F1FE5DB4EFF7A95F060733F5']}
+constexpr uint8_t kU2fAttestationStatementCBOR[] = {
+    // Map(2)
+    0xA2,
+    // Text(3)
+    0x63,
+    // "sig"
+    0x73, 0x69, 0x67,
+    // Bytes(71)
+    0x58, 0x47,
+    // Byte array content
+    0x30, 0x45, 0x02, 0x20, 0x32, 0x47, 0x79, 0xC6, 0x8F, 0x33, 0x80, 0x28,
+    0x8A, 0x11, 0x97, 0xB6, 0x09, 0x5F, 0x7A, 0x6E, 0xB9, 0xB1, 0xB1, 0xC1,
+    0x27, 0xF6, 0x6A, 0xE1, 0x2A, 0x99, 0xFE, 0x85, 0x32, 0xEC, 0x23, 0xB9,
+    0x02, 0x21, 0x00, 0xE3, 0x95, 0x16, 0xAC, 0x4D, 0x61, 0xEE, 0x64, 0x04,
+    0x4D, 0x50, 0xB4, 0x15, 0xA6, 0xA4, 0xD4, 0xD8, 0x4B, 0xA6, 0xD8, 0x95,
+    0xCB, 0x5A, 0xB7, 0xA1, 0xAA, 0x7D, 0x08, 0x1D, 0xE3, 0x41, 0xFA,
+    // Text(3)
+    0x63,
+    // "x5c"
+    0x78, 0x35, 0x63,
+    // Array(1)
+    0x81,
+    // Bytes(590)
+    0x59, 0x02, 0x4E,
+    // Byte array content
+    0x30, 0x82, 0x02, 0x4A, 0x30, 0x82, 0x01, 0x32, 0xA0, 0x03, 0x02, 0x01,
+    0x02, 0x02, 0x04, 0x04, 0x6C, 0x88, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+    0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x2E,
+    0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x59,
+    0x75, 0x62, 0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x52, 0x6F,
+    0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C,
+    0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30, 0x36, 0x33, 0x31, 0x30, 0x20,
+    0x17, 0x0D, 0x31, 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30,
+    0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x30, 0x35, 0x30, 0x30, 0x39, 0x30,
+    0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x2C, 0x31, 0x2A,
+    0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x21, 0x59, 0x75, 0x62,
+    0x69, 0x63, 0x6F, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45, 0x45, 0x20, 0x53,
+    0x65, 0x72, 0x69, 0x61, 0x6C, 0x20, 0x32, 0x34, 0x39, 0x31, 0x38, 0x32,
+    0x33, 0x32, 0x34, 0x37, 0x37, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+    0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,
+    0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x3C, 0xCA, 0xB9,
+    0x2C, 0xCB, 0x97, 0x28, 0x7E, 0xE8, 0xE6, 0x39, 0x43, 0x7E, 0x21, 0xFC,
+    0xD6, 0xB6, 0xF1, 0x65, 0xB2, 0xD5, 0xA3, 0xF3, 0xDB, 0x13, 0x1D, 0x31,
+    0xC1, 0x6B, 0x74, 0x2B, 0xB4, 0x76, 0xD8, 0xD1, 0xE9, 0x90, 0x80, 0xEB,
+    0x54, 0x6C, 0x9B, 0xBD, 0xF5, 0x56, 0xE6, 0x21, 0x0F, 0xD4, 0x27, 0x85,
+    0x89, 0x9E, 0x78, 0xCC, 0x58, 0x9E, 0xBE, 0x31, 0x0F, 0x6C, 0xDB, 0x9F,
+    0xF4, 0xA3, 0x3B, 0x30, 0x39, 0x30, 0x22, 0x06, 0x09, 0x2B, 0x06, 0x01,
+    0x04, 0x01, 0x82, 0xC4, 0x0A, 0x02, 0x04, 0x15, 0x31, 0x2E, 0x33, 0x2E,
+    0x36, 0x2E, 0x31, 0x2E, 0x34, 0x2E, 0x31, 0x2E, 0x34, 0x31, 0x34, 0x38,
+    0x32, 0x2E, 0x31, 0x2E, 0x32, 0x30, 0x13, 0x06, 0x0B, 0x2B, 0x06, 0x01,
+    0x04, 0x01, 0x82, 0xE5, 0x1C, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02,
+    0x04, 0x30, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+    0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x9F, 0x9B,
+    0x05, 0x22, 0x48, 0xBC, 0x4C, 0xF4, 0x2C, 0xC5, 0x99, 0x1F, 0xCA, 0xAB,
+    0xAC, 0x9B, 0x65, 0x1B, 0xBE, 0x5B, 0xDC, 0xDC, 0x8E, 0xF0, 0xAD, 0x2C,
+    0x1C, 0x1F, 0xFB, 0x36, 0xD1, 0x87, 0x15, 0xD4, 0x2E, 0x78, 0xB2, 0x49,
+    0x22, 0x4F, 0x92, 0xC7, 0xE6, 0xE7, 0xA0, 0x5C, 0x49, 0xF0, 0xE7, 0xE4,
+    0xC8, 0x81, 0xBF, 0x2E, 0x94, 0xF4, 0x5E, 0x4A, 0x21, 0x83, 0x3D, 0x74,
+    0x56, 0x85, 0x1D, 0x0F, 0x6C, 0x14, 0x5A, 0x29, 0x54, 0x0C, 0x87, 0x4F,
+    0x30, 0x92, 0xC9, 0x34, 0xB4, 0x3D, 0x22, 0x2B, 0x89, 0x62, 0xC0, 0xF4,
+    0x10, 0xCE, 0xF1, 0xDB, 0x75, 0x89, 0x2A, 0xF1, 0x16, 0xB4, 0x4A, 0x96,
+    0xF5, 0xD3, 0x5A, 0xDE, 0xA3, 0x82, 0x2F, 0xC7, 0x14, 0x6F, 0x60, 0x04,
+    0x38, 0x5B, 0xCB, 0x69, 0xB6, 0x5C, 0x99, 0xE7, 0xEB, 0x69, 0x19, 0x78,
+    0x67, 0x03, 0xC0, 0xD8, 0xCD, 0x41, 0xE8, 0xF7, 0x5C, 0xCA, 0x44, 0xAA,
+    0x8A, 0xB7, 0x25, 0xAD, 0x8E, 0x79, 0x9F, 0xF3, 0xA8, 0x69, 0x6A, 0x6F,
+    0x1B, 0x26, 0x56, 0xE6, 0x31, 0xB1, 0xE4, 0x01, 0x83, 0xC0, 0x8F, 0xDA,
+    0x53, 0xFA, 0x4A, 0x8F, 0x85, 0xA0, 0x56, 0x93, 0x94, 0x4A, 0xE1, 0x79,
+    0xA1, 0x33, 0x9D, 0x00, 0x2D, 0x15, 0xCA, 0xBD, 0x81, 0x00, 0x90, 0xEC,
+    0x72, 0x2E, 0xF5, 0xDE, 0xF9, 0x96, 0x5A, 0x37, 0x1D, 0x41, 0x5D, 0x62,
+    0x4B, 0x68, 0xA2, 0x70, 0x7C, 0xAD, 0x97, 0xBC, 0xDD, 0x17, 0x85, 0xAF,
+    0x97, 0xE2, 0x58, 0xF3, 0x3D, 0xF5, 0x6A, 0x03, 0x1A, 0xA0, 0x35, 0x6D,
+    0x8E, 0x8D, 0x5E, 0xBC, 0xAD, 0xC7, 0x4E, 0x07, 0x16, 0x36, 0xC6, 0xB1,
+    0x10, 0xAC, 0xE5, 0xCC, 0x9B, 0x90, 0xDF, 0xEA, 0xCA, 0xE6, 0x40, 0xFF,
+    0x1B, 0xB0, 0xF1, 0xFE, 0x5D, 0xB4, 0xEF, 0xF7, 0xA9, 0x5F, 0x06, 0x07,
+    0x33, 0xF5,
+};
+
+// U2F response blob produced by a U2F sign request used in example 7 of the
+// CTAP spec.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html#using-the-ctap2-authenticatorgetassertion-command-with-ctap1-u2f-authenticators
+constexpr uint8_t kTestU2fSignResponse[] = {
+    // User presence
+    0x01,
+    // Sign count(4 bytes)
+    0x00, 0x00, 0x00, 0x3B,
+    // Signature
+    0x30, 0x44, 0x02, 0x20, 0x7B, 0xDE, 0x0A, 0x52, 0xAC, 0x1F, 0x4C, 0x8B,
+    0x27, 0xE0, 0x03, 0xA3, 0x70, 0xCD, 0x66, 0xA4, 0xC7, 0x11, 0x8D, 0xD2,
+    0x2D, 0x54, 0x47, 0x83, 0x5F, 0x45, 0xB9, 0x9C, 0x68, 0x42, 0x3F, 0xF7,
+    0x02, 0x20, 0x3C, 0x51, 0x7B, 0x47, 0x87, 0x7F, 0x85, 0x78, 0x2D, 0xE1,
+    0x00, 0x86, 0xA7, 0x83, 0xD1, 0xE7, 0xDF, 0x4E, 0x36, 0x39, 0xE7, 0x71,
+    0xF5, 0xF6, 0xAF, 0xA3, 0x5A, 0xAD, 0x53, 0x73, 0x85, 0x8E,
+};
+
+// CTAP requests ---------------------------------------------------------------
+constexpr uint8_t kCtapMakeCredentialRequest[] = {
+    // authenticatorMakeCredential command
+    0x01,
+    // map(5)
+    0xa5,
+    // key(1) - clientDataHash
+    0x01,
+    // bytes(32)
+    0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e,
+    0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05,
+    0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41,
+    // key(2) - rp
+    0x02,
+    // map(2)
+    0xa2,
+    // key - "id"
+    0x62, 0x69, 0x64,
+    // value - "acme.com"
+    0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+    // key -  "name"
+    0x64, 0x6e, 0x61, 0x6d, 0x65,
+    // value - "Acme"
+    0x64, 0x41, 0x63, 0x6d, 0x65,
+    // key(3) - user
+    0x03,
+    // map(4)
+    0xa4,
+    // key - "id"
+    0x62, 0x69, 0x64,
+    // value - user id
+    0x48, 0x10, 0x98, 0x23, 0x72, 0x35, 0x40, 0x98, 0x72,
+    // key - "icon"
+    0x64, 0x69, 0x63, 0x6f, 0x6e,
+    // value - "https://pics.acme.com/00/p/aBjjjpqPb.png"
+    0x78, 0x28, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x70, 0x69,
+    0x63, 0x73, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+    0x30, 0x30, 0x2f, 0x70, 0x2f, 0x61, 0x42, 0x6a, 0x6a, 0x6a, 0x70, 0x71,
+    0x50, 0x62, 0x2e, 0x70, 0x6e, 0x67,
+    // key - "name"
+    0x64, 0x6e, 0x61, 0x6d, 0x65,
+    // value - "johnpsmith@example.com"
+    0x76, 0x6a, 0x6f, 0x68, 0x6e, 0x70, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x40,
+    0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+    // key - "displayName"
+    0x6b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65,
+    // value - "John P. Smith"
+    0x6d, 0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x50, 0x2e, 0x20, 0x53, 0x6d, 0x69,
+    0x74, 0x68,
+    // key(4) - pubKeyCredParams
+    0x04,
+    // array(2)
+    0x82,
+    // map(2)
+    0xa2,
+    // key - "alg"
+    0x63, 0x61, 0x6c, 0x67,
+    // value - 7
+    0x07,
+    // key - "type"
+    0x64, 0x74, 0x79, 0x70, 0x65,
+    // value - "public-key"
+    0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+    // map(2)
+    0xa2,
+    // key - "alg"
+    0x63, 0x61, 0x6c, 0x67,
+    // value - 257
+    0x19, 0x01, 0x01,
+    // key - "type"
+    0x64, 0x74, 0x79, 0x70, 0x65, // "type"
+    // value - "public-key"
+    0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+    // key(7) - options
+    0x07,
+    // map(2)
+    0xa2,
+    // key - "rk"
+    0x62, 0x72, 0x6b,
+    // True(21)
+    0xf5,
+    // key - "uv"
+    0x62, 0x75, 0x76,
+    // True(21)
+    0xf5};
+
+constexpr uint8_t kTestComplexCtapGetAssertionRequest[] = {
+    // authenticatorGetAssertion command
+    0x02,
+    // map(4)
+    0xa4,
+    // key(01) -rpId
+    0x01,
+    // value - "acme.com"
+    0x68, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+    // key(02) - client data hash
+    0x02,
+    // value - bytes(32)
+    0x58, 0x20, 0x68, 0x71, 0x34, 0x96, 0x82, 0x22, 0xec, 0x17, 0x20, 0x2e,
+    0x42, 0x50, 0x5f, 0x8e, 0xd2, 0xb1, 0x6a, 0xe2, 0x2f, 0x16, 0xbb, 0x05,
+    0xb8, 0x8c, 0x25, 0xdb, 0x9e, 0x60, 0x26, 0x45, 0xf1, 0x41,
+    // key(03) - allow list
+    0x03,
+    // value - array(2)
+    0x82,
+    // map(2)
+    0xa2,
+    // key - "id"
+    0x62, 0x69, 0x64,
+    // value - credential ID
+    0x58, 0x40, 0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43,
+    0x94, 0x2f, 0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b,
+    0x3d, 0xf8, 0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0, 0x34,
+    0x85, 0x8a, 0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98, 0x08, 0xd9,
+    0x4f, 0xcb, 0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77, 0xaf, 0x0a, 0xdc,
+    0xc3, 0x58, 0x52, 0xea, 0x6b, 0x9e,
+    // key - "type"
+    0x64, 0x74, 0x79, 0x70, 0x65,
+    // value - "public-key"
+    0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+    // map(2)
+    0xa2,
+    // key - "id"
+    0x62, 0x69, 0x64,
+    // value - credential ID
+    0x58, 0x32, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+    0x03, 0x03, 0x03, 0x03,
+    // key - "type"
+    0x64, 0x74, 0x79, 0x70, 0x65,
+    // value - "public-key"
+    0x6a, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+    // unsigned(5) - options
+    0x05,
+    // map(2)
+    0xa2,
+    // key -"up"
+    0x62, 0x75, 0x70,
+    // value - False(20)
+    0xf4,
+    // key - "uv"
+    0x62, 0x75, 0x76,
+    // value - True(21)
+    0xf5,
+};
+
+// CTAP responses --------------------------------------------------------------
+
+// A sample well formed response to CTAP AuthenticatorGetInfo request. Cross
+// platform device that supports resident key, and user verification.
+constexpr uint8_t kTestAuthenticatorGetInfoResponse[] = {
+    0x00, 0xA6, 0x01, 0x82, 0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F,
+    0x30, 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32, 0x02, 0x82, 0x63, 0x75,
+    0x76, 0x6D, 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72,
+    0x65, 0x74, 0x03, 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15,
+    0x80, 0x06, 0x17, 0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x04, 0xA5, 0x62, 0x72,
+    0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x62, 0x75, 0x76, 0xF5, 0x64, 0x70,
+    0x6C, 0x61, 0x74, 0xF4, 0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50,
+    0x69, 0x6E, 0xF4, 0x05, 0x19, 0x04, 0xB0, 0x06, 0x81, 0x01,
+};
+
+// AuthenticatorGetInfo request with all configurations equal to that of
+// kTestAuthenticatorGetInfoResponse except platform device option is set to
+// true.
+constexpr uint8_t kTestGetInfoResponsePlatformDevice[] = {
+    0x00, 0xA6, 0x01, 0x82, 0x68, 0x46, 0x49, 0x44, 0x4F, 0x5F, 0x32, 0x5F,
+    0x30, 0x66, 0x55, 0x32, 0x46, 0x5F, 0x56, 0x32, 0x02, 0x82, 0x63, 0x75,
+    0x76, 0x6D, 0x6B, 0x68, 0x6D, 0x61, 0x63, 0x2D, 0x73, 0x65, 0x63, 0x72,
+    0x65, 0x74, 0x03, 0x50, 0xF8, 0xA0, 0x11, 0xF3, 0x8C, 0x0A, 0x4D, 0x15,
+    0x80, 0x06, 0x17, 0x11, 0x1F, 0x9E, 0xDC, 0x7D, 0x04, 0xA5, 0x62, 0x72,
+    0x6B, 0xF5, 0x62, 0x75, 0x70, 0xF5, 0x62, 0x75, 0x76, 0xF5, 0x64, 0x70,
+    // platform device : true
+    0x6C, 0x61, 0x74, 0xF5,
+    // End of platform_device setting.
+    0x69, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x50, 0x69, 0x6E, 0xF4, 0x05,
+    0x19, 0x04, 0xB0, 0x06, 0x81, 0x01,
+};
+
+// A Sample well formed response to CTAP MakeCredential request.
+constexpr uint8_t kTestMakeCredentialResponse[] = {
+    // Success status byte
+    0x00,
+    // Map(03)
+    0xa3,
+    // key(01) - Format
+    0x01,
+    // "packed"
+    0x66, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64,
+    // key(02) - Authenticator Data
+    0x02,
+    // Byte(154)
+    0x58, 0x9a, 0x11, 0x94, 0x22, 0x8d, 0xa8, 0xfd, 0xbd, 0xee, 0xfd, 0x26,
+    0x1b, 0xd7, 0xb6, 0x59, 0x5c, 0xfd, 0x70, 0xa5, 0x0d, 0x70, 0xc6, 0x40,
+    0x7b, 0xcf, 0x01, 0x3d, 0xe9, 0x6d, 0x4e, 0xfb, 0x17, 0xde, 0x41, 0x00,
+    0x00, 0x00, 0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80,
+    0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce,
+    0xad, 0x5b, 0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c,
+    0x6f, 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36,
+    0x61, 0x78, 0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38,
+    0xdf, 0xa4, 0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99,
+    0xf5, 0xe6, 0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a,
+    0x61, 0x79, 0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e,
+    0xa3, 0x21, 0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89,
+    0xdd, 0x61, 0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84,
+    // Key(03) - Attestation object
+    0x03,
+    // Map - Attestation object
+    0xa3, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x63, 0x73, 0x69, 0x67, 0x58, 0x47,
+    0x30, 0x45, 0x02, 0x20, 0x13, 0xf7, 0x3c, 0x5d, 0x9d, 0x53, 0x0e, 0x8c,
+    0xc1, 0x5c, 0xc9, 0xbd, 0x96, 0xad, 0x58, 0x6d, 0x39, 0x36, 0x64, 0xe4,
+    0x62, 0xd5, 0xf0, 0x56, 0x12, 0x35, 0xe6, 0x35, 0x0f, 0x2b, 0x72, 0x89,
+    0x02, 0x21, 0x00, 0x90, 0x35, 0x7f, 0xf9, 0x10, 0xcc, 0xb5, 0x6a, 0xc5,
+    0xb5, 0x96, 0x51, 0x19, 0x48, 0x58, 0x1c, 0x8f, 0xdd, 0xb4, 0xa2, 0xb7,
+    0x99, 0x59, 0x94, 0x80, 0x78, 0xb0, 0x9f, 0x4b, 0xdc, 0x62, 0x29, 0x63,
+    0x78, 0x35, 0x63, 0x81, 0x59, 0x01, 0x97, 0x30, 0x82, 0x01, 0x93, 0x30,
+    0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85,
+    0x9b, 0x72, 0x6c, 0xb2, 0x4b, 0x4c, 0x29, 0x30, 0x0a, 0x06, 0x08, 0x2a,
+    0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x47, 0x31, 0x0b, 0x30,
+    0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x14,
+    0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59, 0x75, 0x62,
+    0x69, 0x63, 0x6f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20,
+    0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65,
+    0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x74, 0x74,
+    0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x1e, 0x17, 0x0d,
+    0x31, 0x36, 0x31, 0x32, 0x30, 0x34, 0x31, 0x31, 0x35, 0x35, 0x30, 0x30,
+    0x5a, 0x17, 0x0d, 0x32, 0x36, 0x31, 0x32, 0x30, 0x32, 0x31, 0x31, 0x35,
+    0x35, 0x30, 0x30, 0x5a, 0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+    0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06,
+    0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f,
+    0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55,
+    0x04, 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69,
+    0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74,
+    0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
+    0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+    0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xad, 0x11, 0xeb, 0x0e,
+    0x88, 0x52, 0xe5, 0x3a, 0xd5, 0xdf, 0xed, 0x86, 0xb4, 0x1e, 0x61, 0x34,
+    0xa1, 0x8e, 0xc4, 0xe1, 0xaf, 0x8f, 0x22, 0x1a, 0x3c, 0x7d, 0x6e, 0x63,
+    0x6c, 0x80, 0xea, 0x13, 0xc3, 0xd5, 0x04, 0xff, 0x2e, 0x76, 0x21, 0x1b,
+    0xb4, 0x45, 0x25, 0xb1, 0x96, 0xc4, 0x4c, 0xb4, 0x84, 0x99, 0x79, 0xcf,
+    0x6f, 0x89, 0x6e, 0xcd, 0x2b, 0xb8, 0x60, 0xde, 0x1b, 0xf4, 0x37, 0x6b,
+    0xa3, 0x0d, 0x30, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
+    0x02, 0x30, 0x00, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0xe9,
+    0xa3, 0x9f, 0x1b, 0x03, 0x19, 0x75, 0x25, 0xf7, 0x37, 0x3e, 0x10, 0xce,
+    0x77, 0xe7, 0x80, 0x21, 0x73, 0x1b, 0x94, 0xd0, 0xc0, 0x3f, 0x3f, 0xda,
+    0x1f, 0xd2, 0x2d, 0xb3, 0xd0, 0x30, 0xe7, 0x02, 0x21, 0x00, 0xc4, 0xfa,
+    0xec, 0x34, 0x45, 0xa8, 0x20, 0xcf, 0x43, 0x12, 0x9c, 0xdb, 0x00, 0xaa,
+    0xbe, 0xfd, 0x9a, 0xe2, 0xd8, 0x74, 0xf9, 0xc5, 0xd3, 0x43, 0xcb, 0x2f,
+    0x11, 0x3d, 0xa2, 0x37, 0x23, 0xf3,
+};
+
+// Below |kCtap2MakeCredentialCertificate|, |kCtap2MakeCredentialAuthData|, and
+// |kCtap2MakeCredentialSignature| leverage example 4 of the CTAP spec.
+// https://fidoalliance.org/specs/fido-v2.0-ps-20170927/fido-client-to-authenticator-protocol-v2.0-ps-20170927.html
+constexpr uint8_t kCtap2MakeCredentialCertificate[] = {
+    0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01,
+    0x02, 0x02, 0x09, 0x00, 0x85, 0x9b, 0x72, 0x6c, 0xb2, 0x4b, 0x4c, 0x29,
+    0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
+    0x30, 0x47, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+    0x02, 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a,
+    0x0c, 0x0b, 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x54, 0x65, 0x73,
+    0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x19,
+    0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f,
+    0x72, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f,
+    0x6e, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x32, 0x30, 0x34, 0x31,
+    0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x31, 0x32,
+    0x30, 0x32, 0x31, 0x31, 0x35, 0x35, 0x30, 0x30, 0x5a, 0x30, 0x47, 0x31,
+    0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+    0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x59,
+    0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x22,
+    0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74,
+    0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x41,
+    0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x59,
+    0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
+    0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
+    0x04, 0xad, 0x11, 0xeb, 0x0e, 0x88, 0x52, 0xe5, 0x3a, 0xd5, 0xdf, 0xed,
+    0x86, 0xb4, 0x1e, 0x61, 0x34, 0xa1, 0x8e, 0xc4, 0xe1, 0xaf, 0x8f, 0x22,
+    0x1a, 0x3c, 0x7d, 0x6e, 0x63, 0x6c, 0x80, 0xea, 0x13, 0xc3, 0xd5, 0x04,
+    0xff, 0x2e, 0x76, 0x21, 0x1b, 0xb4, 0x45, 0x25, 0xb1, 0x96, 0xc4, 0x4c,
+    0xb4, 0x84, 0x99, 0x79, 0xcf, 0x6f, 0x89, 0x6e, 0xcd, 0x2b, 0xb8, 0x60,
+    0xde, 0x1b, 0xf4, 0x37, 0x6b, 0xa3, 0x0d, 0x30, 0x0b, 0x30, 0x09, 0x06,
+    0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0a, 0x06, 0x08,
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49, 0x00, 0x30,
+    0x46, 0x02, 0x21, 0x00, 0xe9, 0xa3, 0x9f, 0x1b, 0x03, 0x19, 0x75, 0x25,
+    0xf7, 0x37, 0x3e, 0x10, 0xce, 0x77, 0xe7, 0x80, 0x21, 0x73, 0x1b, 0x94,
+    0xd0, 0xc0, 0x3f, 0x3f, 0xda, 0x1f, 0xd2, 0x2d, 0xb3, 0xd0, 0x30, 0xe7,
+    0x02, 0x21, 0x00, 0xc4, 0xfa, 0xec, 0x34, 0x45, 0xa8, 0x20, 0xcf, 0x43,
+    0x12, 0x9c, 0xdb, 0x00, 0xaa, 0xbe, 0xfd, 0x9a, 0xe2, 0xd8, 0x74, 0xf9,
+    0xc5, 0xd3, 0x43, 0xcb, 0x2f, 0x11, 0x3d, 0xa2, 0x37, 0x23, 0xf3};
+
+constexpr uint8_t kCtap2MakeCredentialAuthData[] = {
+    0x11, 0x94, 0x22, 0x8D, 0xA8, 0xFD, 0xBD, 0xEE, 0xFD, 0x26, 0x1B, 0xD7,
+    0xB6, 0x59, 0x5C, 0xFD, 0x70, 0xA5, 0x0D, 0x70, 0xC6, 0x40, 0x7B, 0xCF,
+    0x01, 0x3D, 0xE9, 0x6D, 0x4E, 0xFB, 0x17, 0xDE, 0x41, 0x00, 0x00, 0x00,
+    0x0b, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17,
+    0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x10, 0x89, 0x59, 0xce, 0xad, 0x5b,
+    0x5c, 0x48, 0x16, 0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f, 0xa3,
+    0x63, 0x61, 0x6c, 0x67, 0x65, 0x45, 0x53, 0x32, 0x35, 0x36, 0x61, 0x78,
+    0x58, 0x20, 0xf7, 0xc4, 0xf4, 0xa6, 0xf1, 0xd7, 0x95, 0x38, 0xdf, 0xa4,
+    0xc9, 0xac, 0x50, 0x84, 0x8d, 0xf7, 0x08, 0xbc, 0x1c, 0x99, 0xf5, 0xe6,
+    0x0e, 0x51, 0xb4, 0x2a, 0x52, 0x1b, 0x35, 0xd3, 0xb6, 0x9a, 0x61, 0x79,
+    0x58, 0x20, 0xde, 0x7b, 0x7d, 0x6c, 0xa5, 0x64, 0xe7, 0x0e, 0xa3, 0x21,
+    0xa4, 0xd5, 0xd9, 0x6e, 0xa0, 0x0e, 0xf0, 0xe2, 0xdb, 0x89, 0xdd, 0x61,
+    0xd4, 0x89, 0x4c, 0x15, 0xac, 0x58, 0x5b, 0xd2, 0x36, 0x84};
+
+constexpr uint8_t kCtap2MakeCredentialSignature[] = {
+    0x30, 0x45, 0x02, 0x20, 0x13, 0xf7, 0x3c, 0x5d, 0x9d, 0x53, 0x0e, 0x8c,
+    0xc1, 0x5c, 0xc9, 0xbd, 0x96, 0xad, 0x58, 0x6d, 0x39, 0x36, 0x64, 0xe4,
+    0x62, 0xd5, 0xf0, 0x56, 0x12, 0x35, 0xe6, 0x35, 0x0f, 0x2b, 0x72, 0x89,
+    0x02, 0x21, 0x00, 0x90, 0x35, 0x7f, 0xf9, 0x10, 0xcc, 0xb5, 0x6a, 0xc5,
+    0xb5, 0x96, 0x51, 0x19, 0x48, 0x58, 0x1c, 0x8f, 0xdd, 0xb4, 0xa2, 0xb7,
+    0x99, 0x59, 0x94, 0x80, 0x78, 0xb0, 0x9f, 0x4b, 0xdc, 0x62, 0x29};
+
+constexpr uint8_t kCtap2MakeCredentialCredentialId[] = {
+    0x89, 0x59, 0xce, 0xad, 0x5b, 0x5c, 0x48, 0x16,
+    0x4e, 0x8a, 0xbc, 0xd6, 0xd9, 0x43, 0x5c, 0x6f,
+};
+
+constexpr uint8_t kCtap2GetAssertionAuthData[] = {
+    0x62, 0x5d, 0xda, 0xdf, 0x74, 0x3f, 0x57, 0x27, 0xe6, 0x6b,
+    0xba, 0x8c, 0x2e, 0x38, 0x79, 0x22, 0xd1, 0xaf, 0x43, 0xc5,
+    0x03, 0xd9, 0x11, 0x4a, 0x8f, 0xba, 0x10, 0x4d, 0x84, 0xd0,
+    0x2b, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x11};
+
+constexpr uint8_t kCtap2GetAssertionSignature[] = {
+    0x30, 0x45, 0x02, 0x20, 0x4a, 0x5a, 0x9d, 0xd3, 0x92, 0x98, 0x14, 0x9d,
+    0x90, 0x47, 0x69, 0xb5, 0x1a, 0x45, 0x14, 0x33, 0x00, 0x6f, 0x18, 0x2a,
+    0x34, 0xfb, 0xdf, 0x66, 0xde, 0x5f, 0xc7, 0x17, 0xd7, 0x5f, 0xb3, 0x50,
+    0x02, 0x21, 0x00, 0xa4, 0x6b, 0x8e, 0xa3, 0xc3, 0xb9, 0x33, 0x82, 0x1c,
+    0x6e, 0x7f, 0x5e, 0xf9, 0xda, 0xae, 0x94, 0xab, 0x47, 0xf1, 0x8d, 0xb4,
+    0x74, 0xc7, 0x47, 0x90, 0xea, 0xab, 0xb1, 0x44, 0x11, 0xe7, 0xa0,
+};
+
+constexpr uint8_t kDeviceGetAssertionResponse[] = {
+    // Success response code
+    0x00,
+    // map(5)
+    0xa5,
+    // unsigned(1) - Credential
+    0x01,
+    // map(2)
+    0xa2,
+    // text(2)
+    0x62,
+    // "id"
+    0x69, 0x64,
+    // bytes(64)
+    0x58, 0x40,
+    // credential id
+    0xf2, 0x20, 0x06, 0xde, 0x4f, 0x90, 0x5a, 0xf6, 0x8a, 0x43, 0x94, 0x2f,
+    0x02, 0x4f, 0x2a, 0x5e, 0xce, 0x60, 0x3d, 0x9c, 0x6d, 0x4b, 0x3d, 0xf8,
+    0xbe, 0x08, 0xed, 0x01, 0xfc, 0x44, 0x26, 0x46, 0xd0, 0x34, 0x85, 0x8a,
+    0xc7, 0x5b, 0xed, 0x3f, 0xd5, 0x80, 0xbf, 0x98, 0x08, 0xd9, 0x4f, 0xcb,
+    0xee, 0x82, 0xb9, 0xb2, 0xef, 0x66, 0x77, 0xaf, 0x0a, 0xdc, 0xc3, 0x58,
+    0x52, 0xea, 0x6b, 0x9e,
+    // text(4)
+    0x64,
+    // "type"
+    0x74, 0x79, 0x70, 0x65,
+    // text(10)
+    0x6a,
+    // "public-key"
+    0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2D, 0x6B, 0x65, 0x79,
+    // unsigned(2) - Auth data
+    0x02,
+    // bytes(37)
+    0x58, 0x25,
+    // auth data
+    0x62, 0x5d, 0xda, 0xdf, 0x74, 0x3f, 0x57, 0x27, 0xe6, 0x6b, 0xba, 0x8c,
+    0x2e, 0x38, 0x79, 0x22, 0xd1, 0xaf, 0x43, 0xc5, 0x03, 0xd9, 0x11, 0x4a,
+    0x8f, 0xba, 0x10, 0x4d, 0x84, 0xd0, 0x2b, 0xfa, 0x01, 0x00, 0x00, 0x00,
+    0x11,
+    // unsigned(3) - signature
+    0x03,
+    // bytes(71)
+    0x58, 0x47,
+    // signature
+    0x30, 0x45, 0x02, 0x20, 0x4a, 0x5a, 0x9d, 0xd3, 0x92, 0x98, 0x14, 0x9d,
+    0x90, 0x47, 0x69, 0xb5, 0x1a, 0x45, 0x14, 0x33, 0x00, 0x6f, 0x18, 0x2a,
+    0x34, 0xfb, 0xdf, 0x66, 0xde, 0x5f, 0xc7, 0x17, 0xd7, 0x5f, 0xb3, 0x50,
+    0x02, 0x21, 0x00, 0xa4, 0x6b, 0x8e, 0xa3, 0xc3, 0xb9, 0x33, 0x82, 0x1c,
+    0x6e, 0x7f, 0x5e, 0xf9, 0xda, 0xae, 0x94, 0xab, 0x47, 0xf1, 0x8d, 0xb4,
+    0x74, 0xc7, 0x47, 0x90, 0xea, 0xab, 0xb1, 0x44, 0x11, 0xe7, 0xa0,
+    // unsigned(4) - publicKeyCredentialUserEntity
+    0x04,
+    // map(4)
+    0xa4,
+    // text(2)
+    0x62,
+    // "id"
+    0x69, 0x64,
+    // bytes(32) - user id
+    0x58, 0x20,
+    // user id
+    0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02, 0x01,
+    0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82, 0x01, 0x38, 0xa0, 0x03, 0x02,
+    0x01, 0x02, 0x30, 0x82, 0x01, 0x93, 0x30, 0x82,
+    // text(4)
+    0x64,
+    // "icon"
+    0x69, 0x63, 0x6f, 0x6e,
+    // text(40)
+    0x78, 0x28,
+    // "https://pics.acme.com/00/p/aBjjjpqPb.png"
+    0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x70, 0x69, 0x63, 0x73,
+    0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x30,
+    0x2f, 0x70, 0x2f, 0x61, 0x42, 0x6a, 0x6a, 0x6a, 0x70, 0x71, 0x50, 0x62,
+    0x2e, 0x70, 0x6e, 0x67,
+    // text(4)
+    0x64,
+    // "name"
+    0x6e, 0x61, 0x6d, 0x65,
+    // text(22)
+    0x76,
+    // "johnpsmith@example.com"
+    0x6a, 0x6f, 0x68, 0x6e, 0x70, 0x73, 0x6d, 0x69, 0x74, 0x68, 0x40, 0x65,
+    0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+    // text(11)
+    0x6b,
+    // "displayName"
+    0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65,
+    // text(13)
+    0x6d,
+    // "John P. Smith"
+    0x4a, 0x6f, 0x68, 0x6e, 0x20, 0x50, 0x2e, 0x20, 0x53, 0x6d, 0x69, 0x74,
+    0x68,
+    // unsigned(5) - number of credentials
+    0x05,
+    // 1
+    0x01,
+};
+
+} // namespace TestData
+
+} // namespace TestWebKitAPI
+
+#endif // ENABLE(WEB_AUTHN)