// Tests that a certificate with a known critical id-ce extension (in this case, // OID 2.5.29.54, which is id-ce-inhibitAnyPolicy), verifies successfully. TEST_F(pkix_cert_extensions, KnownCriticalCEExtension) { static const uint8_t criticalCEExtensionBytes[] = { 0x30, 0x0d, // SEQUENCE (length = 13) 0x06, 0x03, // OID (length = 3) 0x55, 0x1d, 0x36, // 2.5.29.54 (id-ce-inhibitAnyPolicy) 0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE 0x04, 0x03, // OCTET STRING (length = 3) 0x02, 0x01, 0x00, // INTEGER (length = 1, value = 0) }; static const SECItem criticalCEExtension = { siBuffer, const_cast<unsigned char*>(criticalCEExtensionBytes), sizeof(criticalCEExtensionBytes) }; const char* certCN = "CN=Cert With Known Critical id-ce Extension"; ScopedSECKEYPrivateKey key; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalCEExtension, key, cert)); ScopedCERTCertList results; ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now, EndEntityOrCA::MustBeEndEntity, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr, // stapled OCSP response results)); }
NS_IMETHODIMP nsNSSCertificateDB::FindCertByNickname(const nsAString& nickname, nsIX509Cert** _rvCert) { NS_ENSURE_ARG_POINTER(_rvCert); *_rvCert = nullptr; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } ScopedCERTCertificate cert; char *asciiname = nullptr; NS_ConvertUTF16toUTF8 aUtf8Nickname(nickname); asciiname = const_cast<char*>(aUtf8Nickname.get()); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting \"%s\"\n", asciiname)); cert = PK11_FindCertFromNickname(asciiname, nullptr); if (!cert) { cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname); } if (cert) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("got it\n")); nsCOMPtr<nsIX509Cert> pCert = nsNSSCertificate::Create(cert.get()); if (pCert) { pCert.forget(_rvCert); return NS_OK; } } return NS_ERROR_FAILURE; }
// Tests that a id-pe-authorityInformationAccess critical extension // is detected and that verification succeeds. TEST_F(pkix_cert_extensions, CriticalAIAExtension) { // XXX: According to RFC 5280 an AIA that consists of an empty sequence is // not legal, but we accept it and that is not what we're testing here. static const uint8_t criticalAIAExtensionBytes[] = { 0x30, 0x11, // SEQUENCE (length = 17) 0x06, 0x08, // OID (length = 8) // 1.3.6.1.5.5.7.1.1 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE 0x04, 0x02, // OCTET STRING (length = 2) 0x30, 0x00, // SEQUENCE (length = 0) }; static const SECItem criticalAIAExtension = { siBuffer, const_cast<unsigned char*>(criticalAIAExtensionBytes), sizeof(criticalAIAExtensionBytes) }; const char* certCN = "CN=Cert With Critical AIA Extension"; ScopedSECKEYPrivateKey key; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalAIAExtension, key, cert)); ScopedCERTCertList results; ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now, EndEntityOrCA::MustBeEndEntity, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr, // stapled OCSP response results)); }
// We know about some id-ce extensions (OID arc 2.5.29), but not all of them. // Tests that an unknown id-ce extension is detected and that verification // fails. TEST_F(pkix_cert_extensions, UnknownCriticalCEExtension) { static const uint8_t unknownCriticalCEExtensionBytes[] = { 0x30, 0x0a, // SEQUENCE (length = 10) 0x06, 0x03, // OID (length = 3) 0x55, 0x1d, 0x37, // 2.5.29.55 0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE 0x04, 0x00 // OCTET STRING (length = 0) }; static const SECItem unknownCriticalCEExtension = { siBuffer, const_cast<unsigned char*>(unknownCriticalCEExtensionBytes), sizeof(unknownCriticalCEExtensionBytes) }; const char* certCN = "CN=Cert With Unknown Critical id-ce Extension"; ScopedSECKEYPrivateKey key; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalCEExtension, key, cert)); ScopedCERTCertList results; ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, BuildCertChain(trustDomain, cert.get(), now, EndEntityOrCA::MustBeEndEntity, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr, // stapled OCSP response results)); }
// Tests that an incorrect OID for id-pe-authorityInformationAccess // (when marked critical) is detected and that verification fails. // (Until bug 1020993 was fixed, the code checked for this OID.) TEST_F(pkix_cert_extensions, WrongOIDCriticalExtension) { static const uint8_t wrongOIDCriticalExtensionBytes[] = { 0x30, 0x10, // SEQUENCE (length = 16) 0x06, 0x09, // OID (length = 9) // 1.3.6.6.1.5.5.7.1.1 (there is an extra "6" that shouldn't be there) 0x2b, 0x06, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE 0x04, 0x00 // OCTET STRING (length = 0) }; static const SECItem wrongOIDCriticalExtension = { siBuffer, const_cast<unsigned char*>(wrongOIDCriticalExtensionBytes), sizeof(wrongOIDCriticalExtensionBytes) }; const char* certCN = "CN=Cert With Critical Wrong OID Extension"; ScopedSECKEYPrivateKey key; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), certCN, &wrongOIDCriticalExtension, key, cert)); ScopedCERTCertList results; ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, BuildCertChain(trustDomain, cert.get(), now, EndEntityOrCA::MustBeEndEntity, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr, // stapled OCSP response results)); }
// Tests that a non-critical extension not in the id-ce or id-pe arcs (which is // thus unknown to us) verifies successfully. TEST_F(pkix_cert_extensions, UnknownNonCriticalExtension) { static const uint8_t unknownNonCriticalExtensionBytes[] = { 0x30, 0x16, // SEQUENCE (length = 22) 0x06, 0x12, // OID (length = 18) // 1.3.6.1.4.1.13769.666.666.666.1.500.9.3 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03, 0x04, 0x00 // OCTET STRING (length = 0) }; static const SECItem unknownNonCriticalExtension = { siBuffer, const_cast<unsigned char*>(unknownNonCriticalExtensionBytes), sizeof(unknownNonCriticalExtensionBytes) }; const char* certCN = "CN=Cert With Unknown NonCritical Extension"; ScopedSECKEYPrivateKey key; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownNonCriticalExtension, key, cert)); ScopedCERTCertList results; ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now, EndEntityOrCA::MustBeEndEntity, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr, // stapled OCSP response results)); }
// Tests that a id-pe-authorityInformationAccess critical extension // is detected and that verification succeeds. TEST_F(pkix_cert_extensions, CriticalAIAExtension) { static const uint8_t criticalAIAExtensionBytes[] = { 0x30, 0x0f, // SEQUENCE (length = 15) 0x06, 0x08, // OID (length = 8) // 1.3.6.1.5.5.7.1.1 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE 0x04, 0x00 // OCTET STRING (length = 0) }; static const SECItem criticalAIAExtension = { siBuffer, const_cast<unsigned char*>(criticalAIAExtensionBytes), sizeof(criticalAIAExtensionBytes) }; const char* certCN = "CN=Cert With Critical AIA Extension"; ScopedSECKEYPrivateKey key; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalAIAExtension, key, cert)); ScopedCERTCertList results; ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now, EndEntityOrCA::MustBeEndEntity, 0, // key usage KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr, // stapled OCSP response results)); }
// Tests that a critical extension not in the id-ce or id-pe arcs (which is // thus unknown to us) is detected and that verification fails with the // appropriate error. TEST_F(pkix_cert_extensions, UnknownCriticalExtension) { static const uint8_t unknownCriticalExtensionBytes[] = { 0x30, 0x19, // SEQUENCE (length = 25) 0x06, 0x12, // OID (length = 18) // 1.3.6.1.4.1.13769.666.666.666.1.500.9.3 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03, 0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE 0x04, 0x00 // OCTET STRING (length = 0) }; static const SECItem unknownCriticalExtension = { siBuffer, const_cast<unsigned char*>(unknownCriticalExtensionBytes), sizeof(unknownCriticalExtensionBytes) }; const char* certCN = "CN=Cert With Unknown Critical Extension"; ScopedSECKEYPrivateKey key; ScopedCERTCertificate cert; ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalExtension, key, cert)); ScopedCERTCertList results; ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, BuildCertChain(trustDomain, cert.get(), now, EndEntityOrCA::MustBeEndEntity, 0, // key usage KeyPurposeId::anyExtendedKeyUsage, CertPolicyId::anyPolicy, nullptr, // stapled OCSP response results)); }
// Creates a self-signed certificate with the given extension. static bool CreateCert(PLArenaPool* arena, const char* subjectStr, const SECItem* extension, /*out*/ ScopedSECKEYPrivateKey& subjectKey, /*out*/ ScopedCERTCertificate& subjectCert) { static long serialNumberValue = 0; ++serialNumberValue; const SECItem* serialNumber(CreateEncodedSerialNumber(arena, serialNumberValue)); if (!serialNumber) { return false; } const SECItem* issuerDER(ASCIIToDERName(arena, subjectStr)); if (!issuerDER) { return false; } const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr)); if (!subjectDER) { return false; } const SECItem* extensions[2] = { extension, nullptr }; SECItem* certDER(CreateEncodedCertificate(arena, v3, SEC_OID_SHA256, serialNumber, issuerDER, PR_Now() - ONE_DAY, PR_Now() + ONE_DAY, subjectDER, extensions, nullptr, SEC_OID_SHA256, subjectKey)); if (!certDER) { return false; } subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER, nullptr, false, true); return subjectCert.get() != nullptr; }
NS_IMETHODIMP nsNSSCertificateDB::ImportUserCertificate(uint8_t *data, uint32_t length, nsIInterfaceRequestor *ctx) { if (!NS_IsMainThread()) { NS_ERROR("nsNSSCertificateDB::ImportUserCertificate called off the main thread"); return NS_ERROR_NOT_SAME_THREAD; } nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } ScopedPK11SlotInfo slot; nsAutoCString nickname; nsresult rv = NS_ERROR_FAILURE; int numCACerts; SECItem *CACerts; CERTDERCerts * collectArgs; PLArenaPool *arena; ScopedCERTCertificate cert; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { goto loser; } collectArgs = getCertsFromPackage(arena, data, length, locker); if (!collectArgs) { goto loser; } cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts, nullptr, false, true); if (!cert) { goto loser; } slot = PK11_KeyForCertExists(cert.get(), nullptr, ctx); if (!slot) { nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get()); DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow, locker); goto loser; } slot = nullptr; /* pick a nickname for the cert */ if (cert->nickname) { /* sigh, we need a call to look up other certs with this subject and * identify nicknames from them. We can no longer walk down internal * database structures rjr */ nickname = cert->nickname; } else { get_default_nickname(cert.get(), ctx, nickname, locker); } /* user wants to import the cert */ { char *cast_const_away = const_cast<char*>(nickname.get()); slot = PK11_ImportCertForKey(cert.get(), cast_const_away, ctx); } if (!slot) { goto loser; } slot = nullptr; { nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get()); DisplayCertificateAlert(ctx, "UserCertImported", certToShow, locker); } rv = NS_OK; numCACerts = collectArgs->numcerts - 1; if (numCACerts) { CACerts = collectArgs->rawCerts+1; rv = ImportValidCACerts(numCACerts, CACerts, ctx, locker); } loser: if (arena) { PORT_FreeArena(arena, false); } return rv; }
SECItemArray * GetOCSPResponseForType(OCSPResponseType aORT, CERTCertificate *aCert, PLArenaPool *aArena, const char *aAdditionalCertName) { if (aORT == ORTNone) { if (gDebugLevel >= DEBUG_WARNINGS) { fprintf(stderr, "GetOCSPResponseForType called with type ORTNone, " "which makes no sense.\n"); } return nullptr; } if (aORT == ORTEmpty) { SECItemArray* arr = SECITEM_AllocArray(aArena, nullptr, 1); arr->items[0].data = nullptr; arr->items[0].len = 0; return arr; } PRTime now = PR_Now(); PRTime oneDay = 60*60*24 * (PRTime)PR_USEC_PER_SEC; PRTime oldNow = now - (8 * oneDay); OCSPResponseContext context(aArena, aCert, now); if (aORT == ORTGoodOtherCert) { context.cert = PK11_FindCertFromNickname(aAdditionalCertName, nullptr); if (!context.cert) { PrintPRError("PK11_FindCertFromNickname failed"); return nullptr; } } // XXX CERT_FindCertIssuer uses the old, deprecated path-building logic ScopedCERTCertificate issuerCert(CERT_FindCertIssuer(aCert, now, certUsageSSLCA)); if (!issuerCert) { PrintPRError("CERT_FindCertIssuer failed"); return nullptr; } context.issuerNameDER = &issuerCert->derSubject; context.issuerSPKI = &issuerCert->subjectPublicKeyInfo; ScopedCERTCertificate signerCert; if (aORT == ORTGoodOtherCA || aORT == ORTDelegatedIncluded || aORT == ORTDelegatedIncludedLast || aORT == ORTDelegatedMissing || aORT == ORTDelegatedMissingMultiple) { signerCert = PK11_FindCertFromNickname(aAdditionalCertName, nullptr); if (!signerCert) { PrintPRError("PK11_FindCertFromNickname failed"); return nullptr; } } const SECItem* certs[5] = { nullptr, nullptr, nullptr, nullptr, nullptr }; if (aORT == ORTDelegatedIncluded) { certs[0] = &signerCert->derCert; context.certs = certs; } if (aORT == ORTDelegatedIncludedLast || aORT == ORTDelegatedMissingMultiple) { certs[0] = &issuerCert->derCert; certs[1] = &context.cert->derCert; certs[2] = &issuerCert->derCert; if (aORT != ORTDelegatedMissingMultiple) { certs[3] = &signerCert->derCert; } context.certs = certs; } switch (aORT) { case ORTMalformed: context.responseStatus = 1; break; case ORTSrverr: context.responseStatus = 2; break; case ORTTryLater: context.responseStatus = 3; break; case ORTNeedsSig: context.responseStatus = 5; break; case ORTUnauthorized: context.responseStatus = 6; break; default: // context.responseStatus is 0 in all other cases, and it has // already been initialized in the constructor. break; } if (aORT == ORTSkipResponseBytes) { context.skipResponseBytes = true; } if (aORT == ORTExpired || aORT == ORTExpiredFreshCA) { context.thisUpdate = oldNow; context.nextUpdate = oldNow + 10 * PR_USEC_PER_SEC; } if (aORT == ORTRevoked) { context.certStatus = 1; } if (aORT == ORTUnknown) { context.certStatus = 2; } if (aORT == ORTBadSignature) { context.badSignature = true; } OCSPResponseExtension extension; if (aORT == ORTCriticalExtension || aORT == ORTNoncriticalExtension) { SECItem oidItem = { siBuffer, nullptr, 0 }; // 1.3.6.1.4.1.13769.666.666.666 is the root of Mozilla's testing OID space static const char* testExtensionOID = "1.3.6.1.4.1.13769.666.666.666.1.500.9.2"; if (SEC_StringToOID(aArena, &oidItem, testExtensionOID, PL_strlen(testExtensionOID)) != SECSuccess) { return nullptr; } DERTemplate oidTemplate[2] = { { DER_OBJECT_ID, 0 }, { 0 } }; extension.id.data = nullptr; extension.id.len = 0; if (DER_Encode(aArena, &extension.id, oidTemplate, &oidItem) != SECSuccess) { return nullptr; } extension.critical = (aORT == ORTCriticalExtension); static const uint8_t value[2] = { 0x05, 0x00 }; extension.value.data = const_cast<uint8_t*>(value); extension.value.len = PR_ARRAY_SIZE(value); extension.next = nullptr; context.extensions = &extension; } if (aORT == ORTEmptyExtensions) { context.includeEmptyExtensions = true; } if (!signerCert) { signerCert = CERT_DupCertificate(issuerCert.get()); } context.signerPrivateKey = PK11_FindKeyByAnyCert(signerCert.get(), nullptr); if (!context.signerPrivateKey) { PrintPRError("PK11_FindKeyByAnyCert failed"); return nullptr; } SECItem* response = CreateEncodedOCSPResponse(context); if (!response) { PrintPRError("CreateEncodedOCSPResponse failed"); return nullptr; } SECItemArray* arr = SECITEM_AllocArray(aArena, nullptr, 1); if (!arr) { PrintPRError("SECITEM_AllocArray failed"); return nullptr; } arr->items[0].data = response->data; arr->items[0].len = response->len; return arr; }