--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3430,26 +3430,28 @@ ContentParent::RecvNSSU2FTokenIsCompatib
if (NS_FAILED(rv)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle,
+ nsTArray<uint8_t>&& aApplication,
bool* aIsValidKeyHandle)
{
MOZ_ASSERT(aIsValidKeyHandle);
nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
if (NS_WARN_IF(!nssToken)) {
return IPC_FAIL_NO_REASON(this);
}
nsresult rv = nssToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(),
+ aApplication.Elements(), aApplication.Length(),
aIsValidKeyHandle);
if (NS_FAILED(rv)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -798,16 +798,17 @@ private:
virtual mozilla::ipc::IPCResult
RecvPBlobConstructor(PBlobParent* aActor,
const BlobConstructorParams& params) override;
virtual mozilla::ipc::IPCResult RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion,
bool* aIsCompatible) override;
virtual mozilla::ipc::IPCResult RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle,
+ nsTArray<uint8_t>&& aApplication,
bool* aIsValidKeyHandle) override;
virtual mozilla::ipc::IPCResult RecvNSSU2FTokenRegister(nsTArray<uint8_t>&& aApplication,
nsTArray<uint8_t>&& aChallenge,
nsTArray<uint8_t>* aRegistration) override;
virtual mozilla::ipc::IPCResult RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication,
nsTArray<uint8_t>&& aChallenge,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -761,19 +761,20 @@ parent:
*/
sync NSSU2FTokenIsCompatibleVersion(nsString version)
returns (bool result);
/**
* Return whether the provided KeyHandle belongs to this Token
*
* |keyHandle| Key Handle to evaluate.
+ * |application| The FIDO Application data that is associated with this key.
* Returns |True| if the Key Handle is ours.
*/
- sync NSSU2FTokenIsRegistered(uint8_t[] keyHandle)
+ sync NSSU2FTokenIsRegistered(uint8_t[] keyHandle, uint8_t[] application)
returns (bool isValidKeyHandle);
/**
* Generates a public/private keypair for the provided application
* and challenge, returning the pubkey, challenge response, and
* key handle in the registration data.
*
* |application| The FIDO Application data to associate with the key.
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -206,19 +206,21 @@ U2FPrepTask::Execute()
// TODO: Use a thread pool here, but we have to solve the PContentChild issues
// of being in a worker thread.
mAbstractMainThread->Dispatch(r.forget());
return p;
}
U2FIsRegisteredTask::U2FIsRegisteredTask(const Authenticator& aAuthenticator,
const LocalRegisteredKey& aRegisteredKey,
+ const CryptoBuffer& aAppParam,
AbstractThread* aMainThread)
: U2FPrepTask(aAuthenticator, aMainThread)
, mRegisteredKey(aRegisteredKey)
+ , mAppParam(aAppParam)
{}
U2FIsRegisteredTask::~U2FIsRegisteredTask()
{}
NS_IMETHODIMP
U2FIsRegisteredTask::Run()
{
@@ -243,16 +245,17 @@ U2FIsRegisteredTask::Run()
return NS_ERROR_FAILURE;
}
// We ignore mTransports, as it is intended to be used for sorting the
// available devices by preference, but is not an exclusion factor.
bool isRegistered = false;
rv = mAuthenticator->IsRegistered(keyHandle.Elements(), keyHandle.Length(),
+ mAppParam.Elements(), mAppParam.Length(),
&isRegistered);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise.Reject(ErrorCode::OTHER_ERROR, __func__);
return NS_ERROR_FAILURE;
}
if (isRegistered) {
mPromise.Reject(ErrorCode::DEVICE_INELIGIBLE, __func__);
@@ -373,16 +376,17 @@ U2FSignTask::Run()
if (!isCompatible) {
mPromise.Reject(ErrorCode::BAD_REQUEST, __func__);
return NS_ERROR_FAILURE;
}
bool isRegistered = false;
rv = mAuthenticator->IsRegistered(mKeyHandle.Elements(), mKeyHandle.Length(),
+ mAppParam.Elements(), mAppParam.Length(),
&isRegistered);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise.Reject(ErrorCode::OTHER_ERROR, __func__);
return NS_ERROR_FAILURE;
}
if (!isRegistered) {
mPromise.Reject(ErrorCode::DEVICE_INELIGIBLE, __func__);
@@ -613,23 +617,40 @@ U2FRegisterRunnable::Run()
RefPtr<U2FStatus> status = new U2FStatus();
// Evaluate the AppID
ErrorCode appIdResult = EvaluateAppID();
if (appIdResult != ErrorCode::OK) {
status->Stop(appIdResult);
}
+ // Produce the AppParam from the current AppID
+ nsCString cAppId = NS_ConvertUTF16toUTF8(mAppId);
+ CryptoBuffer appParam;
+ if (!appParam.SetLength(SHA256_LENGTH, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Note: This could use nsICryptoHash to avoid having to interact with NSS
+ // directly.
+ SECStatus srv;
+ srv = PK11_HashBuf(SEC_OID_SHA256, appParam.Elements(),
+ reinterpret_cast<const uint8_t*>(cAppId.BeginReading()),
+ cAppId.Length());
+ if (srv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+
// First, we must determine if any of the RegisteredKeys are already
// registered, e.g., in the whitelist.
for (LocalRegisteredKey key : mRegisteredKeys) {
nsTArray<RefPtr<U2FPrepPromise>> prepPromiseList;
for (const Authenticator& token : mAuthenticators) {
RefPtr<U2FIsRegisteredTask> compTask =
- new U2FIsRegisteredTask(token, key, mAbstractMainThread);
+ new U2FIsRegisteredTask(token, key, appParam, mAbstractMainThread);
prepPromiseList.AppendElement(compTask->Execute());
}
// Treat each call to Promise::All as a work unit, as it completes together
status->WaitGroupAdd();
U2FPrepPromise::All(mAbstractMainThread, prepPromiseList)
->Then(mAbstractMainThread, __func__,
@@ -663,33 +684,16 @@ U2FRegisterRunnable::Run()
}
));
// Don't exit until the main thread runnable completes
status->WaitGroupWait();
return NS_OK;
}
- // Since we're continuing, we hash the AppID into the AppParam
- nsCString cAppId = NS_ConvertUTF16toUTF8(mAppId);
- CryptoBuffer appParam;
- if (!appParam.SetLength(SHA256_LENGTH, fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- // Note: This could use nsICryptoHash to avoid having to interact with NSS
- // directly.
- SECStatus srv;
- srv = PK11_HashBuf(SEC_OID_SHA256, appParam.Elements(),
- reinterpret_cast<const uint8_t*>(cAppId.BeginReading()),
- cAppId.Length());
- if (srv != SECSuccess) {
- return NS_ERROR_FAILURE;
- }
-
// Now proceed to actually register a new key.
for (LocalRegisterRequest req : mRegisterRequests) {
// Hash the ClientData into the ChallengeParam
CryptoBuffer challengeParam;
if (!challengeParam.SetLength(SHA256_LENGTH, fallible)) {
continue;
}
--- a/dom/u2f/U2F.h
+++ b/dom/u2f/U2F.h
@@ -81,23 +81,25 @@ protected:
// Determine whether the provided Authenticator already knows
// of the provided Registered Key.
class U2FIsRegisteredTask final : public U2FPrepTask
{
public:
U2FIsRegisteredTask(const Authenticator& aAuthenticator,
const LocalRegisteredKey& aRegisteredKey,
+ const CryptoBuffer& aAppParam,
AbstractThread* aMainThread);
NS_DECL_NSIRUNNABLE
private:
~U2FIsRegisteredTask();
LocalRegisteredKey mRegisteredKey;
+ CryptoBuffer mAppParam;
};
class U2FTask : public Runnable
{
public:
U2FTask(const nsAString& aOrigin,
const nsAString& aAppId,
const Authenticator& aAuthenticator,
--- a/dom/webauthn/NSSU2FTokenRemote.cpp
+++ b/dom/webauthn/NSSU2FTokenRemote.cpp
@@ -32,30 +32,38 @@ NSSU2FTokenRemote::IsCompatibleVersion(c
nsString(aVersionString), aIsCompatible)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
NSSU2FTokenRemote::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
+ uint8_t* aAppParam, uint32_t aAppParamLen,
bool* aIsRegistered)
{
NS_ENSURE_ARG_POINTER(aKeyHandle);
+ NS_ENSURE_ARG_POINTER(aAppParam);
NS_ENSURE_ARG_POINTER(aIsRegistered);
nsTArray<uint8_t> keyHandle;
if (!keyHandle.ReplaceElementsAt(0, keyHandle.Length(), aKeyHandle,
aKeyHandleLen)) {
return NS_ERROR_OUT_OF_MEMORY;
}
+ nsTArray<uint8_t> appParam;
+ if (!appParam.ReplaceElementsAt(0, appParam.Length(), aAppParam,
+ aAppParamLen)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
- if (!cc->SendNSSU2FTokenIsRegistered(keyHandle, aIsRegistered)) {
+ if (!cc->SendNSSU2FTokenIsRegistered(keyHandle, appParam, aIsRegistered)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
NSSU2FTokenRemote::Register(uint8_t* aApplication,
uint32_t aApplicationLen,
--- a/dom/webauthn/WebAuthentication.cpp
+++ b/dom/webauthn/WebAuthentication.cpp
@@ -375,17 +375,18 @@ WebAuthentication::U2FAuthMakeCredential
uint32_t len;
// data is owned by the Descriptor, do don't free it here.
if (NS_FAILED(ScopedCredentialGetData(scd, &data, &len))) {
aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
- nsresult rv = aToken->IsRegistered(data, len, &isRegistered);
+ nsresult rv = aToken->IsRegistered(data, len, aRpIdHash.Elements(),
+ aRpIdHash.Length(), &isRegistered);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRequest->SetFailure(rv);
return;
}
if (isRegistered) {
aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
return;
@@ -553,16 +554,18 @@ WebAuthentication::U2FAuthGetAssertion(c
// 4.1.2.8.a If the timer for adjustedTimeout expires, then for each entry
// in issuedRequests invoke the authenticatorCancel operation on that
// authenticator and remove its entry from the list.
for (CryptoBuffer& allowedCredential : aAllowList) {
bool isRegistered = false;
nsresult rv = aToken->IsRegistered(allowedCredential.Elements(),
allowedCredential.Length(),
+ aRpIdHash.Elements(),
+ aRpIdHash.Length(),
&isRegistered);
// 4.1.2.8.b If any authenticator returns a status indicating that the user
// cancelled the operation, delete that authenticator’s entry from
// issuedRequests. For each remaining entry in issuedRequests invoke the
// authenticatorCancel operation on that authenticator, and remove its entry
// from the list.
--- a/security/manager/ssl/nsIU2FToken.idl
+++ b/security/manager/ssl/nsIU2FToken.idl
@@ -18,20 +18,23 @@ interface nsIU2FToken : nsISupports {
* @return True if the offered version is compatible
*/
void isCompatibleVersion(in AString version, [retval] out boolean result);
/**
* Return whether the provided KeyHandle belongs to this Token
*
* @param keyHandle Key Handle to evaluate.
+ * @param application The FIDO Application data to associate with the key.
* @return True if the Key Handle is ours.
*/
void isRegistered([array, size_is(keyHandleLen)] in octet keyHandle,
in uint32_t keyHandleLen,
+ [array, size_is(applicationLen)] in octet application,
+ in uint32_t applicationLen,
[retval] out boolean result);
/**
* Generates a public/private keypair for the provided application
* and challenge, returning the pubkey, challenge response, and
* key handle in the registration data.
*
* @param application The FIDO Application data to associate with the key.
--- a/security/manager/ssl/nsNSSU2FToken.cpp
+++ b/security/manager/ssl/nsNSSU2FToken.cpp
@@ -41,25 +41,31 @@ NS_NAMED_LITERAL_CSTRING(kAttestCertSubj
// for the current profile. The attestation certificates that are produced are
// ephemeral to counteract profiling. They have little use for a soft-token
// at any rate, but are required by the specification.
const uint32_t kParamLen = 32;
const uint32_t kPublicKeyLen = 65;
const uint32_t kWrappedKeyBufLen = 256;
const uint32_t kWrappingKeyByteLen = 128/8;
+const uint32_t kSaltByteLen = 64/8;
+const uint32_t kVersion1KeyHandleLen = 162;
NS_NAMED_LITERAL_STRING(kEcAlgorithm, WEBCRYPTO_NAMED_CURVE_P256);
const PRTime kOneDay = PRTime(PR_USEC_PER_SEC)
* PRTime(60) // sec
* PRTime(60) // min
* PRTime(24); // hours
const PRTime kExpirationSlack = kOneDay; // Pre-date for clock skew
const PRTime kExpirationLife = kOneDay;
+enum SoftTokenHandle {
+ Version1 = 0,
+};
+
static mozilla::LazyLogModule gNSSTokenLog("webauth_u2f");
nsNSSU2FToken::nsNSSU2FToken()
: mInitialized(false)
{}
nsNSSU2FToken::~nsNSSU2FToken()
{
@@ -375,84 +381,181 @@ nsNSSU2FToken::Init()
}
mInitialized = true;
MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token initialized."));
return NS_OK;
}
// Convert a Private Key object into an opaque key handle, using AES Key Wrap
-// and aWrappingKey to convert aPrivKey.
+// with the long-lived aPersistentKey mixed with aAppParam to convert aPrivKey.
+// The key handle's format is version || saltLen || salt || wrappedPrivateKey
static UniqueSECItem
KeyHandleFromPrivateKey(const UniquePK11SlotInfo& aSlot,
- const UniquePK11SymKey& aWrappingKey,
+ const UniquePK11SymKey& aPersistentKey,
+ uint8_t* aAppParam, uint32_t aAppParamLen,
const UniqueSECKEYPrivateKey& aPrivKey,
const nsNSSShutDownPreventionLock&)
{
MOZ_ASSERT(aSlot);
- MOZ_ASSERT(aWrappingKey);
+ MOZ_ASSERT(aPersistentKey);
+ MOZ_ASSERT(aAppParam);
MOZ_ASSERT(aPrivKey);
- if (!aSlot || !aWrappingKey || !aPrivKey) {
+ if (!aSlot || !aPersistentKey || !aPrivKey || !aAppParam) {
+ return nullptr;
+ }
+
+ // Generate a random salt
+ uint8_t saltParam[kSaltByteLen];
+ SECStatus srv = PK11_GenerateRandomOnSlot(aSlot.get(), saltParam,
+ sizeof(saltParam));
+ if (srv != SECSuccess) {
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
+ ("Failed to generate a salt, NSS error #%d", PORT_GetError()));
+ return nullptr;
+ }
+
+ // Prepare the HKDF (https://tools.ietf.org/html/rfc5869)
+ CK_NSS_HKDFParams hkdfParams = { true, saltParam, sizeof(saltParam),
+ true, aAppParam, aAppParamLen };
+ SECItem kdfParams = { siBuffer, (unsigned char*)&hkdfParams,
+ sizeof(hkdfParams) };
+
+ // Derive a wrapping key from aPersistentKey, the salt, and the aAppParam.
+ // CKM_AES_KEY_GEN and CKA_WRAP are key type and usage attributes of the
+ // derived symmetric key and don't matter because we ignore them anyway.
+ UniquePK11SymKey wrapKey(PK11_Derive(aPersistentKey.get(), CKM_NSS_HKDF_SHA256,
+ &kdfParams, CKM_AES_KEY_GEN, CKA_WRAP,
+ kWrappingKeyByteLen));
+ if (!wrapKey.get()) {
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
+ ("Failed to derive a wrapping key, NSS error #%d", PORT_GetError()));
return nullptr;
}
UniqueSECItem wrappedKey(SECITEM_AllocItem(/* default arena */ nullptr,
/* no buffer */ nullptr,
kWrappedKeyBufLen));
if (!wrappedKey) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to allocate memory, NSS error #%d", PORT_GetError()));
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
return nullptr;
}
UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD,
/* default IV */ nullptr ));
- SECStatus srv = PK11_WrapPrivKey(aSlot.get(), aWrappingKey.get(),
- aPrivKey.get(), CKM_NSS_AES_KEY_WRAP_PAD,
- param.get(), wrappedKey.get(),
- /* wincx */ nullptr);
+ srv = PK11_WrapPrivKey(aSlot.get(), wrapKey.get(), aPrivKey.get(),
+ CKM_NSS_AES_KEY_WRAP_PAD, param.get(), wrappedKey.get(),
+ /* wincx */ nullptr);
if (srv != SECSuccess) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to wrap U2F key, NSS error #%d", PORT_GetError()));
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
+ ("Failed to wrap U2F key, NSS error #%d", PORT_GetError()));
+ return nullptr;
+ }
+
+ // Concatenate the salt and the wrapped Private Key together
+ mozilla::dom::CryptoBuffer keyHandleBuf;
+ if (!keyHandleBuf.SetCapacity(wrappedKey.get()->len + sizeof(saltParam) + 2,
+ mozilla::fallible)) {
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
return nullptr;
}
- return wrappedKey;
+ // It's OK to ignore the return values here because we're writing into
+ // pre-allocated space
+ keyHandleBuf.AppendElement(SoftTokenHandle::Version1, mozilla::fallible);
+ keyHandleBuf.AppendElement(sizeof(saltParam), mozilla::fallible);
+ keyHandleBuf.AppendElements(saltParam, sizeof(saltParam), mozilla::fallible);
+ keyHandleBuf.AppendSECItem(wrappedKey.get());
+
+ UniqueSECItem keyHandle(SECITEM_AllocItem(nullptr, nullptr, 0));
+ if (!keyHandle) {
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
+ return nullptr;
+ }
+
+ if (!keyHandleBuf.ToSECItem(/* default arena */ nullptr, keyHandle.get())) {
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
+ return nullptr;
+ }
+ return keyHandle;
}
// Convert an opaque key handle aKeyHandle back into a Private Key object, using
-// aWrappingKey and the AES Key Wrap algorithm.
+// the long-lived aPersistentKey mixed with aAppParam and the AES Key Wrap
+// algorithm.
static UniqueSECKEYPrivateKey
PrivateKeyFromKeyHandle(const UniquePK11SlotInfo& aSlot,
- const UniquePK11SymKey& aWrappingKey,
+ const UniquePK11SymKey& aPersistentKey,
uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
+ uint8_t* aAppParam, uint32_t aAppParamLen,
const nsNSSShutDownPreventionLock&)
{
MOZ_ASSERT(aSlot);
- MOZ_ASSERT(aWrappingKey);
+ MOZ_ASSERT(aPersistentKey);
MOZ_ASSERT(aKeyHandle);
- if (!aSlot || !aWrappingKey || !aKeyHandle) {
+ MOZ_ASSERT(aAppParam);
+ MOZ_ASSERT(aAppParamLen == SHA256_LENGTH);
+ if (!aSlot || !aPersistentKey || !aKeyHandle || !aAppParam ||
+ aAppParamLen != SHA256_LENGTH) {
+ return nullptr;
+ }
+
+ // As we only support one key format ourselves (right now), fail early if
+ // we aren't that length
+ if (aKeyHandleLen != kVersion1KeyHandleLen) {
+ return nullptr;
+ }
+
+ if (aKeyHandle[0] != SoftTokenHandle::Version1) {
+ // Unrecognized version
+ return nullptr;
+ }
+
+ uint8_t saltLen = aKeyHandle[1];
+ uint8_t* saltPtr = aKeyHandle + 2;
+ if (saltLen != kSaltByteLen) {
return nullptr;
}
- ScopedAutoSECItem pubKey(kPublicKeyLen);
+ // Prepare the HKDF (https://tools.ietf.org/html/rfc5869)
+ CK_NSS_HKDFParams hkdfParams = { true, saltPtr, saltLen,
+ true, aAppParam, aAppParamLen };
+ SECItem kdfParams = { siBuffer, (unsigned char*)&hkdfParams,
+ sizeof(hkdfParams) };
- ScopedAutoSECItem keyHandleItem(aKeyHandleLen);
- memcpy(keyHandleItem.data, aKeyHandle, keyHandleItem.len);
+ // Derive a wrapping key from aPersistentKey, the salt, and the aAppParam.
+ // CKM_AES_KEY_GEN and CKA_WRAP are key type and usage attributes of the
+ // derived symmetric key and don't matter because we ignore them anyway.
+ UniquePK11SymKey wrapKey(PK11_Derive(aPersistentKey.get(), CKM_NSS_HKDF_SHA256,
+ &kdfParams, CKM_AES_KEY_GEN, CKA_WRAP,
+ kWrappingKeyByteLen));
+ if (!wrapKey.get()) {
+ MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
+ ("Failed to derive a wrapping key, NSS error #%d", PORT_GetError()));
+ return nullptr;
+ }
+
+ uint8_t wrappedLen = aKeyHandleLen - saltLen - 2;
+ uint8_t* wrappedPtr = aKeyHandle + saltLen + 2;
+
+ ScopedAutoSECItem wrappedKeyItem(wrappedLen);
+ memcpy(wrappedKeyItem.data, wrappedPtr, wrappedKeyItem.len);
+
+ ScopedAutoSECItem pubKey(kPublicKeyLen);
UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD,
/* default IV */ nullptr ));
CK_ATTRIBUTE_TYPE usages[] = { CKA_SIGN };
int usageCount = 1;
UniqueSECKEYPrivateKey unwrappedKey(
- PK11_UnwrapPrivKey(aSlot.get(), aWrappingKey.get(), CKM_NSS_AES_KEY_WRAP_PAD,
- param.get(), &keyHandleItem,
+ PK11_UnwrapPrivKey(aSlot.get(), wrapKey.get(), CKM_NSS_AES_KEY_WRAP_PAD,
+ param.get(), &wrappedKeyItem,
/* no nickname */ nullptr,
/* discard pubkey */ &pubKey,
/* not permanent */ false,
/* non-exportable */ true,
CKK_EC, usages, usageCount,
/* wincx */ nullptr));
if (!unwrappedKey) {
// Not our key.
@@ -472,19 +575,21 @@ nsNSSU2FToken::IsCompatibleVersion(const
MOZ_ASSERT(mInitialized);
*aResult = (mVersion == aVersion);
return NS_OK;
}
// IsRegistered determines if the provided key handle is usable by this token.
NS_IMETHODIMP
nsNSSU2FToken::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
+ uint8_t* aAppParam, uint32_t aAppParamLen,
bool* aResult)
{
NS_ENSURE_ARG_POINTER(aKeyHandle);
+ NS_ENSURE_ARG_POINTER(aAppParam);
NS_ENSURE_ARG_POINTER(aResult);
if (!NS_IsMainThread()) {
NS_ERROR("nsNSSU2FToken::IsRegistered called off the main thread");
return NS_ERROR_NOT_SAME_THREAD;
}
nsNSSShutDownPreventionLock locker;
@@ -499,16 +604,18 @@ nsNSSU2FToken::IsRegistered(uint8_t* aKe
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
MOZ_ASSERT(slot.get());
// Decode the key handle
UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
aKeyHandle,
aKeyHandleLen,
+ aAppParam,
+ aAppParamLen,
locker);
*aResult = (privKey.get() != nullptr);
return NS_OK;
}
// A U2F Register operation causes a new key pair to be generated by the token.
// The token then returns the public key of the key pair, and a handle to the
// private key, which is a fancy way of saying "key wrapped private key", as
@@ -578,16 +685,18 @@ nsNSSU2FToken::Register(uint8_t* aApplic
UniqueSECKEYPublicKey pubKey;
rv = GenEcKeypair(slot, privKey, pubKey, locker);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE;
}
// The key handle will be the result of keywrap(privKey, key=mWrappingKey)
UniqueSECItem keyHandleItem = KeyHandleFromPrivateKey(slot, mWrappingKey,
+ aApplication,
+ aApplicationLen,
privKey, locker);
if (!keyHandleItem.get()) {
return NS_ERROR_FAILURE;
}
// Sign the challenge using the Attestation privkey (from attestCert)
mozilla::dom::CryptoBuffer signedDataBuf;
if (!signedDataBuf.SetCapacity(1 + aApplicationLen + aChallengeLen +
@@ -690,16 +799,18 @@ nsNSSU2FToken::Sign(uint8_t* aApplicatio
return NS_ERROR_ILLEGAL_VALUE;
}
// Decode the key handle
UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
aKeyHandle,
aKeyHandleLen,
+ aApplication,
+ aApplicationLen,
locker);
if (!privKey.get()) {
MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!"));
return NS_ERROR_FAILURE;
}
// Increment the counter and turn it into a SECItem
uint32_t counter = Preferences::GetUint(PREF_U2F_NSSTOKEN_COUNTER) + 1;