Bug 1379580 - U2FTokenTransport promises should resolve to U2F data buffers r=qDot

This commit is contained in:
Tim Taubert 2017-07-11 11:09:08 +02:00
parent db58bdb9ca
commit aac46d1b3e
7 changed files with 131 additions and 110 deletions

View File

@ -36,13 +36,12 @@ U2FHIDTokenManager::~U2FHIDTokenManager()
// ASN.1 attestation certificate
// * attestation signature
//
RefPtr<ResultPromise>
RefPtr<U2FRegisterPromise>
U2FHIDTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aRegistration)
const nsTArray<uint8_t>& aChallenge)
{
return ResultPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
}
// A U2F Sign operation creates a signature over the "param" arguments (plus
@ -61,14 +60,12 @@ U2FHIDTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>&
// 4 Counter
// * Signature
//
RefPtr<ResultPromise>
RefPtr<U2FSignPromise>
U2FHIDTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aKeyHandle,
/* out */ nsTArray<uint8_t>& aSignature)
const nsTArray<uint8_t>& aChallenge)
{
return ResultPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_NOT_IMPLEMENTED, __func__);
}
void

View File

@ -22,18 +22,15 @@ class U2FHIDTokenManager final : public U2FTokenTransport
public:
explicit U2FHIDTokenManager();
virtual RefPtr<ResultPromise>
virtual RefPtr<U2FRegisterPromise>
Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aRegistration) override;
const nsTArray<uint8_t>& aChallenge) override;
virtual RefPtr<ResultPromise>
virtual RefPtr<U2FSignPromise>
Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aKeyHandle,
/* out */ nsTArray<uint8_t>& aSignature) override;
const nsTArray<uint8_t>& aChallenge) override;
void Cancel() override;

View File

