Title: [254439] trunk
Revision
254439
Author
[email protected]
Date
2020-01-13 10:53:59 -0800 (Mon, 13 Jan 2020)

Log Message

[WebAuthn] Support CTAP Client Pin
https://bugs.webkit.org/show_bug.cgi?id=191516
<rdar://problem/56558558>

Reviewed by Brent Fulgham.

Source/WebCore:

Covered by API tests.

* Modules/webauthn/fido/DeviceRequestConverter.cpp:
(fido::encodeMakeCredenitalRequestAsCBOR):
(fido::encodeGetAssertionRequestAsCBOR):
* Modules/webauthn/fido/Pin.cpp:
(fido::pin::RetriesResponse::parse):
(fido::pin::TokenResponse::parse):
(fido::pin::TokenRequest::tryCreate):
(fido::pin::encodeAsCBOR):
* Modules/webauthn/fido/Pin.h:
* crypto/algorithms/CryptoAlgorithmAES_CBC.h:
* crypto/gcrypt/CryptoAlgorithmAES_CBCGCrypt.cpp:
(WebCore::CryptoAlgorithmAES_CBC::platformEncrypt):
(WebCore::CryptoAlgorithmAES_CBC::platformDecrypt):
* crypto/mac/CryptoAlgorithmAES_CBCMac.cpp:
(WebCore::transformAES_CBC):
(WebCore::CryptoAlgorithmAES_CBC::platformEncrypt):
(WebCore::CryptoAlgorithmAES_CBC::platformDecrypt):
* testing/MockWebAuthenticationConfiguration.h:
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::encode const):
(WebCore::MockWebAuthenticationConfiguration::HidConfiguration::decode):
* testing/MockWebAuthenticationConfiguration.idl:

Source/WebKit:

This patch implements authenticatorClientPIN from the spec:
https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorClientPIN
Specifically, it implements section 5.5.1, 5.5.3, 5.5.4, 5.5.7, and 5.5.8.

Here is the flow how makeCredential/getAssertion works with a PIN in our implementation:
1. Determine if the connected authenticator has a PIN;
2. If yes, send the makeCredential/getAssertion request to the authenticator with an empty pinAuth
such that the authenticator will wink for user gestures. This step intends to confirm the authenticator
is the one the user wants to use. Otherwise, we don't know which authenticator to send the PIN
if multiple are connected;
3. Once the user confirms the authetnicator, it will return either CTAP2_ERR_PIN_INVALID or
CTAP2_ERR_PIN_AUTH_INVALID. Some authenticators return CTAP2_ERR_PIN_AUTH_INVALID even though
it is not suggested by the spec;
4. Get retries from the authenticator;
5. Get key agreement from the authenticator;
6. Ask the UI client for the PIN and at the meantime inform it the retries;
7. Get pin token from the authenticator;
8. Resend the makeCredential/getAssertion request with the desired pinAuth.

Besides implementating the above flow, this patch also fixes some bugs within the PIN commands encoder:
1. pinAuth/pinProtocol are wrongly encoded for makeCredential/getAssertion;
2. AES CBC should be called without any padding. Therefore, CryptoAlgorithmAES_CBC adds a no padding mode;
3. The sharedSecret is the SHA256 digest of the ECDH key agreement instead of the raw key agreement.

* UIProcess/API/APIWebAuthenticationPanelClient.h:
(API::WebAuthenticationPanelClient::requestPin const):
* UIProcess/WebAuthentication/Authenticator.h:
* UIProcess/WebAuthentication/AuthenticatorManager.cpp:
(WebKit::AuthenticatorManager::requestPin):
* UIProcess/WebAuthentication/AuthenticatorManager.h:
* UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h:
* UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm:
(WebKit::WebAuthenticationPanelClient::WebAuthenticationPanelClient):
(WebKit::WebAuthenticationPanelClient::requestPin const):
* UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
(WebKit::MockHidConnection::feedReports):
* UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
(WebKit::CtapAuthenticator::makeCredential):
(WebKit::CtapAuthenticator::continueMakeCredentialAfterResponseReceived):
(WebKit::CtapAuthenticator::getAssertion):
(WebKit::CtapAuthenticator::continueGetAssertionAfterResponseReceived):
(WebKit::CtapAuthenticator::getRetries):
(WebKit::CtapAuthenticator::continueGetKeyAgreementAfterGetRetries):
(WebKit::CtapAuthenticator::continueRequestPinAfterGetKeyAgreement):
(WebKit::CtapAuthenticator::continueGetPinTokenAfterRequestPin):
(WebKit::CtapAuthenticator::continueRequestAfterGetPinToken):
(WebKit::CtapAuthenticator::continueMakeCredentialAfterResponseReceived const): Deleted.
* UIProcess/WebAuthentication/fido/CtapAuthenticator.h:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebCore/CtapPinTest.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebCore/FidoTestData.h:
* TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
(-[TestWebAuthenticationPanelDelegate panel:requestPINWithRemainingRetries:completionHandler:]):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-pin.html: Added.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-key-agreement-error.html: Added.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-pin-token-error.html: Added.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-retries-error.html: Added.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (254438 => 254439)


--- trunk/Source/WebCore/ChangeLog	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/ChangeLog	2020-01-13 18:53:59 UTC (rev 254439)
@@ -1,3 +1,35 @@
+2020-01-06  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Support CTAP Client Pin
+        https://bugs.webkit.org/show_bug.cgi?id=191516
+        <rdar://problem/56558558>
+
+        Reviewed by Brent Fulgham.
+
+        Covered by API tests.
+
+        * Modules/webauthn/fido/DeviceRequestConverter.cpp:
+        (fido::encodeMakeCredenitalRequestAsCBOR):
+        (fido::encodeGetAssertionRequestAsCBOR):
+        * Modules/webauthn/fido/Pin.cpp:
+        (fido::pin::RetriesResponse::parse):
+        (fido::pin::TokenResponse::parse):
+        (fido::pin::TokenRequest::tryCreate):
+        (fido::pin::encodeAsCBOR):
+        * Modules/webauthn/fido/Pin.h:
+        * crypto/algorithms/CryptoAlgorithmAES_CBC.h:
+        * crypto/gcrypt/CryptoAlgorithmAES_CBCGCrypt.cpp:
+        (WebCore::CryptoAlgorithmAES_CBC::platformEncrypt):
+        (WebCore::CryptoAlgorithmAES_CBC::platformDecrypt):
+        * crypto/mac/CryptoAlgorithmAES_CBCMac.cpp:
+        (WebCore::transformAES_CBC):
+        (WebCore::CryptoAlgorithmAES_CBC::platformEncrypt):
+        (WebCore::CryptoAlgorithmAES_CBC::platformDecrypt):
+        * testing/MockWebAuthenticationConfiguration.h:
+        (WebCore::MockWebAuthenticationConfiguration::HidConfiguration::encode const):
+        (WebCore::MockWebAuthenticationConfiguration::HidConfiguration::decode):
+        * testing/MockWebAuthenticationConfiguration.idl:
+
 2020-01-13  Zalan Bujtas  <[email protected]>
 
         [LFC][Integration] Turn off trailing letter-space trimming for the LineBreaker content

Modified: trunk/Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.cpp (254438 => 254439)


--- trunk/Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/Modules/webauthn/fido/DeviceRequestConverter.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -126,8 +126,8 @@
 
     if (pin) {
         ASSERT(pin->protocol >= 0);
-        cborMap[CBORValue(8)] = CBORValue(pin->protocol);
-        cborMap[CBORValue(9)] = CBORValue(WTFMove(pin->auth));
+        cborMap[CBORValue(8)] = CBORValue(WTFMove(pin->auth));
+        cborMap[CBORValue(9)] = CBORValue(pin->protocol);
     }
 
     auto serializedParam = CBORWriter::write(CBORValue(WTFMove(cborMap)));
@@ -172,8 +172,8 @@
 
     if (pin) {
         ASSERT(pin->protocol >= 0);
-        cborMap[CBORValue(8)] = CBORValue(pin->protocol);
-        cborMap[CBORValue(9)] = CBORValue(WTFMove(pin->auth));
+        cborMap[CBORValue(6)] = CBORValue(WTFMove(pin->auth));
+        cborMap[CBORValue(7)] = CBORValue(pin->protocol);
     }
 
     auto serializedParam = CBORWriter::write(CBORValue(WTFMove(cborMap)));

Modified: trunk/Source/WebCore/Modules/webauthn/fido/Pin.cpp (254438 => 254439)


--- trunk/Source/WebCore/Modules/webauthn/fido/Pin.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/Modules/webauthn/fido/Pin.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -131,7 +131,7 @@
         return WTF::nullopt;
 
     RetriesResponse ret;
-    ret.retries = static_cast<int64_t>(it->second.getUnsigned());
+    ret.retries = static_cast<uint64_t>(it->second.getUnsigned());
     return ret;
 }
 
@@ -232,7 +232,7 @@
         return WTF::nullopt;
     const auto& encryptedToken = it->second.getByteString();
 
-    auto tokenResult = CryptoAlgorithmAES_CBC::platformDecrypt({ }, sharedKey, encryptedToken);
+    auto tokenResult = CryptoAlgorithmAES_CBC::platformDecrypt({ }, sharedKey, encryptedToken, CryptoAlgorithmAES_CBC::Padding::No);
     if (tokenResult.hasException())
         return WTF::nullopt;
     auto token = tokenResult.releaseReturnValue();
