/****************************************************************** * * m a k e _ c e r t */ static CERTCertificate * make_cert(CERTCertificateRequest *req, unsigned long serial, CERTName *ca_subject) { CERTCertificate * cert; CERTValidity * validity = NULL; PRTime now, after; PRExplodedTime printableTime; now = PR_Now(); PR_ExplodeTime (now, PR_GMTParameters, &printableTime); printableTime.tm_month += 3; after = PR_ImplodeTime (&printableTime); validity = CERT_CreateValidity (now, after); if (validity == NULL) { PR_fprintf(errorFD, "%s: error creating certificate validity\n", PROGRAM_NAME); errorCount++; exit (ERRX); } cert = CERT_CreateCertificate (serial, ca_subject, validity, req); if (cert == NULL) { /* should probably be more precise here */ PR_fprintf(errorFD, "%s: error while generating certificate\n", PROGRAM_NAME); errorCount++; exit (ERRX); } return cert; }
nsresult Generate() { nsresult rv; // Get the key slot for generation later UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Remove existing certs with this name (if any) rv = RemoveExisting(); if (NS_FAILED(rv)) { return rv; } // Generate a new cert NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN="); nsAutoCString subjectNameStr(commonNamePrefix + mNickname); UniqueCERTName subjectName(CERT_AsciiToName(subjectNameStr.get())); if (!subjectName) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Use the well-known NIST P-256 curve SECOidData* curveOidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); if (!curveOidData) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Get key params from the curve ScopedAutoSECItem keyParams(2 + curveOidData->oid.len); keyParams.data[0] = SEC_ASN1_OBJECT_ID; keyParams.data[1] = curveOidData->oid.len; memcpy(keyParams.data + 2, curveOidData->oid.data, curveOidData->oid.len); // Generate cert key pair SECKEYPublicKey* tempPublicKey; UniqueSECKEYPrivateKey privateKey( PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &keyParams, &tempPublicKey, true /* token */, true /* sensitive */, nullptr)); UniqueSECKEYPublicKey publicKey(tempPublicKey); tempPublicKey = nullptr; if (!privateKey || !publicKey) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Create subject public key info and cert request UniqueCERTSubjectPublicKeyInfo spki( SECKEY_CreateSubjectPublicKeyInfo(publicKey.get())); if (!spki) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } UniqueCERTCertificateRequest certRequest( CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr)); if (!certRequest) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Valid from one day before to 1 year after static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) * PRTime(60) // sec * PRTime(60) // min * PRTime(24); // hours PRTime now = PR_Now(); PRTime notBefore = now - oneDay; PRTime notAfter = now + (PRTime(365) * oneDay); UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); if (!validity) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Generate random serial unsigned long serial; // This serial in principle could collide, but it's unlikely rv = MapSECStatus(PK11_GenerateRandomOnSlot( slot.get(), BitwiseCast<unsigned char*, unsigned long*>(&serial), sizeof(serial))); if (NS_FAILED(rv)) { return rv; } // Create the cert from these pieces UniqueCERTCertificate cert( CERT_CreateCertificate(serial, subjectName.get(), validity.get(), certRequest.get())); if (!cert) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Update the cert version to X509v3 if (!cert->version.data) { return NS_ERROR_INVALID_POINTER; } *(cert->version.data) = SEC_CERTIFICATE_VERSION_3; cert->version.len = 1; // Set cert signature algorithm PLArenaPool* arena = cert->arena; if (!arena) { return NS_ERROR_INVALID_POINTER; } rv = MapSECStatus( SECOID_SetAlgorithmID(arena, &cert->signature, SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0)); if (NS_FAILED(rv)) { return rv; } // Encode and self-sign the cert UniqueSECItem certDER( SEC_ASN1EncodeItem(nullptr, nullptr, cert.get(), SEC_ASN1_GET(CERT_CertificateTemplate))); if (!certDER) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } rv = MapSECStatus( SEC_DerSignData(arena, &cert->derCert, certDER->data, certDER->len, privateKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE)); if (NS_FAILED(rv)) { return rv; } // Create a CERTCertificate from the signed data UniqueCERTCertificate certFromDER( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &cert->derCert, nullptr, true /* perm */, true /* copyDER */)); if (!certFromDER) { return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); } // Save the cert in the DB rv = MapSECStatus(PK11_ImportCert(slot.get(), certFromDER.get(), CK_INVALID_HANDLE, mNickname.get(), false /* unused */)); if (NS_FAILED(rv)) { return rv; } // We should now have cert in the DB, read it back in nsIX509Cert form return GetFromDB(); }
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; }
RefPtr<DtlsIdentity> DtlsIdentity::Generate() { UniquePK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return nullptr; } uint8_t random_name[16]; SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), random_name, sizeof(random_name)); if (rv != SECSuccess) return nullptr; std::string name; char chunk[3]; for (size_t i = 0; i < sizeof(random_name); ++i) { SprintfLiteral(chunk, "%.2x", random_name[i]); name += chunk; } std::string subject_name_string = "CN=" + name; UniqueCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str())); if (!subject_name) { return nullptr; } unsigned char paramBuf[12]; // OIDs are small SECItem ecdsaParams = { siBuffer, paramBuf, sizeof(paramBuf) }; SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) { return nullptr; } ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID; ecdsaParams.data[1] = oidData->oid.len; memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len); ecdsaParams.len = oidData->oid.len + 2; SECKEYPublicKey *pubkey; UniqueSECKEYPrivateKey private_key( PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsaParams, &pubkey, PR_FALSE, PR_TRUE, nullptr)); if (private_key == nullptr) return nullptr; UniqueSECKEYPublicKey public_key(pubkey); pubkey = nullptr; UniqueCERTSubjectPublicKeyInfo spki( SECKEY_CreateSubjectPublicKeyInfo(public_key.get())); if (!spki) { return nullptr; } UniqueCERTCertificateRequest certreq( CERT_CreateCertificateRequest(subject_name.get(), spki.get(), nullptr)); if (!certreq) { return nullptr; } // From 1 day before todayto 30 days after. // This is a sort of arbitrary range designed to be valid // now with some slack in case the other side expects // some before expiry. // // Note: explicit casts necessary to avoid // warning C4307: '*' : integral constant overflow static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) * PRTime(60) // sec * PRTime(60) // min * PRTime(24); // hours PRTime now = PR_Now(); PRTime notBefore = now - oneDay; PRTime notAfter = now + (PRTime(30) * oneDay); UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); if (!validity) { return nullptr; } unsigned long serial; // Note: This serial in principle could collide, but it's unlikely rv = PK11_GenerateRandomOnSlot(slot.get(), reinterpret_cast<unsigned char *>(&serial), sizeof(serial)); if (rv != SECSuccess) { return nullptr; } UniqueCERTCertificate certificate( CERT_CreateCertificate(serial, subject_name.get(), validity.get(), certreq.get())); if (!certificate) { return nullptr; } PLArenaPool *arena = certificate->arena; rv = SECOID_SetAlgorithmID(arena, &certificate->signature, SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0); if (rv != SECSuccess) return nullptr; // Set version to X509v3. *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3; certificate->version.len = 1; SECItem innerDER; innerDER.len = 0; innerDER.data = nullptr; if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate.get(), SEC_ASN1_GET(CERT_CertificateTemplate))) { return nullptr; } SECItem *signedCert = PORT_ArenaZNew(arena, SECItem); if (!signedCert) { return nullptr; } rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len, private_key.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (rv != SECSuccess) { return nullptr; } certificate->derCert = *signedCert; RefPtr<DtlsIdentity> identity = new DtlsIdentity(Move(private_key), Move(certificate), ssl_kea_ecdh); return identity.forget(); }