@ -633,21 +633,20 @@ U2FSoftTokenManager::IsRegistered(const nsTArray<uint8_t>& aKeyHandle,
// ASN.1 attestation certificate
// * attestation signature
//
RefPtr<ResultPromise>
RefPtr<U2FRegisterPromise>
U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aRegistration)
const nsTArray<uint8_t>& aChallenge)
{
nsNSSShutDownPreventionLock locker;
if (NS_WARN_IF(isAlreadyShutDown())) {
return ResultPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
}
if (!mInitialized) {
nsresult rv = Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return ResultPromise::CreateAndReject(rv, __func__);
return U2FRegisterPromise::CreateAndReject(rv, __func__);
}
}
@ -656,10 +655,10 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
bool isRegistered = false;
nsresult rv = IsRegistered(desc.id(), aApplication, isRegistered);
if (NS_FAILED(rv)) {
return ResultPromise::CreateAndReject(rv, __func__);
return U2FRegisterPromise::CreateAndReject(rv, __func__);
}
if (isRegistered) {
return ResultPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
}
}
@ -675,7 +674,7 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert,
locker);
if (NS_WARN_IF(NS_FAILED(rv))) {
return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
MOZ_ASSERT(attestCert);
MOZ_ASSERT(attestPrivKey);
@ -685,7 +684,7 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
UniqueSECKEYPublicKey pubKey;
rv = GenEcKeypair(slot, privKey, pubKey, locker);
if (NS_WARN_IF(NS_FAILED(rv))) {
return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// The key handle will be the result of keywrap(privKey, key=mWrappingKey)
@ -694,7 +693,7 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
aApplication.Length(),
privKey, locker);
if (NS_WARN_IF(!keyHandleItem.get())) {
return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// Sign the challenge using the Attestation privkey (from attestCert)
@ -702,7 +701,7 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + aApplication.Length() + aChallenge.Length() +
keyHandleItem->len + kPublicKeyLen,
mozilla::fallible))) {
return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
}
// // It's OK to ignore the return values here because we're writing into
@ -720,7 +719,7 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
if (NS_WARN_IF(srv != SECSuccess)) {
MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
("Signature failure: %d", PORT_GetError()));
return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// Serialize the registration data
@ -728,7 +727,7 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
if (NS_WARN_IF(!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len +
attestCert.get()->derCert.len +
signatureItem.len, mozilla::fallible))) {
return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
}
registrationBuf.AppendElement(0x05, mozilla::fallible);
registrationBuf.AppendSECItem(pubKey->u.ec.publicValue);
@ -736,9 +735,9 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
registrationBuf.AppendSECItem(keyHandleItem.get());
registrationBuf.AppendSECItem(attestCert.get()->derCert);
registrationBuf.AppendSECItem(signatureItem);
aRegistration = registrationBuf;
return ResultPromise::CreateAndResolve(NS_OK, __func__);
U2FRegisterResult result((nsTArray<uint8_t>(registrationBuf)));
return U2FRegisterPromise::CreateAndResolve(Move(result), __func__);
}
// A U2F Sign operation creates a signature over the "param" arguments (plus
@ -757,30 +756,29 @@ U2FSoftTokenManager::Register(const nsTArray<WebAuthnScopedCredentialDescriptor>
// 4 Counter
// * Signature
//
RefPtr<ResultPromise>
RefPtr<U2FSignPromise>
U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
nsTArray<uint8_t>& aKeyHandle,
nsTArray<uint8_t>& aSignature)
const nsTArray<uint8_t>& aChallenge)
{
nsNSSShutDownPreventionLock locker;
if (NS_WARN_IF(isAlreadyShutDown())) {
return ResultPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
}
nsTArray<uint8_t> keyHandle;
for (auto desc: aDescriptors) {
bool isRegistered = false;
nsresult rv = IsRegistered(desc.id(), aApplication, isRegistered);
if (NS_SUCCEEDED(rv) && isRegistered) {
aKeyHandle.Assign(desc.id());
keyHandle.Assign(desc.id());
break;
}
}
// Fail if we didn't recognize a key id.
if (aKeyHandle.IsEmpty()) {
return ResultPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
if (keyHandle.IsEmpty()) {
return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
}
MOZ_ASSERT(mWrappingKey);
@ -793,19 +791,19 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aD
("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
(uint32_t)aChallenge.Length(), (uint32_t)aApplication.Length(), kParamLen));
return ResultPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__);
}
// Decode the key handle
UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
const_cast<uint8_t*>(aKeyHandle.Elements()),
aKeyHandle.Length(),
const_cast<uint8_t*>(keyHandle.Elements()),
keyHandle.Length(),
const_cast<uint8_t*>(aApplication.Elements()),
aApplication.Length(),
locker);
if (NS_WARN_IF(!privKey.get())) {
MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!"));
return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// Increment the counter and turn it into a SECItem
@ -826,7 +824,7 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aD
// Compute the signature
mozilla::dom::CryptoBuffer signedDataBuf;
if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible))) {
return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
}
// It's OK to ignore the return values here because we're writing into
@ -843,7 +841,7 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aD
nsresult rv = Base64URLEncode(signedDataBuf.Length(), signedDataBuf.Elements(),
Base64URLEncodePaddingPolicy::Omit, base64);
if (NS_WARN_IF(NS_FAILED(rv))) {
return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
@ -857,14 +855,14 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aD
if (NS_WARN_IF(srv != SECSuccess)) {
MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
("Signature failure: %d", PORT_GetError()));
return ResultPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// Assemble the signature data into a buffer for return
mozilla::dom::CryptoBuffer signatureBuf;
if (NS_WARN_IF(!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
mozilla::fallible))) {
return ResultPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
}
// It's OK to ignore the return values here because we're writing into
@ -873,8 +871,8 @@ U2FSoftTokenManager::Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aD
signatureBuf.AppendSECItem(counterItem);
signatureBuf.AppendSECItem(signatureItem);
aSignature = signatureBuf;
return ResultPromise::CreateAndResolve(NS_OK, __func__);
U2FSignResult result(Move(keyHandle), nsTArray<uint8_t>(signatureBuf));
return U2FSignPromise::CreateAndResolve(Move(result), __func__);
}
void