@@ -272,11 +272,16 @@
     ASSERT(!keyPairResult.hasException());
     auto keyPair = keyPairResult.releaseReturnValue();
 
-    // 2. Use ECDH to compute the shared AES-CBC key.
+    // 2. Use ECDH and SHA-256 to compute the shared AES-CBC key.
     auto sharedKeyResult = CryptoAlgorithmECDH::platformDeriveBits(downcast<CryptoKeyEC>(*keyPair.privateKey), peerKey);
     if (!sharedKeyResult)
         return WTF::nullopt;
-    auto sharedKey = CryptoKeyAES::importRaw(CryptoAlgorithmIdentifier::AES_CBC, WTFMove(*sharedKeyResult), true, CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt);
+
+    auto crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
+    crypto->addBytes(sharedKeyResult->data(), sharedKeyResult->size());
+    auto sharedKeyHash = crypto->computeHash();
+
+    auto sharedKey = CryptoKeyAES::importRaw(CryptoAlgorithmIdentifier::AES_CBC, WTFMove(sharedKeyHash), true, CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt);
     ASSERT(sharedKey);
 
     // The following encodes the public key of the above key pair into COSE format.
@@ -285,7 +290,7 @@
     auto coseKey = encodeCOSEPublicKey(rawPublicKeyResult.returnValue());
 
     // The following calculates a SHA-256 digest of the PIN, and shrink to the left 16 bytes.
-    auto crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
+    crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
     crypto->addBytes(pin.data(), pin.length());
     auto pinHash = crypto->computeHash();
     pinHash.shrink(16);
@@ -307,7 +312,7 @@
 
 Vector<uint8_t> encodeAsCBOR(const TokenRequest& request)
 {
-    auto result = CryptoAlgorithmAES_CBC::platformEncrypt({ }, request.sharedKey(), request.m_pinHash);
+    auto result = CryptoAlgorithmAES_CBC::platformEncrypt({ }, request.sharedKey(), request.m_pinHash, CryptoAlgorithmAES_CBC::Padding::No);
     ASSERT(!result.hasException());
 
     return encodePinCommand(Subcommand::kGetPinToken, [coseKey = WTFMove(request.m_coseKey), encryptedPin = result.releaseReturnValue()] (CBORValue::MapValue* map) mutable {

Modified: trunk/Source/WebCore/Modules/webauthn/fido/Pin.h (254438 => 254439)


--- trunk/Source/WebCore/Modules/webauthn/fido/Pin.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/Modules/webauthn/fido/Pin.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -79,7 +79,7 @@
 
 // kProtocolVersion is the version of the PIN protocol that this code
 // implements.
-constexpr int kProtocolVersion = 1;
+constexpr int64_t kProtocolVersion = 1;
 
 // encodeCOSEPublicKey takes a raw ECDH256 public key and returns it as a COSE structure.
 WEBCORE_EXPORT cbor::CBORValue::MapValue encodeCOSEPublicKey(const Vector<uint8_t>& key);
@@ -110,7 +110,7 @@
 
     // retries is the number of PIN attempts remaining before the authenticator
     // locks.
-    int retries;
+    uint64_t retries;
 
 private:
     RetriesResponse();
@@ -145,7 +145,7 @@
 
     // sharedKey returns the shared ECDH key that was used to encrypt the PIN.
     // This is needed to decrypt the response.
-    const WebCore::CryptoKeyAES& sharedKey() const;
+    WEBCORE_EXPORT const WebCore::CryptoKeyAES& sharedKey() const;
 
     friend Vector<uint8_t> encodeAsCBOR(const TokenRequest&);
 

Modified: trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CBC.h (254438 => 254439)


--- trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CBC.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CBC.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -36,13 +36,18 @@
 
 class CryptoAlgorithmAES_CBC final : public CryptoAlgorithm {
 public:
+    enum class Padding : uint8_t {
+        Yes,
+        No
+    };
+
     static constexpr const char* s_name = "AES-CBC";
     static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::AES_CBC;
     static Ref<CryptoAlgorithm> create();
 
     // Operations can be performed directly.
-    WEBCORE_EXPORT static ExceptionOr<Vector<uint8_t>> platformEncrypt(const CryptoAlgorithmAesCbcCfbParams&, const CryptoKeyAES&, const Vector<uint8_t>&);
-    WEBCORE_EXPORT static ExceptionOr<Vector<uint8_t>> platformDecrypt(const CryptoAlgorithmAesCbcCfbParams&, const CryptoKeyAES&, const Vector<uint8_t>&);
+    WEBCORE_EXPORT static ExceptionOr<Vector<uint8_t>> platformEncrypt(const CryptoAlgorithmAesCbcCfbParams&, const CryptoKeyAES&, const Vector<uint8_t>&, Padding padding = Padding::Yes);
+    WEBCORE_EXPORT static ExceptionOr<Vector<uint8_t>> platformDecrypt(const CryptoAlgorithmAesCbcCfbParams&, const CryptoKeyAES&, const Vector<uint8_t>&, Padding padding = Padding::Yes);
 
 private:
     CryptoAlgorithmAES_CBC() = default;

Modified: trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_CBCGCrypt.cpp (254438 => 254439)


--- trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_CBCGCrypt.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_CBCGCrypt.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -166,7 +166,7 @@
     return output;
 }
 
-ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformEncrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& plainText)
+ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformEncrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& plainText, Padding)
 {
     auto output = gcryptEncrypt(key.key(), parameters.ivVector(), Vector<uint8_t>(plainText));
     if (!output)
@@ -174,7 +174,7 @@
     return WTFMove(*output);
 }
 
-ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformDecrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& cipherText)
+ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformDecrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& cipherText, Padding)
 {
     auto output = gcryptDecrypt(key.key(), parameters.ivVector(), cipherText);
     if (!output)

Modified: trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CBCMac.cpp (254438 => 254439)


--- trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CBCMac.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CBCMac.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -34,10 +34,11 @@
 
 namespace WebCore {
 
-static ExceptionOr<Vector<uint8_t>> transformAES_CBC(CCOperation operation, const Vector<uint8_t>& iv, const Vector<uint8_t>& key, const Vector<uint8_t>& data)
+static ExceptionOr<Vector<uint8_t>> transformAES_CBC(CCOperation operation, const Vector<uint8_t>& iv, const Vector<uint8_t>& key, const Vector<uint8_t>& data, CryptoAlgorithmAES_CBC::Padding padding)
 {
+    CCOptions options = padding == CryptoAlgorithmAES_CBC::Padding::Yes ? kCCOptionPKCS7Padding : 0;
     CCCryptorRef cryptor;
-    CCCryptorStatus status = CCCryptorCreate(operation, kCCAlgorithmAES, kCCOptionPKCS7Padding, key.data(), key.size(), iv.data(), &cryptor);
+    CCCryptorStatus status = CCCryptorCreate(operation, kCCAlgorithmAES, options, key.data(), key.size(), iv.data(), &cryptor);
     if (status)
         return Exception { OperationError };
 
@@ -49,10 +50,12 @@
         return Exception { OperationError };
 
     uint8_t* p = result.data() + bytesWritten;
-    status = CCCryptorFinal(cryptor, p, result.end() - p, &bytesWritten);
-    p += bytesWritten;
-    if (status)
-        return Exception { OperationError };
+    if (padding == CryptoAlgorithmAES_CBC::Padding::Yes) {
+        status = CCCryptorFinal(cryptor, p, result.end() - p, &bytesWritten);
+        p += bytesWritten;
+        if (status)
+            return Exception { OperationError };
+    }
 
     ASSERT(p <= result.end());
     result.shrink(p - result.begin());
@@ -62,16 +65,18 @@
     return WTFMove(result);
 }
 
-ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformEncrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& plainText)
+ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformEncrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& plainText, Padding padding)
 {
     ASSERT(parameters.ivVector().size() == kCCBlockSizeAES128 || parameters.ivVector().isEmpty());
-    return transformAES_CBC(kCCEncrypt, parameters.ivVector(), key.key(), plainText);
+    ASSERT(padding == Padding::Yes || !(plainText.size() % kCCBlockSizeAES128));
+    return transformAES_CBC(kCCEncrypt, parameters.ivVector(), key.key(), plainText, padding);
 }
 
-ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformDecrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& cipherText)
+ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CBC::platformDecrypt(const CryptoAlgorithmAesCbcCfbParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& cipherText, Padding padding)
 {
     ASSERT(parameters.ivVector().size() == kCCBlockSizeAES128 || parameters.ivVector().isEmpty());
-    return transformAES_CBC(kCCDecrypt, parameters.ivVector(), key.key(), cipherText);
+    ASSERT(padding == Padding::Yes || !(cipherText.size() % kCCBlockSizeAES128));
+    return transformAES_CBC(kCCDecrypt, parameters.ivVector(), key.key(), cipherText, padding);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.h (254438 => 254439)


--- trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -84,6 +84,7 @@
         bool continueAfterErrorData { false };
         bool canDowngrade { false };
         bool expectCancel { false };
+        bool supportClientPin { false };
 
         template<class Encoder> void encode(Encoder&) const;
         template<class Decoder> static Optional<HidConfiguration> decode(Decoder&);
@@ -161,7 +162,7 @@
 template<class Encoder>
 void MockWebAuthenticationConfiguration::HidConfiguration::encode(Encoder& encoder) const
 {
-    encoder << payloadBase64 << stage << subStage << error << isU2f << keepAlive << fastDataArrival << continueAfterErrorData << canDowngrade << expectCancel;
+    encoder << payloadBase64 << stage << subStage << error << isU2f << keepAlive << fastDataArrival << continueAfterErrorData << canDowngrade << expectCancel << supportClientPin;
 }
 
 template<class Decoder>
@@ -188,6 +189,8 @@
         return WTF::nullopt;
     if (!decoder.decode(result.expectCancel))
         return WTF::nullopt;
+    if (!decoder.decode(result.supportClientPin))
+        return WTF::nullopt;
     return result;
 }
 

