Changeset 291624 in webkit


Ignore:
Timestamp:
Mar 22, 2022 9:14:25 AM (4 months ago)
Author:
J Pascoe
Message:

[WebAuthn] Support getAssertion for virtual HID authenticators
https://bugs.webkit.org/show_bug.cgi?id=238154
rdar://problem/90593150

Reviewed by Brent Fulgham.

Source/WebCore:

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:

Source/WebKit:

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):

Location:
trunk/Source
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r291623 r291624  
     12022-03-22  J Pascoe  <j_pascoe@apple.com>
     2
     3        [WebAuthn] Support getAssertion for virtual HID authenticators
     4        https://bugs.webkit.org/show_bug.cgi?id=238154
     5        rdar://problem/90593150
     6
     7        Reviewed by Brent Fulgham.
     8
     9        Virtual authenticators for WebAuthn support different transprots: nfc, usb, internal,
     10        and ble. Currently, we only fully support the internal transport and makeCredential for
     11        usb-transport. The default transport for web-platform-tests is usb. This patch implements
     12        getAssertion for hid-based virtual authneticators.
     13
     14        * Modules/webauthn/WebAuthenticationUtils.cpp:
     15        (WebCore::buildUserEntityMap):
     16        (WebCore::buildCredentialDescriptor):
     17        * Modules/webauthn/WebAuthenticationUtils.h:
     18        * Modules/webauthn/fido/FidoConstants.h:
     19
    1202022-03-22  Ricky Mondello  <rmondello@apple.com>
    221
  • trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.cpp

    r291423 r291624  
    3030
    3131#include "CBORWriter.h"
     32#include "FidoConstants.h"
    3233#include "WebAuthenticationConstants.h"
    3334#include <pal/crypto/CryptoDigest.h>
     
    8687
    8788    return attestedCredentialData;
     89}
     90
     91cbor::CBORValue::MapValue buildUserEntityMap(const Vector<uint8_t>& userId, const String& name, const String& displayName)
     92{
     93    cbor::CBORValue::MapValue userEntityMap;
     94    userEntityMap[cbor::CBORValue(fido::kEntityIdMapKey)] = cbor::CBORValue(userId);
     95    userEntityMap[cbor::CBORValue(fido::kEntityNameMapKey)] = cbor::CBORValue(name);
     96    userEntityMap[cbor::CBORValue(fido::kDisplayNameMapKey)] = cbor::CBORValue(displayName);
     97    return userEntityMap;
     98}
     99
     100cbor::CBORValue::MapValue buildCredentialDescriptor(const Vector<uint8_t>& credentialId)
     101{
     102    cbor::CBORValue::MapValue credential;
     103    credential[cbor::CBORValue("id")] = cbor::CBORValue(credentialId);
     104    return credential;
    88105}
    89106
  • trunk/Source/WebCore/Modules/webauthn/WebAuthenticationUtils.h

    r291423 r291624  
    5252WEBCORE_EXPORT cbor::CBORValue::MapValue buildAttestationMap(Vector<uint8_t>&&, String&&, cbor::CBORValue::MapValue&&, const AttestationConveyancePreference&);
    5353
     54WEBCORE_EXPORT cbor::CBORValue::MapValue buildCredentialDescriptor(const Vector<uint8_t>& credentialId);
     55
    5456// https://www.w3.org/TR/webauthn/#attestation-object
    5557WEBCORE_EXPORT Vector<uint8_t> buildAttestationObject(Vector<uint8_t>&& authData, String&& format, cbor::CBORValue::MapValue&& statementMap, const AttestationConveyancePreference&);
     
    5961WEBCORE_EXPORT Vector<uint8_t> buildClientDataJsonHash(const ArrayBuffer& clientDataJson);
    6062
     63WEBCORE_EXPORT cbor::CBORValue::MapValue buildUserEntityMap(const Vector<uint8_t>& userId, const String& name, const String& displayName);
    6164} // namespace WebCore
    6265
  • trunk/Source/WebCore/Modules/webauthn/fido/FidoConstants.h

    r291423 r291624  
    267267const int64_t kCtapMakeCredentialExtensionsKey = 6;
    268268const int64_t kCtapMakeCredentialRequestOptionsKey = 7;
     269
     270const int64_t kCtapGetAssertionRpIdKey = 1;
     271const int64_t kCtapGetAssertionClientDataHashKey = 2;
     272const int64_t kCtapGetAssertionAllowListKey = 3;
     273const int64_t kCtapGetAssertionExtensionsKey = 4;
    269274const int64_t kCtapGetAssertionRequestOptionsKey = 5;
     275const int64_t kCtapGetAssertionPinUvAuthParamKey = 6;
     276const int64_t kCtapGetAssertionPinUvAuthProtocolKey = 7;
    270277
    271278} // namespace fido
  • trunk/Source/WebKit/ChangeLog

    r291622 r291624  
     12022-03-22  J Pascoe  <j_pascoe@apple.com>
     2
     3        [WebAuthn] Support getAssertion for virtual HID authenticators
     4        https://bugs.webkit.org/show_bug.cgi?id=238154
     5        rdar://problem/90593150
     6
     7        Reviewed by Brent Fulgham.
     8
     9        Virtual authenticators for WebAuthn support different transports: nfc, usb, internal,
     10        and ble. Currently, we only fully support the internal transport and makeCredential for
     11        usb-transport. The default transport for web-platform-tests is usb. This patch implements
     12        getAssertion for hid-based virtual authneticators.
     13
     14        Tested via manually creating virtual authenticator and performing create / get.
     15
     16        * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.cpp:
     17        (WebKit::VirtualAuthenticatorManager::addCredential):
     18        (WebKit::VirtualAuthenticatorManager::credentialsMatchingList):
     19        * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.h:
     20        * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.h:
     21        * UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.mm:
     22        (WebKit::privateKeyFromBase64):
     23        (WebKit::signatureForPrivateKey):
     24        * UIProcess/WebAuthentication/Virtual/VirtualHidConnection.cpp:
     25        (WebKit::VirtualHidConnection::parseRequest):
     26
    1272022-03-22  Jer Noble  <jer.noble@apple.com>
    228
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.cpp

    r291423 r291624  
    5858}
    5959
    60 void VirtualAuthenticatorManager::addCredential(const String& authenticatorId, const VirtualCredential& credential)
     60void VirtualAuthenticatorManager::addCredential(const String& authenticatorId, VirtualCredential& credential)
    6161{
    62     m_credentialsByAuthenticator.get(authenticatorId).append(credential);
     62    m_credentialsByAuthenticator.find(authenticatorId)->value.append(WTFMove(credential));
     63}
     64
     65Vector<VirtualCredential> VirtualAuthenticatorManager::credentialsMatchingList(const String& authenticatorId, const String& rpId, const Vector<Vector<uint8_t>>& credentialIds)
     66{
     67    Vector<VirtualCredential> matching;
     68    auto it = m_credentialsByAuthenticator.find(authenticatorId);
     69    for (auto& credential : it->value) {
     70        if (credential.rpId == rpId && ((credentialIds.isEmpty() && credential.isResidentCredential) || credentialIds.contains(credential.credentialId)))
     71            matching.append(credential);
     72    }
     73    return matching;
    6374}
    6475
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorManager.h

    r291423 r291624  
    4545    bool isVirtual() const final { return true; }
    4646
    47     void addCredential(const String&, const VirtualCredential&);
     47    void addCredential(const String&, VirtualCredential&);
     48    Vector<VirtualCredential> credentialsMatchingList(const String& authenticatorId, const String& rpId, const Vector<Vector<uint8_t>>& credentialIds);
    4849
    4950protected:
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.h

    r291423 r291624  
    3636std::pair<Vector<uint8_t>, Vector<uint8_t>> credentialIdAndCosePubKeyForPrivateKey(RetainPtr<SecKeyRef> privateKey);
    3737String base64PrivateKey(RetainPtr<SecKeyRef> privateKey);
     38RetainPtr<SecKeyRef> privateKeyFromBase64(const String& base64PrivateKey);
     39Vector<uint8_t> signatureForPrivateKey(RetainPtr<SecKeyRef> privateKey, const Vector<uint8_t>& authData, const Vector<uint8_t>& clientDataHash);
    3840
    3941} // namespace WebKit
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualAuthenticatorUtils.mm

    r291423 r291624  
    3333#include <WebCore/WebAuthenticationUtils.h>
    3434#include <pal/crypto/CryptoDigest.h>
     35#include <wtf/cocoa/TypeCastsCocoa.h>
     36#include <wtf/cocoa/VectorCocoa.h>
    3537
    3638namespace WebKit {
     
    110112}
    111113
     114RetainPtr<SecKeyRef> privateKeyFromBase64(const String& base64PrivateKey)
     115{
     116    NSDictionary* options = @{
     117        (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
     118        (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
     119        (id)kSecAttrKeySizeInBits: @256,
     120    };
     121    RetainPtr<NSData> privateKey = adoptNS([[NSData alloc] initWithBase64EncodedString:base64PrivateKey options:0]);
     122    CFErrorRef errorRef = nullptr;
     123    auto key = adoptCF(SecKeyCreateWithData(
     124        bridge_cast(privateKey.get()),
     125        bridge_cast(options),
     126        &errorRef
     127    ));
     128    ASSERT(!errorRef);
     129    return key;
     130}
     131
     132Vector<uint8_t> signatureForPrivateKey(RetainPtr<SecKeyRef> privateKey, const Vector<uint8_t>& authData, const Vector<uint8_t>& clientDataHash)
     133{
     134    NSMutableData *dataToSign = [NSMutableData dataWithBytes:authData.data() length:authData.size()];
     135    [dataToSign appendBytes:clientDataHash.data() length:clientDataHash.size()];
     136    RetainPtr<CFDataRef> signature;
     137    {
     138        CFErrorRef errorRef = nullptr;
     139        signature = adoptCF(SecKeyCreateSignature((__bridge SecKeyRef)((id)privateKey.get()), kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (__bridge CFDataRef)dataToSign, &errorRef));
     140        auto retainError = adoptCF(errorRef);
     141        ASSERT(!errorRef);
     142    }
     143
     144    return vectorFromNSData((NSData *)signature.get());
     145}
     146
    112147} // namespace WebKit
    113148
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Virtual/VirtualHidConnection.cpp

    r291423 r291624  
    213213            }
    214214            credential.userHandle = it->second.getByteString();
    215 
     215            auto credentialIdAndCosePubKey = credentialIdAndCosePubKeyForPrivateKey(privateKey);
     216            credential.credentialId = credentialIdAndCosePubKey.first;
    216217            manager->addCredential(m_authenticatorId, credential);
    217             auto credentialIdAndCosePubKey = credentialIdAndCosePubKeyForPrivateKey(privateKey);
    218218
    219219            auto attestedCredentialData = buildAttestedCredentialData(Vector<uint8_t>(aaguidLength, 0), credentialIdAndCosePubKey.first, credentialIdAndCosePubKey.second);
     
    221221            auto authenticatorData = buildAuthData(credential.rpId, flagsForConfig(m_configuration), credential.signCount, attestedCredentialData);
    222222            CBORValue::MapValue response;
     223            response[CBORValue(1)] = CBORValue("none");
    223224            response[CBORValue(2)] = CBORValue(authenticatorData);
    224 
    225225            auto attObj = buildAttestationMap(WTFMove(authenticatorData), "", { }, AttestationConveyancePreference::None);
    226 
    227             response[CBORValue(1)] = CBORValue("none");
    228226            response[CBORValue(3)] = CBORValue(attObj);
    229227            auto payload = CBORWriter::write(CBORValue(response));
     
    235233            auto message = FidoHidMessage::create(m_requestMessage->channelId(), FidoHidDeviceCommand::kCbor, WTFMove(buffer));
    236234            receiveHidMessage(WTFMove(*message));
     235        } else if (cmd == CtapRequestCommand::kAuthenticatorGetAssertion) {
     236            auto it = requestMap->getMap().find(CBORValue(kCtapGetAssertionRpIdKey));
     237            if (it == requestMap->getMap().end()) {
     238                recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrMissingParameter);
     239                return;
     240            }
     241            auto rpId = it->second.getString();
     242            (void)rpId;
     243            it = requestMap->getMap().find(CBORValue(kCtapGetAssertionClientDataHashKey));
     244            if (it == requestMap->getMap().end()) {
     245                recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrMissingParameter);
     246                return;
     247            }
     248            auto clientDataHash = it->second.getByteString();
     249            (void)clientDataHash;
     250            Vector<Vector<uint8_t>> allowList;
     251            it = requestMap->getMap().find(CBORValue(kCtapGetAssertionAllowListKey));
     252            if (it != requestMap->getMap().end()) {
     253                const auto& cborAllowList = it->second.getArray();
     254                if (cborAllowList.isEmpty()) {
     255                    recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrInvalidOption);
     256                    return;
     257                }
     258
     259                for (const auto& cborCredential : cborAllowList) {
     260                    auto& credMap = cborCredential.getMap();
     261                    auto itr = credMap.find(CBORValue(fido::kEntityIdMapKey));
     262                    if (itr == credMap.end() || !itr->second.isByteString()) {
     263                        recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrInvalidOption);
     264                        return;
     265                    }
     266                    allowList.append(itr->second.getByteString());
     267                }
     268            }
     269
     270            it = requestMap->getMap().find(CBORValue(kCtapGetAssertionRequestOptionsKey));
     271            if (it != requestMap->getMap().end()) {
     272                auto& optionMap = it->second.getMap();
     273
     274                auto itr = optionMap.find(CBORValue(kUserVerificationMapKey));
     275                if (itr != optionMap.end()) {
     276                    auto requireUserVerification = itr->second.getBool();
     277                    if (requireUserVerification && !m_configuration.hasUserVerification) {
     278                        recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrInvalidOption);
     279                        return;
     280                    }
     281                    if (requireUserVerification && !m_configuration.isUserVerified) {
     282                        recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrOperationDenied);
     283                        return;
     284                    }
     285                }
     286
     287                itr = optionMap.find(CBORValue(kUserPresenceMapKey));
     288                if (itr != optionMap.end()) {
     289                    auto requireUserPresence = itr->second.getBool();
     290                    if (requireUserPresence && !m_configuration.isUserConsenting) {
     291                        recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrOperationDenied);
     292                        return;
     293                    }
     294                }
     295            }
     296            auto matchingCredentials = m_manager->credentialsMatchingList(m_authenticatorId, rpId, allowList);
     297            if (matchingCredentials.isEmpty()) {
     298                recieveResponseCode(CtapDeviceResponseCode::kCtap2ErrNoCredentials);
     299                return;
     300            }
     301            auto& credential = matchingCredentials[0];
     302            CBORValue::MapValue response;
     303
     304            response[CBORValue(1)] = CBORValue(buildCredentialDescriptor(credential.credentialId));
     305            auto key = privateKeyFromBase64(credential.privateKey);
     306            auto credentialIdAndCosePubKey = credentialIdAndCosePubKeyForPrivateKey(key);
     307            auto attestedCredentialData = buildAttestedCredentialData(Vector<uint8_t>(aaguidLength, 0), credentialIdAndCosePubKey.first, credentialIdAndCosePubKey.second);
     308            auto authData = buildAuthData(rpId, flagsForConfig(m_configuration), credential.signCount, attestedCredentialData);
     309            response[CBORValue(2)] = CBORValue(authData);
     310            response[CBORValue(3)] = CBORValue(signatureForPrivateKey(key, authData, clientDataHash));
     311            if (credential.userHandle)
     312                response[CBORValue(4)] = CBORValue(buildUserEntityMap(*credential.userHandle, "", ""));
     313            response[CBORValue(5)] = CBORValue((int64_t)matchingCredentials.size());
     314            auto payload = CBORWriter::write(CBORValue(response));
     315            Vector<uint8_t> buffer;
     316            buffer.reserveCapacity(payload->size() + 1);
     317            buffer.append(static_cast<uint8_t>(fido::CtapDeviceResponseCode::kSuccess));
     318            buffer.appendVector(*payload);
     319
     320            auto message = FidoHidMessage::create(m_requestMessage->channelId(), FidoHidDeviceCommand::kCbor, WTFMove(buffer));
     321            receiveHidMessage(WTFMove(*message));
    237322        }
    238323        break;
Note: See TracChangeset for help on using the changeset viewer.