Bug 1029247, Part 2: Parse certificates using mozilla::pkix::der, r=keeler

--HG--
extra : rebase_source : e093922497d005734c590a59f175993a7715bce8
This commit is contained in:
Brian Smith 2014-07-03 16:59:42 -07:00
parent 03a7a82015
commit 3fbb0b3f6f
26 changed files with 965 additions and 751 deletions

View File

@ -542,7 +542,7 @@ VerifyCertificate(CERTCertificate* signerCert, void* voidContext, void* pinArg)
if (trustDomain.SetTrustedRoot(context.trustedRoot) != SECSuccess) {
return MapSECStatus(SECFailure);
}
if (BuildCertChain(trustDomain, signerCert, PR_Now(),
if (BuildCertChain(trustDomain, signerCert->derCert, PR_Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature,
KeyPurposeId::id_kp_codeSigning,

View File

@ -162,7 +162,7 @@ AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
}
SECStatus
AppTrustDomain::VerifySignedData(const CERTSignedData* signedData,
AppTrustDomain::VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo)
{
return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,

View File

@ -28,7 +28,7 @@ public:
PRTime time,
/*out*/ mozilla::pkix::ScopedCERTCertList& results)
MOZ_OVERRIDE;
SECStatus VerifySignedData(const CERTSignedData* signedData,
SECStatus VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo) MOZ_OVERRIDE;
SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
const mozilla::pkix::CertID& certID, PRTime time,

View File

@ -164,17 +164,17 @@ BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
const SECItem* stapledOCSPResponse,
ScopedCERTCertList& builtChain)
{
SECStatus rv = BuildCertChain(trustDomain, cert, time,
SECStatus rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity, ku1,
eku, requiredPolicy,
stapledOCSPResponse, builtChain);
if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
rv = BuildCertChain(trustDomain, cert, time,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity, ku2,
eku, requiredPolicy,
stapledOCSPResponse, builtChain);
if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
rv = BuildCertChain(trustDomain, cert, time,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity, ku3,
eku, requiredPolicy,
stapledOCSPResponse, builtChain);
@ -237,7 +237,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
// just use trustEmail as it is the closest alternative.
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature,
KeyPurposeId::id_kp_clientAuth,
@ -303,7 +303,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
case certificateUsageSSLCA: {
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeCA,
KeyUsage::keyCertSign,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
@ -314,7 +315,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
case certificateUsageEmailSigner: {
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature,
KeyPurposeId::id_kp_emailProtection,
@ -329,14 +330,14 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
// based on the result of the verification(s).
NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::keyEncipherment, // RSA
KeyPurposeId::id_kp_emailProtection,
CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
rv = BuildCertChain(trustDomain, cert, time,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::keyAgreement, // ECDH/DH
KeyPurposeId::id_kp_emailProtection,
@ -349,7 +350,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
case certificateUsageObjectSigner: {
NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
mOCSPCache, pinArg, ocspGETConfig);
rv = BuildCertChain(trustDomain, cert, time,
rv = BuildCertChain(trustDomain, cert->derCert, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature,
KeyPurposeId::id_kp_codeSigning,
@ -379,22 +380,23 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
pinArg, ocspGETConfig);
rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
rv = BuildCertChain(sslTrust, cert->derCert, time, endEntityOrCA,
keyUsage, eku, CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
pinArg, ocspGETConfig);
rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
eku, CertPolicyId::anyPolicy,
rv = BuildCertChain(emailTrust, cert->derCert, time, endEntityOrCA,
keyUsage, eku, CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
ocspFetching, mOCSPCache,
pinArg, ocspGETConfig);
rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
keyUsage, eku, CertPolicyId::anyPolicy,
stapledOCSPResponse, builtChain);
rv = BuildCertChain(objectSigningTrust, cert->derCert, time,
endEntityOrCA, keyUsage, eku,
CertPolicyId::anyPolicy, stapledOCSPResponse,
builtChain);
}
}

View File

@ -147,7 +147,7 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
}
SECStatus
NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData,
NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo)
{
return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,

View File

@ -63,7 +63,7 @@ public:
const SECItem& candidateCertDER,
/*out*/ mozilla::pkix::TrustLevel* trustLevel);
virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
virtual SECStatus VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo);
virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,

View File

@ -53,9 +53,9 @@ function run_test() {
load_cert("v3_ca_missing_bc", "CTu,CTu,CTu");
check_ok_ca(cert_from_file('v1_ca.der'));
check_ca_err(cert_from_file('v1_ca_bc.der'), SEC_ERROR_EXTENSION_VALUE_INVALID);
check_ca_err(cert_from_file('v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_ca_err(cert_from_file('v2_ca.der'), SEC_ERROR_CA_CERT_INVALID);
check_ca_err(cert_from_file('v2_ca_bc.der'), SEC_ERROR_EXTENSION_VALUE_INVALID);
check_ca_err(cert_from_file('v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_ok_ca(cert_from_file('v3_ca.der'));
check_ca_err(cert_from_file('v3_ca_missing_bc.der'), SEC_ERROR_CA_CERT_INVALID);
@ -82,21 +82,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v1_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int-v1_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int-v1_ca.der'), SEC_ERROR_BAD_DER);
// v1 intermediate with v3 extensions. CA is invalid.
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v1_int_bc-v1_ca.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca.der'), ee_error);
check_ca_err(cert_from_file('v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
// A v2 intermediate with a v1 CA
@ -107,21 +104,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v2_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int-v1_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int-v1_ca.der'), SEC_ERROR_BAD_DER);
// A v2 intermediate with basic constraints (not allowed in insanity)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v2_int_bc-v1_ca.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca.der'), ee_error);
// A v2 intermediate with basic constraints (not allowed in mozilla::pkix)
check_ca_err(cert_from_file('v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
// Section is OK. A x509 v3 CA MUST have bc
@ -133,9 +127,8 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v1_ca.der'), SEC_ERROR_BAD_DER);
// It is valid for a v1 ca to sign a v3 intemediate.
@ -144,10 +137,8 @@ function run_test() {
check_ok(cert_from_file('v2_ee-v3_int-v1_ca.der'));
check_ok(cert_from_file('v3_missing_bc_ee-v3_int-v1_ca.der'));
check_ok(cert_from_file('v3_bc_ee-v3_int-v1_ca.der'));
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int-v1_ca.der'), SEC_ERROR_BAD_DER);
// The next groups change the v1 ca for a v1 ca with base constraints
@ -162,21 +153,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v1_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
// Using a v1 intermediate with v3 extenstions (invalid).
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v1_int_bc-v1_ca_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca_bc.der'), ee_error);
check_ca_err(cert_from_file('v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
// Using v2 intermediate
@ -187,21 +175,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v2_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
// Using a v2 intermediate with basic constraints (invalid)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v2_int_bc-v1_ca_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca_bc.der'), ee_error);
check_ca_err(cert_from_file('v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
// Using a v3 intermediate that is missing basic constraints (invalid)
@ -212,21 +197,20 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
// these should pass assuming we are OK with v1 ca signing v3 intermediates
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v3_int-v1_ca_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v3_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v3_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int-v1_ca_bc.der'), ee_error);
check_ca_err(cert_from_file('v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int-v1_ca_bc.der'), SEC_ERROR_BAD_DER);
@ -242,21 +226,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v1_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int-v2_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca.der'), ee_error)
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int-v2_ca.der'), SEC_ERROR_BAD_DER);
// v2 ca, v1 intermediate with basic constraints (invalid)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v1_int_bc-v2_ca.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca.der'), ee_error);
check_ca_err(cert_from_file('v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
// v2 ca, v2 intermediate
@ -267,21 +248,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v2_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int-v2_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int-v2_ca.der'), SEC_ERROR_BAD_DER);
// v2 ca, v2 intermediate with basic constraints (invalid)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v2_int_bc-v2_ca.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca.der'), ee_error);
check_ca_err(cert_from_file('v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
// v2 ca, v3 intermediate missing basic constraints
@ -292,9 +270,8 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v2_ca.der'), SEC_ERROR_BAD_DER);
// v2 ca, v3 intermediate
@ -305,10 +282,8 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int-v2_ca.der'), ee_error);
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int-v2_ca.der'), SEC_ERROR_BAD_DER);
// v2 ca, v1 intermediate
@ -319,21 +294,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v1_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
// v2 ca, v1 intermediate with bc (invalid)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v1_int_bc-v2_ca_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca_bc.der'), ee_error);
check_ca_err(cert_from_file('v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
// v2 ca, v2 intermediate
@ -344,21 +316,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v2_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
// v2 ca, v2 intermediate with bc (invalid)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v2_int_bc-v2_ca_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca_bc.der'), ee_error);
check_ca_err(cert_from_file('v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
// v2 ca, invalid v3 intermediate
@ -369,21 +338,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), ee_error)
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
// v2 ca, valid v3 intermediate (is OK if we use 'classic' semantics)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v3_int-v2_ca_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v3_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v3_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int-v2_ca_bc.der'), ee_error);
check_ca_err(cert_from_file('v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int-v2_ca_bc.der'), SEC_ERROR_BAD_DER);
//////////////
@ -398,21 +364,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v1_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int-v3_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int-v3_ca.der'), SEC_ERROR_BAD_DER);
// A v1 intermediate with v3 extensions
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v1_int_bc-v3_ca.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca.der'), ee_error);
check_ca_err(cert_from_file('v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
// reject a v2 cert as intermediate
@ -423,21 +386,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v2_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int-v3_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int-v3_ca.der'), SEC_ERROR_BAD_DER);
// v2 intermediate with bc (invalid)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v2_int_bc-v3_ca.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca.der'), ee_error);
check_ca_err(cert_from_file('v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
// invalid v3 intermediate
@ -448,9 +408,8 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v3_ca.der'), SEC_ERROR_BAD_DER);
// I dont think that v3 intermediates should be allowed to sign v1 or v2
@ -460,10 +419,8 @@ function run_test() {
check_ok(cert_from_file('v2_ee-v3_int-v3_ca.der'));
check_ok(cert_from_file('v3_missing_bc_ee-v3_int-v3_ca.der'));
check_ok(cert_from_file('v3_bc_ee-v3_int-v3_ca.der'));
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int-v3_ca.der'), SEC_ERROR_BAD_DER);
// v3 CA, invalid v3 intermediate
@ -474,21 +431,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
// Int v1 with BC that is just invalid (classic fail insanity OK)
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v1_int_bc-v3_ca_missing_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), ee_error);
check_ca_err(cert_from_file('v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v1_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
// Good section (all fail)
@ -499,21 +453,18 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
// v2 intermediate (even with basic constraints) is invalid
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_ca_err(cert_from_file('v2_int_bc-v3_ca_missing_bc.der'), ca_error);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), ee_error);
check_ca_err(cert_from_file('v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v1_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_missing_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v3_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v2_int_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
// v3 intermediate missing basic constraints is invalid
@ -524,9 +475,8 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int_missing_bc-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
// With a v3 root missing bc and valid v3 intermediate
@ -537,9 +487,7 @@ function run_test() {
check_cert_err(cert_from_file('v2_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_missing_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v3_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
ca_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
ee_error = SEC_ERROR_EXTENSION_VALUE_INVALID;
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca_missing_bc.der'), ee_error);
check_cert_err(cert_from_file('v1_bc_ee-v3_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v2_bc_ee-v3_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
check_cert_err(cert_from_file('v4_bc_ee-v3_int-v3_ca_missing_bc.der'), SEC_ERROR_BAD_DER);
}

View File

@ -89,10 +89,8 @@ namespace mozilla { namespace pkix {
// distrust.
// TODO(bug 968451): Document more of these.
SECStatus BuildCertChain(TrustDomain& trustDomain,
const CERTCertificate* cert,
PRTime time,
EndEntityOrCA endEntityOrCA,
SECStatus BuildCertChain(TrustDomain& trustDomain, const SECItem& cert,
PRTime time, EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
@ -100,7 +98,7 @@ SECStatus BuildCertChain(TrustDomain& trustDomain,
/*out*/ ScopedCERTCertList& results);
// Verify the given signed data using the given public key.
SECStatus VerifySignedData(const CERTSignedData* sd,
SECStatus VerifySignedData(const CERTSignedData& sd,
const SECItem& subjectPublicKeyInfo,
void* pkcs11PinArg);

View File

@ -158,7 +158,7 @@ public:
//
// Most implementations of this function should probably forward the call
// directly to mozilla::pkix::VerifySignedData.
virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
virtual SECStatus VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo) = 0;
// issuerCertToDup is only non-const so CERT_DupCertificate can be called on

View File

@ -27,135 +27,11 @@
#include <limits>
#include "pkixcheck.h"
#include "pkixder.h"
namespace mozilla { namespace pkix {
// We assume ext has been zero-initialized by its constructor and otherwise
// not modified.
//
// TODO(perf): This sorting of extensions should be be moved into the
// certificate decoder so that the results are cached with the certificate, so
// that the decoding doesn't have to happen more than once per cert.
Result
BackCert::Init(const SECItem& certDER)
{
// XXX: Currently-known uses of mozilla::pkix create CERTCertificate objects
// for all certs anyway, so the overhead of CERT_NewTempCertificate will be
// reduced to a lookup in NSS's SECItem* -> CERTCertificate cache and
// a CERT_DupCertificate. Eventually, we should parse the certificate using
// mozilla::pkix::der and avoid the need to create a CERTCertificate at all.
nssCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
const_cast<SECItem*>(&certDER),
nullptr, false, true);
if (!nssCert) {
return MapSECStatus(SECFailure);
}
if (nssCert->version.len == 1 &&
nssCert->version.data[0] == static_cast<uint8_t>(der::Version::v3)) {
version = der::Version::v3;
} else if (nssCert->version.len == 1 &&
nssCert->version.data[0] == static_cast<uint8_t>(der::Version::v2)) {
version = der::Version::v2;
} else if (nssCert->version.len == 1 &&
nssCert->version.data[0] == static_cast<uint8_t>(der::Version::v2)) {
// XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we
// do here for compatibility reasons.
version = der::Version::v1;
} else if (nssCert->version.len == 0) {
version = der::Version::v1;
} else {
// Explicit encoding of v1 is not allowed. We do not support any other
// version except v3.
return Fail(RecoverableError, SEC_ERROR_BAD_DER);
}
const CERTCertExtension* const* exts = nssCert->extensions;
if (!exts) {
return Success;
}
// Extensions are only allowed in v3 certificates, not v1 or v2. Also, we
// use presence of the basic constraints extension with isCA==true to decide
// whether to treat a certificate as a CA certificate, and we don't want to
// allow v1 or v2 intermediate CA certificates; this check is part of that
// enforcement as well.
if (version != der::Version::v3) {
return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
}
const SECItem* dummyEncodedSubjectKeyIdentifier = nullptr;
const SECItem* dummyEncodedAuthorityKeyIdentifier = nullptr;
const SECItem* dummyEncodedSubjectAltName = nullptr;
for (const CERTCertExtension* ext = *exts; ext; ext = *++exts) {
const SECItem** out = nullptr;
// python DottedOIDToCode.py id-ce 2.5.29
static const uint8_t id_ce[] = {
0x55, 0x1d
};
// python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
static const uint8_t id_pe_authorityInfoAccess[] = {
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
};
if (ext->id.len == PR_ARRAY_SIZE(id_ce) + 1 &&
!memcmp(ext->id.data, id_ce, PR_ARRAY_SIZE(id_ce))) {
switch (ext->id.data[ext->id.len - 1]) {
case 14: out = &dummyEncodedSubjectKeyIdentifier; break; // bug 965136
case 15: out = &encodedKeyUsage; break;
case 17: out = &dummyEncodedSubjectAltName; break; // bug 970542
case 19: out = &encodedBasicConstraints; break;
case 30: out = &encodedNameConstraints; break;
case 32: out = &encodedCertificatePolicies; break;
case 35: out = &dummyEncodedAuthorityKeyIdentifier; break; // bug 965136
case 37: out = &encodedExtendedKeyUsage; break;
case 54: out = &encodedInhibitAnyPolicy; break; // Bug 989051
}
} else if (ext->id.len == PR_ARRAY_SIZE(id_pe_authorityInfoAccess) &&
!memcmp(ext->id.data, id_pe_authorityInfoAccess,
PR_ARRAY_SIZE(id_pe_authorityInfoAccess))) {
// We should remember the value of the encoded AIA extension here, but
// since our TrustDomain implementations get the OCSP URI using
// CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to.
out = &encodedAuthorityInfoAccess;
}
// If this is an extension we don't understand and it's marked critical,
// we must reject this certificate.
// (The only valid explicit value of the critical flag is TRUE because
// it is defined as BOOLEAN DEFAULT FALSE, so we just assume it is true.)
if (!out && ext->critical.data && ext->critical.len > 0) {
return Fail(RecoverableError, SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
}
if (out) {
// This is an extension we understand. Save it in results unless we've
// already found the extension previously.
if (*out) {
// Duplicate extension
return Fail(RecoverableError, SEC_ERROR_EXTENSION_VALUE_INVALID);
}
*out = &ext->value;
}
}
return Success;
}
Result
BackCert::VerifyOwnSignatureWithKey(TrustDomain& trustDomain,
const SECItem& subjectPublicKeyInfo) const
{
return MapSECStatus(trustDomain.VerifySignedData(&nssCert->signatureWrap,
subjectPublicKeyInfo));
}
static Result BuildForward(TrustDomain& trustDomain,
BackCert& subject,
const BackCert& subject,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
@ -168,7 +44,7 @@ static Result BuildForward(TrustDomain& trustDomain,
// The code that executes in the inner loop of BuildForward
static Result
BuildForwardInner(TrustDomain& trustDomain,
BackCert& subject,
const BackCert& subject,
PRTime time,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
@ -176,8 +52,9 @@ BuildForwardInner(TrustDomain& trustDomain,
unsigned int subCACount,
/*out*/ ScopedCERTCertList& results)
{
BackCert potentialIssuer(&subject, BackCert::IncludeCN::No);
Result rv = potentialIssuer.Init(potentialIssuerDER);
BackCert potentialIssuer(potentialIssuerDER, &subject,
BackCert::IncludeCN::No);
Result rv = potentialIssuer.Init();
if (rv != Success) {
return rv;
}
@ -189,7 +66,7 @@ BuildForwardInner(TrustDomain& trustDomain,
// TODO: this doesn't account for subjectAltNames!
// TODO(perf): This probably can and should be optimized in some way.
bool loopDetected = false;
for (BackCert* prev = potentialIssuer.childCert;
for (const BackCert* prev = potentialIssuer.childCert;
!loopDetected && prev != nullptr; prev = prev->childCert) {
if (SECITEM_ItemsAreEqual(&potentialIssuer.GetSubjectPublicKeyInfo(),
&prev->GetSubjectPublicKeyInfo()) &&
@ -214,8 +91,14 @@ BuildForwardInner(TrustDomain& trustDomain,
return rv;
}
return subject.VerifyOwnSignatureWithKey(
trustDomain, potentialIssuer.GetSubjectPublicKeyInfo());
SECStatus srv = trustDomain.VerifySignedData(
subject.GetSignedData(),
potentialIssuer.GetSubjectPublicKeyInfo());
if (srv != SECSuccess) {
return MapSECStatus(srv);
}
return Success;
}
// Recursively build the path from the given subject certificate to the root.
@ -226,7 +109,7 @@ BuildForwardInner(TrustDomain& trustDomain,
// pkix/pkix.h.
static Result
BuildForward(TrustDomain& trustDomain,
BackCert& subject,
const BackCert& subject,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
@ -265,13 +148,15 @@ BuildForward(TrustDomain& trustDomain,
if (!results) {
return MapSECStatus(SECFailure);
}
for (BackCert* cert = &subject; cert; cert = cert->childCert) {
CERTCertificate* dup = CERT_DupCertificate(cert->GetNSSCert());
if (CERT_AddCertToListHead(results.get(), dup) != SECSuccess) {
CERT_DestroyCertificate(dup);
for (const BackCert* cert = &subject; cert; cert = cert->childCert) {
ScopedCERTCertificate
nssCert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
const_cast<SECItem*>(&cert->GetDER()),
nullptr, false, true));
if (CERT_AddCertToListHead(results.get(), nssCert.get()) != SECSuccess) {
return MapSECStatus(SECFailure);
}
// dup is now owned by results.
nssCert.release(); // nssCert is now owned by results.
}
// This must be done here, after the chain is built but before any
@ -326,7 +211,7 @@ BuildForward(TrustDomain& trustDomain,
SECStatus srv = trustDomain.CheckRevocation(
endEntityOrCA, certID, time,
stapledOCSPResponse,
subject.encodedAuthorityInfoAccess);
subject.GetAuthorityInfoAccess());
if (srv != SECSuccess) {
return MapSECStatus(SECFailure);
}
@ -365,22 +250,14 @@ BuildForward(TrustDomain& trustDomain,
}
SECStatus
BuildCertChain(TrustDomain& trustDomain,
const CERTCertificate* nssCert,
PRTime time,
EndEntityOrCA endEntityOrCA,
BuildCertChain(TrustDomain& trustDomain, const SECItem& certDER,
PRTime time, EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
/*optional*/ const SECItem* stapledOCSPResponse,
/*out*/ ScopedCERTCertList& results)
{
if (!nssCert) {
PR_NOT_REACHED("null cert passed to BuildCertChain");
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
// XXX: Support the legacy use of the subject CN field for indicating the
// domain name the certificate is valid for.
BackCert::IncludeCN includeCN
@ -389,8 +266,8 @@ BuildCertChain(TrustDomain& trustDomain,
? BackCert::IncludeCN::Yes
: BackCert::IncludeCN::No;
BackCert cert(nullptr, includeCN);
Result rv = cert.Init(nssCert->derCert);
BackCert cert(certDER, nullptr, includeCN);
Result rv = cert.Init();
if (rv != Success) {
return SECFailure;
}

View File

@ -0,0 +1,252 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Copyright 2014 Mozilla Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pkix/bind.h"
#include "pkixutil.h"
namespace mozilla { namespace pkix {
Result
BackCert::Init()
{
// Certificate ::= SEQUENCE {
// tbsCertificate TBSCertificate,
// signatureAlgorithm AlgorithmIdentifier,
// signatureValue BIT STRING }
der::Input tbsCertificate;
// The scope of |input| and |certificate| are limited to this block so we
// don't accidentally confuse them for tbsCertificate later.
{
der::Input input;
if (input.Init(der.data, der.len) != der::Success) {
return MapSECStatus(SECFailure);
}
der::Input certificate;
if (der::ExpectTagAndGetValue(input, der::SEQUENCE, certificate)
!= der::Success) {
return MapSECStatus(SECFailure);
}
if (der::End(input) != der::Success) {
return MapSECStatus(SECFailure);
}
if (der::SignedData(certificate, tbsCertificate, signedData)
!= der::Success) {
return MapSECStatus(SECFailure);
}
if (der::End(certificate) != der::Success) {
return MapSECStatus(SECFailure);
}
}
// TBSCertificate ::= SEQUENCE {
// version [0] EXPLICIT Version DEFAULT v1,
// serialNumber CertificateSerialNumber,
// signature AlgorithmIdentifier,
// issuer Name,
// validity Validity,
// subject Name,
// subjectPublicKeyInfo SubjectPublicKeyInfo,
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
// -- If present, version MUST be v2 or v3
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
// -- If present, version MUST be v2 or v3
// extensions [3] EXPLICIT Extensions OPTIONAL
// -- If present, version MUST be v3
// }
if (der::OptionalVersion(tbsCertificate, version) != der::Success) {
return MapSECStatus(SECFailure);
}
if (der::CertificateSerialNumber(tbsCertificate, serialNumber)
!= der::Success) {
return MapSECStatus(SECFailure);
}
// XXX: Ignored. What are we supposed to check? This seems totally redundant
// with Certificate.signatureAlgorithm. Is it important to check that they
// are consistent with each other? It doesn't seem to matter!
SECAlgorithmID signature;
if (der::AlgorithmIdentifier(tbsCertificate, signature) != der::Success) {
return MapSECStatus(SECFailure);
}
if (der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer)
!= der::Success) {
return MapSECStatus(SECFailure);
}
if (der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity)
!= der::Success) {
return MapSECStatus(SECFailure);
}
// TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate
// that the name is syntactically valid, if they care. In Gecko we do this
// implicitly by parsing the certificate into a CERTCertificate object.
// Instead of relying on the caller to do this, we should do it ourselves.
if (der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject)
!= der::Success) {
return MapSECStatus(SECFailure);
}
// TODO(bug XXXXXXX): We defer parsing/validating subjectPublicKeyInfo to
// the point where the public key is needed. For end-entity certificates, we
// assume that the caller will extract the public key and use it somehow; if
// they don't do that then we'll never know whether the key is invalid. On
// the other hand, if the caller never uses the key then in some ways it
// doesn't matter. Regardless, we should parse and validate
// subjectPublicKeyKeyInfo internally.
if (der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE,
subjectPublicKeyInfo) != der::Success) {
return MapSECStatus(SECFailure);
}
static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED;
// RFC 5280 says: "These fields MUST only appear if the version is 2 or 3
// (Section 4.1.2.1). These fields MUST NOT appear if the version is 1."
if (version != der::Version::v1) {
// Ignore issuerUniqueID if present.
if (tbsCertificate.Peek(CSC | 1)) {
if (der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1) != der::Success) {
return MapSECStatus(SECFailure);
}
}
// Ignore subjectUniqueID if present.
if (tbsCertificate.Peek(CSC | 2)) {
if (der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2) != der::Success) {
return MapSECStatus(SECFailure);
}
}
}
// Extensions were added in v3, so only accept extensions in v3 certificates.
if (version == der::Version::v3) {
if (der::OptionalExtensions(tbsCertificate, CSC | 3,
bind(&BackCert::RememberExtension, this, _1, _2,
_3)) != der::Success) {
return MapSECStatus(SECFailure);
}
}
if (der::End(tbsCertificate) != der::Success) {
return MapSECStatus(SECFailure);
}
return Success;
}
der::Result
BackCert::RememberExtension(der::Input& extnID, const SECItem& extnValue,
/*out*/ bool& understood)
{
understood = false;
// python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15
static const uint8_t id_ce_keyUsage[] = {
0x55, 0x1d, 0x0f
};
// python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17
static const uint8_t id_ce_subjectAltName[] = {
0x55, 0x1d, 0x11
};
// python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19
static const uint8_t id_ce_basicConstraints[] = {
0x55, 0x1d, 0x13
};
// python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30
static const uint8_t id_ce_nameConstraints[] = {
0x55, 0x1d, 0x1e
};
// python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32
static const uint8_t id_ce_certificatePolicies[] = {
0x55, 0x1d, 0x20
};
// python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36
static const uint8_t id_ce_policyConstraints[] = {
0x55, 0x1d, 0x24
};
// python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37
static const uint8_t id_ce_extKeyUsage[] = {
0x55, 0x1d, 0x25
};
// python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54
static const uint8_t id_ce_inhibitAnyPolicy[] = {
0x55, 0x1d, 0x36
};
// python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
static const uint8_t id_pe_authorityInfoAccess[] = {
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
};
SECItem* out = nullptr;
// We already enforce the maximum possible constraints for policies so we
// can safely ignore even critical policy constraint extensions.
//
// XXX: Doing it this way won't allow us to detect duplicate
// policyConstraints extensions, but that's OK because (and only because) we
// ignore the extension.
SECItem dummyPolicyConstraints = { siBuffer, nullptr, 0 };
// RFC says "Conforming CAs MUST mark this extension as non-critical" for
// both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use
// them for anything, so we totally ignore them here.
if (extnID.MatchRest(id_ce_keyUsage)) {
out = &keyUsage;
} else if (extnID.MatchRest(id_ce_subjectAltName)) {
out = &subjectAltName;
} else if (extnID.MatchRest(id_ce_basicConstraints)) {
out = &basicConstraints;
} else if (extnID.MatchRest(id_ce_nameConstraints)) {
out = &nameConstraints;
} else if (extnID.MatchRest(id_ce_certificatePolicies)) {
out = &certificatePolicies;
} else if (extnID.MatchRest(id_ce_policyConstraints)) {
out = &dummyPolicyConstraints;
} else if (extnID.MatchRest(id_ce_extKeyUsage)) {
out = &extKeyUsage;
} else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) {
out = &inhibitAnyPolicy;
} else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
out = &authorityInfoAccess;
}
if (out) {
// Don't allow an empty value for any extension we understand. This way, we
// can test out->len to check for duplicates.
if (extnValue.len == 0) {
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
}
if (out->len != 0) {
// Duplicate extension
return der::Fail(SEC_ERROR_EXTENSION_VALUE_INVALID);
}
*out = extnValue;
understood = true;
}
return der::Success;
}
} } // namespace mozilla::pkix

View File

@ -33,36 +33,33 @@
namespace mozilla { namespace pkix {
Result
CheckTimes(const CERTValidity& validity, PRTime time)
CheckValidity(const SECItem& encodedValidity, PRTime time)
{
der::Input notBeforeInput;
if (notBeforeInput.Init(validity.notBefore.data, validity.notBefore.len)
der::Input validity;
if (validity.Init(encodedValidity.data, encodedValidity.len)
!= der::Success) {
return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
}
PRTime notBefore;
if (der::TimeChoice(validity.notBefore.type, notBeforeInput, notBefore)
!= der::Success) {
if (der::TimeChoice(validity, notBefore) != der::Success) {
return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
}
if (time < notBefore) {
return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
}
der::Input notAfterInput;
if (notAfterInput.Init(validity.notAfter.data, validity.notAfter.len)
!= der::Success) {
return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
}
PRTime notAfter;
if (der::TimeChoice(validity.notAfter.type, notAfterInput, notAfter)
!= der::Success) {
if (der::TimeChoice(validity, notAfter) != der::Success) {
return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
}
if (time > notAfter) {
return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
}
if (der::End(validity) != der::Success) {
return MapSECStatus(SECFailure);
}
return Success;
}
@ -408,7 +405,7 @@ CheckBasicConstraints(EndEntityOrCA endEntityOrCA,
Result
CheckNameConstraints(const BackCert& cert)
{
if (!cert.encodedNameConstraints) {
if (!cert.GetNameConstraints()) {
return Success;
}
@ -419,7 +416,7 @@ CheckNameConstraints(const BackCert& cert)
// Owned by arena
const CERTNameConstraints* constraints =
CERT_DecodeNameConstraintsExtension(arena.get(), cert.encodedNameConstraints);
CERT_DecodeNameConstraintsExtension(arena.get(), cert.GetNameConstraints());
if (!constraints) {
return MapSECStatus(SECFailure);
}
@ -619,7 +616,7 @@ CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
Result
CheckIssuerIndependentProperties(TrustDomain& trustDomain,
BackCert& cert,
const BackCert& cert,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
@ -654,15 +651,15 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
// 4.2.1.2. Subject Key Identifier is ignored (see bug 965136).
// 4.2.1.3. Key Usage
rv = CheckKeyUsage(endEntityOrCA, cert.encodedKeyUsage,
rv = CheckKeyUsage(endEntityOrCA, cert.GetKeyUsage(),
requiredKeyUsageIfPresent);
if (rv != Success) {
return rv;
}
// 4.2.1.4. Certificate Policies
rv = CheckCertificatePolicies(endEntityOrCA, cert.encodedCertificatePolicies,
cert.encodedInhibitAnyPolicy, trustLevel,
rv = CheckCertificatePolicies(endEntityOrCA, cert.GetCertificatePolicies(),
cert.GetInhibitAnyPolicy(), trustLevel,
requiredPolicy);
if (rv != Success) {
return rv;
@ -680,8 +677,8 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
// checking.
// 4.2.1.9. Basic Constraints.
rv = CheckBasicConstraints(endEntityOrCA, cert.encodedBasicConstraints,
cert.version, trustLevel, subCACount);
rv = CheckBasicConstraints(endEntityOrCA, cert.GetBasicConstraints(),
cert.GetVersion(), trustLevel, subCACount);
if (rv != Success) {
return rv;
}
@ -692,7 +689,7 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
// documentation about policy enforcement in pkix.h.
// 4.2.1.12. Extended Key Usage
rv = CheckExtendedKeyUsage(endEntityOrCA, cert.encodedExtendedKeyUsage,
rv = CheckExtendedKeyUsage(endEntityOrCA, cert.GetExtKeyUsage(),
requiredEKUIfPresent);
if (rv != Success) {
return rv;
@ -707,7 +704,7 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
// IMPORTANT: This check must come after the other checks in order for error
// ranking to work correctly.
rv = CheckTimes(cert.GetNSSCert()->validity, time);
rv = CheckValidity(cert.GetValidity(), time);
if (rv != Success) {
return rv;
}

View File

@ -33,7 +33,7 @@ namespace mozilla { namespace pkix {
Result CheckIssuerIndependentProperties(
TrustDomain& trustDomain,
BackCert& cert,
const BackCert& cert,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,

View File

@ -174,26 +174,33 @@ daysBeforeYear(int year)
+ ((year - 1) / 400); // except years divisible by 400.
}
namespace internal {
// We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not
// accept all time formats allowed in the ASN.1 spec. That is,
// GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime
// must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form
// +HH:MM or -HH:MM or NOT accepted.
Result
TimeChoice(SECItemType type, Input& input, /*out*/ PRTime& time)
TimeChoice(Input& tagged, uint8_t expectedTag, /*out*/ PRTime& time)
{
int days;
Input input;
if (ExpectTagAndGetValue(tagged, expectedTag, input) != Success) {
return Failure;
}
int yearHi;
int yearLo;
if (type == siGeneralizedTime) {
if (expectedTag == GENERALIZED_TIME) {
if (ReadTwoDigits(input, 0, 99, yearHi) != Success) {
return Failure;
}
if (ReadTwoDigits(input, 0, 99, yearLo) != Success) {
return Failure;
}
} else if (type == siUTCTime) {
} else if (expectedTag == UTCTime) {
if (ReadTwoDigits(input, 0, 99, yearLo) != Success) {
return Failure;
}
@ -305,4 +312,6 @@ TimeChoice(SECItemType type, Input& input, /*out*/ PRTime& time)
return Success;
}
} // namespace internal
} } } // namespace mozilla::pkix::der

View File

@ -513,33 +513,35 @@ Enumerated(Input& input, uint8_t& value)
return internal::IntegralValue(input, ENUMERATED | 0, value);
}
// XXX: This function should have the signature:
//
// Result TimeChoice(Input& input, /*out*/ PRTime& time);
//
// and parse the tag (and length and value) from the input like the other
// functions here. However, currently we get TimeChoices already partially
// decoded by NSS, so for now we'll have this signature, where the input
// parameter contains the value of the time choice.
//
// type must be either siGeneralizedTime or siUTCTime.
namespace internal {
// internal::TimeChoice implements the shared functionality of GeneralizedTime
// and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME.
//
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
// eliminate the chance for complications in converting times to traditional
// time formats that start at 1970.
Result TimeChoice(SECItemType type, Input& input, /*out*/ PRTime& time);
Result TimeChoice(Input& input, uint8_t tag, /*out*/ PRTime& time);
} // namespace internal
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
// eliminate the chance for complications in converting times to traditional
// time formats that start at 1970.
inline Result
GeneralizedTime(Input& input, PRTime& time)
GeneralizedTime(Input& input, /*out*/ PRTime& time)
{
Input value;
if (ExpectTagAndGetValue(input, GENERALIZED_TIME, value) != Success) {
return Failure;
}
return TimeChoice(siGeneralizedTime, value, time);
return internal::TimeChoice(input, GENERALIZED_TIME, time);
}
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
// eliminate the chance for complications in converting times to traditional
// time formats that start at 1970.
inline Result
TimeChoice(Input& input, /*out*/ PRTime& time)
{
uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME;
return internal::TimeChoice(input, expectedTag, time);
}
// This parser will only parse values between 0..127. If this range is

View File

@ -35,24 +35,24 @@
namespace mozilla { namespace pkix {
SECStatus
VerifySignedData(const CERTSignedData* sd, const SECItem& subjectPublicKeyInfo,
VerifySignedData(const CERTSignedData& sd, const SECItem& subjectPublicKeyInfo,
void* pkcs11PinArg)
{
if (!sd || !sd->data.data || !sd->signatureAlgorithm.algorithm.data ||
!sd->signature.data) {
if (!sd.data.data || !sd.signatureAlgorithm.algorithm.data ||
!sd.signature.data) {
PR_NOT_REACHED("invalid args to VerifySignedData");
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
// See bug 921585.
if (sd->data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) {
if (sd.data.len > static_cast<unsigned int>(std::numeric_limits<int>::max())) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
// convert sig->len from bit counts to byte count.
SECItem sig = sd->signature;
SECItem sig = sd.signature;
DER_ConvertBitString(&sig);
ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
@ -67,8 +67,8 @@ VerifySignedData(const CERTSignedData* sd, const SECItem& subjectPublicKeyInfo,
}
SECOidTag hashAlg;
if (VFY_VerifyDataWithAlgorithmID(sd->data.data, static_cast<int>(sd->data.len),
pubKey.get(), &sig, &sd->signatureAlgorithm,
if (VFY_VerifyDataWithAlgorithmID(sd.data.data, static_cast<int>(sd.data.len),
pubKey.get(), &sig, &sd.signatureAlgorithm,
&hashAlg, pkcs11PinArg) != SECSuccess) {
return SECFailure;
}

View File

@ -156,12 +156,17 @@ CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
}
// TODO(bug 926260): check name constraints
return potentialSigner.VerifyOwnSignatureWithKey(trustDomain,
issuerSubjectPublicKeyInfo);
SECStatus srv = trustDomain.VerifySignedData(potentialSigner.GetSignedData(),
issuerSubjectPublicKeyInfo);
if (srv != SECSuccess) {
return MapSECStatus(srv);
}
// TODO: check for revocation of the OCSP responder certificate unless no-check
// or the caller forcing no-check. To properly support the no-check policy, we'd
// need to enforce policy constraints from the issuerChain.
return Success;
}
MOZILLA_PKIX_ENUM_CLASS ResponderIDType : uint8_t
@ -233,7 +238,7 @@ VerifyOCSPSignedData(TrustDomain& trustDomain,
const CERTSignedData& signedResponseData,
const SECItem& spki)
{
SECStatus srv(trustDomain.VerifySignedData(&signedResponseData, spki));
SECStatus srv = trustDomain.VerifySignedData(signedResponseData, spki);
if (srv != SECSuccess) {
if (PR_GetError() == SEC_ERROR_BAD_SIGNATURE) {
PR_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE, 0);
@ -267,8 +272,8 @@ VerifySignature(Context& context, ResponderIDType responderIDType,
}
for (size_t i = 0; i < numCerts; ++i) {
BackCert cert(nullptr, BackCert::IncludeCN::No);
rv = cert.Init(certs[i]);
BackCert cert(certs[i], nullptr, BackCert::IncludeCN::No);
rv = cert.Init();
if (rv != Success) {
return rv;
}
@ -314,7 +319,7 @@ SECStatus
VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID,
PRTime time, uint16_t maxOCSPLifetimeInDays,
const SECItem& encodedResponse,
bool& expired,
/*out*/ bool& expired,
/*optional out*/ PRTime* thisUpdate,
/*optional out*/ PRTime* validThrough)
{
@ -444,7 +449,7 @@ BasicResponse(der::Input& input, Context& context)
// [0] wrapper
if (der::ExpectTagAndSkipLength(
input, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0)
input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0)
!= der::Success) {
return der::Failure;
}

View File

@ -95,56 +95,110 @@ public:
// constraint enforcement will consider the subject CN as a possible dNSName.
MOZILLA_PKIX_ENUM_CLASS IncludeCN { No = 0, Yes = 1 };
// nssCert and childCert must be valid for the lifetime of BackCert
BackCert(BackCert* childCert, IncludeCN includeCN)
: encodedAuthorityInfoAccess(nullptr)
, encodedBasicConstraints(nullptr)
, encodedCertificatePolicies(nullptr)
, encodedExtendedKeyUsage(nullptr)
, encodedKeyUsage(nullptr)
, encodedNameConstraints(nullptr)
, encodedInhibitAnyPolicy(nullptr)
// certDER and childCert must be valid for the lifetime of BackCert.
BackCert(const SECItem& certDER, const BackCert* childCert,
IncludeCN includeCN)
: der(certDER)
, childCert(childCert)
, includeCN(includeCN)
{
}
Result Init(const SECItem& certDER);
Result Init();
const SECItem& GetDER() const { return nssCert->derCert; }
const SECItem& GetIssuer() const { return nssCert->derIssuer; }
const SECItem& GetSerialNumber() const { return nssCert->serialNumber; }
const SECItem& GetSubject() const { return nssCert->derSubject; }
const SECItem& GetDER() const { return der; }
const der::Version GetVersion() const { return version; }
const CERTSignedData& GetSignedData() const { return signedData; }
const SECItem& GetIssuer() const { return issuer; }
// XXX: "validity" is a horrible name for the structure that holds
// notBefore & notAfter, but that is the name used in RFC 5280 and we use the
// RFC 5280 names for everything.
const SECItem& GetValidity() const { return validity; }
const SECItem& GetSerialNumber() const { return serialNumber; }
const SECItem& GetSubject() const { return subject; }
const SECItem& GetSubjectPublicKeyInfo() const
{
return nssCert->derPublicKey;
return subjectPublicKeyInfo;
}
const SECItem* GetAuthorityInfoAccess() const
{
return MaybeSECItem(authorityInfoAccess);
}
const SECItem* GetBasicConstraints() const
{
return MaybeSECItem(basicConstraints);
}
const SECItem* GetCertificatePolicies() const
{
return MaybeSECItem(certificatePolicies);
}
const SECItem* GetExtKeyUsage() const { return MaybeSECItem(extKeyUsage); }
const SECItem* GetKeyUsage() const { return MaybeSECItem(keyUsage); }
const SECItem* GetInhibitAnyPolicy() const
{
return MaybeSECItem(inhibitAnyPolicy);
}
const SECItem* GetNameConstraints() const
{
return MaybeSECItem(nameConstraints);
}
Result VerifyOwnSignatureWithKey(TrustDomain& trustDomain,
const SECItem& subjectPublicKeyInfo) const;
private:
const SECItem& der;
der::Version version;
const SECItem* encodedAuthorityInfoAccess;
const SECItem* encodedBasicConstraints;
const SECItem* encodedCertificatePolicies;
const SECItem* encodedExtendedKeyUsage;
const SECItem* encodedKeyUsage;
const SECItem* encodedNameConstraints;
const SECItem* encodedInhibitAnyPolicy;
BackCert* const childCert;
public:
BackCert const* const childCert;
const IncludeCN includeCN;
// Only non-const so that we can pass this to TrustDomain::IsRevoked,
// which only takes a non-const pointer because VerifyEncodedOCSPResponse
// requires it, and that is only because the implementation of
// VerifyEncodedOCSPResponse does a CERT_DupCertificate. TODO: get rid
// of that CERT_DupCertificate so that we can make all these things const.
/*const*/ CERTCertificate* GetNSSCert() const { return nssCert.get(); }
private:
ScopedCERTCertificate nssCert;
der::Version version;
// When parsing certificates in BackCert::Init, we don't accept empty
// extensions. Consequently, we don't have to store a distinction between
// empty extensions and extensions that weren't included. However, when
// *processing* extensions, we distinguish between whether an extension was
// included or not based on whetehr the GetXXX function for the extension
// returns nullptr.
static inline const SECItem* MaybeSECItem(const SECItem& item)
{
return item.len > 0 ? &item : nullptr;
}
// Helper classes to zero-initialize these fields on construction and to
// document that they contain non-owning pointers to the data they point
// to.
struct NonOwningSECItem : public SECItemStr {
NonOwningSECItem()
{
data = nullptr;
len = 0;
}
};
struct NonOwningCERTSignedData : public CERTSignedDataStr {
NonOwningCERTSignedData() { memset(this, 0, sizeof(*this)); }
};
NonOwningCERTSignedData signedData;
NonOwningSECItem issuer;
// XXX: "validity" is a horrible name for the structure that holds
// notBefore & notAfter, but that is the name used in RFC 5280 and we use the
// RFC 5280 names for everything.
NonOwningSECItem validity;
NonOwningSECItem serialNumber;
NonOwningSECItem subject;
NonOwningSECItem subjectPublicKeyInfo;
NonOwningSECItem authorityInfoAccess;
NonOwningSECItem basicConstraints;
NonOwningSECItem certificatePolicies;
NonOwningSECItem extKeyUsage;
NonOwningSECItem inhibitAnyPolicy;
NonOwningSECItem keyUsage;
NonOwningSECItem nameConstraints;
NonOwningSECItem subjectAltName;
der::Result RememberExtension(der::Input& extnID, const SECItem& extnValue,
/*out*/ bool& understood);
BackCert(const BackCert&) /* = delete */;
void operator=(const BackCert&); /* = delete */;

View File

@ -7,6 +7,7 @@
SOURCES += [
'lib/pkixbind.cpp',
'lib/pkixbuild.cpp',
'lib/pkixcert.cpp',
'lib/pkixcheck.cpp',
'lib/pkixder.cpp',
'lib/pkixkey.cpp',

View File

@ -12,7 +12,7 @@ SOURCES += [
'pkix_cert_extension_tests.cpp',
'pkix_ocsp_request_tests.cpp',
'pkixcheck_CheckKeyUsage_tests.cpp',
'pkixcheck_CheckTimes_tests.cpp',
'pkixcheck_CheckValidity_tests.cpp',
'pkixder_input_tests.cpp',
'pkixder_pki_types_tests.cpp',
'pkixder_universal_types_tests.cpp',

View File

@ -135,7 +135,7 @@ private:
return SECSuccess;
}
SECStatus VerifySignedData(const CERTSignedData* signedData,
SECStatus VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo)
{
return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
@ -188,7 +188,8 @@ protected:
TEST_F(pkix_cert_chain_length, MaxAcceptableCertChainLength)
{
ScopedCERTCertList results;
ASSERT_SECSuccess(BuildCertChain(trustDomain, trustDomain.GetLeafeCACert(),
ASSERT_SECSuccess(BuildCertChain(trustDomain,
trustDomain.GetLeafeCACert()->derCert,
now, EndEntityOrCA::MustBeCA,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
@ -203,7 +204,7 @@ TEST_F(pkix_cert_chain_length, MaxAcceptableCertChainLength)
"CN=Direct End-Entity",
EndEntityOrCA::MustBeEndEntity,
trustDomain.leafCAKey.get(), privateKey, cert));
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now,
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert->derCert, now,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
@ -225,7 +226,7 @@ TEST_F(pkix_cert_chain_length, BeyondMaxAcceptableCertChainLength)
caPrivateKey, caCert));
PR_SetError(0, 0);
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
BuildCertChain(trustDomain, caCert.get(), now,
BuildCertChain(trustDomain, caCert->derCert, now,
EndEntityOrCA::MustBeCA,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
@ -241,7 +242,7 @@ TEST_F(pkix_cert_chain_length, BeyondMaxAcceptableCertChainLength)
caPrivateKey.get(), privateKey, cert));
PR_SetError(0, 0);
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
BuildCertChain(trustDomain, cert.get(), now,
BuildCertChain(trustDomain, cert->derCert, now,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,

View File

@ -30,43 +30,43 @@ using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
// Creates a self-signed certificate with the given extension.
static bool
static const SECItem*
CreateCert(PLArenaPool* arena, const char* subjectStr,
const SECItem* extension,
/*out*/ ScopedSECKEYPrivateKey& subjectKey,
/*out*/ ScopedCERTCertificate& subjectCert)
SECItem const* const* extensions, // null-terminated array
/*out*/ ScopedSECKEYPrivateKey& subjectKey)
{
static long serialNumberValue = 0;
++serialNumberValue;
const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
serialNumberValue));
if (!serialNumber) {
return false;
return nullptr;
}
const SECItem* issuerDER(ASCIIToDERName(arena, subjectStr));
if (!issuerDER) {
return false;
return nullptr;
}
const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
if (!subjectDER) {
return false;
return nullptr;
}
const SECItem* extensions[2] = { extension, nullptr };
return CreateEncodedCertificate(arena, v3, SEC_OID_SHA256,
serialNumber, issuerDER,
PR_Now() - ONE_DAY,
PR_Now() + ONE_DAY,
subjectDER, extensions,
nullptr, SEC_OID_SHA256, subjectKey);
}
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;
// Creates a self-signed certificate with the given extension.
static const SECItem*
CreateCert(PLArenaPool* arena, const char* subjectStr,
const SECItem* extension,
/*out*/ ScopedSECKEYPrivateKey& subjectKey)
{
const SECItem * extensions[] = { extension, nullptr };
return CreateCert(arena, subjectStr, extensions, subjectKey);
}
class TrustEverythingTrustDomain : public TrustDomain
@ -88,7 +88,7 @@ private:
return SECSuccess;
}
SECStatus VerifySignedData(const CERTSignedData* signedData,
SECStatus VerifySignedData(const CERTSignedData& signedData,
const SECItem& subjectPublicKeyInfo)
{
return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
@ -143,13 +143,14 @@ TEST_F(pkix_cert_extensions, UnknownCriticalExtension)
};
const char* certCN = "CN=Cert With Unknown Critical Extension";
ScopedSECKEYPrivateKey key;
ScopedCERTCertificate cert;
ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalExtension, key,
cert));
// cert is owned by the arena
const SECItem* cert(CreateCert(arena.get(), certCN,
&unknownCriticalExtension, key));
ASSERT_TRUE(cert);
ScopedCERTCertList results;
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
BuildCertChain(trustDomain, *cert, now,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
@ -176,12 +177,13 @@ TEST_F(pkix_cert_extensions, UnknownNonCriticalExtension)
};
const char* certCN = "CN=Cert With Unknown NonCritical Extension";
ScopedSECKEYPrivateKey key;
ScopedCERTCertificate cert;
ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownNonCriticalExtension, key,
cert));
// cert is owned by the arena
const SECItem* cert(CreateCert(arena.get(), certCN,
&unknownNonCriticalExtension, key));
ASSERT_TRUE(cert);
ScopedCERTCertList results;
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
@ -209,13 +211,14 @@ TEST_F(pkix_cert_extensions, WrongOIDCriticalExtension)
};
const char* certCN = "CN=Cert With Critical Wrong OID Extension";
ScopedSECKEYPrivateKey key;
ScopedCERTCertificate cert;
ASSERT_TRUE(CreateCert(arena.get(), certCN, &wrongOIDCriticalExtension, key,
cert));
// cert is owned by the arena
const SECItem* cert(CreateCert(arena.get(), certCN,
&wrongOIDCriticalExtension, key));
ASSERT_TRUE(cert);
ScopedCERTCertList results;
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
BuildCertChain(trustDomain, *cert, now,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
@ -245,12 +248,13 @@ TEST_F(pkix_cert_extensions, CriticalAIAExtension)
};
const char* certCN = "CN=Cert With Critical AIA Extension";
ScopedSECKEYPrivateKey key;
ScopedCERTCertificate cert;
ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalAIAExtension, key,
cert));
// cert is owned by the arena
const SECItem* cert(CreateCert(arena.get(), certCN, &criticalAIAExtension,
key));
ASSERT_TRUE(cert);
ScopedCERTCertList results;
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert, now,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
@ -277,13 +281,14 @@ TEST_F(pkix_cert_extensions, UnknownCriticalCEExtension)
};
const char* certCN = "CN=Cert With Unknown Critical id-ce Extension";
ScopedSECKEYPrivateKey key;
ScopedCERTCertificate cert;
ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalCEExtension, key,
cert));
// cert is owned by the arena
const SECItem* cert(CreateCert(arena.get(), certCN,
&unknownCriticalCEExtension, key));
ASSERT_TRUE(cert);
ScopedCERTCertList results;
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
BuildCertChain(trustDomain, *cert, now,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
@ -310,10 +315,46 @@ TEST_F(pkix_cert_extensions, KnownCriticalCEExtension)
};
const char* certCN = "CN=Cert With Known Critical id-ce Extension";
ScopedSECKEYPrivateKey key;
ScopedCERTCertificate cert;
ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalCEExtension, key, cert));
// cert is owned by the arena
const SECItem* cert(CreateCert(arena.get(), certCN, &criticalCEExtension,
key));
ASSERT_TRUE(cert);
ScopedCERTCertList results;
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
ASSERT_SECSuccess(BuildCertChain(trustDomain, *cert,
now, EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr, // stapled OCSP response
results));
}
// Two subjectAltNames must result in an error.
TEST_F(pkix_cert_extensions, DuplicateSubjectAltName)
{
static const uint8_t DER_BYTES[] = {
0x30, 22, // SEQUENCE (length = 22)
0x06, 3, // OID (length = 3)
0x55, 0x1d, 0x11, // 2.5.29.17
0x04, 15, // OCTET STRING (length = 15)
0x30, 13, // GeneralNames (SEQUENCE) (length = 13)
0x82, 11, // [2] (dNSName) (length = 11)
'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm'
};
static const SECItem DER = {
siBuffer,
const_cast<unsigned char*>(DER_BYTES),
sizeof(DER_BYTES)
};
static SECItem const* const extensions[] = { &DER, &DER, nullptr };
static const char* certCN = "CN=Cert With Duplicate subjectAltName";
ScopedSECKEYPrivateKey key;
// cert is owned by the arena
const SECItem* cert(CreateCert(arena.get(), certCN, extensions, key));
ASSERT_TRUE(cert);
ScopedCERTCertList results;
ASSERT_SECFailure(SEC_ERROR_EXTENSION_VALUE_INVALID,
BuildCertChain(trustDomain, *cert,
now, EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,

View File

@ -1,174 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Copyright 2014 Mozilla Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pkixgtest.h"
#include "pkixtestutil.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result CheckTimes(const CERTValidity& validity, PRTime time);
} } // namespace mozilla::pkix
static const SECItem empty_null = { siBuffer, nullptr, 0 };
static const PRTime PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
static const uint8_t OLDER_GENERALIZEDTIME_DATA[] = {
'1', '9', '9', '9', '0', '1', '0', '1', // 1999-01-01
'0', '0', '0', '0', '0', '0', 'Z' // 00:00:00Z
};
static const SECItem OLDER_GENERALIZEDTIME = {
siGeneralizedTime,
const_cast<uint8_t*>(OLDER_GENERALIZEDTIME_DATA),
sizeof(OLDER_GENERALIZEDTIME_DATA)
};
static const uint8_t OLDER_UTCTIME_DATA[] = {
'9', '9', '0', '1', '0', '1', // (19)99-01-01
'0', '0', '0', '0', '0', '0', 'Z' // 00:00:00Z
};
static const SECItem OLDER_UTCTIME = {
siUTCTime,
const_cast<uint8_t*>(OLDER_UTCTIME_DATA),
sizeof(OLDER_UTCTIME_DATA)
};
static const PRTime NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
static const uint8_t NEWER_GENERALIZEDTIME_DATA[] = {
'2', '0', '2', '1', '0', '1', '0', '1', // 2021-01-01
'0', '0', '0', '0', '0', '0', 'Z' // 00:00:00Z
};
static const SECItem NEWER_GENERALIZEDTIME = {
siGeneralizedTime,
const_cast<uint8_t*>(NEWER_GENERALIZEDTIME_DATA),
sizeof(NEWER_GENERALIZEDTIME_DATA)
};
static const uint8_t NEWER_UTCTIME_DATA[] = {
'2', '1', '0', '1', '0', '1', // 2021-01-01
'0', '0', '0', '0', '0', '0', 'Z' // 00:00:00Z
};
static const SECItem NEWER_UTCTIME = {
siUTCTime,
const_cast<uint8_t*>(NEWER_UTCTIME_DATA),
sizeof(NEWER_UTCTIME_DATA)
};
static const PRTime FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
class pkixcheck_CheckTimes : public ::testing::Test
{
public:
virtual void SetUp()
{
PR_SetError(0, 0);
}
};
TEST_F(pkixcheck_CheckTimes, BothEmptyNull)
{
static const CERTValidity validity = { nullptr, empty_null, empty_null };
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckTimes(validity, NOW));
}
TEST_F(pkixcheck_CheckTimes, NotBeforeEmptyNull)
{
static const CERTValidity validity = { nullptr, empty_null, NEWER_UTCTIME };
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckTimes(validity, NOW));
}
TEST_F(pkixcheck_CheckTimes, NotAfterEmptyNull)
{
static const CERTValidity validity = { nullptr, OLDER_UTCTIME, empty_null };
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckTimes(validity, NOW));
}
TEST_F(pkixcheck_CheckTimes, Valid_UTCTIME_UTCTIME)
{
static const CERTValidity validity = {
nullptr, OLDER_UTCTIME, NEWER_UTCTIME
};
ASSERT_Success(CheckTimes(validity, NOW));
}
TEST_F(pkixcheck_CheckTimes, Valid_GENERALIZEDTIME_GENERALIZEDTIME)
{
static const CERTValidity validity = {
nullptr, OLDER_GENERALIZEDTIME, NEWER_GENERALIZEDTIME
};
ASSERT_Success(CheckTimes(validity, NOW));
}
TEST_F(pkixcheck_CheckTimes, Valid_GENERALIZEDTIME_UTCTIME)
{
static const CERTValidity validity = {
nullptr, OLDER_GENERALIZEDTIME, NEWER_UTCTIME
};
ASSERT_Success(CheckTimes(validity, NOW));
}
TEST_F(pkixcheck_CheckTimes, Valid_UTCTIME_GENERALIZEDTIME)
{
static const CERTValidity validity = {
nullptr, OLDER_UTCTIME, NEWER_GENERALIZEDTIME
};
ASSERT_Success(CheckTimes(validity, NOW));
}
TEST_F(pkixcheck_CheckTimes, InvalidBeforeNotBefore)
{
static const CERTValidity validity = {
nullptr, OLDER_UTCTIME, NEWER_UTCTIME
};
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckTimes(validity, PAST_TIME));
}
TEST_F(pkixcheck_CheckTimes, InvalidAfterNotAfter)
{
static const CERTValidity validity = {
nullptr, OLDER_UTCTIME, NEWER_UTCTIME
};
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckTimes(validity, FUTURE_TIME));
}
TEST_F(pkixcheck_CheckTimes, InvalidNotAfterBeforeNotBefore)
{
static const CERTValidity validity = {
nullptr, NEWER_UTCTIME, OLDER_UTCTIME
};
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckTimes(validity, NOW));
}

View File

@ -0,0 +1,203 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Copyright 2014 Mozilla Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pkixgtest.h"
#include "pkixtestutil.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
Result CheckValidity(const SECItem& encodedValidity, PRTime time);
} } // namespace mozilla::pkix
static const SECItem empty_null = { siBuffer, nullptr, 0 };
static const PRTime PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
#define OLDER_GENERALIZEDTIME \
0x18, 15, /* tag, length */ \
'1', '9', '9', '9', '0', '1', '0', '1', /* 1999-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define OLDER_UTCTIME \
0x17, 13, /* tag, length */ \
'9', '9', '0', '1', '0', '1', /* (19)99-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const PRTime NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
#define NEWER_GENERALIZEDTIME \
0x18, 15, /* tag, length */ \
'2', '0', '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define NEWER_UTCTIME \
0x17, 13, /* tag, length */ \
'2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const PRTime FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
class pkixcheck_CheckValidity : public ::testing::Test
{
public:
virtual void SetUp()
{
PR_SetError(0, 0);
}
};
TEST_F(pkixcheck_CheckValidity, BothEmptyNull)
{
static const uint8_t DER[] = {
0x17/*UTCTime*/, 0/*length*/,
0x17/*UTCTime*/, 0/*length*/,
};
static const SECItem validity = {
siBuffer,
const_cast<uint8_t*>(DER),
sizeof(DER)
};
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckValidity(validity, NOW));
}
TEST_F(pkixcheck_CheckValidity, NotBeforeEmptyNull)
{
static const uint8_t DER[] = {
0x17/*UTCTime*/, 0x00/*length*/,
NEWER_UTCTIME
};
static const SECItem validity = {
siBuffer,
const_cast<uint8_t*>(DER),
sizeof(DER)
};
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckValidity(validity, NOW));
}
TEST_F(pkixcheck_CheckValidity, NotAfterEmptyNull)
{
static const uint8_t DER[] = {
NEWER_UTCTIME,
0x17/*UTCTime*/, 0x00/*length*/,
};
static const SECItem validity = {
siBuffer,
const_cast<uint8_t*>(DER),
sizeof(DER)
};
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckValidity(validity, NOW));
}
static const uint8_t OLDER_UTCTIME_NEWER_UTCTIME_DATA[] = {
OLDER_UTCTIME,
NEWER_UTCTIME,
};
static const SECItem OLDER_UTCTIME_NEWER_UTCTIME = {
siBuffer,
const_cast<uint8_t*>(OLDER_UTCTIME_NEWER_UTCTIME_DATA),
sizeof(OLDER_UTCTIME_NEWER_UTCTIME_DATA)
};
TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME)
{
ASSERT_Success(CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, NOW));
}
TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_GENERALIZEDTIME)
{
static const uint8_t DER[] = {
OLDER_GENERALIZEDTIME,
NEWER_GENERALIZEDTIME,
};
static const SECItem validity = {
siBuffer,
const_cast<uint8_t*>(DER),
sizeof(DER)
};
ASSERT_Success(CheckValidity(validity, NOW));
}
TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_UTCTIME)
{
static const uint8_t DER[] = {
OLDER_GENERALIZEDTIME,
NEWER_UTCTIME,
};
static const SECItem validity = {
siBuffer,
const_cast<uint8_t*>(DER),
sizeof(DER)
};
ASSERT_Success(CheckValidity(validity, NOW));
}
TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_GENERALIZEDTIME)
{
static const uint8_t DER[] = {
OLDER_UTCTIME,
NEWER_GENERALIZEDTIME,
};
static const SECItem validity = {
siBuffer,
const_cast<uint8_t*>(DER),
sizeof(DER)
};
ASSERT_Success(CheckValidity(validity, NOW));
}
TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore)
{
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME,
PAST_TIME));
}
TEST_F(pkixcheck_CheckValidity, InvalidAfterNotAfter)
{
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME,
FUTURE_TIME));
}
TEST_F(pkixcheck_CheckValidity, InvalidNotAfterBeforeNotBefore)
{
static const uint8_t DER[] = {
NEWER_UTCTIME,
OLDER_UTCTIME,
};
static const SECItem validity = {
siBuffer,
const_cast<uint8_t*>(DER),
sizeof(DER)
};
ASSERT_RecoverableError(SEC_ERROR_EXPIRED_CERTIFICATE,
CheckValidity(validity, NOW));
}

View File

@ -332,23 +332,34 @@ TEST_F(pkixder_universal_types_tests, EnumeratedInvalidZeroLength)
#define TWO_CHARS(t) static_cast<uint8_t>('0' + ((t) / 10u)), \
static_cast<uint8_t>('0' + ((t) % 10u))
// Given a DER-encoded GeneralizedTime where we want to extract the value, we
// need to skip two bytes: the tag and the length.
static const uint16_t GT_VALUE_OFFSET = 2;
// Calls TimeChoice on the UTCTime variant of the given generalized time.
template <uint16_t LENGTH>
Result
TimeChoiceForEquivalentUTCTime(const uint8_t (&generalizedTimeDER)[LENGTH],
/*out*/ PRTime& value)
{
static_assert(LENGTH >= 4,
"TimeChoiceForEquivalentUTCTime input too small");
uint8_t utcTimeDER[LENGTH - 2];
utcTimeDER[0] = 0x17; // tag UTCTime
utcTimeDER[1] = LENGTH - 1/*tag*/ - 1/*value*/ - 2/*century*/;
// Copy the value except for the first two digits of the year
for (size_t i = 2; i < LENGTH - 2; ++i) {
utcTimeDER[i] = generalizedTimeDER[i + 2];
}
// Given a DER-encoded GeneralizedTime where we want to extract the value as
// though it were a UTC time, we need to skip four bytes: the tag, the length
// and the first two digits of the year.
static const uint16_t UTC_VALUE_OFFSET = 4;
Input input;
if (input.Init(utcTimeDER, sizeof utcTimeDER) != Success) {
return Failure;
}
return TimeChoice(input, value);
}
template <uint16_t LENGTH>
void
ExpectGoodTime(PRTime expectedValue,
const uint8_t (&generalizedTimeDER)[LENGTH])
{
static_assert(LENGTH >= UTC_VALUE_OFFSET,
"ExpectGoodTime requires input at least UTC_VALUE_OFFSET bytes");
// GeneralizedTime
{
Input input;
@ -361,20 +372,17 @@ ExpectGoodTime(PRTime expectedValue,
// TimeChoice: GeneralizedTime
{
Input input;
ASSERT_EQ(Success, input.Init(generalizedTimeDER + GT_VALUE_OFFSET,
LENGTH - GT_VALUE_OFFSET));
ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
PRTime value = 0;
ASSERT_EQ(Success, TimeChoice(siGeneralizedTime, input, value));
ASSERT_EQ(Success, TimeChoice(input, value));
EXPECT_EQ(expectedValue, value);
}
// TimeChoice: UTCTime
{
Input input;
ASSERT_EQ(Success, input.Init(generalizedTimeDER + UTC_VALUE_OFFSET,
LENGTH - UTC_VALUE_OFFSET));
PRTime value = 0;
ASSERT_EQ(Success, TimeChoice(siUTCTime, input, value));
ASSERT_EQ(Success,
TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
EXPECT_EQ(expectedValue, value);
}
}
@ -383,9 +391,6 @@ template <uint16_t LENGTH>
void
ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH])
{
static_assert(LENGTH >= UTC_VALUE_OFFSET,
"ExpectBadTime requires input at least UTC_VALUE_OFFSET bytes");
// GeneralizedTime
{
Input input;
@ -398,23 +403,17 @@ ExpectBadTime(const uint8_t (&generalizedTimeDER)[LENGTH])
// TimeChoice: GeneralizedTime
{
Input input;
ASSERT_EQ(Success,
input.Init(generalizedTimeDER + GT_VALUE_OFFSET,
LENGTH - GT_VALUE_OFFSET));
ASSERT_EQ(Success, input.Init(generalizedTimeDER, LENGTH));
PRTime value;
ASSERT_EQ(Failure, TimeChoice(siGeneralizedTime, input, value));
ASSERT_EQ(Failure, TimeChoice(input, value));
EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
}
// TimeChoice: UTCTime
{
Input input;
ASSERT_EQ(Success,
input.Init(generalizedTimeDER + UTC_VALUE_OFFSET,
LENGTH - UTC_VALUE_OFFSET));
PRTime value;
ASSERT_EQ(Failure, TimeChoice(siUTCTime, input, value));
EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
ASSERT_EQ(Failure,
TimeChoiceForEquivalentUTCTime(generalizedTimeDER, value));
}
}
@ -457,18 +456,23 @@ TEST_F(pkixder_universal_types_tests, TimeInvalidZeroLength)
ASSERT_EQ(Failure, GeneralizedTime(gt, value));
ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
static const uint8_t dummy[1] = { 'X' };
// TimeChoice: GeneralizedTime
Input tc_gt;
ASSERT_EQ(Success, tc_gt.Init(dummy, 0));
ASSERT_EQ(Failure, TimeChoice(siGeneralizedTime, tc_gt, value));
ASSERT_EQ(Success,
tc_gt.Init(DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH,
sizeof DER_GENERALIZED_TIME_INVALID_ZERO_LENGTH));
ASSERT_EQ(Failure, TimeChoice(tc_gt, value));
ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
// TimeChoice: UTCTime
const uint8_t DER_UTCTIME_INVALID_ZERO_LENGTH[] = {
0x17, // UTCTime
0x00 // Length = 0
};
Input tc_utc;
ASSERT_EQ(Success, tc_utc.Init(dummy, 0));
ASSERT_EQ(Failure, TimeChoice(siUTCTime, tc_utc, value));
ASSERT_EQ(Success, tc_utc.Init(DER_UTCTIME_INVALID_ZERO_LENGTH,
sizeof DER_UTCTIME_INVALID_ZERO_LENGTH));
ASSERT_EQ(Failure, TimeChoice(tc_utc, value));
ASSERT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
}
@ -494,7 +498,7 @@ TEST_F(pkixder_universal_types_tests, TimeInvalidTruncated)
ExpectBadTime(DER_GENERALIZED_TIME_INVALID_TRUNCATED);
}
TEST_F(pkixder_universal_types_tests, GeneralizedTimeNoSeconds)
TEST_F(pkixder_universal_types_tests, TimeNoSeconds)
{
const uint8_t DER_GENERALIZED_TIME_NO_SECONDS[] = {
0x18, // Generalized Time
@ -559,20 +563,17 @@ TEST_F(pkixder_universal_types_tests, GeneralizedTimeYearValidRange)
// TimeChoice: GeneralizedTime
{
Input input;
ASSERT_EQ(Success, input.Init(DER + GT_VALUE_OFFSET,
sizeof(DER) - GT_VALUE_OFFSET));
ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
PRTime value = 0;
ASSERT_EQ(Success, TimeChoice(siGeneralizedTime, input, value));
ASSERT_EQ(Success, TimeChoice(input, value));
EXPECT_EQ(expectedValue, value);
}
// TimeChoice: UTCTime, which is limited to years less than 2049.
if (i <= 2049) {
Input input;
ASSERT_EQ(Success, input.Init(DER + UTC_VALUE_OFFSET,
sizeof(DER) - UTC_VALUE_OFFSET));
PRTime value = 0;
ASSERT_EQ(Success, TimeChoice(siUTCTime, input, value));
ASSERT_EQ(Success, TimeChoiceForEquivalentUTCTime(DER, value));
EXPECT_EQ(expectedValue, value);
}
}
@ -718,10 +719,9 @@ TEST_F(pkixder_universal_types_tests, TimeMonthFebLeapYear2400)
// TimeChoice: GeneralizedTime
{
Input input;
ASSERT_EQ(Success, input.Init(DER + GT_VALUE_OFFSET,
sizeof(DER) - GT_VALUE_OFFSET));
ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
PRTime value = 0;
ASSERT_EQ(Success, TimeChoice(siGeneralizedTime, input, value));
ASSERT_EQ(Success, TimeChoice(input, value));
EXPECT_EQ(expectedValue, value);
}
}
@ -760,11 +760,9 @@ TEST_F(pkixder_universal_types_tests, TimeMonthFebNotLeapYear2100)
// TimeChoice: GeneralizedTime
{
Input input;
ASSERT_EQ(Success,
input.Init(DER + GT_VALUE_OFFSET,
sizeof(DER) - GT_VALUE_OFFSET));
ASSERT_EQ(Success, input.Init(DER, sizeof(DER)));
PRTime value;
ASSERT_EQ(Failure, TimeChoice(siGeneralizedTime, input, value));
ASSERT_EQ(Failure, TimeChoice(input, value));
EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
}
}
@ -908,7 +906,7 @@ TEST_F(pkixder_universal_types_tests, TimeInvalidCenturyChar)
input.Init(DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR,
sizeof DER_GENERALIZED_TIME_INVALID_CENTURY_CHAR));
PRTime value = 0;
ASSERT_EQ(Failure, TimeChoice(siGeneralizedTime, input, value));
ASSERT_EQ(Failure, TimeChoice(input, value));
EXPECT_EQ(SEC_ERROR_INVALID_TIME, PR_GetError());
}

View File

@ -496,7 +496,7 @@ SignedData(PLArenaPool* arena, const SECItem* tbsData,
return nullptr;
}
certsNested = EncodeNested(arena,
der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
certsSequence);
if (!certsNested) {
return nullptr;
@ -745,7 +745,7 @@ TBSCertificate(PLArenaPool* arena, long versionValue,
return nullptr;
}
SECItem* version(EncodeNested(arena,
der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0,
der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0,
versionInteger));
if (!version) {
return nullptr;
@ -829,7 +829,7 @@ TBSCertificate(PLArenaPool* arena, long versionValue,
return nullptr;
}
SECItem* extensionsWrapped(
EncodeNested(arena, der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 3,
EncodeNested(arena, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3,
allExtensions));
if (!extensions) {
return nullptr;
@ -1354,7 +1354,7 @@ CertStatus(OCSPResponseContext& context)
}
// TODO(bug 980536): add support for revocationReason
return EncodeNested(context.arena,
der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1,
der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1,
revocationTime);
}
default: