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>