View File

@ -25,18 +25,15 @@ class U2FSoftTokenManager final : public U2FTokenTransport,
public:
explicit U2FSoftTokenManager(uint32_t aCounter);
virtual RefPtr<ResultPromise>
virtual RefPtr<U2FRegisterPromise>
Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aRegistration) override;
const nsTArray<uint8_t>& aChallenge) override;
virtual RefPtr<ResultPromise>
virtual RefPtr<U2FSignPromise>
Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aKeyHandle,
/* out */ nsTArray<uint8_t>& aSignature) override;
const nsTArray<uint8_t>& aChallenge) override;
virtual void Cancel() override;

View File

@ -191,8 +191,9 @@ U2FTokenManager::ClearTransaction()
mTransactionParent = nullptr;
// Drop managers at the end of all transactions
mTokenManagerImpl = nullptr;
// Drop promise.
mResultPromise = nullptr;
// Drop promises.
mRegisterPromise = nullptr;
mSignPromise = nullptr;
// Increase in case we're called by the WebAuthnTransactionParent.
mTransactionId++;
}
@ -251,34 +252,34 @@ U2FTokenManager::Register(WebAuthnTransactionParent* aTransactionParent,
return;
}
nsTArray<uint8_t> reg;
mResultPromise = mTokenManagerImpl->Register(aTransactionInfo.Descriptors(),
aTransactionInfo.RpIdHash(),
aTransactionInfo.ClientDataHash(),
reg);
mRegisterPromise = mTokenManagerImpl->Register(aTransactionInfo.Descriptors(),
aTransactionInfo.RpIdHash(),
aTransactionInfo.ClientDataHash());
mResultPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
[tid, reg](nsresult rv) {
MOZ_ASSERT(NS_SUCCEEDED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeConfirmRegister(tid, reg);
},
[tid](nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeAbortTransaction(tid, rv);
});
mRegisterPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
[tid](U2FRegisterResult&& aResult) {
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeConfirmRegister(tid, aResult);
},
[tid](nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeAbortTransaction(tid, rv);
});
}
void
U2FTokenManager::MaybeConfirmRegister(uint64_t aTransactionId,
const nsTArray<uint8_t>& aRegister)
U2FRegisterResult& aResult)
{
if (mTransactionId != aTransactionId) {
return;
}
Unused << mTransactionParent->SendConfirmRegister(aRegister);
nsTArray<uint8_t> registration;
aResult.ConsumeRegistration(registration);
Unused << mTransactionParent->SendConfirmRegister(registration);
ClearTransaction();
}
@ -304,37 +305,36 @@ U2FTokenManager::Sign(WebAuthnTransactionParent* aTransactionParent,
return;
}
nsTArray<uint8_t> id;
nsTArray<uint8_t> sig;
mResultPromise = mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(),
aTransactionInfo.RpIdHash(),
aTransactionInfo.ClientDataHash(),
id,
sig);
mSignPromise = mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(),
aTransactionInfo.RpIdHash(),
aTransactionInfo.ClientDataHash());
mResultPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
[tid, id, sig](nsresult rv) {
MOZ_ASSERT(NS_SUCCEEDED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeConfirmSign(tid, id, sig);
},
[tid](nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeAbortTransaction(tid, rv);
});
mSignPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
[tid](U2FSignResult&& aResult) {
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeConfirmSign(tid, aResult);
},
[tid](nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv));
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->MaybeAbortTransaction(tid, rv);
});
}
void
U2FTokenManager::MaybeConfirmSign(uint64_t aTransactionId,
const nsTArray<uint8_t>& aKeyHandle,
const nsTArray<uint8_t>& aSignature)
U2FSignResult& aResult)
{
if (mTransactionId != aTransactionId) {
return;
}
Unused << mTransactionParent->SendConfirmSign(aKeyHandle, aSignature);
nsTArray<uint8_t> keyHandle;
aResult.ConsumeKeyHandle(keyHandle);
nsTArray<uint8_t> signature;
aResult.ConsumeSignature(signature);
Unused << mTransactionParent->SendConfirmSign(keyHandle, signature);
ClearTransaction();
}