Modified: trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.idl (254438 => 254439)


--- trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.idl	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.idl	2020-01-13 18:53:59 UTC (rev 254439)
@@ -92,6 +92,7 @@
     boolean continueAfterErrorData = false;
     boolean canDowngrade = false;
     boolean expectCancel = false;
+    boolean supportClientPin = false;
 };
 
 [

Modified: trunk/Source/WebKit/ChangeLog (254438 => 254439)


--- trunk/Source/WebKit/ChangeLog	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/ChangeLog	2020-01-13 18:53:59 UTC (rev 254439)
@@ -1,3 +1,60 @@
+2020-01-06  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Support CTAP Client Pin
+        https://bugs.webkit.org/show_bug.cgi?id=191516
+        <rdar://problem/56558558>
+
+        Reviewed by Brent Fulgham.
+
+        This patch implements authenticatorClientPIN from the spec:
+        https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#authenticatorClientPIN
+        Specifically, it implements section 5.5.1, 5.5.3, 5.5.4, 5.5.7, and 5.5.8.
+
+        Here is the flow how makeCredential/getAssertion works with a PIN in our implementation:
+        1. Determine if the connected authenticator has a PIN;
+        2. If yes, send the makeCredential/getAssertion request to the authenticator with an empty pinAuth
+        such that the authenticator will wink for user gestures. This step intends to confirm the authenticator
+        is the one the user wants to use. Otherwise, we don't know which authenticator to send the PIN
+        if multiple are connected;
+        3. Once the user confirms the authetnicator, it will return either CTAP2_ERR_PIN_INVALID or
+        CTAP2_ERR_PIN_AUTH_INVALID. Some authenticators return CTAP2_ERR_PIN_AUTH_INVALID even though
+        it is not suggested by the spec;
+        4. Get retries from the authenticator;
+        5. Get key agreement from the authenticator;
+        6. Ask the UI client for the PIN and at the meantime inform it the retries;
+        7. Get pin token from the authenticator;
+        8. Resend the makeCredential/getAssertion request with the desired pinAuth.
+
+        Besides implementating the above flow, this patch also fixes some bugs within the PIN commands encoder:
+        1. pinAuth/pinProtocol are wrongly encoded for makeCredential/getAssertion;
+        2. AES CBC should be called without any padding. Therefore, CryptoAlgorithmAES_CBC adds a no padding mode;
+        3. The sharedSecret is the SHA256 digest of the ECDH key agreement instead of the raw key agreement.
+
+        * UIProcess/API/APIWebAuthenticationPanelClient.h:
+        (API::WebAuthenticationPanelClient::requestPin const):
+        * UIProcess/WebAuthentication/Authenticator.h:
+        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
+        (WebKit::AuthenticatorManager::requestPin):
+        * UIProcess/WebAuthentication/AuthenticatorManager.h:
+        * UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h:
+        * UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm:
+        (WebKit::WebAuthenticationPanelClient::WebAuthenticationPanelClient):
+        (WebKit::WebAuthenticationPanelClient::requestPin const):
+        * UIProcess/WebAuthentication/Mock/MockHidConnection.cpp:
+        (WebKit::MockHidConnection::feedReports):
+        * UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp:
+        (WebKit::CtapAuthenticator::makeCredential):
+        (WebKit::CtapAuthenticator::continueMakeCredentialAfterResponseReceived):
+        (WebKit::CtapAuthenticator::getAssertion):
+        (WebKit::CtapAuthenticator::continueGetAssertionAfterResponseReceived):
+        (WebKit::CtapAuthenticator::getRetries):
+        (WebKit::CtapAuthenticator::continueGetKeyAgreementAfterGetRetries):
+        (WebKit::CtapAuthenticator::continueRequestPinAfterGetKeyAgreement):
+        (WebKit::CtapAuthenticator::continueGetPinTokenAfterRequestPin):
+        (WebKit::CtapAuthenticator::continueRequestAfterGetPinToken):
+        (WebKit::CtapAuthenticator::continueMakeCredentialAfterResponseReceived const): Deleted.
+        * UIProcess/WebAuthentication/fido/CtapAuthenticator.h:
+
 2020-01-13  Brent Fulgham  <[email protected]>
 
         [iOS] Remove 'com.apple.diagnosticd' from WebContent process sandbox

Modified: trunk/Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -30,6 +30,7 @@
 #include <wtf/CompletionHandler.h>
 #include <wtf/HashSet.h>
 #include <wtf/RefCounted.h>
+#include <wtf/text/WTFString.h>
 
 namespace WebCore {
 class AuthenticatorAssertionResponse;
@@ -50,6 +51,7 @@
     virtual void updatePanel(WebKit::WebAuthenticationStatus) const { }
     virtual void dismissPanel(WebKit::WebAuthenticationResult) const { }
     virtual void selectAssertionResponses(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>& responses, CompletionHandler<void(const Ref<WebCore::AuthenticatorAssertionResponse>&)>&& completionHandler) const { completionHandler(*responses.begin()); }
+    virtual void requestPin(uint64_t, CompletionHandler<void(const WTF::String&)>&& completionHandler) const { completionHandler(emptyString()); }
 };
 
 } // namespace API

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -52,6 +52,7 @@
         virtual void downgrade(Authenticator* id, Ref<Authenticator>&& downgradedAuthenticator) = 0;
         virtual void authenticatorStatusUpdated(WebAuthenticationStatus) = 0;
         virtual void selectAssertionResponses(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const Ref<WebCore::AuthenticatorAssertionResponse>&)>&&) = 0;
+        virtual void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) = 0;
     };
 
     virtual ~Authenticator() = default;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -279,6 +279,12 @@
         panel->client().selectAssertionResponses(responses, WTFMove(completionHandler));
 }
 
+void AuthenticatorManager::requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&& completionHandler)
+{
+    if (auto* panel = m_pendingRequestData.panel.get())
+        panel->client().requestPin(retries, WTFMove(completionHandler));
+}
+
 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
 {
     return AuthenticatorTransportService::create(transport, observer);

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -82,6 +82,7 @@
     void downgrade(Authenticator* id, Ref<Authenticator>&& downgradedAuthenticator) final;
     void authenticatorStatusUpdated(WebAuthenticationStatus) final;
     void selectAssertionResponses(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const Ref<WebCore::AuthenticatorAssertionResponse>&)>&&) final;
+    void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) final;
 
     // Overriden by MockAuthenticatorManager.
     virtual UniqueRef<AuthenticatorTransportService> createService(WebCore::AuthenticatorTransport, AuthenticatorTransportService::Observer&) const;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -49,6 +49,7 @@
     // API::WebAuthenticationPanelClient
     void updatePanel(WebAuthenticationStatus) const final;
     void dismissPanel(WebAuthenticationResult) const final;
+    void requestPin(uint64_t, CompletionHandler<void(const WTF::String&)>&&) const final;
 
     _WKWebAuthenticationPanel *m_panel;
     WeakObjCPtr<id <_WKWebAuthenticationPanelDelegate> > m_delegate;
@@ -56,6 +57,7 @@
     struct {
         bool panelUpdateWebAuthenticationPanel : 1;
         bool panelDismissWebAuthenticationPanelWithResult : 1;
+        bool panelRequestPinWithRemainingRetriesCompletionHandler : 1;
     } m_delegateMethods;
 };
 

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm	2020-01-13 18:53:59 UTC (rev 254439)
@@ -28,8 +28,10 @@
 
 #if ENABLE(WEB_AUTHN)
 
+#import "CompletionHandlerCallChecker.h"
 #import "WebAuthenticationFlags.h"
 #import "_WKWebAuthenticationPanel.h"
+#import <wtf/BlockPtr.h>
 #import <wtf/RunLoop.h>
 
 namespace WebKit {
@@ -40,6 +42,7 @@
 {
     m_delegateMethods.panelUpdateWebAuthenticationPanel = [delegate respondsToSelector:@selector(panel:updateWebAuthenticationPanel:)];
     m_delegateMethods.panelDismissWebAuthenticationPanelWithResult = [delegate respondsToSelector:@selector(panel:dismissWebAuthenticationPanelWithResult:)];
+    m_delegateMethods.panelRequestPinWithRemainingRetriesCompletionHandler = [delegate respondsToSelector:@selector(panel:requestPINWithRemainingRetries:completionHandler:)];
 }
 
 RetainPtr<id <_WKWebAuthenticationPanelDelegate> > WebAuthenticationPanelClient::delegate()
@@ -107,6 +110,37 @@
     });
 }
 
