// TODO: Remove #include "pkix/pkixnss.h", #include "cert.h", // #include "ScopedPtr.h", etc. when this is rewritten to be independent of // NSS. Result CheckNameConstraints(Input encodedNameConstraints, const BackCert& firstChild, KeyPurposeId requiredEKUIfPresent) { ScopedPtr<PLArenaPool, PORT_FreeArena_false> arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return Result::FATAL_ERROR_NO_MEMORY; } SECItem encodedNameConstraintsSECItem = UnsafeMapInputToSECItem(encodedNameConstraints); // Owned by arena const CERTNameConstraints* constraints = CERT_DecodeNameConstraintsExtension(arena.get(), &encodedNameConstraintsSECItem); if (!constraints) { return MapPRErrorCodeToResult(PR_GetError()); } for (const BackCert* child = &firstChild; child; child = child->childCert) { SECItem childCertDER = UnsafeMapInputToSECItem(child->GetDER()); ScopedPtr<CERTCertificate, CERT_DestroyCertificate> nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &childCertDER, nullptr, false, true)); if (!nssCert) { return MapPRErrorCodeToResult(PR_GetError()); } bool includeCN = child->endEntityOrCA == EndEntityOrCA::MustBeEndEntity && requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth; // owned by arena const CERTGeneralName* names(CERT_GetConstrainedCertificateNames(nssCert.get(), arena.get(), includeCN)); if (!names) { return MapPRErrorCodeToResult(PR_GetError()); } CERTGeneralName* currentName = const_cast<CERTGeneralName*>(names); do { if (CERT_CheckNameSpace(arena.get(), constraints, currentName) != SECSuccess) { // XXX: It seems like CERT_CheckNameSpace doesn't always call // PR_SetError when it fails, so we ignore what PR_GetError would // return. NSS's cert_VerifyCertChainOld does something similar. return Result::ERROR_CERT_NOT_IN_NAME_SPACE; } currentName = CERT_GetNextGeneralName(currentName); } while (currentName != names); } return Success; }
Result AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy, Input candidateCertDER, /*out*/ TrustLevel& trustLevel) { MOZ_ASSERT(policy.IsAnyPolicy()); MOZ_ASSERT(mTrustedRoot); if (!policy.IsAnyPolicy()) { return Result::FATAL_ERROR_INVALID_ARGS; } if (!mTrustedRoot) { return Result::FATAL_ERROR_INVALID_STATE; } // Handle active distrust of the certificate. // XXX: This would be cleaner and more efficient if we could get the trust // information without constructing a CERTCertificate here, but NSS doesn't // expose it in any other easy-to-use fashion. SECItem candidateCertDERSECItem = UnsafeMapInputToSECItem(candidateCertDER); UniqueCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &candidateCertDERSECItem, nullptr, false, true)); if (!candidateCert) { return MapPRErrorCodeToResult(PR_GetError()); } CERTCertTrust trust; if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) { uint32_t flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning); // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit, // because we can have active distrust for either type of cert. Note that // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the // relevant trust bit isn't set then that means the cert must be considered // distrusted. uint32_t relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA : CERTDB_TRUSTED; if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) == CERTDB_TERMINAL_RECORD) { trustLevel = TrustLevel::ActivelyDistrusted; return Success; } } // mTrustedRoot is the only trust anchor for this validation. if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) { trustLevel = TrustLevel::TrustAnchor; return Success; } trustLevel = TrustLevel::InheritsTrust; return Success; }
Result OCSPCache::Entry::Init(const CertID& aCertID) { SECStatus srv = CertIDHash(mIDHash, aCertID); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; }
Result AppTrustDomain::IsChainValid(const DERArray& certChain, Time time) { SECStatus srv = ConstructCERTCertListFromReversedDERArray(certChain, mCertChain); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; }
Result CertListContainsExpectedKeys(const CERTCertList* certList, const char* hostname, Time time, CertVerifier::PinningMode pinningMode) { if (pinningMode == CertVerifier::pinningDisabled) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Pinning is disabled; not checking keys.")); return Success; } if (!certList) { return Result::FATAL_ERROR_INVALID_ARGS; } CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); if (CERT_LIST_END(rootNode, certList)) { return Result::FATAL_ERROR_INVALID_ARGS; } bool isBuiltInRoot = false; SECStatus srv = IsCertBuiltInRoot(rootNode->cert, isBuiltInRoot); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } // If desired, the user can enable "allow user CA MITM mode", in which // case key pinning is not enforced for certificates that chain to trust // anchors that are not in Mozilla's root program if (!isBuiltInRoot && pinningMode == CertVerifier::pinningAllowUserCAMITM) { return Success; } bool enforceTestMode = (pinningMode == CertVerifier::pinningEnforceTestMode); if (PublicKeyPinningService::ChainHasValidPins(certList, hostname, time, enforceTestMode)) { return Success; } return Result::ERROR_KEY_PINNING_FAILURE; }
Result DigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH, "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)"); if (digestBufLen != TrustDomain::DIGEST_LENGTH) { PR_NOT_REACHED("invalid hash length"); return Result::FATAL_ERROR_INVALID_ARGS; } SECItem itemSECItem = UnsafeMapInputToSECItem(item); if (itemSECItem.len > static_cast<decltype(itemSECItem.len)>( std::numeric_limits<int32_t>::max())) { PR_NOT_REACHED("large items should not be possible here"); return Result::FATAL_ERROR_INVALID_ARGS; } SECStatus srv = PK11_HashBuf(SEC_OID_SHA1, digestBuf, itemSECItem.data, static_cast<int32_t>(itemSECItem.len)); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; }
Result VerifySignedData(const SignedDataWithSignature& sd, Input subjectPublicKeyInfo, void* pkcs11PinArg) { // See bug 921585. if (sd.data.GetLength() > static_cast<unsigned int>(std::numeric_limits<int>::max())) { return Result::FATAL_ERROR_INVALID_ARGS; } SECOidTag pubKeyAlg; SECOidTag digestAlg; switch (sd.algorithm) { case SignatureAlgorithm::ecdsa_with_sha512: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA512; break; case SignatureAlgorithm::ecdsa_with_sha384: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA384; break; case SignatureAlgorithm::ecdsa_with_sha256: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA256; break; case SignatureAlgorithm::ecdsa_with_sha1: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA1; break; case SignatureAlgorithm::rsa_pkcs1_with_sha512: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA512; break; case SignatureAlgorithm::rsa_pkcs1_with_sha384: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA384; break; case SignatureAlgorithm::rsa_pkcs1_with_sha256: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA256; break; case SignatureAlgorithm::rsa_pkcs1_with_sha1: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA1; break; case SignatureAlgorithm::dsa_with_sha256: pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; digestAlg = SEC_OID_SHA256; break; case SignatureAlgorithm::dsa_with_sha1: pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; digestAlg = SEC_OID_SHA1; break; default: PR_NOT_REACHED("unknown signature algorithm"); return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } Result rv; ScopedSECKeyPublicKey pubKey; rv = CheckPublicKeySize(subjectPublicKeyInfo, pubKey); if (rv != Success) { return rv; } // The static_cast is safe according to the check above that references // bug 921585. SECItem dataSECItem(UnsafeMapInputToSECItem(sd.data)); SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature)); SECStatus srv = VFY_VerifyDataDirect(dataSECItem.data, static_cast<int>(dataSECItem.len), pubKey.get(), &signatureSECItem, pubKeyAlg, digestAlg, nullptr, pkcs11PinArg); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; }
Result VerifySignedData(const SignedDataWithSignature& sd, Input subjectPublicKeyInfo, void* pkcs11PinArg) { SECOidTag pubKeyAlg; SECOidTag digestAlg; switch (sd.algorithm) { case SignatureAlgorithm::ecdsa_with_sha512: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA512; break; case SignatureAlgorithm::ecdsa_with_sha384: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA384; break; case SignatureAlgorithm::ecdsa_with_sha256: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA256; break; case SignatureAlgorithm::ecdsa_with_sha1: pubKeyAlg = SEC_OID_ANSIX962_EC_PUBLIC_KEY; digestAlg = SEC_OID_SHA1; break; case SignatureAlgorithm::rsa_pkcs1_with_sha512: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA512; break; case SignatureAlgorithm::rsa_pkcs1_with_sha384: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA384; break; case SignatureAlgorithm::rsa_pkcs1_with_sha256: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA256; break; case SignatureAlgorithm::rsa_pkcs1_with_sha1: pubKeyAlg = SEC_OID_PKCS1_RSA_ENCRYPTION; digestAlg = SEC_OID_SHA1; break; case SignatureAlgorithm::dsa_with_sha256: pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; digestAlg = SEC_OID_SHA256; break; case SignatureAlgorithm::dsa_with_sha1: pubKeyAlg = SEC_OID_ANSIX9_DSA_SIGNATURE; digestAlg = SEC_OID_SHA1; break; default: PR_NOT_REACHED("unknown signature algorithm"); return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } Result rv; ScopedSECKeyPublicKey pubKey; rv = CheckPublicKeySize(subjectPublicKeyInfo, pubKey); if (rv != Success) { return rv; } // The static_cast is safe as long as the length of the data in sd.data can // fit in an int. Right now that length is stored as a uint16_t, so this // works. In the future this may change, hence the assertion. // See also bug 921585. static_assert(sizeof(decltype(sd.data.GetLength())) < sizeof(int), "sd.data.GetLength() must fit in an int"); SECItem dataSECItem(UnsafeMapInputToSECItem(sd.data)); SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature)); SECStatus srv = VFY_VerifyDataDirect(dataSECItem.data, static_cast<int>(dataSECItem.len), pubKey.get(), &signatureSECItem, pubKeyAlg, digestAlg, nullptr, pkcs11PinArg); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; }