View File

@ -52,16 +52,15 @@ private:
void MaybeAbortTransaction(uint64_t aTransactionId,
const nsresult& aError);
void MaybeConfirmRegister(uint64_t aTransactionId,
const nsTArray<uint8_t>& aRegister);
void MaybeConfirmSign(uint64_t aTransactionId,
const nsTArray<uint8_t>& aKeyHandle,
const nsTArray<uint8_t>& aSignature);
U2FRegisterResult& aResult);
void MaybeConfirmSign(uint64_t aTransactionId, U2FSignResult& aResult);
// Using a raw pointer here, as the lifetime of the IPC object is managed by
// the PBackground protocol code. This means we cannot be left holding an
// invalid IPC protocol object after the transaction is finished.
WebAuthnTransactionParent* mTransactionParent;
RefPtr<U2FTokenTransport> mTokenManagerImpl;
RefPtr<ResultPromise> mResultPromise;
RefPtr<U2FRegisterPromise> mRegisterPromise;
RefPtr<U2FSignPromise> mSignPromise;
// Guards the asynchronous promise resolution of token manager impls.
// We don't need to protect this with a lock as it will only be modified
// and checked on the PBackground thread in the parent process.

View File

@ -18,7 +18,43 @@
namespace mozilla {
namespace dom {
typedef MozPromise<nsresult, nsresult, false> ResultPromise;
class U2FRegisterResult {
public:
explicit U2FRegisterResult(nsTArray<uint8_t>&& aRegistration)
: mRegistration(aRegistration)
{ }
void ConsumeRegistration(nsTArray<uint8_t>& aBuffer) {
aBuffer = Move(mRegistration);
}
private:
nsTArray<uint8_t> mRegistration;
};
class U2FSignResult {
public:
explicit U2FSignResult(nsTArray<uint8_t>&& aKeyHandle,
nsTArray<uint8_t>&& aSignature)
: mKeyHandle(aKeyHandle)
, mSignature(aSignature)
{ }
void ConsumeKeyHandle(nsTArray<uint8_t>& aBuffer) {
aBuffer = Move(mKeyHandle);
}
void ConsumeSignature(nsTArray<uint8_t>& aBuffer) {
aBuffer = Move(mSignature);
}
private:
nsTArray<uint8_t> mKeyHandle;
nsTArray<uint8_t> mSignature;
};
typedef MozPromise<U2FRegisterResult, nsresult, true> U2FRegisterPromise;
typedef MozPromise<U2FSignResult, nsresult, true> U2FSignPromise;
class U2FTokenTransport
{
@ -26,18 +62,15 @@ public:
NS_INLINE_DECL_REFCOUNTING(U2FTokenTransport);
U2FTokenTransport() {}
virtual RefPtr<ResultPromise>
virtual RefPtr<U2FRegisterPromise>
Register(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aRegistration) = 0;
const nsTArray<uint8_t>& aChallenge) = 0;
virtual RefPtr<ResultPromise>
virtual RefPtr<U2FSignPromise>
Sign(const nsTArray<WebAuthnScopedCredentialDescriptor>& aDescriptors,
const nsTArray<uint8_t>& aApplication,
const nsTArray<uint8_t>& aChallenge,
/* out */ nsTArray<uint8_t>& aKeyHandle,
/* out */ nsTArray<uint8_t>& aSignature) = 0;
const nsTArray<uint8_t>& aChallenge) = 0;
virtual void Cancel() = 0;