+void WebAuthenticationPanelClient::requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&& completionHandler) const
+{
+    // Call delegates in the next run loop to prevent clients' reentrance that would potentially modify the state
+    // of the current run loop in unexpected ways.
+    RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), this, retries, completionHandler = WTFMove(completionHandler)] () mutable {
+        if (!weakThis) {
+            completionHandler(emptyString());
+            return;
+        }
+
+        if (!m_delegateMethods.panelRequestPinWithRemainingRetriesCompletionHandler) {
+            completionHandler(emptyString());
+            return;
+        }
+
+        auto delegate = m_delegate.get();
+        if (!delegate) {
+            completionHandler(emptyString());
+            return;
+        }
+
+        auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(panel:requestPINWithRemainingRetries:completionHandler:));
+        [delegate panel:m_panel requestPINWithRemainingRetries:retries completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](NSString *pin) mutable {
+            if (checker->completionHandlerHasBeenCalled())
+                return;
+            checker->didCallCompletionHandler();
+            completionHandler(pin);
+        }).get()];
+    });
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockHidConnection.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -31,6 +31,7 @@
 #include <WebCore/AuthenticatorGetInfoResponse.h>
 #include <WebCore/CBORReader.h>
 #include <WebCore/FidoConstants.h>
+#include <WebCore/Pin.h>
 #include <WebCore/WebAuthenticationConstants.h>
 #include <wtf/BlockPtr.h>
 #include <wtf/CryptographicallyRandomNumber.h>
@@ -223,10 +224,18 @@
 
     Optional<FidoHidMessage> message;
     if (m_stage == Mock::HidStage::Info && m_subStage == Mock::HidSubStage::Msg) {
+        // FIXME(205839):
         Vector<uint8_t> infoData;
         if (m_configuration.hid->canDowngrade)
             infoData = encodeAsCBOR(AuthenticatorGetInfoResponse({ ProtocolVersion::kCtap, ProtocolVersion::kU2f }, Vector<uint8_t>(aaguidLength, 0u)));
-        else
+        else if (m_configuration.hid->supportClientPin) {
+            AuthenticatorGetInfoResponse infoResponse({ ProtocolVersion::kCtap }, Vector<uint8_t>(aaguidLength, 0u));
+            infoResponse.setPinProtocols({ pin::kProtocolVersion });
+            AuthenticatorSupportedOptions options;
+            options.setClientPinAvailability(AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet);
+            infoResponse.setOptions(WTFMove(options));
+            infoData = encodeAsCBOR(infoResponse);
+        } else
             infoData = encodeAsCBOR(AuthenticatorGetInfoResponse({ ProtocolVersion::kCtap }, Vector<uint8_t>(aaguidLength, 0u)));
         infoData.insert(0, static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)); // Prepend status code.
         if (stagesMatch() && m_configuration.hid->error == Mock::HidError::WrongChannelId)

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -31,9 +31,13 @@
 #include "CtapDriver.h"
 #include "CtapHidDriver.h"
 #include "U2fAuthenticator.h"
+#include <WebCore/CryptoKeyAES.h>
+#include <WebCore/CryptoKeyEC.h>
+#include <WebCore/CryptoKeyHMAC.h>
 #include <WebCore/DeviceRequestConverter.h>
 #include <WebCore/DeviceResponseConverter.h>
 #include <WebCore/ExceptionData.h>
+#include <WebCore/Pin.h>
 #include <WebCore/U2fCommandConstructor.h>
 #include <wtf/RunLoop.h>
 #include <wtf/text/StringConcatenateNumbers.h>
@@ -53,7 +57,11 @@
     ASSERT(!m_isDowngraded);
     if (processGoogleLegacyAppIdSupportExtension())
         return;
-    auto cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialCreationOptions>(requestData().options), m_info.options().userVerificationAvailability());
+    Vector<uint8_t> cborCmd;
+    if (m_info.options().clientPinAvailability() == AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet)
+        cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialCreationOptions>(requestData().options), m_info.options().userVerificationAvailability(), PinParameters { pin::kProtocolVersion, m_pinAuth });
+    else
+        cborCmd = encodeMakeCredenitalRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialCreationOptions>(requestData().options), m_info.options().userVerificationAvailability());
     driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
         ASSERT(RunLoop::isMain());
         if (!weakThis)
@@ -62,7 +70,7 @@
     });
 }
 
-void CtapAuthenticator::continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&& data) const
+void CtapAuthenticator::continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&& data)
 {
     auto response = readCTAPMakeCredentialResponse(data, WTF::get<PublicKeyCredentialCreationOptions>(requestData().options).attestation);
     if (!response) {
@@ -69,6 +77,9 @@
         auto error = getResponseCode(data);
         if (error == CtapDeviceResponseCode::kCtap2ErrCredentialExcluded)
             receiveRespond(ExceptionData { InvalidStateError, "At least one credential matches an entry of the excludeCredentials list in the authenticator."_s });
+        // FIXME(205837)
+        else if (error == CtapDeviceResponseCode::kCtap2ErrPinInvalid || error == CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid)
+            getRetries();
         else
             receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<uint8_t>(error)) });
         return;
@@ -79,7 +90,11 @@
 void CtapAuthenticator::getAssertion()
 {
     ASSERT(!m_isDowngraded);
-    auto cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialRequestOptions>(requestData().options), m_info.options().userVerificationAvailability());
+    Vector<uint8_t> cborCmd;
+    if (m_info.options().clientPinAvailability() == AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet)
+        cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialRequestOptions>(requestData().options), m_info.options().userVerificationAvailability(), PinParameters { pin::kProtocolVersion, m_pinAuth });
+    else
+        cborCmd = encodeGetAssertionRequestAsCBOR(requestData().hash, WTF::get<PublicKeyCredentialRequestOptions>(requestData().options), m_info.options().userVerificationAvailability());
     driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
         ASSERT(RunLoop::isMain());
         if (!weakThis)
@@ -93,6 +108,11 @@
     auto response = readCTAPGetAssertionResponse(data);
     if (!response) {
         auto error = getResponseCode(data);
+        // FIXME(205837)
+        if (error == CtapDeviceResponseCode::kCtap2ErrPinInvalid || error == CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid) {
+            getRetries();
+            return;
+        }
         if (error != CtapDeviceResponseCode::kCtap2ErrInvalidCBOR && tryDowngrade())
             return;
         if (error == CtapDeviceResponseCode::kCtap2ErrNoCredentials && observer())
@@ -152,6 +172,93 @@
     });
 }
 
