Diff
Modified: trunk/Source/WebCore/ChangeLog (291623 => 291624)
--- trunk/Source/WebCore/ChangeLog 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebCore/ChangeLog 2022-03-22 16:14:25 UTC (rev 291624)
@@ -1,3 +1,22 @@
+2022-03-22 J Pascoe <j_pas...@apple.com>
+
+ [WebAuthn] Support getAssertion for virtual HID authenticators
+ https://bugs.webkit.org/show_bug.cgi?id=238154
+ rdar://problem/90593150
+
+ Reviewed by Brent Fulgham.
+
+ Virtual authenticators for WebAuthn support different transprots: nfc, usb, internal,
+ and ble. Currently, we only fully support the internal transport and makeCredential for
+ usb-transport. The default transport for web-platform-tests is usb. This patch implements
+ getAssertion for hid-based virtual authneticators.
+
+ * Modules/webauthn/WebAuthenticationUtils.cpp:
+ (WebCore::buildUserEntityMap):
+ (WebCore::buildCredentialDescriptor):
+ * Modules/webauthn/WebAuthenticationUtils.h:
+ * Modules/webauthn/fido/FidoConstants.h:
+
2022-03-22 Ricky Mondello <rmonde...@apple.com>
It should be possible to copy text out of "AutoFilledAndViewable" password fields
Modified: trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.cpp (291623 => 291624)
--- trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.cpp 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.cpp 2022-03-22 16:14:25 UTC (rev 291624)
@@ -29,6 +29,7 @@
#if ENABLE(WEB_AUTHN)
#include "CBORWriter.h"
+#include "FidoConstants.h"
#include "WebAuthenticationConstants.h"
#include <pal/crypto/CryptoDigest.h>
#include <wtf/JSONValues.h>
@@ -87,6 +88,22 @@
return attestedCredentialData;
}
+cbor::CBORValue::MapValue buildUserEntityMap(const Vector<uint8_t>& userId, const String& name, const String& displayName)
+{
+ cbor::CBORValue::MapValue userEntityMap;
+ userEntityMap[cbor::CBORValue(fido::kEntityIdMapKey)] = cbor::CBORValue(userId);
+ userEntityMap[cbor::CBORValue(fido::kEntityNameMapKey)] = cbor::CBORValue(name);
+ userEntityMap[cbor::CBORValue(fido::kDisplayNameMapKey)] = cbor::CBORValue(displayName);
+ return userEntityMap;
+}
+
+cbor::CBORValue::MapValue buildCredentialDescriptor(const Vector<uint8_t>& credentialId)
+{
+ cbor::CBORValue::MapValue credential;
+ credential[cbor::CBORValue("id")] = cbor::CBORValue(credentialId);
+ return credential;
+}
+
Vector<uint8_t> buildAuthData(const String& rpId, const uint8_t flags, const uint32_t counter, const Vector<uint8_t>& optionalAttestedCredentialData)
{
Vector<uint8_t> authData;
Modified: trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.h (291623 => 291624)
--- trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.h 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.h 2022-03-22 16:14:25 UTC (rev 291624)
@@ -51,6 +51,8 @@
WEBCORE_EXPORT cbor::CBORValue::MapValue buildAttestationMap(Vector<uint8_t>&&, String&&, cbor::CBORValue::MapValue&&, const AttestationConveyancePreference&);
+WEBCORE_EXPORT cbor::CBORValue::MapValue buildCredentialDescriptor(const Vector<uint8_t>& credentialId);
+
// https://www.w3.org/TR/webauthn/#attestation-object
WEBCORE_EXPORT Vector<uint8_t> buildAttestationObject(Vector<uint8_t>&& authData, String&& format, cbor::CBORValue::MapValue&& statementMap, const AttestationConveyancePreference&);
@@ -58,6 +60,7 @@
WEBCORE_EXPORT Vector<uint8_t> buildClientDataJsonHash(const ArrayBuffer& clientDataJson);
+WEBCORE_EXPORT cbor::CBORValue::MapValue buildUserEntityMap(const Vector<uint8_t>& userId, const String& name, const String& displayName);
} // namespace WebCore
#endif // ENABLE(WEB_AUTHN)
Modified: trunk/Source/WebCore/Modules/webauthn/fido/FidoConstants.h (291623 => 291624)
--- trunk/Source/WebCore/Modules/webauthn/fido/FidoConstants.h 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebCore/Modules/webauthn/fido/FidoConstants.h 2022-03-22 16:14:25 UTC (rev 291624)
@@ -266,7 +266,14 @@
const int64_t kCtapMakeCredentialExcludeListKey = 5;
const int64_t kCtapMakeCredentialExtensionsKey = 6;
const int64_t kCtapMakeCredentialRequestOptionsKey = 7;
+
+const int64_t kCtapGetAssertionRpIdKey = 1;
+const int64_t kCtapGetAssertionClientDataHashKey = 2;
+const int64_t kCtapGetAssertionAllowListKey = 3;
+const int64_t kCtapGetAssertionExtensionsKey = 4;
const int64_t kCtapGetAssertionRequestOptionsKey = 5;
+const int64_t kCtapGetAssertionPinUvAuthParamKey = 6;
+const int64_t kCtapGetAssertionPinUvAuthProtocolKey = 7;
} // namespace fido
Modified: trunk/Source/WebKit/ChangeLog (291623 => 291624)
--- trunk/Source/WebKit/ChangeLog 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebKit/ChangeLog 2022-03-22 16:14:25 UTC (rev 291624)
@@ -1,3 +1,29 @@
+2022-03-22 J Pascoe <j_pas...@apple.com>
+
+ [WebAuthn] Support getAssertion for virtual HID authenticators
+ https://bugs.webkit.org/show_bug.cgi?id=238154
+ rdar://problem/90593150
+
+ Reviewed by Brent Fulgham.
+
+ Virtual authenticators for WebAuthn support different transports: nfc, usb, internal,
+ and ble. Currently, we only fully support the internal transport and makeCredential for
+ usb-transport. The default transport for web-platform-tests is usb. This patch implements
+ getAssertion for hid-based virtual authneticators.
+
+ Tested via manually creating virtual authenticator and performing create / get.
+
+ * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.cpp:
+ (WebKit::VirtualAuthenticatorManager::addCredential):
+ (WebKit::VirtualAuthenticatorManager::credentialsMatchingList):
+ * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.h:
+ * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.h:
+ * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.mm:
+ (WebKit::privateKeyFromBase64):
+ (WebKit::signatureForPrivateKey):
+ * UIProcess/WebAuthentication/Virtual/VirtualHidConnection.cpp:
+ (WebKit::VirtualHidConnection::parseRequest):
+
2022-03-22 Jer Noble <jer.no...@apple.com>
Fetching a Blob URL with an unbounded Range header do not generate a Content-Range response header
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.cpp (291623 => 291624)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.cpp 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.cpp 2022-03-22 16:14:25 UTC (rev 291624)
@@ -57,11 +57,22 @@
return m_virtualAuthenticators.remove(id);
}
-void VirtualAuthenticatorManager::addCredential(const String& authenticatorId, const VirtualCredential& credential)
+void VirtualAuthenticatorManager::addCredential(const String& authenticatorId, VirtualCredential& credential)
{
- m_credentialsByAuthenticator.get(authenticatorId).append(credential);
+ m_credentialsByAuthenticator.find(authenticatorId)->value.append(WTFMove(credential));
}
+Vector<VirtualCredential> VirtualAuthenticatorManager::credentialsMatchingList(const String& authenticatorId, const String& rpId, const Vector<Vector<uint8_t>>& credentialIds)
+{
+ Vector<VirtualCredential> matching;
+ auto it = m_credentialsByAuthenticator.find(authenticatorId);
+ for (auto& credential : it->value) {
+ if (credential.rpId == rpId && ((credentialIds.isEmpty() && credential.isResidentCredential) || credentialIds.contains(credential.credentialId)))
+ matching.append(credential);
+ }
+ return matching;
+}
+
UniqueRef<AuthenticatorTransportService> VirtualAuthenticatorManager::createService(WebCore::AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
{
Vector<std::pair<String, VirtualAuthenticatorConfiguration>> configs;
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.h (291623 => 291624)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.h 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.h 2022-03-22 16:14:25 UTC (rev 291624)
@@ -44,7 +44,8 @@
bool isVirtual() const final { return true; }
- void addCredential(const String&, const VirtualCredential&);
+ void addCredential(const String&, VirtualCredential&);
+ Vector<VirtualCredential> credentialsMatchingList(const String& authenticatorId, const String& rpId, const Vector<Vector<uint8_t>>& credentialIds);
protected:
void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) override;
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.h (291623 => 291624)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.h 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.h 2022-03-22 16:14:25 UTC (rev 291624)
@@ -35,6 +35,8 @@
RetainPtr<SecKeyRef> createPrivateKey();
std::pair<Vector<uint8_t>, Vector<uint8_t>> credentialIdAndCosePubKeyForPrivateKey(RetainPtr<SecKeyRef> privateKey);
String base64PrivateKey(RetainPtr<SecKeyRef> privateKey);
+RetainPtr<SecKeyRef> privateKeyFromBase64(const String& base64PrivateKey);
+Vector<uint8_t> signatureForPrivateKey(RetainPtr<SecKeyRef> privateKey, const Vector<uint8_t>& authData, const Vector<uint8_t>& clientDataHash);
} // namespace WebKit
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.mm (291623 => 291624)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.mm 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.mm 2022-03-22 16:14:25 UTC (rev 291624)
@@ -32,6 +32,8 @@
#include <WebCore/WebAuthenticationConstants.h>
#include <WebCore/WebAuthenticationUtils.h>
#include <pal/crypto/CryptoDigest.h>
+#include <wtf/cocoa/TypeCastsCocoa.h>
+#include <wtf/cocoa/VectorCocoa.h>
namespace WebKit {
using namespace WebCore;
@@ -109,6 +111,39 @@
return String([nsPrivateKeyRep base64EncodedStringWithOptions:0]);
}
+RetainPtr<SecKeyRef> privateKeyFromBase64(const String& base64PrivateKey)
+{
+ NSDictionary* options = @{
+ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
+ (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
+ (id)kSecAttrKeySizeInBits: @256,
+ };
+ RetainPtr<NSData> privateKey = adoptNS([[NSData alloc] initWithBase64EncodedString:base64PrivateKey options:0]);
+ CFErrorRef errorRef = nullptr;
+ auto key = adoptCF(SecKeyCreateWithData(
+ bridge_cast(privateKey.get()),
+ bridge_cast(options),
+ &errorRef
+ ));
+ ASSERT(!errorRef);
+ return key;
+}
+
+Vector<uint8_t> signatureForPrivateKey(RetainPtr<SecKeyRef> privateKey, const Vector<uint8_t>& authData, const Vector<uint8_t>& clientDataHash)
+{
+ NSMutableData *dataToSign = [NSMutableData dataWithBytes:authData.data() length:authData.size()];
+ [dataToSign appendBytes:clientDataHash.data() length:clientDataHash.size()];
+ RetainPtr<CFDataRef> signature;
+ {
+ CFErrorRef errorRef = nullptr;
+ signature = adoptCF(SecKeyCreateSignature((__bridge SecKeyRef)((id)privateKey.get()), kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)dataToSign, &errorRef));
+ auto retainError = adoptCF(errorRef);
+ ASSERT(!errorRef);
+ }
+
+ return vectorFromNSData((NSData *)signature.get());
+}
+
} // namespace WebKit
#endif // ENABLE(WEB_AUTHN)
Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualHidConnection.cpp (291623 => 291624)
--- trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualHidConnection.cpp 2022-03-22 16:01:26 UTC (rev 291623)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualHidConnection.cpp 2022-03-22 16:14:25 UTC (rev 291624)
@@ -212,19 +212,17 @@
return;
}
credential.userHandle = it->second.getByteString();
-
+ auto credentialIdAndCosePubKey = credentialIdAndCosePubKeyForPrivateKey(privateKey);
+ credential.credentialId = credentialIdAndCosePubKey.first;
manager->addCredential(m_authenticatorId, credential);
- auto credentialIdAndCosePubKey = credentialIdAndCosePubKeyForPrivateKey(privateKey);
auto attestedCredentialData = buildAttestedCredentialData(Vector<uint8_t>(aaguidLength, 0), credentialIdAndCosePubKey.first, credentialIdAndCosePubKey.second);
auto authenticatorData = buildAuthData(credential.rpId, flagsForConfig(m_configuration), credential.signCount, attestedCredentialData);
CBORValue::MapValue response;
+ response[CBORValue(1)] = CBORValue("none");
response[CBORValue(2)] = CBORValue(authenticatorData);
-
auto attObj = buildAttestationMap(WTFMove(authenticatorData), "", { }, AttestationConveyancePreference::None);
-
- response[CBORValue(1)] = CBORValue("none");
response[CBORValue(3)] = CBORValue(attObj);
auto payload = CBORWriter::write(CBORValue(response));
Vector<uint8_t> buffer;
@@ -234,6 +232,93 @@
auto message = FidoHidMessage::create(m_requestMessage->channelId(), FidoHidDeviceCommand::kCbor, WTFMove(buffer));
receiveHidMessage(WTFMove(*message));
+ } else if (cmd == CtapRequestCommand::kAuthenticatorGetAssertion) {
+ auto it = requestMap->getMap().find(CBORValue(kCtapGetAssertionRpIdKey));
+ if (it == requestMap->getMap().end()) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrMissingParameter);
+ return;
+ }
+ auto rpId = it->second.getString();
+ (void)rpId;
+ it = requestMap->getMap().find(CBORValue(kCtapGetAssertionClientDataHashKey));
+ if (it == requestMap->getMap().end()) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrMissingParameter);
+ return;
+ }
+ auto clientDataHash = it->second.getByteString();
+ (void)clientDataHash;
+ Vector<Vector<uint8_t>> allowList;
+ it = requestMap->getMap().find(CBORValue(kCtapGetAssertionAllowListKey));
+ if (it != requestMap->getMap().end()) {
+ const auto& cborAllowList = it->second.getArray();
+ if (cborAllowList.isEmpty()) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrInvalidOption);
+ return;
+ }
+
+ for (const auto& cborCredential : cborAllowList) {
+ auto& credMap = cborCredential.getMap();
+ auto itr = credMap.find(CBORValue(fido::kEntityIdMapKey));
+ if (itr == credMap.end() || !itr->second.isByteString()) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrInvalidOption);
+ return;
+ }
+ allowList.append(itr->second.getByteString());
+ }
+ }
+
+ it = requestMap->getMap().find(CBORValue(kCtapGetAssertionRequestOptionsKey));
+ if (it != requestMap->getMap().end()) {
+ auto& optionMap = it->second.getMap();
+
+ auto itr = optionMap.find(CBORValue(kUserVerificationMapKey));
+ if (itr != optionMap.end()) {
+ auto requireUserVerification = itr->second.getBool();
+ if (requireUserVerification && !m_configuration.hasUserVerification) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrInvalidOption);
+ return;
+ }
+ if (requireUserVerification && !m_configuration.isUserVerified) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+ return;
+ }
+ }
+
+ itr = optionMap.find(CBORValue(kUserPresenceMapKey));
+ if (itr != optionMap.end()) {
+ auto requireUserPresence = itr->second.getBool();
+ if (requireUserPresence && !m_configuration.isUserConsenting) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrOperationDenied);
+ return;
+ }
+ }
+ }
+ auto matchingCredentials = m_manager->credentialsMatchingList(m_authenticatorId, rpId, allowList);
+ if (matchingCredentials.isEmpty()) {
+ recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrNoCredentials);
+ return;
+ }
+ auto& credential = matchingCredentials[0];
+ CBORValue::MapValue response;
+
+ response[CBORValue(1)] = CBORValue(buildCredentialDescriptor(credential.credentialId));
+ auto key = privateKeyFromBase64(credential.privateKey);
+ auto credentialIdAndCosePubKey = credentialIdAndCosePubKeyForPrivateKey(key);
+ auto attestedCredentialData = buildAttestedCredentialData(Vector<uint8_t>(aaguidLength, 0), credentialIdAndCosePubKey.first, credentialIdAndCosePubKey.second);
+ auto authData = buildAuthData(rpId, flagsForConfig(m_configuration), credential.signCount, attestedCredentialData);
+ response[CBORValue(2)] = CBORValue(authData);
+ response[CBORValue(3)] = CBORValue(signatureForPrivateKey(key, authData, clientDataHash));
+ if (credential.userHandle)
+ response[CBORValue(4)] = CBORValue(buildUserEntityMap(*credential.userHandle, "", ""));
+ response[CBORValue(5)] = CBORValue((int64_t)matchingCredentials.size());
+ auto payload = CBORWriter::write(CBORValue(response));
+ Vector<uint8_t> buffer;
+ buffer.reserveCapacity(payload->size() + 1);
+ buffer.append(static_cast<uint8_t>(fido::CtapDeviceResponseCode::kSuccess));
+ buffer.appendVector(*payload);
+
+ auto message = FidoHidMessage::create(m_requestMessage->channelId(), FidoHidDeviceCommand::kCbor, WTFMove(buffer));
+ receiveHidMessage(WTFMove(*message));
}
break;
}