void U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent, const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aTransactionInfo) { MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister")); ClearTransaction(); mTransactionParent = aTransactionParent; mTokenManagerImpl = GetTokenManagerImpl(); if (!mTokenManagerImpl) { AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR); return; } mLastTransactionId = aTransactionId; // Determine whether direct attestation was requested. bool directAttestationRequested = false; if (aTransactionInfo.Extra().type() == WebAuthnMaybeMakeCredentialExtraInfo::TWebAuthnMakeCredentialExtraInfo) { const auto& extra = aTransactionInfo.Extra().get_WebAuthnMakeCredentialExtraInfo(); directAttestationRequested = extra.RequestDirectAttestation(); } // Start a register request immediately if direct attestation // wasn't requested or the test pref is set. if (!directAttestationRequested || U2FPrefManager::Get()->GetAllowDirectAttestationForTesting()) { // Force "none" attestation when "direct" attestation wasn't requested. DoRegister(aTransactionInfo, !directAttestationRequested); return; } // If the RP request direct attestation, ask the user for permission and // store the transaction info until the user proceeds or cancels. NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin()); SendPromptNotification(kRegisterDirectPromptNotifcation, aTransactionId, origin.get()); MOZ_ASSERT(mPendingRegisterInfo.isNothing()); mPendingRegisterInfo = Some(aTransactionInfo); }
void U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent, const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aTransactionInfo) { MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister")); ClearTransaction(); mTransactionParent = aTransactionParent; mTokenManagerImpl = GetTokenManagerImpl(); if (!mTokenManagerImpl) { AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR); return; } // Check if all the supplied parameters are syntactically well-formed and // of the correct length. If not, return an error code equivalent to // UnknownError and terminate the operation. if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) || (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) { AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR); return; } mLastTransactionId = aTransactionId; // If the RP request direct attestation, ask the user for permission and // store the transaction info until the user proceeds or cancels. // Might be overriden by a pref for testing purposes. if (aTransactionInfo.RequestDirectAttestation() && !U2FPrefManager::Get()->GetAllowDirectAttestationForTesting()) { NS_ConvertUTF16toUTF8 origin(aTransactionInfo.Origin()); SendPromptNotification(kRegisterDirectPromptNotifcation, aTransactionId, origin.get()); MOZ_ASSERT(mPendingRegisterInfo.isNothing()); mPendingRegisterInfo = Some(aTransactionInfo); } else { DoRegister(aTransactionInfo); } }
void U2FTokenManager::DoRegister(const WebAuthnMakeCredentialInfo& aInfo) { mozilla::ipc::AssertIsOnBackgroundThread(); MOZ_ASSERT(mLastTransactionId > 0); // Show a prompt that lets the user cancel the ongoing transaction. NS_ConvertUTF16toUTF8 origin(aInfo.Origin()); SendPromptNotification(kRegisterPromptNotifcation, mLastTransactionId, origin.get()); uint64_t tid = mLastTransactionId; mozilla::TimeStamp startTime = mozilla::TimeStamp::Now(); bool requestDirectAttestation = aInfo.RequestDirectAttestation(); mTokenManagerImpl ->Register(aInfo) ->Then(GetCurrentThreadSerialEventTarget(), __func__, [tid, startTime, requestDirectAttestation](WebAuthnMakeCredentialResult&& aResult) { U2FTokenManager* mgr = U2FTokenManager::Get(); // The token manager implementations set DirectAttestationPermitted // to false by default. Override this here with information from // the JS prompt. aResult.DirectAttestationPermitted() = requestDirectAttestation; mgr->MaybeConfirmRegister(tid, aResult); Telemetry::ScalarAdd( Telemetry::ScalarID::SECURITY_WEBAUTHN_USED, NS_LITERAL_STRING("U2FRegisterFinish"), 1); Telemetry::AccumulateTimeDelta( Telemetry::WEBAUTHN_CREATE_CREDENTIAL_MS, startTime); }, [tid](nsresult rv) { MOZ_ASSERT(NS_FAILED(rv)); U2FTokenManager* mgr = U2FTokenManager::Get(); mgr->MaybeAbortRegister(tid, rv); Telemetry::ScalarAdd( Telemetry::ScalarID::SECURITY_WEBAUTHN_USED, NS_LITERAL_STRING("U2FRegisterAbort"), 1); }) ->Track(mRegisterPromise); }
void U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent, const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aTransactionInfo) { MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister")); ClearTransaction(); mTransactionParent = aTransactionParent; mTokenManagerImpl = GetTokenManagerImpl(); if (!mTokenManagerImpl) { AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR); return; } // Check if all the supplied parameters are syntactically well-formed and // of the correct length. If not, return an error code equivalent to // UnknownError and terminate the operation. if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) || (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) { AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR); return; } uint64_t tid = mLastTransactionId = aTransactionId; mozilla::TimeStamp startTime = mozilla::TimeStamp::Now(); mTokenManagerImpl->Register(aTransactionInfo.ExcludeList(), aTransactionInfo.AuthenticatorSelection(), aTransactionInfo.RpIdHash(), aTransactionInfo.ClientDataHash(), aTransactionInfo.TimeoutMS()) ->Then(GetCurrentThreadSerialEventTarget(), __func__, [tid, startTime](U2FRegisterResult&& aResult) { U2FTokenManager* mgr = U2FTokenManager::Get(); mgr->MaybeConfirmRegister(tid, aResult); Telemetry::ScalarAdd( Telemetry::ScalarID::SECURITY_WEBAUTHN_USED, NS_LITERAL_STRING("U2FRegisterFinish"), 1); Telemetry::AccumulateTimeDelta( Telemetry::WEBAUTHN_CREATE_CREDENTIAL_MS, startTime); }, [tid](nsresult rv) { MOZ_ASSERT(NS_FAILED(rv)); U2FTokenManager* mgr = U2FTokenManager::Get(); mgr->MaybeAbortRegister(tid, rv); Telemetry::ScalarAdd( Telemetry::ScalarID::SECURITY_WEBAUTHN_USED, NS_LITERAL_STRING("U2FRegisterAbort"), 1); }) ->Track(mRegisterPromise); }
void WinWebAuthnManager::Register( PWebAuthnTransactionParent* aTransactionParent, const uint64_t& aTransactionId, const WebAuthnMakeCredentialInfo& aInfo) { MOZ_LOG(gWinWebAuthnManagerLog, LogLevel::Debug, ("WinWebAuthNRegister")); ClearTransaction(); mTransactionParent = aTransactionParent; BYTE U2FUserId = 0x01; // RP Information WEBAUTHN_RP_ENTITY_INFORMATION rpInfo = { WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, aInfo.RpId().get(), nullptr, nullptr}; // User Information WEBAUTHN_USER_ENTITY_INFORMATION userInfo = { WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION, 0, nullptr, nullptr, nullptr, nullptr}; // Client Data WEBAUTHN_CLIENT_DATA WebAuthNClientData = { WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, aInfo.ClientDataJSON().Length(), (BYTE*)(aInfo.ClientDataJSON().get()), WEBAUTHN_HASH_ALGORITHM_SHA_256}; // Algorithms nsTArray<WEBAUTHN_COSE_CREDENTIAL_PARAMETER> coseParams; // User Verification Requirement DWORD winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; // Attachment DWORD winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; // Resident Key BOOL winRequireResidentKey = FALSE; // AttestationConveyance DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY; if (aInfo.Extra().type() == WebAuthnMaybeMakeCredentialExtraInfo::TWebAuthnMakeCredentialExtraInfo) { const auto& extra = aInfo.Extra().get_WebAuthnMakeCredentialExtraInfo(); rpInfo.pwszName = extra.Rp().Name().get(); rpInfo.pwszIcon = extra.Rp().Icon().get(); userInfo.cbId = static_cast<DWORD>(extra.User().Id().Length()); userInfo.pbId = const_cast<unsigned char*>(extra.User().Id().Elements()); userInfo.pwszName = extra.User().Name().get(); userInfo.pwszIcon = extra.User().Icon().get(); userInfo.pwszDisplayName = extra.User().DisplayName().get(); for (const auto& coseAlg : extra.coseAlgs()) { WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = { WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION, WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, coseAlg.alg()}; coseParams.AppendElement(coseAlgorithm); } const auto& sel = extra.AuthenticatorSelection(); UserVerificationRequirement userVerificationReq = static_cast<UserVerificationRequirement>( sel.userVerificationRequirement()); switch (userVerificationReq) { case UserVerificationRequirement::Required: winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; break; case UserVerificationRequirement::Preferred: winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; break; case UserVerificationRequirement::Discouraged: winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; break; default: winUserVerificationReq = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; break; } if (sel.authenticatorAttachment().type() == WebAuthnMaybeAuthenticatorAttachment::Tuint8_t) { const AuthenticatorAttachment authenticatorAttachment = static_cast<AuthenticatorAttachment>( sel.authenticatorAttachment().get_uint8_t()); switch (authenticatorAttachment) { case AuthenticatorAttachment::Platform: winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM; break; case AuthenticatorAttachment::Cross_platform: winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM; break; default: break; } } winRequireResidentKey = sel.requireResidentKey(); // AttestationConveyance AttestationConveyancePreference attestation = static_cast<AttestationConveyancePreference>( extra.attestationConveyancePreference()); DWORD winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY; switch (attestation) { case AttestationConveyancePreference::Direct: winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; break; case AttestationConveyancePreference::Indirect: winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT; break; case AttestationConveyancePreference::None: winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE; break; default: winAttestation = WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ANY; break; } } else { userInfo.cbId = sizeof(BYTE); userInfo.pbId = &U2FUserId; WEBAUTHN_COSE_CREDENTIAL_PARAMETER coseAlgorithm = { WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION, WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256}; coseParams.AppendElement(coseAlgorithm); winAttachment = WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM_U2F_V2; } WEBAUTHN_COSE_CREDENTIAL_PARAMETERS WebAuthNCredentialParameters = { static_cast<DWORD>(coseParams.Length()), coseParams.Elements()}; // Exclude Credentials nsTArray<WEBAUTHN_CREDENTIAL_EX> excludeCredentials; WEBAUTHN_CREDENTIAL_EX* pExcludeCredentials = nullptr; nsTArray<WEBAUTHN_CREDENTIAL_EX*> excludeCredentialsPtrs; WEBAUTHN_CREDENTIAL_LIST excludeCredentialList = {0}; WEBAUTHN_CREDENTIAL_LIST* pExcludeCredentialList = nullptr; for (auto& cred : aInfo.ExcludeList()) { uint8_t transports = cred.transports(); DWORD winTransports = 0; if (transports & U2F_AUTHENTICATOR_TRANSPORT_USB) { winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB; } if (transports & U2F_AUTHENTICATOR_TRANSPORT_NFC) { winTransports |= WEBAUTHN_CTAP_TRANSPORT_NFC; } if (transports & U2F_AUTHENTICATOR_TRANSPORT_BLE) { winTransports |= WEBAUTHN_CTAP_TRANSPORT_BLE; } if (transports & CTAP_AUTHENTICATOR_TRANSPORT_INTERNAL) { winTransports |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL; } WEBAUTHN_CREDENTIAL_EX credential = { WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION, static_cast<DWORD>(cred.id().Length()), (PBYTE)(cred.id().Elements()), WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY, winTransports}; excludeCredentials.AppendElement(credential); } if (!excludeCredentials.IsEmpty()) { pExcludeCredentials = excludeCredentials.Elements(); for (DWORD i = 0; i < excludeCredentials.Length(); i++) { excludeCredentialsPtrs.AppendElement(&pExcludeCredentials[i]); } excludeCredentialList.cCredentials = excludeCredentials.Length(); excludeCredentialList.ppCredentials = excludeCredentialsPtrs.Elements(); pExcludeCredentialList = &excludeCredentialList; } // MakeCredentialOptions WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS WebAuthNCredentialOptions = { WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_CURRENT_VERSION, aInfo.TimeoutMS(), {0, NULL}, {0, NULL}, winAttachment, winRequireResidentKey, winUserVerificationReq, winAttestation, 0, // Flags NULL, // CancellationId pExcludeCredentialList}; GUID cancellationId = {0}; if (gWinWebauthnGetCancellationId(&cancellationId) == S_OK) { WebAuthNCredentialOptions.pCancellationId = &cancellationId; mCancellationIds.emplace(aTransactionId, &cancellationId); } WEBAUTHN_CREDENTIAL_ATTESTATION* pWebAuthNCredentialAttestation = nullptr; // Bug 1518876: Get Window Handle from Content process for Windows WebAuthN // APIs HWND hWnd = GetForegroundWindow(); HRESULT hr = gWinWebauthnMakeCredential( hWnd, &rpInfo, &userInfo, &WebAuthNCredentialParameters, &WebAuthNClientData, &WebAuthNCredentialOptions, &pWebAuthNCredentialAttestation); mCancellationIds.erase(aTransactionId); if (hr == S_OK) { nsTArray<uint8_t> attObject; attObject.AppendElements( pWebAuthNCredentialAttestation->pbAttestationObject, pWebAuthNCredentialAttestation->cbAttestationObject); nsTArray<uint8_t> credentialId; credentialId.AppendElements(pWebAuthNCredentialAttestation->pbCredentialId, pWebAuthNCredentialAttestation->cbCredentialId); nsTArray<uint8_t> authenticatorData; if (aInfo.Extra().type() == WebAuthnMaybeMakeCredentialExtraInfo:: TWebAuthnMakeCredentialExtraInfo) { authenticatorData.AppendElements( pWebAuthNCredentialAttestation->pbAuthenticatorData, pWebAuthNCredentialAttestation->cbAuthenticatorData); } else { PWEBAUTHN_COMMON_ATTESTATION attestation = reinterpret_cast<PWEBAUTHN_COMMON_ATTESTATION>( pWebAuthNCredentialAttestation->pvAttestationDecode); DWORD coseKeyOffset = 32 + // RPIDHash 1 + // Flags 4 + // Counter 16 + // AAGuid 2 + // Credential ID Length field pWebAuthNCredentialAttestation->cbCredentialId; // Hardcoding as couldn't finder decoder and it is an ECC key. DWORD xOffset = coseKeyOffset + 10; DWORD yOffset = coseKeyOffset + 45; // Authenticator Data length check. if (pWebAuthNCredentialAttestation->cbAuthenticatorData < yOffset + 32) { MaybeAbortRegister(aTransactionId, NS_ERROR_DOM_INVALID_STATE_ERR); } authenticatorData.AppendElement(0x05); // Reserved Byte authenticatorData.AppendElement(0x04); // ECC Uncompressed Key authenticatorData.AppendElements( pWebAuthNCredentialAttestation->pbAuthenticatorData + xOffset, 32); // X Coordinate authenticatorData.AppendElements( pWebAuthNCredentialAttestation->pbAuthenticatorData + yOffset, 32); // Y Coordinate authenticatorData.AppendElement( pWebAuthNCredentialAttestation->cbCredentialId); authenticatorData.AppendElements( pWebAuthNCredentialAttestation->pbCredentialId, pWebAuthNCredentialAttestation->cbCredentialId); authenticatorData.AppendElements(attestation->pX5c->pbData, attestation->pX5c->cbData); authenticatorData.AppendElements(attestation->pbSignature, attestation->cbSignature); } WebAuthnMakeCredentialResult result(aInfo.ClientDataJSON(), attObject, credentialId, authenticatorData); Unused << mTransactionParent->SendConfirmRegister(aTransactionId, result); ClearTransaction(); gWinWebauthnFreeCredentialAttestation(pWebAuthNCredentialAttestation); } else { PCWSTR errorName = gWinWebauthnGetErrorName(hr); nsresult aError = NS_ERROR_DOM_ABORT_ERR; if (_wcsicmp(errorName, L"InvalidStateError") == 0) { aError = NS_ERROR_DOM_INVALID_STATE_ERR; } else if (_wcsicmp(errorName, L"ConstraintError") == 0 || _wcsicmp(errorName, L"UnknownError") == 0) { aError = NS_ERROR_DOM_UNKNOWN_ERR; } else if (_wcsicmp(errorName, L"NotSupportedError") == 0) { aError = NS_ERROR_DOM_INVALID_STATE_ERR; } else if (_wcsicmp(errorName, L"NotAllowedError") == 0) { aError = NS_ERROR_DOM_NOT_ALLOWED_ERR; } MaybeAbortRegister(aTransactionId, aError); } }