+void CtapAuthenticator::getRetries()
+{
+    auto cborCmd = encodeAsCBOR(pin::RetriesRequest { });
+    driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this)](Vector<uint8_t>&& data) {
+        ASSERT(RunLoop::isMain());
+        if (!weakThis)
+            return;
+        weakThis->continueGetKeyAgreementAfterGetRetries(WTFMove(data));
+    });
+}
+
+void CtapAuthenticator::continueGetKeyAgreementAfterGetRetries(Vector<uint8_t>&& data)
+{
+    auto retries = pin::RetriesResponse::parse(data);
+    if (!retries) {
+        auto error = getResponseCode(data);
+        receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<uint8_t>(error)) });
+        return;
+    }
+
+    auto cborCmd = encodeAsCBOR(pin::KeyAgreementRequest { });
+    driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this), retries = retries->retries] (Vector<uint8_t>&& data) {
+        ASSERT(RunLoop::isMain());
+        if (!weakThis)
+            return;
+        weakThis->continueRequestPinAfterGetKeyAgreement(WTFMove(data), retries);
+    });
+}
+
+void CtapAuthenticator::continueRequestPinAfterGetKeyAgreement(Vector<uint8_t>&& data, uint64_t retries)
+{
+    auto keyAgreement = pin::KeyAgreementResponse::parse(data);
+    if (!keyAgreement) {
+        auto error = getResponseCode(data);
+        receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<uint8_t>(error)) });
+        return;
+    }
+
+    if (auto* observer = this->observer()) {
+        observer->requestPin(retries, [weakThis = makeWeakPtr(*this), keyAgreement = WTFMove(*keyAgreement)] (const String& pin) {
+            ASSERT(RunLoop::isMain());
+            if (!weakThis)
+                return;
+            weakThis->continueGetPinTokenAfterRequestPin(pin, keyAgreement.peerKey);
+        });
+    }
+}
+
+void CtapAuthenticator::continueGetPinTokenAfterRequestPin(const String& pin, const CryptoKeyEC& peerKey)
+{
+    auto pinUtf8 = pin::validateAndConvertToUTF8(pin);
+    if (!pinUtf8) {
+        receiveRespond(ExceptionData { UnknownError, makeString("Pin is not valid: ", pin) });
+        return;
+    }
+    auto tokenRequest = pin::TokenRequest::tryCreate(*pinUtf8, peerKey);
+    if (!tokenRequest) {
+        receiveRespond(ExceptionData { UnknownError, "Cannot create a TokenRequest."_s });
+        return;
+    }
+
+    auto cborCmd = encodeAsCBOR(*tokenRequest);
+    driver().transact(WTFMove(cborCmd), [weakThis = makeWeakPtr(*this), tokenRequest = WTFMove(*tokenRequest)] (Vector<uint8_t>&& data) {
+        ASSERT(RunLoop::isMain());
+        if (!weakThis)
+            return;
+        weakThis->continueRequestAfterGetPinToken(WTFMove(data), tokenRequest);
+    });
+}
+
+void CtapAuthenticator::continueRequestAfterGetPinToken(Vector<uint8_t>&& data, const fido::pin::TokenRequest& tokenRequest)
+{
+    auto token = pin::TokenResponse::parse(tokenRequest.sharedKey(), data);
+    if (!token) {
+        auto error = getResponseCode(data);
+        receiveRespond(ExceptionData { UnknownError, makeString("Unknown internal error. Error code: ", static_cast<uint8_t>(error)) });
+        return;
+    }
+
+    m_pinAuth = token->pinAuth(requestData().hash);
+    WTF::switchOn(requestData().options, [&](const PublicKeyCredentialCreationOptions& options) {
+        makeCredential();
+    }, [&](const PublicKeyCredentialRequestOptions& options) {
+        getAssertion();
+    });
+}
+
 bool CtapAuthenticator::tryDowngrade()
 {
     if (m_info.versions().find(ProtocolVersion::kU2f) == m_info.versions().end())

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.h (254438 => 254439)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapAuthenticator.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -30,6 +30,16 @@
 #include "FidoAuthenticator.h"
 #include <WebCore/AuthenticatorGetInfoResponse.h>
 
+namespace fido {
+namespace pin {
+class TokenRequest;
+}
+}
+
+namespace WebCore {
+class CryptoKeyEC;
+}
+
 namespace WebKit {
 
 class CtapDriver;
@@ -45,11 +55,17 @@
     explicit CtapAuthenticator(std::unique_ptr<CtapDriver>&&, fido::AuthenticatorGetInfoResponse&&);
 
     void makeCredential() final;
-    void continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&&) const;
+    void continueMakeCredentialAfterResponseReceived(Vector<uint8_t>&&);
     void getAssertion() final;
     void continueGetAssertionAfterResponseReceived(Vector<uint8_t>&&);
     void continueGetNextAssertionAfterResponseReceived(Vector<uint8_t>&&);
 
+    void getRetries();
+    void continueGetKeyAgreementAfterGetRetries(Vector<uint8_t>&&);
+    void continueRequestPinAfterGetKeyAgreement(Vector<uint8_t>&&, uint64_t retries);
+    void continueGetPinTokenAfterRequestPin(const String& pin, const CryptoKeyEC&);
+    void continueRequestAfterGetPinToken(Vector<uint8_t>&&, const fido::pin::TokenRequest&);
+
     bool tryDowngrade();
     bool processGoogleLegacyAppIdSupportExtension();
 
@@ -57,6 +73,7 @@
     bool m_isDowngraded { false };
     size_t m_remainingAssertionResponses { 0 };
     HashSet<Ref<WebCore::AuthenticatorAssertionResponse>> m_assertionResponses;
+    Vector<uint8_t> m_pinAuth;
 };
 
 } // namespace WebKit

Modified: trunk/Tools/ChangeLog (254438 => 254439)


--- trunk/Tools/ChangeLog	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Tools/ChangeLog	2020-01-13 18:53:59 UTC (rev 254439)
@@ -1,3 +1,24 @@
+2020-01-06  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Support CTAP Client Pin
+        https://bugs.webkit.org/show_bug.cgi?id=191516
+        <rdar://problem/56558558>
+
+        Reviewed by Brent Fulgham.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebCore/CtapPinTest.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebCore/FidoTestData.h:
+        * TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
+        (-[TestWebAuthenticationPanelDelegate panel:requestPINWithRemainingRetries:completionHandler:]):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-pin.html: Added.
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-key-agreement-error.html: Added.
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-pin-token-error.html: Added.
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-retries-error.html: Added.
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin.html: Added.
+
 2020-01-11  Alex Christensen  <[email protected]>
 
         Expose frame information on _WKResourceLoadInfo

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (254438 => 254439)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2020-01-13 18:53:59 UTC (rev 254439)
@@ -327,6 +327,10 @@
 		55A817FF2181021A0004A39A /* 100x100-red.tga in Copy Resources */ = {isa = PBXBuildFile; fileRef = 55A817FE218101DF0004A39A /* 100x100-red.tga */; };
 		55A81800218102210004A39A /* 400x400-green.png in Copy Resources */ = {isa = PBXBuildFile; fileRef = 55A817FD218101DF0004A39A /* 400x400-green.png */; };
 		55F9D2E52205031800A9AB38 /* AdditionalSupportedImageTypes.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55F9D2E42205031800A9AB38 /* AdditionalSupportedImageTypes.mm */; };
+		570D26F423C3CA6A00D5CF67 /* web-authentication-make-credential-hid-pin-get-key-agreement-error.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 570D26F323C3CA5500D5CF67 /* web-authentication-make-credential-hid-pin-get-key-agreement-error.html */; };
+		570D26F623C3D33000D5CF67 /* web-authentication-make-credential-hid-pin.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 570D26F523C3D32700D5CF67 /* web-authentication-make-credential-hid-pin.html */; };
+		570D26FA23C3F25100D5CF67 /* web-authentication-make-credential-hid-pin-get-pin-token-error.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 570D26F923C3F24500D5CF67 /* web-authentication-make-credential-hid-pin-get-pin-token-error.html */; };
+		570D26FC23C3F87000D5CF67 /* web-authentication-get-assertion-hid-pin.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 570D26FB23C3F86500D5CF67 /* web-authentication-get-assertion-hid-pin.html */; };
 		5714ECB91CA8B5B000051AC8 /* DownloadRequestOriginalURL.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECB81CA8B58800051AC8 /* DownloadRequestOriginalURL.html */; };
 		5714ECBB1CA8BFE400051AC8 /* DownloadRequestOriginalURLFrame.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */; };
 		5714ECBD1CA8C22A00051AC8 /* DownloadRequestOriginalURL2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5714ECBC1CA8C21800051AC8 /* DownloadRequestOriginalURL2.html */; };
@@ -343,6 +347,7 @@
 		573255A722139BC700396AE8 /* load-web-archive-2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 573255A322139B9000396AE8 /* load-web-archive-2.html */; };
 		574F55D2204D47F0002948C6 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 574F55D0204D471C002948C6 /* Security.framework */; };
 		5758597F23A2527A00C74572 /* CtapPinTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5758597E23A2527A00C74572 /* CtapPinTest.cpp */; };
+		5758598423C3C3A400C74572 /* web-authentication-make-credential-hid-pin-get-retries-error.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5758598323C3C36200C74572 /* web-authentication-make-credential-hid-pin-get-retries-error.html */; };
 		57599E211F07191900A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm in Sources */ = {isa = PBXBuildFile; fileRef = 57599E201F07191700A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm */; };
 		57599E271F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3 in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E241F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3 */; };
 		57599E281F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E261F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm */; };
@@ -1480,11 +1485,16 @@
 				CD577799211CE0E4001B371E /* web-audio-only.html in Copy Resources */,
 				57663DF32357E48900E85E09 /* web-authentication-get-assertion-hid-cancel.html in Copy Resources */,
 				577454D02359B378008E1ED7 /* web-authentication-get-assertion-hid-no-credentials.html in Copy Resources */,
+				570D26FC23C3F87000D5CF67 /* web-authentication-get-assertion-hid-pin.html in Copy Resources */,
 				57663DEC234F1F9300E85E09 /* web-authentication-get-assertion-hid.html in Copy Resources */,
 				579833922368FA37008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html in Copy Resources */,
 				57663DEA234EA66D00E85E09 /* web-authentication-get-assertion-nfc.html in Copy Resources */,
 				577454D22359BB01008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html in Copy Resources */,
 				57C624502346C21E00383FE7 /* web-authentication-get-assertion.html in Copy Resources */,
+				570D26F423C3CA6A00D5CF67 /* web-authentication-make-credential-hid-pin-get-key-agreement-error.html in Copy Resources */,
+				570D26FA23C3F25100D5CF67 /* web-authentication-make-credential-hid-pin-get-pin-token-error.html in Copy Resources */,
+				5758598423C3C3A400C74572 /* web-authentication-make-credential-hid-pin-get-retries-error.html in Copy Resources */,
+				570D26F623C3D33000D5CF67 /* web-authentication-make-credential-hid-pin.html in Copy Resources */,
 				5798337E236019A4008E5547 /* web-authentication-make-credential-hid.html in Copy Resources */,
 				1C2B81861C89259D00A5529F /* webfont.html in Copy Resources */,
 				51714EB41CF8C78C004723C4 /* WebProcessKillIDBCleanup-1.html in Copy Resources */,
