123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsNSSU2FToken.h"
- #include "CryptoBuffer.h"
- #include "mozilla/Casting.h"
- #include "nsNSSComponent.h"
- #include "pk11pub.h"
- #include "prerror.h"
- #include "secerr.h"
- #include "WebCryptoCommon.h"
- using namespace mozilla;
- using mozilla::dom::CreateECParamsForCurve;
- NS_IMPL_ISUPPORTS(nsNSSU2FToken, nsIU2FToken, nsINSSU2FToken)
- // Not named "security.webauth.u2f_softtoken_counter" because setting that
- // name causes the window.u2f object to disappear until preferences get
- // reloaded, as its' pref is a substring!
- #define PREF_U2F_NSSTOKEN_COUNTER "security.webauth.softtoken_counter"
- const nsCString nsNSSU2FToken::mSecretNickname =
- NS_LITERAL_CSTRING("U2F_NSSTOKEN");
- const nsString nsNSSU2FToken::mVersion =
- NS_LITERAL_STRING("U2F_V2");
- NS_NAMED_LITERAL_CSTRING(kAttestCertSubjectName, "CN=Firefox U2F Soft Token");
- // This U2F-compatible soft token uses FIDO U2F-compatible ECDSA keypairs
- // on the SEC_OID_SECG_EC_SECP256R1 curve. When asked to Register, it will
- // generate and return a new keypair KP, where the private component is wrapped
- // using AES-KW with the 128-bit mWrappingKey to make an opaque "key handle".
- // In other words, Register yields { KP_pub, AES-KW(KP_priv, key=mWrappingKey) }
- //
- // The value mWrappingKey is long-lived; it is persisted as part of the NSS DB
- // 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;
- 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;
- static mozilla::LazyLogModule gNSSTokenLog("webauth_u2f");
- nsNSSU2FToken::nsNSSU2FToken()
- : mInitialized(false)
- {}
- nsNSSU2FToken::~nsNSSU2FToken()
- {
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown()) {
- return;
- }
- destructorSafeDestroyNSSReference();
- shutdown(ShutdownCalledFrom::Object);
- }
- void
- nsNSSU2FToken::virtualDestroyNSSReference()
- {
- destructorSafeDestroyNSSReference();
- }
- void
- nsNSSU2FToken::destructorSafeDestroyNSSReference()
- {
- mWrappingKey = nullptr;
- }
- /**
- * Gets the first key with the given nickname from the given slot. Any other
- * keys found are not returned.
- * PK11_GetNextSymKey() should not be called on the returned key.
- *
- * @param aSlot Slot to search.
- * @param aNickname Nickname the key should have.
- * @return The first key found. nullptr if no key could be found.
- */
- static UniquePK11SymKey
- GetSymKeyByNickname(const UniquePK11SlotInfo& aSlot,
- const nsCString& aNickname,
- const nsNSSShutDownPreventionLock&)
- {
- MOZ_ASSERT(aSlot);
- if (!aSlot) {
- return nullptr;
- }
- MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
- ("Searching for a symmetric key named %s", aNickname.get()));
- UniquePK11SymKey keyListHead(
- PK11_ListFixedKeysInSlot(aSlot.get(), const_cast<char*>(aNickname.get()),
- /* wincx */ nullptr));
- if (!keyListHead) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key not found."));
- return nullptr;
- }
- // Sanity check PK11_ListFixedKeysInSlot() only returns keys with the correct
- // nickname.
- MOZ_ASSERT(aNickname ==
- UniquePORTString(PK11_GetSymKeyNickname(keyListHead.get())).get());
- MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key found!"));
- // Free any remaining keys in the key list.
- UniquePK11SymKey freeKey(PK11_GetNextSymKey(keyListHead.get()));
- while (freeKey) {
- freeKey = UniquePK11SymKey(PK11_GetNextSymKey(freeKey.get()));
- }
- return keyListHead;
- }
- static nsresult
- GenEcKeypair(const UniquePK11SlotInfo& aSlot,
- /*out*/ UniqueSECKEYPrivateKey& aPrivKey,
- /*out*/ UniqueSECKEYPublicKey& aPubKey,
- const nsNSSShutDownPreventionLock&)
- {
- MOZ_ASSERT(aSlot);
- if (!aSlot) {
- return NS_ERROR_INVALID_ARG;
- }
- UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
- if (!arena) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // Set the curve parameters; keyParams belongs to the arena memory space
- SECItem* keyParams = CreateECParamsForCurve(kEcAlgorithm, arena.get());
- if (!keyParams) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // Generate a key pair
- CK_MECHANISM_TYPE mechanism = CKM_EC_KEY_PAIR_GEN;
- SECKEYPublicKey* pubKeyRaw;
- aPrivKey = UniqueSECKEYPrivateKey(
- PK11_GenerateKeyPair(aSlot.get(), mechanism, keyParams, &pubKeyRaw,
- /* ephemeral */ false, false,
- /* wincx */ nullptr));
- aPubKey = UniqueSECKEYPublicKey(pubKeyRaw);
- pubKeyRaw = nullptr;
- if (!aPrivKey.get() || !aPubKey.get()) {
- return NS_ERROR_FAILURE;
- }
- // Check that the public key has the correct length
- if (aPubKey->u.ec.publicValue.len != kPublicKeyLen) {
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- nsresult
- nsNSSU2FToken::GetOrCreateWrappingKey(const UniquePK11SlotInfo& aSlot,
- const nsNSSShutDownPreventionLock& locker)
- {
- MOZ_ASSERT(aSlot);
- if (!aSlot) {
- return NS_ERROR_INVALID_ARG;
- }
- // Search for an existing wrapping key. If we find it,
- // store it for later and mark ourselves initialized.
- mWrappingKey = GetSymKeyByNickname(aSlot, mSecretNickname, locker);
- if (mWrappingKey) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token Key found."));
- mInitialized = true;
- return NS_OK;
- }
- MOZ_LOG(gNSSTokenLog, LogLevel::Info,
- ("No keys found. Generating new U2F Soft Token wrapping key."));
- // We did not find an existing wrapping key, so we generate one in the
- // persistent database (e.g, Token).
- mWrappingKey = UniquePK11SymKey(
- PK11_TokenKeyGenWithFlags(aSlot.get(), CKM_AES_KEY_GEN,
- /* default params */ nullptr,
- kWrappingKeyByteLen,
- /* empty keyid */ nullptr,
- /* flags */ CKF_WRAP | CKF_UNWRAP,
- /* attributes */ PK11_ATTR_TOKEN |
- PK11_ATTR_PRIVATE,
- /* wincx */ nullptr));
- if (!mWrappingKey) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to store wrapping key, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- SECStatus srv = PK11_SetSymKeyNickname(mWrappingKey.get(),
- mSecretNickname.get());
- if (srv != SECSuccess) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to set nickname, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
- ("Key stored, nickname set to %s.", mSecretNickname.get()));
- Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, 0);
- return NS_OK;
- }
- static nsresult
- GetAttestationCertificate(const UniquePK11SlotInfo& aSlot,
- /*out*/ UniqueSECKEYPrivateKey& aAttestPrivKey,
- /*out*/ UniqueCERTCertificate& aAttestCert,
- const nsNSSShutDownPreventionLock& locker)
- {
- MOZ_ASSERT(aSlot);
- if (!aSlot) {
- return NS_ERROR_INVALID_ARG;
- }
- UniqueSECKEYPublicKey pubKey;
- // Construct an ephemeral keypair for this Attestation Certificate
- nsresult rv = GenEcKeypair(aSlot, aAttestPrivKey, pubKey, locker);
- if (NS_FAILED(rv) || !aAttestPrivKey || !pubKey) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to gen keypair, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- // Construct the Attestation Certificate itself
- UniqueCERTName subjectName(CERT_AsciiToName(kAttestCertSubjectName.get()));
- if (!subjectName) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to set subject name, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- UniqueCERTSubjectPublicKeyInfo spki(
- SECKEY_CreateSubjectPublicKeyInfo(pubKey.get()));
- if (!spki) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to set SPKI, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- UniqueCERTCertificateRequest certreq(
- CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr));
- if (!certreq) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to gen CSR, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- PRTime now = PR_Now();
- PRTime notBefore = now - kExpirationSlack;
- PRTime notAfter = now + kExpirationLife;
- UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
- if (!validity) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to gen validity, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- unsigned long serial;
- unsigned char* serialBytes =
- mozilla::BitwiseCast<unsigned char*, unsigned long*>(&serial);
- SECStatus srv = PK11_GenerateRandomOnSlot(aSlot.get(), serialBytes,
- sizeof(serial));
- if (srv != SECSuccess) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to gen serial, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- // Ensure that the most significant bit isn't set (which would
- // indicate a negative number, which isn't valid for serial
- // numbers).
- serialBytes[0] &= 0x7f;
- // Also ensure that the least significant bit on the most
- // significant byte is set (to prevent a leading zero byte,
- // which also wouldn't be valid).
- serialBytes[0] |= 0x01;
- aAttestCert = UniqueCERTCertificate(
- CERT_CreateCertificate(serial, subjectName.get(), validity.get(),
- certreq.get()));
- if (!aAttestCert) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to gen certificate, NSS error #%d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- PLArenaPool* arena = aAttestCert->arena;
- srv = SECOID_SetAlgorithmID(arena, &aAttestCert->signature,
- SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE,
- /* wincx */ nullptr);
- if (srv != SECSuccess) {
- return NS_ERROR_FAILURE;
- }
- // Set version to X509v3.
- *(aAttestCert->version.data) = SEC_CERTIFICATE_VERSION_3;
- aAttestCert->version.len = 1;
- SECItem innerDER = { siBuffer, nullptr, 0 };
- if (!SEC_ASN1EncodeItem(arena, &innerDER, aAttestCert.get(),
- SEC_ASN1_GET(CERT_CertificateTemplate))) {
- return NS_ERROR_FAILURE;
- }
- SECItem* signedCert = PORT_ArenaZNew(arena, SECItem);
- if (!signedCert) {
- return NS_ERROR_FAILURE;
- }
- srv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
- aAttestPrivKey.get(),
- SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
- if (srv != SECSuccess) {
- return NS_ERROR_FAILURE;
- }
- aAttestCert->derCert = *signedCert;
- MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
- ("U2F Soft Token attestation certificate generated."));
- return NS_OK;
- }
- // Set up the context for the soft U2F Token. This is called by NSS
- // initialization.
- NS_IMETHODIMP
- nsNSSU2FToken::Init()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(!mInitialized);
- if (mInitialized) {
- return NS_ERROR_FAILURE;
- }
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
- MOZ_ASSERT(slot.get());
- // Search for an existing wrapping key, or create one.
- nsresult rv = GetOrCreateWrappingKey(slot, locker);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- 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.
- static UniqueSECItem
- KeyHandleFromPrivateKey(const UniquePK11SlotInfo& aSlot,
- const UniquePK11SymKey& aWrappingKey,
- const UniqueSECKEYPrivateKey& aPrivKey,
- const nsNSSShutDownPreventionLock&)
- {
- MOZ_ASSERT(aSlot);
- MOZ_ASSERT(aWrappingKey);
- MOZ_ASSERT(aPrivKey);
- if (!aSlot || !aWrappingKey || !aPrivKey) {
- 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()));
- 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);
- if (srv != SECSuccess) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Failed to wrap U2F key, NSS error #%d", PORT_GetError()));
- return nullptr;
- }
- return wrappedKey;
- }
- // Convert an opaque key handle aKeyHandle back into a Private Key object, using
- // aWrappingKey and the AES Key Wrap algorithm.
- static UniqueSECKEYPrivateKey
- PrivateKeyFromKeyHandle(const UniquePK11SlotInfo& aSlot,
- const UniquePK11SymKey& aWrappingKey,
- uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
- const nsNSSShutDownPreventionLock&)
- {
- MOZ_ASSERT(aSlot);
- MOZ_ASSERT(aWrappingKey);
- MOZ_ASSERT(aKeyHandle);
- if (!aSlot || !aWrappingKey || !aKeyHandle) {
- return nullptr;
- }
- ScopedAutoSECItem pubKey(kPublicKeyLen);
- ScopedAutoSECItem keyHandleItem(aKeyHandleLen);
- memcpy(keyHandleItem.data, aKeyHandle, keyHandleItem.len);
- 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,
- /* no nickname */ nullptr,
- /* discard pubkey */ &pubKey,
- /* not permanent */ false,
- /* non-exportable */ true,
- CKK_EC, usages, usageCount,
- /* wincx */ nullptr));
- if (!unwrappedKey) {
- // Not our key.
- MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
- ("Could not unwrap key handle, NSS Error #%d", PORT_GetError()));
- return nullptr;
- }
- return unwrappedKey;
- }
- // Return whether the provided version is supported by this token.
- NS_IMETHODIMP
- nsNSSU2FToken::IsCompatibleVersion(const nsAString& aVersion, bool* aResult)
- {
- NS_ENSURE_ARG_POINTER(aResult);
- 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,
- bool* aResult)
- {
- NS_ENSURE_ARG_POINTER(aKeyHandle);
- 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;
- if (isAlreadyShutDown()) {
- return NS_ERROR_FAILURE;
- }
- MOZ_ASSERT(mInitialized);
- if (!mInitialized) {
- return NS_ERROR_FAILURE;
- }
- UniquePK11SlotInfo slot(PK11_GetInternalSlot());
- MOZ_ASSERT(slot.get());
- // Decode the key handle
- UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
- aKeyHandle,
- aKeyHandleLen,
- 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
- // well as the generated attestation certificate and a signature using that
- // certificate's private key.
- //
- // The KeyHandleFromPrivateKey and PrivateKeyFromKeyHandle methods perform
- // the actual key wrap/unwrap operations.
- //
- // The format of the return registration data is as follows:
- //
- // Bytes Value
- // 1 0x05
- // 65 public key
- // 1 key handle length
- // * key handle
- // ASN.1 attestation certificate
- // * attestation signature
- //
- NS_IMETHODIMP
- nsNSSU2FToken::Register(uint8_t* aApplication,
- uint32_t aApplicationLen,
- uint8_t* aChallenge,
- uint32_t aChallengeLen,
- uint8_t** aRegistration,
- uint32_t* aRegistrationLen)
- {
- NS_ENSURE_ARG_POINTER(aApplication);
- NS_ENSURE_ARG_POINTER(aChallenge);
- NS_ENSURE_ARG_POINTER(aRegistration);
- NS_ENSURE_ARG_POINTER(aRegistrationLen);
- if (!NS_IsMainThread()) {
- NS_ERROR("nsNSSU2FToken::Register called off the main thread");
- return NS_ERROR_NOT_SAME_THREAD;
- }
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- MOZ_ASSERT(mInitialized);
- if (!mInitialized) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- // We should already have a wrapping key
- MOZ_ASSERT(mWrappingKey);
- UniquePK11SlotInfo slot(PK11_GetInternalSlot());
- MOZ_ASSERT(slot.get());
- // Construct a one-time-use Attestation Certificate
- UniqueSECKEYPrivateKey attestPrivKey;
- UniqueCERTCertificate attestCert;
- nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert,
- locker);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return NS_ERROR_FAILURE;
- }
- MOZ_ASSERT(attestCert);
- MOZ_ASSERT(attestPrivKey);
- // Generate a new keypair; the private will be wrapped into a Key Handle
- UniqueSECKEYPrivateKey privKey;
- 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,
- 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 +
- keyHandleItem->len + kPublicKeyLen,
- mozilla::fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // It's OK to ignore the return values here because we're writing into
- // pre-allocated space
- signedDataBuf.AppendElement(0x00, mozilla::fallible);
- signedDataBuf.AppendElements(aApplication, aApplicationLen, mozilla::fallible);
- signedDataBuf.AppendElements(aChallenge, aChallengeLen, mozilla::fallible);
- signedDataBuf.AppendSECItem(keyHandleItem.get());
- signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue);
- ScopedAutoSECItem signatureItem;
- SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(),
- signedDataBuf.Length(), attestPrivKey.get(),
- SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
- if (srv != SECSuccess) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Signature failure: %d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- // Serialize the registration data
- mozilla::dom::CryptoBuffer registrationBuf;
- if (!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len +
- attestCert.get()->derCert.len +
- signatureItem.len, mozilla::fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- registrationBuf.AppendElement(0x05, mozilla::fallible);
- registrationBuf.AppendSECItem(pubKey->u.ec.publicValue);
- registrationBuf.AppendElement(keyHandleItem->len, mozilla::fallible);
- registrationBuf.AppendSECItem(keyHandleItem.get());
- registrationBuf.AppendSECItem(attestCert.get()->derCert);
- registrationBuf.AppendSECItem(signatureItem);
- if (!registrationBuf.ToNewUnsignedBuffer(aRegistration, aRegistrationLen)) {
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- // A U2F Sign operation creates a signature over the "param" arguments (plus
- // some other stuff) using the private key indicated in the key handle argument.
- //
- // The format of the signed data is as follows:
- //
- // 32 Application parameter
- // 1 User presence (0x01)
- // 4 Counter
- // 32 Challenge parameter
- //
- // The format of the signature data is as follows:
- //
- // 1 User presence
- // 4 Counter
- // * Signature
- //
- NS_IMETHODIMP
- nsNSSU2FToken::Sign(uint8_t* aApplication, uint32_t aApplicationLen,
- uint8_t* aChallenge, uint32_t aChallengeLen,
- uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
- uint8_t** aSignature, uint32_t* aSignatureLen)
- {
- NS_ENSURE_ARG_POINTER(aApplication);
- NS_ENSURE_ARG_POINTER(aChallenge);
- NS_ENSURE_ARG_POINTER(aKeyHandle);
- NS_ENSURE_ARG_POINTER(aKeyHandleLen);
- NS_ENSURE_ARG_POINTER(aSignature);
- NS_ENSURE_ARG_POINTER(aSignatureLen);
- if (!NS_IsMainThread()) {
- NS_ERROR("nsNSSU2FToken::Sign called off the main thread");
- return NS_ERROR_NOT_SAME_THREAD;
- }
- nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- MOZ_ASSERT(mInitialized);
- if (!mInitialized) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- MOZ_ASSERT(mWrappingKey);
- UniquePK11SlotInfo slot(PK11_GetInternalSlot());
- MOZ_ASSERT(slot.get());
- if ((aChallengeLen != kParamLen) || (aApplicationLen != kParamLen)) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
- aChallengeLen, aApplicationLen, kParamLen));
- return NS_ERROR_ILLEGAL_VALUE;
- }
- // Decode the key handle
- UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
- aKeyHandle,
- aKeyHandleLen,
- 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;
- Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, counter);
- ScopedAutoSECItem counterItem(4);
- counterItem.data[0] = (counter >> 24) & 0xFF;
- counterItem.data[1] = (counter >> 16) & 0xFF;
- counterItem.data[2] = (counter >> 8) & 0xFF;
- counterItem.data[3] = (counter >> 0) & 0xFF;
- // Compute the signature
- mozilla::dom::CryptoBuffer signedDataBuf;
- if (!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // It's OK to ignore the return values here because we're writing into
- // pre-allocated space
- signedDataBuf.AppendElements(aApplication, aApplicationLen, mozilla::fallible);
- signedDataBuf.AppendElement(0x01, mozilla::fallible);
- signedDataBuf.AppendSECItem(counterItem);
- signedDataBuf.AppendElements(aChallenge, aChallengeLen, mozilla::fallible);
- ScopedAutoSECItem signatureItem;
- SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(),
- signedDataBuf.Length(), privKey.get(),
- SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
- if (srv != SECSuccess) {
- MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
- ("Signature failure: %d", PORT_GetError()));
- return NS_ERROR_FAILURE;
- }
- // Assemble the signature data into a buffer for return
- mozilla::dom::CryptoBuffer signatureBuf;
- if (!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
- mozilla::fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // It's OK to ignore the return values here because we're writing into
- // pre-allocated space
- signatureBuf.AppendElement(0x01, mozilla::fallible);
- signatureBuf.AppendSECItem(counterItem);
- signatureBuf.AppendSECItem(signatureItem);
- if (!signatureBuf.ToNewUnsignedBuffer(aSignature, aSignatureLen)) {
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
|