bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey, CK_ATTRIBUTE_TYPE aAttribute, Optional<nsString>& aDst) { ScopedSECItem item(::SECITEM_AllocItem(nullptr, nullptr, 0)); if (!item) { return false; } if (PK11_ReadRawAttribute(PK11_TypePrivKey, aKey, aAttribute, item) != SECSuccess) { return false; } CryptoBuffer buffer; if (!buffer.Assign(item)) { return false; } if (NS_FAILED(buffer.ToJwkBase64(aDst.Value()))) { return false; } return true; }
static nsresult AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge, /* out */ nsACString& aJsonOut) { MOZ_ASSERT(NS_IsMainThread()); nsString challengeBase64; nsresult rv = aChallenge.ToJwkBase64(challengeBase64); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } WebAuthnClientData clientDataObject; clientDataObject.mOrigin.Assign(aOrigin); clientDataObject.mHashAlg.SetAsString().Assign(NS_LITERAL_STRING("S256")); clientDataObject.mChallenge.Assign(challengeBase64); nsAutoString temp; if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) { return NS_ERROR_FAILURE; } aJsonOut.Assign(NS_ConvertUTF16toUTF8(temp)); return NS_OK; }
NS_IMETHODIMP U2FSignTask::Run() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } // Search the requests for one a token can fulfill for (size_t i = 0; i < mRegisteredKeys.Length(); i += 1) { RegisteredKey request(mRegisteredKeys[i]); // Check for required attributes if (!(request.mVersion.WasPassed() && request.mKeyHandle.WasPassed())) { continue; } // Do not permit an individual RegisteredKey to assert a different AppID if (request.mAppId.WasPassed() && mAppId != request.mAppId.Value()) { continue; } // Assemble a clientData object CryptoBuffer clientData; nsresult rv = AssembleClientData(mOrigin, kGetAssertion, mChallenge, clientData); if (NS_WARN_IF(NS_FAILED(rv))) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } // Hash the AppID and the ClientData into the AppParam and ChallengeParam SECStatus srv; nsCString cAppId = NS_ConvertUTF16toUTF8(mAppId); CryptoBuffer appParam; CryptoBuffer challengeParam; if (!appParam.SetLength(SHA256_LENGTH, fallible) || !challengeParam.SetLength(SHA256_LENGTH, fallible)) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } srv = PK11_HashBuf(SEC_OID_SHA256, appParam.Elements(), reinterpret_cast<const uint8_t*>(cAppId.BeginReading()), cAppId.Length()); if (srv != SECSuccess) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } srv = PK11_HashBuf(SEC_OID_SHA256, challengeParam.Elements(), clientData.Elements(), clientData.Length()); if (srv != SECSuccess) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } // Decode the key handle CryptoBuffer keyHandle; rv = keyHandle.FromJwkBase64(request.mKeyHandle.Value()); if (NS_WARN_IF(NS_FAILED(rv))) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } // Get the signature from the token CryptoBuffer signatureData; bool signSuccess = false; // We ignore mTransports, as it is intended to be used for sorting the // available devices by preference, but is not an exclusion factor. for (size_t a = 0; a < mAuthenticators.Length() && !signSuccess; ++a) { Authenticator token(mAuthenticators[a]); bool isCompatible = false; bool isRegistered = false; rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible); if (NS_FAILED(rv)) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } if (!isCompatible) { continue; } rv = token->IsRegistered(keyHandle.Elements(), keyHandle.Length(), &isRegistered); if (NS_FAILED(rv)) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } if (isCompatible && isRegistered) { uint8_t* buffer; uint32_t bufferlen; nsresult rv = token->Sign(appParam.Elements(), appParam.Length(), challengeParam.Elements(), challengeParam.Length(), keyHandle.Elements(), keyHandle.Length(), &buffer, &bufferlen); if (NS_FAILED(rv)) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } MOZ_ASSERT(buffer); signatureData.Assign(buffer, bufferlen); free(buffer); signSuccess = true; } } if (!signSuccess) { // Try another request continue; } // Assemble a response object to return nsString clientDataBase64, signatureDataBase64; nsresult rvClientData = clientData.ToJwkBase64(clientDataBase64); nsresult rvSignatureData = signatureData.ToJwkBase64(signatureDataBase64); if (NS_WARN_IF(NS_FAILED(rvClientData)) || NS_WARN_IF(NS_FAILED(rvSignatureData))) { ReturnError(ErrorCode::OTHER_ERROR); return NS_ERROR_FAILURE; } SignResponse response; response.mKeyHandle.Construct(request.mKeyHandle.Value()); response.mClientData.Construct(clientDataBase64); response.mSignatureData.Construct(signatureDataBase64); response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK)); ErrorResult result; mCallback->Call(response, result); NS_WARN_IF(result.Failed()); // Useful exceptions already got reported. result.SuppressException(); return NS_OK; } // Nothing could satisfy ReturnError(ErrorCode::DEVICE_INELIGIBLE); return NS_ERROR_FAILURE; }