@@ -1893,6 +1903,10 @@
 		55A817FD218101DF0004A39A /* 400x400-green.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "400x400-green.png"; sourceTree = "<group>"; };
 		55A817FE218101DF0004A39A /* 100x100-red.tga */ = {isa = PBXFileReference; lastKnownFileType = file; path = "100x100-red.tga"; sourceTree = "<group>"; };
 		55F9D2E42205031800A9AB38 /* AdditionalSupportedImageTypes.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = AdditionalSupportedImageTypes.mm; sourceTree = "<group>"; };
+		570D26F323C3CA5500D5CF67 /* web-authentication-make-credential-hid-pin-get-key-agreement-error.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-hid-pin-get-key-agreement-error.html"; sourceTree = "<group>"; };
+		570D26F523C3D32700D5CF67 /* web-authentication-make-credential-hid-pin.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-hid-pin.html"; sourceTree = "<group>"; };
+		570D26F923C3F24500D5CF67 /* web-authentication-make-credential-hid-pin-get-pin-token-error.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-hid-pin-get-pin-token-error.html"; sourceTree = "<group>"; };
+		570D26FB23C3F86500D5CF67 /* web-authentication-get-assertion-hid-pin.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-get-assertion-hid-pin.html"; sourceTree = "<group>"; };
 		5714ECB81CA8B58800051AC8 /* DownloadRequestOriginalURL.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = DownloadRequestOriginalURL.html; sourceTree = "<group>"; };
 		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>"; };
@@ -1911,6 +1925,7 @@
 		574F55D0204D471C002948C6 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
 		5758597D23A2527A00C74572 /* CtapPinTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CtapPinTest.h; sourceTree = "<group>"; };
 		5758597E23A2527A00C74572 /* CtapPinTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CtapPinTest.cpp; sourceTree = "<group>"; };
+		5758598323C3C36200C74572 /* web-authentication-make-credential-hid-pin-get-retries-error.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-hid-pin-get-retries-error.html"; sourceTree = "<group>"; };
 		57599E201F07191700A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = IndexedDBStructuredCloneBackwardCompatibility.mm; sourceTree = "<group>"; };
 		57599E231F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = IndexedDBStructuredCloneBackwardCompatibilityWrite.html; sourceTree = "<group>"; };
 		57599E241F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3 */ = {isa = PBXFileReference; lastKnownFileType = file; path = IndexedDBStructuredCloneBackwardCompatibility.sqlite3; sourceTree = "<group>"; };
@@ -3487,11 +3502,16 @@
 				CD577798211CDE8F001B371E /* web-audio-only.html */,
 				57663DF22357E45D00E85E09 /* web-authentication-get-assertion-hid-cancel.html */,
 				577454CF2359B338008E1ED7 /* web-authentication-get-assertion-hid-no-credentials.html */,
+				570D26FB23C3F86500D5CF67 /* web-authentication-get-assertion-hid-pin.html */,
 				57663DEB234F1F8000E85E09 /* web-authentication-get-assertion-hid.html */,
 				5798337B235EB65C008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html */,
 				57663DE9234EA60B00E85E09 /* web-authentication-get-assertion-nfc.html */,
 				577454D12359BAD5008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html */,
 				57C6244F2346C1EC00383FE7 /* web-authentication-get-assertion.html */,
+				570D26F323C3CA5500D5CF67 /* web-authentication-make-credential-hid-pin-get-key-agreement-error.html */,
+				570D26F923C3F24500D5CF67 /* web-authentication-make-credential-hid-pin-get-pin-token-error.html */,
+				5758598323C3C36200C74572 /* web-authentication-make-credential-hid-pin-get-retries-error.html */,
+				570D26F523C3D32700D5CF67 /* web-authentication-make-credential-hid-pin.html */,
 				5798337D2360196D008E5547 /* web-authentication-make-credential-hid.html */,
 				51714EB21CF8C761004723C4 /* WebProcessKillIDBCleanup-1.html */,
 				51714EB31CF8C761004723C4 /* WebProcessKillIDBCleanup-2.html */,

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/CtapPinTest.cpp (254438 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/CtapPinTest.cpp	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/CtapPinTest.cpp	2020-01-13 18:53:59 UTC (rev 254439)
@@ -41,7 +41,7 @@
 #include <WebCore/Pin.h>
 #include <WebCore/WebAuthenticationConstants.h>
 #include <WebCore/WebAuthenticationUtils.h>
-#include <wtf/text/Base64.h>
+#include <pal/crypto/CryptoDigest.h>
 
 namespace TestWebKitAPI {
 using namespace WebCore;
@@ -98,7 +98,7 @@
     // Success cases
     result = RetriesResponse::parse(convertBytesToVector(TestData::kCtapClientPinRetriesResponse, sizeof(TestData::kCtapClientPinRetriesResponse)));
     EXPECT_TRUE(result);
-    EXPECT_EQ(result->retries, 8);
+    EXPECT_EQ(result->retries, 8u);
 }
 
 TEST(CtapPinTest, TestKeyAgreementRequest)
@@ -125,6 +125,11 @@
     result = KeyAgreementResponse::parse(convertBytesToVector(TestData::kCtapClientPinTokenResponse, sizeof(TestData::kCtapClientPinTokenResponse))); // wrong response
     EXPECT_FALSE(result);
 
+#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || PLATFORM(IOS)
+    result = KeyAgreementResponse::parse(convertBytesToVector(TestData::kCtapClientPinInvalidKeyAgreementResponse, sizeof(TestData::kCtapClientPinInvalidKeyAgreementResponse))); // The point is not on the curve.
+    EXPECT_FALSE(result);
+#endif
+
     // Test COSE
     auto coseKey = encodeCOSEPublicKey(Vector<uint8_t>(65));
     coseKey[CBORValue(COSE::kty)] = CBORValue(0); // wrong kty
@@ -176,7 +181,7 @@
     auto token = TokenRequest::tryCreate(pin, downcast<CryptoKeyEC>(*keyPair.publicKey));
     EXPECT_TRUE(token);
     auto result = encodeAsCBOR(*token);
-    EXPECT_EQ(result.size(), 120u);
+    EXPECT_EQ(result.size(), 103u);
     EXPECT_EQ(result[0], static_cast<uint8_t>(CtapRequestCommand::kAuthenticatorClientPin));
 
     // Decode the CBOR binary to check if each field is encoded correctly.
@@ -224,12 +229,17 @@
     // Check the encrypted Pin.
     auto sharedKeyResult = CryptoAlgorithmECDH::platformDeriveBits(downcast<CryptoKeyEC>(*keyPair.privateKey), *cosePublicKey);
     EXPECT_TRUE(sharedKeyResult);
-    auto aesKey = CryptoKeyAES::importRaw(CryptoAlgorithmIdentifier::AES_CBC, WTFMove(*sharedKeyResult), true, CryptoKeyUsageDecrypt);
+
+    auto crypto = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256);
+    crypto->addBytes(sharedKeyResult->data(), sharedKeyResult->size());
+    auto sharedKeyHash = crypto->computeHash();
+
+    auto aesKey = CryptoKeyAES::importRaw(CryptoAlgorithmIdentifier::AES_CBC, WTFMove(sharedKeyHash), true, CryptoKeyUsageDecrypt);
     EXPECT_TRUE(aesKey);
 
     const auto& it6 = responseMap.find(CBORValue(static_cast<uint8_t>(RequestKey::kPinHashEnc)));
     EXPECT_NE(it6, responseMap.end());
-    auto pinHashResult = CryptoAlgorithmAES_CBC::platformDecrypt({ }, *aesKey, it6->second.getByteString());
+    auto pinHashResult = CryptoAlgorithmAES_CBC::platformDecrypt({ }, *aesKey, it6->second.getByteString(), CryptoAlgorithmAES_CBC::Padding::No);
     EXPECT_FALSE(pinHashResult.hasException());
     auto pinHash = pinHashResult.releaseReturnValue();
     const uint8_t expectedPinHash[] = { 0x03, 0xac, 0x67, 0x42, 0x16, 0xf3, 0xe1, 0x5c, 0x76, 0x1e, 0xe1, 0xa5, 0xe2, 0x55, 0xf0, 0x67 };
@@ -240,10 +250,10 @@
 TEST(CtapPinTest, TestTokenResponse)
 {
     const uint8_t sharedKeyData[] = {
-        0x03, 0xac, 0x67, 0x42, 0x16, 0xf3, 0xe1, 0x5c,
-        0x76, 0x1e, 0xe1, 0xa5, 0xe2, 0x55, 0xf0, 0x67,
-        0x95, 0x36, 0x23, 0xc8, 0xb3, 0x88, 0xb4, 0x45,
-        0x9e, 0x13, 0xf9, 0x78, 0xd7, 0xc8, 0x46, 0xf4, };
+        0x29, 0x9E, 0x65, 0xB8, 0xE7, 0x71, 0xB8, 0x1D,
+        0xB1, 0xC4, 0x8D, 0xBE, 0xCE, 0x50, 0x2A, 0x84,
+        0x05, 0x44, 0x7F, 0x46, 0x2D, 0xE6, 0x81, 0xFA,
+        0xEF, 0x0A, 0x6C, 0x67, 0xA7, 0x2B, 0xB5, 0x0F, };
     auto sharedKey = CryptoKeyAES::importRaw(CryptoAlgorithmIdentifier::AES_CBC, convertBytesToVector(sharedKeyData, sizeof(sharedKeyData)), true, CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt);
     ASSERT_TRUE(sharedKey);
 
