// 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; }
// Let derIssuer be the DER encoding of the issuer of aCert. // Let derPublicKey be the DER encoding of the public key of aIssuerCert. // Let serialNumber be the bytes of the serial number of aCert. // The value calculated is SHA384(derIssuer || derPublicKey || serialNumber). // Because the DER encodings include the length of the data encoded, // there do not exist A(derIssuerA, derPublicKeyA, serialNumberA) and // B(derIssuerB, derPublicKeyB, serialNumberB) such that the concatenation of // each triplet results in the same string of bytes but where each part in A is // not equal to its counterpart in B. This is important because as a result it // is computationally infeasible to find collisions that would subvert this // cache (given that SHA384 is a cryptographically-secure hash function). static SECStatus CertIDHash(SHA384Buffer& buf, const CertID& certID, const char* aIsolationKey) { ScopedPK11Context context(PK11_CreateDigestContext(SEC_OID_SHA384)); if (!context) { return SECFailure; } SECStatus rv = PK11_DigestBegin(context.get()); if (rv != SECSuccess) { return rv; } SECItem certIDIssuer = UnsafeMapInputToSECItem(certID.issuer); rv = PK11_DigestOp(context.get(), certIDIssuer.data, certIDIssuer.len); if (rv != SECSuccess) { return rv; } SECItem certIDIssuerSubjectPublicKeyInfo = UnsafeMapInputToSECItem(certID.issuerSubjectPublicKeyInfo); rv = PK11_DigestOp(context.get(), certIDIssuerSubjectPublicKeyInfo.data, certIDIssuerSubjectPublicKeyInfo.len); if (rv != SECSuccess) { return rv; } SECItem certIDSerialNumber = UnsafeMapInputToSECItem(certID.serialNumber); rv = PK11_DigestOp(context.get(), certIDSerialNumber.data, certIDSerialNumber.len); if (rv != SECSuccess) { return rv; } if (aIsolationKey) { rv = PK11_DigestOp(context.get(), (const unsigned char*) aIsolationKey, strlen(aIsolationKey)); if (rv != SECSuccess) { return rv; } } uint32_t outLen = 0; rv = PK11_DigestFinal(context.get(), buf, &outLen, SHA384_LENGTH); if (outLen != SHA384_LENGTH) { return SECFailure; } return rv; }
Result AppTrustDomain::FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time) { MOZ_ASSERT(mTrustedRoot); if (!mTrustedRoot) { return Result::FATAL_ERROR_INVALID_STATE; } // TODO(bug 1035418): If/when mozilla::pkix relaxes the restriction that // FindIssuer must only pass certificates with a matching subject name to // checker.Check, we can stop using CERT_CreateSubjectCertList and instead // use logic like this: // // 1. First, try the trusted trust anchor. // 2. Secondly, iterate through the certificates that were stored in the CMS // message, passing each one to checker.Check. SECItem encodedIssuerNameSECItem = UnsafeMapInputToSECItem(encodedIssuerName); UniqueCERTCertList candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameSECItem, 0, false)); if (candidates) { for (CERTCertListNode* n = CERT_LIST_HEAD(candidates); !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) { Input certDER; Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len); if (rv != Success) { continue; // probably too big } bool keepGoing; rv = checker.Check(certDER, nullptr/*additionalNameConstraints*/, keepGoing); if (rv != Success) { return rv; } if (!keepGoing) { break; } } } return Success; }
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; }