NS_IMETHODIMP nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,nsIX509Cert** _cert) { NS_ENSURE_ARG_POINTER(aDBKey); NS_ENSURE_ARG(aDBKey[0]); NS_ENSURE_ARG_POINTER(_cert); *_cert = nullptr; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } UniqueCERTCertificate cert; nsresult rv = FindCertByDBKey(aDBKey, cert); if (NS_FAILED(rv)) { return rv; } // If we can't find the certificate, that's not an error. Just return null. if (!cert) { return NS_OK; } nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get()); if (!nssCert) { return NS_ERROR_OUT_OF_MEMORY; } nssCert.forget(_cert); return NS_OK; }
nsresult GetFromDB() { UniqueCERTCertificate cert; nsresult rv = FindLocalCertByName(mNickname, cert); if (NS_FAILED(rv)) { return rv; } if (!cert) { return NS_ERROR_FAILURE; } mCert = nsNSSCertificate::Create(cert.get()); return NS_OK; }
nsresult RemoveExisting() { // Search for any existing self-signed certs with this name and remove them for (;;) { UniqueCERTCertificate cert; nsresult rv = FindLocalCertByName(mNickname, cert); if (NS_FAILED(rv)) { return rv; } // If we didn't find a match, we're done. if (!cert) { return NS_OK; } rv = MapSECStatus(PK11_DeleteTokenCertAndKey(cert.get(), nullptr)); if (NS_FAILED(rv)) { return rv; } } }
// 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 // nsresult U2FSoftTokenManager::Register(nsTArray<uint8_t>& aApplication, nsTArray<uint8_t>& aChallenge, /* out */ nsTArray<uint8_t>& aRegistration, /* out */ nsTArray<uint8_t>& aSignature) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { return NS_ERROR_NOT_AVAILABLE; } if (!mInitialized) { nsresult rv = Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } // 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, aApplication.Elements(), aApplication.Length(), privKey, locker); if (NS_WARN_IF(!keyHandleItem.get())) { return NS_ERROR_FAILURE; } // Sign the challenge using the Attestation privkey (from attestCert) mozilla::dom::CryptoBuffer signedDataBuf; if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + aApplication.Length() + aChallenge.Length() + 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, mozilla::fallible); signedDataBuf.AppendElements(aChallenge, 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 (NS_WARN_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 (NS_WARN_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); aRegistration = registrationBuf; return NS_OK; }
static nsresult GetAttestationCertificate(const UniquePK11SlotInfo& aSlot, /*out*/ UniqueSECKEYPrivateKey& aAttestPrivKey, /*out*/ UniqueCERTCertificate& aAttestCert, const nsNSSShutDownPreventionLock& locker) { MOZ_ASSERT(aSlot); if (NS_WARN_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_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_IF(!SEC_ASN1EncodeItem(arena, &innerDER, aAttestCert.get(), SEC_ASN1_GET(CERT_CertificateTemplate)))) { return NS_ERROR_FAILURE; } SECItem* signedCert = PORT_ArenaZNew(arena, SECItem); if (NS_WARN_IF(!signedCert)) { return NS_ERROR_FAILURE; } srv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len, aAttestPrivKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (NS_WARN_IF(srv != SECSuccess)) { return NS_ERROR_FAILURE; } aAttestCert->derCert = *signedCert; MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token attestation certificate generated.")); return NS_OK; }
SECStatus CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert, /*optional*/ const SECItem* stapledOCSPResponse, Time time, /*optional*/ void* pinarg, const char* hostname, /*out*/ ScopedCERTCertList& builtChain, /*optional*/ bool saveIntermediatesInPermanentDatabase, /*optional*/ Flags flags, /*optional out*/ SECOidTag* evOidPolicy, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, /*optional out*/ KeySizeStatus* keySizeStatus, /*optional out*/ SHA1ModeResult* sha1ModeResult, /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo) { PR_ASSERT(peerCert); // XXX: PR_ASSERT(pinarg) PR_ASSERT(hostname); PR_ASSERT(hostname[0]); if (evOidPolicy) { *evOidPolicy = SEC_OID_UNKNOWN; } if (!hostname || !hostname[0]) { PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0); return SECFailure; } // CreateCertErrorRunnable assumes that CheckCertHostname is only called // if VerifyCert succeeded. SECStatus rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time, pinarg, hostname, builtChain, flags, stapledOCSPResponse, evOidPolicy, ocspStaplingStatus, keySizeStatus, sha1ModeResult, pinningTelemetryInfo); if (rv != SECSuccess) { return rv; } Input peerCertInput; Result result = peerCertInput.Init(peerCert->derCert.data, peerCert->derCert.len); if (result != Success) { PR_SetError(MapResultToPRErrorCode(result), 0); return SECFailure; } Input stapledOCSPResponseInput; Input* responseInputPtr = nullptr; if (stapledOCSPResponse) { result = stapledOCSPResponseInput.Init(stapledOCSPResponse->data, stapledOCSPResponse->len); if (result != Success) { // The stapled OCSP response was too big. PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0); return SECFailure; } responseInputPtr = &stapledOCSPResponseInput; } if (!(flags & FLAG_TLS_IGNORE_STATUS_REQUEST)) { result = CheckTLSFeaturesAreSatisfied(peerCertInput, responseInputPtr); if (result != Success) { PR_SetError(MapResultToPRErrorCode(result), 0); return SECFailure; } } Input hostnameInput; result = hostnameInput.Init(uint8_t_ptr_cast(hostname), strlen(hostname)); if (result != Success) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } bool isBuiltInRoot; result = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot); if (result != Success) { PR_SetError(MapResultToPRErrorCode(result), 0); return SECFailure; } BRNameMatchingPolicy nameMatchingPolicy( isBuiltInRoot ? mNameMatchingMode : BRNameMatchingPolicy::Mode::DoNotEnforce); result = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy); if (result != Success) { // Treat malformed name information as a domain mismatch. if (result == Result::ERROR_BAD_DER) { PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0); } else { PR_SetError(MapResultToPRErrorCode(result), 0); } return SECFailure; } if (saveIntermediatesInPermanentDatabase) { SaveIntermediateCerts(builtChain); } return SECSuccess; }
// 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, 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 + 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; }