@@ -274,10 +284,10 @@
 {
     // 1. Generate the token.
     const uint8_t sharedKeyData[] = {
-        0x03, 0xac, 0x67, 0x42, 0x16, 0xf3, 0xe1, 0x5c,
-        0x76, 0x1e, 0xe1, 0xa5, 0xe2, 0x55, 0xf0, 0x67,
-        0x95, 0x36, 0x23, 0xc8, 0xb3, 0x88, 0xb4, 0x45,
-        0x9e, 0x13, 0xf9, 0x78, 0xd7, 0xc8, 0x46, 0xf4, };
+        0x29, 0x9E, 0x65, 0xB8, 0xE7, 0x71, 0xB8, 0x1D,
+        0xB1, 0xC4, 0x8D, 0xBE, 0xCE, 0x50, 0x2A, 0x84,
+        0x05, 0x44, 0x7F, 0x46, 0x2D, 0xE6, 0x81, 0xFA,
+        0xEF, 0x0A, 0x6C, 0x67, 0xA7, 0x2B, 0xB5, 0x0F, };
     auto sharedKey = CryptoKeyAES::importRaw(CryptoAlgorithmIdentifier::AES_CBC, convertBytesToVector(sharedKeyData, sizeof(sharedKeyData)), true, CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt);
     ASSERT_TRUE(sharedKey);
     auto result = TokenResponse::parse(*sharedKey, convertBytesToVector(TestData::kCtapClientPinTokenResponse, sizeof(TestData::kCtapClientPinTokenResponse)));
@@ -285,7 +295,7 @@
 
     // 2. Generate the pinAuth.
     auto pinAuth = result->pinAuth(convertBytesToVector(sharedKeyData, sizeof(sharedKeyData))); // sharedKeyData pretends to be clientDataHash
-    const uint8_t expectedPinAuth[] = { 0xb3, 0xc2, 0x65, 0x1c, 0xfd, 0xc8, 0x42, 0xb4, 0x60, 0x16, 0xed, 0x20, 0x64, 0x53, 0xaf, 0x84 };
+    const uint8_t expectedPinAuth[] = { 0x0b, 0xec, 0x9d, 0xba, 0x69, 0xb0, 0x0f, 0x45, 0x0b, 0xec, 0x66, 0xb4, 0x75, 0x7f, 0x93, 0x85 };
     EXPECT_EQ(pinAuth.size(), 16u);
     EXPECT_EQ(memcmp(pinAuth.data(), expectedPinAuth, pinAuth.size()), 0);
 }

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h (254438 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/FidoTestData.h	2020-01-13 18:53:59 UTC (rev 254439)
@@ -612,15 +612,15 @@
     0x62, 0x75, 0x76,
     // True(21)
     0xf5,
-    // key(8) - pinProtocol
+    // key(8) - pinAuth
     0x08,
+    // bytes(16)
+    0x50, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    // key(9) - pinProtocol
+    0x09,
     // value - 1
     0x01,
-    // key(9) - pinAuth
-    0x09,
-    // bytes(16)
-    0x50, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
-    0x0b, 0x0c, 0x0d, 0x0e, 0x0f
 };
 
 constexpr uint8_t kTestComplexCtapGetAssertionRequest[] = {
@@ -745,15 +745,15 @@
     0x62, 0x75, 0x76,
     // value - True(21)
     0xf5,
-    // key(8) - pinProtocol
-    0x08,
+    // key(6) - pinAuth
+    0x06,
+    // bytes(16)
+    0x50, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+    0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    // key(7) - pinProtocol
+    0x07,
     // value - 1
     0x01,
-    // key(9) - pinAuth
-    0x09,
-    // bytes(16)
-    0x50, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
-    0x0b, 0x0c, 0x0d, 0x0e, 0x0f
 };
 
 // CTAP responses --------------------------------------------------------------
@@ -1227,12 +1227,11 @@
     0xa1,
     // key(2) - pinToken
     0x02,
-    // bytes(32)
-    0x58, 0x20,
+    // bytes(16)
+    0x50,
     // encrypted token
-    0x48, 0xb9, 0x5d, 0x3d, 0x9e, 0xee, 0xe5, 0x00, 0xab, 0x51, 0x13, 0x2b,
-    0x35, 0x77, 0x66, 0x5c, 0x41, 0x7a, 0x56, 0x3e, 0xb3, 0xe2, 0x3c, 0xb7,
-    0x6a, 0x37, 0xb3, 0xde, 0x57, 0x2b, 0xca, 0xaa,
+    0x13, 0xA4, 0xEE, 0xB7, 0x0E, 0xC9, 0x1A, 0xEA, 0x00, 0x1E, 0x93, 0x16,
+    0xF6, 0x1E, 0x41, 0xF7,
 };
 
 constexpr uint8_t kCtapClientPinKeyAgreementResponse[] = {
@@ -1268,6 +1267,39 @@
     0x70, 0x45, 0xF4, 0x61, 0x2F, 0xB2, 0x0C, 0x91,
 };
 
+constexpr uint8_t kCtapClientPinInvalidKeyAgreementResponse[] = {
+    // Success
+    0x00,
+    // map(1)
+    0xA1,
+    // key(1) - keyAgreement
+    0x01,
+    // Map(5)
+    0xA5,
+    // kty: EC key type
+    0x01, 0x02,
+    // alg: ECDH256 signature algorithm
+    0x03, 0x38, 0x18,
+    // crv: P-256 curve
+    0x20, 0x01,
+    // x-coordinate
+    0x21,
+    // Bytes(32)
+    0x58, 0x20,
+    // Byte array content
+    0xE8, 0x76, 0x25, 0x89, 0x6E, 0xE4, 0xE4, 0x66, 0xC0, 0x32, 0x76, 0x6E,
+    0x80, 0x87, 0x96, 0x2F, 0x36, 0xDF, 0x9D, 0xFF, 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,
+};
+
 constexpr uint8_t kCtapClientPinRetriesResponse[] = {
     // Success
     0x00,

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm (254438 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm	2020-01-13 18:34:42 UTC (rev 254438)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm	2020-01-13 18:53:59 UTC (rev 254439)
@@ -45,6 +45,7 @@
 static bool webAuthenticationPanelUpdateMultipleNFCTagsPresent = false;
 static bool webAuthenticationPanelUpdateNoCredentialsFound = false;
 static bool webAuthenticationPanelCancelImmediately = false;
+static String webAuthenticationPanelPin;
 
 @interface TestWebAuthenticationPanelDelegate : NSObject <_WKWebAuthenticationPanelDelegate>
 @end
@@ -83,6 +84,13 @@
     }
 }
 
+- (void)panel:(_WKWebAuthenticationPanel *)panel requestPINWithRemainingRetries:(NSUInteger)retries completionHandler:(void (^)(NSString *))completionHandler
+{
+    ASSERT_NE(panel, nil);
+    EXPECT_EQ(retries, 8ul);
+    completionHandler(webAuthenticationPanelPin);
+}
+
 @end
 
 @interface TestWebAuthenticationPanelFakeDelegate : NSObject <_WKWebAuthenticationPanelDelegate>
@@ -740,6 +748,130 @@
     Util::run(&webAuthenticationPanelUpdateNoCredentialsFound);
 }
 
+TEST(WebAuthenticationPanel, PinGetRetriesError)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-pin-get-retries-error" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Unknown internal error. Error code: 2"];
+}
+
+TEST(WebAuthenticationPanel, PinGetKeyAgreementError)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-pin-get-key-agreement-error" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Unknown internal error. Error code: 2"];
+}
+
+TEST(WebAuthenticationPanel, PinRequestPinErrorNoDelegate)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-pin" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Pin is not valid: "];
+}
+
+TEST(WebAuthenticationPanel, PinRequestPinErrorNullDelegate)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-pin" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [delegate setIsNull:true];
+    [webView setUIDelegate:delegate.get()];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Pin is not valid: "];
+}
+
+TEST(WebAuthenticationPanel, PinRequestPinError)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-pin" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    webAuthenticationPanelPin = "123";
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Pin is not valid: 123"];
+}
+
+TEST(WebAuthenticationPanel, PinGetPinTokenError)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-pin-get-pin-token-error" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    webAuthenticationPanelPin = "1234";
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Unknown internal error. Error code: 2"];
+}
+
+TEST(WebAuthenticationPanel, MakeCredentialPin)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-hid-pin" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    webAuthenticationPanelPin = "1234";
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Succeeded!"];
+}
+
+TEST(WebAuthenticationPanel, GetAssertionPin)
+{
+    reset();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-hid-pin" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
+    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
+    [webView setUIDelegate:delegate.get()];
+
+    webAuthenticationPanelPin = "1234";
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    [webView waitForMessage:@"Succeeded!"];
+}
+
 } // namespace TestWebKitAPI
 
 #endif // ENABLE(WEB_AUTHN)

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-pin.html (0 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-pin.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-hid-pin.html	2020-01-13 18:53:59 UTC (rev 254439)
@@ -0,0 +1,32 @@
+<input type="text" id="input">
+<script>
+    const testCtapPinAuthInvalidErrorBase64 = "Mw==";
+    const testPinGetRetriesResponseBase64 = "AKEDCA==";
+    const testPinGetKeyAgreementResponseBase64 = "AKEBpQECAzgYIAEhWCDodiWJbuTkbcAydm6Ah5YvNt+d/otWfzdjAVsZkKYOFCJYICfeYS1mQYvaGVBYHrxcjB2tcQyxTCL4yXBF9GEvsgyR";
+    const testPinGetPinTokenResponseBase64 = "AKECUBOk7rcOyRrqAB6TFvYeQfc=";
+    const testAssertionMessageBase64 =
+        "AKMBomJpZFhAKAitzuj+Tslzelf3/vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2" +
+        "w/mooa7tQtGgDdwZIjOhjcuZ0pQ1ajoE4GR0eXBlanB1YmxpYy1rZXkCWCVGzH+5" +
+        "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
+        "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
+        "QoJ1L7Fe64G9uBc=";
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ hid: { supportClientPin: true, payloadBase64: [testCtapPinAuthInvalidErrorBase64, testPinGetRetriesResponseBase64, testPinGetKeyAgreementResponseBase64, testPinGetPinTokenResponseBase64, testAssertionMessageBase64] } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
+
+    const options = {
+        publicKey: {
+            challenge: new Uint8Array(16),
+            timeout: 100
+        }
+    };
+
+    navigator.credentials.get(options).then(credential => {
+        // console.log("Succeeded!");
+        window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
+    }, error => {
+        // console.log(error.message);
+        window.webkit.messageHandlers.testHandler.postMessage(error.message);
+    });
+</script>

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-key-agreement-error.html (0 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-key-agreement-error.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-key-agreement-error.html	2020-01-13 18:53:59 UTC (rev 254439)
@@ -0,0 +1,33 @@
+<input type="text" id="input">
+<script>
+    const testCtapPinInvalidErrorBase64 = "MQ==";
+    const testPinGetRetriesResponseBase64 = "AKEDCA==";
+    const testCtapInvalidParameterErrorBase64 = "Ag==";
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ hid: { supportClientPin: true, payloadBase64: [testCtapPinInvalidErrorBase64, testPinGetRetriesResponseBase64, testCtapInvalidParameterErrorBase64] } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
+
+    const options = {
+        publicKey: {
+            rp: {
+                name: "localhost",
+            },
+            user: {
+                name: "John Appleseed",
+                id: new Uint8Array(16),
+                displayName: "Appleseed",
+            },
+            challenge: new Uint8Array(16),
+            pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+        }
+    };
+
+    navigator.credentials.create(options).then(credential => {
+        // console.log("Succeeded!");
+        window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
+    }, error => {
+        // console.log(error.message);
+        window.webkit.messageHandlers.testHandler.postMessage(error.message);
+    });
+</script>

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-pin-token-error.html (0 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-pin-token-error.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-pin-token-error.html	2020-01-13 18:53:59 UTC (rev 254439)
@@ -0,0 +1,34 @@
+<input type="text" id="input">
+<script>
+    const testCtapPinInvalidErrorBase64 = "MQ==";
+    const testPinGetRetriesResponseBase64 = "AKEDCA==";
+    const testPinGetKeyAgreementResponseBase64 = "AKEBpQECAzgYIAEhWCDodiWJbuTkbcAydm6Ah5YvNt+d/otWfzdjAVsZkKYOFCJYICfeYS1mQYvaGVBYHrxcjB2tcQyxTCL4yXBF9GEvsgyR";
+    const testCtapInvalidParameterErrorBase64 = "Ag==";
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ hid: { supportClientPin: true, payloadBase64: [testCtapPinInvalidErrorBase64, testPinGetRetriesResponseBase64, testPinGetKeyAgreementResponseBase64, testCtapInvalidParameterErrorBase64] } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
+
+    const options = {
+        publicKey: {
+            rp: {
+                name: "localhost",
+            },
+            user: {
+                name: "John Appleseed",
+                id: new Uint8Array(16),
+                displayName: "Appleseed",
+            },
+            challenge: new Uint8Array(16),
+            pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+        }
+    };
+
+    navigator.credentials.create(options).then(credential => {
+        // console.log("Succeeded!");
+        window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
+    }, error => {
+        // console.log(error.message);
+        window.webkit.messageHandlers.testHandler.postMessage(error.message);
+    });
+</script>

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-retries-error.html (0 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-retries-error.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin-get-retries-error.html	2020-01-13 18:53:59 UTC (rev 254439)
@@ -0,0 +1,32 @@
+<input type="text" id="input">
+<script>
+    const testCtapPinInvalidErrorBase64 = "MQ==";
+    const testCtapInvalidParameterErrorBase64 = "Ag==";
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ hid: { supportClientPin: true, payloadBase64: [testCtapPinInvalidErrorBase64, testCtapInvalidParameterErrorBase64] } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
+
+    const options = {
+        publicKey: {
+            rp: {
+                name: "localhost",
+            },
+            user: {
+                name: "John Appleseed",
+                id: new Uint8Array(16),
+                displayName: "Appleseed",
+            },
+            challenge: new Uint8Array(16),
+            pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+        }
+    };
+
+    navigator.credentials.create(options).then(credential => {
+        // console.log("Succeeded!");
+        window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
+    }, error => {
+        // console.log(error.message);
+        window.webkit.messageHandlers.testHandler.postMessage(error.message);
+    });
+</script>

Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin.html (0 => 254439)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-hid-pin.html	2020-01-13 18:53:59 UTC (rev 254439)
@@ -0,0 +1,56 @@
+<input type="text" id="input">
+<script>
+    const testCtapPinAuthInvalidErrorBase64 = "Mw==";
+    const testPinGetRetriesResponseBase64 = "AKEDCA==";
+    const testPinGetKeyAgreementResponseBase64 = "AKEBpQECAzgYIAEhWCDodiWJbuTkbcAydm6Ah5YvNt+d/otWfzdjAVsZkKYOFCJYICfeYS1mQYvaGVBYHrxcjB2tcQyxTCL4yXBF9GEvsgyR";
+    const testPinGetPinTokenResponseBase64 = "AKECUBOk7rcOyRrqAB6TFvYeQfc=";
+    const testCreationMessageBase64 =
+        "AKMBZnBhY2tlZAJYxEbMf7lnnVWy25CS4cjZ5eHQK3WA8LSBLHcJYuHkj1rYQQAA" +
+        "AE74oBHzjApNFYAGFxEfntx9AEAoCK3O6P5OyXN6V/f+9nAga0NA2Cgp4V3mgSJ5" +
+        "jOHLMDrmxp/S0rbD+aihru1C0aAN3BkiM6GNy5nSlDVqOgTgpQECAyYgASFYIEFb" +
+        "he3RkNud6sgyraBGjlh1pzTlCZehQlL/b18HZ6WGIlggJgfUd/en9p5AIqMQbUni" +
+        "nEeXdFLkvW0/zV5BpEjjNxADo2NhbGcmY3NpZ1hHMEUCIQDKg+ZBmEBtf0lWq4Re" +
+        "dH4/i/LOYqOR4uR2NAj2zQmw9QIgbTXb4hvFbj4T27bv/rGrc+y+0puoYOBkBk9P" +
+        "mCewWlNjeDVjgVkCwjCCAr4wggGmoAMCAQICBHSG/cIwDQYJKoZIhvcNAQELBQAw" +
+        "LjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEw" +
+        "IBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG8xCzAJBgNVBAYTAlNF" +
+        "MRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0" +
+        "ZXN0YXRpb24xKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NTUwMDM4" +
+        "NDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASVXfOt9yR9MXXv/ZzE8xpOh466" +
+        "4YEJVmFQ+ziLLl9lJ79XQJqlgaUNCsUvGERcChNUihNTyKTlmnBOUjvATevto2ww" +
+        "ajAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMTATBgsrBgEEAYLl" +
+        "HAIBAQQEAwIFIDAhBgsrBgEEAYLlHAEBBAQSBBD4oBHzjApNFYAGFxEfntx9MAwG" +
+        "A1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBADFcSIDmmlJ+OGaJvWn9Cqhv" +
+        "SeueToVFQVVvqtALOgCKHdwB+Wx29mg2GpHiMsgQp5xjB0ybbnpG6x212FxESJ+G" +
+        "inZD0ipchi7APwPlhIvjgH16zVX44a4e4hOsc6tLIOP71SaMsHuHgCcdH0vg5d2s" +
+        "c006WJe9TXO6fzV+ogjJnYpNKQLmCXoAXE3JBNwKGBIOCvfQDPyWmiiG5bGxYfPt" +
+        "y8Z3pnjX+1MDnM2hhr40ulMxlSNDnX/ZSnDyMGIbk8TOQmjTF02UO8auP8k3wt5D" +
+        "1rROIRU9+FCSX5WQYi68RuDrGMZB8P5+byoJqbKQdxn2LmE1oZAyohPAmLcoPO4=";
+    if (window.internals) {
+        internals.setMockWebAuthenticationConfiguration({ hid: { supportClientPin: true, payloadBase64: [testCtapPinAuthInvalidErrorBase64, testPinGetRetriesResponseBase64, testPinGetKeyAgreementResponseBase64, testPinGetPinTokenResponseBase64, testCreationMessageBase64] } });
+        internals.withUserGesture(() => { input.focus(); });
+    }
+
+    const options = {
+        publicKey: {
+            rp: {
+                name: "localhost",
+            },
+            user: {
+                name: "John Appleseed",
+                id: new Uint8Array(16),
+                displayName: "Appleseed",
+            },
+            challenge: new Uint8Array(16),
+            pubKeyCredParams: [{ type: "public-key", alg: -7 }]
+        }
+    };
+
+    navigator.credentials.create(options).then(credential => {
+        // console.log("Succeeded!");
+        window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
+    }, error => {
+        // console.log(error.message);
+        window.webkit.messageHandlers.testHandler.postMessage(error.message);
+    });
+</script>
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to