summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests')
-rw-r--r--security/manager/ssl/tests/.eslintrc.js8
-rw-r--r--security/manager/ssl/tests/gtest/CertDBTest.cpp52
-rw-r--r--security/manager/ssl/tests/gtest/CertListTest.cpp332
-rw-r--r--security/manager/ssl/tests/gtest/CoseTest.cpp756
-rw-r--r--security/manager/ssl/tests/gtest/DataStorageTest.cpp201
-rw-r--r--security/manager/ssl/tests/gtest/DeserializeCertTest.cpp247
-rw-r--r--security/manager/ssl/tests/gtest/MD4Test.cpp62
-rw-r--r--security/manager/ssl/tests/gtest/OCSPCacheTest.cpp321
-rw-r--r--security/manager/ssl/tests/gtest/README.txt2
-rw-r--r--security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp383
-rw-r--r--security/manager/ssl/tests/gtest/moz.build29
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser.ini25
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js101
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_certViewer.js107
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_certificateManager.js179
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js257
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js342
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js199
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js257
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js133
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js140
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_exportP12_passwordUI.js163
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js312
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ca.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem19
-rw-r--r--security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem20
-rw-r--r--security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/code-ee.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/code-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/email-ee.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/email-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/expired-ca.pem18
-rw-r--r--security/manager/ssl/tests/mochitest/browser/expired-ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-cn.pem18
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-cn.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem16
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-o.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-o.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-ou.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/has-ou.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/head.js83
-rw-r--r--security/manager/ssl/tests/mochitest/browser/intermediate.pem20
-rw-r--r--security/manager/ssl/tests/mochitest/browser/intermediate.pem.certspec4
-rw-r--r--security/manager/ssl/tests/mochitest/browser/invalid.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/invalid.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/longOID.pem25
-rw-r--r--security/manager/ssl/tests/mochitest/browser/longOID.pem.certspec4
-rw-r--r--security/manager/ssl/tests/mochitest/browser/md5-ee.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/md5-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/moz.build41
-rw-r--r--security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem21
-rw-r--r--security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem.certspec4
-rw-r--r--security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem21
-rw-r--r--security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem.certspec4
-rw-r--r--security/manager/ssl/tests/mochitest/browser/revoked.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/revoked.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ssl-ee.pem18
-rw-r--r--security/manager/ssl/tests/mochitest/browser/ssl-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem17
-rw-r--r--security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem.certspec2
-rw-r--r--security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem18
-rw-r--r--security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs6
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/backward.html18
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/bug329869.js11
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html28
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/bug383369step3.html29
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/download.auto1
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/download.auto^headers^2
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs5
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs13
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/iframe.html14
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/iframe2.html15
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html8
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs5
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs5
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs5
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs5
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js211
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/mochitest.ini65
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpgbin0 -> 52159 bytes
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/moz.build7
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs4
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs5
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/somestyle.css4
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html92
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html37
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html46
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_bug477118.html34
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html39
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html42
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html41
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html46
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html38
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html40
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html47
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html48
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html44
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html44
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html46
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureRedirect.html39
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html42
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html40
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_javascriptPicture.html34
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_secureAll.html42
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_securePicture.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureBackground.html35
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html38
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe2.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeMetaRedirect.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeRedirect.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePicture.html34
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureDup.html20
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureInIframe.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureRedirect.html36
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/unsecureIframe.html9
-rw-r--r--security/manager/ssl/tests/mochitest/mixedcontent/unsecurePictureDup.html34
-rw-r--r--security/manager/ssl/tests/mochitest/moz.build11
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini6
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/mochitest.ini12
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/moz.build9
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html28
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^1
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html5
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html28
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html^headers^2
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html28
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html^headers^2
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html126
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html268
-rw-r--r--security/manager/ssl/tests/mochitest/stricttransportsecurity/verify.sjs47
-rw-r--r--security/manager/ssl/tests/moz.build17
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/default-ee.key28
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/default-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/default-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/default-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem17
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ev-test.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ev-test.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/evroot.key28
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/evroot.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/evroot.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/evroot.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/expired-ee.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/expired-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/expiredINT.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/expiredINT.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key16
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem17
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/md5signature.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/md5signature.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatch.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem17
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mitm.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/mitm.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/moz.build71
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/noValidNames.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/noValidNames.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/notYetValid.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/notYetValid.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem19
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/other-test-ca.key28
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/other-test-ca.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem21
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem21
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/selfsigned.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/selfsigned.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/test-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/test-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/test-int.pem18
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/test-int.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem22
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem20
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/v1Cert.pem17
-rw-r--r--security/manager/ssl/tests/unit/bad_certs/v1Cert.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/head_psm.js1231
-rw-r--r--security/manager/ssl/tests/unit/moz.build42
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem20
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/default-ee.key28
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/default-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/moz.build42
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem20
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key28
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key16
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem15
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-int.pem18
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-int.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem19
-rw-r--r--security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/pkcs11testmodule/moz.build20
-rw-r--r--security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.cpp577
-rw-r--r--security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.symbols1
-rwxr-xr-xsecurity/manager/ssl/tests/unit/pycert.py815
-rwxr-xr-xsecurity/manager/ssl/tests/unit/pycms.py219
-rw-r--r--security/manager/ssl/tests/unit/pyct.py103
-rwxr-xr-xsecurity/manager/ssl/tests/unit/pykey.py958
-rw-r--r--security/manager/ssl/tests/unit/requirements.txt6
-rwxr-xr-xsecurity/manager/ssl/tests/unit/sign_app.py399
-rw-r--r--security/manager/ssl/tests/unit/sss_readstate_child_worker.js66
-rw-r--r--security/manager/ssl/tests/unit/test_add_preexisting_cert.js46
-rw-r--r--security/manager/ssl/tests/unit/test_allow_all_cert_errors.js25
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/moz.build19
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js295
-rw-r--r--security/manager/ssl/tests/unit/test_blocklist_onecrl.js75
-rw-r--r--security/manager/ssl/tests/unit/test_blocklist_pinning.js127
-rw-r--r--security/manager/ssl/tests/unit/test_broken_fips.js64
-rw-r--r--security/manager/ssl/tests/unit/test_broken_fips/key4.dbbin0 -> 36864 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_broken_fips/pkcs11.txt5
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js57
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js117
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import.js217
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows.pfxbin0 -> 2041 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_emptypass.pfxbin0 -> 2068 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_nopass.pfxbin0 -> 2068 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import/moz.build14
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js117
-rw-r--r--security/manager/ssl/tests/unit/test_certDB_import_with_master_password.js148
-rw-r--r--security/manager/ssl/tests/unit/test_cert_chains.js814
-rw-r--r--security/manager/ssl/tests/unit/test_cert_dbKey.js263
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku.js189
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_eku/moz.build35
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null.js54
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_embedded_null/moz.build17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_expiration_canary.js40
-rw-r--r--security/manager/ssl/tests/unit/test_cert_isBuiltInRoot.js73
-rw-r--r--security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload.js124
-rw-r--r--security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/cert9.dbbin0 -> 28672 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/key4.dbbin0 -> 36864 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage.js76
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_keyUsage/moz.build27
-rw-r--r--security/manager/ssl/tests/unit/test_cert_override_bits_mismatches.js116
-rw-r--r--security/manager/ssl/tests/unit/test_cert_overrides.js718
-rw-r--r--security/manager/ssl/tests/unit/test_cert_overrides_read_only.js126
-rw-r--r--security/manager/ssl/tests/unit/test_cert_overrides_read_only/cert9.dbbin0 -> 28672 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_overrides_read_only/key4.dbbin0 -> 36864 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1.js187
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ca.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_cert_sha1/moz.build18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures.js140
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem11
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem11
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_cert_signatures/moz.build20
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage.js286
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_broken_db.js95
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct.js533
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem27
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem41
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlitebin0 -> 15244 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.stashbin0 -> 64267 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem27
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem34
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_preexisting.js59
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.mdbbin0 -> 45056 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.safe.binbin0 -> 122 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_preexisting/lock.mdbbin0 -> 8192 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js71
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/crlite.filterbin0 -> 15244 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/data.safe.binbin0 -> 1607775 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_cert_storage_prefs.js34
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust.js324
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust/ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust/ee.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust/ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust/int.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust/int.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_trust/moz.build15
-rw-r--r--security/manager/ssl/tests/unit/test_cert_utf8.js79
-rw-r--r--security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem24
-rw-r--r--security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_utf8/moz.build13
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version.js304
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem.certspec2
-rwxr-xr-xsecurity/manager/ssl/tests/unit/test_cert_version/generate.py93
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/moz.build61
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_constructX509FromBase64.js90
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing.js435
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem15
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem15
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem15
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem14
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem14
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem15
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/moz.build21
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/pysign.py35
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/test.txt1
-rw-r--r--security/manager/ssl/tests/unit/test_content_signing/test.txt.signature1
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters.js697
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/20201017-0-filterbin0 -> 5770363 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/20201017-1-filter.stashbin0 -> 36632 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/20201201-3-filter.stashbin0 -> 57737 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/issuer.pem28
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/no-sct-issuer.pem27
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/no-sct.pem33
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash-2.pem36
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash.pem36
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/revoked.pem42
-rw-r--r--security/manager/ssl/tests/unit/test_crlite_filters/valid.pem39
-rw-r--r--security/manager/ssl/tests/unit/test_ct.js75
-rw-r--r--security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem28
-rw-r--r--security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem34
-rw-r--r--security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_ct/default-ee.key28
-rw-r--r--security/manager/ssl/tests/unit/test_ct/default-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_ct/default-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ct/default-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_ct/moz.build23
-rw-r--r--security/manager/ssl/tests/unit/test_ct/test-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ct/test-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_db_format_pref_new.js22
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials.js91
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key5
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem15
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem16
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key6
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/moz.build24
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_der.js380
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello.js93
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key28
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/moz.build24
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key28
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_encrypted_client_hello_client_only.js33
-rw-r--r--security/manager/ssl/tests/unit/test_enterprise_roots.js82
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs.js437
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem23
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem22
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/evroot.key28
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/evroot.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/evroot.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/evroot.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/moz.build48
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem23
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem22
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem22
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key28
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js205
-rw-r--r--security/manager/ssl/tests/unit/test_hash_algorithms.js158
-rw-r--r--security/manager/ssl/tests/unit/test_hash_algorithms_wrap.js5
-rw-r--r--security/manager/ssl/tests/unit/test_hmac.js157
-rw-r--r--security/manager/ssl/tests/unit/test_imminent_distrust.js39
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js138
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build35
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads.js645
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key28
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_intermediate_preloads/moz.build24
-rw-r--r--security/manager/ssl/tests/unit/test_keysize.js204
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem13
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem13
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem15
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem13
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem11
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem11
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem12
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem13
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem12
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem13
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem13
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem14
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem11
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/moz.build41
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem12
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem12
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem9
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem10
-rw-r--r--security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev.js171
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem.certspec8
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key28
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key28
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key28
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/evroot.key28
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/evroot.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_keysize_ev/moz.build31
-rw-r--r--security/manager/ssl/tests/unit/test_local_cert.js87
-rw-r--r--security/manager/ssl/tests/unit/test_logoutAndTeardown.js207
-rw-r--r--security/manager/ssl/tests/unit/test_missing_intermediate.js95
-rw-r--r--security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_missing_intermediate/moz.build19
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints.js71
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/dciss.pem22
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/dciss.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_name_constraints/moz.build21
-rw-r--r--security/manager/ssl/tests/unit/test_nonascii_path.js62
-rw-r--r--security/manager/ssl/tests/unit/test_nsCertType.js32
-rw-r--r--security/manager/ssl/tests/unit/test_nsIX509CertValidity.js70
-rw-r--r--security/manager/ssl/tests/unit/test_nsIX509Cert_utf8.js96
-rw-r--r--security/manager/ssl/tests/unit/test_nss_shutdown.js48
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_caching.js406
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_enabled_pref.js150
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_must_staple.js160
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js68
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_private_caching.js138
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_required.js74
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_stapling.js393
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js319
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js48
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_timeout.js100
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url.js122
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/ca.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/int.key28
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/int.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/int.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/int.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/moz.build33
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem.certspec2
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/moz.build18
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/sample_revocations.txt41
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem.certspec3
-rw-r--r--security/manager/ssl/tests/unit/test_osclientcerts_module.js63
-rw-r--r--security/manager/ssl/tests/unit/test_oskeystore.js413
-rw-r--r--security/manager/ssl/tests/unit/test_osreauthenticator.js27
-rw-r--r--security/manager/ssl/tests/unit/test_password_prompt.js87
-rw-r--r--security/manager/ssl/tests/unit/test_pinning.js319
-rw-r--r--security/manager/ssl/tests/unit/test_pkcs11_module.js58
-rw-r--r--security/manager/ssl/tests/unit/test_pkcs11_moduleDB.js50
-rw-r--r--security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js61
-rw-r--r--security/manager/ssl/tests/unit/test_pkcs11_slot.js135
-rw-r--r--security/manager/ssl/tests/unit/test_pkcs11_token.js149
-rw-r--r--security/manager/ssl/tests/unit/test_pkcs11_tokenDB.js20
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/apple-ist-ca-8-g1-intermediate.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/default-ee.key28
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/default-ee.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/default-ee.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/default-ee.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/gspe72-4-ssl-ls-apple-com.pem38
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/moz.build21
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem22
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem22
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem23
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_sanctions_symantec_apple_google.js107
-rw-r--r--security/manager/ssl/tests/unit/test_sdr.js272
-rw-r--r--security/manager/ssl/tests/unit/test_sdr_preexisting.js79
-rw-r--r--security/manager/ssl/tests/unit/test_sdr_preexisting/key4.dbbin0 -> 36864 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js138
-rw-r--r--security/manager/ssl/tests/unit/test_sdr_preexisting_with_password/key4.dbbin0 -> 36864 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_self_signed_certs.js69
-rw-r--r--security/manager/ssl/tests/unit/test_self_signed_certs/cert9.dbbin0 -> 45056 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_session_resumption.js298
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps.js1013
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app/README1
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app/data/image.pngbin0 -> 534 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app/manifest.json5
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.manifest10
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.sigbin0 -> 655 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/README2
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/data/image.pngbin0 -> 534 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/manifest.json5
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1-256.zipbin0 -> 2678 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1.zipbin0 -> 2341 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-256.zipbin0 -> 2362 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1-256.zipbin0 -> 2625 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1.zipbin0 -> 2289 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-256.zipbin0 -> 2309 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1-256.zipbin0 -> 2643 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1.zipbin0 -> 2307 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-256.zipbin0 -> 2327 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1-256.zipbin0 -> 2563 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1.zipbin0 -> 2226 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-256.zipbin0 -> 2247 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1-256.zipbin0 -> 2513 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1.zipbin0 -> 2174 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-256.zipbin0 -> 2196 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1-256.zipbin0 -> 2526 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1.zipbin0 -> 2192 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-256.zipbin0 -> 2210 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1-256.zipbin0 -> 2601 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1.zipbin0 -> 2264 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-256.zipbin0 -> 2287 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1-256.zipbin0 -> 2549 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1.zipbin0 -> 2213 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-256.zipbin0 -> 2234 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1-256.zipbin0 -> 2568 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1.zipbin0 -> 2232 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-256.zipbin0 -> 2251 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384.zipbin0 -> 459148 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256.zipbin0 -> 459984 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256.zipbin0 -> 458382 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256.zipbin0 -> 459272 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-ES384.zipbin0 -> 459192 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-PS256.zipbin0 -> 460028 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256.zipbin0 -> 458426 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-PS256.zipbin0 -> 459315 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/big_manifest.zipbin0 -> 8107 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/bug_1411458.zipbin0 -> 2698 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/cose_int_signed_with_pkcs7.zipbin0 -> 4051 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/cose_multiple_signed_with_pkcs7.zipbin0 -> 3945 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/cose_signed_with_pkcs7.zipbin0 -> 3401 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/cose_tampered_good_pkcs7.zipbin0 -> 3379 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/empty_signerInfos.zipbin0 -> 1892 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/huge_manifest.zipbin0 -> 31397 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/moz.build72
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/only_cose_multiple_signed.zipbin0 -> 2109 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/only_cose_signed.zipbin0 -> 1565 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app.zipbin0 -> 2255 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/unsigned_app.zipbin0 -> 510 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.derbin0 -> 794 bytes
-rw-r--r--security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.pem.certspec6
-rw-r--r--security/manager/ssl/tests/unit/test_ssl_status.js76
-rw-r--r--security/manager/ssl/tests/unit/test_sss_enumerate.js101
-rw-r--r--security/manager/ssl/tests/unit/test_sss_eviction.js100
-rw-r--r--security/manager/ssl/tests/unit/test_sss_originAttributes.js179
-rw-r--r--security/manager/ssl/tests/unit/test_sss_readstate.js161
-rw-r--r--security/manager/ssl/tests/unit/test_sss_readstate_child.js40
-rw-r--r--security/manager/ssl/tests/unit/test_sss_readstate_empty.js56
-rw-r--r--security/manager/ssl/tests/unit/test_sss_readstate_garbage.js110
-rw-r--r--security/manager/ssl/tests/unit/test_sss_readstate_huge.js98
-rw-r--r--security/manager/ssl/tests/unit/test_sss_resetState.js113
-rw-r--r--security/manager/ssl/tests/unit/test_sss_sanitizeOnShutdown.js72
-rw-r--r--security/manager/ssl/tests/unit/test_sss_savestate.js122
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign.js67
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem.certspec4
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem19
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem17
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_startcom_wosign/moz.build19
-rw-r--r--security/manager/ssl/tests/unit/test_sts_fqdn.js50
-rw-r--r--security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js59
-rw-r--r--security/manager/ssl/tests/unit/test_sts_parser.js146
-rw-r--r--security/manager/ssl/tests/unit/test_sts_preload_dynamic.js86
-rw-r--r--security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js455
-rw-r--r--security/manager/ssl/tests/unit/test_sts_preloadlist_selfdestruct.js22
-rw-r--r--security/manager/ssl/tests/unit/test_validity.js108
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem21
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem.certspec5
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key28
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem20
-rw-r--r--security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem.certspec8
-rw-r--r--security/manager/ssl/tests/unit/test_validity/evroot.key28
-rw-r--r--security/manager/ssl/tests/unit/test_validity/evroot.key.keyspec1
-rw-r--r--security/manager/ssl/tests/unit/test_validity/evroot.pem18
-rw-r--r--security/manager/ssl/tests/unit/test_validity/evroot.pem.certspec7
-rw-r--r--security/manager/ssl/tests/unit/test_validity/moz.build24
-rw-r--r--security/manager/ssl/tests/unit/test_x509.js142
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/BadCertAndPinningServer.cpp140
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/DelegatedCredentialsServer.cpp142
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/EncryptedClientHelloServer.cpp143
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp168
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp153
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/SanctionsTestServer.cpp87
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/moz.build30
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/default-ee.der3
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp204
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h66
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp669
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h93
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/lib/moz.build16
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/moz.build8
-rw-r--r--security/manager/ssl/tests/unit/xpcshell-smartcards.ini15
-rw-r--r--security/manager/ssl/tests/unit/xpcshell.ini237
1164 files changed, 42767 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/.eslintrc.js b/security/manager/ssl/tests/.eslintrc.js
new file mode 100644
index 0000000000..379eabb2d0
--- /dev/null
+++ b/security/manager/ssl/tests/.eslintrc.js
@@ -0,0 +1,8 @@
+"use strict";
+
+module.exports = {
+ rules: {
+ // Disallow non-top level |var| declarations.
+ "mozilla/var-only-at-top-level": "error",
+ },
+};
diff --git a/security/manager/ssl/tests/gtest/CertDBTest.cpp b/security/manager/ssl/tests/gtest/CertDBTest.cpp
new file mode 100644
index 0000000000..310b0226e1
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/CertDBTest.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsIPrefService.h"
+#include "nsIX509Cert.h"
+#include "nsIX509CertDB.h"
+#include "nsServiceManagerUtils.h"
+
+TEST(psm_CertDB, Test)
+{
+ {
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ ASSERT_TRUE(prefs)
+ << "couldn't get nsIPrefBranch";
+
+ // When PSM initializes, it attempts to get some localized strings.
+ // As a result, Android flips out if this isn't set.
+ nsresult rv = prefs->SetBoolPref("intl.locale.matchOS", true);
+ ASSERT_TRUE(NS_SUCCEEDED(rv))
+ << "couldn't set pref 'intl.locale.matchOS'";
+
+ nsCOMPtr<nsIX509CertDB> certdb(do_GetService(NS_X509CERTDB_CONTRACTID));
+ ASSERT_TRUE(certdb)
+ << "couldn't get certdb";
+
+ nsTArray<RefPtr<nsIX509Cert>> certList;
+ rv = certdb->GetCerts(certList);
+ ASSERT_TRUE(NS_SUCCEEDED(rv))
+ << "couldn't get list of certificates";
+
+ bool foundBuiltIn = false;
+ for (const auto& cert : certList) {
+ ASSERT_TRUE(cert)
+ << "certlist shouldn't have null certificate";
+ ASSERT_TRUE(NS_SUCCEEDED(cert->GetIsBuiltInRoot(&foundBuiltIn)))
+ << "GetIsBuiltInRoot failed";
+ if (foundBuiltIn) {
+ break;
+ }
+ }
+
+ ASSERT_TRUE(foundBuiltIn)
+ << "didn't load any built-in certificates";
+
+ printf("successfully loaded at least one built-in certificate\n");
+
+ } // this scopes the nsCOMPtrs
+}
diff --git a/security/manager/ssl/tests/gtest/CertListTest.cpp b/security/manager/ssl/tests/gtest/CertListTest.cpp
new file mode 100644
index 0000000000..db4cec19e5
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/CertListTest.cpp
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsIPrefService.h"
+#include "nsIX509Cert.h"
+#include "nsIX509CertDB.h"
+#include "nsNSSCertificate.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+
+// certspec (for pycert.py)
+//
+// issuer:ca
+// subject:ca
+// extension:basicConstraints:cA,
+// extension:keyUsage:cRLSign,keyCertSign
+// serialNumber:1
+const char* kCaPem =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICsjCCAZygAwIBAgIBATALBgkqhkiG9w0BAQswDTELMAkGA1UEAwwCY2EwIhgP\n"
+ "MjAxNTExMjgwMDAwMDBaGA8yMDE4MDIwNTAwMDAwMFowDTELMAkGA1UEAwwCY2Ew\n"
+ "ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ\n"
+ "PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH\n"
+ "9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw\n"
+ "4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86\n"
+ "exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0\n"
+ "ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N\n"
+ "AgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAsGCSqGSIb3DQEB\n"
+ "CwOCAQEAchHf1yV+blE6fvS53L3DGmvxEpn9+t+xwOvWczBmLFEzUPdncakdaWlQ\n"
+ "v7q81BPyjBqkYbQi15Ws81hY3dnXn8LT1QktCL9guvc3z4fMdQbRjpjcIReCYt3E\n"
+ "PB22Jl2FCm6ii4XL0qDFD26WK3zMe2Uks6t55f8VeDTBGNoPp2JMsWY1Pi4vR6wK\n"
+ "AY96WoXS/qrYkmMEOgFu907pApeAeE8VJzXjqMLF6/W1VN7ISnGzWQ8zKQnlp3YA\n"
+ "mvWZQcD6INK8mvpZxIeu6NtHaKEXGw7tlGekmkVhapPtQZYnWcsXybRrZf5g3hOh\n"
+ "JFPl8kW42VoxXL11PP5NX2ylTsJ//g==\n"
+ "-----END CERTIFICATE-----";
+
+// certspec (for pycert.py)
+//
+// issuer:ca
+// subject:ca-intermediate
+// extension:basicConstraints:cA,
+// extension:keyUsage:cRLSign,keyCertSign
+// serialNumber:2
+const char* kCaIntermediatePem =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICvzCCAamgAwIBAgIBAjALBgkqhkiG9w0BAQswDTELMAkGA1UEAwwCY2EwIhgP\n"
+ "MjAxNTExMjgwMDAwMDBaGA8yMDE4MDIwNTAwMDAwMFowGjEYMBYGA1UEAwwPY2Et\n"
+ "aW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohR\n"
+ "qESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+Kv\n"
+ "WnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+\n"
+ "rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPv\n"
+ "JxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5\n"
+ "Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6\n"
+ "clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB\n"
+ "BjALBgkqhkiG9w0BAQsDggEBAC0ys8UOmYgvH5rrTeV6u79ocHqdQFwdmR7/4d08\n"
+ "i3asC7b70dw0ehA5vi4cq5mwBvQOGZq4wvsR4jSJW0+0hjWL1dr2M6VxmCfjdqhU\n"
+ "NQHPlY6y7lLfYQbFfUHX99ZgygJjdmmm7H8MBP4UgPb8jl6Xq53FgYykiX/qPmfb\n"
+ "pzpOFHDi+Tk67DLCvPz03UUDYNx1H0OhRimj0DWhdYGUg2DHfLQkOEYvrYG4wYB8\n"
+ "AB/0hrG51yFsuXrzhYcinTKby11Qk6PjnOQ/aZvK00Jffep/RHs8lIOWty9SarMG\n"
+ "oNSECn+6I9AgStJdo6LuP1QPyrQe3DZtAHhRJAPAoU7BSqM=\n"
+ "-----END CERTIFICATE-----";
+
+// certspec (for pycert.py)
+//
+// issuer:ca-intermediate
+// subject:ca-second-intermediate
+// extension:basicConstraints:cA,
+// extension:keyUsage:cRLSign,keyCertSign
+// serialNumber:3
+const char* kCaSecondIntermediatePem =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIC0zCCAb2gAwIBAgIBAzALBgkqhkiG9w0BAQswGjEYMBYGA1UEAwwPY2EtaW50\n"
+ "ZXJtZWRpYXRlMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAwMDBaMCEx\n"
+ "HzAdBgNVBAMMFmNhLXNlY29uZC1pbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEB\n"
+ "AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk\n"
+ "e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg\n"
+ "KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI\n"
+ "YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi\n"
+ "lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL\n"
+ "HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1Ud\n"
+ "EwQFMAMBAf8wCwYDVR0PBAQDAgEGMAsGCSqGSIb3DQEBCwOCAQEAaK6K7/0Y+PkG\n"
+ "MQJjumTlt6XUQjQ3Y6zuSOMlZ1wmJoBqWabYhJ4qXfcSMQiw+kZ+mQTFk+IdurGC\n"
+ "RHrAKwDGNRqmjnQ56qjwHNTTxhJozP09vBCgs3fIQQY/Nq/uISoQvOZmoIriFZf6\n"
+ "8czHMlj1vIC6zp4XHWdqkQ7aF4YFsTfM0kBPrm0Y6Nn0VKzWNdmaIs/X5OcR6ZAG\n"
+ "zGN9UZV+ZftcfdqI0XSVCVRAK5MeEa+twLr5PE/Nl7/Ig/tUJMWGSbcrWRZQTXQu\n"
+ "Rx7CSKcoatyMhJOd2YT4BvoijEJCxTKWMJzFe2uZAphQHUlVmE9IbUQM0T1N6RNd\n"
+ "1dH4o4UeuQ==\n"
+ "-----END CERTIFICATE-----";
+
+// certspec (for pycert.py)
+//
+// issuer:ca-second-intermediate
+// subject:ee
+const char* kEePem =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICujCCAaSgAwIBAgIUMy8NE67P/4jkaCra7rOVVvX4+GswCwYJKoZIhvcNAQEL\n"
+ "MCExHzAdBgNVBAMMFmNhLXNlY29uZC1pbnRlcm1lZGlhdGUwIhgPMjAxNTExMjgw\n"
+ "MDAwMDBaGA8yMDE4MDIwNTAwMDAwMFowDTELMAkGA1UEAwwCZWUwggEiMA0GCSqG\n"
+ "SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0\n"
+ "7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D\n"
+ "/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw\n"
+ "JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX\n"
+ "rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd\n"
+ "q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwCwYJ\n"
+ "KoZIhvcNAQELA4IBAQCE5V5YiFPtbb1dOCIMGC5X/6kfQkQmIfvEZIol0MRXmP4g\n"
+ "CsOPbTI+BNxYVNk5RHIlr+6e0d8TNiABem4FZK3kea4ugN8ez3IsK7ug7qdrooNA\n"
+ "MiHOvrLmAw2nQWexdDRf7OPeVj03BwELzGTOGPjAqDktTsK57OfXyFTm9nl75WQo\n"
+ "+EWX+CdV4L1o2rgABvSiMnMdycftCC73Hr/3ypADqY7nDrKpxYdrGgzAQvx3DjPv\n"
+ "b7nBKH/gXg3kzoWpeQmJYPl9Vd+DvGljS5i71oLbvCwlDX7ZswGcvb8pQ7Tni5HA\n"
+ "VYpAYLokxIDFnyVT9oCACJuJ5LvpBBrhd0+1uUPE\n"
+ "-----END CERTIFICATE-----";
+
+class psm_CertList : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ ASSERT_TRUE(prefs)
+ << "couldn't get nsIPrefBranch";
+
+ // When PSM initializes, it attempts to get some localized strings.
+ // As a result, Android flips out if this isn't set.
+ nsresult rv = prefs->SetBoolPref("intl.locale.matchOS", true);
+ ASSERT_TRUE(NS_SUCCEEDED(rv))
+ << "couldn't set pref 'intl.locale.matchOS'";
+
+ nsCOMPtr<nsIX509CertDB> certdb(do_GetService(NS_X509CERTDB_CONTRACTID));
+ ASSERT_TRUE(certdb)
+ << "couldn't get certdb";
+ }
+};
+
+static nsresult AddCertFromStringToList(
+ const char* aPem, nsTArray<RefPtr<nsIX509Cert>>& aCertList) {
+ RefPtr<nsIX509Cert> cert;
+ cert =
+ nsNSSCertificate::ConstructFromDER(const_cast<char*>(aPem), strlen(aPem));
+ if (!cert) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aCertList.AppendElement(cert);
+ return NS_OK;
+}
+
+TEST_F(psm_CertList, TestInvalidSegmenting) {
+ nsTArray<RefPtr<nsIX509Cert>> certList;
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ nsTArray<RefPtr<nsIX509Cert>> intCerts;
+ nsCOMPtr<nsIX509Cert> eeCert;
+ nsresult rv = nsNSSCertificate::SegmentCertificateChain(certList, rootCert,
+ intCerts, eeCert);
+ ASSERT_EQ(rv, NS_ERROR_INVALID_ARG) << "Empty lists can't be segmented";
+
+ rv = AddCertFromStringToList(kCaPem, certList);
+ ASSERT_EQ(rv, NS_OK) << "Should have loaded OK";
+
+ intCerts.Clear();
+ rootCert = nullptr;
+ eeCert = nullptr;
+
+ rv = nsNSSCertificate::SegmentCertificateChain(certList, rootCert, intCerts,
+ eeCert);
+ ASSERT_EQ(rv, NS_ERROR_INVALID_ARG) << "Lists of one can't be segmented";
+}
+
+TEST_F(psm_CertList, TestValidSegmenting) {
+ nsTArray<RefPtr<nsIX509Cert>> certList;
+
+ nsresult rv = AddCertFromStringToList(kEePem, certList);
+ ASSERT_EQ(rv, NS_OK) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaSecondIntermediatePem, certList);
+ ASSERT_EQ(rv, NS_OK) << "Should have loaded OK";
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ nsTArray<RefPtr<nsIX509Cert>> intCerts;
+ nsCOMPtr<nsIX509Cert> eeCert;
+
+ rv = nsNSSCertificate::SegmentCertificateChain(certList, rootCert, intCerts,
+ eeCert);
+ ASSERT_EQ(rv, NS_OK) << "Should have segmented OK";
+ ASSERT_TRUE(rootCert)
+ << "Root cert should be filled in";
+ ASSERT_TRUE(eeCert)
+ << "End entity cert should be filled in";
+ ASSERT_EQ(intCerts.Length(), static_cast<size_t>(0))
+ << "There should be no intermediates";
+
+ nsAutoString rootCn;
+ ASSERT_TRUE(NS_SUCCEEDED(rootCert->GetCommonName(rootCn)))
+ << "Getters should work.";
+ ASSERT_TRUE(rootCn.EqualsLiteral("ca-second-intermediate"))
+ << "Second Intermediate CN should match";
+
+ rv = AddCertFromStringToList(kCaIntermediatePem, certList);
+ ASSERT_EQ(rv, NS_OK) << "Should have loaded OK";
+
+ intCerts.Clear();
+ rootCert = nullptr;
+ eeCert = nullptr;
+ rv = nsNSSCertificate::SegmentCertificateChain(certList, rootCert, intCerts,
+ eeCert);
+ ASSERT_EQ(rv, NS_OK) << "Should have segmented OK";
+
+ ASSERT_TRUE(rootCert)
+ << "Root cert should be filled in";
+ ASSERT_TRUE(eeCert)
+ << "End entity cert should be filled in";
+ ASSERT_EQ(intCerts.Length(), static_cast<size_t>(1))
+ << "There should be one intermediate";
+
+ rv = AddCertFromStringToList(kCaPem, certList);
+ ASSERT_EQ(rv, NS_OK) << "Should have loaded OK";
+
+ intCerts.Clear();
+ rootCert = nullptr;
+ eeCert = nullptr;
+ rv = nsNSSCertificate::SegmentCertificateChain(certList, rootCert, intCerts,
+ eeCert);
+ ASSERT_EQ(rv, NS_OK) << "Should have segmented OK";
+
+ ASSERT_TRUE(rootCert)
+ << "Root cert should be filled in";
+ ASSERT_TRUE(eeCert)
+ << "End entity cert should be filled in";
+ ASSERT_EQ(intCerts.Length(), static_cast<size_t>(2))
+ << "There should be two intermediates";
+
+ ASSERT_TRUE(NS_SUCCEEDED(rootCert->GetCommonName(rootCn)))
+ << "Getters should work.";
+ ASSERT_TRUE(rootCn.EqualsLiteral("ca"))
+ << "Root CN should match";
+
+ nsAutoString eeCn;
+ ASSERT_TRUE(NS_SUCCEEDED(eeCert->GetCommonName(eeCn)))
+ << "Getters should work.";
+ ASSERT_TRUE(eeCn.EqualsLiteral("ee"))
+ << "EE CN should match";
+
+ for (size_t i = 0; i < intCerts.Length(); ++i) {
+ nsAutoString cn;
+ const auto& cert = intCerts[i];
+ rv = cert->GetCommonName(cn);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+
+ if (i < intCerts.Length() - 1) {
+ if (!cn.EqualsLiteral("ca-second-intermediate")) {
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ } else {
+ if (!cn.EqualsLiteral("ca-intermediate")) {
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ }
+ }
+
+ ASSERT_EQ(rv, NS_OK) << "Should have looped OK.";
+}
+
+TEST_F(psm_CertList, TestGetRootCertificateChainTwo) {
+ nsTArray<RefPtr<nsIX509Cert>> certList;
+
+ nsresult rv = AddCertFromStringToList(kCaIntermediatePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaPem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ rv = nsNSSCertificate::GetRootCertificate(certList, rootCert);
+ EXPECT_EQ(NS_OK, rv) << "Should have fetched the root OK";
+ ASSERT_TRUE(rootCert)
+ << "Root cert should be filled in";
+
+ nsAutoString rootCn;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCert->GetCommonName(rootCn)))
+ << "Getters should work.";
+ EXPECT_TRUE(rootCn.EqualsLiteral("ca")) << "Root CN should match";
+
+ // Re-fetch and ensure we get the same certificate.
+ nsCOMPtr<nsIX509Cert> rootCertRepeat;
+ rv = nsNSSCertificate::GetRootCertificate(certList, rootCertRepeat);
+ EXPECT_EQ(NS_OK, rv) << "Should have fetched the root OK the second time";
+ ASSERT_TRUE(rootCertRepeat)
+ << "Root cert should still be filled in";
+
+ nsAutoString rootRepeatCn;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCertRepeat->GetCommonName(rootRepeatCn)))
+ << "Getters should work.";
+ EXPECT_TRUE(rootRepeatCn.EqualsLiteral("ca")) << "Root CN should still match";
+}
+
+TEST_F(psm_CertList, TestGetRootCertificateChainFour) {
+ nsTArray<RefPtr<nsIX509Cert>> certList;
+
+ nsresult rv = AddCertFromStringToList(kEePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaSecondIntermediatePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaIntermediatePem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+ rv = AddCertFromStringToList(kCaPem, certList);
+ ASSERT_EQ(NS_OK, rv) << "Should have loaded OK";
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ rv = nsNSSCertificate::GetRootCertificate(certList, rootCert);
+ EXPECT_EQ(NS_OK, rv) << "Should have again fetched the root OK";
+ ASSERT_TRUE(rootCert)
+ << "Root cert should be filled in";
+
+ nsAutoString rootCn;
+ EXPECT_TRUE(NS_SUCCEEDED(rootCert->GetCommonName(rootCn)))
+ << "Getters should work.";
+ EXPECT_TRUE(rootCn.EqualsLiteral("ca")) << "Root CN should match";
+}
+
+TEST_F(psm_CertList, TestGetRootCertificateChainEmpty) {
+ nsTArray<RefPtr<nsIX509Cert>> certList;
+
+ nsCOMPtr<nsIX509Cert> rootCert;
+ nsresult rv = nsNSSCertificate::GetRootCertificate(certList, rootCert);
+ EXPECT_EQ(NS_ERROR_FAILURE, rv)
+ << "Should have returned NS_ERROR_FAILURE because certList was empty";
+ EXPECT_FALSE(rootCert) << "Root cert should be empty";
+}
diff --git a/security/manager/ssl/tests/gtest/CoseTest.cpp b/security/manager/ssl/tests/gtest/CoseTest.cpp
new file mode 100644
index 0000000000..2f05cad3a7
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/CoseTest.cpp
@@ -0,0 +1,756 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "gtest/gtest.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <cstddef>
+#include <memory>
+
+#include <keyhi.h>
+#include <nss.h>
+#include <pk11pub.h>
+
+#include <pkcs11t.h>
+#include <secmodt.h>
+#include <cert.h>
+
+#include "ScopedNSSTypes.h"
+#include "cosec.h"
+
+namespace mozilla {
+
+// "This is the content."
+const uint8_t PAYLOAD[] = {84, 104, 105, 115, 32, 105, 115, 32, 116, 104,
+ 101, 32, 99, 111, 110, 116, 101, 110, 116, 46};
+
+// This is a COSE signature generated with the cose rust library (see
+// third-party/rust/cose). The payload is signed with the P256 key from
+// pykey.py.
+const uint8_t SIGNATURE[] = {
+ 0xd8, 0x62, 0x84, 0x59, 0x02, 0xa3, 0xa1, 0x04, 0x82, 0x59, 0x01, 0x4e,
+ 0x30, 0x82, 0x01, 0x4a, 0x30, 0x81, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02,
+ 0x02, 0x14, 0x5f, 0x3f, 0xae, 0x90, 0x49, 0x30, 0x2f, 0x33, 0x6e, 0x95,
+ 0x23, 0xa7, 0xcb, 0x23, 0xd7, 0x65, 0x4f, 0xea, 0x3c, 0xf7, 0x30, 0x0a,
+ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x14,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18, 0x0f,
+ 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32, 0x33,
+ 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x14, 0x31, 0x12,
+ 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb,
+ 0xbb, 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04,
+ 0xe2, 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c,
+ 0x4f, 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36,
+ 0xc4, 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90,
+ 0xd7, 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6,
+ 0xc0, 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0a, 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30,
+ 0x45, 0x02, 0x20, 0x5c, 0x75, 0x51, 0x9f, 0x13, 0x11, 0x50, 0xcd, 0x5d,
+ 0x8a, 0xde, 0x20, 0xa3, 0xbc, 0x06, 0x30, 0x91, 0xff, 0xb2, 0x73, 0x75,
+ 0x5f, 0x31, 0x64, 0xec, 0xfd, 0xcb, 0x42, 0x80, 0x0a, 0x70, 0xe6, 0x02,
+ 0x21, 0x00, 0xc2, 0xe4, 0xc1, 0xa8, 0xe2, 0x89, 0xdc, 0xa1, 0xbb, 0xe7,
+ 0xd5, 0x4f, 0x5c, 0x88, 0xad, 0xeb, 0xa4, 0x78, 0xa1, 0x19, 0xbe, 0x22,
+ 0x54, 0xc8, 0x9f, 0xef, 0xb8, 0x5d, 0xa2, 0x40, 0xd9, 0x8b, 0x59, 0x01,
+ 0x4c, 0x30, 0x82, 0x01, 0x48, 0x30, 0x81, 0xf0, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x43, 0x63, 0x59, 0xad, 0x04, 0x34, 0x56, 0x80, 0x43,
+ 0xec, 0x90, 0x6a, 0xd4, 0x10, 0x64, 0x7c, 0x7f, 0x38, 0x32, 0xe2, 0x30,
+ 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30,
+ 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09,
+ 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18,
+ 0x0f, 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x13, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x69, 0x6e,
+ 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb,
+ 0xbb, 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04,
+ 0xe2, 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c,
+ 0x4f, 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36,
+ 0xc4, 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90,
+ 0xd7, 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6,
+ 0xc0, 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0a, 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30,
+ 0x44, 0x02, 0x20, 0x63, 0x59, 0x02, 0x01, 0x89, 0xd7, 0x3e, 0x5b, 0xff,
+ 0xd1, 0x16, 0x4e, 0xe3, 0xe2, 0x0a, 0xe0, 0x4a, 0xd8, 0x75, 0xaf, 0x77,
+ 0x5c, 0x93, 0x60, 0xba, 0x10, 0x1f, 0x97, 0xdd, 0x27, 0x2d, 0x24, 0x02,
+ 0x20, 0x3d, 0x87, 0x0f, 0xac, 0x22, 0x4d, 0x16, 0xd9, 0xa1, 0x95, 0xbb,
+ 0x56, 0xe0, 0x21, 0x05, 0x93, 0xd1, 0x07, 0xb5, 0x25, 0x3b, 0xf4, 0x57,
+ 0x20, 0x87, 0x13, 0xa2, 0xf7, 0x78, 0x15, 0x30, 0xa7, 0xa0, 0xf6, 0x81,
+ 0x83, 0x59, 0x01, 0x33, 0xa2, 0x01, 0x26, 0x04, 0x59, 0x01, 0x2c, 0x30,
+ 0x82, 0x01, 0x28, 0x30, 0x81, 0xcf, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
+ 0x14, 0x2f, 0xc3, 0x5f, 0x05, 0x80, 0xb4, 0x49, 0x45, 0x13, 0x92, 0xd6,
+ 0x93, 0xb7, 0x2d, 0x71, 0x19, 0xc5, 0x8c, 0x40, 0x39, 0x30, 0x0a, 0x06,
+ 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x13, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x69, 0x6e,
+ 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18, 0x0f, 0x32, 0x30,
+ 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32, 0x33, 0x31, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x65, 0x65, 0x2d, 0x70, 0x32,
+ 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01,
+ 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb, 0xbb, 0x61, 0xe0, 0xf8,
+ 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04, 0xe2, 0xec, 0x05, 0x0b,
+ 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c, 0x4f, 0x79, 0x4b, 0x45,
+ 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36, 0xc4, 0x11, 0x9d, 0x07,
+ 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90, 0xd7, 0x99, 0x1b, 0x7b,
+ 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6, 0xc0, 0x30, 0x0a, 0x06,
+ 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00,
+ 0x30, 0x45, 0x02, 0x20, 0x5c, 0x75, 0x51, 0x9f, 0x13, 0x11, 0x50, 0xcd,
+ 0x5d, 0x8a, 0xde, 0x20, 0xa3, 0xbc, 0x06, 0x30, 0x91, 0xff, 0xb2, 0x73,
+ 0x75, 0x5f, 0x31, 0x64, 0xec, 0xfd, 0xcb, 0x42, 0x80, 0x0a, 0x70, 0xe6,
+ 0x02, 0x21, 0x00, 0xff, 0x81, 0xbe, 0xa8, 0x0d, 0x03, 0x36, 0x6b, 0x75,
+ 0xe2, 0x70, 0x6a, 0xac, 0x07, 0x2e, 0x4c, 0xdc, 0xf9, 0xc5, 0x89, 0xc1,
+ 0xcf, 0x88, 0xc2, 0xc8, 0x2a, 0x32, 0xf5, 0x42, 0x0c, 0xfa, 0x0b, 0xa0,
+ 0x58, 0x40, 0x1e, 0x6e, 0x08, 0xdf, 0x8f, 0x4f, 0xd6, 0xab, 0x23, 0xae,
+ 0x84, 0xaa, 0xf3, 0x43, 0x35, 0x9a, 0x53, 0xb9, 0x8b, 0xf9, 0x81, 0xa1,
+ 0xbc, 0x1e, 0x5c, 0x57, 0x5c, 0x0a, 0x20, 0x37, 0xf4, 0x3d, 0x11, 0x08,
+ 0xa0, 0x97, 0x4b, 0x68, 0xa4, 0x0f, 0x80, 0xe9, 0x96, 0x30, 0x04, 0x24,
+ 0x0e, 0x81, 0x3d, 0x2a, 0x8a, 0x64, 0x40, 0x61, 0x5a, 0x19, 0x00, 0xff,
+ 0x74, 0x40, 0x71, 0x82, 0x65, 0xe9};
+
+// This is a COSE signature generated with the cose rust library (see
+// third-party/rust/cose). The payload is signed twice; with the P256 and the
+// RSA key from pykey.py.
+const uint8_t SIGNATURE_ES256_PS256[] = {
+ 0xd8, 0x62, 0x84, 0x59, 0x08, 0x52, 0xa1, 0x04, 0x84, 0x59, 0x01, 0x4e,
+ 0x30, 0x82, 0x01, 0x4a, 0x30, 0x81, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02,
+ 0x02, 0x14, 0x5f, 0x3f, 0xae, 0x90, 0x49, 0x30, 0x2f, 0x33, 0x6e, 0x95,
+ 0x23, 0xa7, 0xcb, 0x23, 0xd7, 0x65, 0x4f, 0xea, 0x3c, 0xf7, 0x30, 0x0a,
+ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x14,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18, 0x0f,
+ 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32, 0x33,
+ 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x14, 0x31, 0x12,
+ 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb,
+ 0xbb, 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04,
+ 0xe2, 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c,
+ 0x4f, 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36,
+ 0xc4, 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90,
+ 0xd7, 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6,
+ 0xc0, 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0a, 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30,
+ 0x45, 0x02, 0x20, 0x5c, 0x75, 0x51, 0x9f, 0x13, 0x11, 0x50, 0xcd, 0x5d,
+ 0x8a, 0xde, 0x20, 0xa3, 0xbc, 0x06, 0x30, 0x91, 0xff, 0xb2, 0x73, 0x75,
+ 0x5f, 0x31, 0x64, 0xec, 0xfd, 0xcb, 0x42, 0x80, 0x0a, 0x70, 0xe6, 0x02,
+ 0x21, 0x00, 0xc2, 0xe4, 0xc1, 0xa8, 0xe2, 0x89, 0xdc, 0xa1, 0xbb, 0xe7,
+ 0xd5, 0x4f, 0x5c, 0x88, 0xad, 0xeb, 0xa4, 0x78, 0xa1, 0x19, 0xbe, 0x22,
+ 0x54, 0xc8, 0x9f, 0xef, 0xb8, 0x5d, 0xa2, 0x40, 0xd9, 0x8b, 0x59, 0x01,
+ 0x4c, 0x30, 0x82, 0x01, 0x48, 0x30, 0x81, 0xf0, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x43, 0x63, 0x59, 0xad, 0x04, 0x34, 0x56, 0x80, 0x43,
+ 0xec, 0x90, 0x6a, 0xd4, 0x10, 0x64, 0x7c, 0x7f, 0x38, 0x32, 0xe2, 0x30,
+ 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30,
+ 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09,
+ 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18,
+ 0x0f, 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x13, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x69, 0x6e,
+ 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb,
+ 0xbb, 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04,
+ 0xe2, 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c,
+ 0x4f, 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36,
+ 0xc4, 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90,
+ 0xd7, 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6,
+ 0xc0, 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0a, 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30,
+ 0x44, 0x02, 0x20, 0x63, 0x59, 0x02, 0x01, 0x89, 0xd7, 0x3e, 0x5b, 0xff,
+ 0xd1, 0x16, 0x4e, 0xe3, 0xe2, 0x0a, 0xe0, 0x4a, 0xd8, 0x75, 0xaf, 0x77,
+ 0x5c, 0x93, 0x60, 0xba, 0x10, 0x1f, 0x97, 0xdd, 0x27, 0x2d, 0x24, 0x02,
+ 0x20, 0x3d, 0x87, 0x0f, 0xac, 0x22, 0x4d, 0x16, 0xd9, 0xa1, 0x95, 0xbb,
+ 0x56, 0xe0, 0x21, 0x05, 0x93, 0xd1, 0x07, 0xb5, 0x25, 0x3b, 0xf4, 0x57,
+ 0x20, 0x87, 0x13, 0xa2, 0xf7, 0x78, 0x15, 0x30, 0xa7, 0x59, 0x02, 0xd5,
+ 0x30, 0x82, 0x02, 0xd1, 0x30, 0x82, 0x01, 0xbb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x29, 0x6c, 0x1a, 0xd8, 0x20, 0xcd, 0x74, 0x6d, 0x4b,
+ 0x00, 0xf3, 0x16, 0x88, 0xd9, 0x66, 0x87, 0x5f, 0x28, 0x56, 0x6a, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x30, 0x13, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x22, 0x18,
+ 0x0f, 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x13, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x72, 0x6f,
+ 0x6f, 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xba, 0x88, 0x51, 0xa8, 0x44, 0x8e, 0x16, 0xd6, 0x41,
+ 0xfd, 0x6e, 0xb6, 0x88, 0x06, 0x36, 0x10, 0x3d, 0x3c, 0x13, 0xd9, 0xea,
+ 0xe4, 0x35, 0x4a, 0xb4, 0xec, 0xf5, 0x68, 0x57, 0x6c, 0x24, 0x7b, 0xc1,
+ 0xc7, 0x25, 0xa8, 0xe0, 0xd8, 0x1f, 0xbd, 0xb1, 0x9c, 0x06, 0x9b, 0x6e,
+ 0x1a, 0x86, 0xf2, 0x6b, 0xe2, 0xaf, 0x5a, 0x75, 0x6b, 0x6a, 0x64, 0x71,
+ 0x08, 0x7a, 0xa5, 0x5a, 0xa7, 0x45, 0x87, 0xf7, 0x1c, 0xd5, 0x24, 0x9c,
+ 0x02, 0x7e, 0xcd, 0x43, 0xfc, 0x1e, 0x69, 0xd0, 0x38, 0x20, 0x29, 0x93,
+ 0xab, 0x20, 0xc3, 0x49, 0xe4, 0xdb, 0xb9, 0x4c, 0xc2, 0x6b, 0x6c, 0x0e,
+ 0xed, 0x15, 0x82, 0x0f, 0xf1, 0x7e, 0xad, 0x69, 0x1a, 0xb1, 0xd3, 0x02,
+ 0x3a, 0x8b, 0x2a, 0x41, 0xee, 0xa7, 0x70, 0xe0, 0x0f, 0x0d, 0x8d, 0xfd,
+ 0x66, 0x0b, 0x2b, 0xb0, 0x24, 0x92, 0xa4, 0x7d, 0xb9, 0x88, 0x61, 0x79,
+ 0x90, 0xb1, 0x57, 0x90, 0x3d, 0xd2, 0x3b, 0xc5, 0xe0, 0xb8, 0x48, 0x1f,
+ 0xa8, 0x37, 0xd3, 0x88, 0x43, 0xef, 0x27, 0x16, 0xd8, 0x55, 0xb7, 0x66,
+ 0x5a, 0xaa, 0x7e, 0x02, 0x90, 0x2f, 0x3a, 0x7b, 0x10, 0x80, 0x06, 0x24,
+ 0xcc, 0x1c, 0x6c, 0x97, 0xad, 0x96, 0x61, 0x5b, 0xb7, 0xe2, 0x96, 0x12,
+ 0xc0, 0x75, 0x31, 0xa3, 0x0c, 0x91, 0xdd, 0xb4, 0xca, 0xf7, 0xfc, 0xad,
+ 0x1d, 0x25, 0xd3, 0x09, 0xef, 0xb9, 0x17, 0x0e, 0xa7, 0x68, 0xe1, 0xb3,
+ 0x7b, 0x2f, 0x22, 0x6f, 0x69, 0xe3, 0xb4, 0x8a, 0x95, 0x61, 0x1d, 0xee,
+ 0x26, 0xd6, 0x25, 0x9d, 0xab, 0x91, 0x08, 0x4e, 0x36, 0xcb, 0x1c, 0x24,
+ 0x04, 0x2c, 0xbf, 0x16, 0x8b, 0x2f, 0xe5, 0xf1, 0x8f, 0x99, 0x17, 0x31,
+ 0xb8, 0xb3, 0xfe, 0x49, 0x23, 0xfa, 0x72, 0x51, 0xc4, 0x31, 0xd5, 0x03,
+ 0xac, 0xda, 0x18, 0x0a, 0x35, 0xed, 0x8d, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0b, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x23, 0x2f, 0x9f, 0x72, 0xeb, 0x70, 0x6d, 0x9e, 0x3e, 0x9f, 0xd7,
+ 0x9c, 0xd9, 0x19, 0x7c, 0x99, 0x07, 0xc5, 0x5c, 0x9d, 0xf5, 0x66, 0x9f,
+ 0x28, 0x8d, 0xfe, 0x0e, 0x3f, 0x38, 0x75, 0xed, 0xee, 0x4e, 0x3f, 0xf6,
+ 0x6e, 0x35, 0xe0, 0x95, 0x3f, 0x08, 0x4a, 0x71, 0x5a, 0xf2, 0x4f, 0xc9,
+ 0x96, 0x61, 0x8d, 0x45, 0x4b, 0x97, 0x85, 0xff, 0xb0, 0xe3, 0xbb, 0xb5,
+ 0xd7, 0x7e, 0xfb, 0xd2, 0xfc, 0xec, 0xfe, 0x42, 0x9f, 0x4e, 0x7b, 0xbf,
+ 0x97, 0xbb, 0xb4, 0x3a, 0x93, 0x0b, 0x13, 0x61, 0x90, 0x0c, 0x3a, 0xce,
+ 0xf7, 0x8e, 0xef, 0x80, 0xf5, 0x4a, 0x92, 0xc5, 0xa5, 0x03, 0x78, 0xc2,
+ 0xee, 0xb8, 0x66, 0x60, 0x6b, 0x76, 0x4f, 0x32, 0x5a, 0x1a, 0xa2, 0x4b,
+ 0x7e, 0x2b, 0xa6, 0x1a, 0x89, 0x01, 0xe3, 0xbb, 0x55, 0x13, 0x7c, 0x4c,
+ 0xf4, 0x6a, 0x99, 0x94, 0xd1, 0xa0, 0x84, 0x1c, 0x1a, 0xc2, 0x7b, 0xb4,
+ 0xa0, 0xb0, 0x3b, 0xdc, 0x5a, 0x7b, 0xc7, 0xe0, 0x44, 0xb2, 0x1f, 0x46,
+ 0xd5, 0x8b, 0x39, 0x8b, 0xdc, 0x9e, 0xce, 0xa8, 0x7f, 0x85, 0x1d, 0x4b,
+ 0x63, 0x06, 0x1e, 0x8e, 0xe5, 0xe5, 0x99, 0xd9, 0xf7, 0x4d, 0x89, 0x0b,
+ 0x1d, 0x5c, 0x27, 0x33, 0x66, 0x21, 0xcf, 0x9a, 0xbd, 0x98, 0x68, 0x23,
+ 0x3a, 0x66, 0x9d, 0xd4, 0x46, 0xed, 0x63, 0x58, 0xf3, 0x42, 0xe4, 0x1d,
+ 0xe2, 0x47, 0x65, 0x13, 0x8d, 0xd4, 0x1f, 0x4b, 0x7e, 0xde, 0x11, 0x56,
+ 0xf8, 0x6d, 0x01, 0x0c, 0x99, 0xbd, 0x8d, 0xca, 0x8a, 0x2e, 0xe3, 0x8a,
+ 0x9c, 0x3d, 0x83, 0x8d, 0x69, 0x62, 0x8d, 0x05, 0xea, 0xb7, 0xf5, 0xa3,
+ 0x4b, 0xfc, 0x96, 0xcf, 0x18, 0x21, 0x0a, 0xc7, 0xf3, 0x23, 0x7e, 0x1c,
+ 0xab, 0xe2, 0xa2, 0xd1, 0x83, 0xc4, 0x25, 0x93, 0x37, 0x80, 0xca, 0xda,
+ 0xf0, 0xef, 0x7d, 0x94, 0xb5, 0x59, 0x02, 0xd4, 0x30, 0x82, 0x02, 0xd0,
+ 0x30, 0x82, 0x01, 0xba, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x07,
+ 0x10, 0xaf, 0xc4, 0x1a, 0x3a, 0x56, 0x4f, 0xd8, 0xc2, 0xcc, 0x46, 0xd7,
+ 0x5b, 0xdf, 0x1c, 0x4e, 0x2f, 0x49, 0x3a, 0x30, 0x0b, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x30, 0x13, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x22, 0x18, 0x0f, 0x32, 0x30, 0x31,
+ 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32, 0x33, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x69, 0x6e, 0x74, 0x2d, 0x72, 0x73,
+ 0x61, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xba, 0x88,
+ 0x51, 0xa8, 0x44, 0x8e, 0x16, 0xd6, 0x41, 0xfd, 0x6e, 0xb6, 0x88, 0x06,
+ 0x36, 0x10, 0x3d, 0x3c, 0x13, 0xd9, 0xea, 0xe4, 0x35, 0x4a, 0xb4, 0xec,
+ 0xf5, 0x68, 0x57, 0x6c, 0x24, 0x7b, 0xc1, 0xc7, 0x25, 0xa8, 0xe0, 0xd8,
+ 0x1f, 0xbd, 0xb1, 0x9c, 0x06, 0x9b, 0x6e, 0x1a, 0x86, 0xf2, 0x6b, 0xe2,
+ 0xaf, 0x5a, 0x75, 0x6b, 0x6a, 0x64, 0x71, 0x08, 0x7a, 0xa5, 0x5a, 0xa7,
+ 0x45, 0x87, 0xf7, 0x1c, 0xd5, 0x24, 0x9c, 0x02, 0x7e, 0xcd, 0x43, 0xfc,
+ 0x1e, 0x69, 0xd0, 0x38, 0x20, 0x29, 0x93, 0xab, 0x20, 0xc3, 0x49, 0xe4,
+ 0xdb, 0xb9, 0x4c, 0xc2, 0x6b, 0x6c, 0x0e, 0xed, 0x15, 0x82, 0x0f, 0xf1,
+ 0x7e, 0xad, 0x69, 0x1a, 0xb1, 0xd3, 0x02, 0x3a, 0x8b, 0x2a, 0x41, 0xee,
+ 0xa7, 0x70, 0xe0, 0x0f, 0x0d, 0x8d, 0xfd, 0x66, 0x0b, 0x2b, 0xb0, 0x24,
+ 0x92, 0xa4, 0x7d, 0xb9, 0x88, 0x61, 0x79, 0x90, 0xb1, 0x57, 0x90, 0x3d,
+ 0xd2, 0x3b, 0xc5, 0xe0, 0xb8, 0x48, 0x1f, 0xa8, 0x37, 0xd3, 0x88, 0x43,
+ 0xef, 0x27, 0x16, 0xd8, 0x55, 0xb7, 0x66, 0x5a, 0xaa, 0x7e, 0x02, 0x90,
+ 0x2f, 0x3a, 0x7b, 0x10, 0x80, 0x06, 0x24, 0xcc, 0x1c, 0x6c, 0x97, 0xad,
+ 0x96, 0x61, 0x5b, 0xb7, 0xe2, 0x96, 0x12, 0xc0, 0x75, 0x31, 0xa3, 0x0c,
+ 0x91, 0xdd, 0xb4, 0xca, 0xf7, 0xfc, 0xad, 0x1d, 0x25, 0xd3, 0x09, 0xef,
+ 0xb9, 0x17, 0x0e, 0xa7, 0x68, 0xe1, 0xb3, 0x7b, 0x2f, 0x22, 0x6f, 0x69,
+ 0xe3, 0xb4, 0x8a, 0x95, 0x61, 0x1d, 0xee, 0x26, 0xd6, 0x25, 0x9d, 0xab,
+ 0x91, 0x08, 0x4e, 0x36, 0xcb, 0x1c, 0x24, 0x04, 0x2c, 0xbf, 0x16, 0x8b,
+ 0x2f, 0xe5, 0xf1, 0x8f, 0x99, 0x17, 0x31, 0xb8, 0xb3, 0xfe, 0x49, 0x23,
+ 0xfa, 0x72, 0x51, 0xc4, 0x31, 0xd5, 0x03, 0xac, 0xda, 0x18, 0x0a, 0x35,
+ 0xed, 0x8d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x1d, 0x30, 0x1b, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01,
+ 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01, 0x00, 0x5e, 0xba, 0x69, 0x55,
+ 0x9f, 0xf8, 0xeb, 0x16, 0x21, 0x98, 0xde, 0xb7, 0x31, 0x3e, 0x66, 0xe1,
+ 0x3b, 0x0c, 0x29, 0xf7, 0x48, 0x73, 0x05, 0xd9, 0xce, 0x5e, 0x4c, 0xbe,
+ 0x03, 0xc4, 0x51, 0xd6, 0x21, 0x92, 0x40, 0x38, 0xaa, 0x5b, 0x28, 0xb5,
+ 0xa1, 0x10, 0x52, 0x57, 0xff, 0x91, 0x54, 0x82, 0x86, 0x9e, 0x74, 0xd5,
+ 0x3d, 0x82, 0x29, 0xee, 0xd1, 0xcf, 0x93, 0xb1, 0x24, 0x76, 0xbb, 0x95,
+ 0x41, 0x06, 0x7e, 0x40, 0x9b, 0xb4, 0xab, 0x44, 0x34, 0x10, 0x8f, 0xb1,
+ 0x51, 0x6f, 0xc0, 0x89, 0xd1, 0xa3, 0xc4, 0x9f, 0xb3, 0x48, 0xe1, 0xcd,
+ 0x73, 0xad, 0xff, 0x42, 0x5f, 0x76, 0x05, 0x60, 0xc5, 0xe0, 0x45, 0x79,
+ 0x18, 0xa1, 0x19, 0xb8, 0xa7, 0x3a, 0x64, 0xb3, 0x19, 0xba, 0x14, 0xa1,
+ 0xb5, 0xdc, 0x32, 0xec, 0x09, 0x39, 0x58, 0x54, 0x5b, 0x04, 0xdc, 0x1b,
+ 0x66, 0x0d, 0x1d, 0x0d, 0xce, 0x7f, 0xfa, 0x24, 0x52, 0x6a, 0xad, 0xe2,
+ 0xc8, 0x30, 0xaf, 0xf2, 0xaf, 0x63, 0xc5, 0xe2, 0xbf, 0xe2, 0x20, 0x1b,
+ 0x9e, 0xf9, 0x3d, 0xbc, 0xfb, 0x04, 0x8e, 0xda, 0x7a, 0x1a, 0x5d, 0xd3,
+ 0x13, 0xd7, 0x00, 0x8e, 0x9b, 0x5d, 0x85, 0x51, 0xda, 0xd3, 0x91, 0x25,
+ 0xf5, 0x67, 0x85, 0x3e, 0x25, 0x89, 0x5e, 0xcb, 0x89, 0x8a, 0xec, 0x8a,
+ 0xde, 0x8b, 0xf4, 0x33, 0x5f, 0x76, 0xdb, 0x3d, 0xfc, 0x6a, 0x05, 0x21,
+ 0x43, 0xb2, 0x41, 0xd8, 0x33, 0x8d, 0xfd, 0x05, 0x5c, 0x22, 0x0a, 0xf6,
+ 0x90, 0x65, 0x9c, 0x4f, 0x8c, 0x44, 0x9f, 0x2d, 0xca, 0xf3, 0x49, 0x9c,
+ 0x3a, 0x14, 0x88, 0xab, 0xe4, 0xce, 0xb7, 0xbc, 0x95, 0x22, 0x2e, 0xb1,
+ 0x82, 0x4c, 0xbf, 0x83, 0x3e, 0x49, 0x72, 0x03, 0x2a, 0x68, 0xe7, 0x2d,
+ 0xe5, 0x2d, 0x4b, 0x61, 0xb0, 0x8d, 0x0d, 0x0c, 0x87, 0xc6, 0x5c, 0x51,
+ 0xa0, 0xf6, 0x82, 0x83, 0x59, 0x01, 0x33, 0xa2, 0x01, 0x26, 0x04, 0x59,
+ 0x01, 0x2c, 0x30, 0x82, 0x01, 0x28, 0x30, 0x81, 0xcf, 0xa0, 0x03, 0x02,
+ 0x01, 0x02, 0x02, 0x14, 0x2f, 0xc3, 0x5f, 0x05, 0x80, 0xb4, 0x49, 0x45,
+ 0x13, 0x92, 0xd6, 0x93, 0xb7, 0x2d, 0x71, 0x19, 0xc5, 0x8c, 0x40, 0x39,
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
+ 0x30, 0x13, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x08, 0x69, 0x6e, 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18,
+ 0x0f, 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x12, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x65, 0x65,
+ 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb, 0xbb,
+ 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04, 0xe2,
+ 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c, 0x4f,
+ 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36, 0xc4,
+ 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90, 0xd7,
+ 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6, 0xc0,
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
+ 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x5c, 0x75, 0x51, 0x9f, 0x13,
+ 0x11, 0x50, 0xcd, 0x5d, 0x8a, 0xde, 0x20, 0xa3, 0xbc, 0x06, 0x30, 0x91,
+ 0xff, 0xb2, 0x73, 0x75, 0x5f, 0x31, 0x64, 0xec, 0xfd, 0xcb, 0x42, 0x80,
+ 0x0a, 0x70, 0xe6, 0x02, 0x21, 0x00, 0xff, 0x81, 0xbe, 0xa8, 0x0d, 0x03,
+ 0x36, 0x6b, 0x75, 0xe2, 0x70, 0x6a, 0xac, 0x07, 0x2e, 0x4c, 0xdc, 0xf9,
+ 0xc5, 0x89, 0xc1, 0xcf, 0x88, 0xc2, 0xc8, 0x2a, 0x32, 0xf5, 0x42, 0x0c,
+ 0xfa, 0x0b, 0xa0, 0x58, 0x40, 0xa3, 0xfb, 0x49, 0xe6, 0x45, 0x29, 0x64,
+ 0x76, 0xeb, 0x9d, 0xbd, 0xf5, 0x38, 0x56, 0xbe, 0x6e, 0x31, 0x57, 0x73,
+ 0xc1, 0x2d, 0x3e, 0xac, 0xee, 0xba, 0x55, 0x8e, 0x37, 0xd4, 0xea, 0x80,
+ 0x25, 0x31, 0x99, 0x9f, 0x4a, 0xb0, 0xf9, 0xd8, 0xb0, 0xed, 0x74, 0xfc,
+ 0x8c, 0x02, 0xf0, 0x9f, 0x95, 0xf1, 0xaa, 0x71, 0xcc, 0xd2, 0xe7, 0x1a,
+ 0x6d, 0xd4, 0xed, 0xff, 0xf2, 0x78, 0x09, 0x83, 0x7e, 0x83, 0x59, 0x02,
+ 0xbb, 0xa2, 0x01, 0x38, 0x24, 0x04, 0x59, 0x02, 0xb3, 0x30, 0x82, 0x02,
+ 0xaf, 0x30, 0x82, 0x01, 0x99, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14,
+ 0x07, 0x1c, 0x3b, 0x71, 0x08, 0xbe, 0xd7, 0x9f, 0xfd, 0xaf, 0x26, 0xb6,
+ 0x08, 0xa3, 0x99, 0x06, 0x77, 0x69, 0x32, 0x7e, 0x30, 0x0b, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x30, 0x12, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x69, 0x6e,
+ 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x22, 0x18, 0x0f, 0x32, 0x30, 0x31,
+ 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32, 0x33, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x65, 0x65, 0x2d, 0x72, 0x73, 0x61,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xba, 0x88, 0x51,
+ 0xa8, 0x44, 0x8e, 0x16, 0xd6, 0x41, 0xfd, 0x6e, 0xb6, 0x88, 0x06, 0x36,
+ 0x10, 0x3d, 0x3c, 0x13, 0xd9, 0xea, 0xe4, 0x35, 0x4a, 0xb4, 0xec, 0xf5,
+ 0x68, 0x57, 0x6c, 0x24, 0x7b, 0xc1, 0xc7, 0x25, 0xa8, 0xe0, 0xd8, 0x1f,
+ 0xbd, 0xb1, 0x9c, 0x06, 0x9b, 0x6e, 0x1a, 0x86, 0xf2, 0x6b, 0xe2, 0xaf,
+ 0x5a, 0x75, 0x6b, 0x6a, 0x64, 0x71, 0x08, 0x7a, 0xa5, 0x5a, 0xa7, 0x45,
+ 0x87, 0xf7, 0x1c, 0xd5, 0x24, 0x9c, 0x02, 0x7e, 0xcd, 0x43, 0xfc, 0x1e,
+ 0x69, 0xd0, 0x38, 0x20, 0x29, 0x93, 0xab, 0x20, 0xc3, 0x49, 0xe4, 0xdb,
+ 0xb9, 0x4c, 0xc2, 0x6b, 0x6c, 0x0e, 0xed, 0x15, 0x82, 0x0f, 0xf1, 0x7e,
+ 0xad, 0x69, 0x1a, 0xb1, 0xd3, 0x02, 0x3a, 0x8b, 0x2a, 0x41, 0xee, 0xa7,
+ 0x70, 0xe0, 0x0f, 0x0d, 0x8d, 0xfd, 0x66, 0x0b, 0x2b, 0xb0, 0x24, 0x92,
+ 0xa4, 0x7d, 0xb9, 0x88, 0x61, 0x79, 0x90, 0xb1, 0x57, 0x90, 0x3d, 0xd2,
+ 0x3b, 0xc5, 0xe0, 0xb8, 0x48, 0x1f, 0xa8, 0x37, 0xd3, 0x88, 0x43, 0xef,
+ 0x27, 0x16, 0xd8, 0x55, 0xb7, 0x66, 0x5a, 0xaa, 0x7e, 0x02, 0x90, 0x2f,
+ 0x3a, 0x7b, 0x10, 0x80, 0x06, 0x24, 0xcc, 0x1c, 0x6c, 0x97, 0xad, 0x96,
+ 0x61, 0x5b, 0xb7, 0xe2, 0x96, 0x12, 0xc0, 0x75, 0x31, 0xa3, 0x0c, 0x91,
+ 0xdd, 0xb4, 0xca, 0xf7, 0xfc, 0xad, 0x1d, 0x25, 0xd3, 0x09, 0xef, 0xb9,
+ 0x17, 0x0e, 0xa7, 0x68, 0xe1, 0xb3, 0x7b, 0x2f, 0x22, 0x6f, 0x69, 0xe3,
+ 0xb4, 0x8a, 0x95, 0x61, 0x1d, 0xee, 0x26, 0xd6, 0x25, 0x9d, 0xab, 0x91,
+ 0x08, 0x4e, 0x36, 0xcb, 0x1c, 0x24, 0x04, 0x2c, 0xbf, 0x16, 0x8b, 0x2f,
+ 0xe5, 0xf1, 0x8f, 0x99, 0x17, 0x31, 0xb8, 0xb3, 0xfe, 0x49, 0x23, 0xfa,
+ 0x72, 0x51, 0xc4, 0x31, 0xd5, 0x03, 0xac, 0xda, 0x18, 0x0a, 0x35, 0xed,
+ 0x8d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x44, 0x92, 0xbb, 0x8e, 0x83, 0x58, 0x56, 0x2e, 0x7a, 0x86, 0xfa, 0x1d,
+ 0x77, 0x50, 0x3f, 0x45, 0x8d, 0x90, 0xc4, 0x62, 0x27, 0x21, 0x96, 0x5a,
+ 0xef, 0x51, 0x78, 0xd7, 0x7d, 0x0d, 0x02, 0x2d, 0x5a, 0x0e, 0x3c, 0x82,
+ 0x6f, 0x1d, 0x92, 0x87, 0xd5, 0x1a, 0x44, 0xae, 0xa7, 0x92, 0xd1, 0x8b,
+ 0xfa, 0x16, 0x53, 0x7f, 0xa3, 0x22, 0x96, 0x1a, 0x51, 0x8c, 0xeb, 0xa1,
+ 0xe6, 0xf6, 0x37, 0x11, 0xfe, 0x7d, 0x53, 0x3f, 0xae, 0xf0, 0x6b, 0xb9,
+ 0xb1, 0x7a, 0x73, 0x07, 0x14, 0xcf, 0x04, 0x05, 0x93, 0x9e, 0xe3, 0xd2,
+ 0x4d, 0x9d, 0x6d, 0x35, 0x68, 0xf9, 0x36, 0xe5, 0x10, 0x0a, 0x36, 0xd9,
+ 0x48, 0xb0, 0x83, 0xd0, 0xb9, 0x58, 0x74, 0x53, 0xb3, 0xbc, 0x99, 0xab,
+ 0xe1, 0x3e, 0xd5, 0x01, 0x8e, 0xcf, 0x3a, 0x69, 0x93, 0x9e, 0xa7, 0x88,
+ 0xd4, 0xad, 0x95, 0xf9, 0x2a, 0xb4, 0x7f, 0x95, 0x97, 0x86, 0x50, 0x38,
+ 0xb1, 0x04, 0x0a, 0xe4, 0x7a, 0xd5, 0x2d, 0x6c, 0xde, 0x3e, 0x1a, 0x47,
+ 0x17, 0x88, 0x63, 0x20, 0x9d, 0x21, 0x3e, 0x0c, 0x6f, 0xfd, 0x20, 0x54,
+ 0xd0, 0x67, 0xd2, 0x6b, 0x06, 0xfe, 0x60, 0x13, 0x42, 0x3d, 0xb7, 0xca,
+ 0xcb, 0xab, 0x7b, 0x5f, 0x5d, 0x01, 0x56, 0xd3, 0x99, 0x80, 0x0f, 0xde,
+ 0x7f, 0x3a, 0x61, 0x9c, 0xd3, 0x6b, 0x5e, 0xfe, 0xb5, 0xfc, 0x39, 0x8b,
+ 0x8e, 0xf0, 0x8c, 0x8b, 0x65, 0x46, 0x45, 0xff, 0x47, 0x8f, 0xd4, 0xdd,
+ 0xae, 0xc9, 0x72, 0xc7, 0x7f, 0x28, 0x86, 0xf1, 0xf7, 0x6e, 0xcb, 0x86,
+ 0x03, 0xeb, 0x0c, 0x46, 0xe5, 0xa0, 0x6b, 0xef, 0xd4, 0x5e, 0xa4, 0x0f,
+ 0x53, 0xe1, 0xbc, 0xb4, 0xc9, 0x37, 0x0e, 0x75, 0xdd, 0x93, 0xe8, 0x0f,
+ 0x18, 0x0a, 0x02, 0x83, 0x17, 0x74, 0xbb, 0x1a, 0x42, 0x5b, 0x63, 0x2c,
+ 0x80, 0x80, 0xa6, 0x84, 0xa0, 0x59, 0x01, 0x00, 0x51, 0xf4, 0xe6, 0x1c,
+ 0x18, 0x7b, 0x28, 0xa0, 0x1f, 0x63, 0xbf, 0xa5, 0xbd, 0x89, 0x9f, 0xd9,
+ 0x30, 0x46, 0x4b, 0x34, 0x9b, 0x9d, 0x0f, 0xb0, 0x33, 0x11, 0xf8, 0xaa,
+ 0x84, 0x4e, 0xb2, 0xca, 0x29, 0x83, 0x54, 0x28, 0x99, 0x2a, 0x43, 0x7f,
+ 0xe0, 0xe6, 0xd8, 0xdc, 0xd7, 0xf4, 0xb3, 0xd7, 0xf7, 0x39, 0xd5, 0xdc,
+ 0xde, 0xdc, 0x23, 0x78, 0xd7, 0x90, 0xc0, 0x52, 0xf5, 0xd2, 0x14, 0x6f,
+ 0xf9, 0x24, 0x48, 0xc8, 0x75, 0x4a, 0x9a, 0x4c, 0x61, 0x2f, 0x96, 0x4e,
+ 0xc8, 0x02, 0x95, 0x72, 0xef, 0xbc, 0x91, 0xae, 0xf8, 0x23, 0xfb, 0xba,
+ 0x9f, 0xfd, 0xe0, 0x1a, 0x8e, 0xa9, 0x03, 0x16, 0x76, 0xf4, 0xdb, 0x81,
+ 0x5a, 0x69, 0xeb, 0xf5, 0x55, 0xd7, 0x68, 0x28, 0xe4, 0xce, 0xde, 0x1b,
+ 0xb4, 0x90, 0xac, 0x97, 0x07, 0x15, 0xe0, 0xce, 0x5f, 0x3f, 0x89, 0xaf,
+ 0xc1, 0xb8, 0x46, 0x5e, 0x87, 0xa1, 0x8d, 0xa7, 0x44, 0x09, 0x02, 0x4e,
+ 0xbe, 0x6b, 0xfb, 0xab, 0xeb, 0x19, 0x62, 0x9e, 0xb0, 0xef, 0x0a, 0x6b,
+ 0xcf, 0xe0, 0x00, 0xa9, 0x68, 0x2a, 0x8e, 0xfe, 0x8a, 0xb9, 0x57, 0x52,
+ 0xb3, 0x08, 0x80, 0x5e, 0xa6, 0x88, 0x5f, 0x31, 0xd1, 0xe9, 0x6d, 0xf7,
+ 0x54, 0x4e, 0xf8, 0x17, 0xb0, 0x1c, 0xca, 0xa6, 0xa6, 0x80, 0xf8, 0xd8,
+ 0xf5, 0x94, 0xa4, 0xb2, 0xd0, 0x7e, 0xbb, 0x4f, 0xdb, 0x3a, 0x91, 0x5f,
+ 0xb3, 0xc1, 0xfa, 0x60, 0xe4, 0xce, 0xe3, 0xe5, 0x14, 0x1f, 0x9c, 0x01,
+ 0x60, 0xff, 0xe2, 0x09, 0xe6, 0x1a, 0x82, 0x69, 0xb6, 0xeb, 0x52, 0x1e,
+ 0x3d, 0xc7, 0xfd, 0x69, 0x9d, 0x2a, 0xa5, 0xdb, 0xc1, 0x6a, 0x5a, 0x7d,
+ 0x23, 0x2a, 0x00, 0xe4, 0x53, 0x16, 0x8e, 0xc1, 0x56, 0xf5, 0x5a, 0x8d,
+ 0x59, 0x1f, 0x7f, 0xff, 0x77, 0x6f, 0x92, 0xea, 0x5d, 0x31, 0xe9, 0x18};
+
+// The RSA intermediate certificate that issued the EE cert used in the
+// signature above. The certificate was generated with pycert.py
+const uint8_t RSA_INT[] = {
+ 0x30, 0x82, 0x02, 0xd0, 0x30, 0x82, 0x01, 0xba, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x07, 0x10, 0xaf, 0xc4, 0x1a, 0x3a, 0x56, 0x4f, 0xd8,
+ 0xc2, 0xcc, 0x46, 0xd7, 0x5b, 0xdf, 0x1c, 0x4e, 0x2f, 0x49, 0x3a, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x30, 0x13, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x22, 0x18,
+ 0x0f, 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x12, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x69, 0x6e,
+ 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xba, 0x88, 0x51, 0xa8, 0x44, 0x8e, 0x16, 0xd6, 0x41, 0xfd,
+ 0x6e, 0xb6, 0x88, 0x06, 0x36, 0x10, 0x3d, 0x3c, 0x13, 0xd9, 0xea, 0xe4,
+ 0x35, 0x4a, 0xb4, 0xec, 0xf5, 0x68, 0x57, 0x6c, 0x24, 0x7b, 0xc1, 0xc7,
+ 0x25, 0xa8, 0xe0, 0xd8, 0x1f, 0xbd, 0xb1, 0x9c, 0x06, 0x9b, 0x6e, 0x1a,
+ 0x86, 0xf2, 0x6b, 0xe2, 0xaf, 0x5a, 0x75, 0x6b, 0x6a, 0x64, 0x71, 0x08,
+ 0x7a, 0xa5, 0x5a, 0xa7, 0x45, 0x87, 0xf7, 0x1c, 0xd5, 0x24, 0x9c, 0x02,
+ 0x7e, 0xcd, 0x43, 0xfc, 0x1e, 0x69, 0xd0, 0x38, 0x20, 0x29, 0x93, 0xab,
+ 0x20, 0xc3, 0x49, 0xe4, 0xdb, 0xb9, 0x4c, 0xc2, 0x6b, 0x6c, 0x0e, 0xed,
+ 0x15, 0x82, 0x0f, 0xf1, 0x7e, 0xad, 0x69, 0x1a, 0xb1, 0xd3, 0x02, 0x3a,
+ 0x8b, 0x2a, 0x41, 0xee, 0xa7, 0x70, 0xe0, 0x0f, 0x0d, 0x8d, 0xfd, 0x66,
+ 0x0b, 0x2b, 0xb0, 0x24, 0x92, 0xa4, 0x7d, 0xb9, 0x88, 0x61, 0x79, 0x90,
+ 0xb1, 0x57, 0x90, 0x3d, 0xd2, 0x3b, 0xc5, 0xe0, 0xb8, 0x48, 0x1f, 0xa8,
+ 0x37, 0xd3, 0x88, 0x43, 0xef, 0x27, 0x16, 0xd8, 0x55, 0xb7, 0x66, 0x5a,
+ 0xaa, 0x7e, 0x02, 0x90, 0x2f, 0x3a, 0x7b, 0x10, 0x80, 0x06, 0x24, 0xcc,
+ 0x1c, 0x6c, 0x97, 0xad, 0x96, 0x61, 0x5b, 0xb7, 0xe2, 0x96, 0x12, 0xc0,
+ 0x75, 0x31, 0xa3, 0x0c, 0x91, 0xdd, 0xb4, 0xca, 0xf7, 0xfc, 0xad, 0x1d,
+ 0x25, 0xd3, 0x09, 0xef, 0xb9, 0x17, 0x0e, 0xa7, 0x68, 0xe1, 0xb3, 0x7b,
+ 0x2f, 0x22, 0x6f, 0x69, 0xe3, 0xb4, 0x8a, 0x95, 0x61, 0x1d, 0xee, 0x26,
+ 0xd6, 0x25, 0x9d, 0xab, 0x91, 0x08, 0x4e, 0x36, 0xcb, 0x1c, 0x24, 0x04,
+ 0x2c, 0xbf, 0x16, 0x8b, 0x2f, 0xe5, 0xf1, 0x8f, 0x99, 0x17, 0x31, 0xb8,
+ 0xb3, 0xfe, 0x49, 0x23, 0xfa, 0x72, 0x51, 0xc4, 0x31, 0xd5, 0x03, 0xac,
+ 0xda, 0x18, 0x0a, 0x35, 0xed, 0x8d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x5e, 0xba, 0x69, 0x55, 0x9f, 0xf8, 0xeb, 0x16, 0x21, 0x98, 0xde, 0xb7,
+ 0x31, 0x3e, 0x66, 0xe1, 0x3b, 0x0c, 0x29, 0xf7, 0x48, 0x73, 0x05, 0xd9,
+ 0xce, 0x5e, 0x4c, 0xbe, 0x03, 0xc4, 0x51, 0xd6, 0x21, 0x92, 0x40, 0x38,
+ 0xaa, 0x5b, 0x28, 0xb5, 0xa1, 0x10, 0x52, 0x57, 0xff, 0x91, 0x54, 0x82,
+ 0x86, 0x9e, 0x74, 0xd5, 0x3d, 0x82, 0x29, 0xee, 0xd1, 0xcf, 0x93, 0xb1,
+ 0x24, 0x76, 0xbb, 0x95, 0x41, 0x06, 0x7e, 0x40, 0x9b, 0xb4, 0xab, 0x44,
+ 0x34, 0x10, 0x8f, 0xb1, 0x51, 0x6f, 0xc0, 0x89, 0xd1, 0xa3, 0xc4, 0x9f,
+ 0xb3, 0x48, 0xe1, 0xcd, 0x73, 0xad, 0xff, 0x42, 0x5f, 0x76, 0x05, 0x60,
+ 0xc5, 0xe0, 0x45, 0x79, 0x18, 0xa1, 0x19, 0xb8, 0xa7, 0x3a, 0x64, 0xb3,
+ 0x19, 0xba, 0x14, 0xa1, 0xb5, 0xdc, 0x32, 0xec, 0x09, 0x39, 0x58, 0x54,
+ 0x5b, 0x04, 0xdc, 0x1b, 0x66, 0x0d, 0x1d, 0x0d, 0xce, 0x7f, 0xfa, 0x24,
+ 0x52, 0x6a, 0xad, 0xe2, 0xc8, 0x30, 0xaf, 0xf2, 0xaf, 0x63, 0xc5, 0xe2,
+ 0xbf, 0xe2, 0x20, 0x1b, 0x9e, 0xf9, 0x3d, 0xbc, 0xfb, 0x04, 0x8e, 0xda,
+ 0x7a, 0x1a, 0x5d, 0xd3, 0x13, 0xd7, 0x00, 0x8e, 0x9b, 0x5d, 0x85, 0x51,
+ 0xda, 0xd3, 0x91, 0x25, 0xf5, 0x67, 0x85, 0x3e, 0x25, 0x89, 0x5e, 0xcb,
+ 0x89, 0x8a, 0xec, 0x8a, 0xde, 0x8b, 0xf4, 0x33, 0x5f, 0x76, 0xdb, 0x3d,
+ 0xfc, 0x6a, 0x05, 0x21, 0x43, 0xb2, 0x41, 0xd8, 0x33, 0x8d, 0xfd, 0x05,
+ 0x5c, 0x22, 0x0a, 0xf6, 0x90, 0x65, 0x9c, 0x4f, 0x8c, 0x44, 0x9f, 0x2d,
+ 0xca, 0xf3, 0x49, 0x9c, 0x3a, 0x14, 0x88, 0xab, 0xe4, 0xce, 0xb7, 0xbc,
+ 0x95, 0x22, 0x2e, 0xb1, 0x82, 0x4c, 0xbf, 0x83, 0x3e, 0x49, 0x72, 0x03,
+ 0x2a, 0x68, 0xe7, 0x2d, 0xe5, 0x2d, 0x4b, 0x61, 0xb0, 0x8d, 0x0d, 0x0c,
+ 0x87, 0xc6, 0x5c, 0x51};
+
+// The RSA root certificate that issued the RSA intermediate certificate above.
+// The certificate was generated with pycert.py
+const uint8_t RSA_ROOT[] = {
+ 0x30, 0x82, 0x02, 0xd1, 0x30, 0x82, 0x01, 0xbb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x14, 0x29, 0x6c, 0x1a, 0xd8, 0x20, 0xcd, 0x74, 0x6d, 0x4b,
+ 0x00, 0xf3, 0x16, 0x88, 0xd9, 0x66, 0x87, 0x5f, 0x28, 0x56, 0x6a, 0x30,
+ 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x30, 0x13, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+ 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x22, 0x18,
+ 0x0f, 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32,
+ 0x33, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x13, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x72, 0x6f,
+ 0x6f, 0x74, 0x2d, 0x72, 0x73, 0x61, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xba, 0x88, 0x51, 0xa8, 0x44, 0x8e, 0x16, 0xd6, 0x41,
+ 0xfd, 0x6e, 0xb6, 0x88, 0x06, 0x36, 0x10, 0x3d, 0x3c, 0x13, 0xd9, 0xea,
+ 0xe4, 0x35, 0x4a, 0xb4, 0xec, 0xf5, 0x68, 0x57, 0x6c, 0x24, 0x7b, 0xc1,
+ 0xc7, 0x25, 0xa8, 0xe0, 0xd8, 0x1f, 0xbd, 0xb1, 0x9c, 0x06, 0x9b, 0x6e,
+ 0x1a, 0x86, 0xf2, 0x6b, 0xe2, 0xaf, 0x5a, 0x75, 0x6b, 0x6a, 0x64, 0x71,
+ 0x08, 0x7a, 0xa5, 0x5a, 0xa7, 0x45, 0x87, 0xf7, 0x1c, 0xd5, 0x24, 0x9c,
+ 0x02, 0x7e, 0xcd, 0x43, 0xfc, 0x1e, 0x69, 0xd0, 0x38, 0x20, 0x29, 0x93,
+ 0xab, 0x20, 0xc3, 0x49, 0xe4, 0xdb, 0xb9, 0x4c, 0xc2, 0x6b, 0x6c, 0x0e,
+ 0xed, 0x15, 0x82, 0x0f, 0xf1, 0x7e, 0xad, 0x69, 0x1a, 0xb1, 0xd3, 0x02,
+ 0x3a, 0x8b, 0x2a, 0x41, 0xee, 0xa7, 0x70, 0xe0, 0x0f, 0x0d, 0x8d, 0xfd,
+ 0x66, 0x0b, 0x2b, 0xb0, 0x24, 0x92, 0xa4, 0x7d, 0xb9, 0x88, 0x61, 0x79,
+ 0x90, 0xb1, 0x57, 0x90, 0x3d, 0xd2, 0x3b, 0xc5, 0xe0, 0xb8, 0x48, 0x1f,
+ 0xa8, 0x37, 0xd3, 0x88, 0x43, 0xef, 0x27, 0x16, 0xd8, 0x55, 0xb7, 0x66,
+ 0x5a, 0xaa, 0x7e, 0x02, 0x90, 0x2f, 0x3a, 0x7b, 0x10, 0x80, 0x06, 0x24,
+ 0xcc, 0x1c, 0x6c, 0x97, 0xad, 0x96, 0x61, 0x5b, 0xb7, 0xe2, 0x96, 0x12,
+ 0xc0, 0x75, 0x31, 0xa3, 0x0c, 0x91, 0xdd, 0xb4, 0xca, 0xf7, 0xfc, 0xad,
+ 0x1d, 0x25, 0xd3, 0x09, 0xef, 0xb9, 0x17, 0x0e, 0xa7, 0x68, 0xe1, 0xb3,
+ 0x7b, 0x2f, 0x22, 0x6f, 0x69, 0xe3, 0xb4, 0x8a, 0x95, 0x61, 0x1d, 0xee,
+ 0x26, 0xd6, 0x25, 0x9d, 0xab, 0x91, 0x08, 0x4e, 0x36, 0xcb, 0x1c, 0x24,
+ 0x04, 0x2c, 0xbf, 0x16, 0x8b, 0x2f, 0xe5, 0xf1, 0x8f, 0x99, 0x17, 0x31,
+ 0xb8, 0xb3, 0xfe, 0x49, 0x23, 0xfa, 0x72, 0x51, 0xc4, 0x31, 0xd5, 0x03,
+ 0xac, 0xda, 0x18, 0x0a, 0x35, 0xed, 0x8d, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0b, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x23, 0x2f, 0x9f, 0x72, 0xeb, 0x70, 0x6d, 0x9e, 0x3e, 0x9f, 0xd7,
+ 0x9c, 0xd9, 0x19, 0x7c, 0x99, 0x07, 0xc5, 0x5c, 0x9d, 0xf5, 0x66, 0x9f,
+ 0x28, 0x8d, 0xfe, 0x0e, 0x3f, 0x38, 0x75, 0xed, 0xee, 0x4e, 0x3f, 0xf6,
+ 0x6e, 0x35, 0xe0, 0x95, 0x3f, 0x08, 0x4a, 0x71, 0x5a, 0xf2, 0x4f, 0xc9,
+ 0x96, 0x61, 0x8d, 0x45, 0x4b, 0x97, 0x85, 0xff, 0xb0, 0xe3, 0xbb, 0xb5,
+ 0xd7, 0x7e, 0xfb, 0xd2, 0xfc, 0xec, 0xfe, 0x42, 0x9f, 0x4e, 0x7b, 0xbf,
+ 0x97, 0xbb, 0xb4, 0x3a, 0x93, 0x0b, 0x13, 0x61, 0x90, 0x0c, 0x3a, 0xce,
+ 0xf7, 0x8e, 0xef, 0x80, 0xf5, 0x4a, 0x92, 0xc5, 0xa5, 0x03, 0x78, 0xc2,
+ 0xee, 0xb8, 0x66, 0x60, 0x6b, 0x76, 0x4f, 0x32, 0x5a, 0x1a, 0xa2, 0x4b,
+ 0x7e, 0x2b, 0xa6, 0x1a, 0x89, 0x01, 0xe3, 0xbb, 0x55, 0x13, 0x7c, 0x4c,
+ 0xf4, 0x6a, 0x99, 0x94, 0xd1, 0xa0, 0x84, 0x1c, 0x1a, 0xc2, 0x7b, 0xb4,
+ 0xa0, 0xb0, 0x3b, 0xdc, 0x5a, 0x7b, 0xc7, 0xe0, 0x44, 0xb2, 0x1f, 0x46,
+ 0xd5, 0x8b, 0x39, 0x8b, 0xdc, 0x9e, 0xce, 0xa8, 0x7f, 0x85, 0x1d, 0x4b,
+ 0x63, 0x06, 0x1e, 0x8e, 0xe5, 0xe5, 0x99, 0xd9, 0xf7, 0x4d, 0x89, 0x0b,
+ 0x1d, 0x5c, 0x27, 0x33, 0x66, 0x21, 0xcf, 0x9a, 0xbd, 0x98, 0x68, 0x23,
+ 0x3a, 0x66, 0x9d, 0xd4, 0x46, 0xed, 0x63, 0x58, 0xf3, 0x42, 0xe4, 0x1d,
+ 0xe2, 0x47, 0x65, 0x13, 0x8d, 0xd4, 0x1f, 0x4b, 0x7e, 0xde, 0x11, 0x56,
+ 0xf8, 0x6d, 0x01, 0x0c, 0x99, 0xbd, 0x8d, 0xca, 0x8a, 0x2e, 0xe3, 0x8a,
+ 0x9c, 0x3d, 0x83, 0x8d, 0x69, 0x62, 0x8d, 0x05, 0xea, 0xb7, 0xf5, 0xa3,
+ 0x4b, 0xfc, 0x96, 0xcf, 0x18, 0x21, 0x0a, 0xc7, 0xf3, 0x23, 0x7e, 0x1c,
+ 0xab, 0xe2, 0xa2, 0xd1, 0x83, 0xc4, 0x25, 0x93, 0x37, 0x80, 0xca, 0xda,
+ 0xf0, 0xef, 0x7d, 0x94, 0xb5};
+
+// The P256 intermediate certificate that issued the EE cert used in the
+// signatures above. The certificate was generated with pycert.py
+const uint8_t P256_INT[] = {
+ 0x30, 0x82, 0x01, 0x48, 0x30, 0x81, 0xf0, 0xa0, 0x03, 0x02, 0x01, 0x02,
+ 0x02, 0x14, 0x43, 0x63, 0x59, 0xad, 0x04, 0x34, 0x56, 0x80, 0x43, 0xec,
+ 0x90, 0x6a, 0xd4, 0x10, 0x64, 0x7c, 0x7f, 0x38, 0x32, 0xe2, 0x30, 0x0a,
+ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x14,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18, 0x0f,
+ 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32, 0x33,
+ 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x13, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x69, 0x6e, 0x74,
+ 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb, 0xbb,
+ 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04, 0xe2,
+ 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c, 0x4f,
+ 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36, 0xc4,
+ 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90, 0xd7,
+ 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6, 0xc0,
+ 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0a, 0x06, 0x08, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44,
+ 0x02, 0x20, 0x63, 0x59, 0x02, 0x01, 0x89, 0xd7, 0x3e, 0x5b, 0xff, 0xd1,
+ 0x16, 0x4e, 0xe3, 0xe2, 0x0a, 0xe0, 0x4a, 0xd8, 0x75, 0xaf, 0x77, 0x5c,
+ 0x93, 0x60, 0xba, 0x10, 0x1f, 0x97, 0xdd, 0x27, 0x2d, 0x24, 0x02, 0x20,
+ 0x3d, 0x87, 0x0f, 0xac, 0x22, 0x4d, 0x16, 0xd9, 0xa1, 0x95, 0xbb, 0x56,
+ 0xe0, 0x21, 0x05, 0x93, 0xd1, 0x07, 0xb5, 0x25, 0x3b, 0xf4, 0x57, 0x20,
+ 0x87, 0x13, 0xa2, 0xf7, 0x78, 0x15, 0x30, 0xa7};
+
+// The P256 root certificate that issued the P256 intermediate certificate
+// above. The certificate was generated with pycert.py
+const uint8_t P256_ROOT[] = {
+ 0x30, 0x82, 0x01, 0x4a, 0x30, 0x81, 0xf1, 0xa0, 0x03, 0x02, 0x01, 0x02,
+ 0x02, 0x14, 0x5f, 0x3f, 0xae, 0x90, 0x49, 0x30, 0x2f, 0x33, 0x6e, 0x95,
+ 0x23, 0xa7, 0xcb, 0x23, 0xd7, 0x65, 0x4f, 0xea, 0x3c, 0xf7, 0x30, 0x0a,
+ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x14,
+ 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x22, 0x18, 0x0f,
+ 0x32, 0x30, 0x31, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x31, 0x31, 0x32, 0x33,
+ 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x14, 0x31, 0x12,
+ 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2d, 0x70, 0x32, 0x35, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4f, 0xbf, 0xbb,
+ 0xbb, 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, 0x0a, 0x59, 0xac, 0x87, 0x04,
+ 0xe2, 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, 0xf7, 0x2e, 0x92, 0x3f, 0x2c,
+ 0x4f, 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, 0xd2, 0x33, 0x45, 0x6c, 0x36,
+ 0xc4, 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, 0xed, 0xc8, 0xd1, 0x93, 0x90,
+ 0xd7, 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, 0x04, 0xea, 0xa0, 0x4a, 0xa6,
+ 0xc0, 0xa3, 0x1d, 0x30, 0x1b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0a, 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30,
+ 0x45, 0x02, 0x20, 0x5c, 0x75, 0x51, 0x9f, 0x13, 0x11, 0x50, 0xcd, 0x5d,
+ 0x8a, 0xde, 0x20, 0xa3, 0xbc, 0x06, 0x30, 0x91, 0xff, 0xb2, 0x73, 0x75,
+ 0x5f, 0x31, 0x64, 0xec, 0xfd, 0xcb, 0x42, 0x80, 0x0a, 0x70, 0xe6, 0x02,
+ 0x21, 0x00, 0xc2, 0xe4, 0xc1, 0xa8, 0xe2, 0x89, 0xdc, 0xa1, 0xbb, 0xe7,
+ 0xd5, 0x4f, 0x5c, 0x88, 0xad, 0xeb, 0xa4, 0x78, 0xa1, 0x19, 0xbe, 0x22,
+ 0x54, 0xc8, 0x9f, 0xef, 0xb8, 0x5d, 0xa2, 0x40, 0xd9, 0x8b};
+
+void check_hard_coded_certs(const uint8_t** cert_chain, size_t cert_chain_len,
+ const size_t* certs_len) {
+ // Very hacky and fragile check that the intermediate certs are correct.
+ switch (cert_chain_len) {
+ case 2: {
+ const uint8_t* cert = cert_chain[0];
+ size_t cert_len = certs_len[0];
+ ASSERT_EQ(cert_len, sizeof(P256_ROOT));
+ ASSERT_EQ(0, memcmp(cert, P256_ROOT, cert_len));
+ cert = cert_chain[1];
+ cert_len = certs_len[1];
+ ASSERT_EQ(cert_len, sizeof(P256_INT));
+ ASSERT_EQ(0, memcmp(cert, P256_INT, cert_len));
+ break;
+ }
+ case 4: {
+ const uint8_t* cert = cert_chain[0];
+ size_t cert_len = certs_len[0];
+ ASSERT_EQ(cert_len, sizeof(P256_ROOT));
+ ASSERT_EQ(0, memcmp(cert, P256_ROOT, cert_len));
+ cert = cert_chain[1];
+ cert_len = certs_len[1];
+ ASSERT_EQ(cert_len, sizeof(P256_INT));
+ ASSERT_EQ(0, memcmp(cert, P256_INT, cert_len));
+ cert = cert_chain[2];
+ cert_len = certs_len[2];
+ ASSERT_EQ(cert_len, sizeof(RSA_ROOT));
+ ASSERT_EQ(0, memcmp(cert, RSA_ROOT, cert_len));
+ cert = cert_chain[3];
+ cert_len = certs_len[3];
+ ASSERT_EQ(cert_len, sizeof(RSA_INT));
+ ASSERT_EQ(0, memcmp(cert, RSA_INT, cert_len));
+ break;
+ }
+ default:
+ // In this case something went wrong.
+ ASSERT_EQ(true, false);
+ }
+}
+
+/* Verification function called from cose-rust.
+ * Returns true if everything goes well and the signature is good, false in any
+ * other case. */
+bool verify_callback(const uint8_t* payload, size_t payload_len,
+ const uint8_t** cert_chain, size_t cert_chain_len,
+ const size_t* certs_len, const uint8_t* ee_cert,
+ size_t ee_cert_len, const uint8_t* signature,
+ size_t signature_len, uint8_t signature_algorithm,
+ void* ctx) {
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return false;
+ }
+
+ CK_MECHANISM_TYPE mechanism;
+ SECOidTag oid;
+ uint32_t hash_length;
+ SECItem param = {siBuffer, nullptr, 0};
+ CK_RSA_PKCS_PSS_PARAMS rsa_pss_params = {CKM_SHA256, CKG_MGF1_SHA256,
+ SHA256_LENGTH};
+ switch (signature_algorithm) {
+ case (ES256):
+ mechanism = CKM_ECDSA;
+ oid = SEC_OID_SHA256;
+ hash_length = SHA256_LENGTH;
+ break;
+ case (PS256):
+ mechanism = CKM_RSA_PKCS_PSS;
+ oid = SEC_OID_SHA256;
+ hash_length = SHA256_LENGTH;
+ param = {siBuffer, reinterpret_cast<unsigned char*>(&rsa_pss_params),
+ sizeof(rsa_pss_params)};
+ break;
+ default:
+ return false;
+ }
+ check_hard_coded_certs(cert_chain, cert_chain_len, certs_len);
+
+ uint8_t hash_buf[HASH_LENGTH_MAX];
+ SECStatus rv = PK11_HashBuf(oid, hash_buf, payload, payload_len);
+ if (rv != SECSuccess) {
+ return false;
+ }
+ SECItem hash_item = {siBuffer, hash_buf, hash_length};
+ CERTCertDBHandle* db_handle = CERT_GetDefaultCertDB();
+ if (!db_handle) {
+ return false;
+ }
+ SECItem der_cert = {siBuffer, const_cast<uint8_t*>(ee_cert),
+ static_cast<unsigned int>(ee_cert_len)};
+ UniqueCERTCertificate cert(
+ CERT_NewTempCertificate(db_handle, &der_cert, nullptr, false, true));
+ if (!cert) {
+ return false;
+ }
+ UniqueSECKEYPublicKey key(CERT_ExtractPublicKey(cert.get()));
+ if (!key) {
+ return false;
+ }
+ SECItem signature_item = {siBuffer, const_cast<uint8_t*>(signature),
+ static_cast<unsigned int>(signature_len)};
+ rv = PK11_VerifyWithMechanism(key.get(), mechanism, &param, &signature_item,
+ &hash_item, nullptr);
+ if (rv != SECSuccess) {
+ return false;
+ }
+
+ return true;
+}
+
+class psm_COSE : public ::testing::Test {};
+
+TEST_F(psm_COSE, CoseTestingSingleSignature) {
+ SECStatus rv = NSS_NoDB_Init(nullptr);
+ ASSERT_EQ(SECSuccess, rv);
+ bool result =
+ verify_cose_signature_ffi(PAYLOAD, sizeof(PAYLOAD), SIGNATURE,
+ sizeof(SIGNATURE), nullptr, verify_callback);
+ ASSERT_TRUE(result);
+}
+
+TEST_F(psm_COSE, CoseTestingTwoSignatures) {
+ SECStatus rv = NSS_NoDB_Init(nullptr);
+ ASSERT_EQ(SECSuccess, rv);
+ bool result = verify_cose_signature_ffi(
+ PAYLOAD, sizeof(PAYLOAD), SIGNATURE_ES256_PS256,
+ sizeof(SIGNATURE_ES256_PS256), nullptr, verify_callback);
+ ASSERT_TRUE(result);
+}
+
+TEST_F(psm_COSE, CoseTestingAlteredPayload) {
+ SECStatus rv = NSS_NoDB_Init(nullptr);
+ ASSERT_EQ(SECSuccess, rv);
+ uint8_t altered_payload[20] = {84, 104, 105, 115, 32, 104, 115,
+ 32, 116, 104, 101, 32, 99, 111,
+ 110, 116, 101, 110, 116, 46};
+ bool result = verify_cose_signature_ffi(
+ altered_payload, sizeof(altered_payload), SIGNATURE_ES256_PS256,
+ sizeof(SIGNATURE_ES256_PS256), nullptr, verify_callback);
+ ASSERT_FALSE(result);
+}
+
+} // namespace mozilla
diff --git a/security/manager/ssl/tests/gtest/DataStorageTest.cpp b/security/manager/ssl/tests/gtest/DataStorageTest.cpp
new file mode 100644
index 0000000000..791ef87f7d
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/DataStorageTest.cpp
@@ -0,0 +1,201 @@
+/* -*- 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 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/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/DataStorage.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+#include "nsStreamUtils.h"
+#include "prtime.h"
+
+using namespace mozilla;
+
+class psm_DataStorageTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ NS_ConvertUTF8toUTF16 testName(testInfo->name());
+ storage = DataStorage::GetFromRawFileName(testName);
+ storage->Init(nullptr);
+ }
+
+ RefPtr<DataStorage> storage;
+};
+
+constexpr auto testKey = "test"_ns;
+constexpr auto testValue = "value"_ns;
+constexpr auto privateTestValue = "private"_ns;
+
+TEST_F(psm_DataStorageTest, GetPutRemove) {
+ // Test Put/Get on Persistent data
+ EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
+ // Don't re-use testKey / testValue here, to make sure that this works as
+ // expected with objects that have the same semantic value but are not
+ // literally the same object.
+ nsCString result = storage->Get("test"_ns, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+
+ // Get on Temporary/Private data with the same key should give nothing
+ result = storage->Get(testKey, DataStorage_Temporary);
+ EXPECT_TRUE(result.IsEmpty());
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_TRUE(result.IsEmpty());
+
+ // Put with Temporary/Private data shouldn't affect Persistent data
+ constexpr auto temporaryTestValue = "temporary"_ns;
+ EXPECT_EQ(NS_OK,
+ storage->Put(testKey, temporaryTestValue, DataStorage_Temporary));
+ EXPECT_EQ(NS_OK,
+ storage->Put(testKey, privateTestValue, DataStorage_Private));
+ result = storage->Get(testKey, DataStorage_Temporary);
+ EXPECT_STREQ("temporary", result.get());
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_STREQ("private", result.get());
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+
+ // Put of a previously-present key overwrites it (if of the same type)
+ constexpr auto newValue = "new"_ns;
+ EXPECT_EQ(NS_OK, storage->Put(testKey, newValue, DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("new", result.get());
+
+ // Removal should work
+ storage->Remove(testKey, DataStorage_Temporary);
+ result = storage->Get(testKey, DataStorage_Temporary);
+ EXPECT_TRUE(result.IsEmpty());
+ // But removing one type shouldn't affect the others
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_STREQ("private", result.get());
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("new", result.get());
+ // Test removing the other types as well
+ storage->Remove(testKey, DataStorage_Private);
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_TRUE(result.IsEmpty());
+ storage->Remove(testKey, DataStorage_Persistent);
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+}
+
+TEST_F(psm_DataStorageTest, InputValidation) {
+ // Keys may not have tabs or newlines
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put("key\thas tab"_ns, testValue, DataStorage_Persistent));
+ nsCString result = storage->Get("key\thas tab"_ns, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+ EXPECT_EQ(NS_ERROR_INVALID_ARG, storage->Put("key has\nnewline"_ns, testValue,
+ DataStorage_Persistent));
+ result = storage->Get("keyhas\nnewline"_ns, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+ // Values may not have newlines
+ EXPECT_EQ(NS_ERROR_INVALID_ARG, storage->Put(testKey, "value\nhas newline"_ns,
+ DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ // Values may have tabs
+ EXPECT_TRUE(result.IsEmpty());
+ EXPECT_EQ(NS_OK, storage->Put(testKey, "val\thas tab; this is ok"_ns,
+ DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("val\thas tab; this is ok", result.get());
+
+ nsCString longKey("a");
+ for (int i = 0; i < 8; i++) {
+ longKey.Append(longKey);
+ }
+ // A key of length 256 will work
+ EXPECT_EQ(NS_OK, storage->Put(longKey, testValue, DataStorage_Persistent));
+ result = storage->Get(longKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+ longKey.AppendLiteral("a");
+ // A key longer than that will not work
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put(longKey, testValue, DataStorage_Persistent));
+ result = storage->Get(longKey, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+
+ nsCString longValue("a");
+ for (int i = 0; i < 10; i++) {
+ longValue.Append(longValue);
+ }
+ // A value of length 1024 will work
+ EXPECT_EQ(NS_OK, storage->Put(testKey, longValue, DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ(longValue.get(), result.get());
+ longValue.AppendLiteral("a");
+ // A value longer than that will not work
+ storage->Remove(testKey, DataStorage_Persistent);
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put(testKey, longValue, DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+}
+
+TEST_F(psm_DataStorageTest, Eviction) {
+ // Eviction is on a per-table basis. Tables shouldn't affect each other.
+ EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
+ for (int i = 0; i < 1025; i++) {
+ EXPECT_EQ(NS_OK,
+ storage->Put(nsPrintfCString("%d", i), nsPrintfCString("%d", i),
+ DataStorage_Temporary));
+ nsCString result =
+ storage->Get(nsPrintfCString("%d", i), DataStorage_Temporary);
+ EXPECT_STREQ(nsPrintfCString("%d", i).get(), result.get());
+ }
+ // We don't know which entry got evicted, but we can count them.
+ int entries = 0;
+ for (int i = 0; i < 1025; i++) {
+ nsCString result =
+ storage->Get(nsPrintfCString("%d", i), DataStorage_Temporary);
+ if (!result.IsEmpty()) {
+ entries++;
+ }
+ }
+ EXPECT_EQ(entries, 1024);
+ nsCString result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+}
+
+TEST_F(psm_DataStorageTest, ClearPrivateData) {
+ EXPECT_EQ(NS_OK,
+ storage->Put(testKey, privateTestValue, DataStorage_Private));
+ nsCString result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_STREQ("private", result.get());
+ storage->Observe(nullptr, "last-pb-context-exited", nullptr);
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_TRUE(result.IsEmpty());
+}
+
+TEST_F(psm_DataStorageTest, Shutdown) {
+ EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
+ nsCString result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+ // Get "now" (in days) close to when the data was last touched, so we won't
+ // get intermittent failures with the day not matching.
+ int64_t microsecondsPerDay = 24 * 60 * 60 * int64_t(PR_USEC_PER_SEC);
+ int32_t nowInDays = int32_t(PR_Now() / microsecondsPerDay);
+ // Simulate shutdown.
+ storage->Observe(nullptr, "profile-before-change", nullptr);
+ nsCOMPtr<nsIFile> backingFile;
+ EXPECT_EQ(NS_OK, NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(backingFile)));
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ NS_ConvertUTF8toUTF16 testName(testInfo->name());
+ EXPECT_EQ(NS_OK, backingFile->Append(testName));
+ nsCOMPtr<nsIInputStream> fileInputStream;
+ EXPECT_EQ(NS_OK, NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
+ backingFile));
+ nsCString data;
+ EXPECT_EQ(NS_OK, NS_ConsumeStream(fileInputStream, UINT32_MAX, data));
+ // The data will be of the form 'test\t0\t<days since the epoch>\tvalue'
+ EXPECT_STREQ(nsPrintfCString("test\t0\t%d\tvalue\n", nowInDays).get(),
+ data.get());
+}
diff --git a/security/manager/ssl/tests/gtest/DeserializeCertTest.cpp b/security/manager/ssl/tests/gtest/DeserializeCertTest.cpp
new file mode 100644
index 0000000000..005dc03ea6
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/DeserializeCertTest.cpp
@@ -0,0 +1,247 @@
+/* -*- 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 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/. */
+
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsISimpleEnumerator.h"
+#include "nsITransportSecurityInfo.h"
+#include "nsIX509Cert.h"
+#include "nsSerializationHelper.h"
+#include "nsString.h"
+
+// nsITransportSecurityInfo de-serializatin tests
+//
+// These tests verify that we can still deserialize old binary strings
+// generated for security info. This is necessary because service workers
+// stores these strings on disk.
+//
+// If you make a change and start breaking these tests, you will need to
+// add a compat fix for loading the old versions. For things that affect
+// the UUID, but do not break the rest of the format you can simply add
+// another hack condition in nsBinaryInputStream::ReadObject(). If you
+// change the overall format of the serialization then we will need more
+// complex handling in the security info concrete classes.
+//
+// We would like to move away from this binary compatibility requirement
+// in service workers. See bug 1248628.
+void deserializeAndVerify(const nsCString& serializedSecInfo,
+ bool hasFailedCertChain,
+ size_t failedCertChainLength = 0) {
+ nsCOMPtr<nsISupports> secInfo;
+ nsresult rv =
+ NS_DeserializeObject(serializedSecInfo, getter_AddRefs(secInfo));
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_TRUE(secInfo);
+
+ nsCOMPtr<nsITransportSecurityInfo> securityInfo = do_QueryInterface(secInfo);
+ ASSERT_TRUE(securityInfo);
+
+ nsCOMPtr<nsIX509Cert> cert;
+ rv = securityInfo->GetServerCert(getter_AddRefs(cert));
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_TRUE(cert);
+
+ nsTArray<RefPtr<nsIX509Cert>> failedCertArray;
+ rv = securityInfo->GetFailedCertChain(failedCertArray);
+ ASSERT_EQ(NS_OK, rv);
+
+ if (hasFailedCertChain) {
+ ASSERT_FALSE(failedCertArray.IsEmpty());
+ for (const auto& cert : failedCertArray) {
+ ASSERT_TRUE(cert);
+ }
+ ASSERT_EQ(failedCertChainLength, failedCertArray.Length());
+ } else {
+ ASSERT_TRUE(failedCertArray.IsEmpty());
+ }
+}
+
+TEST(psm_DeserializeCert, gecko33)
+{
+ // clang-format off
+ // Gecko 33+ vintage Security info serialized with UUIDs:
+ // - nsISupports 00000000-0000-0000-c000-000000000046
+ // - nsISSLStatus fa9ba95b-ca3b-498a-b889-7c79cf28fee8
+ // - nsIX509Cert f8ed8364-ced9-4c6e-86ba-48af53c393e6
+ nsCString base64Serialization(
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAQAAgAAAAAAAAAAAAAAAAAAAAA"
+ "B4vFIJp5wRkeyPxAQ9RJGKPqbqVvKO0mKuIl8ec8o/uhmCjImkVxP+7sgiYWmMt8F+O2DZM7ZTG6GukivU8OT5gAAAAIAAAWpMII"
+ "FpTCCBI2gAwIBAgIQD4svsaKEC+QtqtsU2TF8ITANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUN"
+ "lcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNzdXJhbmNlIFN"
+ "lcnZlciBDQTAeFw0xNTAyMjMwMDAwMDBaFw0xNjAzMDIxMjAwMDBaMGoxCzAJBgNVBAYTAlVTMRYwFAYDVQQHEw1TYW4gRnJhbmN"
+ "pc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRUwEwYDVQQKEwxGYXN0bHksIEluYy4xFzAVBgNVBAMTDnd3dy5naXRodWIuY29tMII"
+ "BIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+9WUCgrgUNwP/JC3cUefLAXeDpq8Ko/U8p8IRvny0Ri0I6Uq0t+RP/nF0LJ"
+ "Avda8QHYujdgeDTePepBX7+OiwBFhA0YO+rM3C2Z8IRaN/i9eLln+Yyc68+1z+E10s1EXdZrtDGvN6MHqygGsdfkXKfBLUJ1BZEh"
+ "s9sBnfcjq3kh5gZdBArdG9l5NpdmQhtceaFGsPiWuJxGxRzS4i95veUHWkhMpEYDEEBdcDGxqArvQCvzSlngdttQCfx8OUkBTb3B"
+ "A2okpTwwJfqPsxVetA6qR7UNc+fVb6KHwvm0bzi2rQ3xw3D/syRHwdMkpoVDQPCk43H9WufgfBKRen87dFwIDAQABo4ICPzCCAjs"
+ "wHwYDVR0jBBgwFoAUUWj/kK8CB3U8zNllZGKiErhZcjswHQYDVR0OBBYEFGS/RLNGCZvPWh1xSaIEcouINIQjMHsGA1UdEQR0MHK"
+ "CDnd3dy5naXRodWIuY29tggpnaXRodWIuY29tggwqLmdpdGh1Yi5jb22CCyouZ2l0aHViLmlvgglnaXRodWIuaW+CFyouZ2l0aHV"
+ "idXNlcmNvbnRlbnQuY29tghVnaXRodWJ1c2VyY29udGVudC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwM"
+ "BBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzMuY3J"
+ "sMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzMuY3JsMEIGA1UdIAQ7MDkwNwYJYIZIAYb"
+ "9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgYMGCCsGAQUFBwEBBHcwdTAkBggrBgEFBQc"
+ "wAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME0GCCsGAQUFBzAChkFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUN"
+ "lcnRTSEEySGlnaEFzc3VyYW5jZVNlcnZlckNBLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAc4dbVmuKvyI7"
+ "KZ4Txk+ZqcAYToJGKUIVaPL94e5SZGweUisjaCbplAOihnf6Mxt8n6vnuH2IsCaz2NRHqhdcosjT3CwAiJpJNkXPKWVL/txgdSTV"
+ "2cqB1GG4esFOalvI52dzn+J4fTIYZvNF+AtGyHSLm2XRXYZCw455laUKf6Sk9RDShDgUvzhOKL4GXfTwKXv12MyMknJybH8UCpjC"
+ "HZmFBVHMcUN/87HsQo20PdOekeEvkjrrMIxW+gxw22Yb67yF/qKgwrWr+43bLN709iyw+LWiU7sQcHL2xk9SYiWQDj2tYz2soObV"
+ "QYTJm0VUZMEVFhtALq46cx92Zu4vFwC8AAwAAAAABAQAA");
+ // clang-format on
+
+ deserializeAndVerify(base64Serialization, false);
+}
+
+TEST(psm_DeserializeCert, gecko46)
+{
+ // clang-format off
+ // Gecko 46+ vintage Security info serialized with UUIDs:
+ // - nsISupports 00000000-0000-0000-c000-000000000046
+ // - nsISSLStatus fa9ba95b-ca3b-498a-b889-7c79cf28fee8
+ // - nsIX509Cert bdc3979a-5422-4cd5-8589-696b6e96ea83
+ nsCString base64Serialization(
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAQAAgAAAAAAAAAAAAAAAAAAAAA"
+ "B4vFIJp5wRkeyPxAQ9RJGKPqbqVvKO0mKuIl8ec8o/uhmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAAIAAAWzMII"
+ "FrzCCBJegAwIBAgIQB3pdwzYjAfmJ/lT3+G8+ZDANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUN"
+ "lcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNzdXJhbmNlIFN"
+ "lcnZlciBDQTAeFw0xNjAxMjAwMDAwMDBaFw0xNzA0MDYxMjAwMDBaMGoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybml"
+ "hMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxGYXN0bHksIEluYy4xFzAVBgNVBAMTDnd3dy5naXRodWIuY29tMII"
+ "BIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+9WUCgrgUNwP/JC3cUefLAXeDpq8Ko/U8p8IRvny0Ri0I6Uq0t+RP/nF0LJ"
+ "Avda8QHYujdgeDTePepBX7+OiwBFhA0YO+rM3C2Z8IRaN/i9eLln+Yyc68+1z+E10s1EXdZrtDGvN6MHqygGsdfkXKfBLUJ1BZEh"
+ "s9sBnfcjq3kh5gZdBArdG9l5NpdmQhtceaFGsPiWuJxGxRzS4i95veUHWkhMpEYDEEBdcDGxqArvQCvzSlngdttQCfx8OUkBTb3B"
+ "A2okpTwwJfqPsxVetA6qR7UNc+fVb6KHwvm0bzi2rQ3xw3D/syRHwdMkpoVDQPCk43H9WufgfBKRen87dFwIDAQABo4ICSTCCAkU"
+ "wHwYDVR0jBBgwFoAUUWj/kK8CB3U8zNllZGKiErhZcjswHQYDVR0OBBYEFGS/RLNGCZvPWh1xSaIEcouINIQjMHsGA1UdEQR0MHK"
+ "CDnd3dy5naXRodWIuY29tggwqLmdpdGh1Yi5jb22CCmdpdGh1Yi5jb22CCyouZ2l0aHViLmlvgglnaXRodWIuaW+CFyouZ2l0aHV"
+ "idXNlcmNvbnRlbnQuY29tghVnaXRodWJ1c2VyY29udGVudC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwM"
+ "BBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzUuY3J"
+ "sMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzUuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb"
+ "9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQICMIGDBggrBgEFBQcBAQR3MHU"
+ "wJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBNBggrBgEFBQcwAoZBaHR0cDovL2NhY2VydHMuZGlnaWNlcnQ"
+ "uY29tL0RpZ2lDZXJ0U0hBMkhpZ2hBc3N1cmFuY2VTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQE"
+ "ATxbRdPg+o49+96/P+rbdp4ie+CGtfCgUubT/Z9C54k+BfQO0nbxVgCSM5WZQuLgo2Q+0lcxisod8zxZeU0j5wviQINwOln/iN89"
+ "Bx3VmDRynTe4CqhsAwOoO1ERmCAmsAJBwY/rNr4mK22p8erBrqMW0nYXYU5NFynI+pNTjojhKD4II8PNV8G2yMWwYOb/u4+WPzUA"
+ "HC9DpZdrWTEH/W69Cr/KxRqGsWPwpgMv2Wqav8jaT35JxqTXjOlhQqzo6fNn3eYOeCf4PkCxZKwckWjy10qDaRbjhwAMHAGj2TPr"
+ "idlvOj/7QyyX5m8up/1US8z1fRW4yoCSOt6V2bwuH6cAvAAMAAAAAAQEAAA==");
+ // clang-format on
+
+ deserializeAndVerify(base64Serialization, false);
+}
+
+TEST(psm_DeserializeCert, preSSLStatusConsolidation)
+{
+ // clang-format off
+ // Generated using serialized output of test "good.include-subdomains.pinning.example.com"
+ // in security/manager/ssl/tests/unit/test_cert_chains.js
+ nsCString base64Serialization(
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAgAAgAAAAAAAAAAAAAAAAAAAAAB4vFIJp5w"
+ "RkeyPxAQ9RJGKPqbqVvKO0mKuIl8ec8o/uhmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAAAAAAONMIIDiTCCAnGg"
+ "AwIBAgIUWbWLTwLBvfwcoiU7I8lDz9snfUgwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE2MTEyNzAw"
+ "MDAwMFoYDzIwMTkwMjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw"
+ "ggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzV"
+ "JJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+o"
+ "N9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd"
+ "q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcowgccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0"
+ "gg0qLmV4YW1wbGUuY29tghUqLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBs"
+ "ZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAB"
+ "hhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQBE+6IPJK5OeonoQPC4CCWMd69SjhwS7X6TNgxDJzW7"
+ "qpVm4SFyYZ2xqzr2zib5LsYek6/jok5LPSpJVeFuSeiesvGMxk0O4ZEihPxSM4uR4xpCnPzz7LoFIzMELJv5i+cgLw4+6cINPkLj"
+ "oCUdb+AXSTur7THJaO75B44I2JjJfMfzgW1FwoWgXL/PQWRw+VY6OY1glqZOXzP+vfSja1SoggpiCzdPx7h1/SEEZov7zhCZXv1C"
+ "enx1njlpcj9wWEJMsyZczMNtiz5GkRrLaqCz9F8ah3NvkvPAZ0oOqtxuQgMXK/c0OXJVKi0SCJsWqZDoZhCrS/dE9guxlseZqhSI"
+ "wC8DAwAAAAABAQAAAAAAAAZ4MjU1MTkAAAAOUlNBLVBTUy1TSEEyNTYBlZ+xZWUXSH+rm9iRO+Uxl650zaXNL0c/lvXwt//2LGgA"
+ "AAACZgoyJpFcT/u7IImFpjLfBb3Dl5pUIkzVhYlpa26W6oMAAAAAAAADjTCCA4kwggJxoAMCAQICFFm1i08Cwb38HKIlOyPJQ8/b"
+ "J31IMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwIhgPMjAxNjExMjcwMDAwMDBaGA8yMDE5MDIwNTAwMDAwMFow"
+ "GjEYMBYGA1UEAwwPVGVzdCBFbmQtZW50aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2"
+ "ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC"
+ "a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk"
+ "zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+"
+ "SSP6clHEMdUDrNoYCjXtjQIDAQABo4HKMIHHMIGQBgNVHREEgYgwgYWCCWxvY2FsaG9zdIINKi5leGFtcGxlLmNvbYIVKi5waW5u"
+ "aW5nLmV4YW1wbGUuY29tgigqLmluY2x1ZGUtc3ViZG9tYWlucy5waW5uaW5nLmV4YW1wbGUuY29tgigqLmV4Y2x1ZGUtc3ViZG9t"
+ "YWlucy5waW5uaW5nLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4"
+ "LzANBgkqhkiG9w0BAQsFAAOCAQEARPuiDySuTnqJ6EDwuAgljHevUo4cEu1+kzYMQyc1u6qVZuEhcmGdsas69s4m+S7GHpOv46JO"
+ "Sz0qSVXhbknonrLxjMZNDuGRIoT8UjOLkeMaQpz88+y6BSMzBCyb+YvnIC8OPunCDT5C46AlHW/gF0k7q+0xyWju+QeOCNiYyXzH"
+ "84FtRcKFoFy/z0FkcPlWOjmNYJamTl8z/r30o2tUqIIKYgs3T8e4df0hBGaL+84QmV79Qnp8dZ45aXI/cFhCTLMmXMzDbYs+RpEa"
+ "y2qgs/RfGodzb5LzwGdKDqrcbkIDFyv3NDlyVSotEgibFqmQ6GYQq0v3RPYLsZbHmaoUiGYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM"
+ "1YWJaWtuluqDAAAAAAAAAtcwggLTMIIBu6ADAgECAhRdBTvvC7swO3cbVWIGn/56DrQ+cjANBgkqhkiG9w0BAQsFADASMRAwDgYD"
+ "VQQDDAdUZXN0IENBMCIYDzIwMTYxMTI3MDAwMDAwWhgPMjAxOTAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0G"
+ "CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr"
+ "4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk"
+ "fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo"
+ "4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQF"
+ "MAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCDjewR53YLc3HzZKugRDbQVxjJNILW6fSIyW9dSglYcWh6aiOK"
+ "9cZFVtzRWYEYkIlicAyTiPw34bXzxU1cK6sCSmBR+UTXbRPGb4OOy3MRaoF1m3jxwnPkQwxezDiqJTydCbYcBu0sKwURAZOd5QK9"
+ "22MsOsnrLjNlpRDmuH0VFhb5uN2I5mM3NvMnP2Or19O1Bk//iGD6AyJfiZFcii+FsDrJhbzw6lakEV7O/EnD0kk2l7I0VMtg1xZB"
+ "bEw7P6+V9zz5cAzaaq7EB0mCE+jJckSzSETBN+7lyVD8gwmHYxxZfPnUM/yvPbMU9L3xWD/z6HHwO6r+9m7BT+2pHjBCAAA=");
+ // clang-format on
+
+ deserializeAndVerify(base64Serialization, false);
+}
+
+TEST(psm_DeserializeCert, preSSLStatusConsolidationFailedCertChain)
+{
+ // clang-format off
+ // Generated using serialized output of test "expired.example.com"
+ // in security/manager/ssl/tests/unit/test_cert_chains.js
+ nsCString base64Serialization(
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAAABAAAAAAAAAAA///gCwAAAAAB4vFIJp5w"
+ "RkeyPxAQ9RJGKPqbqVvKO0mKuIl8ec8o/uhmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAAAAAAMgMIIDHDCCAgSg"
+ "AwIBAgIUY9ERAIKj0js/YbhJoMrcLnj++uowDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDEzMDEwMTAw"
+ "MDAwMFoYDzIwMTQwMTAxMDAwMDAwWjAiMSAwHgYDVQQDDBdFeHBpcmVkIFRlc3QgRW5kLWVudGl0eTCCASIwDQYJKoZIhvcNAQEB"
+ "BQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6"
+ "pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9"
+ "0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK"
+ "lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNWMFQwHgYDVR0RBBcwFYITZXhwaXJl"
+ "ZC5leGFtcGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZIhvcN"
+ "AQELBQADggEBAImiFuy275T6b+Ud6gl/El6qpgWHUXeYiv2sp7d+HVzfT+ow5WVsxI/GMKhdA43JaKT9gfMsbnP1qiI2zel3U+F7"
+ "IAMO1CEr5FVdCOVTma5hmu/81rkJLmZ8RQDWWOhZKyn/7aD7TH1C1e768yCt5E2DDl8mHil9zR8BPsoXwuS3L9zJ2JqNc60+hB8l"
+ "297ZaSl0nbKffb47ukvn5kSJ7tI9n/fSXdj1JrukwjZP+74VkQyNobaFzDZ+Zr3QmfbejEsY2EYnq8XuENgIO4DuYrm80/p6bMO6"
+ "laB0Uv5W6uXZgBZdRTe1WMdYWGhmvnFFQmf+naeOOl6ryFwWwtnoK7IAAAMAAAEAAAEAAQAAAAAAAAAAAAAAAZWfsWVlF0h/q5vY"
+ "kTvlMZeudM2lzS9HP5b18Lf/9ixoAAAAAmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAAAAAAAyAwggMcMIICBKAD"
+ "AgECAhRj0REAgqPSOz9huEmgytwueP766jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0IENBMCIYDzIwMTMwMTAxMDAw"
+ "MDAwWhgPMjAxNDAxMDEwMDAwMDBaMCIxIDAeBgNVBAMMF0V4cGlyZWQgVGVzdCBFbmQtZW50aXR5MIIBIjANBgkqhkiG9w0BAQEF"
+ "AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHql"
+ "WqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S"
+ "O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqV"
+ "YR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo1YwVDAeBgNVHREEFzAVghNleHBpcmVk"
+ "LmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0B"
+ "AQsFAAOCAQEAiaIW7LbvlPpv5R3qCX8SXqqmBYdRd5iK/aynt34dXN9P6jDlZWzEj8YwqF0DjclopP2B8yxuc/WqIjbN6XdT4Xsg"
+ "Aw7UISvkVV0I5VOZrmGa7/zWuQkuZnxFANZY6FkrKf/toPtMfULV7vrzIK3kTYMOXyYeKX3NHwE+yhfC5Lcv3MnYmo1zrT6EHyXb"
+ "3tlpKXSdsp99vju6S+fmRInu0j2f99Jd2PUmu6TCNk/7vhWRDI2htoXMNn5mvdCZ9t6MSxjYRierxe4Q2Ag7gO5iubzT+npsw7qV"
+ "oHRS/lbq5dmAFl1FN7VYx1hYaGa+cUVCZ/6dp446XqvIXBbC2egrsmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAA"
+ "AAAAAtcwggLTMIIBu6ADAgECAhRdBTvvC7swO3cbVWIGn/56DrQ+cjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0IENB"
+ "MCIYDzIwMTYxMTI3MDAwMDAwWhgPMjAxOTAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUA"
+ "A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa"
+ "p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7"
+ "xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVh"
+ "He4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0P"
+ "BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCDjewR53YLc3HzZKugRDbQVxjJNILW6fSIyW9dSglYcWh6aiOK9cZFVtzRWYEYkIli"
+ "cAyTiPw34bXzxU1cK6sCSmBR+UTXbRPGb4OOy3MRaoF1m3jxwnPkQwxezDiqJTydCbYcBu0sKwURAZOd5QK922MsOsnrLjNlpRDm"
+ "uH0VFhb5uN2I5mM3NvMnP2Or19O1Bk//iGD6AyJfiZFcii+FsDrJhbzw6lakEV7O/EnD0kk2l7I0VMtg1xZBbEw7P6+V9zz5cAza"
+ "aq7EB0mCE+jJckSzSETBN+7lyVD8gwmHYxxZfPnUM/yvPbMU9L3xWD/z6HHwO6r+9m7BT+2pHjBCAZWfsWVlF0h/q5vYkTvlMZeu"
+ "dM2lzS9HP5b18Lf/9ixoAAAAAmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAAAAAAAyAwggMcMIICBKADAgECAhRj"
+ "0REAgqPSOz9huEmgytwueP766jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0IENBMCIYDzIwMTMwMTAxMDAwMDAwWhgP"
+ "MjAxNDAxMDEwMDAwMDBaMCIxIDAeBgNVBAMMF0V4cGlyZWQgVGVzdCBFbmQtZW50aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A"
+ "MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc"
+ "1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf"
+ "qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYl"
+ "nauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo1YwVDAeBgNVHREEFzAVghNleHBpcmVkLmV4YW1w"
+ "bGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOC"
+ "AQEAiaIW7LbvlPpv5R3qCX8SXqqmBYdRd5iK/aynt34dXN9P6jDlZWzEj8YwqF0DjclopP2B8yxuc/WqIjbN6XdT4XsgAw7UISvk"
+ "VV0I5VOZrmGa7/zWuQkuZnxFANZY6FkrKf/toPtMfULV7vrzIK3kTYMOXyYeKX3NHwE+yhfC5Lcv3MnYmo1zrT6EHyXb3tlpKXSd"
+ "sp99vju6S+fmRInu0j2f99Jd2PUmu6TCNk/7vhWRDI2htoXMNn5mvdCZ9t6MSxjYRierxe4Q2Ag7gO5iubzT+npsw7qVoHRS/lbq"
+ "5dmAFl1FN7VYx1hYaGa+cUVCZ/6dp446XqvIXBbC2egrsmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAAAAAAAtcw"
+ "ggLTMIIBu6ADAgECAhRdBTvvC7swO3cbVWIGn/56DrQ+cjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0IENBMCIYDzIw"
+ "MTYxMTI3MDAwMDAwWhgPMjAxOTAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw"
+ "ggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzV"
+ "JJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+o"
+ "N9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd"
+ "q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEG"
+ "MA0GCSqGSIb3DQEBCwUAA4IBAQCDjewR53YLc3HzZKugRDbQVxjJNILW6fSIyW9dSglYcWh6aiOK9cZFVtzRWYEYkIlicAyTiPw3"
+ "4bXzxU1cK6sCSmBR+UTXbRPGb4OOy3MRaoF1m3jxwnPkQwxezDiqJTydCbYcBu0sKwURAZOd5QK922MsOsnrLjNlpRDmuH0VFhb5"
+ "uN2I5mM3NvMnP2Or19O1Bk//iGD6AyJfiZFcii+FsDrJhbzw6lakEV7O/EnD0kk2l7I0VMtg1xZBbEw7P6+V9zz5cAzaaq7EB0mC"
+ "E+jJckSzSETBN+7lyVD8gwmHYxxZfPnUM/yvPbMU9L3xWD/z6HHwO6r+9m7BT+2pHjBC");
+ // clang-format on
+
+ deserializeAndVerify(base64Serialization, true, 2);
+}
diff --git a/security/manager/ssl/tests/gtest/MD4Test.cpp b/security/manager/ssl/tests/gtest/MD4Test.cpp
new file mode 100644
index 0000000000..6e28e6dbc4
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/MD4Test.cpp
@@ -0,0 +1,62 @@
+/* -*- 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 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/. */
+
+// This file tests the md4.c implementation.
+
+#include "gtest/gtest.h"
+#include "md4.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Casting.h"
+
+struct RFC1320TestParams {
+ const char* data;
+ const uint8_t expectedHash[16];
+};
+
+static const RFC1320TestParams RFC1320_TEST_PARAMS[] = {
+ {"",
+ {0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31, 0xb7, 0x3c, 0x59, 0xd7,
+ 0xe0, 0xc0, 0x89, 0xc0}},
+ {"a",
+ {0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3, 0x3e, 0x46, 0x24, 0x5e, 0x05, 0xfb,
+ 0xdb, 0xd6, 0xfb, 0x24}},
+ {"abc",
+ {0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52, 0x5f, 0xc1, 0x0a, 0xe8,
+ 0x7a, 0xa6, 0x72, 0x9d}},
+ {"message digest",
+ {0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54, 0x9f, 0xe8, 0x18, 0x87, 0x48, 0x06,
+ 0xe1, 0xc7, 0x01, 0x4b}},
+ {
+ "abcdefghijklmnopqrstuvwxyz",
+ {0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5, 0xbb, 0xcd, 0xee, 0xa8, 0xed, 0x63,
+ 0xdf, 0x41, 0x2d, 0xa9},
+ },
+ {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ {0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41, 0xdb, 0x35, 0x1c, 0xe6, 0x27, 0xe1,
+ 0x53, 0xe7, 0xf0, 0xe4},
+ },
+ {
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890",
+ {0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38, 0xf2, 0x19, 0x9c, 0x3e, 0x7b, 0x16,
+ 0x4f, 0xcc, 0x05, 0x36},
+ }};
+
+class psm_MD4 : public ::testing::Test,
+ public ::testing::WithParamInterface<RFC1320TestParams> {};
+
+TEST_P(psm_MD4, RFC1320TestValues) {
+ const RFC1320TestParams& params(GetParam());
+ uint8_t actualHash[16];
+ md4sum(mozilla::BitwiseCast<const uint8_t*, const char*>(params.data),
+ strlen(params.data), actualHash);
+ EXPECT_TRUE(mozilla::ArrayEqual(actualHash, params.expectedHash))
+ << "MD4 hashes aren't equal for input: '" << params.data << "'";
+}
+
+INSTANTIATE_TEST_CASE_P(psm_MD4, psm_MD4,
+ testing::ValuesIn(RFC1320_TEST_PARAMS));
diff --git a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
new file mode 100644
index 0000000000..77e9368ff6
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
@@ -0,0 +1,321 @@
+/* -*- 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 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/. */
+
+#include "CertVerifier.h"
+#include "OCSPCache.h"
+#include "gtest/gtest.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Casting.h"
+#include "mozilla/Sprintf.h"
+#include "nss.h"
+#include "mozpkix/pkixtypes.h"
+#include "mozpkix/test/pkixtestutil.h"
+#include "prerr.h"
+#include "secerr.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+using mozilla::OriginAttributes;
+
+template <size_t N>
+inline Input LiteralInput(const char (&valueString)[N]) {
+ // Ideally we would use mozilla::BitwiseCast() here rather than
+ // reinterpret_cast for better type checking, but the |N - 1| part trips
+ // static asserts.
+ return Input(reinterpret_cast<const uint8_t(&)[N - 1]>(valueString));
+}
+
+const int MaxCacheEntries = 1024;
+
+class psm_OCSPCacheTest : public ::testing::Test {
+ protected:
+ psm_OCSPCacheTest() : now(Now()) {}
+
+ static void SetUpTestCase() { NSS_NoDB_Init(nullptr); }
+
+ const Time now;
+ mozilla::psm::OCSPCache cache;
+};
+
+static void PutAndGet(
+ mozilla::psm::OCSPCache& cache, const CertID& certID, Result result,
+ Time time, const OriginAttributes& originAttributes = OriginAttributes()) {
+ // The first time is thisUpdate. The second is validUntil.
+ // The caller is expecting the validUntil returned with Get
+ // to be equal to the passed-in time. Since these values will
+ // be different in practice, make thisUpdate less than validUntil.
+ Time thisUpdate(time);
+ ASSERT_EQ(Success, thisUpdate.SubtractSeconds(10));
+ Result rv = cache.Put(certID, originAttributes, result, thisUpdate, time);
+ ASSERT_TRUE(rv == Success);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, originAttributes, resultOut, timeOut));
+ ASSERT_EQ(result, resultOut);
+ ASSERT_EQ(time, timeOut);
+}
+
+Input fakeIssuer1(LiteralInput("CN=issuer1"));
+Input fakeKey000(LiteralInput("key000"));
+Input fakeKey001(LiteralInput("key001"));
+Input fakeSerial0000(LiteralInput("0000"));
+
+TEST_F(psm_OCSPCacheTest, TestPutAndGet) {
+ Input fakeSerial000(LiteralInput("000"));
+ Input fakeSerial001(LiteralInput("001"));
+
+ SCOPED_TRACE("");
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001), Success,
+ now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestVariousGets) {
+ SCOPED_TRACE("");
+ for (int i = 0; i < MaxCacheEntries; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), Success,
+ timeIn);
+ }
+
+ Time timeIn(now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+
+ // This will be at the end of the list in the cache
+ CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000);
+ ASSERT_TRUE(cache.Get(cert0000, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+ // Once we access it, it goes to the front
+ ASSERT_TRUE(cache.Get(cert0000, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+
+ // This will be in the middle
+ Time timeInPlus512(now);
+ ASSERT_EQ(Success, timeInPlus512.AddSeconds(512));
+
+ static const Input fakeSerial0512(LiteralInput("0512"));
+ CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512);
+ ASSERT_TRUE(cache.Get(cert0512, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeInPlus512, timeOut);
+ ASSERT_TRUE(cache.Get(cert0512, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeInPlus512, timeOut);
+
+ // We've never seen this certificate
+ static const Input fakeSerial1111(LiteralInput("1111"));
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestEviction) {
+ SCOPED_TRACE("");
+ // By putting more distinct entries in the cache than it can hold,
+ // we cause the least recently used entry to be evicted.
+ for (int i = 0; i < MaxCacheEntries + 1; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), Success,
+ timeIn);
+ }
+
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestNoEvictionForRevokedResponses) {
+ SCOPED_TRACE("");
+ CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000);
+ Time timeIn(now);
+ PutAndGet(cache, notEvicted, Result::ERROR_REVOKED_CERTIFICATE, timeIn);
+ // By putting more distinct entries in the cache than it can hold,
+ // we cause the least recently used entry that isn't revoked to be evicted.
+ for (int i = 1; i < MaxCacheEntries + 1; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial), Success,
+ timeIn);
+ }
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(notEvicted, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+
+ Input fakeSerial0001(LiteralInput("0001"));
+ CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001);
+ ASSERT_FALSE(cache.Get(evicted, OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestEverythingIsRevoked) {
+ SCOPED_TRACE("");
+ Time timeIn(now);
+ // Fill up the cache with revoked responses.
+ for (int i = 0; i < MaxCacheEntries; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf),
+ sizeof(serialBuf), "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+ Result::ERROR_REVOKED_CERTIFICATE, timeIn);
+ }
+ static const Input fakeSerial1025(LiteralInput("1025"));
+ CertID good(fakeIssuer1, fakeKey000, fakeSerial1025);
+ // This will "succeed", allowing verification to continue. However,
+ // nothing was actually put in the cache.
+ Time timeInPlus1025(timeIn);
+ ASSERT_EQ(Success, timeInPlus1025.AddSeconds(1025));
+ Time timeInPlus1025Minus50(timeInPlus1025);
+ ASSERT_EQ(Success, timeInPlus1025Minus50.SubtractSeconds(50));
+ Result result = cache.Put(good, OriginAttributes(), Success,
+ timeInPlus1025Minus50, timeInPlus1025);
+ ASSERT_EQ(Success, result);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(good, OriginAttributes(), resultOut, timeOut));
+
+ static const Input fakeSerial1026(LiteralInput("1026"));
+ CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026);
+ // This will fail, causing verification to fail.
+ Time timeInPlus1026(timeIn);
+ ASSERT_EQ(Success, timeInPlus1026.AddSeconds(1026));
+ Time timeInPlus1026Minus50(timeInPlus1026);
+ ASSERT_EQ(Success, timeInPlus1026Minus50.SubtractSeconds(50));
+ result =
+ cache.Put(revoked, OriginAttributes(), Result::ERROR_REVOKED_CERTIFICATE,
+ timeInPlus1026Minus50, timeInPlus1026);
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, result);
+}
+
+TEST_F(psm_OCSPCacheTest, VariousIssuers) {
+ SCOPED_TRACE("");
+ Time timeIn(now);
+ static const Input fakeIssuer2(LiteralInput("CN=issuer2"));
+ static const Input fakeSerial001(LiteralInput("001"));
+ CertID subject(fakeIssuer1, fakeKey000, fakeSerial001);
+ PutAndGet(cache, subject, Success, now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(subject, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+ // Test that we don't match a different issuer DN
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001),
+ OriginAttributes(), resultOut, timeOut));
+ // Test that we don't match a different issuer key
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001),
+ OriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, Times) {
+ SCOPED_TRACE("");
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+ PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(100));
+ PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
+ // This should not override the more recent entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(100), TimeFromElapsedSecondsAD(100)));
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ // Here we see the more recent time.
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
+
+ // Result::ERROR_REVOKED_CERTIFICATE overrides everything
+ PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+ TimeFromElapsedSecondsAD(50));
+}
+
+TEST_F(psm_OCSPCacheTest, NetworkFailure) {
+ SCOPED_TRACE("");
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+ PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(100));
+ PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
+ // This should not override the already present entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(300), TimeFromElapsedSecondsAD(350)));
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
+
+ PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(400));
+ // This should not override the already present entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(500), TimeFromElapsedSecondsAD(550)));
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(400), timeOut);
+
+ PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+ TimeFromElapsedSecondsAD(600));
+ // This should not override the already present entry.
+ ASSERT_EQ(
+ Success,
+ cache.Put(certID, OriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(700), TimeFromElapsedSecondsAD(750)));
+ ASSERT_TRUE(cache.Get(certID, OriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(600), timeOut);
+}
+
+TEST_F(psm_OCSPCacheTest, TestOriginAttributes) {
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+
+ SCOPED_TRACE("");
+ OriginAttributes attrs;
+ attrs.mFirstPartyDomain.AssignLiteral("foo.com");
+ PutAndGet(cache, certID, Success, now, attrs);
+
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ attrs.mFirstPartyDomain.AssignLiteral("bar.com");
+ ASSERT_FALSE(cache.Get(certID, attrs, resultOut, timeOut));
+
+ // OCSP cache should not be isolated by containers.
+ attrs.mUserContextId = 1;
+ attrs.mFirstPartyDomain.AssignLiteral("foo.com");
+ ASSERT_TRUE(cache.Get(certID, attrs, resultOut, timeOut));
+}
diff --git a/security/manager/ssl/tests/gtest/README.txt b/security/manager/ssl/tests/gtest/README.txt
new file mode 100644
index 0000000000..0e51322690
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/README.txt
@@ -0,0 +1,2 @@
+Please name all test cases in this directory with the prefix "psm". This makes
+it easier to run all PSM related GTests at once.
diff --git a/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
new file mode 100644
index 0000000000..0c9d3ef60d
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
@@ -0,0 +1,383 @@
+/* -*- 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 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/. */
+
+#include "nsNSSIOLayer.h"
+#include "sslproto.h"
+#include "sslerr.h"
+
+#include "gtest/gtest.h"
+
+constexpr auto HOST = "example.org"_ns;
+const int16_t PORT = 443;
+
+class psm_TLSIntoleranceTest : public ::testing::Test {
+ protected:
+ nsSSLIOLayerHelpers helpers;
+};
+
+TEST_F(psm_TLSIntoleranceTest, FullFallbackProcess) {
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, helpers.mVersionFallbackLimit);
+
+ // No adjustment made when there is no entry for the site.
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ }
+
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT, range.min,
+ range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT, range.min,
+ range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.max);
+
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT, range.min,
+ range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ // When rememberIntolerantAtVersion returns false, it also resets the
+ // intolerance information for the server.
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, DisableFallbackWithHighLimit) {
+ // this value disables version fallback entirely: with this value, all efforts
+ // to mark an origin as version intolerant fail
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2;
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_2, 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_1, 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_0, 0));
+}
+
+TEST_F(psm_TLSIntoleranceTest, FallbackLimitBelowMin) {
+ // check that we still respect the minimum version,
+ // when it is higher than the fallback limit
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_2, 0));
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ }
+
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_1, 0));
+}
+
+TEST_F(psm_TLSIntoleranceTest, TolerantOverridesIntolerant1) {
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_1, 0));
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+}
+
+TEST_F(psm_TLSIntoleranceTest, TolerantOverridesIntolerant2) {
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_1, 0));
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_2);
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntolerantDoesNotOverrideTolerant) {
+ // No adjustment made when there is no entry for the site.
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+ // false because we reached the floor set by rememberTolerantAtVersion.
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_1, 0));
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+}
+
+TEST_F(psm_TLSIntoleranceTest, PortIsRelevant) {
+ helpers.rememberTolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_2);
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(
+ HOST, 1, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_2, 0));
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(
+ HOST, 2, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_2, 0));
+
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, 1, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ }
+
+ {
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, 2, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntoleranceReasonInitial) {
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberTolerantAtVersion(HOST, 2, SSL_LIBRARY_VERSION_TLS_1_2);
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 2));
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntoleranceReasonStored) {
+ helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_ERROR_BAD_SERVER);
+ ASSERT_EQ(SSL_ERROR_BAD_SERVER, helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_ERROR_BAD_MAC_READ);
+ ASSERT_EQ(SSL_ERROR_BAD_MAC_READ, helpers.getIntoleranceReason(HOST, 1));
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntoleranceReasonCleared) {
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+ ASSERT_EQ(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT,
+ helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberTolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_2);
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+}
+
+TEST_F(psm_TLSIntoleranceTest, TLSForgetIntolerance) {
+ {
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ }
+
+ {
+ helpers.forgetIntolerance(HOST, PORT);
+
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, TLSDontForgetTolerance) {
+ {
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ }
+
+ {
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(
+ HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0, SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ }
+
+ {
+ helpers.forgetIntolerance(HOST, PORT);
+
+ SSLVersionRange range = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2};
+ helpers.adjustForTLSIntolerance(HOST, PORT, range);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, TLSPerSiteFallbackLimit) {
+ constexpr auto example_com = "example.com"_ns;
+ constexpr auto example_net = "example.net"_ns;
+ constexpr auto example_org = "example.org"_ns;
+
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_0;
+
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2;
+
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites(example_com);
+
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites("example.com,example.net"_ns);
+
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites(example_net);
+
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites(""_ns);
+
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(
+ helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+}
diff --git a/security/manager/ssl/tests/gtest/moz.build b/security/manager/ssl/tests/gtest/moz.build
new file mode 100644
index 0000000000..caa4b839ab
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/moz.build
@@ -0,0 +1,29 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ "CertDBTest.cpp",
+ "CertListTest.cpp",
+ "CoseTest.cpp",
+ "DataStorageTest.cpp",
+ "DeserializeCertTest.cpp",
+ "MD4Test.cpp",
+ "OCSPCacheTest.cpp",
+ "TLSIntoleranceTest.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/security/certverifier",
+ "/security/manager/ssl",
+ "/third_party/rust/cose-c/include",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]
diff --git a/security/manager/ssl/tests/mochitest/browser/browser.ini b/security/manager/ssl/tests/mochitest/browser/browser.ini
new file mode 100644
index 0000000000..4a590a74e4
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser.ini
@@ -0,0 +1,25 @@
+[DEFAULT]
+tags = psm
+support-files =
+ head.js
+ *.pem
+
+[browser_bug627234_perwindowpb.js]
+[browser_certificateManager.js]
+[browser_certViewer.js]
+skip-if = verify
+[browser_clientAuth_connection.js]
+# Any test that has to delete certificates (e.g. as part of cleanup) is
+# fundamentally incompatible with verify due to how NSS handles deleting
+# certificates.
+skip-if = verify
+[browser_clientAuth_ui.js]
+[browser_clientAuthRememberService.js]
+[browser_deleteCert_ui.js]
+[browser_downloadCert_ui.js]
+[browser_editCACertTrust.js]
+# An earlier attempt at landing this test resulted in frequent intermittent
+# failures, almost entirely on Linux. See Bug 1309519.
+skip-if = os == "linux"
+[browser_exportP12_passwordUI.js]
+[browser_loadPKCS11Module_ui.js]
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js b/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
new file mode 100644
index 0000000000..a1be1d2a29
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_bug627234_perwindowpb.js
@@ -0,0 +1,101 @@
+/* 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/. */
+"use strict";
+
+function whenNewWindowLoaded(aOptions, aCallback) {
+ let win = OpenBrowserWindow(aOptions);
+ win.addEventListener(
+ "load",
+ function() {
+ aCallback(win);
+ },
+ { once: true }
+ );
+}
+
+// This is a template to help porting global private browsing tests
+// to per-window private browsing tests
+function test() {
+ // initialization
+ waitForExplicitFinish();
+ let windowsToClose = [];
+ let testURI = "about:blank";
+ let uri;
+ let gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+
+ function privacyFlags(aIsPrivateMode) {
+ return aIsPrivateMode ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;
+ }
+
+ function doTest(aIsPrivateMode, aWindow, aCallback) {
+ BrowserTestUtils.browserLoaded(aWindow.gBrowser.selectedBrowser).then(
+ () => {
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+ uri = aWindow.Services.io.newURI("https://localhost/img.png");
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1000",
+ secInfo,
+ privacyFlags(aIsPrivateMode),
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ privacyFlags(aIsPrivateMode)
+ ),
+ "checking sts host"
+ );
+
+ aCallback();
+ }
+ );
+
+ BrowserTestUtils.loadURI(aWindow.gBrowser.selectedBrowser, testURI);
+ }
+
+ function testOnWindow(aOptions, aCallback) {
+ whenNewWindowLoaded(aOptions, function(aWin) {
+ windowsToClose.push(aWin);
+ // execute should only be called when need, like when you are opening
+ // web pages on the test. If calling executeSoon() is not necesary, then
+ // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+ executeSoon(function() {
+ aCallback(aWin);
+ });
+ });
+ }
+
+ // this function is called after calling finish() on the test.
+ registerCleanupFunction(function() {
+ windowsToClose.forEach(function(aWin) {
+ aWin.close();
+ });
+ uri = Services.io.newURI("http://localhost");
+ gSSService.resetState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0);
+ });
+
+ // test first when on private mode
+ testOnWindow({ private: true }, function(aWin) {
+ doTest(true, aWin, function() {
+ // test when not on private mode
+ testOnWindow({}, function(aWin) {
+ doTest(false, aWin, function() {
+ // test again when on private mode
+ testOnWindow({ private: true }, function(aWin) {
+ doTest(true, aWin, function() {
+ finish();
+ });
+ });
+ });
+ });
+ });
+ });
+}
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
new file mode 100644
index 0000000000..d686653413
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
@@ -0,0 +1,107 @@
+/* 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/. */
+"use strict";
+
+// Repeatedly opens the certificate viewer dialog with various certificates and
+// determines that the viewer correctly identifies either what usages those
+// certificates are valid for or what errors prevented the certificates from
+// being verified.
+
+var { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+
+add_task(async function testCAandTitle() {
+ let cert = await readCertificate("ca.pem", "CTu,CTu,CTu");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "ca");
+});
+
+add_task(async function testSSLEndEntity() {
+ let cert = await readCertificate("ssl-ee.pem", ",,");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "ssl-ee");
+});
+
+add_task(async function testEmailEndEntity() {
+ let cert = await readCertificate("email-ee.pem", ",,");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "email-ee");
+});
+
+add_task(async function testCodeSignEndEntity() {
+ let cert = await readCertificate("code-ee.pem", ",,");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "code-ee");
+});
+
+add_task(async function testExpired() {
+ let cert = await readCertificate("expired-ca.pem", ",,");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "expired-ca");
+});
+
+add_task(async function testUntrusted() {
+ let cert = await readCertificate("untrusted-ca.pem", "p,p,p");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "untrusted-ca");
+});
+
+add_task(async function testInvalid() {
+ // This certificate has a keyUsage extension asserting cRLSign and
+ // keyCertSign, but it doesn't have a basicConstraints extension. This
+ // shouldn't be valid for any usage. Sadly, we give a pretty bad error
+ // message in this case.
+ let cert = await readCertificate("invalid.pem", ",,");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "invalid");
+});
+
+add_task(async function testLongOID() {
+ // This certificate has a certificatePolicies extension with a policy with a
+ // very long OID. This tests that we don't crash when looking at it.
+ let cert = await readCertificate("longOID.pem", ",,");
+ let url = getURL(cert);
+ await openCertViewerAndCheckTabName(url, "Long OID");
+});
+
+/**
+ * Given a certificate, returns its PEMs (each one of the certificate chain) string in a url.
+ * @param {Object} cert
+ * A certificate object
+ * @returns {String} an URL for opening the certificate viewer
+ */
+function getURL(cert) {
+ // Note that we don't get the certificate chain as in e.g browser/base/content/browser.js,
+ // because all the .pem files when opened with CS (https://github.com/april/certainly-something)
+ // shows only one certificate
+ let derb64 = encodeURIComponent(cert.getBase64DERString());
+ return `about:certificate?cert=${derb64}`;
+}
+
+/**
+ * Given an certificate URL, opens the new certificate viewer and check
+ * if a certain element exists, with its expected result.
+ *
+ * @param {String} url
+ * The URL with the certificate info
+ * @param {String} expectedTabName
+ * The expected name of the tab in the certificate viewer
+ */
+async function openCertViewerAndCheckTabName(url, expectedTabName) {
+ await BrowserTestUtils.withNewTab({ gBrowser, url }, async function(browser) {
+ await SpecialPowers.spawn(browser, [expectedTabName], async function(
+ expectedTabName
+ ) {
+ let certificateSection = await ContentTaskUtils.waitForCondition(() => {
+ return content.document.querySelector("certificate-section");
+ }, "Certificate section found");
+ let tabName = certificateSection.shadowRoot.querySelector(
+ ".tab[idnumber='0']"
+ ).textContent;
+ Assert.equal(tabName, expectedTabName);
+ });
+ });
+}
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_certificateManager.js b/security/manager/ssl/tests/mochitest/browser/browser_certificateManager.js
new file mode 100644
index 0000000000..3b6ab0d6f3
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_certificateManager.js
@@ -0,0 +1,179 @@
+/* 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/. */
+"use strict";
+
+const TEST_CERT_BASE64 =
+ "MIICrjCCAZagAwIBAgIUCR+2dzKgSt0CBU86EgRdJIa/72owDQYJKoZIhvcNAQEEBQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowETEPMA0GA1UEAwwGbWQ1LWVlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3DQEBBAUAA4IBAQArfDtm5sEuzsPZps2oZhvrOYRViP0sRIAbAjuJVnAMA/7xLzI3LweXOlQJHBh3m3mrPUMAXfr4xJ5XjGySqHBUtFat9EBl/0bynd67sCMA7UOf51GC4ABOPAV0CkDj/FNqL/KSt8WB90FW+ZKnt1ojKikMIjmPjxDaaHj7KZVBQ2KtBEz1Igt5Bvbrp2AMNvjyhUCN4/z4NDPPbzkeDKYC7vmvYhN1Cs/73Jp3A0LU4z0gIyrWi6a7YEAVBzhIvJJ98U8AQQMm+iiIDGZMoAZyvEFouofF9te4xMHStUnYfa1jLY93dL1TuXrWvKB0pWg4REoQuFZUE8GS/LczW5xX";
+
+async function checkServerCertificates(win, expectedValues = []) {
+ await TestUtils.waitForCondition(() => {
+ return (
+ win.document.getElementById("serverList").itemChildren.length ==
+ expectedValues.length
+ );
+ }, `Expected to have ${expectedValues.length} but got ${win.document.getElementById("serverList").itemChildren.length}`);
+
+ let labels = win.document
+ .getElementById("serverList")
+ .querySelectorAll("label");
+
+ expectedValues.forEach((item, i) => {
+ let hostPort = labels[i * 3].value;
+ let certString = labels[i * 3 + 1].value || labels[i * 3 + 1].textContent;
+ let isTemporaryString =
+ labels[i * 3 + 2].value || labels[i * 3 + 2].textContent;
+
+ Assert.equal(
+ hostPort,
+ item.hostPort,
+ `Expected override to be ${item.hostPort} but got ${hostPort}`
+ );
+
+ Assert.equal(
+ certString,
+ item.certName,
+ `Expected override to have field ${item.certName}`
+ );
+
+ Assert.equal(
+ isTemporaryString,
+ item.isTemporary ? "Temporary" : "Permanent",
+ `Expected override to be ${item.isTemporary ? "Temporary" : "Permanent"}`
+ );
+ });
+}
+
+async function deleteOverride(win, expectedLength) {
+ win.document.getElementById("serverList").selectedIndex = 0;
+ await TestUtils.waitForCondition(() => {
+ return (
+ win.document.getElementById("serverList").itemChildren.length ==
+ expectedLength
+ );
+ });
+ let newWinPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
+ // Since the .click() blocks we need to dispatch it to the main thread avoid that.
+ Services.tm.dispatchToMainThread(() =>
+ win.document.getElementById("websites_deleteButton").click()
+ );
+ let newWin = await newWinPromise;
+ newWin.document.getElementById("deleteCertificate").acceptDialog();
+ Assert.equal(
+ win.document.getElementById("serverList").selectedIndex,
+ 0,
+ "After deletion we expect the selectedItem to be reset."
+ );
+}
+
+async function testViewButton(win) {
+ win.document.getElementById("serverList").selectedIndex = 1;
+
+ Assert.ok(
+ win.document.getElementById("websites_viewButton").disabled,
+ "View button should be disabled for override without cert"
+ );
+
+ win.document.getElementById("serverList").selectedIndex = 0;
+
+ Assert.ok(
+ !win.document.getElementById("websites_viewButton").disabled,
+ "View button should be enabled for override with cert"
+ );
+
+ let loaded = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
+
+ win.document.getElementById("websites_viewButton").click();
+
+ let newTab = await loaded;
+ let spec = newTab.linkedBrowser.documentURI.spec;
+
+ Assert.ok(
+ spec.startsWith("about:certificate"),
+ "about:certificate should habe been opened"
+ );
+
+ let newUrl = new URL(spec);
+ let certEncoded = newUrl.searchParams.get("cert");
+ let certDecoded = decodeURIComponent(certEncoded);
+ Assert.equal(
+ TEST_CERT_BASE64,
+ certDecoded,
+ "Loaded cert should match expected cert"
+ );
+
+ gBrowser.removeCurrentTab();
+}
+
+add_task(async function test_cert_manager_server_tab() {
+ let win = await openCertManager();
+
+ await checkServerCertificates(win);
+
+ win.document.getElementById("certmanager").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ let cert = await readCertificate("md5-ee.pem", ",,");
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.rememberValidityOverride(
+ "example.com",
+ 443,
+ cert,
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ false
+ );
+
+ win = await openCertManager();
+
+ await checkServerCertificates(win, [
+ {
+ hostPort: "example.com:443",
+ certName: "md5-ee",
+ isTemporary: false,
+ },
+ ]);
+
+ win.document.getElementById("certmanager").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ certOverrideService.rememberTemporaryValidityOverrideUsingFingerprint(
+ "example.com",
+ 9999,
+ "40:20:3E:57:FB:82:95:0D:3F:62:D7:04:39:F6:32:CC:B2:2F:70:9F:3E:66:C5:35:64:6E:49:2A:F1:02:75:9F",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED
+ );
+
+ win = await openCertManager();
+
+ await checkServerCertificates(win, [
+ {
+ hostPort: "example.com:443",
+ certName: "md5-ee",
+ isTemporary: false,
+ },
+ {
+ hostPort: "example.com:9999",
+ certName: "(Not Stored)",
+ isTemporary: true,
+ },
+ ]);
+
+ await testViewButton(win);
+
+ await deleteOverride(win, 2);
+
+ await checkServerCertificates(win, [
+ {
+ hostPort: "example.com:9999",
+ certName: "(Not Stored)",
+ isTemporary: true,
+ },
+ ]);
+
+ win.document.getElementById("certmanager").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ certOverrideService.clearAllOverrides();
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js b/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js
new file mode 100644
index 0000000000..3ab2d9792e
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js
@@ -0,0 +1,257 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+/**
+ * Test certificate (i.e. build/pgo/certs/mochitest.client).
+ * @type nsIX509Cert
+ */
+var cert;
+var cert2;
+var cert3;
+
+var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
+var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+var deleted = false;
+
+const { MockRegistrar } = ChromeUtils.import(
+ "resource://testing-common/MockRegistrar.jsm"
+);
+
+function findCertByCommonName(commonName) {
+ for (let cert of certDB.getCerts()) {
+ if (cert.commonName == commonName) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+async function testHelper(connectURL, expectedURL) {
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.default_personal_cert", "Ask Every Time"]],
+ });
+
+ BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, connectURL);
+
+ await BrowserTestUtils.browserLoaded(
+ win.gBrowser.selectedBrowser,
+ false,
+ expectedURL,
+ true
+ );
+ let loadedURL = win.gBrowser.selectedBrowser.documentURI.spec;
+ Assert.ok(
+ loadedURL.startsWith(expectedURL),
+ `Expected and actual URLs should match (got '${loadedURL}', expected '${expectedURL}')`
+ );
+
+ await win.close();
+
+ // This clears the TLS session cache so we don't use a previously-established
+ // ticket to connect and bypass selecting a client auth certificate in
+ // subsequent tests.
+ sdr.logout();
+}
+
+async function openRequireClientCert() {
+ gClientAuthDialogs.chooseCertificateCalled = false;
+ await testHelper(
+ "https://requireclientcert.example.com:443",
+ "https://requireclientcert.example.com/"
+ );
+}
+
+async function openRequireClientCert2() {
+ gClientAuthDialogs.chooseCertificateCalled = false;
+ await testHelper(
+ "https://requireclientcert-2.example.com:443",
+ "https://requireclientcert-2.example.com/"
+ );
+}
+
+// Mock implementation of nsIClientAuthRememberService
+const gClientAuthRememberService = {
+ forgetRememberedDecision(key) {
+ deleted = true;
+ Assert.equal(
+ key,
+ "exampleKey2",
+ "Expected to get the same key that was passed in getDecisions()"
+ );
+ },
+
+ getDecisions() {
+ return [
+ {
+ asciiHost: "example.com",
+ dbKey: cert.dbKey,
+ entryKey: "exampleKey1",
+ },
+ {
+ asciiHost: "example.org",
+ dbKey: cert2.dbKey,
+ entryKey: "exampleKey2",
+ },
+ {
+ asciiHost: "example.test",
+ dbKey: cert3.dbKey,
+ entryKey: "exampleKey3",
+ },
+ ];
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIClientAuthRememberService"]),
+};
+
+const gClientAuthDialogs = {
+ _chooseCertificateCalled: false,
+
+ get chooseCertificateCalled() {
+ return this._chooseCertificateCalled;
+ },
+
+ set chooseCertificateCalled(value) {
+ this._chooseCertificateCalled = value;
+ },
+
+ chooseCertificate(
+ hostname,
+ port,
+ organization,
+ issuerOrg,
+ certList,
+ selectedIndex,
+ rememberClientAuthCertificate
+ ) {
+ rememberClientAuthCertificate.value = true;
+ this.chooseCertificateCalled = true;
+ selectedIndex.value = 0;
+ return true;
+ },
+
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIClientAuthDialogs]),
+};
+
+add_task(async function testRememberedDecisionsUI() {
+ cert = findCertByCommonName("Mochitest client");
+ cert2 = await readCertificate("pgo-ca-all-usages.pem", ",,");
+ cert3 = await readCertificate("client-cert-via-intermediate.pem", ",,");
+ isnot(cert, null, "Should be able to find the test client cert");
+ isnot(cert2, null, "Should be able to find pgo-ca-all-usages.pem");
+ isnot(cert3, null, "Should be able to find client-cert-via-intermediate.pem");
+
+ let clientAuthRememberServiceCID = MockRegistrar.register(
+ "@mozilla.org/security/clientAuthRememberService;1",
+ gClientAuthRememberService
+ );
+
+ let win = await openCertManager();
+
+ let listItems = win.document
+ .getElementById("rememberedList")
+ .querySelectorAll("richlistitem");
+
+ Assert.equal(
+ listItems.length,
+ 3,
+ "Expected rememberedList to only have one item"
+ );
+
+ let labels = win.document
+ .getElementById("rememberedList")
+ .querySelectorAll("label");
+
+ Assert.equal(
+ labels.length,
+ 9,
+ "Expected the rememberedList to have three labels"
+ );
+
+ let expectedHosts = ["example.com", "example.org", "example.test"];
+ let hosts = [labels[0].value, labels[3].value, labels[6].value];
+ let expectedNames = [cert.commonName, cert2.commonName, cert3.commonName];
+ let names = [labels[1].value, labels[4].value, labels[7].value];
+ let expectedSerialNumbers = [
+ cert.serialNumber,
+ cert2.serialNumber,
+ cert3.serialNumber,
+ ];
+ let serialNumbers = [labels[2].value, labels[5].value, labels[8].value];
+
+ for (let i = 0; i < 3; i++) {
+ Assert.equal(hosts[i], expectedHosts[i], "Expected host to be asciiHost");
+ Assert.equal(
+ names[i],
+ expectedNames[i],
+ "Expected name to be the commonName of the cert"
+ );
+ Assert.equal(
+ serialNumbers[i],
+ expectedSerialNumbers[i],
+ "Expected serialNumber to be the serialNumber of the cert"
+ );
+ }
+
+ win.document.getElementById("rememberedList").selectedIndex = 1;
+
+ win.document.getElementById("remembered_deleteButton").click();
+
+ Assert.ok(deleted, "Expected forgetRememberedDecision() to get called");
+
+ win.document.getElementById("certmanager").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ MockRegistrar.unregister(clientAuthRememberServiceCID);
+});
+
+add_task(async function testDeletingRememberedDecisions() {
+ let clientAuthDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsClientAuthDialogs;1",
+ gClientAuthDialogs
+ );
+ let cars = Cc["@mozilla.org/security/clientAuthRememberService;1"].getService(
+ Ci.nsIClientAuthRememberService
+ );
+
+ await openRequireClientCert();
+ Assert.ok(
+ gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should have been called if visiting 'requireclientcert.example.com' for the first time"
+ );
+
+ await openRequireClientCert();
+ Assert.ok(
+ !gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should not have been called if visiting 'requireclientcert.example.com' for the second time"
+ );
+
+ await openRequireClientCert2();
+ Assert.ok(
+ gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should have been called if visiting'requireclientcert-2.example.com' for the first time"
+ );
+
+ let originAttributes = { privateBrowsingId: 0 };
+ cars.deleteDecisionsByHost("requireclientcert.example.com", originAttributes);
+
+ await openRequireClientCert();
+ Assert.ok(
+ gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should have been called after removing all remembered decisions for 'requireclientcert.example.com'"
+ );
+
+ await openRequireClientCert2();
+ Assert.ok(
+ !gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should not have been called if visiting 'requireclientcert-2.example.com' for the second time"
+ );
+
+ MockRegistrar.unregister(clientAuthDialogsCID);
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js
new file mode 100644
index 0000000000..a0e9723f43
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js
@@ -0,0 +1,342 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests various scenarios connecting to a server that requires client cert
+// authentication. Also tests that nsIClientAuthDialogs.chooseCertificate
+// is called at the appropriate times and with the correct arguments.
+
+const { MockRegistrar } = ChromeUtils.import(
+ "resource://testing-common/MockRegistrar.jsm"
+);
+
+const DialogState = {
+ // Assert that chooseCertificate() is never called.
+ ASSERT_NOT_CALLED: "ASSERT_NOT_CALLED",
+ // Return that the user selected the first given cert.
+ RETURN_CERT_SELECTED: "RETURN_CERT_SELECTED",
+ // Return that the user canceled.
+ RETURN_CERT_NOT_SELECTED: "RETURN_CERT_NOT_SELECTED",
+};
+
+var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
+let cars = Cc["@mozilla.org/security/clientAuthRememberService;1"].getService(
+ Ci.nsIClientAuthRememberService
+);
+
+var gExpectedClientCertificateChoices;
+
+// Mock implementation of nsIClientAuthDialogs.
+const gClientAuthDialogs = {
+ _state: DialogState.ASSERT_NOT_CALLED,
+ _rememberClientAuthCertificate: false,
+ _chooseCertificateCalled: false,
+
+ set state(newState) {
+ info(`old state: ${this._state}`);
+ this._state = newState;
+ info(`new state: ${this._state}`);
+ },
+
+ get state() {
+ return this._state;
+ },
+
+ set rememberClientAuthCertificate(value) {
+ this._rememberClientAuthCertificate = value;
+ },
+
+ get rememberClientAuthCertificate() {
+ return this._rememberClientAuthCertificate;
+ },
+
+ get chooseCertificateCalled() {
+ return this._chooseCertificateCalled;
+ },
+
+ set chooseCertificateCalled(value) {
+ this._chooseCertificateCalled = value;
+ },
+
+ chooseCertificate(
+ hostname,
+ port,
+ organization,
+ issuerOrg,
+ certList,
+ selectedIndex,
+ rememberClientAuthCertificate
+ ) {
+ this.chooseCertificateCalled = true;
+ Assert.notEqual(
+ this.state,
+ DialogState.ASSERT_NOT_CALLED,
+ "chooseCertificate() should be called only when expected"
+ );
+
+ rememberClientAuthCertificate.value = this.rememberClientAuthCertificate;
+
+ Assert.equal(
+ hostname,
+ "requireclientcert.example.com",
+ "Hostname should be 'requireclientcert.example.com'"
+ );
+ Assert.equal(port, 443, "Port should be 443");
+ Assert.equal(
+ organization,
+ "",
+ "Server cert Organization should be empty/not present"
+ );
+ Assert.equal(
+ issuerOrg,
+ "Mozilla Testing",
+ "Server cert issuer Organization should be 'Mozilla Testing'"
+ );
+
+ // For mochitests, the cert at build/pgo/certs/mochitest.client should be
+ // selectable as well as one of the PGO certs we loaded in `setup`, so we do
+ // some brief checks to confirm this.
+ Assert.notEqual(certList, null, "Cert list should not be null");
+ Assert.equal(
+ certList.length,
+ gExpectedClientCertificateChoices,
+ `${gExpectedClientCertificateChoices} certificates should be available`
+ );
+
+ for (let cert of certList.enumerate(Ci.nsIX509Cert)) {
+ Assert.notEqual(cert, null, "Cert list should contain nsIX509Certs");
+ Assert.equal(
+ cert.issuerCommonName,
+ "Temporary Certificate Authority",
+ "cert should have expected issuer CN"
+ );
+ }
+
+ if (this.state == DialogState.RETURN_CERT_SELECTED) {
+ selectedIndex.value = 0;
+ return true;
+ }
+ return false;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIClientAuthDialogs"]),
+};
+
+add_task(async function setup() {
+ let clientAuthDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsClientAuthDialogs;1",
+ gClientAuthDialogs
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(clientAuthDialogsCID);
+ });
+
+ // This CA has the expected keyCertSign and cRLSign usages. It should not be
+ // presented for use as a client certificate.
+ await readCertificate("pgo-ca-regular-usages.pem", "CTu,CTu,CTu");
+ // This CA has all keyUsages. For compatibility with preexisting behavior, it
+ // will be presented for use as a client certificate.
+ await readCertificate("pgo-ca-all-usages.pem", "CTu,CTu,CTu");
+ // This client certificate was issued by an intermediate that was issued by
+ // the test CA. The server only lists the test CA's subject distinguished name
+ // as an acceptible issuer name for client certificates. If the implementation
+ // can determine that the test CA is a root CA for the client certificate and
+ // thus is acceptible to use, it should be included in the chooseCertificate
+ // callback. At the beginning of this test (speaking of this file as a whole),
+ // the client is not aware of the intermediate, and so it is not available in
+ // the callback.
+ await readCertificate("client-cert-via-intermediate.pem", ",,");
+ // This certificate has an id-kp-OCSPSigning EKU. Client certificates
+ // shouldn't have this EKU, but there is at least one private PKI where they
+ // do. For interoperability, such certificates will be presented for use.
+ await readCertificate("client-cert-with-ocsp-signing.pem", ",,");
+ gExpectedClientCertificateChoices = 3;
+});
+
+/**
+ * Test helper for the tests below.
+ *
+ * @param {String} prefValue
+ * Value to set the "security.default_personal_cert" pref to.
+ * @param {String} expectedURL
+ * If the connection is expected to load successfully, the URL that
+ * should load. If the connection is expected to fail and result in an
+ * error page, |undefined|.
+ * @param {Boolean} expectCallingChooseCertificate
+ * Determines whether we expect chooseCertificate to be called.
+ * @param {Object} options
+ * Optional options object to pass on to the window that gets opened.
+ */
+async function testHelper(
+ prefValue,
+ expectedURL,
+ expectCallingChooseCertificate,
+ options = undefined
+) {
+ gClientAuthDialogs.chooseCertificateCalled = false;
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.default_personal_cert", prefValue]],
+ });
+
+ let win = await BrowserTestUtils.openNewBrowserWindow(options);
+
+ BrowserTestUtils.loadURI(
+ win.gBrowser.selectedBrowser,
+ "https://requireclientcert.example.com:443"
+ );
+
+ await BrowserTestUtils.browserLoaded(
+ win.gBrowser.selectedBrowser,
+ false,
+ "https://requireclientcert.example.com/",
+ true
+ );
+ let loadedURL = win.gBrowser.selectedBrowser.documentURI.spec;
+ Assert.ok(
+ loadedURL.startsWith(expectedURL),
+ `Expected and actual URLs should match (got '${loadedURL}', expected '${expectedURL}')`
+ );
+ Assert.equal(
+ gClientAuthDialogs.chooseCertificateCalled,
+ expectCallingChooseCertificate,
+ "chooseCertificate should have been called if we were expecting it to be called"
+ );
+
+ await win.close();
+
+ // This clears the TLS session cache so we don't use a previously-established
+ // ticket to connect and bypass selecting a client auth certificate in
+ // subsequent tests.
+ sdr.logout();
+}
+
+// Test that if a certificate is chosen automatically the connection succeeds,
+// and that nsIClientAuthDialogs.chooseCertificate() is never called.
+add_task(async function testCertChosenAutomatically() {
+ gClientAuthDialogs.state = DialogState.ASSERT_NOT_CALLED;
+ await testHelper(
+ "Select Automatically",
+ "https://requireclientcert.example.com/",
+ false
+ );
+ // This clears all saved client auth certificate state so we don't influence
+ // subsequent tests.
+ cars.clearRememberedDecisions();
+});
+
+// Test that if the user doesn't choose a certificate, the connection fails and
+// an error page is displayed.
+add_task(async function testCertNotChosenByUser() {
+ gClientAuthDialogs.state = DialogState.RETURN_CERT_NOT_SELECTED;
+ await testHelper(
+ "Ask Every Time",
+ "about:neterror?e=nssFailure2&u=https%3A//requireclientcert.example.com/",
+ true
+ );
+ cars.clearRememberedDecisions();
+});
+
+// Test that if the user chooses a certificate the connection suceeeds.
+add_task(async function testCertChosenByUser() {
+ gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED;
+ await testHelper(
+ "Ask Every Time",
+ "https://requireclientcert.example.com/",
+ true
+ );
+ cars.clearRememberedDecisions();
+});
+
+// Test that the cancel decision is remembered correctly
+add_task(async function testEmptyCertChosenByUser() {
+ gClientAuthDialogs.state = DialogState.RETURN_CERT_NOT_SELECTED;
+ gClientAuthDialogs.rememberClientAuthCertificate = true;
+ await testHelper(
+ "Ask Every Time",
+ "about:neterror?e=nssFailure2&u=https%3A//requireclientcert.example.com/",
+ true
+ );
+ await testHelper(
+ "Ask Every Time",
+ "about:neterror?e=nssFailure2&u=https%3A//requireclientcert.example.com/",
+ false
+ );
+ cars.clearRememberedDecisions();
+});
+
+// Test that if the user chooses a certificate in a private browsing window,
+// configures Firefox to remember this certificate for the duration of the
+// session, closes that window (and thus all private windows), reopens a private
+// window, and visits that site again, they are re-asked for a certificate (i.e.
+// any state from the previous private session should be gone). Similarly, after
+// closing that private window, if the user opens a non-private window, they
+// again should be asked to choose a certificate (i.e. private state should not
+// be remembered/used in non-private contexts).
+add_task(async function testClearPrivateBrowsingState() {
+ gClientAuthDialogs.rememberClientAuthCertificate = true;
+ gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED;
+ await testHelper(
+ "Ask Every Time",
+ "https://requireclientcert.example.com/",
+ true,
+ {
+ private: true,
+ }
+ );
+ await testHelper(
+ "Ask Every Time",
+ "https://requireclientcert.example.com/",
+ true,
+ {
+ private: true,
+ }
+ );
+ await testHelper(
+ "Ask Every Time",
+ "https://requireclientcert.example.com/",
+ true
+ );
+ // NB: we don't `cars.clearRememberedDecisions()` in between the two calls to
+ // `testHelper` because that would clear all client auth certificate state and
+ // obscure what we're testing (that Firefox properly clears the relevant state
+ // when the last private window closes).
+ cars.clearRememberedDecisions();
+});
+
+// Test that 3rd party certificates are taken into account when filtering client
+// certificates based on the acceptible CA list sent by the server.
+add_task(async function testCertFilteringWithIntermediate() {
+ let intermediateBytes = await OS.File.read(
+ getTestFilePath("intermediate.pem")
+ ).then(
+ data => {
+ let decoder = new TextDecoder();
+ let pem = decoder.decode(data);
+ let base64 = pemToBase64(pem);
+ let bin = atob(base64);
+ let bytes = [];
+ for (let i = 0; i < bin.length; i++) {
+ bytes.push(bin.charCodeAt(i));
+ }
+ return bytes;
+ },
+ error => {
+ throw error;
+ }
+ );
+ let nssComponent = Cc["@mozilla.org/psm;1"].getService(Ci.nsINSSComponent);
+ nssComponent.addEnterpriseIntermediate(intermediateBytes);
+ gExpectedClientCertificateChoices = 4;
+ gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED;
+ await testHelper(
+ "Ask Every Time",
+ "https://requireclientcert.example.com/",
+ true
+ );
+ cars.clearRememberedDecisions();
+ // This will reset the added intermediate.
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.enterprise_roots.enabled", true]],
+ });
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js
new file mode 100644
index 0000000000..c75cf02482
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_ui.js
@@ -0,0 +1,199 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that the client authentication certificate chooser correctly displays
+// provided information and correctly returns user input.
+
+const TEST_HOSTNAME = "Test Hostname";
+const TEST_ORG = "Test Org";
+const TEST_ISSUER_ORG = "Test Issuer Org";
+const TEST_PORT = 123;
+
+var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+/**
+ * Test certificate (i.e. build/pgo/certs/mochitest.client).
+ * @type nsIX509Cert
+ */
+var cert;
+
+/**
+ * Opens the client auth cert chooser dialog.
+ *
+ * @param {nsIX509Cert} cert The cert to pass to the dialog for display.
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading, with
+ * an array consisting of:
+ * 1. The window of the opened dialog.
+ * 2. The return value nsIWritablePropertyBag2 passed to the dialog.
+ */
+function openClientAuthDialog(cert) {
+ let certList = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ certList.appendElement(cert);
+
+ let returnVals = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag2
+ );
+ let win = window.openDialog(
+ "chrome://pippki/content/clientauthask.xhtml",
+ "",
+ "",
+ TEST_HOSTNAME,
+ TEST_ORG,
+ TEST_ISSUER_ORG,
+ TEST_PORT,
+ certList,
+ returnVals
+ );
+ return new Promise((resolve, reject) => {
+ win.addEventListener(
+ "load",
+ function() {
+ executeSoon(() => resolve([win, returnVals]));
+ },
+ { once: true }
+ );
+ });
+}
+
+/**
+ * Checks that the contents of the given cert chooser dialog match the details
+ * of build/pgo/certs/mochitest.client.
+ *
+ * @param {window} win The cert chooser window.
+ * @param {String} notBefore
+ * The notBeforeLocalTime attribute of mochitest.client.
+ * @param {String} notAfter
+ * The notAfterLocalTime attribute of mochitest.client.
+ */
+function checkDialogContents(win, notBefore, notAfter) {
+ is(
+ win.document.getElementById("hostname").textContent,
+ `${TEST_HOSTNAME}:${TEST_PORT}`,
+ "Actual and expected hostname and port should be equal"
+ );
+ is(
+ win.document.getElementById("organization").textContent,
+ `Organization: “${TEST_ORG}”`,
+ "Actual and expected organization should be equal"
+ );
+ is(
+ win.document.getElementById("issuer").textContent,
+ `Issued Under: “${TEST_ISSUER_ORG}”`,
+ "Actual and expected issuer organization should be equal"
+ );
+
+ is(
+ win.document.getElementById("nicknames").label,
+ "Mochitest client [03]",
+ "Actual and expected selected cert nickname and serial should be equal"
+ );
+ is(
+ win.document.getElementById("nicknames").itemCount,
+ 1,
+ "correct number of items"
+ );
+
+ let [
+ subject,
+ serialNum,
+ validity,
+ issuer,
+ tokenName,
+ ] = win.document.getElementById("details").value.split("\n");
+ is(
+ subject,
+ "Issued to: CN=Mochitest client",
+ "Actual and expected subject should be equal"
+ );
+ is(
+ serialNum,
+ "Serial number: 03",
+ "Actual and expected serial number should be equal"
+ );
+ is(
+ validity,
+ `Valid from ${notBefore} to ${notAfter}`,
+ "Actual and expected validity should be equal"
+ );
+ is(
+ issuer,
+ "Issued by: OU=Profile Guided Optimization,O=Mozilla Testing,CN=Temporary Certificate Authority",
+ "Actual and expected issuer should be equal"
+ );
+ is(
+ tokenName,
+ "Stored on: Software Security Device",
+ "Actual and expected token name should be equal"
+ );
+}
+
+function findCertByCommonName(commonName) {
+ for (let cert of certDB.getCerts()) {
+ if (cert.commonName == commonName) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+add_task(async function setup() {
+ cert = findCertByCommonName("Mochitest client");
+ isnot(cert, null, "Should be able to find the test client cert");
+});
+
+// Test that the contents of the dialog correspond to the details of the
+// provided cert.
+add_task(async function testContents() {
+ let [win] = await openClientAuthDialog(cert);
+ checkDialogContents(
+ win,
+ cert.validity.notBeforeLocalTime,
+ cert.validity.notAfterLocalTime
+ );
+ await BrowserTestUtils.closeWindow(win);
+});
+
+// Test that the right values are returned when the dialog is accepted.
+add_task(async function testAcceptDialogReturnValues() {
+ let [win, retVals] = await openClientAuthDialog(cert);
+ win.document.getElementById("rememberBox").checked = true;
+ info("Accepting dialog");
+ win.document.getElementById("certAuthAsk").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ ok(
+ retVals.get("certChosen"),
+ "Return value should signal user chose a certificate"
+ );
+ is(
+ retVals.get("selectedIndex"),
+ 0,
+ "0 should be returned as the selected index"
+ );
+ ok(
+ retVals.get("rememberSelection"),
+ "Return value should signal 'Remember this decision' checkbox was checked"
+ );
+});
+
+// Test that the right values are returned when the dialog is canceled.
+add_task(async function testCancelDialogReturnValues() {
+ let [win, retVals] = await openClientAuthDialog(cert);
+ win.document.getElementById("rememberBox").checked = false;
+ info("Canceling dialog");
+ win.document.getElementById("certAuthAsk").cancelDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ ok(
+ !retVals.get("certChosen"),
+ "Return value should signal user did not choose a certificate"
+ );
+ ok(
+ !retVals.get("rememberSelection"),
+ "Return value should signal 'Remember this decision' checkbox was unchecked"
+ );
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js b/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
new file mode 100644
index 0000000000..0d06c553a2
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
@@ -0,0 +1,257 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests various aspects of the cert delete confirmation dialog.
+// Among other things, tests that for each type of cert that can be deleted:
+// 1. The various lines of explanation text are correctly set.
+// 2. The implementation correctly falls back through multiple cert attributes
+// to determine what to display to represent a cert.
+
+/**
+ * An array of tree items corresponding to TEST_CASES.
+ * @type nsICertTreeItem[]
+ */
+var gCertArray = [];
+
+const FAKE_HOST_PORT = "Fake host and port";
+
+/**
+ * @typedef {TestCase}
+ * @type Object
+ * @property {String} certFilename
+ * Filename of the cert, or null if we don't want to import a cert for
+ * this test case (i.e. we expect the hostPort attribute of
+ * nsICertTreeItem to be used).
+ * @property {String} expectedDisplayString
+ * The string we expect the UI to display to represent the given cert.
+ * @property {String} expectedSerialNumber
+ * The serial number we expect the UI to display if it exists.
+ */
+
+/**
+ * A list of test cases representing certs that get "deleted".
+ * @type TestCase[]
+ */
+const TEST_CASES = [
+ {
+ certFilename: null,
+ expectedDisplayString: FAKE_HOST_PORT,
+ expectedSerialNumber: null,
+ },
+ {
+ certFilename: "has-cn.pem",
+ expectedDisplayString: "Foo",
+ expectedSerialNumber: null,
+ },
+ {
+ certFilename: "has-ou.pem",
+ expectedDisplayString: "Bar",
+ expectedSerialNumber: null,
+ },
+ {
+ certFilename: "has-o.pem",
+ expectedDisplayString: "Baz",
+ expectedSerialNumber: null,
+ },
+ {
+ certFilename: "has-non-empty-subject.pem",
+ expectedDisplayString: "C=US",
+ expectedSerialNumber: null,
+ },
+ {
+ certFilename: "has-empty-subject.pem",
+ expectedDisplayString: "Certificate with serial number: 0A",
+ expectedSerialNumber: "0A",
+ },
+];
+
+/**
+ * Opens the cert delete confirmation dialog.
+ *
+ * @param {String} tabID
+ * The ID of the cert category tab the certs to delete belong to.
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading, with
+ * an array consisting of:
+ * 1. The window of the opened dialog.
+ * 2. The return value object passed to the dialog.
+ */
+function openDeleteCertConfirmDialog(tabID) {
+ let retVals = {
+ deleteConfirmed: false,
+ };
+ let win = window.openDialog(
+ "chrome://pippki/content/deletecert.xhtml",
+ "",
+ "",
+ tabID,
+ gCertArray,
+ retVals
+ );
+ return new Promise((resolve, reject) => {
+ win.addEventListener(
+ "load",
+ function() {
+ executeSoon(() => resolve([win, retVals]));
+ },
+ { once: true }
+ );
+ });
+}
+
+add_task(async function setup() {
+ for (let testCase of TEST_CASES) {
+ let cert = null;
+ if (testCase.certFilename) {
+ cert = await readCertificate(testCase.certFilename, ",,");
+ }
+ let certTreeItem = {
+ hostPort: FAKE_HOST_PORT,
+ cert,
+ QueryInterface: ChromeUtils.generateQI(["nsICertTreeItem"]),
+ };
+ gCertArray.push(certTreeItem);
+ }
+});
+
+/**
+ * Test helper for the below test cases.
+ *
+ * @param {String} tabID
+ * ID of the cert category tab the certs to delete belong to.
+ * @param {String} expectedTitleL10nId
+ * The L10nId of title the dialog is expected to have.
+ * @param {String} expectedConfirmL10nId
+ * The l10n id of confirmation message the dialog expected to show.
+ * @param {String} expectedImpactL10nId
+ * The l10n id of impact the dialog expected to show.
+ */
+async function testHelper(
+ tabID,
+ expectedTitleL10nId,
+ expectedConfirmL10nId,
+ expectedImpactL10nId
+) {
+ let [win] = await openDeleteCertConfirmDialog(tabID);
+ let certList = win.document.getElementById("certlist");
+
+ Assert.deepEqual(
+ win.document.l10n.getAttributes(win.document.documentElement),
+ expectedTitleL10nId,
+ `Actual and expected titles should match for ${tabID}`
+ );
+ let confirm = win.document.getElementById("confirm");
+ Assert.deepEqual(
+ win.document.l10n.getAttributes(confirm),
+ expectedConfirmL10nId,
+ `Actual and expected confirm message should match for ${tabID}`
+ );
+ let impact = win.document.getElementById("impact");
+ Assert.deepEqual(
+ win.document.l10n.getAttributes(impact),
+ expectedImpactL10nId,
+ `Actual and expected impact should match for ${tabID}`
+ );
+
+ Assert.equal(
+ certList.itemCount,
+ TEST_CASES.length,
+ `No. of certs displayed should match for ${tabID}`
+ );
+ for (let i = 0; i < certList.itemCount; i++) {
+ let item = certList.getItemAtIndex(i);
+ if (TEST_CASES[i].expectedSerialNumber == null) {
+ Assert.equal(
+ item.label,
+ TEST_CASES[i].expectedDisplayString,
+ "Actual and expected display string should match for " +
+ `index ${i} for ${tabID}`
+ );
+ } else {
+ Assert.deepEqual(
+ win.document.l10n.getAttributes(item.children[0]),
+ {
+ id: "cert-with-serial",
+ args: { serialNumber: TEST_CASES[i].expectedSerialNumber },
+ },
+ "Actual and expected display string should match for " +
+ `index ${i} for ${tabID}`
+ );
+ }
+ }
+
+ await BrowserTestUtils.closeWindow(win);
+}
+
+// Test deleting certs from the "Your Certificates" tab.
+add_task(async function testDeletePersonalCerts() {
+ const expectedTitleL10nId = { id: "delete-user-cert-title", args: null };
+ const expectedConfirmL10nId = { id: "delete-user-cert-confirm", args: null };
+ const expectedImpactL10nId = { id: "delete-user-cert-impact", args: null };
+ await testHelper(
+ "mine_tab",
+ expectedTitleL10nId,
+ expectedConfirmL10nId,
+ expectedImpactL10nId
+ );
+});
+
+// Test deleting certs from the "People" tab.
+add_task(async function testDeleteOtherPeopleCerts() {
+ const expectedTitleL10nId = { id: "delete-email-cert-title", args: null };
+ // ’ doesn't seem to work when embedded in the following literals, which is
+ // why escape codes are used instead.
+ const expectedConfirmL10nId = { id: "delete-email-cert-confirm", args: null };
+ const expectedImpactL10nId = { id: "delete-email-cert-impact", args: null };
+ await testHelper(
+ "others_tab",
+ expectedTitleL10nId,
+ expectedConfirmL10nId,
+ expectedImpactL10nId
+ );
+});
+
+// Test deleting certs from the "Authorities" tab.
+add_task(async function testDeleteCACerts() {
+ const expectedTitleL10nId = { id: "delete-ca-cert-title", args: null };
+ const expectedConfirmL10nId = { id: "delete-ca-cert-confirm", args: null };
+ const expectedImpactL10nId = { id: "delete-ca-cert-impact", args: null };
+ await testHelper(
+ "ca_tab",
+ expectedTitleL10nId,
+ expectedConfirmL10nId,
+ expectedImpactL10nId
+ );
+});
+
+// Test that the right values are returned when the dialog is accepted.
+add_task(async function testAcceptDialogReturnValues() {
+ let [win, retVals] = await openDeleteCertConfirmDialog(
+ "ca_tab" /* arbitrary */
+ );
+ info("Accepting dialog");
+ win.document.getElementById("deleteCertificate").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ retVals.deleteConfirmed,
+ "Return value should signal user accepted"
+ );
+});
+
+// Test that the right values are returned when the dialog is canceled.
+add_task(async function testCancelDialogReturnValues() {
+ let [win, retVals] = await openDeleteCertConfirmDialog(
+ "ca_tab" /* arbitrary */
+ );
+ info("Canceling dialog");
+ win.document.getElementById("deleteCertificate").cancelDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ !retVals.deleteConfirmed,
+ "Return value should signal user did not accept"
+ );
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js b/security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js
new file mode 100644
index 0000000000..876124d5c2
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_downloadCert_ui.js
@@ -0,0 +1,133 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that the cert download/import UI correctly identifies the cert being
+// downloaded, and allows the trust of the cert to be specified.
+
+const { MockRegistrar } = ChromeUtils.import(
+ "resource://testing-common/MockRegistrar.jsm"
+);
+
+/**
+ * @typedef {TestCase}
+ * @type Object
+ * @property {String} certFilename
+ * Filename of the cert for this test case.
+ * @property {String} expectedDisplayString
+ * The string we expect the UI to display to represent the given cert.
+ * @property {nsIX509Cert} cert
+ * Handle to the cert once read in setup().
+ */
+
+/**
+ * A list of test cases representing certs that get "downloaded".
+ * @type TestCase[]
+ */
+const TEST_CASES = [
+ { certFilename: "has-cn.pem", expectedDisplayString: "Foo", cert: null },
+ {
+ certFilename: "has-empty-subject.pem",
+ expectedDisplayString: "Certificate Authority (unnamed)",
+ cert: null,
+ },
+];
+
+/**
+ * Opens the cert download dialog.
+ *
+ * @param {nsIX509Cert} cert
+ * The cert to pass to the dialog for display.
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading, with
+ * an array consisting of:
+ * 1. The window of the opened dialog.
+ * 2. The return value nsIWritablePropertyBag2 passed to the dialog.
+ */
+function openCertDownloadDialog(cert) {
+ let returnVals = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag2
+ );
+ let win = window.openDialog(
+ "chrome://pippki/content/downloadcert.xhtml",
+ "",
+ "",
+ cert,
+ returnVals
+ );
+ return new Promise((resolve, reject) => {
+ win.addEventListener(
+ "load",
+ function() {
+ executeSoon(() => resolve([win, returnVals]));
+ },
+ { once: true }
+ );
+ });
+}
+
+add_task(async function setup() {
+ for (let testCase of TEST_CASES) {
+ testCase.cert = await readCertificate(testCase.certFilename, ",,");
+ Assert.notEqual(
+ testCase.cert,
+ null,
+ `'${testCase.certFilename}' should have been read`
+ );
+ }
+});
+
+// Test that the trust header message corresponds to the provided cert, and that
+// the View Cert button launches the cert viewer for the provided cert.
+add_task(async function testTrustHeaderAndViewCertButton() {
+ for (let testCase of TEST_CASES) {
+ let [win] = await openCertDownloadDialog(testCase.cert);
+ let expectedTrustHeaderString =
+ `Do you want to trust \u201C${testCase.expectedDisplayString}\u201D ` +
+ "for the following purposes?";
+ Assert.equal(
+ win.document.getElementById("trustHeader").textContent,
+ expectedTrustHeaderString,
+ "Actual and expected trust header text should match for " +
+ `${testCase.certFilename}`
+ );
+
+ await BrowserTestUtils.closeWindow(win);
+ }
+});
+
+// Test that the right values are returned when the dialog is accepted.
+add_task(async function testAcceptDialogReturnValues() {
+ let [win, retVals] = await openCertDownloadDialog(TEST_CASES[0].cert);
+ win.document.getElementById("trustSSL").checked = true;
+ win.document.getElementById("trustEmail").checked = false;
+ info("Accepting dialog");
+ win.document.getElementById("download_cert").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ retVals.get("importConfirmed"),
+ "Return value should signal user chose to import the cert"
+ );
+ Assert.ok(
+ retVals.get("trustForSSL"),
+ "Return value should signal SSL trust checkbox was checked"
+ );
+ Assert.ok(
+ !retVals.get("trustForEmail"),
+ "Return value should signal E-mail trust checkbox was unchecked"
+ );
+});
+
+// Test that the right values are returned when the dialog is canceled.
+add_task(async function testCancelDialogReturnValues() {
+ let [win, retVals] = await openCertDownloadDialog(TEST_CASES[0].cert);
+ info("Canceling dialog");
+ win.document.getElementById("download_cert").cancelDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ !retVals.get("importConfirmed"),
+ "Return value should signal user chose not to import the cert"
+ );
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js b/security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js
new file mode 100644
index 0000000000..f4592ea9dc
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_editCACertTrust.js
@@ -0,0 +1,140 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that the UI for editing the trust of a CA certificate correctly
+// reflects trust in the cert DB, and correctly updates trust in the cert DB
+// when requested.
+
+var gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+/**
+ * The cert we're editing the trust of.
+ * @type nsIX509Cert
+ */
+var gCert;
+
+/**
+ * Opens the cert trust editing dialog.
+ *
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading with
+ * the window of the opened dialog.
+ */
+function openEditCertTrustDialog() {
+ let win = window.openDialog(
+ "chrome://pippki/content/editcacert.xhtml",
+ "",
+ "",
+ gCert
+ );
+ return new Promise((resolve, reject) => {
+ win.addEventListener(
+ "load",
+ function() {
+ executeSoon(() => resolve(win));
+ },
+ { once: true }
+ );
+ });
+}
+
+add_task(async function setup() {
+ // Initially trust ca.pem for SSL but not e-mail.
+ gCert = await readCertificate("ca.pem", "CT,,");
+ Assert.ok(
+ gCertDB.isCertTrusted(
+ gCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_SSL
+ ),
+ "Sanity check: ca.pem should be trusted for SSL"
+ );
+ Assert.ok(
+ !gCertDB.isCertTrusted(
+ gCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_EMAIL
+ ),
+ "Sanity check: ca.pem should not be trusted for e-mail"
+ );
+});
+
+// Tests the following:
+// 1. The checkboxes correctly reflect the trust set in setup().
+// 2. Accepting the dialog after flipping some of the checkboxes results in the
+// correct trust being set in the cert DB.
+add_task(async function testAcceptDialog() {
+ let win = await openEditCertTrustDialog();
+
+ let sslCheckbox = win.document.getElementById("trustSSL");
+ let emailCheckbox = win.document.getElementById("trustEmail");
+ Assert.ok(sslCheckbox.checked, "Cert should be trusted for SSL in UI");
+ Assert.ok(
+ !emailCheckbox.checked,
+ "Cert should not be trusted for e-mail in UI"
+ );
+
+ sslCheckbox.checked = false;
+ emailCheckbox.checked = true;
+
+ info("Accepting dialog");
+ win.document.getElementById("editCaCert").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ !gCertDB.isCertTrusted(
+ gCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_SSL
+ ),
+ "Cert should no longer be trusted for SSL"
+ );
+ Assert.ok(
+ gCertDB.isCertTrusted(
+ gCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_EMAIL
+ ),
+ "Cert should now be trusted for e-mail"
+ );
+});
+
+// Tests the following:
+// 1. The checkboxes correctly reflect the trust set in testAcceptDialog().
+// 2. Canceling the dialog even after flipping the checkboxes doesn't result in
+// a change of trust in the cert DB.
+add_task(async function testCancelDialog() {
+ let win = await openEditCertTrustDialog();
+
+ let sslCheckbox = win.document.getElementById("trustSSL");
+ let emailCheckbox = win.document.getElementById("trustEmail");
+ Assert.ok(!sslCheckbox.checked, "Cert should not be trusted for SSL in UI");
+ Assert.ok(emailCheckbox.checked, "Cert should be trusted for e-mail in UI");
+
+ sslCheckbox.checked = true;
+ emailCheckbox.checked = false;
+
+ info("Canceling dialog");
+ win.document.getElementById("editCaCert").cancelDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ !gCertDB.isCertTrusted(
+ gCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_SSL
+ ),
+ "Cert should still not be trusted for SSL"
+ );
+ Assert.ok(
+ gCertDB.isCertTrusted(
+ gCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_EMAIL
+ ),
+ "Cert should still be trusted for e-mail"
+ );
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_exportP12_passwordUI.js b/security/manager/ssl/tests/mochitest/browser/browser_exportP12_passwordUI.js
new file mode 100644
index 0000000000..b36690be06
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_exportP12_passwordUI.js
@@ -0,0 +1,163 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that the UI for setting the password on a to be exported PKCS #12 file:
+// 1. Correctly requires the password to be typed in twice as confirmation.
+// 2. Calculates and displays the strength of said password.
+
+/**
+ * @typedef {TestCase}
+ * @type Object
+ * @property {String} name
+ * The name of the test case for display purposes.
+ * @property {String} password1
+ * The password to enter into the first password textbox.
+ * @property {String} password2
+ * The password to enter into the second password textbox.
+ * @property {String} strength
+ * The expected strength of the password in the range [0, 100].
+ */
+
+/**
+ * A list of test cases representing various inputs to the password textboxes.
+ * @type TestCase[]
+ */
+const TEST_CASES = [
+ { name: "empty", password1: "", password2: "", strength: "0" },
+ { name: "match-weak", password1: "foo", password2: "foo", strength: "10" },
+ {
+ name: "match-medium",
+ password1: "foo123",
+ password2: "foo123",
+ strength: "60",
+ },
+ {
+ name: "match-strong",
+ password1: "fooBARBAZ 1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?一二三",
+ password2: "fooBARBAZ 1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?一二三",
+ strength: "100",
+ },
+ { name: "mismatch-weak", password1: "foo", password2: "bar", strength: "10" },
+ {
+ name: "mismatch-medium",
+ password1: "foo123",
+ password2: "bar",
+ strength: "60",
+ },
+ {
+ name: "mismatch-strong",
+ password1: "fooBARBAZ 1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?一二三",
+ password2: "bar",
+ strength: "100",
+ },
+];
+
+/**
+ * Opens the dialog shown to set the password on a PKCS #12 file being exported.
+ *
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading, with
+ * an array consisting of:
+ * 1. The window of the opened dialog.
+ * 2. The return value nsIWritablePropertyBag2 passed to the dialog.
+ */
+function openSetP12PasswordDialog() {
+ let returnVals = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag2
+ );
+ let win = window.openDialog(
+ "chrome://pippki/content/setp12password.xhtml",
+ "",
+ "",
+ returnVals
+ );
+ return new Promise((resolve, reject) => {
+ win.addEventListener(
+ "load",
+ function() {
+ executeSoon(() => resolve([win, returnVals]));
+ },
+ { once: true }
+ );
+ });
+}
+
+// Tests that the first password textbox is the element that is initially
+// focused.
+add_task(async function testFocus() {
+ let [win] = await openSetP12PasswordDialog();
+ Assert.equal(
+ win.document.activeElement,
+ win.document.getElementById("pw1"),
+ "First password textbox should have focus"
+ );
+ await BrowserTestUtils.closeWindow(win);
+});
+
+// Tests that the password strength algorithm used is reasonable, and that the
+// Accept button is only enabled if the two passwords match.
+add_task(async function testPasswordStrengthAndEquality() {
+ let [win] = await openSetP12PasswordDialog();
+ let password1Textbox = win.document.getElementById("pw1");
+ let password2Textbox = win.document.getElementById("pw2");
+ let strengthProgressBar = win.document.getElementById("pwmeter");
+
+ for (let testCase of TEST_CASES) {
+ password1Textbox.value = testCase.password1;
+ password2Textbox.value = testCase.password2;
+ // Setting the value of the password textboxes via |.value| apparently
+ // doesn't cause the oninput handlers to be called, so we do it here.
+ password1Textbox.oninput();
+ password2Textbox.oninput();
+
+ Assert.equal(
+ win.document.getElementById("setp12password").getButton("accept")
+ .disabled,
+ password1Textbox.value != password2Textbox.value,
+ "Actual and expected accept button disable state should " +
+ `match for ${testCase.name}`
+ );
+ Assert.equal(
+ strengthProgressBar.value,
+ testCase.strength,
+ `Actual and expected strength value should match for ${testCase.name}`
+ );
+ }
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+// Test that the right values are returned when the dialog is accepted.
+add_task(async function testAcceptDialogReturnValues() {
+ let [win, retVals] = await openSetP12PasswordDialog();
+ const password = "fooBAR 1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?一二三";
+ win.document.getElementById("pw1").value = password;
+ win.document.getElementById("pw2").value = password;
+ info("Accepting dialog");
+ win.document.getElementById("setp12password").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ retVals.get("confirmedPassword"),
+ "Return value should signal user confirmed a password"
+ );
+ Assert.equal(
+ retVals.get("password"),
+ password,
+ "Actual and expected password should match"
+ );
+});
+
+// Test that the right values are returned when the dialog is canceled.
+add_task(async function testCancelDialogReturnValues() {
+ let [win, retVals] = await openSetP12PasswordDialog();
+ info("Canceling dialog");
+ win.document.getElementById("setp12password").cancelDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.ok(
+ !retVals.get("confirmedPassword"),
+ "Return value should signal user didn't confirm a password"
+ );
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js b/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js
new file mode 100644
index 0000000000..4c278d6c1b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js
@@ -0,0 +1,312 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the dialog used for loading PKCS #11 modules.
+
+const { MockRegistrar } = ChromeUtils.import(
+ "resource://testing-common/MockRegistrar.jsm"
+);
+
+const gMockPKCS11ModuleDB = {
+ addModuleCallCount: 0,
+ expectedLibPath: "",
+ expectedModuleName: "",
+ throwOnAddModule: false,
+
+ addModule(moduleName, libraryFullPath, cryptoMechanismFlags, cipherFlags) {
+ this.addModuleCallCount++;
+ Assert.equal(
+ moduleName,
+ this.expectedModuleName,
+ "addModule: Name given should be what's in the name textbox"
+ );
+ Assert.equal(
+ libraryFullPath,
+ this.expectedLibPath,
+ "addModule: Path given should be what's in the path textbox"
+ );
+ Assert.equal(
+ cryptoMechanismFlags,
+ 0,
+ "addModule: No crypto mechanism flags should be passed"
+ );
+ Assert.equal(cipherFlags, 0, "addModule: No cipher flags should be passed");
+
+ if (this.throwOnAddModule) {
+ throw new Error(`addModule: Throwing exception`);
+ }
+ },
+
+ deleteModule(moduleName) {
+ Assert.ok(false, `deleteModule: should not be called`);
+ },
+
+ getInternal() {
+ throw new Error("not expecting getInternal() to be called");
+ },
+
+ getInternalFIPS() {
+ throw new Error("not expecting getInternalFIPS() to be called");
+ },
+
+ listModules() {
+ throw new Error("not expecting listModules() to be called");
+ },
+
+ get canToggleFIPS() {
+ throw new Error("not expecting get canToggleFIPS() to be called");
+ },
+
+ toggleFIPSMode() {
+ throw new Error("not expecting toggleFIPSMode() to be called");
+ },
+
+ get isFIPSEnabled() {
+ throw new Error("not expecting get isFIPSEnabled() to be called");
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPKCS11ModuleDB"]),
+};
+
+const gMockPromptService = {
+ alertCallCount: 0,
+ expectedText: "",
+ expectedWindow: null,
+
+ alert(parent, dialogTitle, text) {
+ this.alertCallCount++;
+ Assert.equal(
+ parent,
+ this.expectedWindow,
+ "alert: Parent should be expected window"
+ );
+ Assert.equal(dialogTitle, null, "alert: Title should be null");
+ Assert.equal(
+ text,
+ this.expectedText,
+ "alert: Actual and expected text should match"
+ );
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]),
+};
+
+var gMockPKCS11CID = MockRegistrar.register(
+ "@mozilla.org/security/pkcs11moduledb;1",
+ gMockPKCS11ModuleDB
+);
+var gMockPromptServiceCID = MockRegistrar.register(
+ "@mozilla.org/embedcomp/prompt-service;1",
+ gMockPromptService
+);
+
+var gMockFilePicker = SpecialPowers.MockFilePicker;
+gMockFilePicker.init(window);
+
+var gTempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+gTempFile.append("browser_loadPKCS11Module_ui-fakeModule");
+
+registerCleanupFunction(() => {
+ gMockFilePicker.cleanup();
+ MockRegistrar.unregister(gMockPKCS11CID);
+ MockRegistrar.unregister(gMockPromptServiceCID);
+});
+
+function resetCallCounts() {
+ gMockPKCS11ModuleDB.addModuleCallCount = 0;
+ gMockPromptService.alertCallCount = 0;
+}
+
+/**
+ * Opens the dialog shown to load a PKCS #11 module.
+ *
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading, with
+ * the window of the opened dialog.
+ */
+function openLoadModuleDialog() {
+ let win = window.openDialog(
+ "chrome://pippki/content/load_device.xhtml",
+ "",
+ ""
+ );
+ return new Promise(resolve => {
+ win.addEventListener(
+ "load",
+ function() {
+ executeSoon(() => resolve(win));
+ },
+ { once: true }
+ );
+ });
+}
+
+/**
+ * Presses the browse button and simulates interacting with the file picker that
+ * should be triggered.
+ *
+ * @param {window} win
+ * The dialog window.
+ * @param {Boolean} cancel
+ * If true, the file picker is canceled. If false, gTempFile is chosen in
+ * the file picker and the file picker is accepted.
+ */
+async function browseToTempFile(win, cancel) {
+ gMockFilePicker.showCallback = () => {
+ gMockFilePicker.setFiles([gTempFile]);
+
+ if (cancel) {
+ info("MockFilePicker returning cancel");
+ return Ci.nsIFilePicker.returnCancel;
+ }
+
+ info("MockFilePicker returning OK");
+ return Ci.nsIFilePicker.returnOK;
+ };
+
+ info("Pressing browse button");
+ win.document.getElementById("browse").doCommand();
+ await TestUtils.topicObserved("LoadPKCS11Module:FilePickHandled");
+}
+
+add_task(async function testBrowseButton() {
+ let win = await openLoadModuleDialog();
+ let pathBox = win.document.getElementById("device_path");
+ let originalPathBoxValue = "expected path if picker is canceled";
+ pathBox.value = originalPathBoxValue;
+
+ // Test what happens if the file picker is canceled.
+ await browseToTempFile(win, true);
+ Assert.equal(
+ pathBox.value,
+ originalPathBoxValue,
+ "Path shown should be unchanged due to canceled picker"
+ );
+
+ // Test what happens if the file picker is not canceled.
+ await browseToTempFile(win, false);
+ Assert.equal(
+ pathBox.value,
+ gTempFile.path,
+ "Path shown should be same as the one chosen in the file picker"
+ );
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+function testAddModuleHelper(win, throwOnAddModule) {
+ resetCallCounts();
+ gMockPKCS11ModuleDB.expectedLibPath = gTempFile.path;
+ gMockPKCS11ModuleDB.expectedModuleName = "test module";
+ gMockPKCS11ModuleDB.throwOnAddModule = throwOnAddModule;
+
+ win.document.getElementById("device_name").value =
+ gMockPKCS11ModuleDB.expectedModuleName;
+ win.document.getElementById("device_path").value =
+ gMockPKCS11ModuleDB.expectedLibPath;
+
+ info("Accepting dialog");
+ win.document.getElementById("loaddevice").acceptDialog();
+}
+
+add_task(async function testAddModuleSuccess() {
+ let win = await openLoadModuleDialog();
+
+ testAddModuleHelper(win, false);
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.equal(
+ gMockPKCS11ModuleDB.addModuleCallCount,
+ 1,
+ "addModule() should have been called once"
+ );
+ Assert.equal(
+ gMockPromptService.alertCallCount,
+ 0,
+ "alert() should never have been called"
+ );
+});
+
+add_task(async function testAddModuleFailure() {
+ let win = await openLoadModuleDialog();
+ gMockPromptService.expectedText = "Unable to add module";
+ gMockPromptService.expectedWindow = win;
+
+ // The exception we throw in addModule is first reported as an uncaught
+ // exception by XPConnect before an exception is propagated to the actual
+ // caller.
+ expectUncaughtException(true);
+
+ testAddModuleHelper(win, true);
+ expectUncaughtException(false);
+ // If adding a module fails, the dialog will not close. As such, we have to
+ // close the window ourselves.
+ await BrowserTestUtils.closeWindow(win);
+
+ Assert.equal(
+ gMockPKCS11ModuleDB.addModuleCallCount,
+ 1,
+ "addModule() should have been called once"
+ );
+ Assert.equal(
+ gMockPromptService.alertCallCount,
+ 1,
+ "alert() should have been called once"
+ );
+});
+
+add_task(async function testCancel() {
+ let win = await openLoadModuleDialog();
+ resetCallCounts();
+
+ info("Canceling dialog");
+ win.document.getElementById("loaddevice").cancelDialog();
+
+ Assert.equal(
+ gMockPKCS11ModuleDB.addModuleCallCount,
+ 0,
+ "addModule() should never have been called"
+ );
+ Assert.equal(
+ gMockPromptService.alertCallCount,
+ 0,
+ "alert() should never have been called"
+ );
+
+ await BrowserTestUtils.windowClosed(win);
+});
+
+async function testModuleNameHelper(moduleName, acceptButtonShouldBeDisabled) {
+ let win = await openLoadModuleDialog();
+ resetCallCounts();
+
+ info(`Setting Module Name to '${moduleName}'`);
+ let moduleNameBox = win.document.getElementById("device_name");
+ moduleNameBox.value = moduleName;
+ // this makes this not a great test, but it's the easiest way to simulate this
+ moduleNameBox.onchange();
+
+ let dialogNode = win.document.querySelector("dialog");
+ Assert.equal(
+ dialogNode.getAttribute("buttondisabledaccept"),
+ acceptButtonShouldBeDisabled ? "true" : "", // it's a string
+ `dialog accept button should ${
+ acceptButtonShouldBeDisabled ? "" : "not "
+ }be disabled`
+ );
+
+ return BrowserTestUtils.closeWindow(win);
+}
+
+add_task(async function testEmptyModuleName() {
+ await testModuleNameHelper("", true);
+});
+
+add_task(async function testReservedModuleName() {
+ await testModuleNameHelper("Root Certs", true);
+});
+
+add_task(async function testAcceptableModuleName() {
+ await testModuleNameHelper("Some Module Name", false);
+});
diff --git a/security/manager/ssl/tests/mochitest/browser/ca.pem b/security/manager/ssl/tests/mochitest/browser/ca.pem
new file mode 100644
index 0000000000..48031d478c
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUb2Np/S2xMtQR+BvitAuzKtjWxMswDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYD
+VR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBPEDwwrdd6h+SHkc0J+p4Ik/yc
+aoLWNdGsVD4hJYa0IOgNh6Z/GyhTXM9hr/q0f7fewjfNhXi/nrXNcW5w4FJERhHp
+HvZO6Z08i8CrVYSiijFcLf6ClK4e163j0LXUutBWY2PCf4TDS+fnF9PJmle0kPLU
+IxqptfSSoCjLuWyYyRmjZIdNCtrNCU9g85SDaUO6l79vBm0TYOLhOiPBN0F7DWXo
+JlOZKWEco6qqS22yIM1r/5KNc5ekPTD+UJFh7qprAq0riEfR38DYJmUq3w07q5Tb
+HQdG8JHEtemi7dHr/WoJY18MLZaF7BSLAwBWmBWzx8Lj2V+/4TP4NtRWkL7y
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/ca.pem.certspec b/security/manager/ssl/tests/mochitest/browser/ca.pem.certspec
new file mode 100644
index 0000000000..6660f5d478
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem b/security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem
new file mode 100644
index 0000000000..698b70da88
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfmgAwIBAgIUcepT9pQ4IQkGYiLxzM+rfwju1TYwDQYJKoZIhvcNAQEL
+BQAwQTEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEV
+MBMGA1UECwwMSW50ZXJtZWRpYXRlMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCcxJTAjBgNVBAMMHGNsaWVudCBjZXJ0IHZpYSBpbnRlcm1lZGlh
+dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
+BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
+p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
+7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
+kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
+aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
+Ne2NAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUA
+A4IBAQAcVbcUEeT+hYt/z82f9Ut81G6QKtq5lr/1ifSkLqV6t7P5chDt7bpujsFw
+ZI4jxHLZLL3M1znCSMBNseNRGWLW8cGlapOsO2KDNQdXbHcNJpLxETiWMRp/62Ii
+jW2/rdp4wYekjp/lYy6qQDj85N2Zc+zimqz1ELIPHQyxGQUy5uQ78+sE/eLIR8A4
+XvYwyd1pIatfYHkc2pb8LeZAAtSQ9lFQ2sJiBMac2n68wAOj1Dq3AG9MdcBuGMsh
+TiA7k4mdBiQywg8QG4GmXoP1j6QDJG0o5/8hIpbo95cmZ81Jye3TsZ8Sbmz3LR1J
+6lOk2w1lAfxwCim2cMMZ44StOBsq
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem.certspec b/security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem.certspec
new file mode 100644
index 0000000000..cab2448889
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/client-cert-via-intermediate.pem.certspec
@@ -0,0 +1,3 @@
+issuer:/CN=Temporary Certificate Authority/OU=Intermediate
+subject:client cert via intermediate
+extension:extKeyUsage:clientAuth
diff --git a/security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem b/security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem
new file mode 100644
index 0000000000..a9052d4611
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSDCCAjCgAwIBAgIUUZw/zP5GmSytJJWBwEzzqZzjcTYwDQYJKoZIhvcNAQEL
+BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAw
+MFowKzEpMCcGA1UEAwwgY2xpZW50IGNlcnQgd2l0aCBPQ1NQU2lnbmluZyBla3Uw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ
+PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH
+9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw
+4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86
+exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0
+ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N
+AgMBAAGjITAfMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDCTANBgkqhkiG
+9w0BAQsFAAOCAQEAFuiPgjMg34f/TNocFSU4Qc3hPod0s0W7oH53wp8I3kJXsPGs
+/P16w6YJ5dd5R9JZh6xBq63OnhFjS+j1Z6+NXalZrgSD2Q8FVNFS1z3P7bR+GSQ+
+r8ltlqkNV+Wu+Hk6TvfkrZhDX3VQ1IXtXXDQ0SuWLvczrPK3h+GCBE/LG1KybyBw
+aHHl8aqXSd7eXcwJF5/hLQlaGZlHHxVh451WHrE1i8/kX+9GdyNwlAXMP9JySqei
+YRwQPMa7MPDM5qkmFhlDh4+LWj8ugssv6n+6L5gFRNm6Db52aeNR17H2vezI7OUO
+5u0gDZ8IG4XwS1u1IRPxNtK5ZT8uV+BqhOPmhg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem.certspec b/security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem.certspec
new file mode 100644
index 0000000000..5cbd5af8f0
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/client-cert-with-ocsp-signing.pem.certspec
@@ -0,0 +1,3 @@
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+subject:client cert with OCSPSigning eku
+extension:extKeyUsage:clientAuth,OCSPSigning
diff --git a/security/manager/ssl/tests/mochitest/browser/code-ee.pem b/security/manager/ssl/tests/mochitest/browser/code-ee.pem
new file mode 100644
index 0000000000..2e5941318a
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/code-ee.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyDCCAbCgAwIBAgIUUaEQHoacA0ankeJpt9iGsfyV7rYwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEjEQMA4GA1UEAwwHY29kZS1lZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODY
+H72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk
+27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9
+0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMM
+kd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaL
+L+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMXMBUwEwYDVR0lBAwwCgYI
+KwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAADhEW+xHUzkTkX1/RW4aODeW+NM
+aSI20y3CtLtF2BDcl08YoCCnPVdbBG/DSbKb+eZecgvKWgHGOPKFRWA4YXm8TXXW
+naVEWTDbbspVKjC6GzViuy2CsnNS5Bb5mhnGiMTI/zFvnfE+OwnfPFL+FAsLIaOm
+OhyWU6/pwWN3hQytWHK6+QI22A58nVc/vLiyLMfWnn/8Akkolob3gFj0hOZaXYbn
+mSSvBmWVTjq/7PfZLb/SD0VfGhbZMx7BebJGkUaikICS1w5F3bnM9ToRzkshhsBw
+9nI7Kq8kff8PHBSaVWR36Ygix157D7bMiGuZ5ZLpXAayYcXhGQhhIZs2+lE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/code-ee.pem.certspec b/security/manager/ssl/tests/mochitest/browser/code-ee.pem.certspec
new file mode 100644
index 0000000000..93f9a84265
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/code-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:code-ee
+extension:extKeyUsage:codeSigning
diff --git a/security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem b/security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem
new file mode 100644
index 0000000000..f4ff304c1c
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgIUa3NpO02kyKRl+mnC7LrTUIC0W8EwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKZXhwaXJlZC1jYTAiGA8yMDE5MTEyODAwMDAwMFoYDzIw
+MjIwMjA1MDAwMDAwWjAdMRswGQYDVQQDDBJlZS1mcm9tLWV4cGlyZWQtY2EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT
+2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzV
+JJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8N
+jf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCA
+BiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVh
+He4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMB
+AAEwDQYJKoZIhvcNAQELBQADggEBAB7gNu/A0HmfIZQmKxaZno6txhlnzqU3jtCy
+TwmiYWpqCJgjNHdKKaPDNTILaDtcPtdG55JqbIxbv3mpHfbiwy9XLSYT56V3/XJy
+buMpUpXYTpsm+bXfU6aqsndiGwwuSSSrKP0MHi3R9hz/A+3hOqldu+a0d6zs4ByK
+YSjAvQwORGgLf0cLRY0CIroiMttCC2x1whvwVl4jWsa3p0wciRstsIwHUxbhcGJC
+R8bKFvK0VbNxcvyU3zwWwFnN+3ACBJT8ucI+MStFtR4oCILvvo0kL1O9C+tpWhEA
+9b7J+h9WJVroYvfDIjvuYrdkeBhaQaSk0b1WvO+0ZGKGstLFC/c=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem.certspec b/security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem.certspec
new file mode 100644
index 0000000000..3e280fc4fc
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ee-from-expired-ca.pem.certspec
@@ -0,0 +1,2 @@
+issuer:expired-ca
+subject:ee-from-expired-ca
diff --git a/security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem b/security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem
new file mode 100644
index 0000000000..890292763c
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAgIUd3uwPuHZt768G7dbGuv/MlnjS4YwDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMdW50cnVzdGVkLWNhMCIYDzIwMTkxMTI4MDAwMDAwWhgP
+MjAyMjAyMDUwMDAwMDBaMB8xHTAbBgNVBAMMFGVlLWZyb20tdW50cnVzdGVkLWNh
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+jQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCE43NUMM7jR2Xic3/8RdN8axQxqqnA
+YlRedr8BaEVDdiwneEG17EEGil1NU/a2DyBn6OC0I9Xx7NaB4ukYcJqheaczMHkG
+1SvpddaNmjteAtdfc+vQ2ktvwvVAi+wQDtV23Bz5kgYvONRHhk2NgTb/HPrAyG3x
+jg40Gi6wePb2vrzXQUxat88AEqh7BjCkeyJSXu/o56Pxo+TxHMX8QlZhaFT22Kj+
+J5u+WXEYygNdeTwDfIsyh0a6R/Eko+onzcT4JkNdFwI5u5bmoDQn1SjW1MBCmhJy
+Css4M/wP5BY4RzgbKA2uDdVQ2M2xPg2IeGi3bS4qwlUNNPm1iOx8WlZK
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem.certspec b/security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem.certspec
new file mode 100644
index 0000000000..833e1a23a6
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ee-from-untrusted-ca.pem.certspec
@@ -0,0 +1,2 @@
+issuer:untrusted-ca
+subject:ee-from-untrusted-ca
diff --git a/security/manager/ssl/tests/mochitest/browser/email-ee.pem b/security/manager/ssl/tests/mochitest/browser/email-ee.pem
new file mode 100644
index 0000000000..eb51c01b1b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/email-ee.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUZcgkXcHN7EQAuaIttjnYPdQFvOIwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEzERMA8GA1UEAwwIZW1haWwtZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg
+2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ
+5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQ
+PdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGj
+DJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8W
+iy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjFzAVMBMGA1UdJQQMMAoG
+CCsGAQUFBwMEMA0GCSqGSIb3DQEBCwUAA4IBAQBSMjBmd3FC9Q/AOByGmK8GhlFk
+XhlBYmeVmxDJVDN0Z3OIcZUK1DWUl9M7hLJAUMbanng0jF98npAO1exDYx5ECyhV
+SEe+KjfsQuPj7AS0zb60Uf/4w7b5dFW3y1pKo2ruEoPWvjLzR4IYfkc5p+utc1VT
+0m568rEnF8dDGF19/FpsVjnzfwa8vmafXyXrogocOjk3qxRpx2wUqotbDwz6qDA1
+N/+LeE5+WHEgVDPF22BBECf/iPi1mUQQLURoUbQ499ELSZIRfIzI/MQUHBrF4Cx1
+TRnTDRhtzWvrcSaDBplLZzxTTWNhExwQUlg2rdFAPd9rdLuraimsLcly8xUC
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/email-ee.pem.certspec b/security/manager/ssl/tests/mochitest/browser/email-ee.pem.certspec
new file mode 100644
index 0000000000..82e3296706
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/email-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:email-ee
+extension:extKeyUsage:emailProtection
diff --git a/security/manager/ssl/tests/mochitest/browser/expired-ca.pem b/security/manager/ssl/tests/mochitest/browser/expired-ca.pem
new file mode 100644
index 0000000000..e019b37127
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/expired-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUS6xUkMzG2REizII2g+VecO/KqX8wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDExMDEwMTAw
+MDAwMFowFTETMBEGA1UEAwwKZXhwaXJlZC1jYTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswDAYDVR0TBAUw
+AwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBABJdjrt25wocw4aP
+eR1kZuu3WS0bKfuvhQQPFkAG+HYSC5eu0OriQCRlxn+qHY7du9dePcD6DTMDIVDW
+r+oBJ9BwCEREyTcV8AEaHdcTAakXOMhq6OOltl6HUu3lSlqRslzAhtl1chM0P8m1
+Aj+ceOkCFHvnsDd+zcSP75u8zzJKypSWQwAg/i5S0BNLOWYarPiczuYi4HAOpwtX
+QqlmDNMYySqPFfH72BuQdCLuviBXmMP8/kOouBNP4ti06RR88XgqfoL/jV4gkIM7
+92hXt0WpS/QffjWzLaej39YhW4pMZ+hF4bk9nUCtN/MHtg8WDj1CgfSJZegrl28W
+3riMotA=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/expired-ca.pem.certspec b/security/manager/ssl/tests/mochitest/browser/expired-ca.pem.certspec
new file mode 100644
index 0000000000..15bdcd7d73
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/expired-ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:expired-ca
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+validity:20100101-20110101
diff --git a/security/manager/ssl/tests/mochitest/browser/has-cn.pem b/security/manager/ssl/tests/mochitest/browser/has-cn.pem
new file mode 100644
index 0000000000..f334d5a887
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-cn.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAgIUXfOLVuh26E15Y2GJNpqg+kEUVBEwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowNzEMMAoGA1UEAwwDRm9vMQwwCgYDVQQLDANCYXIxDDAKBgNVBAoMA0Jh
+ejELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAI8sysFQZh4G
+EZKpcpOiGC0KLdbzkYAoALxjgvI+WPgOQBQBHSLGGTeIAe677xToSO6E/n9xocMh
+62YRiZc5PGpUb+T7hinZ1pq+10SXN5ERZKJiKbDfp4iUStySiRs3OL0tNpnjtcVv
+gIMZRcGr50vWBebe8fCRa/TFA3TilspiTjmRZCS6dUO7YHW8aMPWfG4gaSBF8E+6
+JCoK7YL1R/JxP4y7uAoU5+xfDBmC/vOUekUYZOH8w4Vs7dijC+weUU4zhe2L+QMb
+OC1SyG1/MZ9Orzetz3tYGZ0shZTONn/wjAnzRrUGRUmAl+LVlW+Wxs1YL3TjKqTr
+0QZaPOUVoms=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/has-cn.pem.certspec b/security/manager/ssl/tests/mochitest/browser/has-cn.pem.certspec
new file mode 100644
index 0000000000..a4a0fcb5fa
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-cn.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/CN=Foo/OU=Bar/O=Baz/C=US
diff --git a/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem b/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem
new file mode 100644
index 0000000000..e4cf5a14fd
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICijCCAXKgAwIBAgIBCjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJjYTAi
+GA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAAMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1
+aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/we
+adA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSS
+pH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62W
+YVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauR
+CE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqG
+SIb3DQEBCwUAA4IBAQAMkmx5NF2dju8/ckdgRK2M3fvoyEmBAYLOjWNh5ZfBX7VL
+vL6bjzwcwza41feaDwgZbIWzzqKwVJhkOb9ULFMFlkEO0k2WhvTpEc/ELJnE1uCr
+qKokl0b5BdllNGQQ9tSzhAwWumjfkhmVRZXziawWIiquA2UzowsdQX7Qf675Ae1e
+B9WJvTxfxfSscON3xKAThOcMbjs5G9SaIp+C43qRJMn2KBsbu0qmWuMXG7Iy7Dbd
+ak9CD2QG9tKDYOgjcvZRxdyErfjfIRoz3/4fiac7Ca6ooSc+TCF7va1yHNqhaYcH
+VNTl7xyArMu5uPXtcLwEBudlJgPNuLy5A3HgChh4
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem.certspec b/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem.certspec
new file mode 100644
index 0000000000..6346f7b83a
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-empty-subject.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:
+serialNumber:10
diff --git a/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem b/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem
new file mode 100644
index 0000000000..85bd93c62a
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqjCCAZKgAwIBAgIUQ1K/9yfCiqPsZYUo+7EBrddrWTowDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAJXk
+OiIktljwCkbDVVSyHlMX/kXOlzt4sVjWfL/YyFIaNVuiUpOYfEXlYFUc7nX8PXsL
+a4RvATrJ90fUcGxHqWdqJr19zrWW5co+vjDNFMTFFltootym0fQocq55JvVUK9ml
+BmZ+B60I82gI7L/M1NCPkylNvrG/UdD034pqY4ppgu6z/9lH6P6EpXZPseo6Ofx8
+tpSeTfYdtebvBbAkWmel56jzyN6C40ddIIjVHYKfLFgFsuavVhbwMm0AB1dFCnGQ
+06x1QGsAthQ7C7q7mYtoe+TZ6sHahUofE4gNJyQprIyBn/Itae/H6qO6qBewoYWX
+cBATil0Vqe2ZQ7FS5nk=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem.certspec b/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem.certspec
new file mode 100644
index 0000000000..cc1b668a63
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-non-empty-subject.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/C=US
diff --git a/security/manager/ssl/tests/mochitest/browser/has-o.pem b/security/manager/ssl/tests/mochitest/browser/has-o.pem
new file mode 100644
index 0000000000..9e4d7e3a28
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-o.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuDCCAaCgAwIBAgIUL+2cA4H9fH3BS4yDvSfNqAx+SDkwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGzEMMAoGA1UECgwDQmF6MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEABlpU9dWi5TSGL0CISRugnOlpBATWlZbB2UYqN4H8n0R9mA28
+ss8F7+dzWq9yldVP5shpCqoHYbKgvcKwM7ki7l3QjaKvk4jqkxkp+0qw7LlNreFB
+9xH86vn0zcu/9PUYVjAAS2B5pObLhf0RpdS9km026WpL11o298hbuR8sOeADAPwh
+yqxrZz8i+ZCzBzwgNGGIwcgfpIRSQD+4rbpN5h2r3HmZupRuChA5rGR/z3xywD4J
+/81ELM7raPXtq15RoUUc/dR4rIqgDwI8zOL4zndw5Eiuxmw79BnsW7ziXSgZw2rJ
+vpxukd+42dBTnPEPHlFVZlnjn4zd97YwnuKXgw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/has-o.pem.certspec b/security/manager/ssl/tests/mochitest/browser/has-o.pem.certspec
new file mode 100644
index 0000000000..f7cc3ffc73
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-o.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/O=Baz/C=US
diff --git a/security/manager/ssl/tests/mochitest/browser/has-ou.pem b/security/manager/ssl/tests/mochitest/browser/has-ou.pem
new file mode 100644
index 0000000000..dfb8b2266b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-ou.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAgIUB17J0zfbRaw+XdVWt9xzDjrY3o0wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowKTEMMAoGA1UECwwDQmFyMQwwCgYDVQQKDANCYXoxCzAJBgNVBAYTAlVT
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+jQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB83qFAuJObc3l0cOKL6NW2krNxkt/X
+ynRDZV1oVJ8Bw5vKb4PX8owP/answOwYsBK+Sy0g2+Y/JvhaI+PD/i/2MjwdP8qu
+vRXKGbbtfgKqfBonwZl1/6BNNS65Mb0Z1VLdE1Ah79afpHamppD07O7cqA+Hw2qj
+9mnoDoAiCBKDdKHDp+wLs7mUh8JSIOb0ZWkCqtED5l81KUB4T0bOI0HCuS15Zu+E
+wWCGw0w3Un0W0l6sUVIE23J1exo6qJKk9gFWl3mOOZLOCT4Rgpvwyx13PpzU4JqS
+vrxcuwwPKtEo7+smxl3DSpU/Vq0cuYbc2aS1xlCU2Vpl1VBNmao5KKAo
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/has-ou.pem.certspec b/security/manager/ssl/tests/mochitest/browser/has-ou.pem.certspec
new file mode 100644
index 0000000000..8879dabf51
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/has-ou.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:/OU=Bar/O=Baz/C=US
diff --git a/security/manager/ssl/tests/mochitest/browser/head.js b/security/manager/ssl/tests/mochitest/browser/head.js
new file mode 100644
index 0000000000..af3c62ca43
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/head.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+var gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+/**
+ * List of certs imported via readCertificate(). Certs in this list are
+ * automatically deleted from the cert DB when a test including this head file
+ * finishes.
+ * @type nsIX509Cert[]
+ */
+var gImportedCerts = [];
+
+registerCleanupFunction(() => {
+ for (let cert of gImportedCerts) {
+ gCertDB.deleteCertificate(cert);
+ }
+});
+
+// This function serves the same purpose as the one defined in head_psm.js.
+function pemToBase64(pem) {
+ return pem
+ .replace(/-----BEGIN CERTIFICATE-----/, "")
+ .replace(/-----END CERTIFICATE-----/, "")
+ .replace(/[\r\n]/g, "");
+}
+
+/**
+ * Given the filename of a certificate, returns a promise that will resolve with
+ * a handle to the certificate when that certificate has been read and imported
+ * with the given trust settings.
+ *
+ * Certs imported via this function will automatically be deleted from the cert
+ * DB once the calling test finishes.
+ *
+ * @param {String} filename
+ * The filename of the certificate (assumed to be in the same directory).
+ * @param {String} trustString
+ * A string describing how the certificate should be trusted (see
+ * `certutil -A --help`).
+ * @return {Promise}
+ * A promise that will resolve with a handle to the certificate.
+ */
+function readCertificate(filename, trustString) {
+ return OS.File.read(getTestFilePath(filename)).then(
+ data => {
+ let decoder = new TextDecoder();
+ let pem = decoder.decode(data);
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let base64 = pemToBase64(pem);
+ certdb.addCertFromBase64(base64, trustString);
+ let cert = certdb.constructX509FromBase64(base64);
+ gImportedCerts.push(cert);
+ return cert;
+ },
+ error => {
+ throw error;
+ }
+ );
+}
+
+/**
+ * Asynchronously opens the certificate manager.
+ *
+ * @return {Window} a handle on the opened certificate manager window
+ */
+async function openCertManager() {
+ let win = window.openDialog("chrome://pippki/content/certManager.xhtml");
+ return new Promise((resolve, reject) => {
+ win.addEventListener(
+ "load",
+ function() {
+ executeSoon(() => resolve(win));
+ },
+ { once: true }
+ );
+ });
+}
diff --git a/security/manager/ssl/tests/mochitest/browser/intermediate.pem b/security/manager/ssl/tests/mochitest/browser/intermediate.pem
new file mode 100644
index 0000000000..dd9c5ec6c5
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/intermediate.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIUf/ZI9pFYXbeWG/5AXJ9EJIgxBtcwDQYJKoZIhvcNAQEL
+BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAw
+MFowQTEoMCYGA1UEAwwfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEV
+MBMGA1UECwwMSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
+BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
+a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
+uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
+9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
+mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsG
+A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKO/vWs1AonZSTOAG+k2uA+HF
+m6clrXdQxq3cbXG/bHT1c1lMNLu8p3HYojZq4S6394iNDFBKR/EV0RIdYRVW8NOJ
+zfEFK4HIh6ReH5JBNbNqGGBbxBu1OR4UGXiTiKJh8VAPcuKhb01lAlkmmLWwE0em
+rX3YaC7syGCQX/7Il3W8Kt4xvPjTkx8UX6+SiIezaw9thUAFvynaYK+1uw3/3W31
+ObPIQke9dKhth/3+87zrmiZI95CdCpWXzsL48OJseU53HpEnA3Du0CHQYLWrAS+T
+fQIyV1qGMIXYRP0QW4eBVfVlKfACYZqRMOozR5AXIBp3ns5t3MXlyWS/0uAfJA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/intermediate.pem.certspec b/security/manager/ssl/tests/mochitest/browser/intermediate.pem.certspec
new file mode 100644
index 0000000000..a562814041
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/intermediate.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+subject:/CN=Temporary Certificate Authority/OU=Intermediate
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/mochitest/browser/invalid.pem b/security/manager/ssl/tests/mochitest/browser/invalid.pem
new file mode 100644
index 0000000000..dc8c0730fc
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/invalid.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwDCCAaigAwIBAgIUaXqQ23/rq8JrbOwIIDACtHsydH0wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEjEQMA4GA1UEAwwHaW52YWxpZDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODY
+H72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk
+27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9
+0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMM
+kd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaL
+L+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMPMA0wCwYDVR0PBAQDAgEG
+MA0GCSqGSIb3DQEBCwUAA4IBAQAUtQnvaYAoyfHVMIPYiEctg/dBhiqjp1ZYUbQO
+nIuHETKtzTLUGJOSNFBIAaDcGHvJH7K4Ldp74+73EwDpGlK/wOQ4Of57pfNlYuCe
+PQ9CGRKsEGSPsE6w9EstcbnEvqZJmKFY+YEJU936KqJOA052EMBnW3dy2sXwqyX5
+zzNKSC5+3gLGn2rk0Xa1CIyZpNSGLP5Q801iO7bXXkKG5rOV0FTfR1mu39vnu/RI
+vO7BdLSbGPfdAv0io6aFLYw3kP5vSLFzUAoYHxrPokIoEM7pZtlSfECAXengcfis
+L5WYqNQ3Y+XXf02VaRhEGnyL1MMmPGVpvBwWAJnR97YSfylx
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/invalid.pem.certspec b/security/manager/ssl/tests/mochitest/browser/invalid.pem.certspec
new file mode 100644
index 0000000000..71a1707c35
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/invalid.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:invalid
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/mochitest/browser/longOID.pem b/security/manager/ssl/tests/mochitest/browser/longOID.pem
new file mode 100644
index 0000000000..a966355b2d
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/longOID.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIESjCCAzKgAwIBAgIUUUlZgo9gztazitkyNqsantBeQO4wDQYJKoZIhvcNAQEL
+BQAwEzERMA8GA1UEAwwITG9uZyBPSUQwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIy
+MDIwNTAwMDAwMFowEzERMA8GA1UEAwwITG9uZyBPSUQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk
+e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg
+KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI
+YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi
+lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL
+HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjggGQMIIBjDAM
+BgNVHRMEBTADAQH/MIIBegYDVR0gBIIBcTCCAW0wggFpBoIBZSqD3OuTf4Pc65N/
+g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zr
+k3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D
+3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuT
+f4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc
+65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/
+g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zr
+k3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D3OuTf4Pc65N/g9zrk3+D
+3OuTf4Pc65N/ATANBgkqhkiG9w0BAQsFAAOCAQEAcsD8I4kt/jjepOgtZMI38IB5
+wRhrnfvGvPE7/1mWkQyz2tRRdbMOrhLrWzMHjZwQtwWmEeQczPVeXHEyt9Sz+v0/
+rghH+32VAPI6fDYzj9KNBn60sNiLCBlOucegpW/Dechcf25O/xhF6G9ljU2RIbpj
+BAwTGd9gIYOpuAYGprht9d9smznPkO5qBpU7GV1SOzzhAldFYOMuYvjpgvtL7UXJ
+2FhaWxyC11Sw1aUdm8V93Mf4rs8RzizjY/4pKyl6yRarNXx7lP6qf95fWi0A3LYT
+3giV0e6NKrWjGLl1vnj8919wTzsh3OsVaAKDQNMz3I9/ENdDHMFl0WpAWjfQpg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/longOID.pem.certspec b/security/manager/ssl/tests/mochitest/browser/longOID.pem.certspec
new file mode 100644
index 0000000000..c3c08ac84b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/longOID.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Long OID
+subject:Long OID
+extension:basicConstraints:cA,
+extension:certificatePolicies:1.2.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.999999999.1
diff --git a/security/manager/ssl/tests/mochitest/browser/md5-ee.pem b/security/manager/ssl/tests/mochitest/browser/md5-ee.pem
new file mode 100644
index 0000000000..39fab2dbea
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/md5-ee.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrjCCAZagAwIBAgIUCR+2dzKgSt0CBU86EgRdJIa/72owDQYJKoZIhvcNAQEE
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowETEPMA0GA1UEAwwGbWQ1LWVlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3DQEBBAUAA4IB
+AQArfDtm5sEuzsPZps2oZhvrOYRViP0sRIAbAjuJVnAMA/7xLzI3LweXOlQJHBh3
+m3mrPUMAXfr4xJ5XjGySqHBUtFat9EBl/0bynd67sCMA7UOf51GC4ABOPAV0CkDj
+/FNqL/KSt8WB90FW+ZKnt1ojKikMIjmPjxDaaHj7KZVBQ2KtBEz1Igt5Bvbrp2AM
+NvjyhUCN4/z4NDPPbzkeDKYC7vmvYhN1Cs/73Jp3A0LU4z0gIyrWi6a7YEAVBzhI
+vJJ98U8AQQMm+iiIDGZMoAZyvEFouofF9te4xMHStUnYfa1jLY93dL1TuXrWvKB0
+pWg4REoQuFZUE8GS/LczW5xX
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/md5-ee.pem.certspec b/security/manager/ssl/tests/mochitest/browser/md5-ee.pem.certspec
new file mode 100644
index 0000000000..279c158026
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/md5-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:md5-ee
+signature:md5WithRSAEncryption
diff --git a/security/manager/ssl/tests/mochitest/browser/moz.build b/security/manager/ssl/tests/mochitest/browser/moz.build
new file mode 100644
index 0000000000..51ea96f9e2
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/moz.build
@@ -0,0 +1,41 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+BROWSER_CHROME_MANIFESTS += ["browser.ini"]
+
+# Temporarily disabled. See bug 1256495.
+# (Note that when this gets enabled, some extra work will have to happen so
+# that the mochitest harness knows where to get the generated certificates -
+# right now it assumes they're in the source directory, which isn't the case
+# when they're automatically generated.)
+# test_certificates = (
+# 'ca.pem',
+# 'client-cert-via-intermediate.pem',
+# 'client-cert-with-ocsp-signing.pem',
+# 'code-ee.pem',
+# 'ee-from-expired-ca.pem',
+# 'ee-from-untrusted-ca.pem',
+# 'email-ee.pem',
+# 'expired-ca.pem',
+# 'has-cn.pem',
+# 'has-empty-subject.pem',
+# 'has-non-empty-subject.pem',
+# 'has-o.pem',
+# 'has-ou.pem',
+# 'intermediate.pem',
+# 'invalid.pem',
+# 'longOID.pem',
+# 'md5-ee.pem',
+# 'pgo-ca-all-usages.pem',
+# 'pgo-ca-regular-usages.pem',
+# 'revoked.pem',
+# 'ssl-ee.pem',
+# 'unknown-issuer.pem',
+# 'untrusted-ca.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem b/security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem
new file mode 100644
index 0000000000..b67283580b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDgzCCAmugAwIBAgIUCYMVPDJVUSkrLVxErDqoXsUuy2AwDQYJKoZIhvcNAQEL
+BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAw
+MFowajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
+AgH+MA0GCSqGSIb3DQEBCwUAA4IBAQBXqqT2fBXtmQaPHr86unEVaXirLmnc9qR2
+yMhrBSbZP1w6MWQDD/TWXjnjqeADqXZy1feI/yBBQDnkJa1x4IVz5oujQDuf/chQ
+vAemh7t7DhyfIZ+OQCMadSIOva06PhQuiwYXqM3Pg5MyBNp8NKP9fwSri39if4ru
+4VgSudV5yo2Z/pduJMl2zFGsu+ZbYco0AKqN4RX7Rwkp7Uf7uiizgAPVEpCrm+Ja
+EJUitKh1ejVH3SakdUfQEboQYsZl9c2S5/oKUlON4weoYctvc0zQkscRnqyuBkBJ
+RB/YMKaHcphMRfmJFbwA5Vs0AOJ5E9u/hhvhcdBL6ZTUi3uEermS
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem.certspec b/security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem.certspec
new file mode 100644
index 0000000000..4def496f67
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/pgo-ca-all-usages.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+subject:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:basicConstraints:cA,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem b/security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem
new file mode 100644
index 0000000000..c52d36d1bc
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDgzCCAmugAwIBAgIUF5e1WSjUvqgAuz9/WyPcq+OgkOAwDQYJKoZIhvcNAQEL
+BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAw
+MFowajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
+AgEGMA0GCSqGSIb3DQEBCwUAA4IBAQA2+3qj2Bme+jVFlNva/gyvL0QOg8lgDc++
+pWTDd+NMpeqwarX6z4+wjzXh9ZPKk29alBWpnhVVvD4WAOa1TSLbevTlV+viGloP
+uvZwoxn6naumwv992hJS8TJda8ZgKB5N5REdsDoD1hWeLevjWACMToSaXN+Bx8jF
+GFoh1ifj1SD4DN51sc4KkKscjcSbBwyWJUCJGsDw7H5FtSSNEfGnctdsoLCWS84z
+Jw/WUs/3hOB+MRsdqPBISlLrd5fnmL6RL7ZkDqad1Fi69GZQBDZUBmTxXhNl4VHK
+RkQZCVLg9YWDzaKDj1Q326+sSur3PvQGJBplMEOWIOz/MVs2skeT
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem.certspec b/security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem.certspec
new file mode 100644
index 0000000000..448e167bd0
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/pgo-ca-regular-usages.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+subject:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/mochitest/browser/revoked.pem b/security/manager/ssl/tests/mochitest/browser/revoked.pem
new file mode 100644
index 0000000000..02c06872e1
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/revoked.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrzCCAZegAwIBAgIUJf4afZUeBhMdHovc6HjvffKmXJYwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEjEQMA4GA1UEAwwHcmV2b2tlZDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODY
+H72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk
+27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9
+0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMM
+kd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaL
+L+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0BAQsFAAOC
+AQEAKYqs7wd9/1ufx9s+YsRsd/MBRE/figWWVI+Gha3qLnx2NI7389n9qY8ES0az
+bqNV71w8qYcn8tzlLCTLrgkhnt/rkMwXorvI+vfF+7fQeFlqFgXw46WxM4NtzLTz
+0Hdt3+QULysZdG/my/HB1mcBWPRSrSZer7s1oKRcsoNgbSvmuyIWrrUVmn/tiaKy
+kC5oOADLIoSHeGh4SvyUnP1oF3Aq5vxsykd66q/bP4MrmZinlJYNq/ybeEvrTEuA
+XrBkRkslcVE6+VS/1ieT/5aa4IUyQ7DYy8i+IyspkD0xJ6cklSNodMl55odQK67Y
+iC/MEn7uG9DJmwEH0ZN6Qxc3LA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/revoked.pem.certspec b/security/manager/ssl/tests/mochitest/browser/revoked.pem.certspec
new file mode 100644
index 0000000000..daf75c670f
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/revoked.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:revoked
diff --git a/security/manager/ssl/tests/mochitest/browser/ssl-ee.pem b/security/manager/ssl/tests/mochitest/browser/ssl-ee.pem
new file mode 100644
index 0000000000..aff528d39b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ssl-ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUU9i1j3V1LGw7IBlKgfVezTka1I0wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowETEPMA0GA1UEAwwGc3NsLWVlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyEwHzAdBgNVHSUEFjAUBggr
+BgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBACoLO/x/rat1m2td
+0FMcOoXAJiTmHIIg22yC7RgFZxkSxnp8Wr2cevTUkBhwsITD6I7T6UTkqwnaXFBt
+eeTxYuGoYx7VdefLNvAIdBKWZgtMvBr9+KHrhoFXOxL/+7oBFjFH0EP7B53aflJc
+/rVf0CUb4iwS3ebjk5wOLZhB+sk3PfuzsdSOPjpUQZSmni/hI1d+Am1Kn0Qs0COI
+0SPezVt7U5Gr0Ewc3eP2fI1I7YJbWKP/s5bd0C/aWW+tLBWxolYT3X4+S/7Bg52N
+bCxwkd5GHJZkpA/M2++kiDQVfedMsN2E7IlsXILxckQsGlODJgrSbQb9vIYP1hGm
+ryWS8kc=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/ssl-ee.pem.certspec b/security/manager/ssl/tests/mochitest/browser/ssl-ee.pem.certspec
new file mode 100644
index 0000000000..c4037675f1
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/ssl-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ssl-ee
+extension:extKeyUsage:serverAuth,clientAuth
diff --git a/security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem b/security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem
new file mode 100644
index 0000000000..ef4515983d
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuzCCAaOgAwIBAgIUI0xny6j77OiAn+msMVdsgBLMj/swDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHdW5rbm93bjAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAZMRcwFQYDVQQDDA51bmtub3duLWlzc3VlcjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs
+9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8
+HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7Ak
+kqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJet
+lmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2r
+kQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkq
+hkiG9w0BAQsFAAOCAQEAP/jp5cJkeSP5DmAgvymt6zviEHmm24nHgHuLDwTPf446
+pCl4YGFSwzV44d6503AyFNOUbTg+l+2lOpG3CU3mDi+agcG+r4QEG3a01Mh9uWfB
+I60VBHfQwq4yoA7gwnhUyf5dEOgwJkerc8uZMaoVv0OlRjp89ycqkmBjHpzRUR9A
+6WzIKzA69foFI5b2UVMtUuXAZPyzPep2D35XNc1iSA4OsA5IhLJD7n7caQKoEiYv
+i6A0Ze4ZgOHU/w7Aj+DUjq8JK85cdpUksCxAnPGannRhpK90/uc+d6IyovxjCdo1
+YQCfU6R3RXWPQmhHRxrQX6LMde5sxUADtW/GDutaAQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem.certspec b/security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem.certspec
new file mode 100644
index 0000000000..c76a4e2c7b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/unknown-issuer.pem.certspec
@@ -0,0 +1,2 @@
+issuer:unknown
+subject:unknown-issuer
diff --git a/security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem b/security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem
new file mode 100644
index 0000000000..0be7744cc8
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUA0ih/Zea7LtjFynUOzwIwcOQCjkwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFzEVMBMGA1UEAwwMdW50cnVzdGVkLWNhMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRME
+BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAb7HneKXYzHjB
+ehF6bTye2hTACi84WqaW9c2K7KrqBa+zqR+/aF5pjRdlYoSOOiAKIWzHdJZYY0sh
+fgoDFCCYuPg6Bim4AVthRxMDn07TDAkSxAV+M1P3bJLEeWx3/muTL0t98P/JXTS3
+GfmXjcWJ1E1bCMSrUclSk9Y6AKhyIOeapB0YovhvP8fWLso9RT7mv7W/xGmWohhP
+bdjjislgWouFtOWBrata/u4ifS3hsBpYESXL9D2EzdoDtgzRG0yetCLmxTogfDqm
+MeW75eQqQYiqU+VfnbvI46AvI9ihfXXzEfqVSzKkLwuYKiPa42QtU/sLNTbZeSqB
+bR9mMRqyKQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem.certspec b/security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem.certspec
new file mode 100644
index 0000000000..04f4430574
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/untrusted-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:untrusted-ca
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs
new file mode 100644
index 0000000000..8e9afb098c
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ response.write("<html><body>hello!</body></html>");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/backward.html b/security/manager/ssl/tests/mochitest/mixedcontent/backward.html
new file mode 100644
index 0000000000..8699a07dda
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/backward.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="text/javascript">
+ "use strict";
+ window.onload = function()
+ {
+ window.setTimeout(function()
+ {
+ SpecialPowers.wrap(window).docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .goBack();
+ }, 100);
+ };
+
+ </script>
+</head>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/bug329869.js b/security/manager/ssl/tests/mochitest/mixedcontent/bug329869.js
new file mode 100644
index 0000000000..9d67ba1f92
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/bug329869.js
@@ -0,0 +1,11 @@
+/* import-globals-from mixedContentTest.js */
+"use strict";
+
+document.open();
+// eslint-disable-next-line no-unsanitized/method
+document.write("This is insecure XSS script " + document.cookie);
+isSecurityState(
+ "broken",
+ "security broken after document write from unsecure script"
+);
+finish();
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html b/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html
new file mode 100644
index 0000000000..4bbf9bfe8c
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 383369 test, step 2</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/does_not_exist.css">
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ window.onload = function runTest() {
+ window.setTimeout(function () {
+ window.location =
+ "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step3.html?runtest";
+ }, 0);
+ };
+
+ async function afterNavigationTest()
+ {
+ }
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step3.html b/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step3.html
new file mode 100644
index 0000000000..276c2343fd
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/bug383369step3.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 383369 test, final step</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("secure", "secure page after insecure download and insecure subcontent still secure");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("secure", "still secure after back/forward");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/download.auto b/security/manager/ssl/tests/mochitest/mixedcontent/download.auto
new file mode 100644
index 0000000000..4d2fb7d5ae
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/download.auto
@@ -0,0 +1 @@
+Temporary file for security/mixedconent tests \ No newline at end of file
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/download.auto^headers^ b/security/manager/ssl/tests/mochitest/mixedcontent/download.auto^headers^
new file mode 100644
index 0000000000..9c3159e153
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/download.auto^headers^
@@ -0,0 +1,2 @@
+Content-disposition: "attachment"
+Content-type: application/x-auto-download
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs
new file mode 100644
index 0000000000..5cce2826f8
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ //response.setHeader("Content-type", "image/gif");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs
new file mode 100644
index 0000000000..20d9dea829
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs
@@ -0,0 +1,13 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-type", "image/bitmap");
+
+ let bmpheader = "\x42\x4D\x36\x10\x0E\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00\x80\x02\x00\x00\xE0\x01\x00\x00\x01\x00\x18\x00\x00\x00\x00\x00\x00\x10\x0E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+ let bmpdatapiece = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+ response.bodyOutputStream.write(bmpheader, 54);
+ // Fill 640*480*3 nulls
+ for (let i = 0; i < (640 * 480 * 3) / 64; ++i)
+ response.bodyOutputStream.write(bmpdatapiece, 64);
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html b/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html
new file mode 100644
index 0000000000..064783e0cb
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+
+<body>
+ This is frame 1:
+ <script>
+ "use strict";
+ // eslint-disable-next-line no-unsanitized/method
+ document.write(location.href);
+ </script>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframe2.html b/security/manager/ssl/tests/mochitest/mixedcontent/iframe2.html
new file mode 100644
index 0000000000..37fc604ea6
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframe2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+
+<body>
+ This is frame 2:
+ <script>
+ "use strict";
+ // eslint-disable-next-line no-unsanitized/method
+ document.write(location.href);
+ </script>
+ <iframe src="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html
new file mode 100644
index 0000000000..6c7a5473cb
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<META http-equiv="Refresh"
+ Content="0; URL=http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html">
+<html>
+ <body>
+ Redirecting by meta tag...
+ </body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs
new file mode 100644
index 0000000000..ea93b80b7d
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
+ response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs
new file mode 100644
index 0000000000..937e29954f
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
+ response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs
new file mode 100644
index 0000000000..7af6116e10
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
+ response.setHeader("Location", "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs
new file mode 100644
index 0000000000..a8eb26642a
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
+ response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
new file mode 100644
index 0000000000..6c300b7fc3
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mixedContentTest.js
@@ -0,0 +1,211 @@
+"use strict";
+
+/**
+ * Helper script for mixed content testing. It opens a new top-level window
+ * from a secure origin and '?runtest' query. That tells us to run the test
+ * body, function runTest(). Then we wait for call of finish(). On its first
+ * call it loads helper page 'backward.html' that immediately navigates
+ * back to the test secure test. This checks the bfcache. We got second call
+ * to onload and this time we call afterNavigationTest() function to let the
+ * test check security state after re-navigation back. Then we again wait for
+ * finish() call, that this time finishes completelly the test.
+ */
+
+// Tells the framework if to load the test in an insecure page (http://)
+var loadAsInsecure = false;
+// Set true to bypass the navigation forward/back test
+var bypassNavigationTest = false;
+// Set true to do forward/back navigation over an http:// page, test state leaks
+var navigateToInsecure = false;
+// Open the test in two separate windows, test requests sharing among windows
+var openTwoWindows = false;
+// Override the name of the test page to load, useful e.g. to prevent load
+// of images or other content before the test starts; this is actually
+// a 'redirect' to a different test page.
+var testPage = "";
+// Assign a function to this variable to have a clean up at the end
+var testCleanUp = null;
+// Contains mixed active content that needs to load to run the test
+var hasMixedActiveContent = false;
+
+// Internal variables
+var _windowCount = 0;
+
+window.onload = async function onLoad() {
+ if (location.search == "?runtest") {
+ try {
+ if (history.length == 1) {
+ // Each test that includes this helper file is supposed to define
+ // runTest(). See the top level comment.
+ await runTest(); // eslint-disable-line no-undef
+ } else {
+ // Each test that includes this helper file is supposed to define
+ // afterNavigationTest(). See the top level comment.
+ await afterNavigationTest(); // eslint-disable-line no-undef
+ }
+ } catch (ex) {
+ ok(false, "Exception thrown during test: " + ex);
+ finish();
+ }
+ } else {
+ window.addEventListener("message", onMessageReceived);
+
+ let secureTestLocation = loadAsInsecure
+ ? "http://example.com"
+ : "https://example.com";
+ secureTestLocation += location.pathname;
+ if (testPage != "") {
+ let array = secureTestLocation.split("/");
+ array.pop();
+ array.push(testPage);
+ secureTestLocation = array.join("/");
+ }
+ secureTestLocation += "?runtest";
+
+ if (hasMixedActiveContent) {
+ SpecialPowers.pushPrefEnv(
+ { set: [["security.mixed_content.block_active_content", false]] },
+ null
+ );
+ }
+ if (openTwoWindows) {
+ _windowCount = 2;
+ window.open(secureTestLocation, "_new1", "");
+ window.open(secureTestLocation, "_new2", "");
+ } else {
+ _windowCount = 1;
+ window.open(secureTestLocation);
+ }
+ }
+};
+
+function onMessageReceived(event) {
+ switch (event.data) {
+ // Indication of all test parts finish (from any of the frames)
+ case "done":
+ if (--_windowCount == 0) {
+ if (testCleanUp) {
+ testCleanUp();
+ }
+ if (hasMixedActiveContent) {
+ SpecialPowers.popPrefEnv(null);
+ }
+
+ SimpleTest.finish();
+ }
+ break;
+
+ // Any other message is an error or success message of a test.
+ default:
+ SimpleTest.ok(!event.data.match(/^FAILURE/), event.data);
+ break;
+ }
+}
+
+function postMsg(message) {
+ opener.postMessage(message, "http://mochi.test:8888");
+}
+
+function finish() {
+ if (history.length == 1 && !bypassNavigationTest) {
+ window.setTimeout(() => {
+ window.location.assign(
+ navigateToInsecure
+ ? "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/backward.html"
+ : "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/backward.html"
+ );
+ }, 0);
+ } else {
+ postMsg("done");
+ window.close();
+ }
+}
+
+function ok(a, message) {
+ if (!a) {
+ postMsg("FAILURE: " + message);
+ } else {
+ postMsg(message);
+ }
+}
+
+function is(a, b, message) {
+ if (a != b) {
+ postMsg(`FAILURE: ${message}, expected ${b} got ${a}`);
+ } else {
+ postMsg(`${message}, expected ${b} got ${a}`);
+ }
+}
+
+async function isSecurityState(expectedState, message, test) {
+ if (!test) {
+ test = ok;
+ }
+
+ let state = await SpecialPowers.getSecurityState(window);
+
+ let isInsecure =
+ state & SpecialPowers.Ci.nsIWebProgressListener.STATE_IS_INSECURE;
+ let isBroken =
+ state & SpecialPowers.Ci.nsIWebProgressListener.STATE_IS_BROKEN;
+ let isEV =
+ state & SpecialPowers.Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
+
+ let gotState = "secure";
+ if (isInsecure) {
+ gotState = "insecure";
+ } else if (isBroken) {
+ gotState = "broken";
+ } else if (isEV) {
+ gotState = "EV";
+ }
+
+ test(
+ gotState == expectedState,
+ (message || "") + ", expected " + expectedState + " got " + gotState
+ );
+
+ switch (expectedState) {
+ case "insecure":
+ test(
+ isInsecure && !isBroken && !isEV,
+ "for 'insecure' excpected flags [1,0,0], " + (message || "")
+ );
+ break;
+ case "broken":
+ test(
+ !isInsecure && isBroken && !isEV,
+ "for 'broken' expected flags [0,1,0], " + (message || "")
+ );
+ break;
+ case "secure":
+ test(
+ !isInsecure && !isBroken && !isEV,
+ "for 'secure' expected flags [0,0,0], " + (message || "")
+ );
+ break;
+ case "EV":
+ test(
+ !isInsecure && !isBroken && isEV,
+ "for 'EV' expected flags [0,0,1], " + (message || "")
+ );
+ break;
+ default:
+ throw new Error("Invalid isSecurityState state");
+ }
+}
+
+function waitForSecurityState(expectedState, callback) {
+ let roundsLeft = 200; // Wait for 20 seconds (=200*100ms)
+ let interval = window.setInterval(async () => {
+ await isSecurityState(expectedState, "", isok => {
+ if (isok) {
+ roundsLeft = 0;
+ }
+ });
+ if (!roundsLeft--) {
+ window.clearInterval(interval);
+ callback();
+ }
+ }, 100);
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/mochitest.ini b/security/manager/ssl/tests/mochitest/mixedcontent/mochitest.ini
new file mode 100644
index 0000000000..b63031004f
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mochitest.ini
@@ -0,0 +1,65 @@
+[DEFAULT]
+prefs =
+ security.mixed_content.upgrade_display_content=false
+support-files =
+ alloworigin.sjs
+ backward.html
+ bug329869.js
+ bug383369step2.html
+ bug383369step3.html
+ download.auto
+ download.auto^headers^
+ emptyimage.sjs
+ hugebmp.sjs
+ iframe.html
+ iframe2.html
+ iframeMetaRedirect.html
+ iframesecredirect.sjs
+ iframeunsecredirect.sjs
+ imgsecredirect.sjs
+ imgunsecredirect.sjs
+ mixedContentTest.js
+ moonsurface.jpg
+ nocontent.sjs
+ redirecttoemptyimage.sjs
+ somestyle.css
+ unsecureIframe.html
+ unsecurePictureDup.html
+
+[test_bug329869.html]
+[test_bug383369.html]
+skip-if = toolkit == 'android'
+[test_bug455367.html]
+[test_bug472986.html]
+[test_bug477118.html]
+[test_bug521461.html]
+[test_cssBefore1.html]
+[test_cssContent1.html]
+[test_cssContent2.html]
+[test_documentWrite1.html]
+[test_documentWrite2.html]
+[test_dynDelayedUnsecurePicture.html]
+[test_dynDelayedUnsecureXHR.html]
+[test_dynUnsecureBackground.html]
+[test_dynUnsecureIframeRedirect.html]
+[test_dynUnsecurePicture.html]
+[test_dynUnsecurePicturePreload.html]
+[test_dynUnsecureRedirect.html]
+disabled=intermitently fails, quite often, bug 487402
+[test_innerHtmlDelayedUnsecurePicture.html]
+[test_innerHtmlUnsecurePicture.html]
+[test_javascriptPicture.html]
+[test_secureAll.html]
+[test_securePicture.html]
+[test_unsecureBackground.html]
+[test_unsecureCSS.html]
+[test_unsecureIframe.html]
+[test_unsecureIframe2.html]
+[test_unsecureIframeMetaRedirect.html]
+disabled=intermittently fails, less often, bug 487632
+[test_unsecureIframeRedirect.html]
+[test_unsecurePicture.html]
+[test_unsecurePictureDup.html]
+[test_unsecurePictureInIframe.html]
+[test_unsecureRedirect.html]
+skip-if = verify && debug && (os == 'linux' || os == 'mac')
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg b/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg
new file mode 100644
index 0000000000..c0ffca256a
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg
Binary files differ
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/moz.build b/security/manager/ssl/tests/mochitest/mixedcontent/moz.build
new file mode 100644
index 0000000000..7c990fbc62
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_MANIFESTS += ["mochitest.ini"]
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs
new file mode 100644
index 0000000000..53fa4bc0c2
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 204, "No Content");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs b/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs
new file mode 100644
index 0000000000..4d85e16581
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 307, "Moved temporarly");
+ response.setHeader("Location", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/emptyimage.sjs");
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/somestyle.css b/security/manager/ssl/tests/mochitest/mixedcontent/somestyle.css
new file mode 100644
index 0000000000..9867e3c41e
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/somestyle.css
@@ -0,0 +1,4 @@
+body
+{
+ background-color: lightBlue;
+}
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html
new file mode 100644
index 0000000000..ccb9a8d9cf
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug329869.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>dymanic script load</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ window.setTimeout(function () {
+ let newElement = document.createElement("script");
+ newElement.src = "http://example.org/tests/security/manager/ssl/tests/" +
+ "mochitest/mixedcontent/bug329869.js";
+ document.body.appendChild(newElement);
+ }, 0);
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("secure", "when we navigate back, we're loading our secure page again and not loading an insecure script, so our security state is secure");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html
new file mode 100644
index 0000000000..d45d5eb761
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 383369 test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ /* sendAsyncMessage isn't actually a global - this just mollifies eslint */
+ /* global sendAsyncMessage */
+ "use strict";
+
+ // We want to start this test from an insecure context
+ loadAsInsecure = true;
+ // We don't want to go through the navigation back/forward test
+ bypassNavigationTest = true;
+
+ async function runTest() {
+ let script = SpecialPowers.loadChromeScript(function() {
+ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ // Force download to be w/o user assistance for our testing mime type
+ const mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+ let handlerInfo =
+ mimeSvc.getFromTypeAndExtension("application/x-auto-download", "auto");
+ handlerInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk;
+ handlerInfo.alwaysAskBeforeHandling = false;
+ handlerInfo.preferredApplicationHandler = null;
+
+ const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"]
+ .getService(Ci.nsIHandlerService);
+ handlerSvc.store(handlerInfo);
+
+ let profileDir = Services.dirsvc.get("ProfDS", Ci.nsIFile);
+ profileDir.append("downloads");
+
+ let prefBranch = Services.prefs.getBranch("browser.download.");
+
+ prefBranch.setCharPref("dir", profileDir.path);
+ prefBranch.setBoolPref("useDownloadDir", true);
+ prefBranch.setIntPref("folderList", 2);
+
+ const { Downloads } =
+ ChromeUtils.import("resource://gre/modules/Downloads.jsm");
+ Downloads.getList(Downloads.PUBLIC).then(list => {
+ list.addView({
+ onDownloadAdded(aDownload) {
+ list.removeView(this);
+ aDownload.whenSucceeded().then(() => {
+ list.removeFinished();
+ sendAsyncMessage("navigate", "bug383369step2.html");
+ });
+ },
+ });
+ sendAsyncMessage("navigate", "download.auto");
+ }).catch(Cu.reportError);
+ });
+ script.addMessageListener("navigate", function(url) {
+ window.location = url;
+ });
+ }
+
+ async function afterNavigationTest() {}
+
+ testCleanUp = function cleanup() {
+ SpecialPowers.loadChromeScript(function() {
+ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ const mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+ let handlerInfo =
+ mimeSvc.getFromTypeAndExtension("application/x-auto-download", "auto");
+
+ const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"]
+ .getService(Ci.nsIHandlerService);
+ handlerSvc.remove(handlerInfo);
+
+ let prefBranch = Services.prefs.getBranch("browser.download.");
+
+ const prefKeys = ["dir", "useDownloadDir", "folderList"];
+ for (let prefKey of prefKeys) {
+ if (prefBranch.prefHasUserValue(prefKey)) {
+ prefBranch.clearUserPref(prefKey);
+ }
+ }
+ });
+ };
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
new file mode 100644
index 0000000000..d2ad64c454
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>No content image doesn't break security</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ SpecialPowers.pushPrefEnv(
+ {"set": [["security.mixed_content.upgrade_display_content", false]]},
+ null);
+ await isSecurityState("broken", "broken");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/redirecttoemptyimage.sjs" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html
new file mode 100644
index 0000000000..bd55a600ca
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug472986.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>img.src replace</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ SimpleTest.expectAssertions(0, 4);
+
+ // Clear the default onload assigned to test start because we must
+ // wait for replaced image to load and only after that test the security state
+ var onLoadFunction = window.onload;
+ window.onload = function()
+ {
+ let img1 = document.getElementById("img1");
+ img1.addEventListener("load", onLoadFunction);
+ img1.src = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+ };
+
+ async function runTest()
+ {
+ await isSecurityState("secure", "secure");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("secure", "secure after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img id="img1" src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug477118.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug477118.html
new file mode 100644
index 0000000000..90932790f0
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug477118.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 477118</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("secure", "data <img> doesn't break security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("secure", "still secure after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAN1wAADdcBQiibeAAAAAd0SU1FB9kBDxIAOdxBIbcAAAI+SURBVDjLxZO7TxRhFMV/M/PN7EPYhS02yytggo+ERAgxERsNhYnGzsQojaHT3r9F0cTaUjs6NUQLBRKDi4I8FEwkLMgO+5rdeX3XAoVYiZWnP797cu69Bv8gqZ+0QTqIdRpl1VHGvnV884CJwQgwicVtoB/ku/H4ycOnURgP/Q1g21Fw4+r0RjbbNmaq4ayO35UkXp9S2UzHxK2bE4QNl82FVwD0D49jn+hERBCRgwTiIX7pvGUGYF/BlO327d3ENfV7wuL0AyjPICIsfPvAmev3qdVqlMtlCoUCGxurDPT1kEnPomQKkRTFpXOjh4DN4kvGLuSJ/JC38y/ouXyXYrGIiBDHMVtbu1SrvXTlbfp6HcQcoVwp5hRAaWedPXFYfL+E1lAKswSRy+joKEEQICLkcjm01hiGgRcksW0b4SMKYHb+OSt6ma+VffxWRF3S5OeeMX7pHkqpwy5EBK31H90ogFODF1lbm8OtlEhloDORpbdnhDAMaTabhwbbtkkkEkRRhGVZR4BCfojJO4+o1VwQAItEIolpmliWRVtbG7s/dlDKotGo47U8CvkuAEwA3/eJoxhE0WyGuO4+nucRBAHVahXDMKg3agRhQBAGVKsVtNZHCRzHwTAMcrnc0eX9it3e3g7A6cGzhx0AKHWwQOV5jdXXb2ZOaa3BOP5ffF5ZptVqflG+3xpbWv5kh2FolEo7dnmv7Lium6zXG0nf95MiguM4QTqd9jPZTCvX2eF393QHqVRKZ7KZkP+unzPGLX8Jr7F8AAAAAElFTkSuQmCC" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html
new file mode 100644
index 0000000000..59085a5ec4
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug521461.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 521461</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+ SimpleTest.requestFlakyTimeout("Timeout in mixedContentTest");
+
+ loadAsInsecure = true;
+
+ async function runTest()
+ {
+ window.location = "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/nocontent.sjs";
+ window.setTimeout(async () => {
+ await isSecurityState("insecure", "location.href doesn't effect the security state");
+ is(document.body.innerHTML, "This is an unsecure page!\n", "Document has not changed content");
+ finish();
+ }, 1000);
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("insecure", "still not secure after navigation");
+ is(document.body.innerHTML, "This is an unsecure page!\n", "Document has not changed content");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>This is an unsecure page!</body></html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html
new file mode 100644
index 0000000000..98cee1bb53
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssBefore1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>CSS :before styling 1</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+ p:before
+ {
+ content: url(http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg);
+ }
+ </style>
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure content added by :before styling breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <p>
+ There is a moon surface left to this text
+ </p>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html
new file mode 100644
index 0000000000..5c5019ca78
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>CSS conent styling 1</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+ p
+ {
+ content: url(http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg);
+ }
+ </style>
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure content added by :before styling breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <p></p>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html
new file mode 100644
index 0000000000..19e5784334
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_cssContent2.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>CSS conent styling 2</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ if (navigator.platform.startsWith("Mac")) {
+ SimpleTest.expectAssertions(0, 1);
+ }
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ document.getElementById("para").style.content =
+ "url('http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg')";
+
+ waitForSecurityState("broken", async () =>
+ {
+ await isSecurityState("broken", "insecure content added by styling breaks security");
+ finish();
+ });
+ }
+
+ async function afterNavigationTest()
+ {
+ is(document.getElementById("para").style.content, "");
+ await isSecurityState("secure", "security full after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <p id="para"></p>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html
new file mode 100644
index 0000000000..90eca5bcdb
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite1.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>document.write('<img src="http://">')</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <img> written dynamically breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <script class="testbody" type="text/javascript">
+ "use strict";
+ document.write(
+ "<img src='http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg' />");
+ </script>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html
new file mode 100644
index 0000000000..e009fd4f54
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_documentWrite2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>document.write('<iframe src="http://">')</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure iframe written dynamically breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <script class="testbody" type="text/javascript">
+ "use strict";
+ document.write(
+ "<iframe src='http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html'></iframe>");
+ </script>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html
new file mode 100644
index 0000000000..514902d047
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecurePicture.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>img.src changes to unsecure test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ window.setTimeout(function() {
+ // Don't do this synchronously from onload handler
+ document.getElementById("image1").src =
+ "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+ }, 0);
+
+ waitForSecurityState("broken", async () =>
+ {
+ await isSecurityState("broken", "src='http://...' changed to broken");
+ finish();
+ });
+ }
+
+ async function afterNavigationTest()
+ {
+ is(document.getElementById("image1").src,
+ "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg",
+ "img.src secure again");
+ await isSecurityState("secure", "security full after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img id="image1" src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html
new file mode 100644
index 0000000000..d9a8cc8af7
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynDelayedUnsecureXHR.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>unsecure XHR test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ window.setTimeout(async () => {
+ try {
+ let req = new XMLHttpRequest();
+ req.open("GET", "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/alloworigin.sjs", false);
+ req.send(null);
+
+ // Change should be immediate, the request was sent synchronously
+ await isSecurityState("broken", "security broken after insecure XHR");
+ } catch (ex) {
+ ok(false, ex);
+ }
+
+ finish();
+ }, 0);
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("secure", "security full after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html
new file mode 100644
index 0000000000..fd66b21a64
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureBackground.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>body.background changes to unsecure test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ // This test, as is, equals to https://kuix.de/misc/test17/358438.php
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ document.body.background =
+ "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+
+ waitForSecurityState("broken", async () => {
+ await isSecurityState("broken", "document.body.background='http://...' changed to broken");
+ finish();
+ });
+ }
+
+ async function afterNavigationTest()
+ {
+ is(document.body.background,
+ "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg",
+ "document backround secure again");
+ await isSecurityState("secure", "secure after re-navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body background="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg">
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html
new file mode 100644
index 0000000000..8934de4b79
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureIframeRedirect.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>iframe.src changes to unsecure redirect test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ let self = window;
+ let iframe = document.getElementById("iframe1");
+ iframe.onload = async () => {
+ await self.isSecurityState("broken", "src='redirect to unsecure' changed to broken");
+ self.finish();
+ };
+
+ iframe.src =
+ "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs";
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <iframe id="iframe1" src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html
new file mode 100644
index 0000000000..5ef5a28b2c
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicture.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>img.src changes to unsecure test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+ SimpleTest.requestFlakyTimeout("Timeout in mixedContentTest");
+
+ // This test, as is, equals to https://kuix.de/misc/test17/358438.php
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ document.getElementById("image1").src =
+ "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+
+ window.setTimeout(async () => {
+ await isSecurityState("broken", "src='http://...' changed to broken");
+ finish();
+ }, 500);
+ }
+
+ async function afterNavigationTest()
+ {
+ is(document.getElementById("image1").src,
+ "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg",
+ "img.src secure again");
+ await isSecurityState("secure", "security full after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img id="image1" src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html
new file mode 100644
index 0000000000..d8506e8cc5
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecurePicturePreload.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>img.src changes to unsecure test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ (new Image()).src =
+ "http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg";
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "(new Image()).src='http://...' changed to broken");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureRedirect.html
new file mode 100644
index 0000000000..a73c7f8619
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_dynUnsecureRedirect.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>img.src changes to unsecure redirect test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+ document.getElementById("image1").src =
+ "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs";
+
+ window.setTimeout(async () => {
+ await isSecurityState("broken", "src='redirect to unsecure' changed to broken");
+ finish();
+ }, 500);
+ }
+
+ async function afterNavigationTest()
+ {
+ is(document.getElementById("image1").src,
+ "https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg",
+ "img.src secure again");
+ await isSecurityState("secure", "security full after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img id="image1" src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html
new file mode 100644
index 0000000000..45bf140384
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlDelayedUnsecurePicture.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>innerHTML changes to unsecure test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+ SimpleTest.requestFlakyTimeout("Timeout in mixedContentTest");
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+
+ window.setTimeout(function () {
+ document.getElementById("buddy").innerHTML =
+ "<img id='image1' src='http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg' />";
+ }, 1);
+
+ waitForSecurityState("broken", async () => {
+ await isSecurityState("broken", "innerHTML loading insecure changed to broken");
+ finish();
+ });
+ }
+
+ async function afterNavigationTest()
+ {
+ is(document.getElementById("buddy").innerHTML, "\n", "innerHTML back to previous");
+ await isSecurityState("secure");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body id="buddy"></body></html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html
new file mode 100644
index 0000000000..d8b3e5f6e0
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_innerHtmlUnsecurePicture.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>innerHTML changes to unsecure test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+ SimpleTest.requestFlakyTimeout("Timeout in mixedContentTest");
+
+ async function runTest()
+ {
+ await isSecurityState("secure");
+
+ document.getElementById("buddy").innerHTML =
+ "<img id='image1' src='http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg' />";
+
+ window.setTimeout(async () => {
+ await isSecurityState("broken", "innerHTML loading insecure changed to broken");
+ finish();
+ }, 500);
+ }
+
+ async function afterNavigationTest()
+ {
+ is(document.getElementById("buddy").innerHTML, "\n", "innerHTML back to previous");
+ await isSecurityState("secure");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body id="buddy"></body></html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_javascriptPicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_javascriptPicture.html
new file mode 100644
index 0000000000..66a28ce74e
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_javascriptPicture.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Secure img load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("secure", "javascript: <img> should not break security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("secure", "Still secure after renavigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="javascript:'Random data'" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_secureAll.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_secureAll.html
new file mode 100644
index 0000000000..efd754dd58
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_secureAll.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>All secure anti-regression check</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <link rel="stylesheet" type="text/css"
+ href="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/somestyle.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ // Navigation test goes over an insecure page, test state leak
+ navigateToInsecure = true;
+
+ async function runTest()
+ {
+ await isSecurityState("secure", "insecure <img> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("secure", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" />
+ <img src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/imgsecredirect.sjs" />
+ <iframe src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframesecredirect.sjs" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_securePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_securePicture.html
new file mode 100644
index 0000000000..961713a2da
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_securePicture.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Secure img load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ loadAsInsecure = true;
+
+ async function runTest()
+ {
+ await isSecurityState("insecure", "left insecure");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("insecure", "left insecure after renavigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureBackground.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureBackground.html
new file mode 100644
index 0000000000..02fdb29d41
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureBackground.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>background unsecure test</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ // This test, as is, equals to https://kuix.de/misc/test17/358438.php
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "security broken");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body background="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg">
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html
new file mode 100644
index 0000000000..c775347e72
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureCSS.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure css load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <link rel="stylesheet" type="text/css"
+ href="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/somestyle.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <img> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe.html
new file mode 100644
index 0000000000..291ce3747e
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure iframe load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <iframe> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <iframe src="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe.html"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe2.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe2.html
new file mode 100644
index 0000000000..8f49ecda51
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframe2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure iframe load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <iframe> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <iframe src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframe2.html"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeMetaRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeMetaRedirect.html
new file mode 100644
index 0000000000..4eebbf5b22
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeMetaRedirect.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure redirect iframe load</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ window.setTimeout(async () =>
+ {
+ await isSecurityState("broken", "insecure meta-tag <iframe> load breaks security");
+ finish();
+ }, 500);
+ }
+
+ async function afterNavigationTest()
+ {
+ window.setTimeout(async () =>
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }, 500);
+ }
+
+ </script>
+</head>
+
+<body>
+ <iframe src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframeMetaRedirect.html"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeRedirect.html
new file mode 100644
index 0000000000..12a4233494
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureIframeRedirect.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure redirect iframe load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <iframe> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <iframe src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/iframeunsecredirect.sjs"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePicture.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePicture.html
new file mode 100644
index 0000000000..3c19811db9
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePicture.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure img load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <img> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureDup.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureDup.html
new file mode 100644
index 0000000000..81ed58ffde
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureDup.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure img load in two windows</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ openTwoWindows = true;
+ testPage = "unsecurePictureDup.html";
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureInIframe.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureInIframe.html
new file mode 100644
index 0000000000..21bcf5f810
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecurePictureInIframe.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure img in iframe load</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <img> in an <iframe> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <iframe src="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/unsecureIframe.html"></iframe>
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureRedirect.html b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureRedirect.html
new file mode 100644
index 0000000000..82611ff3fe
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_unsecureRedirect.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Redirect from secure to unsecure img</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ hasMixedActiveContent = true;
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <img> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="https://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/imgunsecredirect.sjs" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/unsecureIframe.html b/security/manager/ssl/tests/mochitest/mixedcontent/unsecureIframe.html
new file mode 100644
index 0000000000..2282677418
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/unsecureIframe.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+
+<body>
+ <img src="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/moonsurface.jpg" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/mixedcontent/unsecurePictureDup.html b/security/manager/ssl/tests/mochitest/mixedcontent/unsecurePictureDup.html
new file mode 100644
index 0000000000..7ce3701620
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/unsecurePictureDup.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Unsecure img load in two windows</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="mixedContentTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ async function runTest()
+ {
+ await isSecurityState("broken", "insecure <img> load breaks security");
+ finish();
+ }
+
+ async function afterNavigationTest()
+ {
+ await isSecurityState("broken", "security still broken after navigation");
+ finish();
+ }
+
+ </script>
+</head>
+
+<body>
+ <img src="http://example.com/tests/security/manager/ssl/tests/mochitest/mixedcontent/hugebmp.sjs" />
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/moz.build b/security/manager/ssl/tests/mochitest/moz.build
new file mode 100644
index 0000000000..3b7f2ba585
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+TEST_DIRS += [
+ "browser",
+ "mixedcontent",
+ "stricttransportsecurity",
+]
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini b/security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini
new file mode 100644
index 0000000000..b2ce8fef00
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/chrome.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+tags = psm
+skip-if = os == 'android'
+support-files = page_blank.html
+
+[test_sts_privatebrowsing_perwindowpb.html]
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/mochitest.ini b/security/manager/ssl/tests/mochitest/stricttransportsecurity/mochitest.ini
new file mode 100644
index 0000000000..7970639053
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/mochitest.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+tags = psm
+support-files =
+ nosts_bootstrap.html
+ nosts_bootstrap.html^headers^
+ plain_bootstrap.html
+ plain_bootstrap.html^headers^
+ subdom_bootstrap.html
+ subdom_bootstrap.html^headers^
+ verify.sjs
+
+[test_stricttransportsecurity.html]
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/moz.build b/security/manager/ssl/tests/mochitest/stricttransportsecurity/moz.build
new file mode 100644
index 0000000000..28769dedc1
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_MANIFESTS += ["mochitest.ini"]
+
+MOCHITEST_CHROME_MANIFESTS += ["chrome.ini"]
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html
new file mode 100644
index 0000000000..e528a40abb
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html
@@ -0,0 +1,28 @@
+<!-- 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/. -->
+
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>STS test iframe</title>
+ <script>
+ "use strict";
+ let windowRef = window;
+ window.addEventListener("load", function() {
+ windowRef.parent.postMessage("BOOTSTRAP plain",
+ "http://mochi.test:8888");
+ });
+ </script>
+ </head>
+ <body>
+ <!-- This frame should be loaded over HTTPS to set the STS header. -->
+ This frame was loaded using
+ <script>
+ "use strict";
+ // eslint-disable-next-line no-unsanitized/method
+ document.write(document.location.protocol);
+ </script>
+ and set the STS header to force this site and allow subdomain upgrading.
+ </body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^
new file mode 100644
index 0000000000..9e23c73b7f
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/nosts_bootstrap.html^headers^
@@ -0,0 +1 @@
+Cache-Control: no-cache
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html
new file mode 100644
index 0000000000..db5d31a96b
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ PAGE BLANK
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html
new file mode 100644
index 0000000000..e528a40abb
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html
@@ -0,0 +1,28 @@
+<!-- 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/. -->
+
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>STS test iframe</title>
+ <script>
+ "use strict";
+ let windowRef = window;
+ window.addEventListener("load", function() {
+ windowRef.parent.postMessage("BOOTSTRAP plain",
+ "http://mochi.test:8888");
+ });
+ </script>
+ </head>
+ <body>
+ <!-- This frame should be loaded over HTTPS to set the STS header. -->
+ This frame was loaded using
+ <script>
+ "use strict";
+ // eslint-disable-next-line no-unsanitized/method
+ document.write(document.location.protocol);
+ </script>
+ and set the STS header to force this site and allow subdomain upgrading.
+ </body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html^headers^ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html^headers^
new file mode 100644
index 0000000000..a46bf65bd9
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/plain_bootstrap.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Strict-Transport-Security: max-age=60
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html
new file mode 100644
index 0000000000..9fca3457e4
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html
@@ -0,0 +1,28 @@
+<!-- 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/. -->
+
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>STS test iframe</title>
+ <script>
+ "use strict";
+ let windowRef = window;
+ window.addEventListener("load", function() {
+ windowRef.parent.postMessage("BOOTSTRAP subdom",
+ "http://mochi.test:8888");
+ });
+ </script>
+ </head>
+ <body>
+ <!-- This frame should be loaded over HTTPS to set the STS header. -->
+ This frame was loaded using
+ <script>
+ "use strict";
+ // eslint-disable-next-line no-unsanitized/method
+ document.write(document.location.protocol);
+ </script>
+ and set the STS header to force this site and allow subdomain upgrading.
+ </body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html^headers^ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html^headers^
new file mode 100644
index 0000000000..e54fc6a1d5
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/subdom_bootstrap.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Strict-Transport-Security: max-age=60; includeSubDomains
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html
new file mode 100644
index 0000000000..040357d8b8
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_stricttransportsecurity.html
@@ -0,0 +1,126 @@
+<!-- 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/. -->
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>opens additional content that should be converted to https</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+
+ const STSPATH =
+ "tests/security/manager/ssl/tests/mochitest/stricttransportsecurity";
+
+ // initialized manually here
+ var testsleft = {plain: 4, subdom: 4};
+ var roundsLeft = 2;
+
+ var testframes = {
+ samedom: {
+ url: `http://example.com/${STSPATH}/verify.sjs`,
+ expected: {plain: "SECURE", subdom: "SECURE"},
+ },
+ subdom: {
+ url: `http://test1.example.com/${STSPATH}/verify.sjs`,
+ expected: {plain: "INSECURE", subdom: "SECURE"},
+ },
+ otherdom: {
+ url: `http://example.org/${STSPATH}/verify.sjs`,
+ expected: {plain: "INSECURE", subdom: "INSECURE"},
+ },
+ alreadysecure: {
+ url: `https://test2.example.com/${STSPATH}/verify.sjs`,
+ expected: {plain: "SECURE", subdom: "SECURE"},
+ },
+ };
+
+ function startRound(round) {
+ let frame = document.createElement("iframe");
+ frame.setAttribute("id", "ifr_bootstrap");
+ frame.setAttribute("src",
+ `https://example.com/${STSPATH}/${round}_bootstrap.html`);
+ document.body.appendChild(frame);
+ }
+
+ function endRound(round) {
+ // remove all the iframes in the document
+ document.body.removeChild(document.getElementById("ifr_bootstrap"));
+ for (let test in testframes) {
+ document.body.removeChild(document.getElementById("ifr_" + test));
+ }
+
+ // clean up the STS state
+ SpecialPowers.cleanUpSTSData("http://example.com");
+ }
+
+ function loadVerifyFrames(round) {
+ for (let test in testframes) {
+ let frame = document.createElement("iframe");
+ frame.setAttribute("id", "ifr_" + test);
+ frame.setAttribute("src", testframes[test].url + "?id=" + test);
+ document.body.appendChild(frame);
+ }
+ }
+
+ /* Messages received are in this format:
+ * (BOOTSTRAP|SECURE|INSECURE) testid
+ * For example: "BOOTSTRAP plain"
+ * or: "INSECURE otherdom"
+ */
+ function onMessageReceived(event) {
+ let result = event.data.split(/\s+/);
+ if (result.length != 2) {
+ SimpleTest.ok(false, event.data);
+ return;
+ }
+
+ // figure out which round of tests we're in
+ let round = (roundsLeft == 2) ? "plain" : "subdom";
+
+ if (result[0] === "BOOTSTRAP") {
+ loadVerifyFrames(round);
+ return;
+ }
+
+ // check if the result (SECURE/INSECURE) is expected for this round/test combo
+ SimpleTest.is(result[0], testframes[result[1]].expected[round],
+ "in ROUND " + round + ", test " + result[1]);
+ testsleft[round]--;
+
+ // check if there are more tests to run.
+ if (testsleft[round] < 1) {
+ // if not, advance to next round
+ endRound(round);
+ roundsLeft--;
+
+ // defer this so it doesn't muck with the stack too much.
+ if (roundsLeft == 1) {
+ setTimeout(function () {
+ startRound("subdom");
+ }, 0);
+ }
+ }
+
+ if (roundsLeft < 1) {
+ SimpleTest.finish();
+ }
+ }
+
+ // listen for calls back from the sts-setting iframe and then
+ // the verification frames.
+ window.addEventListener("message", onMessageReceived);
+ window.addEventListener("load", () => { startRound("plain"); });
+ </script>
+</head>
+
+<body>
+ This test will load some iframes and do some tests.
+
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html
new file mode 100644
index 0000000000..510b6f7435
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html
@@ -0,0 +1,268 @@
+<!-- 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/. -->
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>opens additional content that should be converted to https</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+
+ <script class="testbody" type="text/javascript">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+
+ const STSPATH =
+ "tests/security/manager/ssl/tests/mochitest/stricttransportsecurity";
+ const NUM_TEST_FRAMES = 4;
+ const CONTENT_PAGE =
+ "http://mochi.test:8888/chrome/security/manager/ssl/tests/mochitest/stricttransportsecurity/page_blank.html";
+
+ const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ // This is how many sub-tests (testframes) in each round.
+ // When the round begins, this will be initialized.
+ var testsleftinround = 0;
+ var currentround = "";
+ var mainWindow = window.browsingContext.topChromeWindow;
+
+ SpecialPowers.Services.prefs.setIntPref("browser.startup.page", 0);
+ SpecialPowers.Services.prefs.setBoolPref("privacy.partition.network_state", false);
+
+ var testframes = {
+ samedom: {
+ url: `http://example.com/${STSPATH}/verify.sjs`,
+ expected: {plain: "SECURE", subdom: "SECURE", nosts: "INSECURE"},
+ },
+ subdom: {
+ url: `http://test1.example.com/${STSPATH}/verify.sjs`,
+ expected: {plain: "INSECURE", subdom: "SECURE", nosts: "INSECURE"},
+ },
+ otherdom: {
+ url: `http://example.org/${STSPATH}/verify.sjs`,
+ expected: {plain: "INSECURE", subdom: "INSECURE", nosts: "INSECURE"},
+ },
+ alreadysecure: {
+ url: `https://test2.example.com/${STSPATH}/verify.sjs`,
+ expected: {plain: "SECURE", subdom: "SECURE", nosts: "SECURE"},
+ },
+ };
+
+ function whenDelayedStartupFinished(aWindow, aCallback) {
+ SpecialPowers.Services.obs.addObserver(function observer(aSubject, aTopic) {
+ if (aWindow == aSubject) {
+ SpecialPowers.Services.obs.removeObserver(observer, aTopic);
+ SimpleTest.executeSoon(aCallback);
+ }
+ }, "browser-delayed-startup-finished");
+ }
+
+ function testOnWindow(aIsPrivate, aCallback) {
+ let win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
+
+ (async function() {
+ await new Promise(resolve => whenDelayedStartupFinished(win, resolve));
+
+ let browser = win.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(browser, CONTENT_PAGE);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ aCallback(win);
+ })();
+ }
+
+ function startRound(win, isPrivate, round) {
+ currentround = round;
+ testsleftinround = NUM_TEST_FRAMES;
+ SimpleTest.info("TESTS LEFT IN ROUND " + currentround + ": " + testsleftinround);
+
+ let browser = win.gBrowser.selectedBrowser;
+ let src = `https://example.com/${STSPATH}/${round}_bootstrap.html`;
+
+ SpecialPowers.spawn(browser, [src], async function(contentSrc) {
+ let frame = content.document.createElement("iframe");
+ frame.setAttribute("id", "ifr_bootstrap");
+ frame.setAttribute("src", contentSrc);
+
+ return new Promise(resolve => {
+ frame.addEventListener("load", () => resolve(), { once: true });
+ content.document.body.appendChild(frame);
+ });
+ }).then(() => {
+ onMessageReceived(win, isPrivate, "BOOTSTRAP " + round);
+ });
+ }
+
+ function loadVerifyFrames(win, isPrivate, round) {
+ loadVerifyFrame(win, isPrivate, testframes.samedom, "samedom", function() {
+ loadVerifyFrame(win, isPrivate, testframes.subdom, "subdom", function() {
+ loadVerifyFrame(win, isPrivate, testframes.otherdom, "otherdom", function() {
+ loadVerifyFrame(win, isPrivate, testframes.alreadysecure, "alreadysecure");
+ });
+ });
+ });
+ }
+
+ function loadVerifyFrame(win, isPrivate, test, testName, aCallback) {
+ let id = "ifr_" + testName;
+ let src = test.url + "?id=" + testName;
+ let browser = win.gBrowser.selectedBrowser;
+
+ SpecialPowers.spawn(browser, [[id, src]], async function([contentId, contentSrc]) {
+ let frame = content.document.createElement("iframe");
+ frame.setAttribute("id", contentId);
+ frame.setAttribute("src", contentSrc);
+
+ return new Promise(resolve => {
+ frame.addEventListener("load", () => {
+ resolve(frame.contentDocument.location.protocol);
+ });
+
+ content.document.body.appendChild(frame);
+ });
+ }).then(scheme => {
+ if (scheme == "https:") {
+ onMessageReceived(win, isPrivate, "SECURE " + testName);
+ } else {
+ onMessageReceived(win, isPrivate, "INSECURE " + testName);
+ }
+
+ if (aCallback) {
+ aCallback();
+ }
+ });
+ }
+
+ /**
+ * @param {DOMWindow} win
+ * Test window.
+ * @param {Boolean} isPrivate
+ * Whether the given window is in private browsing mode.
+ * @param {String} data
+ * A message that is expected to be in this format:
+ * (BOOTSTRAP|SECURE|INSECURE) testid
+ * For example: "BOOTSTRAP subdom"
+ * or: "INSECURE otherdom"
+ */
+ function onMessageReceived(win, isPrivate, data) {
+ let result = data.split(/\s+/);
+ if (result.length != 2) {
+ SimpleTest.ok(false, data);
+ return;
+ }
+
+ if (result[0] === "BOOTSTRAP") {
+ loadVerifyFrames(win, isPrivate, currentround);
+ return;
+ }
+
+ // check if the result (SECURE/INSECURE) is expected for this round/test
+ // combo
+ dump_STSState(isPrivate);
+ SimpleTest.is(result[0], testframes[result[1]].expected[currentround],
+ "in ROUND " + currentround +
+ ", test " + result[1]);
+ testsleftinround--;
+
+ // if this round is complete...
+ if (testsleftinround < 1) {
+ SimpleTest.info("DONE WITH ROUND " + currentround);
+ // remove all the iframes in the document
+ let browser = win.gBrowser.selectedBrowser;
+ SpecialPowers.spawn(browser, [testframes], async function(contentTestFrames) {
+ content.document.body.removeChild(
+ content.document.getElementById("ifr_bootstrap"));
+ for (let test in contentTestFrames) {
+ content.document.body.removeChild(
+ content.document.getElementById("ifr_" + test));
+ }
+ }).then(async () => {
+ currentround = "";
+
+ if (!isPrivate) {
+ await clean_up_sts_state(isPrivate);
+ }
+ // Close test window.
+ win.close();
+ // And advance to the next test.
+ // Defer this so it doesn't muck with the stack too much.
+ SimpleTest.executeSoon(nextTest);
+ });
+ }
+ }
+
+ function test_sts_before_private_mode() {
+ testOnWindow(false, function(win) {
+ SimpleTest.info("In public window");
+ dump_STSState(false);
+ startRound(win, false, "plain");
+ });
+ }
+
+ function test_sts_in_private_mode() {
+ testOnWindow(true, function(win) {
+ SimpleTest.info("In private window");
+ dump_STSState(true);
+ startRound(win, true, "subdom");
+ });
+ }
+
+ function test_sts_after_exiting_private_mode() {
+ testOnWindow(false, function(win) {
+ SimpleTest.info("In a new public window");
+ dump_STSState(false);
+ startRound(win, false, "nosts");
+ });
+ }
+
+ async function clean_up_sts_state(isPrivate) {
+ // erase all signs that this test ran.
+ SimpleTest.info("Cleaning up STS data");
+ let flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;
+ await SpecialPowers.cleanUpSTSData("http://example.com", flags);
+ dump_STSState(isPrivate);
+ }
+
+ function dump_STSState(isPrivate) {
+ let sss = Cc["@mozilla.org/ssservice;1"]
+ .getService(Ci.nsISiteSecurityService);
+ let flags = isPrivate ? Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;
+ SimpleTest.info("State of example.com: " +
+ sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example.com"), flags));
+ }
+
+ // These are executed in the order presented.
+ // 0. test that STS works before entering private browsing mode.
+ // (load sts-bootstrapped "plain" tests)
+ // ... clear any STS data ...
+ // 1. test that STS works in private browsing mode
+ // (load sts-bootstrapped "subdomain" tests)
+ // 2. test that after exiting private browsing, STS data is forgotten
+ // (verified with non-sts-bootstrapped pages)
+ // ... clear any STS data ...
+ var tests = [
+ test_sts_before_private_mode,
+ test_sts_in_private_mode,
+ test_sts_after_exiting_private_mode,
+ ];
+
+ function finish() {
+ SpecialPowers.Services.prefs.clearUserPref("browser.startup.page");
+ SimpleTest.finish();
+ }
+ function nextTest() {
+ SimpleTest.executeSoon(tests.length ? tests.shift() : finish);
+ }
+ window.addEventListener("load", nextTest);
+ </script>
+</head>
+
+<body>
+ This test will load some iframes and do some tests.
+</body>
+</html>
diff --git a/security/manager/ssl/tests/mochitest/stricttransportsecurity/verify.sjs b/security/manager/ssl/tests/mochitest/stricttransportsecurity/verify.sjs
new file mode 100644
index 0000000000..e5e4eb5f67
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/verify.sjs
@@ -0,0 +1,47 @@
+/* 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/. */
+
+// SJS file that serves un-cacheable responses for STS tests that postMessage
+// to the parent saying whether or not they were loaded securely.
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ if ('id' in query) {
+ var outstr = [
+ " <!DOCTYPE html>",
+ " <html> <head> <title>subframe for STS</title>",
+ " <script type='text/javascript'>",
+ " var self = window;",
+ " window.addEventListener('load', function() {",
+ " if (document.location.protocol === 'https:') {",
+ " self.parent.postMessage('SECURE " + query['id'] + "',",
+ " 'http://mochi.test:8888');",
+ " } else {",
+ " self.parent.postMessage('INSECURE " + query['id'] + "',",
+ " 'http://mochi.test:8888');",
+ " }",
+ " }, false);",
+ " </script>",
+ " </head>",
+ " <body>",
+ " STS state verification frame loaded via",
+ " <script>",
+ " document.write(document.location.protocol);",
+ " </script>",
+ " </body>",
+ " </html>"].join("\n");
+ response.write(outstr);
+ } else {
+ response.write("ERROR: no id provided");
+ }
+}
diff --git a/security/manager/ssl/tests/moz.build b/security/manager/ssl/tests/moz.build
new file mode 100644
index 0000000000..d39ca0ff17
--- /dev/null
+++ b/security/manager/ssl/tests/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += ["unit"]
+
+TEST_DIRS += [
+ "gtest",
+ "mochitest",
+]
+
+XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.ini"]
+
+if not CONFIG["MOZ_NO_SMART_CARDS"]:
+ XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell-smartcards.ini"]
diff --git a/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem b/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem
new file mode 100644
index 0000000000..9d8aae350d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6DCCAdCgAwIBAgIUL/mOXLOH4zs7sAoXCffwl7mbFPMwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAmMSQwIgYDVQQDDBtFRSB3aXRoIGJhZCBzdWJqZWN0QWx0TmFt
+ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
+BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
+p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
+7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
+kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
+aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
+Ne2NAgMBAAGjHjAcMBoGA1UdEQQTMBGCDyouKi5leGFtcGxlLmNvbTANBgkqhkiG
+9w0BAQsFAAOCAQEAnyhjk+PoHjlqMAOhxlX6+Xn7x5HXzVRmOYPSHlrKtaSDF2/y
+Io1L/Ji7aMJ932MhaUNc83sFfHqGHLTHGhAe3icjuv745WdGsXzykAI+y4Z1gAH+
++0yzUJsYlNmYgn+8kaYTTI9P+HIRoqYPZ2YqshwNNSxdXnSs1jDYCg3yDsKX6DO6
+5vTET9ef5Ea6ePYZiGbwyvYA6lGz1JorrnZPD8uiQnx8CSa20YywYeZH0UGc8Jd+
+G/xRGd+T0eRMy4Xsvv4kZyiAQFPVRsYCDhrgpkeiYsWwlrgZ/LTZN7muBwsPPyy3
+fM2Y6VfVJhEjyv/nv8C44PnKaYYSK7ZWU+dpkw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem.certspec
new file mode 100644
index 0000000000..1b368c26f1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/badSubjectAltNames.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:EE with bad subjectAltNames
+extension:subjectAlternativeName:*.*.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem b/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem
new file mode 100644
index 0000000000..9faffde6d6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDKzCCAhOgAwIBAgIUPU/rVUoMybyCeEAWx++fMAuZ6k0wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8xOTQ2MDIxNDAwMDAwMFoYDzIwMzEw
+MTAxMDAwMDAwWjAsMSowKAYDVQQDDCFCZWZvcmUgVU5JWCBFcG9jaCBUZXN0IEVu
+ZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W
+1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtq
+ZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx
+0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthV
+t2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo
+4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx
+1QOs2hgKNe2NAgMBAAGjWzBZMCMGA1UdEQQcMBqCGGJlZm9yZS1lcG9jaC5leGFt
+cGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2Nh
+bGhvc3Q6ODg4OC8wDQYJKoZIhvcNAQELBQADggEBALocgfctX/9NDtkQ7zAkGl14
+UXkXBCq7vn5fKss2bCG7D6M/+Q4ZJ64/QXGK96tKqdJctiyIN3KArnk4/pWfYCQG
+DgIHXFAgbKUQU0uiGmPwbPQnYq8la6VEarMrzWnGhqVPG1wsqiO1cnecycvm2W7W
+9n7qvw3s2Gf0WZIQKqpNmuhouboeiK1xFxryLWiNaG087WPHb71e1C+1enVb/hJN
+E1KD7dBtBh0sOC6bOFdc4BzhDHnDzPSznSqouotHf5U8YniKoPcDCml544msKwP8
+LCj3tG1VdMxL+p83ETDHG6GLUY20R0E6WJJfvfZ3hdxRUKgaAs+diT3xYXXYqTA=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem.certspec
new file mode 100644
index 0000000000..ac97b2231a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpoch.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Before UNIX Epoch Test End-entity
+validity:19460214-20310101
+extension:subjectAlternativeName:before-epoch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem b/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem
new file mode 100644
index 0000000000..6cdfa86a25
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7zCCAdegAwIBAgIUL5tQyA2FR4V3eMcfGyWTxS4vmp0wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8xOTQ2MDIxNDAwMDAwMFoYDzIwMzEw
+MTAxMDAwMDAwWjAuMSwwKgYDVQQDDCNCZWZvcmUgVU5JWCBFcG9jaCBUZXN0IElu
+dGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahE
+jhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1
+a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1p
+GrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW
+2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcO
+p2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJR
+xDHVA6zaGAo17Y0CAwEAAaMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYw
+DQYJKoZIhvcNAQELBQADggEBAGDlMbdxc1BHrn6l1svyskkHx68lSeTGVucIpClu
+mJIT3rsTR5swYuyvrWe/gqkVqGRv2gIpMUYAJoHWjF4fRyWkIjJz5JnSP5qxFKKk
+NFrjXFpGSqxJGtuMUNNGk7P30RLje5aE00bqrZHokfrokzChC3G3QJPOwvJtP1Gk
+wldQ8AeoHu/u3oEB3caoC1QpFfgF5kunNETSIxX5bTmsjTSSJnJjnf46FQdbOWUh
+P5Qkr6ZNK4ZAOIm+PRRyJ44JiHab+up/cEs17/T5dNcnHCy4TYXquNKwOe35qakm
+iexwhKycaAEM0TcTI1OA5K4tCwFvaiOr4eTzxN28Cemd89E=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem.certspec
new file mode 100644
index 0000000000..835e63f2b6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpochINT.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Before UNIX Epoch Test Intermediate
+validity:19460214-20310101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem b/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem
new file mode 100644
index 0000000000..070de1796b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIUVV34jakVUF6D+5QkiXJ1kemF5q0wDQYJKoZIhvcNAQEL
+BQAwLjEsMCoGA1UEAwwjQmVmb3JlIFVOSVggRXBvY2ggVGVzdCBJbnRlcm1lZGlh
+dGUwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowODE2MDQGA1UE
+AwwtVGVzdCBFbmQtZW50aXR5IHdpdGggQmVmb3JlIFVOSVggRXBvY2ggaXNzdWVy
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+jQIDAQABo2IwYDAqBgNVHREEIzAhgh9iZWZvcmUtZXBvY2gtaXNzdWVyLmV4YW1w
+bGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2Fs
+aG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAhJFzxTYfWOkyMlXOowz7kFnK
+ChekuiwNJB3r0ZGwExAXmrdPKCfBz+OKsPM+3i+bNzIlJNrI2ZhKzylsAJ1mIeRF
+vvpviu5NeZ0TgT7Man5n7WSBylFgSiKymk6V529k0ZrCvVxU1BZK0kXbqYZmGVDt
+TOtkHEKP1YkNnjOs7pYZ7bslkH94oWbT2Cd+W3362o/ko6CCmkhaj2WAqSRyVEUy
++qdQndSpyvp85tRFZLb8bQkCqTtOVCLQfLHYWXRQ4qZQWy8kFPA7OpCMdXlR6Bwu
+vyYi9dw5cRXwAdbLzMpVouPBBXLbTT2b+R+vW5L/LoNGTgrnL+K8WshCSYZ+YQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem.certspec
new file mode 100644
index 0000000000..9aabe21628
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/beforeEpochIssuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Before UNIX Epoch Test Intermediate
+subject:Test End-entity with Before UNIX Epoch issuer
+extension:subjectAlternativeName:before-epoch-issuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem b/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem
new file mode 100644
index 0000000000..3fffeaf56f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRTCCAi2gAwIBAgIUF4tSVpnzLjrUBoa9WMfShlqrEjwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAvMS0wKwYDVQQDDCRUZXN0IEludGVybWVkaWF0ZSB1c2VkIGFz
+IEVuZC1FbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjcjBwMAwGA1UdEwQFMAMBAf8wMgYIKwYBBQUHAQEE
+JjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMCwGA1UdEQQl
+MCOCIWNhLXVzZWQtYXMtZW5kLWVudGl0eS5leGFtcGxlLmNvbTANBgkqhkiG9w0B
+AQsFAAOCAQEAllsGy7B4yROdLH5rmLV6Q6AxUMKk/ixwfCnkt3ibKGtoypvAG6wn
+vevqezN7BUeqIZd0QeoZBEtKwxv87oCiVDgSPXhkGqxryN9i8Zii07Sa27rCVZHd
+F/AJv7qSgl2mYPYAAcyDX5F5ecbc3i9tc96mYSUJawhgPCwNB6PGB+HUr5fSskzx
+j7Rg+0k9TEpk5bcTlsqH2hFvM9ZycEFC0/9trBVvnvh51WTHX/AzZDaaHPyOd2jD
+RxqCMwde0EfpNQmpz39WnRJqs7bf2Cdc080m/apFL9yjZOuCfNaWdezfNjWoBKNC
+S5tpZOoqNYzdV/8VAy74Nv+cXGNX4Ct7RA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem.certspec
new file mode 100644
index 0000000000..8e16705b50
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ca-used-as-end-entity.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate used as End-Entity
+extension:basicConstraints:cA,
+extension:authorityInformationAccess:http://localhost:8888/
+extension:subjectAlternativeName:ca-used-as-end-entity.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/default-ee.key b/security/manager/ssl/tests/unit/bad_certs/default-ee.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/default-ee.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/default-ee.key.keyspec b/security/manager/ssl/tests/unit/bad_certs/default-ee.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/bad_certs/default-ee.pem b/security/manager/ssl/tests/unit/bad_certs/default-ee.pem
new file mode 100644
index 0000000000..c85c051004
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/default-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDiTCCAnGgAwIBAgIUUwG2e1zCLPYPQc2aSZ4UsI/GiK0wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcow
+gccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tghUqLnBp
+bm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcu
+ZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBs
+ZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxo
+b3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCbcbjTQmzRu++LJ/R1KjA99THZ
+aRGG7u0knPs40bz+rIOAR7SllYTvZ1g5HanNG3GZ5+DExVmVtixcrqJFTV0BJsi0
+rv8XR4F3Cdict+rJ+hCSBqu6BGNWdptsaSPiSm+eL//tgjGY1zm9ln1B/OvTYA/n
+f+OV07v44pwRBUe8C9Awb2J3KMHATPciKTk0Pwmh0jXi4FN9ehG1rXZMY2daHoKq
+hzbBc8EaGzPPAyFumHd6wNqWX+/chEtT00SlcJw/lbQZnK8XvUSOhRuUeRdCM5wX
+3w+Gy4P/FrI5tePoR9606GR6plC8QZxT3+Z6lTyCHz3I05+PNXwfmZH3ABSg
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/default-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/default-ee.pem.certspec
new file mode 100644
index 0000000000..554339ff52
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/default-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem b/security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem
new file mode 100644
index 0000000000..3ea3efa405
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/zCCAeegAwIBAgIUN/wGbPUAtIu5Cd7Ojx955mKb5Y0wDQYJKoZIhvcNAQEL
+BQAwHzEdMBsGA1UEAwwUTWlzc2luZyBJbnRlcm1lZGlhdGUwIhgPMjAxOTExMjgw
+MDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowJzElMCMGA1UEAwwcZWUtZnJvbS1taXNz
+aW5nLWludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG
+8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0V
+gg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g3
+04hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l
+0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz
+/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMnMCUwIwYDVR0RBBwwGoIJbG9jYWxob3N0
+gg0qLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAmRFIXGwnArqj8ekdT
+bg+l3r5pkovY9UJpm5hdAvgKuTuFE3fvrfNt+TaftS90i/OlF9LcyIMzWehiJa3O
+axNeKptICjkcX6U6hF+4qvBxCpw3pD+SAsRgWugNzdFdytUxjX5cEnWMFq2MXSNc
+FRpzVJvspN/NdfoF8HYvnYszH4ReKaKF7uZKUVhu0MZ1+zSZBU9ajOyZ+Zo+AIdL
+Hhq3XsTxGcKwjEQZGaRoyKQe6bBxfejISetjsSsW+ZpFQsA1S+oUaIhHR1FzIMiG
+qyEhll/97Ub1PU3u/XNVBUcNETdHO+HERPpHkQPS8pNdZSzPR9aLY/te7iEv634D
+L4J2
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem.certspec
new file mode 100644
index 0000000000..48bb1c6e4a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ee-from-missing-intermediate.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Missing Intermediate
+subject:ee-from-missing-intermediate
+extension:subjectAlternativeName:localhost,*.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem b/security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem
new file mode 100644
index 0000000000..e47d2c7c6f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAiagAwIBAgIUIeBfVf/r/GMhsN7xxnsSiVo7hQswDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjArMSkwJwYDVQQDEyBJbW1pbmVudGx5IERpc3RydXN0ZWQgRW5k
+IEVudGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaNvMG0wNwYDVR0RBDAwLoIJbG9jYWxob3N0giFpbW1pbmVu
+dGx5LWRpc3RydXN0ZWQuZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsG
+AQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IB
+AQCmalWWhf0TDhN3e7UAbXC4mrg766FnGqrGsmbL2wm0q4IuzF9Xc4x26Y2mFaWg
+tkyv0W5YCpSwwbCI2wL6h2iWBZgz55APndydlwCDO5zrDWMx0tlaVSG6ZJiafQFk
+G3Mjky9dzmBMgAsS1L4y+rpP0Wla6WYxjtXmMOMEfLuOOhEK/8CjYru4bWSLKHxL
+baBhvXM3bMbFl/G3c6AAcLMwi+jau3NCZEXORFAoeoxskAIlyfRaqGatXcO19AZp
+5zURdW4WYMVM1DwuUTdKLR/I17xYuTnXdO9qOW6jUssGrspEpg+5Ruec0Q6kJsx3
+ZSJwMRGt69Y0hEGHDzjE3uwZ
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem.certspec
new file mode 100644
index 0000000000..dd8c6707ce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ee-imminently-distrusted.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:printableString/CN=Imminently Distrusted End Entity
+extension:subjectAlternativeName:localhost,imminently-distrusted.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem
new file mode 100644
index 0000000000..d11edd2ca8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAg2gAwIBAgIUY5v46X4dh8IilHQNDrSwO5u+Og4wDQYJKoZIhvcNAQEL
+BQAwGjEYMBYGA1UEAwwPVGVzdCBFbmQtZW50aXR5MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMB4xHDAaBgNVBAMME0VFIElzc3VlZCBieSBub24t
+Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
+BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
+p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
+7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
+kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
+aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
+Ne2NAgMBAAGjWzBZMCMGA1UdEQQcMBqCCWxvY2FsaG9zdIINKi5leGFtcGxlLmNv
+bTAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6
+ODg4OC8wDQYJKoZIhvcNAQELBQADggEBACbMjO/TTypmr3K1qgJHG4YVBzfXrj/u
+eARxTnPhbHivqP/e1wgVyHHhp/5QMBooXN9sykYwA22ZyzXRGYIXpsvfW/gx6fBT
+VVPNF5F6vt+FVtJEUZ4x101cArcWsizao5dRua1t3mE8tGiju+FIDZksF0m/UAQu
+JoNexlBnG2ooLZ8j+vwzLuo74K/VzVDktNPcNRnaF0TAaS078OYgmZfKHvkF/13z
+lyW5vCzqZLDMNnoszP95n4X9EjI+xo4kp+WfRz6c/bq6bQZdtRZAUo4vZSwxYnDm
++tH+r13KhZGekLczAgxK510RJrykZY+XrpZSBpGesSPzlwe8Ne+nkfI=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem.certspec
new file mode 100644
index 0000000000..63c36d34b3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByNonCA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test End-entity
+subject:EE Issued by non-CA
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem
new file mode 100644
index 0000000000..0045add98f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6jCCAdKgAwIBAgIUf83/oBwb560grMAqpqiLeF/len4wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVjEgQ2VydDAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAfMR0wGwYDVQQDDBRFRSBJc3N1ZWQgYnkgVjEgQ2VydDCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+AaMnMCUwIwYDVR0RBBwwGoIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tMA0GCSqG
+SIb3DQEBCwUAA4IBAQCzt3ZusWZ/JxpErDDTk0/Y3DHseHv+7h4UY/wkMyGycn26
+qEDxDcAR2Xkj5b72DXt/et94b+BT0zd1lq8FFfjL29tSIdSsu+7fwO+lN8JMqGjT
+6F4u2BTjgvpAgoO7Zg9fqblE+r0YNOxlXL0EqDzsFFrz/u6PgsBWdKgEY6nWVFzg
+Pay4REzvXrJMIs0UA8mGgN6Bd9kYA7wGatLU7sa0AWtYppaTk1E+e7F2R4K26JEk
+Zo1BXuSITiYB8GKYyQU9q6HyVm38hq1TCzd8lewuLeLuTh69u7vvnMvIjeN+bjSz
+ExfzO3SZQeTHibGLf7oH2e72Fi+MN1OyTu0C6b2F
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem.certspec
new file mode 100644
index 0000000000..9ed9b33db7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/eeIssuedByV1Cert.pem.certspec
@@ -0,0 +1,3 @@
+issuer:V1 Cert
+subject:EE Issued by V1 Cert
+extension:subjectAlternativeName:localhost,*.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem b/security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem
new file mode 100644
index 0000000000..b42f270d1b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6TCCAdGgAwIBAgIUVQ7hP4Gor8gwKkSGLxvowMGx/BswDQYJKoZIhvcNAQEL
+BQAwADAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAtMSswKQYD
+VQQDDCJFbmQgZW50aXR5IHNpZ25lZCBieSBlbXB0eSBuYW1lIENBMIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVK
+tOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7N
+Q/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39Zgsr
+sCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxs
+l62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYl
+nauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyow
+KDAmBgNVHREEHzAdghtlbXB0eWlzc3Vlcm5hbWUuZXhhbXBsZS5jb20wDQYJKoZI
+hvcNAQELBQADggEBAC8PShkXwq0NGcZQ5nsyWwQDW0mRVL8lOyTU8O9GhHNSlwzs
+zhh1QgRmksWIXuxctIODYUlxLnQSCkUVMJjUNpXkZcnmxcgrazC+dfx9B49TQm8a
+9bvjlmZwWGtXi4fuI+mn0447GSOOBTjo5d7rHuwMZM+tZRNifXdBxFi6458xmFbP
+73+IxRc3Yl5jcCmlN+yb5D1elR9oeH2Sn6MrmNr9OEEWumTXb5Wqzb5D1gxNMV/Y
+V0h+u8QLgG3w1KJGL6u/Ahr2KnkfMMq2VuTvJ5wtjSMnA/9UJH1icJ+Ij9grvuZn
+4LbHY07qNnSr50j0Lki4rM/vrI23/T56QKwYndM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem.certspec
new file mode 100644
index 0000000000..a99d84b79e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/emptyIssuerName.pem.certspec
@@ -0,0 +1,3 @@
+issuer:
+subject:End entity signed by empty name CA
+extension:subjectAlternativeName:emptyissuername.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem b/security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem
new file mode 100644
index 0000000000..848e776155
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwTCCAamgAwIBAgIUVYzw/kKPlc6jC3ssIBL3rywd/VQwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohR
+qESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+Kv
+WnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+
+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPv
+JxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5
+Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6
+clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB
+BjANBgkqhkiG9w0BAQsFAAOCAQEAl6tTwSFSaEMVA8N+h/pQuCF+yG7XKBJKgETn
+y5fYobUbYrT5IOhxLotk/DmXTrNRwY7dycRz+zcbWCLypfpKK45XDHe9OXlMa5SE
+RoR++XrS6Q3xSK/Sg1LFZPcc5crNwKGWYTg+1ec4pwN71fKxi0Dk+y2UiJ6MMc1m
+z8o46kqOVQj1Ct4PiHsHQffKKAFPFnU4S49mOK8oEpn42YPMylfDd+xaSS5NEw1R
+SwUceewlaEOzfO1V6RK1EGNtfLqS4A2PHIDOtTvD6E8EMi1MLIvnXEIpduOuk0zQ
+zmC/1+oG/sadO27BdGLFkCLBQu+TYQ+j4B1n53lEV00vJ599xw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem.certspec
new file mode 100644
index 0000000000..0a7cfdfd84
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/emptyNameCA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem b/security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem
new file mode 100644
index 0000000000..2c2e5da70c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDOzCCAiOgAwIBAgIUL6Ownjd7KeqXI1vuKoBnuMLqzpswDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMB8xHTAbBgNVBAMMFGV2LXRlc3QtaW50ZXJtZWRpYXRlMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq
+5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SSc
+An7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39
+ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk
+zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3u
+JtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQAB
+o3kwdzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjBHBggrBgEFBQcBAQQ7MDkw
+NwYIKwYBBQUHMAGGK2h0dHA6Ly9sb2NhbGhvc3Q6ODg4OC9ldi10ZXN0LWludGVy
+bWVkaWF0ZS8wEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBCwUAA4IBAQAJ
+gL0sq/Ak2xI3iYXPHg9ECQVPlnKRM09u7Ykj2ehg6INT9vf4XJ9Dyj8BWt0B3qtY
+y29bUl7SREVCuMgNWRu6OiflmVuwccJkNm7fYRrq9dto3sYTU1VPANq7jJ7J6S9X
+7kizD5UgV664WfA61uSfXdTb1z8NKCbQv3bO4v4mXeAPe1i2GTHN0fk1wpex1ETp
+XqaIgVULkgdl8wIPXXhbGXugogc1uXytcVPxLAqfH329qPQnSW3S71jMqGE2HxzL
+5oIbWqHJJba8YkDjk8/KC9cyk08bA5OXJ4IPxxRJfQ4+q4EPjXpKKhXdfMjsgryX
+wMwN88kEWkSl+VVy4uWk
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem.certspec
new file mode 100644
index 0000000000..d5b5859672
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ev-test-intermediate.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:ev-test-intermediate
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://localhost:8888/ev-test-intermediate/
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/bad_certs/ev-test.pem b/security/manager/ssl/tests/unit/bad_certs/ev-test.pem
new file mode 100644
index 0000000000..36a96ce2ba
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ev-test.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQjCCAiqgAwIBAgIUQyrN+2RfvyLlvHscTU0HNQdN9O0wDQYJKoZIhvcNAQEL
+BQAwHzEdMBsGA1UEAwwUZXYtdGVzdC1pbnRlcm1lZGlhdGUwIhgPMjAxOTExMjgw
+MDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowEjEQMA4GA1UEAwwHZXYtdGVzdDCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+AaN/MH0wOgYIKwYBBQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vbG9jYWxo
+b3N0Ojg4ODgvZXYtdGVzdC8wHwYDVR0gBBgwFjAUBhIrBgEEAetJhRqFGoUaAYN0
+CQEwHgYDVR0RBBcwFYITZXYtdGVzdC5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsF
+AAOCAQEAcpJJ7FQJ8rtBQBitqPDdYgLYIuPq7kCNTwT2B2m60a8nIzc/wGtAePrN
+80x/VbLXeXOW+Pl6tzPWvja17SMzeEsakXkVZnE0qnQyfxxY69T7tKUsbGCUIIug
+Iwqdz11q2MbiWdOlZDh4o8G/dPM6FVLpKzfC6OmeUrLLmYE0Tme0+ejlZ0kVe4GZ
+CYrvCmXpIjXaF84mAWhd9LdJ42iVGusVbdTGN7xh8FACVZS80h4BKQXyYUzrPFpW
+boKOjshNrzvoTi7ihg46HMHNSAE7zdBrAieQpIZvRqCJbqk8hRvdCk6s1vj2w5hc
+NTE6HialIan5T7+ok0K5ok+ILT4Etg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/ev-test.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ev-test.pem.certspec
new file mode 100644
index 0000000000..10f8022585
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ev-test.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ev-test-intermediate
+subject:ev-test
+extension:authorityInformationAccess:http://localhost:8888/ev-test/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/evroot.key b/security/manager/ssl/tests/unit/bad_certs/evroot.key
new file mode 100644
index 0000000000..1d88a930d5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/evroot.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQC1SYlcnQAQjRGh
++Z+HqePRpdtd+uzxiNpXv2QTaI8s5HIs/xCQOMF0Ask6Kkc9vShq7T/c02PPWikU
+dwG92BjXYVv5NWvV08gzaqqMCXE2igbDzURhuT5RQk4XRLsuqtRqqzjOGWghlh+H
+cUoWY2k/CXYc301roSXqzse+Jw04j3ifbN94rjFE7SjEXnkpOGOnoipImAo2pA5y
+1XnJuSXf+MeTNi/9aJenwXVMXpfJZ8Pq3RquiqLMzjSKAWm4Diii1wwalgxvM18t
+oJubZD9av7pJ6Kqpgelg4n2HSAvdVd2UF/oYUJ+7VUzPgaQ5fouoEoo0vfJ4ZcGJ
+5XNPsikFAgMBAAECggEBAJg9VPlNb0x26yPW+T14UjUwz3Ow0WJUxueBdo1F9VaB
+0dAvsr0qrGq8HDiYYJNcUqDY9BSCAQOUd4MUHYZL/zCANjilwBUlcK6dGPPYyhY+
++0dbDd3zLn4W7HVl5rteAlxBxcZuV6A87eVUIh+DBFNHosTEUcPc5Ha3h84MBXJE
+vp4E7xMRjbuz1eCmzIcCnq/Upp7ZsUdZsV452KmITlb1TS+asBPw0V8xipq2svc9
+HsPJ/idK6JQxoQZAvniZsAEcXlCToYNHCGid4QBjTaveYPvWqu+joz3zSh829gwE
+MDa3SNHJ7pjEAxoK/sYO/aCpkL5ST1YU6sT9s0pS+VECgYEA6twssz5f8co3a72V
+vWoXd9LPT6xHVF6S0RpiCbnV5N7UeDRYHBabPIhHQqCeoYdQXBylVBTY0ltJdjLV
+7CqqBSM0MPrUmJJ3en1o4Dj1YaO4lp5gsKJj3vv9pIqbD/OdlbyIsVJnyK3pe1EH
+lI5B5DMknYf32xCdXXRYTYa8wdcCgYEAxZrldqIWRwJI2USlW56b+TKZ2jQexW5V
+jrqCGrzhv1e3nPQR0pBMd0+duh8VGF9gewV0oIIF1uwotmo21jQjLqry/qN1Yauv
+nWRLaNs4yZZMuMluwKxh66ZNBbRGVC9COXb1rN5OzJVTbS31eJVPk/DP2cWPt4ui
+p23VrChNyIMCgYEAwdLvOQYzHFKspkgR+f5CW+somDIvs9tRAyzo1+n8MiQL6SAZ
+zySA/NXjKYNxJxGLKlmhv+BsiD46REfz8DHNmuvQuNNo/Hl0DSzOjq2zJN9/CR6v
+4VZDYdVJILAbBHEjDl5H2T+O0zljxRe8T8ePbYsfnrqFvM7bcDMCZQjbYoUCgYEA
+hSG421aU376ASjFfnvybZSdcVJCs8qNFbWXm5hC/n2R/xnUB1PV3LyMqxwzN75/C
+pt+kFcfEG2r8evnQfDygP37ZPAnwuZ8sMEQ0Mi8QcXCbvBuqTJFXX6apWeB9SZaV
+bZXiK1eTi25HyNUf/t/Jv4iM4NGj5CtlqJvtS5HT5fUCgYEA3El7BrkgyL4LAHe3
+mOl37vdEqQ7Cxdfmy7IkSPrHLagaMxgODYoC6DFGDH/H/TphL3uZMLYbeZ+OkI5j
+LpugQJtqpwsDo7p4dCYmO1vVhD34R27bXRT2qGE+uvW5zVykL1+9KALgjk5J5XCf
+UVFRDKpassHG6z7+kpXRbowlyRY=
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/evroot.key.keyspec b/security/manager/ssl/tests/unit/bad_certs/evroot.key.keyspec
new file mode 100644
index 0000000000..1a3d76a550
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/evroot.key.keyspec
@@ -0,0 +1 @@
+ev
diff --git a/security/manager/ssl/tests/unit/bad_certs/evroot.pem b/security/manager/ssl/tests/unit/bad_certs/evroot.pem
new file mode 100644
index 0000000000..13c3031905
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/evroot.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUIZSHsVgzcvhPgdfrgdMGlpSfMegwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTUwMTAxMDAwMDAwWhgPMjAzNTAx
+MDEwMDAwMDBaMBExDzANBgNVBAMMBmV2cm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALVJiVydABCNEaH5n4ep49Gl21367PGI2le/ZBNojyzkciz/
+EJA4wXQCyToqRz29KGrtP9zTY89aKRR3Ab3YGNdhW/k1a9XTyDNqqowJcTaKBsPN
+RGG5PlFCThdEuy6q1GqrOM4ZaCGWH4dxShZjaT8JdhzfTWuhJerOx74nDTiPeJ9s
+33iuMUTtKMReeSk4Y6eiKkiYCjakDnLVecm5Jd/4x5M2L/1ol6fBdUxel8lnw+rd
+Gq6KoszONIoBabgOKKLXDBqWDG8zXy2gm5tkP1q/uknoqqmB6WDifYdIC91V3ZQX
++hhQn7tVTM+BpDl+i6gSijS98nhlwYnlc0+yKQUCAwEAAaMdMBswDAYDVR0TBAUw
+AwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBABTOHA9XbfLv/C7+
+5KycYXToOIBRSjQ0j2nsiqFda4Jx+aKsvdpdrrbLHvhrpfsA3ZgB2+eKHunVc4fo
+UHNqZllAs2nx+AEinq4GX8iya5BpiyTIxXWu8v06siGgz1GxlJw1cJ/ZnFEQ9IBf
+cCAr5fCoZ4RC+2OVhiSTnYPCKM+zCyw3YpISjNOg1VVkp46Htp+831Eh12YfwvdY
+Fgh1fc5ohYC5GCLRuXKc9PGTsr3gp7Y0liYbK7v0RBjd+GivNQ3dS3W+lB3Ow0LH
+z/fc3qvrhsd58jHpb1QZQzd9bQjuIIM6Gij7TNdNNarEVZfSJjPYLfXosNdYh5fH
+HmbOwao=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/evroot.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/evroot.pem.certspec
new file mode 100644
index 0000000000..3121f3486e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/evroot.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:evroot
+subjectKey:ev
+issuerKey:ev
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem b/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem
new file mode 100644
index 0000000000..a7d657a970
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDHDCCAgSgAwIBAgIUY9ERAIKj0js/YbhJoMrcLnj++uowDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDEzMDEwMTAwMDAwMFoYDzIwMTQw
+MTAxMDAwMDAwWjAiMSAwHgYDVQQDDBdFeHBpcmVkIFRlc3QgRW5kLWVudGl0eTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAAaNWMFQwHgYDVR0RBBcwFYITZXhwaXJlZC5leGFtcGxlLmNvbTAyBggrBgEF
+BQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJ
+KoZIhvcNAQELBQADggEBAImiFuy275T6b+Ud6gl/El6qpgWHUXeYiv2sp7d+HVzf
+T+ow5WVsxI/GMKhdA43JaKT9gfMsbnP1qiI2zel3U+F7IAMO1CEr5FVdCOVTma5h
+mu/81rkJLmZ8RQDWWOhZKyn/7aD7TH1C1e768yCt5E2DDl8mHil9zR8BPsoXwuS3
+L9zJ2JqNc60+hB8l297ZaSl0nbKffb47ukvn5kSJ7tI9n/fSXdj1JrukwjZP+74V
+kQyNobaFzDZ+Zr3QmfbejEsY2EYnq8XuENgIO4DuYrm80/p6bMO6laB0Uv5W6uXZ
+gBZdRTe1WMdYWGhmvnFFQmf+naeOOl6ryFwWwtnoK7I=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem.certspec
new file mode 100644
index 0000000000..0a03bc36f4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expired-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Expired Test End-entity
+validity:20130101-20140101
+extension:subjectAlternativeName:expired.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem b/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem
new file mode 100644
index 0000000000..e03d862761
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5TCCAc2gAwIBAgIUY9VlD+O8GH3DRfxtYTip4pS6eBYwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDExMDEwMTAwMDAwMFoYDzIwMTMw
+MTAxMDAwMDAwWjAkMSIwIAYDVQQDDBlFeHBpcmVkIFRlc3QgSW50ZXJtZWRpYXRl
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+jQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0B
+AQsFAAOCAQEANf+C+WsnAgYfISDS37prll2DOGYWKajcVZNzkScDzNGkK2s0c/td
+Mb+HXehqvYz20hT4wEwQZnPt9qMWH7bBEWiJfw85OINbKmG/i0gjZZDgbFMMdHvc
+j6BXJoxL0gAy8fOQyTDuMNX0NBJzSmWhzBsL99BHAWdG6XXQTzSyumiekc8ip4GG
+EhJvArbZwgIBigzdpbc/lQI0dR2qER0BUXamWU8fi2RuvQqtEi2ANjJHrWOillXU
+PR3j0F2LyvoiYlhiNhE1g3JH4VYZ+3eboRCrjel4J3rZHksN17r6+Fla1/YZdz2l
+nAGA5McLBKXYBVcARV4adXlBe1z79TiDeQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem.certspec
new file mode 100644
index 0000000000..38a0abd8a4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expiredINT.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Expired Test Intermediate
+validity:20110101-20130101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem b/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem
new file mode 100644
index 0000000000..2d00c1ca6c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIUfTvvLki+Legnl10m+fcP7kxDp1YwDQYJKoZIhvcNAQEL
+BQAwJDEiMCAGA1UEAwwZRXhwaXJlZCBUZXN0IEludGVybWVkaWF0ZTAiGA8yMDE5
+MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAuMSwwKgYDVQQDDCNUZXN0IEVu
+ZC1lbnRpdHkgd2l0aCBleHBpcmVkIGlzc3VlcjCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNcMFowJAYDVR0RBB0w
+G4IZZXhwaXJlZGlzc3Vlci5leGFtcGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYI
+KwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZIhvcNAQELBQAD
+ggEBAF4T4Ye5qxMLGz/5dOgRxn/LXh717T2FKxXcWM50cShOGBU5NOjIVB5fkRS7
+uJsfgvHrT05123/ttDzVZefz2N1ZvXSxuzzJoA0DFhX/XSr4VXgLPEYHO0usDwxE
+HJq8ZmtgM4Xw39QlFU4Sw8ZA5tjOfFjdKv5Ow1ulbVVGZyk/9FsmcsSYZuPXFIvH
+DZyxrz8wFJOyrB/6Q/fOcTHeeh7bcC8YSAVtQvCFYOoKK1dJHwttpIa4/mdfsjpl
+aP+ixciWjoKNmiPTqTZy8BdDSlh+pRFJppGfNfkub55T4+5XegcQPUWkJA1w6woM
+hNZU708Mvf7u8j3XJuWlxNI8rHI=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem.certspec
new file mode 100644
index 0000000000..855f454221
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/expiredissuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Expired Test Intermediate
+subject:Test End-entity with expired issuer
+extension:subjectAlternativeName:expiredissuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem b/security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem
new file mode 100644
index 0000000000..9f306fcb94
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAeWgAwIBAgIUBT+m5UYwTuABfetlYca4GEtsukAwDQYJKoZIhvcNAQEL
+BQAwGTEXMBUGA1UEAwwOVW5rbm93biBJc3N1ZXIwIhgPMjAxOTExMjgwMDAwMDBa
+GA8yMDIyMDIwNTAwMDAwMFowGjEYMBYGA1UEAwwPSUROIENlcnRpZmljYXRlMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08
+E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc
+1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAP
+DY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQ
+gAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqV
+YR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQID
+AQABozgwNjA0BgNVHREELTArgilidWc0MTM5MDkueG4tLWh4YWpiaGVnMmF6M2Fs
+LnhuLS1qeGFscGRscDANBgkqhkiG9w0BAQsFAAOCAQEABk5q77He1tAU87VBBq2R
+E/lyg2rTNFU6oOgGUY9sWbz+RbTSxk8xjg89d6BhW8K9KFEzkTdS74sjxTKc9pMB
+jXDpKL5LzhPtBBRndtzsAaaKMfnR0r7EkxihAP7TNTkLgYBQzIyOXIAZvyDr+fnG
+J3om1g4pkt1rVFc4CtrTqSzr4Bdm7vwYWIU5ogwsF9Gpg7shShacbJam4pA6VzHE
+fl1v1bAmpCyfGiYFbDTSLLFMYiZvf03sy8MrCCxPftGqr9n6trkQfN+hzbImGJFw
+hkSt9hniJmVVglf0UVBmJTJ1Ud8rl9aoeOr9Ic2QvQ6HTvq5xzu8NECInNkzDENt
+Kw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem.certspec
new file mode 100644
index 0000000000..b3d840fbd3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/idn-certificate.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Unknown Issuer
+subject:IDN Certificate
+extension:subjectAlternativeName:bug413909.xn--hxajbheg2az3al.xn--jxalpdlp
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key
new file mode 100644
index 0000000000..d43495f851
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICcAIBADANBgkqhkiG9w0BAQEFAASCAlowggJWAgEAAoGAANKbsS+4T93NKbOl
+GctmxDuNj4vlRbp5OEzmY+0D33WZFgDrkgeQ0lMM7OVE25mnHwWJaj7SBxZVNKqZ
+BX5HxH47yBrab6HhLjcmi1BGpVJo+drXzLSF2BouGdUNTwtoVKyvbXvmnZoIMTbh
+WvqPU8HIyE/GB3J53Q5V1zaaW90CAwEAAQJ/PEllBwvzkMJR1aLFJ3xbX9C97oXK
+1/4rJ5grsoURSlBwBANq4c+K5Usl5Ns5IVq9fpA/YYwtiy8IzGzRLbzNciBeSUW2
+s984nl5D3goUi7LITiQx/b5ZILBEuycvRez/ByG337YDl/xhOp6jXCIwBTDK6PkV
+nFNN878JEJUZAQJAD58XWXyFuAUbnGmvtV71dsmW29CQR9DM3ludYOpcZ/5PrGe+
+gD9LasWj8FD3a5ZvsU9c8QV2HlrebdlgsYO6VQJADXtjcRLOYaVRaMD5yThvsnmr
+QMug1Ukza7plJ3JjqseCYRosgdm2Nc94xAAYhZ4BjF6QBtEuPS7m80bnn6QzaQJA
+Cf1smj6m6RrjIHD5/BwhD/k1L5e+XR7rlRuzloHp3FtnKlMiIbPYkAyanZm50KTh
+AtxFDKG4ewsTid5lFsCuDQJAAUG4MkkbfdSoMwiSACTHnK5kvUR9+IO7TFZyqWur
+SLcSOzTyYyRFLNzrF/IeVw40fL4v1MLY+ZEOrCy22JW4yQJABFjdau4YyIsvm4Hx
+vDB1riDcH5lz0gck8gsGBD1hR8h4nUoHroi8gshDjIk+AXsTlH9i4LGJWKMetmSx
+nmTT4A==
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key.keyspec b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key.keyspec
new file mode 100644
index 0000000000..21ed73d60b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.key.keyspec
@@ -0,0 +1 @@
+rsa1016
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem
new file mode 100644
index 0000000000..de4b933cf1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtzCCAZ+gAwIBAgIUUTSENycPfy4QVpNTpBCq/pVp3nowDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowKTEnMCUGA1UEAwweSW5hZGVxdWF0ZSBLZXkg
+U2l6ZSBFbmQtRW50aXR5MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgADSm7Ev
+uE/dzSmzpRnLZsQ7jY+L5UW6eThM5mPtA991mRYA65IHkNJTDOzlRNuZpx8FiWo+
+0gcWVTSqmQV+R8R+O8ga2m+h4S43JotQRqVSaPna18y0hdgaLhnVDU8LaFSsr217
+5p2aCDE24Vr6j1PByMhPxgdyed0OVdc2mlvdAgMBAAGjZTBjMC0GA1UdEQQmMCSC
+ImluYWRlcXVhdGUta2V5LXNpemUtZWUuZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEE
+JjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3
+DQEBCwUAA4IBAQCf8g6xupfTgr6N78Zru3/XdJ9zzf8gNLNDEzncs8YipkkPEkUY
+F2yGf0bNOwqZnHMTo0D4lwaNTFFv1obY7kZd5auFt9WqI1GUWdPM7FaQS4a1oAqU
+6gWCz+tIbTJXVDrAJ+yYXntV030Qqr7i0DFvwJz5fPFk6V1192Xa2aLUDoenxv5d
+eiczsYvJMgBD6zkXSef+rSf24FgS23km6DHja89recbZ/fz09HJ5q9NDQ/EMilHf
+EJMP1Ny/73Ht6V+Q9RJdtA5PXDyOgc7WHjFY+Z2LZH51VDg1XJ49W4ClYqxB1Yg4
+4qhh6OwIoFEipevWJ2uGNwJfD9KV4jiDwUAB
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem.certspec
new file mode 100644
index 0000000000..02b595dc9a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequateKeySizeEE.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test Intermediate
+subject:Inadequate Key Size End-Entity
+subjectKey:rsa1016
+extension:subjectAlternativeName:inadequate-key-size-ee.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem b/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem
new file mode 100644
index 0000000000..0899f9d613
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgIUJ8CbecVvBfSdpQSAm71sCRjoCTUwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAvMS0wKwYDVQQDDCRJbmFkZXF1YXRlIEtleSBVc2FnZSBUZXN0
+IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjbjBsMAsGA1UdDwQEAwIBAjApBgNVHREEIjAggh5p
+bmFkZXF1YXRla2V5dXNhZ2UuZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIG
+CCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUA
+A4IBAQCog4fCed4qaa2aLX7kx1l+03kSlDg9ZmnyIDR2kBsFZePto9TM6488Gaem
+m3GEHT6d/RQK1g7zG40lmYDAg33PShzxZqgCmyxzpSfc1HbFfh9w4m47JANY7Q7E
+NWDg42P64v/+Vdvxoprn594XklxYc8ci+y0NL9c79BWVEBlpyXaRwCJiKwfkcr6o
+1X/07hkbKCPi9uBR6GFPtQSJmnFRKGT4ePdRHAccqn6CExIhamCK0I/y+lsr8Atg
+ZtSrWLnXbqwYvdDyofhMWttDXPXCiWEdUq031KpQ/ENXxHi2cs7eEAtjjSZNefBv
+vX+wC4ZNznyAb8tR5Zvx3sDoasRT
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem.certspec
new file mode 100644
index 0000000000..4d553890b9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/inadequatekeyusage-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Inadequate Key Usage Test End-entity
+extension:keyUsage:cRLSign
+extension:subjectAlternativeName:inadequatekeyusage.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem b/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem
new file mode 100644
index 0000000000..676c46dfd0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0DCCAbigAwIBAgIUTfYfuzoa/9f2RQTT/c6KssuRWkkwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAUMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk
+e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg
+KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI
+YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi
+lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL
+HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjGDAWMBQGA1Ud
+EQQNMAuCCTEyNy4wLjAuMTANBgkqhkiG9w0BAQsFAAOCAQEAYeN7Q1I0ihJhj2GG
+o/0XZM/WuxPJ0JaJzEWMvf4ZGYeSaxp86tyMFNUOhGaDCvHCu9ZiruHUEC1J5Z7h
+z4kSwG2h95SKLsOr5d3NDN3bGJJtzPARz8xeynFHI2f1AAiGM+ai714jNUgtVoXx
+a1U5J+Ls+xbIzNiRPRzze8shnHrqTXmHL+R4G3F9MRsLUPQs4tVubi2J8+lVRrsK
+QAFfnQp3VVGoWfDAj5OFDC9kCf6fVz9sVjTjHD2LuFCKvagOIxG62Y1qVhd5CLyJ
+HUAzeorgZuUl7QXN2RT2w5itIcpzGRbNmd5CoN7Tbm7TyuS6BIBWyjjxlWM5Ue/j
+ae8XOw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem.certspec
new file mode 100644
index 0000000000..7662338470
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/ipAddressAsDNSNameInSAN.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:127.0.0.1
+extension:subjectAlternativeName:127.0.0.1
diff --git a/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem b/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem
new file mode 100644
index 0000000000..3f373d09f7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDNjCCAh6gAwIBAgIUGUp8qIucVUZ0D+cPoEILDVfteu8wDQYJKoZIhvcNAQEE
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDExMDEwMTAwMDAwMFoYDzIwMTMw
+MTAxMDAwMDAwWjAvMS0wKwYDVQQDDCRUZXN0IE1ENVNpZ25hdHVyZS1FeHBpcmVk
+IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjYzBhMCsGA1UdEQQkMCKCIG1kNXNpZ25hdHVyZS1l
+eHBpcmVkLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYW
+aHR0cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQQFAAOCAQEAYMtZZk0A
+c3xvnK0PSwNxsWidAdOEY5uOKJTPT1LZFUQsNHw/2f7csfDneTeLNoCNKH7LPF3t
+J+zwUMzq1Ut0vIRSs5r84P7LK7KaBRrSwWPSnTP90X4VX3IVIQ7dbwTkrlUaRzfs
+B4Pqa/p2GVuvrLmbtmd2SDw+52GubJYVOF6u2s4KKgUGhHWtegzQOsVKTFEOoqBr
+X3yDEJhK7M82NMiGtq3Fr5F0sLD6DuGL0Mm7ei9junSLS4sH0M+Hac2BVmXwYNTS
+ekKzyjmZ1TYRlo2sCgYyYfCLcTOswG7uVHLU+ie0+Dbaik2NcolUgFNnC7Uk3E+t
+DhSRNeOtKglSxw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem.certspec
new file mode 100644
index 0000000000..e4c2b7008d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/md5signature-expired.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Test CA
+subject:Test MD5Signature-Expired End-entity
+validity:20110101-20130101
+signature:md5WithRSAEncryption
+extension:subjectAlternativeName:md5signature-expired.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/md5signature.pem b/security/manager/ssl/tests/unit/bad_certs/md5signature.pem
new file mode 100644
index 0000000000..a2d46f868d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/md5signature.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDLDCCAhSgAwIBAgIUPbgiSgWOnRxOdlLj8dU/qbEhukQwDQYJKoZIhvcNAQEE
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAtMSswKQYDVQQDDCJUZXN0IEVuZC1lbnRpdHkgd2l0aCBNRDUg
+c2lnbmF0dXJlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESO
+FtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVr
+amRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWka
+sdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbY
+VbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6n
+aOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHE
+MdUDrNoYCjXtjQIDAQABo1swWTAjBgNVHREEHDAaghhtZDVzaWduYXR1cmUuZXhh
+bXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9j
+YWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBBAUAA4IBAQCFfTcMksF9oZhEnsAmGhLx
+u7LvPXarRynLOia8+un6Lx33ZtvItBpLHtbbPkPvlsiv7oyKspzNbk5IpKdYEQbd
+dYx6diFkEP8/xAnX8o1Jng1rbynLmVhnabRSnJfTOihGFlusN3UAVrmSZyZVAdEl
+knnKqDL+U+CSVMdEeaqBECNQOFIUL7F+uutt6jfKNuN/fpDHEz9vCfUGiuZVC7Kd
+2fVCwhDDOC7bN6rvQP3e+06uLoXGGleOM1aZjhNNdiYHvqq8veYiGLZckmkcF1a8
+XqCDuLoQDs0dVvsj4pNEyciXuk5wCuV6bgw4s4unPFMP9EBmgWQGFaW+iePqT6uS
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/md5signature.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/md5signature.pem.certspec
new file mode 100644
index 0000000000..02742d910e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/md5signature.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test End-entity with MD5 signature
+signature:md5WithRSAEncryption
+extension:subjectAlternativeName:md5signature.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem b/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem
new file mode 100644
index 0000000000..d5782da2cd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDKTCCAhGgAwIBAgIUXWNmi3NOMsq8iBKvohtG0kX2YFIwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDEzMDEwMTAwMDAwMFoYDzIwMTQw
+MTAxMDAwMDAwWjArMSkwJwYDVQQDDCBNaXNtYXRjaC1FeHBpcmVkIFRlc3QgRW5k
+LWVudGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaNaMFgwIgYDVR0RBBswGYIXZG9lc250bWF0Y2guZXhhbXBs
+ZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxo
+b3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCt7JzZlMgD/ZHZobiGxcfE/EPQ
+/V405Xu7DGdTJh5cTiFF1h3sQl06BfrCEAo0yhOrpPyMtmhTI+rMyF7PvFWZMQ89
+bk9y4cZ7jG1NEd6B+jlYfD4mFbLR3AbMEbf6QVjYDK29+XnjnhvczT5NPPl8g7rF
+6y1FhFcFMTGiGZCOyhCz0nXbG/LpP/alH+WucXRdpMLUQbEXEDzWOq5WJ1ZPS556
+2Ouurfr1mkydAlXCGc5RYPVLRY48CwX2z+kfHRnF4TCb3ck5oOlqabP+bc+HD7EE
+UgEEdnE6zpUs1D7s8C8Mp89LUcXx+s51/ZUQvGE4btBMML0lNImwwbqxbv02
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem.certspec
new file mode 100644
index 0000000000..262f08d6be
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-expired.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Mismatch-Expired Test End-entity
+validity:20130101-20140101
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem b/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem
new file mode 100644
index 0000000000..b70a632693
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDLzCCAhegAwIBAgIUcYUtGOYg2nrx/OAYr+cxTZVbv34wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDMzMDEwMTAwMDAwMFoYDzIwMzQw
+MTAxMDAwMDAwWjAxMS8wLQYDVQQDDCZNaXNtYXRjaC1Ob3QgWWV0IFZhbGlkIFRl
+c3QgRW5kLWVudGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqI
+UahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvi
+r1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/x
+fq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD
+7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnv
+uRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj
++nJRxDHVA6zaGAo17Y0CAwEAAaNaMFgwIgYDVR0RBBswGYIXZG9lc250bWF0Y2gu
+ZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8v
+bG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCu5v5Saw0htF59xmKf
+CQJ7YI4M/TvuNJjhVdwq5phvEw3k+qweEdpLHERGu4nrzyDTMKqN8kXO6tXUspnh
+LpXCvmaDLNxx/UyYofWslqYqFwM4vQ4l5QEUriR1ndMN256ELRSx2cEpWCi9xHMb
+1cqB12ulLMCpitmK+NXpflViZy7HliTQOCdwhLPDNWRKsRF85EHOuFzcx4uqfWdw
+5zwA0zVOYG21fyY51bhy4oy7HjTWUOrGi/klwzz2TEDTUX6nRHYagrFxO7C1GMlg
+pe32InUfLU0rR8G9lP/tSug8HawSH/TCE+63jo2BGWaIYGSd+k11QzU+CsT15+A2
+tbAC
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem.certspec
new file mode 100644
index 0000000000..947eb7d678
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-notYetValid.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Mismatch-Not Yet Valid Test End-entity
+validity:20330101-20340101
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem
new file mode 100644
index 0000000000..4902855da5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDOTCCAiGgAwIBAgIUO1bWrdrsbGfiDSe6i14vOA3vWsIwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNT3RoZXIgdGVzdCBDQTAiGA8yMDExMDEwMTAwMDAwMFoY
+DzIwMTMwMTAxMDAwMDAwWjA1MTMwMQYDVQQDDCpNaXNtYXRjaC1VbnRydXN0ZWQt
+RXhwaXJlZCBUZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9
+sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5
+TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7
+xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHd
+tMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l
+8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjWjBYMCIGA1UdEQQbMBmCF2Rv
+ZXNudG1hdGNoLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
+AYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAlTFY
+rHGARngEHz3XEngdY1E3hf2GdITTHssgVRvpW3IvNmUJDhk3zVqvjb2hIVDmxFRv
+ad7yCKK7FQum2A1MKZh/Pl7ylWv6vkdtB8HSg9F5qYifyUD/XEjYp3uSrfAXW7Pp
+XHZHgBR6nlOB1EpAqrLtpEjxMX6F0fcXClOlWf0UsPLv1M6JvPNns/4tbHBnrhfm
+e0DqBMfZIJV7x633eZhpLQmOIA27rZtpElWZsA6Gtenl3szr8mpOgy3hxuEs9oFn
+cigHg2ECzaOKM5ayTP0dc2OVbVgUX/2doF8pGHiIKdsThYNLNEOioBZjJhQlreZE
+K67bzkMDhLQmMBUMtw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem.certspec
new file mode 100644
index 0000000000..adc8ebaf8b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted-expired.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Other test CA
+subject:Mismatch-Untrusted-Expired Test End-entity
+validity:20110101-20130101
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem
new file mode 100644
index 0000000000..708fdbe324
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDMTCCAhmgAwIBAgIUIeL7ME9jR6UnL5tbkKtCbMzAstAwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNT3RoZXIgdGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAtMSswKQYDVQQDDCJNaXNtYXRjaC1VbnRydXN0ZWQg
+VGVzdCBFbmQtZW50aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+uohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGoby
+a+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWC
+D/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfT
+iEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXT
+Ce+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+
+SSP6clHEMdUDrNoYCjXtjQIDAQABo1owWDAiBgNVHREEGzAZghdkb2VzbnRtYXRj
+aC5leGFtcGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6
+Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZIhvcNAQELBQADggEBABPy72/P12xlsoLz
+nImXr6gkVB84x2t32n9berLA5BuvB7XW3V5AnHM4cvEPw/SkWgkjb/CgKy/jE5mo
+ExTyrtneG3PcX5KgIJedKgKYPjyji01SnsT4jlWlzRND2ISC5sv/0xQovrEsgkAP
+pOLodr5WfjZhE0E0gA3TNwFW+/xDZeMwYeVuv1XwPtmto/r026VYhlQfhi7cZ0Rf
+6/sW7zBKDhAgg2Puk32w8J7eP+KxkIbAPdfeLrWU4hl4U5Srnt7tFaTaIng5YAGC
+6U3M8fEETS0YIgl1wRU77ywkIeSGX4jcRF3l5zwoexiDbSjSuJ/4lg6YItfW/Rd6
+xbzRon0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem.certspec
new file mode 100644
index 0000000000..91c5f548b6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch-untrusted.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Other test CA
+subject:Mismatch-Untrusted Test End-entity
+extension:subjectAlternativeName:doesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch.pem b/security/manager/ssl/tests/unit/bad_certs/mismatch.pem
new file mode 100644
index 0000000000..2699b8196c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIUU/pv4TGBlwPGJxIpKC/9rJ31RaUwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAjMSEwHwYDVQQDDBhNaXNtYXRjaCBUZXN0IEVuZC1lbnRpdHkw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ
+PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH
+9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw
+4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86
+exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0
+ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N
+AgMBAAGjeTB3MEEGA1UdEQQ6MDiCF2RvZXNudG1hdGNoLmV4YW1wbGUuY29tgh0q
+LmFsc29kb2VzbnRtYXRjaC5leGFtcGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYI
+KwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZIhvcNAQELBQAD
+ggEBAIQ9iWQWypTHSxt73EdcGiorVJTczAisC4ar683CcwwcbonkG8c6Ca8OZZFz
+QWYZYwKO6xouFMpKYoby933dKiEwQrtyckaBKHsGlgOQWKzY4BiaCULeYg24d6S0
+rWzKOAwZgmA5eaAqHxmoIz2qefNLOED1sYFzg85jeT6vL+SeEuHFh8GOGct2RHEA
+I+EcdKgYT6/kFrHtxEkCu0QULVVk0g9PDlVeZZ8eOOMBYi1bsQq03QjKFTzUaQY2
+18iwDvWbIgh21MwTYGMDhCvEfXYXWKe+AqiMHNYDyfk2lmgxOHB48J7Akow4MiBX
+zGuf1lvgdACSETNaPq5TXniBnMI=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatch.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatch.pem.certspec
new file mode 100644
index 0000000000..b93599fc88
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatch.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Mismatch Test End-entity
+extension:subjectAlternativeName:doesntmatch.example.com,*.alsodoesntmatch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem b/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem
new file mode 100644
index 0000000000..4c1e16b065
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxDCCAaygAwIBAgIURUZJw9lGOsp7dEOmmehwl1FeaBwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAiMSAwHgYDVQQDDBdkb2VzbnRtYXRjaC5leGFtcGxlLmNvbTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAATANBgkqhkiG9w0BAQsFAAOCAQEABN5ha7Gn+zVm7AdnMKIIuDt5C5X3CfF7
+YKRgqPby5clFJybaYHz9msYoxgSFAyoA+nuD1KCCSbQA77rMFKVJ2mDQJh7a1tEI
+RdSDZMhgKbuA8qG3knr0Q6eBjfwDI4Gx8SIxU9BdJ3NhY9a31TQfzuhKeV+hN8FR
+9GGOO1121x29U4jih4vtt35blMZu4VLrvMAR+aapyBOTK2UzZeqYb8JTyWQrkdBT
+03nvtThsVl1SMZuyKDwCBAbenDqCknuZ/1FED7njbl9VuoEz8ApMopn0XnliM4RU
+AuyvWWF9oBY6+xLIZsDfK+shmlaH5hRUMQC25NVwOpE/VfIxdJPp6w==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem.certspec
new file mode 100644
index 0000000000..86ef45b7ce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mismatchCN.pem.certspec
@@ -0,0 +1,2 @@
+issuer:Test CA
+subject:doesntmatch.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/mitm.pem b/security/manager/ssl/tests/unit/bad_certs/mitm.pem
new file mode 100644
index 0000000000..37567fe10c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mitm.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+jCCAeKgAwIBAgIUS4dnYahNJmxk0T1Z0H+W8A+5tcgwDQYJKoZIhvcNAQEL
+BQAwGTEXMBUGA1UEAwwOVGVzdCBNSVRNIFJvb3QwIhgPMjAxOTExMjgwMDAwMDBa
+GA8yMDIyMDIwNTAwMDAwMFowMDEuMCwGA1UEAwwlVGVzdCBlbmQtZW50aXR5IGlz
+c3VlZCBmcm9tIE1JVE0gUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAab
+bhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmts
+Du0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhI
+H6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8
+rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kX
+Mbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMfMB0wGwYDVR0RBBQwEoIQbWl0bS5l
+eGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAKNQhLbnyWK6cd8qvK8ZD/8cY
+Je/PuuCew7E76VtbQFX3/d5foJJE2sKY/SHtfvFocAQbLp68/Pydfs2CmrvFlyFa
+3rSUMJBSoFDOJ4az1Ht5iF374fnFCx61CXpxYl6b7SguWgZRd5qrstsTMw3cL+Er
+/8f88N+aOX8P92eU20S4ZyCq0aO1yOBoOpmidPGqtNTuCQa32kyi96mCDGoBrWii
+k9OQb8nrr/sORVGSIK377+/PdXyjPCPvDwYhhVEyIomYQrXXWrL6TTy5I5e1kpIs
+9zi7T7XnQbcRaHSWOwYkRGAkuKlXbq7eS3LF+PSHSzcBr5r9vYCXBZMmtBToMg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/mitm.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/mitm.pem.certspec
new file mode 100644
index 0000000000..1439391f1b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/mitm.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test MITM Root
+subject:Test end-entity issued from MITM Root
+extension:subjectAlternativeName:mitm.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/moz.build b/security/manager/ssl/tests/unit/bad_certs/moz.build
new file mode 100644
index 0000000000..9390fd9c87
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/moz.build
@@ -0,0 +1,71 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'badSubjectAltNames.pem',
+# 'beforeEpoch.pem',
+# 'beforeEpochINT.pem',
+# 'beforeEpochIssuer.pem',
+# 'ca-used-as-end-entity.pem',
+# 'default-ee.pem',
+# 'ee-from-missing-intermediate.pem',
+# 'ee-imminently-distrusted.pem',
+# 'eeIssuedByNonCA.pem',
+# 'eeIssuedByV1Cert.pem',
+# 'emptyIssuerName.pem',
+# 'emptyNameCA.pem',
+# 'ev-test-intermediate.pem',
+# 'ev-test.pem',
+# 'evroot.pem',
+# 'expired-ee.pem',
+# 'expiredINT.pem',
+# 'expiredissuer.pem',
+# 'idn-certificate.pem',
+# 'inadequateKeySizeEE.pem',
+# 'inadequatekeyusage-ee.pem',
+# 'ipAddressAsDNSNameInSAN.pem',
+# 'md5signature-expired.pem',
+# 'md5signature.pem',
+# 'mismatch-expired.pem',
+# 'mismatch-notYetValid.pem',
+# 'mismatch-untrusted-expired.pem',
+# 'mismatch-untrusted.pem',
+# 'mismatch.pem',
+# 'mismatchCN.pem',
+# 'mitm.pem',
+# 'noValidNames.pem',
+# 'notYetValid.pem',
+# 'notYetValidINT.pem',
+# 'notYetValidIssuer.pem',
+# 'nsCertTypeCritical.pem',
+# 'nsCertTypeCriticalWithExtKeyUsage.pem',
+# 'nsCertTypeNotCritical.pem',
+# 'other-issuer-ee.pem',
+# 'other-test-ca.pem',
+# 'self-signed-EE-with-cA-true.pem',
+# 'selfsigned-inadequateEKU.pem',
+# 'selfsigned.pem',
+# 'test-ca.pem',
+# 'test-int.pem',
+# 'unknownissuer.pem',
+# 'untrusted-expired.pem',
+# 'untrustedissuer.pem',
+# 'v1Cert.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'default-ee.key',
+# 'evroot.key',
+# 'inadequateKeySizeEE.key',
+# 'other-test-ca.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem b/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem
new file mode 100644
index 0000000000..0086ff5310
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAeugAwIBAgIUc9BglcR5VS6X67v9lA6Nojd9gPUwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjApMScwJQYDVQQDDB5FbmQtZW50aXR5IHdpdGggbm8gdmFsaWQg
+bmFtZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAGjNjA0MDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0
+cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAbE3XcdHBwtBh
+Kqy2B6BDvVnYn06/rx0T+6HrWPkn5BRWwhQW3K6OBf/Mfm+F9QI73YGWSalVZPs/
+pIpfM2qvjXztkEJ0ZfZTEjo6b0Jd5gJ72XFYC0CZbRS8C81ck4f4hjegT+Fbyp3f
+hzNWHbdfo6yRIVtv3aGcfzn1K8RWy33620t+mNroaSNQoVVuO+TVGHWEp5g4vn8A
+GxUc0gx0ePNtENLpEC071aPBO4OEU/SiT1bVCp9XNCP6GSyVAl1b/VguBWn+G9pD
+m6kn0PjaNxLH9JQESIKrgHyj1s0vBizLdb4Yx+hmQVdHUESt99cmlkeJi6fBHwUU
+ekQ1Wipf1Q==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem.certspec
new file mode 100644
index 0000000000..87088e87e5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/noValidNames.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:End-entity with no valid names
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem b/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem
new file mode 100644
index 0000000000..79cc10aa68
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDJjCCAg6gAwIBAgIUH+AmYB0Pf6g+gBjtVqu94DStb3UwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDMxMDEwMTAwMDAwMFoYDzIwMzIw
+MTAxMDAwMDAwWjAoMSYwJAYDVQQDDB1Ob3QgWWV0IFZhbGlkIFRlc3QgRW5kLWVu
+dGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1u
+togGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6
+pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqL
+KkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3Zlqq
+fgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3sv
+Im9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6za
+GAo17Y0CAwEAAaNaMFgwIgYDVR0RBBswGYIXbm90eWV0dmFsaWQuZXhhbXBsZS5j
+b20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0
+Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQB1xgChilLhS38NJG3FU3vyD8LN3uGV
+wgzFB33egzO4s4GkPK5zjGpkyQG0ofYFmlbP8NpVrjKykbhbgusdvsZOknuPBBAJ
+jgHDrt5fdT4ah7xlRr+o4KC8RpYOZ8pGy6eoheCZZdNvhV3cDVxYWrkFZbdbtvw9
+YqqgpgiFx/j85ZqFudq/ApdMwcueZWPjmc47Y2fsTtrqO7rVVtX9FrG4O3BWcrVH
+5RJpwN5mBsC//zNhhuvaGNWZB3XGNH0wgxEweI+cpfufWHnshJFKoEESJBrdHywz
++kuH3ha9W3JwyorON9M7uduDBdUBsjwjwLpYLd+Saie6X+XjROrGYe8f
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem.certspec
new file mode 100644
index 0000000000..5b60c29ebe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValid.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Not Yet Valid Test End-entity
+validity:20310101-20320101
+extension:subjectAlternativeName:notyetvalid.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem b/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem
new file mode 100644
index 0000000000..0a111582bf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6zCCAdOgAwIBAgIUW7kXNYkW5OLObAOtdMQ9uL8FHUkwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDMxMDEwMTAwMDAwMFoYDzIwMzMw
+MTAxMDAwMDAwWjAqMSgwJgYDVQQDDB9Ob3QgWWV0IFZhbGlkIFRlc3QgSW50ZXJt
+ZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB
+/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRx
+CHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMC
+OosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdm
+Wqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGz
+ey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUD
+rNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq
+hkiG9w0BAQsFAAOCAQEAJgm8BvVnIEy9uGjgTxLcNnIoOZeLgQ9CPrNir6RHzGIK
+1hIHcNCDdC2mDc9DvyQ8MQPzJvAiMAOvo1v8yHDijC/mmYi6hTGuC6hDxF2YHKXu
+8VZZOSMdaE9pkKUQ8sckzKMS+Oxw8EJOV7VeW/WZsSLKSpba0qAioae7wEBMg0QS
+0Mp72+l4nMMUG5T/gzea6gy/nGTb0ghV97K1r5jekFNuy1pZZbKun9lLd9KJNmEs
+XiJCn2M7Ce3nEhJfaJcDmz9sde4HOlJ/BVLV9ljkOL2cQ3TxdEkLT9/Unp9dmcLo
+1sdvWkwimWiKT3XAs6Df64uM4GYl3PqPH5tQmUyPFg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem.certspec
new file mode 100644
index 0000000000..8a00f2ee23
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValidINT.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Not Yet Valid Test Intermediate
+validity:20310101-20330101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem b/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem
new file mode 100644
index 0000000000..07e38c01fd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUDCCAjigAwIBAgIUWzSKPj2hWVPCNbbwxWOZDW3/ESwwDQYJKoZIhvcNAQEL
+BQAwKjEoMCYGA1UEAwwfTm90IFlldCBWYWxpZCBUZXN0IEludGVybWVkaWF0ZTAi
+GA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjA0MTIwMAYDVQQDDClU
+ZXN0IEVuZC1lbnRpdHkgd2l0aCBub3QgeWV0IHZhbGlkIGlzc3VlcjCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1
+SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+
+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYL
+K7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwc
+bJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibW
+JZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNg
+MF4wKAYDVR0RBCEwH4Idbm90eWV0dmFsaWRpc3N1ZXIuZXhhbXBsZS5jb20wMgYI
+KwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgv
+MA0GCSqGSIb3DQEBCwUAA4IBAQA6+mauWSqBTyI7KT2VVkNlmB0d0AZRnel1MMp8
+XkwBajtIMfwFiuQ5TZhIKbqPgV2tpUex4Uv5EPre2runpGWCBJNcWdQiTPPewHfK
+vWwVvSEOcsNIzo5jaJLM14JDQUgTlqjVjiIxbIAdouck6is4oFCBLbpkE3pUQCC9
+l6dzuDwmMRNEp0HEv/lDl5slubTOVrRN1G5CLEGRI9BdWk4IsJiNcvSJeVF1PhlV
+oE2/gcEcJo8gGzxwP07v6RAEf5quUCFDWbZwO9wba8vq1zVItMPHcxnM9n5vDzDa
+lognzXkxApHHzP0TJMi6PV5899iJgk+4tOJq+VrW7aIY9piS
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem.certspec
new file mode 100644
index 0000000000..d8420898e7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/notYetValidIssuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Not Yet Valid Test Intermediate
+subject:Test End-entity with not yet valid issuer
+extension:subjectAlternativeName:notyetvalidissuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem
new file mode 100644
index 0000000000..af14e9b6a1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/zCCAeegAwIBAgIUO53hCvwCYNgrXfxkrdB945nRkCcwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAeMRwwGgYDVQQDDBNuc0NlcnRUeXBlIENyaXRpY2FsMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq
+5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SSc
+An7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39
+ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk
+zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3u
+JtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQAB
+oz0wOzAjBgNVHREEHDAagglsb2NhbGhvc3SCDSouZXhhbXBsZS5jb20wFAYJYIZI
+AYb4QgEBAQH/BAQDAgZAMA0GCSqGSIb3DQEBCwUAA4IBAQAk0AOu1pTYiYQ4ojdG
+1foeVBLBzCaC7jOK8FW/Ow04r5mli8saKVzC5YsPBRZreJp6nVnlBnjtmgHB4PW9
+XJcHzd46uzD7h/w8kFDpLsEgM3tTZ5oezNz82EFp9aoUvTXDrdMxBnC0S/lB2fek
+9B5XTmmooU+DPYhTYYOVXXpjPCsRlKsAncu+vTGmagHGHCsRmrv6HkbSfLUEkdCl
+dmyC9L9Fsr79RjtqPNQoLmc3zD78iH6fW2oSJnhYLGJrAJDIHuSLvVZ42puKxzMV
+ZV01nNh3vXkxCFlNoQxlz8YTHr7dSku6+DGs9yMldhCgR+c3lrvhZk6d6tcM83iA
+mqr3
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem.certspec
new file mode 100644
index 0000000000..b236bdea47
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCritical.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:nsCertType Critical
+extension:subjectAlternativeName:localhost,*.example.com
+extension:nsCertType[critical]:sslServer
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem
new file mode 100644
index 0000000000..60664c7c8c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDMDCCAhigAwIBAgIUO8zMxMDLlO213zx4ekxrMaUbPiowDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAvMS0wKwYDVQQDDCRuc0NlcnRUeXBlIENyaXRpY2FsIFdpdGgg
+ZXh0S2V5VXNhZ2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjXTBbMCMGA1UdEQQcMBqCCWxvY2FsaG9zdIINKi5l
+eGFtcGxlLmNvbTAUBglghkgBhvhCAQEBAf8EBAMCBkAwCQYDVR0TBAIwADATBgNV
+HSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEAJFaCPufuePUQLh1a
+TzJHqLoTZrAg+9L/NpGnFLpsyENIQOHiKf53KQ53oFT0lv7+g+s9qU/KKf7xu2IF
+egggCwb5VD4KeU72EgmNwDT3i7oYuyU8nq+wo9XegChNdhMz3luyoVtwAp9I2oe9
+yOb/9JOWOH0+qzIExpqCOx0GSNHVk6H9QQKIDwfPiaqmUXQA/HruHeoLI6LSI6WF
+CYyLzxoE8QKttN+1kL7e6GEWqvOExf1ZWRtvnPpt1qf5httvNq7aDZ0PID4hP4kP
+SHlJu7PmrQe9AHJULTTSCJGTEwi/ll5HInMOFltxYNfDZ+H+cTnZb5ej+xwHLfdf
+4uU4pQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem.certspec
new file mode 100644
index 0000000000..0ae63e20f2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeCriticalWithExtKeyUsage.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Test CA
+subject:nsCertType Critical With extKeyUsage
+extension:subjectAlternativeName:localhost,*.example.com
+extension:nsCertType[critical]:sslServer
+extension:basicConstraints:,
+extension:extKeyUsage:serverAuth
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem
new file mode 100644
index 0000000000..5a715769ad
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDADCCAeigAwIBAgIUZ/ocY0tl1j/n07vD7m9BRpTDAUUwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAiMSAwHgYDVQQDDBduc0NlcnRUeXBlIE5vdCBDcml0aWNhbDCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAAaM6MDgwIwYDVR0RBBwwGoIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tMBEG
+CWCGSAGG+EIBAQQEAwIGQDANBgkqhkiG9w0BAQsFAAOCAQEACDqdp7f3wMnxSwIE
+GSzvCwSBjMkV1ELElEiSYuhXzh1jQ+ke+xLmLkjYJi3E8BmTmJ3g+9jUlMFL6n1y
+b4OOdRXM3JJyCnv2EBURvonfk1hIzWqK6Fuiwfzs5YdRLeHPrUMw1trGvovQqZfY
+9qCnkgmeJL8OO9AeicCDYRhl/KMid+rHNJePhfkURKs0ww9AzrBFepAorZ5UOmDe
+8UfP4dJarEledosnoIZ6GSEwpLoQQ9ChzsdpaIAhKLIPz52l7Na/N/qUXKw2CMQv
+RnifJXwS2/DhaEjGsXGHq7rmC2tGaVJci+zamUhJdyb5iJlbsAqXQ6Ufq9jhNUoB
+4nnKNw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem.certspec
new file mode 100644
index 0000000000..a44a1feeef
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/nsCertTypeNotCritical.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:nsCertType Not Critical
+extension:subjectAlternativeName:localhost,*.example.com
+extension:nsCertType:sslServer
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem b/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem
new file mode 100644
index 0000000000..580b682319
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDfzCCAmegAwIBAgIUbYx/Zvi5+U/zqP9O3mq26pzGgIAwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNT3RoZXIgdGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAnMSUwIwYDVQQDDBxXcm9uZyBDQSBQaW4gVGVzdCBF
+bmQtRW50aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXXGUmYJ
+n3cIKmeR8bh2w39c5TiwbErNIrHL1G+mWtoq3UHIwkmKxKOzwfYUh/QbaYlBvYCl
+HDwSAkTFhKTESDMF5ROMAQbPCL6ahidguuai6PNvI8XZgxO53683g0XazlHU1tzS
+pss8xwbrzTBw7JjM5AqlkdcpWn9xxb5maR0rLf7ISURZC8Wj6kn9k7HXU0BfF3N2
+mZWGZiVHl+1CaQiICBFCIGmYikP+5Izmh4HdIramnNKDdRMfkysSjOKG+n0lHAYq
+0n7wFvGHzdVOgys1uJMPdLqQqovHYWckKrH9bWIUDRjEwLjGj8N0hFcyStfehuZV
+Lx0eGR1xIWjTuwIDAQABo4GtMIGqMHQGA1UdEQRtMGuCKCouaW5jbHVkZS1zdWJk
+b21haW5zLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5z
+LnBpbm5pbmcuZXhhbXBsZS5jb22CFSoucGlubmluZy5leGFtcGxlLmNvbTAyBggr
+BgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8w
+DQYJKoZIhvcNAQELBQADggEBAF95ssZxomk3Jj886Bqm8kDAHWR/njF15jNyLnVQ
+JOTQcbOItFbgftUOuY4aN3MQ2q+LzuDwsalcsm7vvmOXf/tp4LWjGKrFOdIkO0XG
+/yUZy5xBJgSnIKuDUtES3hrl2X6LllZBhLiKBiKuFqudYo8LG1JeyLdxDpCKoDZh
+dyDK3XQ42oqJz9GOXBIZiwl4x8AmaLSE3o+CRYwNxwNOpdqRWeSwlETC46LEdtSo
+0StD9MEu+onWbizWCsCB8p6ILvvPr1JJGI6GcR1sPtr2qTkT5veXWXBTZYuqW2Cl
+gs288iz7Kbbq1ABu9ZCQ6SzpyA+LAIadtkHeDc4p87KcjQc=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem.certspec
new file mode 100644
index 0000000000..a905a66ac2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-issuer-ee.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Other test CA
+subject:Wrong CA Pin Test End-Entity
+issuerKey:alternate
+subjectKey:alternate
+extension:subjectAlternativeName:*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com,*.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key
new file mode 100644
index 0000000000..abde350c28
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBdcZSZgmfdwgq
+Z5HxuHbDf1zlOLBsSs0iscvUb6Za2irdQcjCSYrEo7PB9hSH9BtpiUG9gKUcPBIC
+RMWEpMRIMwXlE4wBBs8IvpqGJ2C65qLo828jxdmDE7nfrzeDRdrOUdTW3NKmyzzH
+BuvNMHDsmMzkCqWR1ylaf3HFvmZpHSst/shJRFkLxaPqSf2TsddTQF8Xc3aZlYZm
+JUeX7UJpCIgIEUIgaZiKQ/7kjOaHgd0itqac0oN1Ex+TKxKM4ob6fSUcBirSfvAW
+8YfN1U6DKzW4kw90upCqi8dhZyQqsf1tYhQNGMTAuMaPw3SEVzJK196G5lUvHR4Z
+HXEhaNO7AgMBAAECggEAfj9tfLg572auXX3ZL/VBC7NB3BRyjTkDRXDho3B5DzDw
+aBNV//QeKtTpqdn86/vRJ736uMAK/7Hzzqcyfq1HqhYh8qwe4UygLwSzsnhgF5gL
+GBpEnQOwPmnRErg1ceVUNPASBWV10oMu1nMdznmeN8g/bVHFWrcetYAVrwXhrxXH
+R2A+9/J9A6b/BJ2Wu/hUweTlDvWwWND7CBgOCsf3vo8v8Wc9l/yeVduoOAd7v4p8
+/ylihXeFJpzZ1brStXRp5K/NM8TKLS9pnxHnyPvc1ITwjY77ijy4qXLrJL7Zcu+q
+5LtxIJPkj+lKRutimodQeMQCGposk8mnA5Dp0KVEAQKBgQDmP8clprp2klp/+MtZ
+xPVt1+yD/oW/H1PhHKyagSWLz8CugZB3sPLRR3qvho3mqOy+r3uyKxlvKprYLTKG
+8NDMKd5xnl8r6OUJtyhNWWPt02L5J4h6TEqJeZ00DVGzAax2AasnF5Ak/KrdOL9l
+Iq9j6xZGHsAqfyewb+Cd3afAoQKBgQDXGLH+n4+Z8A6DKuH73G/iqyfzTgScSYAQ
++g63CEhSGCNGCDtclsPu5VksAUpBDGuTCxZcE7XCaqMurG58klqFUcJRNPL0pyxk
+IfGacxSKDt+rpdOmiIs1y6GMAP047lqvC1RXMdcgdhu8ze50SlLKQV6Y5N4Bzf52
+TBlns+jK2wKBgAHlrKJmyUqI0i4TwrkuokcRbGV6B2gXvf0w20s6nTCVuaS2dJZH
+4vhOenhPx4OLCMhZcc96A2+jDjuRw8TQ3yePgMG26FnYRWrbE33vqp8fCsW6yakY
+T9TqJ51yLqYm8WDXiq17yDhFzLKd8RXIP2G3YiuZvUOcYJtXkKY8WVGBAoGBAIDM
+RdENJITuDRKX/Ae/gLO+/0Yeon4fOPNxeJw69mtKDt0hksIneR208cd64ka/NC8x
+hWsPVlgbWKlbETHAxTltsqjDxvOeouM2vCBa5qKgs2hp/KmMu6czzwExmm+bsmt8
+oj0wF/xVHNjaiv3Rf2+i4w00hoeYHNYjTVcekLffAoGAb3fAwfKuesFpVhzKSZxS
+vfvgTN3M29wSrsWoVpHoWUt+4pkI8w57lqpiVLgO1K7sm5k3gr38ebadjVjGiHD6
+S+G8DDUnKIxcgrtK668V7f8RBAP8eOas5qgoJ79C8M+nUeUHZRxWONuTk90j3R9r
+KVFR3kS3f+Vaew3yceGaZcA=
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key.keyspec b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key.keyspec
new file mode 100644
index 0000000000..cbd5f309c0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.key.keyspec
@@ -0,0 +1 @@
+alternate
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem
new file mode 100644
index 0000000000..a2e264030a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAcegAwIBAgIURym6o+VN9xgZXT/QLrvN/nv1ZN4wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNT3RoZXIgdGVzdCBDQTAiGA8yMDE1MDEwMTAwMDAwMFoY
+DzIwMjUwMTAxMDAwMDAwWjAYMRYwFAYDVQQDDA1PdGhlciB0ZXN0IENBMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXXGUmYJn3cIKmeR8bh2w39c5Tiw
+bErNIrHL1G+mWtoq3UHIwkmKxKOzwfYUh/QbaYlBvYClHDwSAkTFhKTESDMF5ROM
+AQbPCL6ahidguuai6PNvI8XZgxO53683g0XazlHU1tzSpss8xwbrzTBw7JjM5Aql
+kdcpWn9xxb5maR0rLf7ISURZC8Wj6kn9k7HXU0BfF3N2mZWGZiVHl+1CaQiICBFC
+IGmYikP+5Izmh4HdIramnNKDdRMfkysSjOKG+n0lHAYq0n7wFvGHzdVOgys1uJMP
+dLqQqovHYWckKrH9bWIUDRjEwLjGj8N0hFcyStfehuZVLx0eGR1xIWjTuwIDAQAB
+ox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC
+AQEAtXplrvls6HSbbibpzfGxOPmSuh2TH05bE4vQk+d7Kz6EOAFvgTiZbLwTxbrQ
+gfrM05t+67C2nAeiwAtW34nUnu6S8MYA6mJjURWICbl7cAvCHuNjg1atVr6f1Y+9
+VFFG6aUibw3bzKneREmDEVcxlEWUaMvv/JjfyMA5veSyX6iTJYkIBrEiVV5Alzg5
+yVHBi6+tpuJDO/YLlG8kmfzkYeJkTyAGx1EJ2yQHim7R232638yb0KrhS4zKsfFU
+egHhM4c+MpiCLc9q2EgblbYGx5GM+2leuzXunj1KPClHFrnmkRRm3rcESG2pK9RN
+/48Nd38VNofRojEbzDSCdOFmow==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem.certspec
new file mode 100644
index 0000000000..3bc975aa22
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/other-test-ca.pem.certspec
@@ -0,0 +1,7 @@
+issuer:Other test CA
+subject:Other test CA
+issuerKey:alternate
+subjectKey:alternate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem b/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem
new file mode 100644
index 0000000000..fdbb9a06db
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDeTCCAmGgAwIBAgIUXSAD8MMxkJHsAS4PFETbUKRZyQ0wDQYJKoZIhvcNAQEL
+BQAwMzExMC8GA1UEAwwoVGVzdCBTZWxmLXNpZ25lZCBFbmQtZW50aXR5IHdpdGgg
+Q0EgdHJ1ZTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAzMTEw
+LwYDVQQDDChUZXN0IFNlbGYtc2lnbmVkIEVuZC1lbnRpdHkgd2l0aCBDQSB0cnVl
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+jQIDAQABo4GAMH4wDAYDVR0TBAUwAwEB/zAyBggrBgEFBQcBAQQmMCQwIgYIKwYB
+BQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wOgYDVR0RBDMwMYIvc2VsZi1z
+aWduZWQtZW5kLWVudGl0eS13aXRoLWNBLXRydWUuZXhhbXBsZS5jb20wDQYJKoZI
+hvcNAQELBQADggEBAGSgc1erifMbfX4bRpm4+jylXmCnAnBbdxrQKtSDpMMGZV//
+a66yGUEHRQ0SGNWo042NlLvQ52Z5OlS2eocrlny+eKCi7n4mVCjwKKRjZ9zfQU9r
+wbKozCA75PTkdYI1GforX4dkWhCCIMRKPDQv/zoI9bikJuMd+fadAjbLUeEiwkOM
+xmmDJ5wikGlj0U56+mu+8FN2A9NDDJHRAJd9YoD68cSb/IZSoOUVVsVGokLextDo
+/IxkvhignOEko5NWQOu2m6TbiqWzMlvwZZ1FcshgGuilrHpXPPmsbpILtzx3pG8Y
+E/PNSVRHKre5sPAEGPkt5q6l9WWUjgmXYnclKuY=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem.certspec
new file mode 100644
index 0000000000..0ca92d7fd1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/self-signed-EE-with-cA-true.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test Self-signed End-entity with CA true
+subject:Test Self-signed End-entity with CA true
+extension:basicConstraints:cA,
+extension:authorityInformationAccess:http://localhost:8888/
+extension:subjectAlternativeName:self-signed-end-entity-with-cA-true.example.com
diff --git a/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem b/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem
new file mode 100644
index 0000000000..06924b41e9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDhzCCAm+gAwIBAgIUf+URCTKBiuOZl+FIY/BdYQHsrx8wDQYJKoZIhvcNAQEL
+BQAwNTEzMDEGA1UEAwwqU2VsZi1zaWduZWQgSW5hZGVxdWF0ZSBFS1UgVGVzdCBF
+bmQtZW50aXR5MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMDUx
+MzAxBgNVBAMMKlNlbGYtc2lnbmVkIEluYWRlcXVhdGUgRUtVIFRlc3QgRW5kLWVu
+dGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1u
+togGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6
+pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqL
+KkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3Zlqq
+fgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3sv
+Im9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6za
+GAo17Y0CAwEAAaOBijCBhzALBgNVHQ8EBAMCBDAwEwYDVR0lBAwwCgYIKwYBBQUH
+AwEwLwYDVR0RBCgwJoIkc2VsZnNpZ25lZC1pbmFkZXF1YXRlRUtVLmV4YW1wbGUu
+Y29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9z
+dDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAeotRluVjBUDHNaKs8g5VCS5keHXt
+nZMRxjAY0P6IqKdlfx3MUR8MbGHpHK1/f+3nzkKUTwemJXpUmsuPTTxOQwN5maSd
+0ekOG/Ycdpy3QF6vT0SFlv3AZDmYIAo13qt3mmpFYkaeMbBqXURZzBsdxzNF5IJ9
+bN9BlQzGj7VKd7bAWXAo8HMeR83OzwANONXoMSolT6JN5NX/JRd79Iyl7Vc3WBRy
+XDRlr74y5/YvETbQjDBOqi5+vWdceHmX3GekexXqtfnO/oX1hhupnMC+Mzjr3Awi
+euQP2QHwdxy0TQg5h8elCLOcYkpAF1IASZ6VFqWpSa/Sobu9hc5laau5TA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem.certspec
new file mode 100644
index 0000000000..477b90ce14
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/selfsigned-inadequateEKU.pem.certspec
@@ -0,0 +1,6 @@
+issuer:Self-signed Inadequate EKU Test End-entity
+subject:Self-signed Inadequate EKU Test End-entity
+extension:keyUsage:keyEncipherment,dataEncipherment
+extension:extKeyUsage:serverAuth
+extension:subjectAlternativeName:selfsigned-inadequateEKU.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem b/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem
new file mode 100644
index 0000000000..2743bd61a3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDNzCCAh+gAwIBAgIUR1TNNr+Uv6ZR4ml0F8x6mwwA/lEwDQYJKoZIhvcNAQEL
+BQAwJjEkMCIGA1UEAwwbU2VsZi1zaWduZWQgVGVzdCBFbmQtZW50aXR5MCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCYxJDAiBgNVBAMMG1NlbGYt
+c2lnbmVkIFRlc3QgRW5kLWVudGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNZMFcwIQYDVR0RBBowGIIWc2Vs
+ZnNpZ25lZC5leGFtcGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGG
+Fmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZIhvcNAQELBQADggEBADbaX7tZ
+XI5N3VzxZ3TUQ07croUVJj7DcduwDS1m2fc2w+Lub1odVm38RQGxvIROyfNdQA2q
+Djb5QyKYTnQgfFNR+rOFdDT22pRWzHQ/jqtNUzaxwrO4vGKTlISRrZBRkxde6c8m
+7Z5MDu6MyDkfJyFcm5oZX4L84wDTeALRAizR2c6dHT3CxVSqoCVK1hBLe7H5gy7R
+nwL6OSFy0FCnibqJwoeSFQbbga9nYsmIm165+r/4qQRrdhrRVZXtZmMmrauua86r
+nRqr5ZZWGV3kM3M5mlLiH7UYgYuC2i4XwxGoxuOX7ekX6ro3/grctareAzgsNWzv
+8skVQ1O9v6l22b0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem.certspec
new file mode 100644
index 0000000000..99a814be17
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/selfsigned.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Self-signed Test End-entity
+subject:Self-signed Test End-entity
+extension:subjectAlternativeName:selfsigned.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/test-ca.pem b/security/manager/ssl/tests/unit/bad_certs/test-ca.pem
new file mode 100644
index 0000000000..40515addbd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/test-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUZekW+kmMLmUyacMUQAExM6vEFpswDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjASMRAwDgYDVQQDDAdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRME
+BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAoabyPFisrr0c
+kmPrDCY8pyDZ6ScxE/ziG+5RG4yJzW0QLrnt6rusMdKuxwaLiTISsNI14vh9QQyG
+XQsVeiGNGIwyJ9k2ZbLvw6pjdUeaswkthSqUDYwYgCljBwViuFkK4SAwya0Vb0p8
+pErDX8pzSF78inMB/7f0+DPdEQgtAGPYfB0gMRWOliyJSVoJXTgJ2B6PTMMvIcIO
+OXcqjDvVYY4o9+YtsDfmzGOSa/YmbM4hO6lv/cO3zv3aT+szyQK8X44koSkvct2P
+QL4cY3/incY0l0I12PaScOJDuvITiRNca7gxbUiT2jz1eqzGPIyVS6ku2cO0v9tP
+TIiD5XDdFg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/test-ca.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/test-ca.pem.certspec
new file mode 100644
index 0000000000..5d2435d7bb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/test-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test CA
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/test-int.pem b/security/manager/ssl/tests/unit/bad_certs/test-int.pem
new file mode 100644
index 0000000000..08249b863e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/test-int.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3TCCAcWgAwIBAgIUa0X7/7DlTaedpgrIJg25iBPOkIMwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE1MDEwMTAwMDAwMFoYDzIwMjUw
+MTAxMDAwMDAwWjAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1
+SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+
+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYL
+K7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwc
+bJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibW
+JZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMd
+MBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEB
+AILNZM9yT9ylMpjyi0tXaDORzpHiJ8vEoVKk98bC2BQF0kMEEB547p+Ms8zdJY00
+Bxe9qigT8rQwKprXq5RvgIZ32QLn/yMPiCp/e6zBdsx77TkfmnSnxvPi+0nlA+eM
+8JYN0UST4vWD4vPPX9GgZDVoGQTiF3hUivJ5R8sHb/ozcSukMKQQ22+AIU7w6wyA
+IbCAG7Pab4k2XFAeEnUZsl9fCym5jsPN9Pnv9rlBi6h8shHw1R2ROXjgxubjiMr3
+B456vFTJImLJjyA1iTSlr/+VXGUYg6Z0/HYnsO00+8xUKM71dPxGAfIFNaSscpyk
+rGFLvocT/kym6r8galxCJUo=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/test-int.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/test-int.pem.certspec
new file mode 100644
index 0000000000..33b42c2f41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/test-int.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem b/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem
new file mode 100644
index 0000000000..b2c5370f76
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDqTCCApGgAwIBAgIUexjAmwfKg8l4RNHPND1Dh+75sXEwDQYJKoZIhvcNAQEL
+BQAwJjEkMCIGA1UEAwwbVGVzdCBJbnRlcm1lZGlhdGUgdG8gZGVsZXRlMCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMC4xLDAqBgNVBAMMI1Rlc3Qg
+RW5kLWVudGl0eSBmcm9tIHVua25vd24gaXNzdWVyMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4HCMIG/MIGIBgNV
+HREEgYAwfoIZdW5rbm93bmlzc3Vlci5leGFtcGxlLmNvbYI0dW5rbm93bmlzc3Vl
+ci5pbmNsdWRlLXN1YmRvbWFpbnMucGlubmluZy5leGFtcGxlLmNvbYIrdW5rbm93
+bmlzc3Vlci50ZXN0LW1vZGUucGlubmluZy5leGFtcGxlLmNvbTAyBggrBgEFBQcB
+AQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZI
+hvcNAQELBQADggEBABITeIp7PtTCDIpSYqXqkf8cibmOkSHPDxBmHxR463rGXNiD
+ZGT3ATksVoKh/HugKxb8FC6e2oEszN3bVARypjkMFEmqD2LkC5SIU5KmvU0+E4Eq
+cjlvh7rDf0KED3aYXro3QUbmUKg12owyn0xzMqdwlvD6cWT2R6bBC0g6LN11BiLq
+/BSMGU+p62LEjhqZjxzBNflGnj5lY9BCIelZ9rhO5WhIFURqZANXKw+MKAwTREl3
+AD1TmJztLABQukbGnYYplmn/COnzPDo//3mTCtYZlg158srgsDMNINfEvPcA2ZsS
+Yg/nywbaXMCMyNiSUJJWf8Z/gDX8sfDbD7k/ets=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem.certspec
new file mode 100644
index 0000000000..a735c730ca
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/unknownissuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate to delete
+subject:Test End-entity from unknown issuer
+extension:subjectAlternativeName:unknownissuer.example.com,unknownissuer.include-subdomains.pinning.example.com,unknownissuer.test-mode.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem b/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem
new file mode 100644
index 0000000000..921d14583f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDNjCCAh6gAwIBAgIUGTh5myT/JfbNsso9a1ZhGr8B11gwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNT3RoZXIgdGVzdCBDQTAiGA8yMDExMDEwMTAwMDAwMFoY
+DzIwMTMwMTAxMDAwMDAwWjAsMSowKAYDVQQDDCFVbnRydXN0ZWQtRXhwaXJlZCBU
+ZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAGjYDBeMCgGA1UdEQQhMB+CHXVudHJ1c3RlZC1l
+eHBpcmVkLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYW
+aHR0cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAMktkDNyd
+MHFk4K4dcOfi0McOkxvdZW6/Nqts51scx8A3q8sT5pdW3ftBjvN7O+3c1cNGoThG
+9SA6uLDV2NT2EQp65yxMZe8j1OHX3qybqZ/RVY5r7VHF4JDsPHHXsgew5+CRGraj
+MTIFUqmgAYLMcv74vB0OLIt3JL4XnHVP7atULMLJrOP8QUmkUhZ49MDqyslN0i6w
+X7KwhdM00+JKadqUFqOAfhacMHsH5ErsQ3LXrQRMJiFoaVbElMy9e/jJmY7rg4pw
+0EMnkt+Vj650FkWsCnLndBia+6z+81L4EZ7uwBeeks2fYhXt85qqkuzmZq2T2diu
+ArcEblKIbUYlVg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem.certspec
new file mode 100644
index 0000000000..3efd1ce677
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/untrusted-expired.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Other test CA
+subject:Untrusted-Expired Test End-entity
+validity:20110101-20130101
+extension:subjectAlternativeName:untrusted-expired.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem b/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem
new file mode 100644
index 0000000000..61cabc2522
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDODCCAiCgAwIBAgIUM0gXawDCbh+O00f2TLkI3uS0aRswDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNT3RoZXIgdGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAwMS4wLAYDVQQDDCVUZXN0IEVuZC1lbnRpdHkgd2l0
+aCB1bnRydXN0ZWQgaXNzdWVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo14wXDAmBgNVHREEHzAdght1bnRydXN0
+ZWRpc3N1ZXIuZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAB
+hhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCoqxjz
+UdEXTpLlinCBiDl0BM5qRYcWRM7YKaUAVfX0Wn7Csuk67PQS0armkRUQYJqY8Q1a
+/ChTD44Y9dtoe50gIIlWhXfOrVEWQHu36NQZx4uosrFXHmL96EmH4tznvWVydzh8
+4z7NQQ2rg9buiyY5zEyne2S2xDADvnYk/uszUv4AQ2+zQA2oARH3S9OO9VtFy1ZF
+wfkt12IGRnv7kptP6YdAbZqCHfXWVc2Dg8lS0OwGxtpRU9bYJZt7ojlIOmUSJjQs
+6iXqCUvax1KAWGNosVUlOgU0lEpyG94NkChiwgRgTpUEdnabnme0DYuhhm/suQEZ
+/1Tb6W2ER3oiR1I+
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem.certspec
new file mode 100644
index 0000000000..5ba0bc2535
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/untrustedissuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Other test CA
+subject:Test End-entity with untrusted issuer
+extension:subjectAlternativeName:untrustedissuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem b/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem
new file mode 100644
index 0000000000..ae4712fe05
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrzCCAZcCFAOC1GLXYEEbe03otHMT9P0Sz1UpMA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB1Rlc3QgQ0EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEjEQMA4GA1UEAwwHVjEgQ2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODY
+H72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk
+27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9
+0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMM
+kd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaL
+L+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0BAQsFAAOC
+AQEAPfp2lSeHPS2M1Snc9ZMJ39bNbpLT3dJpbUFZ8lz3eh3NZEhDNig1ZgINC0Z4
+pwzAmt5Jurq6cHjr4akF+7I3LMmG6mdVrmcQY88ArkfC/PhYvh9eswhzq7C1y7nX
+KLFP30U5h9R8Y4+6XEyz+yB9z3Yyf8Pq7a9qWDdPNE9faHU628h74FFu0y+EvSD+
+4CU0Zte8w2+OBracUPne52Gi196rcO0Ic+H0d0PCSgC+KqkTbQjynqpgkGif3W54
+yXflu6sBODd6Xil7qjlw9Bky5VNyLDuKOH+nz+15U3gMfPHm+mJjWXhBjqSyD743
+72mMwbtT7ze7kCeAER4p/a2JZg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem.certspec b/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem.certspec
new file mode 100644
index 0000000000..7824630bbc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/bad_certs/v1Cert.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:V1 Cert
+version:1
diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js
new file mode 100644
index 0000000000..cfec935e86
--- /dev/null
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -0,0 +1,1231 @@
+/* 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/.
+ */
+"use strict";
+
+const { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+const { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm");
+const { FileUtils } = ChromeUtils.import(
+ "resource://gre/modules/FileUtils.jsm"
+);
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+const { MockRegistrar } = ChromeUtils.import(
+ "resource://testing-common/MockRegistrar.jsm"
+);
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+const { Promise } = ChromeUtils.import("resource://gre/modules/Promise.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+const { X509 } = ChromeUtils.import("resource://gre/modules/psm/X509.jsm");
+
+const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2)
+ .isDebugBuild;
+
+// The test EV roots are only enabled in debug builds as a security measure.
+const gEVExpected = isDebugBuild;
+
+const CLIENT_AUTH_FILE_NAME = "ClientAuthRememberList.txt";
+const SSS_STATE_FILE_NAME = "SiteSecurityServiceState.txt";
+const PRELOAD_STATE_FILE_NAME = "SecurityPreloadState.txt";
+
+const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
+const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
+const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
+
+// This isn't really a valid PRErrorCode, but is useful for signalling that
+// a test is expected to succeed.
+const PRErrorCodeSuccess = 0;
+
+// Sort in numerical order
+const SEC_ERROR_INVALID_TIME = SEC_ERROR_BASE + 8;
+const SEC_ERROR_BAD_DER = SEC_ERROR_BASE + 9;
+const SEC_ERROR_BAD_SIGNATURE = SEC_ERROR_BASE + 10;
+const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11;
+const SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12;
+const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
+const SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20;
+const SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21;
+const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = SEC_ERROR_BASE + 30;
+const SEC_ERROR_CA_CERT_INVALID = SEC_ERROR_BASE + 36;
+const SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION = SEC_ERROR_BASE + 41;
+const SEC_ERROR_PKCS7_BAD_SIGNATURE = SEC_ERROR_BASE + 47;
+const SEC_ERROR_INADEQUATE_KEY_USAGE = SEC_ERROR_BASE + 90;
+const SEC_ERROR_INADEQUATE_CERT_TYPE = SEC_ERROR_BASE + 91;
+const SEC_ERROR_CERT_NOT_IN_NAME_SPACE = SEC_ERROR_BASE + 112;
+const SEC_ERROR_CERT_BAD_ACCESS_LOCATION = SEC_ERROR_BASE + 117;
+const SEC_ERROR_OCSP_MALFORMED_REQUEST = SEC_ERROR_BASE + 120;
+const SEC_ERROR_OCSP_SERVER_ERROR = SEC_ERROR_BASE + 121;
+const SEC_ERROR_OCSP_TRY_SERVER_LATER = SEC_ERROR_BASE + 122;
+const SEC_ERROR_OCSP_REQUEST_NEEDS_SIG = SEC_ERROR_BASE + 123;
+const SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST = SEC_ERROR_BASE + 124;
+const SEC_ERROR_OCSP_UNKNOWN_CERT = SEC_ERROR_BASE + 126;
+const SEC_ERROR_OCSP_MALFORMED_RESPONSE = SEC_ERROR_BASE + 129;
+const SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = SEC_ERROR_BASE + 130;
+const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132;
+const SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE = SEC_ERROR_BASE + 141;
+const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144;
+const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160;
+const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157;
+const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
+
+const SSL_ERROR_NO_CYPHER_OVERLAP = SSL_ERROR_BASE + 2;
+const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12;
+const SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17;
+const SSL_ERROR_WEAK_SERVER_CERT_KEY = SSL_ERROR_BASE + 132;
+const SSL_ERROR_DC_INVALID_KEY_USAGE = SSL_ERROR_BASE + 184;
+
+const SSL_ERROR_ECH_RETRY_WITH_ECH = SSL_ERROR_BASE + 188;
+const SSL_ERROR_ECH_RETRY_WITHOUT_ECH = SSL_ERROR_BASE + 189;
+const SSL_ERROR_ECH_FAILED = SSL_ERROR_BASE + 190;
+const SSL_ERROR_ECH_REQUIRED_ALERT = SSL_ERROR_BASE + 191;
+
+const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = MOZILLA_PKIX_ERROR_BASE + 0;
+const MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY =
+ MOZILLA_PKIX_ERROR_BASE + 1;
+const MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE = MOZILLA_PKIX_ERROR_BASE + 2;
+const MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = MOZILLA_PKIX_ERROR_BASE + 3;
+const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE =
+ MOZILLA_PKIX_ERROR_BASE + 5;
+const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE =
+ MOZILLA_PKIX_ERROR_BASE + 6;
+const MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING =
+ MOZILLA_PKIX_ERROR_BASE + 8;
+const MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING =
+ MOZILLA_PKIX_ERROR_BASE + 10;
+const MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME = MOZILLA_PKIX_ERROR_BASE + 12;
+const MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED =
+ MOZILLA_PKIX_ERROR_BASE + 13;
+const MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = MOZILLA_PKIX_ERROR_BASE + 14;
+const MOZILLA_PKIX_ERROR_MITM_DETECTED = MOZILLA_PKIX_ERROR_BASE + 15;
+
+// Supported Certificate Usages
+const certificateUsageSSLClient = 0x0001;
+const certificateUsageSSLServer = 0x0002;
+const certificateUsageSSLCA = 0x0008;
+const certificateUsageEmailSigner = 0x0010;
+const certificateUsageEmailRecipient = 0x0020;
+
+// A map from the name of a certificate usage to the value of the usage.
+// Useful for printing debugging information and for enumerating all supported
+// usages.
+const allCertificateUsages = {
+ certificateUsageSSLClient,
+ certificateUsageSSLServer,
+ certificateUsageSSLCA,
+ certificateUsageEmailSigner,
+ certificateUsageEmailRecipient,
+};
+
+const NO_FLAGS = 0;
+
+const CRLiteModeDisabledPrefValue = 0;
+const CRLiteModeTelemetryOnlyPrefValue = 1;
+const CRLiteModeEnforcePrefValue = 2;
+
+// Convert a string to an array of bytes consisting of the char code at each
+// index.
+function stringToArray(s) {
+ let a = [];
+ for (let i = 0; i < s.length; i++) {
+ a.push(s.charCodeAt(i));
+ }
+ return a;
+}
+
+// Converts an array of bytes to a JS string using fromCharCode on each byte.
+function arrayToString(a) {
+ let s = "";
+ for (let b of a) {
+ s += String.fromCharCode(b);
+ }
+ return s;
+}
+
+// Commonly certificates are represented as PEM. The format is roughly as
+// follows:
+//
+// -----BEGIN CERTIFICATE-----
+// [some lines of base64, each typically 64 characters long]
+// -----END CERTIFICATE-----
+//
+// However, nsIX509CertDB.constructX509FromBase64 and related functions do not
+// handle input of this form. Instead, they require a single string of base64
+// with no newlines or BEGIN/END headers. This is a helper function to convert
+// PEM to the format that nsIX509CertDB requires.
+function pemToBase64(pem) {
+ return pem
+ .replace(/-----BEGIN CERTIFICATE-----/, "")
+ .replace(/-----END CERTIFICATE-----/, "")
+ .replace(/[\r\n]/g, "");
+}
+
+function build_cert_chain(certNames, testDirectory = "bad_certs") {
+ let certList = [];
+ certNames.forEach(function(certName) {
+ let cert = constructCertFromFile(`${testDirectory}/${certName}.pem`);
+ certList.push(cert);
+ });
+ return certList;
+}
+
+function areCertArraysEqual(certArrayA, certArrayB) {
+ if (certArrayA.length != certArrayB.length) {
+ return false;
+ }
+
+ for (let i = 0; i < certArrayA.length; i++) {
+ const certA = certArrayA[i];
+ const certB = certArrayB[i];
+ if (!certA.equals(certB)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function readFile(file) {
+ let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ fstream.init(file, -1, 0, 0);
+ let available = fstream.available();
+ let data =
+ available > 0 ? NetUtil.readInputStreamToString(fstream, available) : "";
+ fstream.close();
+ return data;
+}
+
+function addCertFromFile(certdb, filename, trustString) {
+ let certFile = do_get_file(filename, false);
+ let certBytes = readFile(certFile);
+ try {
+ return certdb.addCert(certBytes, trustString);
+ } catch (e) {}
+ // It might be PEM instead of DER.
+ return certdb.addCertFromBase64(pemToBase64(certBytes), trustString);
+}
+
+function constructCertFromFile(filename) {
+ let certFile = do_get_file(filename, false);
+ let certBytes = readFile(certFile);
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ try {
+ return certdb.constructX509(stringToArray(certBytes));
+ } catch (e) {}
+ // It might be PEM instead of DER.
+ return certdb.constructX509FromBase64(pemToBase64(certBytes));
+}
+
+function setCertTrust(cert, trustString) {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ certdb.setCertTrustFromString(cert, trustString);
+}
+
+function getXPCOMStatusFromNSS(statusNSS) {
+ let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"].getService(
+ Ci.nsINSSErrorsService
+ );
+ return nssErrorsService.getXPCOMFromNSSError(statusNSS);
+}
+
+// Helper for checkCertErrorGenericAtTime
+class CertVerificationExpectedErrorResult {
+ constructor(certName, expectedError, expectedEVStatus, resolve) {
+ this.certName = certName;
+ this.expectedError = expectedError;
+ this.expectedEVStatus = expectedEVStatus;
+ this.resolve = resolve;
+ }
+
+ verifyCertFinished(aPRErrorCode, aVerifiedChain, aHasEVPolicy) {
+ equal(
+ aPRErrorCode,
+ this.expectedError,
+ `verifying ${this.certName}: should get error ${this.expectedError}`
+ );
+ if (this.expectedEVStatus != undefined) {
+ equal(
+ aHasEVPolicy,
+ this.expectedEVStatus,
+ `verifying ${this.certName}: ` +
+ `should ${this.expectedEVStatus ? "be" : "not be"} EV`
+ );
+ }
+ this.resolve();
+ }
+}
+
+// certdb implements nsIX509CertDB. See nsIX509CertDB.idl for documentation.
+// In particular, hostname is optional.
+function checkCertErrorGenericAtTime(
+ certdb,
+ cert,
+ expectedError,
+ usage,
+ time,
+ /* optional */ isEVExpected,
+ /* optional */ hostname,
+ /* optional */ flags = NO_FLAGS
+) {
+ return new Promise((resolve, reject) => {
+ let result = new CertVerificationExpectedErrorResult(
+ cert.commonName,
+ expectedError,
+ isEVExpected,
+ resolve
+ );
+ certdb.asyncVerifyCertAtTime(cert, usage, flags, hostname, time, result);
+ });
+}
+
+// certdb implements nsIX509CertDB. See nsIX509CertDB.idl for documentation.
+// In particular, hostname is optional.
+function checkCertErrorGeneric(
+ certdb,
+ cert,
+ expectedError,
+ usage,
+ /* optional */ isEVExpected,
+ /* optional */ hostname
+) {
+ let now = new Date().getTime() / 1000;
+ return checkCertErrorGenericAtTime(
+ certdb,
+ cert,
+ expectedError,
+ usage,
+ now,
+ isEVExpected,
+ hostname
+ );
+}
+
+function checkEVStatus(certDB, cert, usage, isEVExpected) {
+ return checkCertErrorGeneric(
+ certDB,
+ cert,
+ PRErrorCodeSuccess,
+ usage,
+ isEVExpected
+ );
+}
+
+function _getLibraryFunctionWithNoArguments(
+ functionName,
+ libraryName,
+ returnType
+) {
+ // Open the NSS library. copied from services/crypto/modules/WeaveCrypto.js
+ let path = ctypes.libraryName(libraryName);
+
+ // XXX really want to be able to pass specific dlopen flags here.
+ let nsslib;
+ try {
+ nsslib = ctypes.open(path);
+ } catch (e) {
+ // In case opening the library without a full path fails,
+ // try again with a full path.
+ let file = Services.dirsvc.get("GreBinD", Ci.nsIFile);
+ file.append(path);
+ nsslib = ctypes.open(file.path);
+ }
+
+ let SECStatus = ctypes.int;
+ let func = nsslib.declare(
+ functionName,
+ ctypes.default_abi,
+ returnType || SECStatus
+ );
+ return func;
+}
+
+function clearOCSPCache() {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ certdb.clearOCSPCache();
+}
+
+function clearSessionCache() {
+ let nssComponent = Cc["@mozilla.org/psm;1"].getService(Ci.nsINSSComponent);
+ nssComponent.clearSSLExternalAndInternalSessionCache();
+}
+
+function getSSLStatistics() {
+ let SSL3Statistics = new ctypes.StructType("SSL3Statistics", [
+ { sch_sid_cache_hits: ctypes.long },
+ { sch_sid_cache_misses: ctypes.long },
+ { sch_sid_cache_not_ok: ctypes.long },
+ { hsh_sid_cache_hits: ctypes.long },
+ { hsh_sid_cache_misses: ctypes.long },
+ { hsh_sid_cache_not_ok: ctypes.long },
+ { hch_sid_cache_hits: ctypes.long },
+ { hch_sid_cache_misses: ctypes.long },
+ { hch_sid_cache_not_ok: ctypes.long },
+ { sch_sid_stateless_resumes: ctypes.long },
+ { hsh_sid_stateless_resumes: ctypes.long },
+ { hch_sid_stateless_resumes: ctypes.long },
+ { hch_sid_ticket_parse_failures: ctypes.long },
+ ]);
+ let SSL3StatisticsPtr = new ctypes.PointerType(SSL3Statistics);
+ let SSL_GetStatistics = null;
+ try {
+ SSL_GetStatistics = _getLibraryFunctionWithNoArguments(
+ "SSL_GetStatistics",
+ "ssl3",
+ SSL3StatisticsPtr
+ );
+ } catch (e) {
+ // On Windows, this is actually in the nss3 library.
+ SSL_GetStatistics = _getLibraryFunctionWithNoArguments(
+ "SSL_GetStatistics",
+ "nss3",
+ SSL3StatisticsPtr
+ );
+ }
+ if (!SSL_GetStatistics) {
+ throw new Error("Failed to get SSL statistics");
+ }
+ return SSL_GetStatistics();
+}
+
+// Set up a TLS testing environment that has a TLS server running and
+// ready to accept connections. This async function starts the server and
+// waits for the server to indicate that it is ready.
+//
+// Each test should have its own subdomain of example.com, for example
+// my-first-connection-test.example.com. The server can use the server
+// name (passed through the SNI TLS extension) to determine what behavior
+// the server side of the text should exhibit. See TLSServer.h for more
+// information on how to write the server side of tests.
+//
+// Create a new source file for your new server executable in
+// security/manager/ssl/tests/unit/tlsserver/cmd similar to the other ones in
+// that directory, and add a reference to it to the sources variable in that
+// directory's moz.build.
+//
+// Modify TEST_HARNESS_BINS in
+// testing/mochitest/Makefile.in and NO_PKG_FILES in
+// toolkit/mozapps/installer/packager.mk to make sure the new executable
+// gets included in the packages used for shipping the tests to the test
+// runners in our build/test farm. (Things will work fine locally without
+// these changes but will break on TBPL.)
+//
+// Your test script should look something like this:
+/*
+
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// 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/.
+"use strict";
+
+// <documentation on your test>
+
+function run_test() {
+ do_get_profile();
+ add_tls_server_setup("<test-server-name>", "<path-to-certificate-directory>");
+
+ add_connection_test("<test-name-1>.example.com",
+ SEC_ERROR_xxx,
+ function() { ... },
+ function(aTransportSecurityInfo) { ... },
+ function(aTransport) { ... });
+ [...]
+ add_connection_test("<test-name-n>.example.com", PRErrorCodeSuccess);
+
+ run_next_test();
+}
+*/
+
+function add_tls_server_setup(serverBinName, certsPath, addDefaultRoot = true) {
+ add_test(function() {
+ _setupTLSServerTest(serverBinName, certsPath, addDefaultRoot);
+ });
+}
+
+/**
+ * Add a TLS connection test case.
+ *
+ * @param {String} aHost
+ * The hostname to pass in the SNI TLS extension; this should unambiguously
+ * identify which test is being run.
+ * @param {PRErrorCode} aExpectedResult
+ * The expected result of the connection. If an error is not expected, pass
+ * in PRErrorCodeSuccess.
+ * @param {Function} aBeforeConnect
+ * A callback function that takes no arguments that will be called before the
+ * connection is attempted.
+ * @param {Function} aWithSecurityInfo
+ * A callback function that takes an nsITransportSecurityInfo, which is called
+ * after the TLS handshake succeeds.
+ * @param {Function} aAfterStreamOpen
+ * A callback function that is called with the nsISocketTransport once the
+ * output stream is ready.
+ * @param {OriginAttributes} aOriginAttributes (optional)
+ * The origin attributes that the socket transport will have. This parameter
+ * affects OCSP because OCSP cache is double-keyed by origin attributes' first
+ * party domain.
+ *
+ * @param {OriginAttributes} aEchConfig (optional)
+ * A Base64-encoded ECHConfig. If non-empty, it will be configured to the client
+ * socket resulting in an Encrypted Client Hello extension being sent. The client
+ * keypair is ephermeral and generated within NSS.
+ */
+function add_connection_test(
+ aHost,
+ aExpectedResult,
+ aBeforeConnect,
+ aWithSecurityInfo,
+ aAfterStreamOpen,
+ /* optional */ aOriginAttributes,
+ /* optional */ aEchConfig
+) {
+ add_test(function() {
+ if (aBeforeConnect) {
+ aBeforeConnect();
+ }
+ asyncConnectTo(
+ aHost,
+ aExpectedResult,
+ aWithSecurityInfo,
+ aAfterStreamOpen,
+ aOriginAttributes,
+ aEchConfig
+ ).then(run_next_test);
+ });
+}
+
+async function asyncConnectTo(
+ aHost,
+ aExpectedResult,
+ /* optional */ aWithSecurityInfo = undefined,
+ /* optional */ aAfterStreamOpen = undefined,
+ /* optional */ aOriginAttributes = undefined,
+ /* optional */ aEchConfig = undefined
+) {
+ const REMOTE_PORT = 8443;
+
+ function Connection(host) {
+ this.host = host;
+ this.thread = Services.tm.currentThread;
+ this.defer = Promise.defer();
+ let sts = Cc["@mozilla.org/network/socket-transport-service;1"].getService(
+ Ci.nsISocketTransportService
+ );
+ this.transport = sts.createTransport(["ssl"], host, REMOTE_PORT, null);
+ if (aEchConfig) {
+ this.transport.setEchConfig(atob(aEchConfig));
+ }
+ // See bug 1129771 - attempting to connect to [::1] when the server is
+ // listening on 127.0.0.1 causes frequent failures on OS X 10.10.
+ this.transport.connectionFlags |= Ci.nsISocketTransport.DISABLE_IPV6;
+ this.transport.setEventSink(this, this.thread);
+ if (aOriginAttributes) {
+ this.transport.originAttributes = aOriginAttributes;
+ }
+ this.inputStream = null;
+ this.outputStream = null;
+ this.connected = false;
+ }
+
+ Connection.prototype = {
+ // nsITransportEventSink
+ onTransportStatus(aTransport, aStatus, aProgress, aProgressMax) {
+ if (
+ !this.connected &&
+ aStatus == Ci.nsISocketTransport.STATUS_CONNECTED_TO
+ ) {
+ this.connected = true;
+ this.outputStream.asyncWait(this, 0, 0, this.thread);
+ }
+ },
+
+ // nsIInputStreamCallback
+ onInputStreamReady(aStream) {
+ try {
+ // this will throw if the stream has been closed by an error
+ let str = NetUtil.readInputStreamToString(aStream, aStream.available());
+ Assert.equal(str, "0", "Should have received ASCII '0' from server");
+ this.inputStream.close();
+ this.outputStream.close();
+ this.result = Cr.NS_OK;
+ } catch (e) {
+ this.result = e.result;
+ }
+ this.defer.resolve(this);
+ },
+
+ // nsIOutputStreamCallback
+ onOutputStreamReady(aStream) {
+ if (aAfterStreamOpen) {
+ aAfterStreamOpen(this.transport);
+ }
+ this.outputStream.write("0", 1);
+ let inStream = this.transport
+ .openInputStream(0, 0, 0)
+ .QueryInterface(Ci.nsIAsyncInputStream);
+ this.inputStream = inStream;
+ this.inputStream.asyncWait(this, 0, 0, this.thread);
+ },
+
+ go() {
+ this.outputStream = this.transport
+ .openOutputStream(0, 0, 0)
+ .QueryInterface(Ci.nsIAsyncOutputStream);
+ return this.defer.promise;
+ },
+ };
+
+ /* Returns a promise to connect to host that resolves to the result of that
+ * connection */
+ function connectTo(host) {
+ Services.prefs.setCharPref("network.dns.localDomains", host);
+ let connection = new Connection(host);
+ return connection.go();
+ }
+
+ return connectTo(aHost).then(function(conn) {
+ info("handling " + aHost);
+ let expectedNSResult =
+ aExpectedResult == PRErrorCodeSuccess
+ ? Cr.NS_OK
+ : getXPCOMStatusFromNSS(aExpectedResult);
+ Assert.equal(
+ conn.result,
+ expectedNSResult,
+ "Actual and expected connection result should match"
+ );
+ if (aWithSecurityInfo) {
+ aWithSecurityInfo(
+ conn.transport.securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
+ );
+ }
+ });
+}
+
+function _getBinaryUtil(binaryUtilName) {
+ let utilBin = Services.dirsvc.get("GreD", Ci.nsIFile);
+ // On macOS, GreD is .../Contents/Resources, and most binary utilities
+ // are located there, but certutil is in GreBinD (or .../Contents/MacOS),
+ // so we have to change the path accordingly.
+ if (binaryUtilName === "certutil") {
+ utilBin = Services.dirsvc.get("GreBinD", Ci.nsIFile);
+ }
+ utilBin.append(binaryUtilName + mozinfo.bin_suffix);
+ // If we're testing locally, the above works. If not, the server executable
+ // is in another location.
+ if (!utilBin.exists()) {
+ utilBin = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ while (utilBin.path.includes("xpcshell")) {
+ utilBin = utilBin.parent;
+ }
+ utilBin.append("bin");
+ utilBin.append(binaryUtilName + mozinfo.bin_suffix);
+ }
+ // But maybe we're on Android, where binaries are in /data/local/xpcb.
+ if (!utilBin.exists()) {
+ utilBin.initWithPath("/data/local/xpcb/");
+ utilBin.append(binaryUtilName);
+ }
+ Assert.ok(utilBin.exists(), `Binary util ${binaryUtilName} should exist`);
+ return utilBin;
+}
+
+// Do not call this directly; use add_tls_server_setup
+function _setupTLSServerTest(serverBinName, certsPath, addDefaultRoot) {
+ asyncStartTLSTestServer(serverBinName, certsPath, addDefaultRoot).then(
+ run_next_test
+ );
+}
+
+async function asyncStartTLSTestServer(
+ serverBinName,
+ certsPath,
+ addDefaultRoot
+) {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ // The trusted CA that is typically used for "good" certificates.
+ if (addDefaultRoot) {
+ addCertFromFile(certdb, `${certsPath}/test-ca.pem`, "CTu,u,u");
+ }
+
+ const CALLBACK_PORT = 8444;
+
+ let envSvc = Cc["@mozilla.org/process/environment;1"].getService(
+ Ci.nsIEnvironment
+ );
+ let greBinDir = Services.dirsvc.get("GreBinD", Ci.nsIFile);
+ envSvc.set("DYLD_LIBRARY_PATH", greBinDir.path);
+ // TODO(bug 1107794): Android libraries are in /data/local/xpcb, but "GreBinD"
+ // does not return this path on Android, so hard code it here.
+ envSvc.set("LD_LIBRARY_PATH", greBinDir.path + ":/data/local/xpcb");
+ envSvc.set("MOZ_TLS_SERVER_DEBUG_LEVEL", "3");
+ envSvc.set("MOZ_TLS_SERVER_CALLBACK_PORT", CALLBACK_PORT);
+
+ let httpServer = new HttpServer();
+ let serverReady = new Promise(resolve => {
+ httpServer.registerPathHandler("/", function handleServerCallback(
+ aRequest,
+ aResponse
+ ) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+ aResponse.setHeader("Content-Type", "text/plain");
+ let responseBody = "OK!";
+ aResponse.bodyOutputStream.write(responseBody, responseBody.length);
+ executeSoon(function() {
+ httpServer.stop(resolve);
+ });
+ });
+ httpServer.start(CALLBACK_PORT);
+ });
+
+ let serverBin = _getBinaryUtil(serverBinName);
+ let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+ process.init(serverBin);
+ let certDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ certDir.append(`${certsPath}`);
+ Assert.ok(certDir.exists(), `certificate folder (${certsPath}) should exist`);
+ // Using "sql:" causes the SQL DB to be used so we can run tests on Android.
+ process.run(false, ["sql:" + certDir.path, Services.appinfo.processID], 2);
+
+ registerCleanupFunction(function() {
+ process.kill();
+ });
+
+ await serverReady;
+}
+
+// Returns an Array of OCSP responses for a given ocspRespArray and a location
+// for a nssDB where the certs and public keys are prepopulated.
+// ocspRespArray is an array of arrays like:
+// [ [typeOfResponse, certnick, extracertnick, thisUpdateSkew]...]
+function generateOCSPResponses(ocspRespArray, nssDBlocation) {
+ let utilBinName = "GenerateOCSPResponse";
+ let ocspGenBin = _getBinaryUtil(utilBinName);
+ let retArray = [];
+
+ for (let i = 0; i < ocspRespArray.length; i++) {
+ let argArray = [];
+ let ocspFilepre = do_get_file(i.toString() + ".ocsp", true);
+ let filename = ocspFilepre.path;
+ // Using "sql:" causes the SQL DB to be used so we can run tests on Android.
+ argArray.push("sql:" + nssDBlocation);
+ argArray.push(ocspRespArray[i][0]); // ocsRespType;
+ argArray.push(ocspRespArray[i][1]); // nick;
+ argArray.push(ocspRespArray[i][2]); // extranickname
+ argArray.push(ocspRespArray[i][3]); // thisUpdate skew
+ argArray.push(filename);
+ info("argArray = " + argArray);
+
+ let process = Cc["@mozilla.org/process/util;1"].createInstance(
+ Ci.nsIProcess
+ );
+ process.init(ocspGenBin);
+ process.run(true, argArray, argArray.length);
+ Assert.equal(0, process.exitValue, "Process exit value should be 0");
+ let ocspFile = do_get_file(i.toString() + ".ocsp", false);
+ retArray.push(readFile(ocspFile));
+ ocspFile.remove(false);
+ }
+ return retArray;
+}
+
+// Starts and returns an http responder that will cause a test failure if it is
+// queried. The server identities are given by a non-empty array
+// serverIdentities.
+function getFailingHttpServer(serverPort, serverIdentities) {
+ let httpServer = new HttpServer();
+ httpServer.registerPrefixHandler("/", function(request, response) {
+ Assert.ok(false, "HTTP responder should not have been queried");
+ });
+ httpServer.identity.setPrimary("http", serverIdentities.shift(), serverPort);
+ serverIdentities.forEach(function(identity) {
+ httpServer.identity.add("http", identity, serverPort);
+ });
+ httpServer.start(serverPort);
+ return httpServer;
+}
+
+// Starts an http OCSP responder that serves good OCSP responses and
+// returns an object with a method stop that should be called to stop
+// the http server.
+// NB: Because generating OCSP responses inside the HTTP request
+// handler can cause timeouts, the expected responses are pre-generated
+// all at once before starting the server. This means that their producedAt
+// times will all be the same. If a test depends on this not being the case,
+// perhaps calling startOCSPResponder twice (at different times) will be
+// necessary.
+//
+// serverPort is the port of the http OCSP responder
+// identity is the http hostname that will answer the OCSP requests
+// nssDBLocation is the location of the NSS database from where the OCSP
+// responses will be generated (assumes appropiate keys are present)
+// expectedCertNames is an array of nicks of the certs to be responsed
+// expectedBasePaths is an optional array that is used to indicate
+// what is the expected base path of the OCSP request.
+// expectedMethods is an optional array of methods ("GET" or "POST") indicating
+// by which HTTP method the server is expected to be queried.
+// expectedResponseTypes is an optional array of OCSP response types to use (see
+// GenerateOCSPResponse.cpp).
+// responseHeaderPairs is an optional array of HTTP header (name, value) pairs
+// to set in each response.
+function startOCSPResponder(
+ serverPort,
+ identity,
+ nssDBLocation,
+ expectedCertNames,
+ expectedBasePaths,
+ expectedMethods,
+ expectedResponseTypes,
+ responseHeaderPairs = []
+) {
+ let ocspResponseGenerationArgs = expectedCertNames.map(function(
+ expectedNick
+ ) {
+ let responseType = "good";
+ if (expectedResponseTypes && expectedResponseTypes.length >= 1) {
+ responseType = expectedResponseTypes.shift();
+ }
+ return [responseType, expectedNick, "unused", 0];
+ });
+ let ocspResponses = generateOCSPResponses(
+ ocspResponseGenerationArgs,
+ nssDBLocation
+ );
+ let httpServer = new HttpServer();
+ httpServer.registerPrefixHandler("/", function handleServerCallback(
+ aRequest,
+ aResponse
+ ) {
+ info("got request for: " + aRequest.path);
+ let basePath = aRequest.path.slice(1).split("/")[0];
+ if (expectedBasePaths.length >= 1) {
+ Assert.equal(
+ basePath,
+ expectedBasePaths.shift(),
+ "Actual and expected base path should match"
+ );
+ }
+ Assert.ok(
+ expectedCertNames.length >= 1,
+ "expectedCertNames should contain >= 1 entries"
+ );
+ if (expectedMethods && expectedMethods.length >= 1) {
+ Assert.equal(
+ aRequest.method,
+ expectedMethods.shift(),
+ "Actual and expected fetch method should match"
+ );
+ }
+ aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+ aResponse.setHeader("Content-Type", "application/ocsp-response");
+ for (let headerPair of responseHeaderPairs) {
+ aResponse.setHeader(headerPair[0], headerPair[1]);
+ }
+ aResponse.write(ocspResponses.shift());
+ });
+ httpServer.identity.setPrimary("http", identity, serverPort);
+ httpServer.start(serverPort);
+ return {
+ stop(callback) {
+ // make sure we consumed each expected response
+ Assert.equal(
+ ocspResponses.length,
+ 0,
+ "Should have 0 remaining expected OCSP responses"
+ );
+ if (expectedMethods) {
+ Assert.equal(
+ expectedMethods.length,
+ 0,
+ "Should have 0 remaining expected fetch methods"
+ );
+ }
+ if (expectedBasePaths) {
+ Assert.equal(
+ expectedBasePaths.length,
+ 0,
+ "Should have 0 remaining expected base paths"
+ );
+ }
+ if (expectedResponseTypes) {
+ Assert.equal(
+ expectedResponseTypes.length,
+ 0,
+ "Should have 0 remaining expected response types"
+ );
+ }
+ httpServer.stop(callback);
+ },
+ };
+}
+
+// Given an OCSP responder (see startOCSPResponder), returns a promise that
+// resolves when the responder has successfully stopped.
+function stopOCSPResponder(responder) {
+ return new Promise((resolve, reject) => {
+ responder.stop(resolve);
+ });
+}
+
+// Utility functions for adding tests relating to certificate error overrides
+
+// Helper function for add_cert_override_test. Probably doesn't need to be
+// called directly.
+function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
+ let bits =
+ (aSecurityInfo.isUntrusted
+ ? Ci.nsICertOverrideService.ERROR_UNTRUSTED
+ : 0) |
+ (aSecurityInfo.isDomainMismatch
+ ? Ci.nsICertOverrideService.ERROR_MISMATCH
+ : 0) |
+ (aSecurityInfo.isNotValidAtThisTime
+ ? Ci.nsICertOverrideService.ERROR_TIME
+ : 0);
+
+ Assert.equal(
+ bits,
+ aExpectedBits,
+ "Actual and expected override bits should match"
+ );
+ let cert = aSecurityInfo.serverCert;
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.rememberValidityOverride(
+ aHost,
+ 8443,
+ cert,
+ aExpectedBits,
+ true
+ );
+}
+
+// Given a host, expected error bits (see nsICertOverrideService.idl), and an
+// expected error code, tests that an initial connection to the host fails
+// with the expected errors and that adding an override results in a subsequent
+// connection succeeding.
+function add_cert_override_test(
+ aHost,
+ aExpectedBits,
+ aExpectedError,
+ aExpectedSecInfo = undefined
+) {
+ add_connection_test(
+ aHost,
+ aExpectedError,
+ null,
+ add_cert_override.bind(this, aHost, aExpectedBits)
+ );
+ add_connection_test(aHost, PRErrorCodeSuccess, null, aSecurityInfo => {
+ Assert.ok(
+ aSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
+ "Cert override flag should be set on the security state"
+ );
+ if (aExpectedSecInfo) {
+ if (aExpectedSecInfo.failedCertChain) {
+ ok(
+ aExpectedSecInfo.failedCertChain.equals(aSecurityInfo.failedCertChain)
+ );
+ }
+ }
+ });
+}
+
+// Helper function for add_prevented_cert_override_test. This is much like
+// add_cert_override except it may not be the case that the connection has an
+// SecInfo set on it. In this case, the error was not overridable anyway, so
+// we consider it a success.
+function attempt_adding_cert_override(aHost, aExpectedBits, aSecurityInfo) {
+ if (aSecurityInfo.serverCert) {
+ let bits =
+ (aSecurityInfo.isUntrusted
+ ? Ci.nsICertOverrideService.ERROR_UNTRUSTED
+ : 0) |
+ (aSecurityInfo.isDomainMismatch
+ ? Ci.nsICertOverrideService.ERROR_MISMATCH
+ : 0) |
+ (aSecurityInfo.isNotValidAtThisTime
+ ? Ci.nsICertOverrideService.ERROR_TIME
+ : 0);
+ Assert.equal(
+ bits,
+ aExpectedBits,
+ "Actual and expected override bits should match"
+ );
+ let cert = aSecurityInfo.serverCert;
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.rememberValidityOverride(
+ aHost,
+ 8443,
+ cert,
+ aExpectedBits,
+ true
+ );
+ }
+}
+
+// Given a host, expected error bits (see nsICertOverrideService.idl), and
+// an expected error code, tests that an initial connection to the host fails
+// with the expected errors and that adding an override does not result in a
+// subsequent connection succeeding (i.e. the same error code is encountered).
+// The idea here is that for HSTS hosts or hosts with key pins, no error is
+// overridable, even if an entry is added to the override service.
+function add_prevented_cert_override_test(
+ aHost,
+ aExpectedBits,
+ aExpectedError
+) {
+ add_connection_test(
+ aHost,
+ aExpectedError,
+ null,
+ attempt_adding_cert_override.bind(this, aHost, aExpectedBits)
+ );
+ add_connection_test(aHost, aExpectedError);
+}
+
+// Helper for asyncTestCertificateUsages.
+class CertVerificationResult {
+ constructor(certName, usageString, successExpected, resolve) {
+ this.certName = certName;
+ this.usageString = usageString;
+ this.successExpected = successExpected;
+ this.resolve = resolve;
+ }
+
+ verifyCertFinished(aPRErrorCode, aVerifiedChain, aHasEVPolicy) {
+ if (this.successExpected) {
+ equal(
+ aPRErrorCode,
+ PRErrorCodeSuccess,
+ `verifying ${this.certName} for ${this.usageString} should succeed`
+ );
+ } else {
+ notEqual(
+ aPRErrorCode,
+ PRErrorCodeSuccess,
+ `verifying ${this.certName} for ${this.usageString} should fail`
+ );
+ }
+ this.resolve();
+ }
+}
+
+/**
+ * Asynchronously attempts to verify the given certificate for all supported
+ * usages (see allCertificateUsages). Verifies that the results match the
+ * expected successful usages. Returns a promise that will resolve when all
+ * verifications have been performed.
+ * Verification happens "now" with no specified flags or hostname.
+ *
+ * @param {nsIX509CertDB} certdb
+ * The certificate database to use to verify the certificate.
+ * @param {nsIX509Cert} cert
+ * The certificate to be verified.
+ * @param {Number[]} expectedUsages
+ * A list of usages (as their integer values) that are expected to verify
+ * successfully.
+ * @return {Promise}
+ * A promise that will resolve with no value when all asynchronous operations
+ * have completed.
+ */
+function asyncTestCertificateUsages(certdb, cert, expectedUsages) {
+ let now = new Date().getTime() / 1000;
+ let promises = [];
+ Object.keys(allCertificateUsages).forEach(usageString => {
+ let promise = new Promise((resolve, reject) => {
+ let usage = allCertificateUsages[usageString];
+ let successExpected = expectedUsages.includes(usage);
+ let result = new CertVerificationResult(
+ cert.commonName,
+ usageString,
+ successExpected,
+ resolve
+ );
+ let flags = Ci.nsIX509CertDB.FLAG_LOCAL_ONLY;
+ certdb.asyncVerifyCertAtTime(cert, usage, flags, null, now, result);
+ });
+ promises.push(promise);
+ });
+ return Promise.all(promises);
+}
+
+/**
+ * Loads the pkcs11testmodule.cpp test PKCS #11 module, and registers a cleanup
+ * function that unloads it once the calling test completes.
+ *
+ * @param {nsIFile} libraryFile
+ * The dynamic library file that implements the module to
+ * load.
+ * @param {String} moduleName
+ * What to call the module.
+ * @param {Boolean} expectModuleUnloadToFail
+ * Should be set to true for tests that manually unload the
+ * test module, so the attempt to auto unload the test module
+ * doesn't cause a test failure. Should be set to false
+ * otherwise, so failure to automatically unload the test
+ * module gets reported.
+ */
+function loadPKCS11Module(libraryFile, moduleName, expectModuleUnloadToFail) {
+ ok(libraryFile.exists(), "The PKCS11 module file should exist");
+
+ let pkcs11ModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ registerCleanupFunction(() => {
+ try {
+ pkcs11ModuleDB.deleteModule(moduleName);
+ } catch (e) {
+ Assert.ok(
+ expectModuleUnloadToFail,
+ `Module unload should suceed only when expected: ${e}`
+ );
+ }
+ });
+ pkcs11ModuleDB.addModule(moduleName, libraryFile.path, 0, 0);
+}
+
+/**
+ * @param {String} data
+ * @returns {String}
+ */
+function hexify(data) {
+ // |slice(-2)| chomps off the last two characters of a string.
+ // Therefore, if the Unicode value is < 0x10, we have a single-character hex
+ // string when we want one that's two characters, and unconditionally
+ // prepending a "0" solves the problem.
+ return Array.from(data, (c, i) =>
+ ("0" + data.charCodeAt(i).toString(16)).slice(-2)
+ ).join("");
+}
+
+/**
+ * @param {String[]} lines
+ * Lines to write. Each line automatically has "\n" appended to it when
+ * being written.
+ * @param {nsIFileOutputStream} outputStream
+ */
+function writeLinesAndClose(lines, outputStream) {
+ for (let line of lines) {
+ line += "\n";
+ outputStream.write(line, line.length);
+ }
+ outputStream.close();
+}
+
+/**
+ * @param {String} moduleName
+ * The name of the module that should not be loaded.
+ * @param {String} libraryName
+ * A unique substring of name of the dynamic library file of the module
+ * that should not be loaded.
+ */
+function checkPKCS11ModuleNotPresent(moduleName, libraryName) {
+ let moduleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ let modules = moduleDB.listModules();
+ ok(
+ modules.hasMoreElements(),
+ "One or more modules should be present with test module not present"
+ );
+ for (let module of modules) {
+ notEqual(
+ module.name,
+ moduleName,
+ `Non-test module name shouldn't equal '${moduleName}'`
+ );
+ ok(
+ !(module.libName && module.libName.includes(libraryName)),
+ `Non-test module lib name should not include '${libraryName}'`
+ );
+ }
+}
+
+/**
+ * Checks that the test module exists in the module list.
+ * Also checks various attributes of the test module for correctness.
+ *
+ * @param {String} moduleName
+ * The name of the module that should be present.
+ * @param {String} libraryName
+ * A unique substring of the name of the dynamic library file
+ * of the module that should be loaded.
+ * @returns {nsIPKCS11Module}
+ * The test module.
+ */
+function checkPKCS11ModuleExists(moduleName, libraryName) {
+ let moduleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ let modules = moduleDB.listModules();
+ ok(
+ modules.hasMoreElements(),
+ "One or more modules should be present with test module present"
+ );
+ let testModule = null;
+ for (let module of modules) {
+ if (module.name == moduleName) {
+ testModule = module;
+ break;
+ }
+ }
+ notEqual(testModule, null, "Test module should have been found");
+ notEqual(testModule.libName, null, "Test module lib name should not be null");
+ ok(
+ testModule.libName.includes(ctypes.libraryName(libraryName)),
+ `Test module lib name should include lib name of '${libraryName}'`
+ );
+
+ return testModule;
+}
+
+// Given an nsIX509Cert, return the bytes of its subject DN (as a JS string) and
+// the sha-256 hash of its subject public key info, base64-encoded.
+function getSubjectAndSPKIHash(nsCert) {
+ let certBytes = nsCert.getRawDER();
+ let cert = new X509.Certificate();
+ cert.parse(certBytes);
+ let subject = cert.tbsCertificate.subject._der._bytes;
+ let subjectString = arrayToString(subject);
+ let spkiHashString = nsCert.sha256SubjectPublicKeyInfoDigest;
+ return { subjectString, spkiHashString };
+}
+
+function run_certutil_on_directory(directory, args, expectSuccess = true) {
+ let envSvc = Cc["@mozilla.org/process/environment;1"].getService(
+ Ci.nsIEnvironment
+ );
+ let greBinDir = Services.dirsvc.get("GreBinD", Ci.nsIFile);
+ envSvc.set("DYLD_LIBRARY_PATH", greBinDir.path);
+ // TODO(bug 1107794): Android libraries are in /data/local/xpcb, but "GreBinD"
+ // does not return this path on Android, so hard code it here.
+ envSvc.set("LD_LIBRARY_PATH", greBinDir.path + ":/data/local/xpcb");
+ let certutilBin = _getBinaryUtil("certutil");
+ let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+ process.init(certutilBin);
+ args.push("-d");
+ args.push(`sql:${directory}`);
+ process.run(true, args, args.length);
+ if (expectSuccess) {
+ Assert.equal(process.exitValue, 0, "certutil should succeed");
+ }
+}
diff --git a/security/manager/ssl/tests/unit/moz.build b/security/manager/ssl/tests/unit/moz.build
new file mode 100644
index 0000000000..212f14a22f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += ["tlsserver"]
+
+if not CONFIG["MOZ_NO_SMART_CARDS"]:
+ DIRS += ["pkcs11testmodule"]
+
+TEST_DIRS += [
+ "bad_certs",
+ "ocsp_certs",
+ "test_baseline_requirements",
+ "test_cert_eku",
+ "test_cert_embedded_null",
+ "test_cert_keyUsage",
+ "test_cert_sha1",
+ "test_cert_signatures",
+ "test_cert_trust",
+ "test_cert_utf8",
+ "test_cert_version",
+ "test_certDB_import",
+ "test_content_signing",
+ "test_ct",
+ "test_delegated_credentials",
+ "test_encrypted_client_hello",
+ "test_ev_certs",
+ "test_intermediate_basic_usage_constraints",
+ "test_intermediate_preloads",
+ "test_keysize",
+ "test_keysize_ev",
+ "test_missing_intermediate",
+ "test_name_constraints",
+ "test_ocsp_url",
+ "test_onecrl",
+ "test_sanctions",
+ "test_signed_apps",
+ "test_startcom_wosign",
+ "test_validity",
+]
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem b/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem
new file mode 100644
index 0000000000..3fffeaf56f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRTCCAi2gAwIBAgIUF4tSVpnzLjrUBoa9WMfShlqrEjwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAvMS0wKwYDVQQDDCRUZXN0IEludGVybWVkaWF0ZSB1c2VkIGFz
+IEVuZC1FbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjcjBwMAwGA1UdEwQFMAMBAf8wMgYIKwYBBQUHAQEE
+JjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMCwGA1UdEQQl
+MCOCIWNhLXVzZWQtYXMtZW5kLWVudGl0eS5leGFtcGxlLmNvbTANBgkqhkiG9w0B
+AQsFAAOCAQEAllsGy7B4yROdLH5rmLV6Q6AxUMKk/ixwfCnkt3ibKGtoypvAG6wn
+vevqezN7BUeqIZd0QeoZBEtKwxv87oCiVDgSPXhkGqxryN9i8Zii07Sa27rCVZHd
+F/AJv7qSgl2mYPYAAcyDX5F5ecbc3i9tc96mYSUJawhgPCwNB6PGB+HUr5fSskzx
+j7Rg+0k9TEpk5bcTlsqH2hFvM9ZycEFC0/9trBVvnvh51WTHX/AzZDaaHPyOd2jD
+RxqCMwde0EfpNQmpz39WnRJqs7bf2Cdc080m/apFL9yjZOuCfNaWdezfNjWoBKNC
+S5tpZOoqNYzdV/8VAy74Nv+cXGNX4Ct7RA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem.certspec
new file mode 100644
index 0000000000..8e16705b50
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ca-used-as-end-entity.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate used as End-Entity
+extension:basicConstraints:cA,
+extension:authorityInformationAccess:http://localhost:8888/
+extension:subjectAlternativeName:ca-used-as-end-entity.example.com
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key.keyspec b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem
new file mode 100644
index 0000000000..c85c051004
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDiTCCAnGgAwIBAgIUUwG2e1zCLPYPQc2aSZ4UsI/GiK0wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcow
+gccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tghUqLnBp
+bm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcu
+ZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBs
+ZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxo
+b3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCbcbjTQmzRu++LJ/R1KjA99THZ
+aRGG7u0knPs40bz+rIOAR7SllYTvZ1g5HanNG3GZ5+DExVmVtixcrqJFTV0BJsi0
+rv8XR4F3Cdict+rJ+hCSBqu6BGNWdptsaSPiSm+eL//tgjGY1zm9ln1B/OvTYA/n
+f+OV07v44pwRBUe8C9Awb2J3KMHATPciKTk0Pwmh0jXi4FN9ehG1rXZMY2daHoKq
+hzbBc8EaGzPPAyFumHd6wNqWX+/chEtT00SlcJw/lbQZnK8XvUSOhRuUeRdCM5wX
+3w+Gy4P/FrI5tePoR9606GR6plC8QZxT3+Z6lTyCHz3I05+PNXwfmZH3ABSg
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem.certspec
new file mode 100644
index 0000000000..554339ff52
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/default-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem
new file mode 100644
index 0000000000..8e6085c3e1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4zCCAcugAwIBAgIUE031neMThT1B9azoKkIDscqx+88wDQYJKoZIhvcNAQEF
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAoMSYwJAYDVQQDDB1UZXN0IFNIQTEgRGVsZWdhdGVkIFJlc3Bv
+bmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMF1xlJmCZ93CCpn
+kfG4dsN/XOU4sGxKzSKxy9RvplraKt1ByMJJisSjs8H2FIf0G2mJQb2ApRw8EgJE
+xYSkxEgzBeUTjAEGzwi+moYnYLrmoujzbyPF2YMTud+vN4NF2s5R1Nbc0qbLPMcG
+680wcOyYzOQKpZHXKVp/ccW+ZmkdKy3+yElEWQvFo+pJ/ZOx11NAXxdzdpmVhmYl
+R5ftQmkIiAgRQiBpmIpD/uSM5oeB3SK2ppzSg3UTH5MrEozihvp9JRwGKtJ+8Bbx
+h83VToMrNbiTD3S6kKqLx2FnJCqx/W1iFA0YxMC4xo/DdIRXMkrX3obmVS8dHhkd
+cSFo07sCAwEAAaMXMBUwEwYDVR0lBAwwCgYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
+BQADggEBAI2xNUwZgoTpe439ouS6dI/8JRgWKTq9pzZdW5v7a0Tlwx7KUmoZ6mlZ
+xgvCVlk/6zIXT+eN0l79YDzWGzqjLpsFPuxPuczb1KraEFBteHwm/lrUpij2cnYD
+yOUNgCpIpUfR0UO5AOzAs1yHeMb/5YkfFl+4xU6rY6QL0RaI5n9rFbaPbXE0cNGg
+ORxg8K6/JwlwsfXg0pT/5KtcWEvdo2YVU5X26UROFBbwtWmcp6xpqnYVJY69mXGN
+utLR039Nv6IO5F6m0looSt0p3SSCNgqyls3GY6c8xXqS0YOR04IvGDHqWg2QKPtQ
+T1HiTliyNYNFzr2e84eTeqQm8aTUMYs=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem.certspec
new file mode 100644
index 0000000000..bdf3e2ee4d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSHA1Signer.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test SHA1 Delegated Responder
+subjectKey:alternate
+signature:sha1WithRSAEncryption
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem
new file mode 100644
index 0000000000..9c5ceb2af7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3jCCAcagAwIBAgIUM8qmS5w0gb5569IOxnYzJV4lfV4wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAjMSEwHwYDVQQDDBhUZXN0IERlbGVnYXRlZCBSZXNwb25kZXIw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBdcZSZgmfdwgqZ5HxuHbD
+f1zlOLBsSs0iscvUb6Za2irdQcjCSYrEo7PB9hSH9BtpiUG9gKUcPBICRMWEpMRI
+MwXlE4wBBs8IvpqGJ2C65qLo828jxdmDE7nfrzeDRdrOUdTW3NKmyzzHBuvNMHDs
+mMzkCqWR1ylaf3HFvmZpHSst/shJRFkLxaPqSf2TsddTQF8Xc3aZlYZmJUeX7UJp
+CIgIEUIgaZiKQ/7kjOaHgd0itqac0oN1Ex+TKxKM4ob6fSUcBirSfvAW8YfN1U6D
+KzW4kw90upCqi8dhZyQqsf1tYhQNGMTAuMaPw3SEVzJK196G5lUvHR4ZHXEhaNO7
+AgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMJMA0GCSqGSIb3DQEBCwUAA4IB
+AQCeOJWvm8T7quyhSh8Yyu6/h7Yzi6h7YcVaK0azDuvWpWvJALZcYnKir6qOFbbD
+x9pVEZ1r5DmwuC0UqBKo3KC8sDykT4fkjxKzW1OfOI0Iy6i76LHDGN7Ohg/U8I6x
+V3rdTyVkNxFDKo4nK5WHNkMR5CBY7LcysV5sDFI79O3eM62UVSgP7+RaOV0cul0x
+S8a1uad+azyE7EIM9qVkaO9456Z8ZT9P04fX5LRjrY/RVKXAUmpWGFmGes+matjw
+2StNMW9jmbxc+eFoMUqUD9R4iJdElGj8ut4JbRvAHmtPv/Bj64WwIvsdvLRkCN06
+7vmqrN3x/8Bjga6uDx+zMHkr
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem.certspec
new file mode 100644
index 0000000000..19971eeb4d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/delegatedSigner.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test Delegated Responder
+subjectKey:alternate
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem
new file mode 100644
index 0000000000..7d22b7e3d1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAeqgAwIBAgIUEePkwqVPaz9IVH+MGS7msFHPqyowDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowPTE7MDkGA1UEAwwyVGVzdCBJbnZhbGlkIERl
+bGVnYXRlZCBSZXNwb25kZXIgRnJvbSBJbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDBdcZSZgmfdwgqZ5HxuHbDf1zlOLBsSs0iscvU
+b6Za2irdQcjCSYrEo7PB9hSH9BtpiUG9gKUcPBICRMWEpMRIMwXlE4wBBs8IvpqG
+J2C65qLo828jxdmDE7nfrzeDRdrOUdTW3NKmyzzHBuvNMHDsmMzkCqWR1ylaf3HF
+vmZpHSst/shJRFkLxaPqSf2TsddTQF8Xc3aZlYZmJUeX7UJpCIgIEUIgaZiKQ/7k
+jOaHgd0itqac0oN1Ex+TKxKM4ob6fSUcBirSfvAW8YfN1U6DKzW4kw90upCqi8dh
+ZyQqsf1tYhQNGMTAuMaPw3SEVzJK196G5lUvHR4ZHXEhaNO7AgMBAAGjFzAVMBMG
+A1UdJQQMMAoGCCsGAQUFBwMJMA0GCSqGSIb3DQEBCwUAA4IBAQBFmLHca/8lrjch
+HtvuzERgSHeF8gsOjCKbkKmIJqAX7oAl+xoGV7sRzbk9Jcsl1be+xoffBCfuBPXI
+a1Mr08nB7HV5h1fKu1Bt68i1rpKp0cDawfJeccovZQCbEpmwJ+sF5SCdA628KxoX
+JclPsYODRqt6JSTb7WTRD7tIDO+CO9Q9Z2fis3zj6ZEN+mgnqj/SRFjkHnGKaRLN
+1Aaj5Lbr6B1HZN5UbhAKySqkDAm/iWg8pFm6syJ3qMfjf65jecvbP6T0gXDueYG/
+qAcnddtqGmQQvU1WbmbtC4Vxg71yw62OhRtfUd5gVpRaVWE9Rcv09JHsu5Nox14p
+sTEjoV/Q
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem.certspec
new file mode 100644
index 0000000000..be0d3e9e5f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerFromIntermediate.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate
+subject:Test Invalid Delegated Responder From Intermediate
+subjectKey:alternate
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem
new file mode 100644
index 0000000000..f44489f591
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8jCCAdqgAwIBAgIUbf11GwRR/gsZC/ZdyZ4rphMYM9AwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjA/MT0wOwYDVQQDDDRUZXN0IEludmFsaWQgRGVsZWdhdGVkIFJl
+c3BvbmRlciBrZXlVc2FnZSBjcmxTaWduaW5nMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAwXXGUmYJn3cIKmeR8bh2w39c5TiwbErNIrHL1G+mWtoq3UHI
+wkmKxKOzwfYUh/QbaYlBvYClHDwSAkTFhKTESDMF5ROMAQbPCL6ahidguuai6PNv
+I8XZgxO53683g0XazlHU1tzSpss8xwbrzTBw7JjM5AqlkdcpWn9xxb5maR0rLf7I
+SURZC8Wj6kn9k7HXU0BfF3N2mZWGZiVHl+1CaQiICBFCIGmYikP+5Izmh4HdIram
+nNKDdRMfkysSjOKG+n0lHAYq0n7wFvGHzdVOgys1uJMPdLqQqovHYWckKrH9bWIU
+DRjEwLjGj8N0hFcyStfehuZVLx0eGR1xIWjTuwIDAQABow8wDTALBgNVHQ8EBAMC
+AQIwDQYJKoZIhvcNAQELBQADggEBADLwe7YTejSPwSk2T+oDdGunG96A7fTOadGo
+/ygjd90S4ockZ+Bi/WgpEf/nPAhArYjKOdL0ND1lm2Zv5f5Ge/XrqX2rL4s7Owfq
+sOounyEP2RMwX58I1bkrCsx7kKGDOIRXyhukaCdiircMiu/GT8tiBmxCJ5NfTsEi
+k0R3+RaFQ0uhuryJWJ0IzjwPx7MaekSJuZ3rwSkm41zgCLU6cU9PkBW3jGW+M4Dg
+cTvUzqryhFfdctaESk5OUT2XW7f4s1QlcbjyK5XDD97vC7zey/C8K+2xizaLDBVZ
+iWdoE3WDmOHcYlLFeVg2l2sVAO1slQItVuQ1myy3VZo1r+SXEgs=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec
new file mode 100644
index 0000000000..2833ed9b52
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test Invalid Delegated Responder keyUsage crlSigning
+subjectKey:alternate
+extension:keyUsage:cRLSign
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem
new file mode 100644
index 0000000000..4d8de4e52d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcSgAwIBAgIULfke5oAO5ZbtLXezjOOcrqEQIHwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjA6MTgwNgYDVQQDDC9UZXN0IEludmFsaWQgRGVsZWdhdGVkIFJl
+c3BvbmRlciBObyBleHRLZXlVc2FnZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMF1xlJmCZ93CCpnkfG4dsN/XOU4sGxKzSKxy9RvplraKt1ByMJJisSj
+s8H2FIf0G2mJQb2ApRw8EgJExYSkxEgzBeUTjAEGzwi+moYnYLrmoujzbyPF2YMT
+ud+vN4NF2s5R1Nbc0qbLPMcG680wcOyYzOQKpZHXKVp/ccW+ZmkdKy3+yElEWQvF
+o+pJ/ZOx11NAXxdzdpmVhmYlR5ftQmkIiAgRQiBpmIpD/uSM5oeB3SK2ppzSg3UT
+H5MrEozihvp9JRwGKtJ+8Bbxh83VToMrNbiTD3S6kKqLx2FnJCqx/W1iFA0YxMC4
+xo/DdIRXMkrX3obmVS8dHhkdcSFo07sCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+Yco2Ah5HxHlmkSm1YhZ6AyuGv/FyXmMkJ+7Jr7NCXy/MuQAGj1QtKbhFCtt2iotU
+D8V+sNPgUbtimTkstt3cdUsQhT61l+S9ZN1Be8Vza0S6RBA48RbkyDCx69DfjOWa
+yJF0WB0eg5H2GMq2+i2LNU1qgFhld62miB1g7We80GYX/+bBEfTvjuojaeSWijN2
+6txGUC1udv4o2ELsuvma0nymBr3et9xVwuFfWoLbQAeyRHCScdSku0vB/kDQb6Pt
+uCasUnBGiMPpf4w5DDNetLFh9FITYLCb4xWoPsj+6DSizVmD2/rOXhaNSwyHdWn9
+v2FW/J0cNFFuZLTSBA3z4w==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem.certspec
new file mode 100644
index 0000000000..92444c94ad
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerNoExtKeyUsage.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:Test Invalid Delegated Responder No extKeyUsage
+subjectKey:alternate
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem
new file mode 100644
index 0000000000..ddc5ca01b8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+DCCAeCgAwIBAgIUdw7kK4XEwM9Ipz41NlM1pF9VSEkwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjA9MTswOQYDVQQDDDJUZXN0IEludmFsaWQgRGVsZWdhdGVkIFJl
+c3BvbmRlciBXcm9uZyBleHRLZXlVc2FnZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAMF1xlJmCZ93CCpnkfG4dsN/XOU4sGxKzSKxy9RvplraKt1ByMJJ
+isSjs8H2FIf0G2mJQb2ApRw8EgJExYSkxEgzBeUTjAEGzwi+moYnYLrmoujzbyPF
+2YMTud+vN4NF2s5R1Nbc0qbLPMcG680wcOyYzOQKpZHXKVp/ccW+ZmkdKy3+yElE
+WQvFo+pJ/ZOx11NAXxdzdpmVhmYlR5ftQmkIiAgRQiBpmIpD/uSM5oeB3SK2ppzS
+g3UTH5MrEozihvp9JRwGKtJ+8Bbxh83VToMrNbiTD3S6kKqLx2FnJCqx/W1iFA0Y
+xMC4xo/DdIRXMkrX3obmVS8dHhkdcSFo07sCAwEAAaMXMBUwEwYDVR0lBAwwCgYI
+KwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBABdCK3EKY/6lwuCehtiHVJ6XcFBO
+rTUmmRxkrRfXzn47Llofu/QC9OISWhfV/JefrNh44mYkkscsWV76eHsEqOp2F3Gz
+Kv0bhlFt2ihCh1dzgz6tGWT5fwidow3nBSdssMZGqHD51kZXXGgB0xWopKDhnXgS
+hUforJ1X4anMZzDgV52DSGB+dTo0atgq6ke2m+nOsW8CErYVGHs2ey4lugNPo+AO
+XYaZqTclKGtrPjXt0P0E8ZaUs+Vxzx8jh0S096FDSPWMLdrfIETRB90legO8M2Qo
+woGV4FsA48aPqqy6LPEke0NltJ/AlBwS5wz54TwWkhjjBV/dmBrFV2bFQzU=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec
new file mode 100644
index 0000000000..bc704fbd41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test Invalid Delegated Responder Wrong extKeyUsage
+subjectKey:alternate
+extension:extKeyUsage:codeSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/moz.build b/security/manager/ssl/tests/unit/ocsp_certs/moz.build
new file mode 100644
index 0000000000..212b3cad1d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca-used-as-end-entity.pem',
+# 'default-ee.pem',
+# 'delegatedSHA1Signer.pem',
+# 'delegatedSigner.pem',
+# 'invalidDelegatedSignerFromIntermediate.pem',
+# 'invalidDelegatedSignerKeyUsageCrlSigning.pem',
+# 'invalidDelegatedSignerNoExtKeyUsage.pem',
+# 'invalidDelegatedSignerWrongExtKeyUsage.pem',
+# 'multi-tls-feature-bad-ee.pem',
+# 'multi-tls-feature-good-ee.pem',
+# 'must-staple-ee.pem',
+# 'must-staple-ee-with-must-staple-int.pem',
+# 'must-staple-missing-ee.pem',
+# 'ocspEEWithIntermediate.pem',
+# 'ocspOtherEndEntity.pem',
+# 'other-test-ca.pem',
+# 'rsa-1016-keysizeDelegatedSigner.pem',
+# 'test-ca.pem',
+# 'test-int.pem',
+# 'test-multi-tls-feature-int.pem',
+# 'test-must-staple-int.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'default-ee.key',
+# 'other-test-ca.key',
+# 'rsa-1016-keysizeDelegatedSigner.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem
new file mode 100644
index 0000000000..be14c630f8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDJDCCAgygAwIBAgIUX5dBLWiffAZEKD2dHegBV9jFkhMwDQYJKoZIhvcNAQEL
+BQAwNzE1MDMGA1UEAwwsVGVzdCBJbnRlcm1lZGlhdGUgV2l0aCBNdWx0aXBsZSBU
+TFMgRmVhdHVyZXMwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFow
+LDEqMCgGA1UEAwwhTXVsdGkgVExTIEZlYXR1cmUgVGVzdCBFbmQtRW50aXR5MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08
+E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc
+1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAP
+DY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQ
+gAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqV
+YR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQID
+AQABoy8wLTAYBgNVHREEETAPgg0qLmV4YW1wbGUuY29tMBEGCCsGAQUFBwEYBAUw
+AwIBBTANBgkqhkiG9w0BAQsFAAOCAQEAoxlWVngbuVoxZdE2qM6gPpMbCR0vW99b
+XWki1fUm9RwQ0XhobwDXJvpopSguoZ5gNsXoGHpLUTsQNVgdcNz9YJkjU05JhQQF
+KRAufrkry4TLe+PmGmLCWkMJh2hemv8Ih5TUs2GPS4QsbAXKa3me6T3J4q4KBlH/
+RNN0cozoIbGdUmCmSFKgxnMuUN5E5/RpN06lgZIthtjcCvgbWHrOTPDJOs50KZSr
+l4+fHiHxBoRvW0MJ3S/oixA6SQWrO86kv0iahn0kZA5cnqhrC8qI9HUnFT31k7AZ
+XS4+EolIyjDrlse8fnehhW00dXjzH+zgvUNJFyafB3AYtJTco0gVqQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem.certspec
new file mode 100644
index 0000000000..3fa2793b30
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-bad-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate With Multiple TLS Features
+subject:Multi TLS Feature Test End-Entity
+extension:subjectAlternativeName:*.example.com
+extension:TLSFeature:OCSPMustStaple
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem
new file mode 100644
index 0000000000..60a6c8b511
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDJzCCAg+gAwIBAgIUNbatscZX2IBW2HGooNJW4304zyEwDQYJKoZIhvcNAQEL
+BQAwNzE1MDMGA1UEAwwsVGVzdCBJbnRlcm1lZGlhdGUgV2l0aCBNdWx0aXBsZSBU
+TFMgRmVhdHVyZXMwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFow
+LDEqMCgGA1UEAwwhTXVsdGkgVExTIEZlYXR1cmUgVGVzdCBFbmQtRW50aXR5MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08
+E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc
+1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAP
+DY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQ
+gAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqV
+YR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQID
+AQABozIwMDAYBgNVHREEETAPgg0qLmV4YW1wbGUuY29tMBQGCCsGAQUFBwEYBAgw
+BgIBBQIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKvmpaXwjW7///EBD0Qt6u4eYWxOf
+kItU9yAiOjoKpSl803Jskqpt1WAYeOWv0+vjlFlkSR+Wb4bUiWFUG0ETXQPnpndy
+zc0smDI+2ILQSRAUqF6UYvMqqkMQfTEfZN3CTSH48BdBw2G1brWnPBl/2qRZTFRy
+/mdOZqJmE4UZhkf8ihtyQstqlVNqNAW6luS8u3tm5Uwk/ZVsH80yphF0OPQAFMGV
+GxYbNYpmaUcJ4xGH1bwvxpUc7eVvu18GpZpl74VYThaizvXWTlIQGMxQU0OgBSAx
+1uNpLNMo2eoGXPt7sC9v9fMq/KFN/VfCULlIwrALDfQdhJ4ctTQbHNCZtQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem.certspec
new file mode 100644
index 0000000000..7a8dd223d0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/multi-tls-feature-good-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate With Multiple TLS Features
+subject:Multi TLS Feature Test End-Entity
+extension:subjectAlternativeName:*.example.com
+extension:TLSFeature:OCSPMustStaple,6
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem
new file mode 100644
index 0000000000..f82cc96a06
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCDCCAfCgAwIBAgIUcXF8++LMW19oSQg8vdU5I0GGZWkwDQYJKoZIhvcNAQEL
+BQAwLTErMCkGA1UEAwwiVGVzdCBJbnRlcm1lZGlhdGUgV2l0aCBNdXN0LVN0YXBs
+ZTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAaMRgwFgYDVQQD
+DA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24a
+hvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7t
+FYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+o
+N9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0d
+JdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4
+s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjLzAtMBgGA1UdEQQRMA+CDSouZXhhbXBs
+ZS5jb20wEQYIKwYBBQUHARgEBTADAgEFMA0GCSqGSIb3DQEBCwUAA4IBAQArhytK
+0cN71epgzB/5M26RujocEL8Puk5O04x1uEbU7j3y+mNTO7bLB5z76ZKEzU98blLq
+V6Ds7PvmBHW/41KP8e7MwkOGprvUllfI8zmTvxXDV2iHGtmOvvEbfOf/QZS0e4Dj
+eZCcew9p4/+Efe5lcto6QazTfFSJLxjkpKHU6S0ZgAu5mEZl5UW7VdR5LHzHp17t
+dT4EWMt41CPoGxeJWYL5pOK14sD5GmUQ9OADzPBiN1M2v/9JY9IOWRabP1VafTYK
+95PHA21eXqmA6SjCugaCMXW99pdStcBSCvQEAWfHBWloyALzG5Mh7H14tuM41ldF
+abWEJfmtmGP2F2ru
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem.certspec
new file mode 100644
index 0000000000..352a60675d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee-with-must-staple-int.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate With Must-Staple
+subject:Test End-entity
+extension:subjectAlternativeName:*.example.com
+extension:TLSFeature:OCSPMustStaple
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem
new file mode 100644
index 0000000000..9453f79225
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAgmgAwIBAgIUFdjcRo9oZDqo1b9UOWgfewuqbdwwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjYzBh
+MBgGA1UdEQQRMA+CDSouZXhhbXBsZS5jb20wEQYIKwYBBQUHARgEBTADAgEFMDIG
+CCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4
+LzANBgkqhkiG9w0BAQsFAAOCAQEAMkt3s3WLtDF364ZlSZ+WSei6kpk8XNmQ8PlM
+SswAApahPrWlYlZ/V7Y18MHLHF26Rmoh2NdUczmLcOvvwRxq9/ekooDP4HkB7WFz
+YnfDo5lWhqRUyTVmSVUS2SS7mmvt1fPbZrB8kjATqhvAmOu23XmxZzJu3ylGfBn2
+MJhLxt3VhOymRkiX3sPFboxCsipj5zD3jeqGbwJCnlr3kwqvTayUEke35mvkwCew
+3L7akzbjXdJ8sFeLw/ZNpPbslRkVmV3lCv1Kl08U0POM6tKoow4sw4P0iVXGQqRT
+H/4Stc8jzHop21H5chKAfMVQqIM3u05o022+rzrCQomyoVCLaA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem.certspec
new file mode 100644
index 0000000000..43edfff0d9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test End-entity
+extension:subjectAlternativeName:*.example.com
+extension:TLSFeature:OCSPMustStaple
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem
new file mode 100644
index 0000000000..a436415f30
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9TCCAd2gAwIBAgIUL+F6WGk9sZuSuDMbhvPWt8PKv3owDQYJKoZIhvcNAQEL
+BQAwLTErMCkGA1UEAwwiVGVzdCBJbnRlcm1lZGlhdGUgV2l0aCBNdXN0LVN0YXBs
+ZTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAaMRgwFgYDVQQD
+DA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24a
+hvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7t
+FYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+o
+N9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0d
+JdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4
+s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHDAaMBgGA1UdEQQRMA+CDSouZXhhbXBs
+ZS5jb20wDQYJKoZIhvcNAQELBQADggEBALem6GEzJyvZWTVaYg54X2qWTnFfHVyu
+4F/UbGX54D+8hl/zumovWsAtPPfcls94uAMeVvAoRTBP8dUcubilzMwMig7QUPUs
+2YeqeQZ+2+NFHwmMG9SnkSU+CK8lnD4JJClLUuS2rYh5q+tp7c+U7+jFuktqIAQg
+PJijEhiFq0wlqjXg2ytvbdurEm/LAz+wu8sEJtWsWLCZt8QiQfYhX/epFRreO8tB
+Ulu/FCh/1kWx7JAIudigMSQqsg2R7ohgHyOV5Bm9ad2OyTuBjQ8xGFba3Y4jtPvy
+w6mpRAeFEJB7FVTdDQFAyEryoSuOE8MAve2Ld/+9YDddom4S5+ocXo8=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem.certspec
new file mode 100644
index 0000000000..8e4a6ac0c5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/must-staple-missing-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test Intermediate With Must-Staple
+subject:Test End-entity
+extension:subjectAlternativeName:*.example.com
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem b/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem
new file mode 100644
index 0000000000..71459bdd6d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDNTCCAh2gAwIBAgIUc2xqnbHi4aDQ1Rj+6hXoSA+ut+8wDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowLDEqMCgGA1UEAwwhVGVzdCBFbmQtZW50aXR5
+IHdpdGggSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo1swWTAjBgNVHREEHDAagglsb2NhbGhv
+c3SCDSouZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZo
+dHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCig/et41o2
+7hC3LJBIt5yh/eOgjZEUiuJlLAtrMssa5VjdxMDRyqBgn7FUMZMVnIpqRe9V0/a5
+E+yMZp+PvMWxexPlue7SOe7VYaDGvStdJ4ecsEqZzw151k6EuvyT2NJZPj92Mx+0
+gAAo2GysadZEn4E1juLZDCk+fTU1sq8Fek3XocrB/5on3UotDtglBIKW+O8N/vVh
+0Fymnt8owdmt9anGJW9GSLf11b2qZ9XS+5Wlyn3ujDDaeL6+d70mKWo1AuxA7IQx
+ODcsUb6lCGtyyJCLul6oW1ABJNGh1QGAPc3XSeONc2/w7UZcc5IzpmD46V1soJXl
+yLUPGwF1W//L
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem.certspec
new file mode 100644
index 0000000000..ae3a51565a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ocspEEWithIntermediate.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test Intermediate
+subject:Test End-entity with Intermediate
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem b/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem
new file mode 100644
index 0000000000..25036c7c21
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDFDCCAfygAwIBAgIUX1EMg6wBeHSbS/NgLV/M/PaxdWswDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAVMRMwEQYDVQQDDApPdGhlciBDZXJ0MIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo1swWTAjBgNV
+HREEHDAagglsb2NhbGhvc3SCDSouZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAk
+MCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEB
+CwUAA4IBAQCvTyo5F7zhOPBd0LZJpC0ZQAktZtct7z9j0E+iUbo6tohRsvRMmbDs
+lO/cwks/I2sNa3fzHEzE+oC/0kiCS9JUiHUSUrHQrPOraP1urmRdhzECbyFVQsdw
+E9YEtfZemxBQd/pHj7VnYlZ9xNimGZ7rcSi0vif3QbB3BlJiFfAe+IrJ1tkfYOGY
+3xwoCCWOIGj0xl79MXzlG6iI9678y0vUu45tM1JbnauekEjF65CqME035e3eFq/t
+oqghb07JW7JSGmIz0JE+0qli2iT2QIML77Wk0OTE9niDPAbORFPRbNytycNQ8yHk
+xDvo21FgYZFATLiYf4oUADqp23sTIhac
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem.certspec
new file mode 100644
index 0000000000..5756f6ab5f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/ocspOtherEndEntity.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Other Cert
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key
new file mode 100644
index 0000000000..abde350c28
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBdcZSZgmfdwgq
+Z5HxuHbDf1zlOLBsSs0iscvUb6Za2irdQcjCSYrEo7PB9hSH9BtpiUG9gKUcPBIC
+RMWEpMRIMwXlE4wBBs8IvpqGJ2C65qLo828jxdmDE7nfrzeDRdrOUdTW3NKmyzzH
+BuvNMHDsmMzkCqWR1ylaf3HFvmZpHSst/shJRFkLxaPqSf2TsddTQF8Xc3aZlYZm
+JUeX7UJpCIgIEUIgaZiKQ/7kjOaHgd0itqac0oN1Ex+TKxKM4ob6fSUcBirSfvAW
+8YfN1U6DKzW4kw90upCqi8dhZyQqsf1tYhQNGMTAuMaPw3SEVzJK196G5lUvHR4Z
+HXEhaNO7AgMBAAECggEAfj9tfLg572auXX3ZL/VBC7NB3BRyjTkDRXDho3B5DzDw
+aBNV//QeKtTpqdn86/vRJ736uMAK/7Hzzqcyfq1HqhYh8qwe4UygLwSzsnhgF5gL
+GBpEnQOwPmnRErg1ceVUNPASBWV10oMu1nMdznmeN8g/bVHFWrcetYAVrwXhrxXH
+R2A+9/J9A6b/BJ2Wu/hUweTlDvWwWND7CBgOCsf3vo8v8Wc9l/yeVduoOAd7v4p8
+/ylihXeFJpzZ1brStXRp5K/NM8TKLS9pnxHnyPvc1ITwjY77ijy4qXLrJL7Zcu+q
+5LtxIJPkj+lKRutimodQeMQCGposk8mnA5Dp0KVEAQKBgQDmP8clprp2klp/+MtZ
+xPVt1+yD/oW/H1PhHKyagSWLz8CugZB3sPLRR3qvho3mqOy+r3uyKxlvKprYLTKG
+8NDMKd5xnl8r6OUJtyhNWWPt02L5J4h6TEqJeZ00DVGzAax2AasnF5Ak/KrdOL9l
+Iq9j6xZGHsAqfyewb+Cd3afAoQKBgQDXGLH+n4+Z8A6DKuH73G/iqyfzTgScSYAQ
++g63CEhSGCNGCDtclsPu5VksAUpBDGuTCxZcE7XCaqMurG58klqFUcJRNPL0pyxk
+IfGacxSKDt+rpdOmiIs1y6GMAP047lqvC1RXMdcgdhu8ze50SlLKQV6Y5N4Bzf52
+TBlns+jK2wKBgAHlrKJmyUqI0i4TwrkuokcRbGV6B2gXvf0w20s6nTCVuaS2dJZH
+4vhOenhPx4OLCMhZcc96A2+jDjuRw8TQ3yePgMG26FnYRWrbE33vqp8fCsW6yakY
+T9TqJ51yLqYm8WDXiq17yDhFzLKd8RXIP2G3YiuZvUOcYJtXkKY8WVGBAoGBAIDM
+RdENJITuDRKX/Ae/gLO+/0Yeon4fOPNxeJw69mtKDt0hksIneR208cd64ka/NC8x
+hWsPVlgbWKlbETHAxTltsqjDxvOeouM2vCBa5qKgs2hp/KmMu6czzwExmm+bsmt8
+oj0wF/xVHNjaiv3Rf2+i4w00hoeYHNYjTVcekLffAoGAb3fAwfKuesFpVhzKSZxS
+vfvgTN3M29wSrsWoVpHoWUt+4pkI8w57lqpiVLgO1K7sm5k3gr38ebadjVjGiHD6
+S+G8DDUnKIxcgrtK668V7f8RBAP8eOas5qgoJ79C8M+nUeUHZRxWONuTk90j3R9r
+KVFR3kS3f+Vaew3yceGaZcA=
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key.keyspec b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key.keyspec
new file mode 100644
index 0000000000..cbd5f309c0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.key.keyspec
@@ -0,0 +1 @@
+alternate
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem
new file mode 100644
index 0000000000..a2e264030a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAcegAwIBAgIURym6o+VN9xgZXT/QLrvN/nv1ZN4wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNT3RoZXIgdGVzdCBDQTAiGA8yMDE1MDEwMTAwMDAwMFoY
+DzIwMjUwMTAxMDAwMDAwWjAYMRYwFAYDVQQDDA1PdGhlciB0ZXN0IENBMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXXGUmYJn3cIKmeR8bh2w39c5Tiw
+bErNIrHL1G+mWtoq3UHIwkmKxKOzwfYUh/QbaYlBvYClHDwSAkTFhKTESDMF5ROM
+AQbPCL6ahidguuai6PNvI8XZgxO53683g0XazlHU1tzSpss8xwbrzTBw7JjM5Aql
+kdcpWn9xxb5maR0rLf7ISURZC8Wj6kn9k7HXU0BfF3N2mZWGZiVHl+1CaQiICBFC
+IGmYikP+5Izmh4HdIramnNKDdRMfkysSjOKG+n0lHAYq0n7wFvGHzdVOgys1uJMP
+dLqQqovHYWckKrH9bWIUDRjEwLjGj8N0hFcyStfehuZVLx0eGR1xIWjTuwIDAQAB
+ox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC
+AQEAtXplrvls6HSbbibpzfGxOPmSuh2TH05bE4vQk+d7Kz6EOAFvgTiZbLwTxbrQ
+gfrM05t+67C2nAeiwAtW34nUnu6S8MYA6mJjURWICbl7cAvCHuNjg1atVr6f1Y+9
+VFFG6aUibw3bzKneREmDEVcxlEWUaMvv/JjfyMA5veSyX6iTJYkIBrEiVV5Alzg5
+yVHBi6+tpuJDO/YLlG8kmfzkYeJkTyAGx1EJ2yQHim7R232638yb0KrhS4zKsfFU
+egHhM4c+MpiCLc9q2EgblbYGx5GM+2leuzXunj1KPClHFrnmkRRm3rcESG2pK9RN
+/48Nd38VNofRojEbzDSCdOFmow==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem.certspec
new file mode 100644
index 0000000000..3bc975aa22
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/other-test-ca.pem.certspec
@@ -0,0 +1,7 @@
+issuer:Other test CA
+subject:Other test CA
+issuerKey:alternate
+subjectKey:alternate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key
new file mode 100644
index 0000000000..d43495f851
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICcAIBADANBgkqhkiG9w0BAQEFAASCAlowggJWAgEAAoGAANKbsS+4T93NKbOl
+GctmxDuNj4vlRbp5OEzmY+0D33WZFgDrkgeQ0lMM7OVE25mnHwWJaj7SBxZVNKqZ
+BX5HxH47yBrab6HhLjcmi1BGpVJo+drXzLSF2BouGdUNTwtoVKyvbXvmnZoIMTbh
+WvqPU8HIyE/GB3J53Q5V1zaaW90CAwEAAQJ/PEllBwvzkMJR1aLFJ3xbX9C97oXK
+1/4rJ5grsoURSlBwBANq4c+K5Usl5Ns5IVq9fpA/YYwtiy8IzGzRLbzNciBeSUW2
+s984nl5D3goUi7LITiQx/b5ZILBEuycvRez/ByG337YDl/xhOp6jXCIwBTDK6PkV
+nFNN878JEJUZAQJAD58XWXyFuAUbnGmvtV71dsmW29CQR9DM3ludYOpcZ/5PrGe+
+gD9LasWj8FD3a5ZvsU9c8QV2HlrebdlgsYO6VQJADXtjcRLOYaVRaMD5yThvsnmr
+QMug1Ukza7plJ3JjqseCYRosgdm2Nc94xAAYhZ4BjF6QBtEuPS7m80bnn6QzaQJA
+Cf1smj6m6RrjIHD5/BwhD/k1L5e+XR7rlRuzloHp3FtnKlMiIbPYkAyanZm50KTh
+AtxFDKG4ewsTid5lFsCuDQJAAUG4MkkbfdSoMwiSACTHnK5kvUR9+IO7TFZyqWur
+SLcSOzTyYyRFLNzrF/IeVw40fL4v1MLY+ZEOrCy22JW4yQJABFjdau4YyIsvm4Hx
+vDB1riDcH5lz0gck8gsGBD1hR8h4nUoHroi8gshDjIk+AXsTlH9i4LGJWKMetmSx
+nmTT4A==
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key.keyspec b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key.keyspec
new file mode 100644
index 0000000000..21ed73d60b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.key.keyspec
@@ -0,0 +1 @@
+rsa1016
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem
new file mode 100644
index 0000000000..f16060beee
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICazCCAVOgAwIBAgIUYU3JfdG046x00L5zAkjGn4xjoZUwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjA1MTMwMQYDVQQDDCpSU0EgMTAxNiBLZXkgU2l6ZSBUZXN0IERl
+bGVnYXRlZCBSZXNwb25kZXIwgZ4wDQYJKoZIhvcNAQEBBQADgYwAMIGIAoGAANKb
+sS+4T93NKbOlGctmxDuNj4vlRbp5OEzmY+0D33WZFgDrkgeQ0lMM7OVE25mnHwWJ
+aj7SBxZVNKqZBX5HxH47yBrab6HhLjcmi1BGpVJo+drXzLSF2BouGdUNTwtoVKyv
+bXvmnZoIMTbhWvqPU8HIyE/GB3J53Q5V1zaaW90CAwEAAaMXMBUwEwYDVR0lBAww
+CgYIKwYBBQUHAwkwDQYJKoZIhvcNAQELBQADggEBAHkGbdeUVBJCqfgXfmU4Acl1
+ZNunakiEmkZA2oOKvvksDaDg3rKbfZU6WwXhploKPtoWbCIyEkP3BTRJ6SqxNdq0
+S9yE3jR4CmfRy6+4f+cUIexihlI8l8PYC6ccXcZ5gvawCuMZ1YcVlq3LZS1WuNbV
+qBhLURgwzXjpKMIzmsz0e6zIHNBskGX/vXLJjjUJSwnxxdTuCOZ0BUNpz26jMfdl
+fEfYkPIEW0jvGSmg52NM6Uu20JUGM13lxp7WSrBPNqDnnV0Ivpvyiy1O7eOYBDRL
+9LpnghoviPlE04OgvWtiEETNYkDxs606xOGd3eb/1HYLEdJtWdR7F2/KweJBqe8=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem.certspec
new file mode 100644
index 0000000000..05f73368a8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/rsa-1016-keysizeDelegatedSigner.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:RSA 1016 Key Size Test Delegated Responder
+subjectKey:rsa1016
+extension:extKeyUsage:OCSPSigning
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem b/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem
new file mode 100644
index 0000000000..40515addbd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUZekW+kmMLmUyacMUQAExM6vEFpswDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjASMRAwDgYDVQQDDAdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRME
+BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAoabyPFisrr0c
+kmPrDCY8pyDZ6ScxE/ziG+5RG4yJzW0QLrnt6rusMdKuxwaLiTISsNI14vh9QQyG
+XQsVeiGNGIwyJ9k2ZbLvw6pjdUeaswkthSqUDYwYgCljBwViuFkK4SAwya0Vb0p8
+pErDX8pzSF78inMB/7f0+DPdEQgtAGPYfB0gMRWOliyJSVoJXTgJ2B6PTMMvIcIO
+OXcqjDvVYY4o9+YtsDfmzGOSa/YmbM4hO6lv/cO3zv3aT+szyQK8X44koSkvct2P
+QL4cY3/incY0l0I12PaScOJDuvITiRNca7gxbUiT2jz1eqzGPIyVS6ku2cO0v9tP
+TIiD5XDdFg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem.certspec
new file mode 100644
index 0000000000..5d2435d7bb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test CA
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem b/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem
new file mode 100644
index 0000000000..08249b863e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3TCCAcWgAwIBAgIUa0X7/7DlTaedpgrIJg25iBPOkIMwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE1MDEwMTAwMDAwMFoYDzIwMjUw
+MTAxMDAwMDAwWjAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1
+SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+
+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYL
+K7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwc
+bJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibW
+JZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMd
+MBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEB
+AILNZM9yT9ylMpjyi0tXaDORzpHiJ8vEoVKk98bC2BQF0kMEEB547p+Ms8zdJY00
+Bxe9qigT8rQwKprXq5RvgIZ32QLn/yMPiCp/e6zBdsx77TkfmnSnxvPi+0nlA+eM
+8JYN0UST4vWD4vPPX9GgZDVoGQTiF3hUivJ5R8sHb/ozcSukMKQQ22+AIU7w6wyA
+IbCAG7Pab4k2XFAeEnUZsl9fCym5jsPN9Pnv9rlBi6h8shHw1R2ROXjgxubjiMr3
+B456vFTJImLJjyA1iTSlr/+VXGUYg6Z0/HYnsO00+8xUKM71dPxGAfIFNaSscpyk
+rGFLvocT/kym6r8galxCJUo=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem.certspec
new file mode 100644
index 0000000000..33b42c2f41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-int.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate
+validity:20150101-20250101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem b/security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem
new file mode 100644
index 0000000000..63e94388af
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDjCCAfagAwIBAgIUZVYCAllpNYOv9692qhipPYQMx/4wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjA3MTUwMwYDVQQDDCxUZXN0IEludGVybWVkaWF0ZSBXaXRoIE11
+bHRpcGxlIFRMUyBGZWF0dXJlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAab
+bhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmts
+Du0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhI
+H6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8
+rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kX
+Mbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMzMDEwDAYDVR0TBAUwAwEB/zALBgNV
+HQ8EBAMCAQYwFAYIKwYBBQUHARgECDAGAgEFAgEGMA0GCSqGSIb3DQEBCwUAA4IB
+AQAdkLVuX3A0Q9pzWuUV0XADf/nao4Wo5tlpQgsPFNmtrZSeeMJhue7JczJpeuKj
+0b176lb9VSKh8gdU7r1yvEYadl146UZnUvjbKGKQUlU9TLZBdgLnLIs845zRT8+T
+6fQcWzapnhXVt7YSaNCri/iWXyWbOU/PdYjTZejU0lcCEml+t3J7KaKmrZqWZQT/
+CJa7hphC6qEmgE4zoWdmur3nhRFq7JkPGl1JeVEK4M4coec7gTqM/Z1gCkKLmBW9
+fHsYVtf+viYfL6I6xxQtTa7WdgGFKKJyJJ70A6om6TFiD5RHZDZ90wuwtjg1y25x
+4rfoP1xZ9+2So4/8fNiX6PuD
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem.certspec
new file mode 100644
index 0000000000..3f0e925aae
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-multi-tls-feature-int.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate With Multiple TLS Features
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:TLSFeature:OCSPMustStaple,6
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem b/security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem
new file mode 100644
index 0000000000..ecdd4a9261
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDATCCAemgAwIBAgIUV+gXM8WzfHTQR4sK51eSBZrYF9IwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAtMSswKQYDVQQDDCJUZXN0IEludGVybWVkaWF0ZSBXaXRoIE11
+c3QtU3RhcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESO
+FtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVr
+amRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWka
+sdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbY
+VbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6n
+aOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHE
+MdUDrNoYCjXtjQIDAQABozAwLjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAR
+BggrBgEFBQcBGAQFMAMCAQUwDQYJKoZIhvcNAQELBQADggEBAJ3n7RFMk6L0VPZi
+1inbBkR3qLljszxJIkBQiYEV+leqxMXzn3S1CyTD1Pgnc59TCcf2W6upn58duyFN
+NQWj9o5BDGNoq4vs8LZAxs7KivhtDsrGAsr049nVG9VbbpxvCjsEA+oygqquPbhF
+N+775DpAYL/ZDdl/M3/R3XjLgK4FwIu9MDCc45HuYCPTIAp9ASNH65fBfV0be3FM
+2k1/luQygte7VkXKRLxA0/+q498Q3awyWdkFin1/0sTIcMpr2MAuFUMO9j1/BdD+
+2xRUpuHc5sEMHdCNEDZylCLrEMa6tqz9FzUclxAVL7y1irrlGxOfjm4tYzO3P2rr
+bT6iDxk=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem.certspec b/security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem.certspec
new file mode 100644
index 0000000000..7c29aa0ad1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/ocsp_certs/test-must-staple-int.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate With Must-Staple
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:TLSFeature:OCSPMustStaple
diff --git a/security/manager/ssl/tests/unit/pkcs11testmodule/moz.build b/security/manager/ssl/tests/unit/pkcs11testmodule/moz.build
new file mode 100644
index 0000000000..0eef91b076
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pkcs11testmodule/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+FINAL_TARGET = "_tests/xpcshell/security/manager/ssl/tests/unit/pkcs11testmodule"
+
+UNIFIED_SOURCES += [
+ "pkcs11testmodule.cpp",
+]
+
+SharedLibrary("pkcs11testmodule")
+
+# C_GetFunctionList needs to be exported. As it turns out, it's much easier to
+# just export all the symbols.
+NoVisibilityFlags()
+SYMBOLS_FILE = "pkcs11testmodule.symbols"
+
+NO_PGO = True
diff --git a/security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.cpp b/security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.cpp
new file mode 100644
index 0000000000..fcc332bc40
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.cpp
@@ -0,0 +1,577 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+// This is a testing PKCS #11 module that simulates a token being inserted and
+// removed from a slot every 50ms. This is achieved mainly in
+// Test_C_WaitForSlotEvent. If the application that loaded this module calls
+// C_WaitForSlotEvent, this module waits for 50ms and returns, having changed
+// its internal state to report that the token has either been inserted or
+// removed, as appropriate.
+// This module also provides an alternate token that is always present for tests
+// that don't want the cyclic behavior described above.
+
+#include <assert.h>
+#include <string.h>
+
+#if defined(WIN32)
+# include <windows.h> // for Sleep
+#else
+# include <unistd.h> // for usleep
+#endif
+
+#include "pkcs11.h"
+
+CK_RV Test_C_Initialize(CK_VOID_PTR) { return CKR_OK; }
+
+CK_RV Test_C_Finalize(CK_VOID_PTR) { return CKR_OK; }
+
+static const CK_VERSION CryptokiVersion = {2, 2};
+static const CK_VERSION TestLibraryVersion = {0, 0};
+static const char TestLibraryDescription[] = "Test PKCS11 Library";
+static const char TestManufacturerID[] = "Test PKCS11 Manufacturer ID";
+
+/* The dest buffer is one in the CK_INFO or CK_TOKEN_INFO structs.
+ * Those buffers are padded with spaces. DestSize corresponds to the declared
+ * size for those buffers (e.g. 32 for `char foo[32]`).
+ * The src buffer is a string literal. SrcSize includes the string
+ * termination character (e.g. 4 for `const char foo[] = "foo"` */
+template <size_t DestSize, size_t SrcSize>
+void CopyString(unsigned char (&dest)[DestSize], const char (&src)[SrcSize]) {
+ static_assert(DestSize >= SrcSize - 1, "DestSize >= SrcSize - 1");
+ memcpy(dest, src, SrcSize - 1);
+ memset(dest + SrcSize - 1, ' ', DestSize - SrcSize + 1);
+}
+
+CK_RV Test_C_GetInfo(CK_INFO_PTR pInfo) {
+ if (!pInfo) {
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ pInfo->cryptokiVersion = CryptokiVersion;
+ CopyString(pInfo->manufacturerID, TestManufacturerID);
+ pInfo->flags = 0; // must be 0
+ CopyString(pInfo->libraryDescription, TestLibraryDescription);
+ pInfo->libraryVersion = TestLibraryVersion;
+ return CKR_OK;
+}
+
+CK_RV Test_C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR) { return CKR_OK; }
+
+static int tokenPresent = 0;
+
+CK_RV Test_C_GetSlotList(CK_BBOOL limitToTokensPresent,
+ CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) {
+ if (!pulCount) {
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ // We always return slot 2
+ CK_ULONG slotCount = 1;
+ if (!limitToTokensPresent) {
+ // If we want empty slots, we also return slots 1 and 3
+ slotCount += 2;
+ } else if (tokenPresent) {
+ // If we don't want empty slots, but token 1 is present, return that (but
+ // not slot 3)
+ slotCount++;
+ }
+
+ if (pSlotList) {
+ if (*pulCount < slotCount) {
+ return CKR_BUFFER_TOO_SMALL;
+ }
+ // apparently CK_SLOT_IDs are integers [1,N] because
+ // who likes counting from 0 all the time?
+ switch (slotCount) {
+ case 1:
+ pSlotList[0] = 2;
+ break;
+ case 2:
+ if (tokenPresent) {
+ pSlotList[0] = 1;
+ pSlotList[1] = 2;
+ } else {
+ pSlotList[0] = 2;
+ pSlotList[1] = 3;
+ }
+ break;
+ case 3:
+ pSlotList[0] = 1;
+ pSlotList[1] = 2;
+ pSlotList[2] = 3;
+ break;
+ default:
+ assert("Unexpected slot count in Test_C_GetSlotList" == NULL);
+ return CKR_GENERAL_ERROR;
+ }
+ }
+
+ *pulCount = slotCount;
+ return CKR_OK;
+}
+
+static const char TestSlotDescription[] = "Test PKCS11 Slot";
+static const char TestSlot2Description[] = "Test PKCS11 Slot 二";
+static const char TestSlot3Description[] = "Empty PKCS11 Slot";
+
+CK_RV Test_C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) {
+ if (!pInfo) {
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ switch (slotID) {
+ case 1:
+ CopyString(pInfo->slotDescription, TestSlotDescription);
+ pInfo->flags =
+ (tokenPresent ? CKF_TOKEN_PRESENT : 0) | CKF_REMOVABLE_DEVICE;
+ break;
+ case 2:
+ CopyString(pInfo->slotDescription, TestSlot2Description);
+ pInfo->flags = CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE;
+ break;
+ case 3:
+ CopyString(pInfo->slotDescription, TestSlot3Description);
+ pInfo->flags = CKF_REMOVABLE_DEVICE;
+ break;
+ default:
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ CopyString(pInfo->manufacturerID, TestManufacturerID);
+ pInfo->hardwareVersion = TestLibraryVersion;
+ pInfo->firmwareVersion = TestLibraryVersion;
+ return CKR_OK;
+}
+
+// Deliberately include énye to ensure we're handling encoding correctly.
+// The PKCS #11 base specification v2.20 specifies that strings be encoded
+// as UTF-8.
+static const char TestTokenLabel[] = "Test PKCS11 Tokeñ Label";
+static const char TestToken2Label[] = "Test PKCS11 Tokeñ 2 Label";
+static const char TestTokenModel[] = "Test Model";
+
+CK_RV Test_C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) {
+ if (!pInfo) {
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ switch (slotID) {
+ case 1:
+ CopyString(pInfo->label, TestTokenLabel);
+ break;
+ case 2:
+ CopyString(pInfo->label, TestToken2Label);
+ break;
+ default:
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ CopyString(pInfo->manufacturerID, TestManufacturerID);
+ CopyString(pInfo->model, TestTokenModel);
+ memset(pInfo->serialNumber, 0, sizeof(pInfo->serialNumber));
+ pInfo->flags = CKF_TOKEN_INITIALIZED;
+ pInfo->ulMaxSessionCount = 1;
+ pInfo->ulSessionCount = 0;
+ pInfo->ulMaxRwSessionCount = 1;
+ pInfo->ulRwSessionCount = 0;
+ pInfo->ulMaxPinLen = 4;
+ pInfo->ulMinPinLen = 4;
+ pInfo->ulTotalPublicMemory = 1024;
+ pInfo->ulFreePublicMemory = 1024;
+ pInfo->ulTotalPrivateMemory = 1024;
+ pInfo->ulFreePrivateMemory = 1024;
+ pInfo->hardwareVersion = TestLibraryVersion;
+ pInfo->firmwareVersion = TestLibraryVersion;
+ memset(pInfo->utcTime, 0, sizeof(pInfo->utcTime));
+ return CKR_OK;
+}
+
+CK_RV Test_C_GetMechanismList(CK_SLOT_ID, CK_MECHANISM_TYPE_PTR,
+ CK_ULONG_PTR pulCount) {
+ if (!pulCount) {
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ *pulCount = 0;
+ return CKR_OK;
+}
+
+CK_RV Test_C_GetMechanismInfo(CK_SLOT_ID, CK_MECHANISM_TYPE,
+ CK_MECHANISM_INFO_PTR) {
+ return CKR_OK;
+}
+
+CK_RV Test_C_InitToken(CK_SLOT_ID, CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR) {
+ return CKR_OK;
+}
+
+CK_RV Test_C_InitPIN(CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SetPIN(CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG,
+ CK_UTF8CHAR_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS, CK_VOID_PTR, CK_NOTIFY,
+ CK_SESSION_HANDLE_PTR phSession) {
+ switch (slotID) {
+ case 1:
+ *phSession = 1;
+ break;
+ case 2:
+ *phSession = 2;
+ break;
+ default:
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ return CKR_OK;
+}
+
+CK_RV Test_C_CloseSession(CK_SESSION_HANDLE) { return CKR_OK; }
+
+CK_RV Test_C_CloseAllSessions(CK_SLOT_ID) { return CKR_OK; }
+
+CK_RV Test_C_GetSessionInfo(CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo) {
+ if (!pInfo) {
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ switch (hSession) {
+ case 1:
+ pInfo->slotID = 1;
+ break;
+ case 2:
+ pInfo->slotID = 2;
+ break;
+ default:
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ pInfo->state = CKS_RO_PUBLIC_SESSION;
+ pInfo->flags = CKF_SERIAL_SESSION;
+ return CKR_OK;
+}
+
+CK_RV Test_C_GetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_OBJECT_HANDLE, CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_Login(CK_SESSION_HANDLE, CK_USER_TYPE, CK_UTF8CHAR_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_Logout(CK_SESSION_HANDLE) { return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV Test_C_CreateObject(CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG,
+ CK_OBJECT_HANDLE_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_CopyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR,
+ CK_ULONG, CK_OBJECT_HANDLE_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DestroyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_GetObjectSize(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_GetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE,
+ CK_ATTRIBUTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE,
+ CK_ATTRIBUTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_FindObjectsInit(CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG) {
+ return CKR_OK;
+}
+
+CK_RV Test_C_FindObjects(CK_SESSION_HANDLE, CK_OBJECT_HANDLE_PTR, CK_ULONG,
+ CK_ULONG_PTR pulObjectCount) {
+ *pulObjectCount = 0;
+ return CKR_OK;
+}
+
+CK_RV Test_C_FindObjectsFinal(CK_SESSION_HANDLE) { return CKR_OK; }
+
+CK_RV Test_C_EncryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
+ CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_Encrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
+ CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_EncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_EncryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DecryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
+ CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_Decrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
+ CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DecryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DecryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DigestInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_Digest(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
+ CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DigestKey(CK_SESSION_HANDLE, CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DigestFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SignInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_Sign(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
+ CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SignUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SignFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SignRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
+ CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SignRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
+ CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_VerifyInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_Verify(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
+ CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_VerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_VerifyFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_VerifyRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
+ CK_OBJECT_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_VerifyRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DigestEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DecryptDigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SignEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DecryptVerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
+ CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_GenerateKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR,
+ CK_ULONG, CK_OBJECT_HANDLE_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_GenerateKeyPair(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
+ CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR,
+ CK_ULONG, CK_OBJECT_HANDLE_PTR,
+ CK_OBJECT_HANDLE_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_WrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
+ CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_UnwrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
+ CK_BYTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG,
+ CK_OBJECT_HANDLE_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_DeriveKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
+ CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_SeedRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_GenerateRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_GetFunctionStatus(CK_SESSION_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_CancelFunction(CK_SESSION_HANDLE) {
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV Test_C_WaitForSlotEvent(CK_FLAGS, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR) {
+#ifdef WIN32
+ Sleep(50); // Sleep takes the duration argument as milliseconds
+#else
+ usleep(50000); // usleep takes the duration argument as microseconds
+#endif
+ *pSlot = 1;
+ tokenPresent = !tokenPresent;
+ return CKR_OK;
+}
+
+static CK_FUNCTION_LIST FunctionList = {{2, 2},
+ Test_C_Initialize,
+ Test_C_Finalize,
+ Test_C_GetInfo,
+ Test_C_GetFunctionList,
+ Test_C_GetSlotList,
+ Test_C_GetSlotInfo,
+ Test_C_GetTokenInfo,
+ Test_C_GetMechanismList,
+ Test_C_GetMechanismInfo,
+ Test_C_InitToken,
+ Test_C_InitPIN,
+ Test_C_SetPIN,
+ Test_C_OpenSession,
+ Test_C_CloseSession,
+ Test_C_CloseAllSessions,
+ Test_C_GetSessionInfo,
+ Test_C_GetOperationState,
+ Test_C_SetOperationState,
+ Test_C_Login,
+ Test_C_Logout,
+ Test_C_CreateObject,
+ Test_C_CopyObject,
+ Test_C_DestroyObject,
+ Test_C_GetObjectSize,
+ Test_C_GetAttributeValue,
+ Test_C_SetAttributeValue,
+ Test_C_FindObjectsInit,
+ Test_C_FindObjects,
+ Test_C_FindObjectsFinal,
+ Test_C_EncryptInit,
+ Test_C_Encrypt,
+ Test_C_EncryptUpdate,
+ Test_C_EncryptFinal,
+ Test_C_DecryptInit,
+ Test_C_Decrypt,
+ Test_C_DecryptUpdate,
+ Test_C_DecryptFinal,
+ Test_C_DigestInit,
+ Test_C_Digest,
+ Test_C_DigestUpdate,
+ Test_C_DigestKey,
+ Test_C_DigestFinal,
+ Test_C_SignInit,
+ Test_C_Sign,
+ Test_C_SignUpdate,
+ Test_C_SignFinal,
+ Test_C_SignRecoverInit,
+ Test_C_SignRecover,
+ Test_C_VerifyInit,
+ Test_C_Verify,
+ Test_C_VerifyUpdate,
+ Test_C_VerifyFinal,
+ Test_C_VerifyRecoverInit,
+ Test_C_VerifyRecover,
+ Test_C_DigestEncryptUpdate,
+ Test_C_DecryptDigestUpdate,
+ Test_C_SignEncryptUpdate,
+ Test_C_DecryptVerifyUpdate,
+ Test_C_GenerateKey,
+ Test_C_GenerateKeyPair,
+ Test_C_WrapKey,
+ Test_C_UnwrapKey,
+ Test_C_DeriveKey,
+ Test_C_SeedRandom,
+ Test_C_GenerateRandom,
+ Test_C_GetFunctionStatus,
+ Test_C_CancelFunction,
+ Test_C_WaitForSlotEvent};
+
+CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
+ *ppFunctionList = &FunctionList;
+ return CKR_OK;
+}
diff --git a/security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.symbols b/security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.symbols
new file mode 100644
index 0000000000..562ecea21d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pkcs11testmodule/pkcs11testmodule.symbols
@@ -0,0 +1 @@
+C_GetFunctionList
diff --git a/security/manager/ssl/tests/unit/pycert.py b/security/manager/ssl/tests/unit/pycert.py
new file mode 100755
index 0000000000..627f7b6ad1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -0,0 +1,815 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Reads a certificate specification from stdin or a file and outputs a
+signed x509 certificate with the desired properties.
+
+The input format is as follows:
+
+issuer:<issuer distinguished name specification>
+subject:<subject distinguished name specification>
+[version:{1,2,3,4}]
+[validity:<YYYYMMDD-YYYYMMDD|duration in days>]
+[issuerKey:<key specification>]
+[subjectKey:<key specification>]
+[signature:{sha256WithRSAEncryption,sha1WithRSAEncryption,
+ md5WithRSAEncryption,ecdsaWithSHA256,ecdsaWithSHA384,
+ ecdsaWithSHA512}]
+[serialNumber:<integer in the interval [1, 127]>]
+[extension:<extension name:<extension-specific data>>]
+[...]
+
+Known extensions are:
+basicConstraints:[cA],[pathLenConstraint]
+keyUsage:[digitalSignature,nonRepudiation,keyEncipherment,
+ dataEncipherment,keyAgreement,keyCertSign,cRLSign]
+extKeyUsage:[serverAuth,clientAuth,codeSigning,emailProtection
+ nsSGC, # Netscape Server Gated Crypto
+ OCSPSigning,timeStamping]
+subjectAlternativeName:[<dNSName|directoryName|"ip4:"iPV4Address>,...]
+authorityInformationAccess:<OCSP URI>
+certificatePolicies:[<policy OID>,...]
+nameConstraints:{permitted,excluded}:[<dNSName|directoryName>,...]
+nsCertType:sslServer
+TLSFeature:[<TLSFeature>,...]
+embeddedSCTList:[<key specification>:<YYYYMMDD>,...]
+delegationUsage:
+
+Where:
+ [] indicates an optional field or component of a field
+ <> indicates a required component of a field
+ {} indicates a choice of exactly one value among a set of values
+ [a,b,c] indicates a list of potential values, of which zero or more
+ may be used
+
+For instance, the version field is optional. However, if it is
+specified, it must have exactly one value from the set {1,2,3,4}.
+
+Most fields have reasonable default values. By default one shared RSA
+key is used for all signatures and subject public key information
+fields. Using "issuerKey:<key specification>" or
+"subjectKey:<key specification>" causes a different key be used for
+signing or as the subject public key information field, respectively.
+See pykey.py for the list of available specifications.
+The signature algorithm is sha256WithRSAEncryption by default.
+
+The validity period may be specified as either concrete notBefore and
+notAfter values or as a validity period centered around 'now'. For the
+latter, this will result in a notBefore of 'now' - duration/2 and a
+notAfter of 'now' + duration/2.
+
+Issuer and subject distinguished name specifications are of the form
+'[stringEncoding]/C=XX/O=Example/CN=example.com'. C (country name), ST
+(state or province name), L (locality name), O (organization name), OU
+(organizational unit name), CN (common name) and emailAddress (email
+address) are currently supported. The optional stringEncoding field may
+be 'utf8String' or 'printableString'. If the given string does not
+contain a '/', it is assumed to represent a common name. If an empty
+string is provided, then an empty distinguished name is returned.
+DirectoryNames also use this format. When specifying a directoryName in
+a nameConstraints extension, the implicit form may not be used.
+
+If an extension name has '[critical]' after it, it will be marked as
+critical. Otherwise (by default), it will not be marked as critical.
+
+TLSFeature values can either consist of a named value (currently only
+'OCSPMustStaple' which corresponds to status_request) or a numeric TLS
+feature value (see rfc7633 for more information).
+
+If a serial number is not explicitly specified, it is automatically
+generated based on the contents of the certificate.
+"""
+
+from pyasn1.codec.der import decoder
+from pyasn1.codec.der import encoder
+from pyasn1.type import constraint, tag, univ, useful
+from pyasn1_modules import rfc2459
+from struct import pack
+import base64
+import datetime
+import hashlib
+import re
+import socket
+import six
+import sys
+
+import pyct
+import pykey
+
+
+class Error(Exception):
+ """Base class for exceptions in this module."""
+
+ pass
+
+
+class UnknownBaseError(Error):
+ """Base class for handling unexpected input in this module."""
+
+ def __init__(self, value):
+ super(UnknownBaseError, self).__init__()
+ self.value = value
+ self.category = "input"
+
+ def __str__(self):
+ return 'Unknown %s type "%s"' % (self.category, repr(self.value))
+
+
+class UnknownAlgorithmTypeError(UnknownBaseError):
+ """Helper exception type to handle unknown algorithm types."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "algorithm"
+
+
+class UnknownParameterTypeError(UnknownBaseError):
+ """Helper exception type to handle unknown input parameters."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "parameter"
+
+
+class UnknownExtensionTypeError(UnknownBaseError):
+ """Helper exception type to handle unknown input extensions."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "extension"
+
+
+class UnknownKeyPurposeTypeError(UnknownBaseError):
+ """Helper exception type to handle unknown key purposes."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "keyPurpose"
+
+
+class UnknownKeyTargetError(UnknownBaseError):
+ """Helper exception type to handle unknown key targets."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "key target"
+
+
+class UnknownVersionError(UnknownBaseError):
+ """Helper exception type to handle unknown specified versions."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "version"
+
+
+class UnknownNameConstraintsSpecificationError(UnknownBaseError):
+ """Helper exception type to handle unknown specified
+ nameConstraints."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "nameConstraints specification"
+
+
+class UnknownDNTypeError(UnknownBaseError):
+ """Helper exception type to handle unknown DN types."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "DN"
+
+
+class UnknownNSCertTypeError(UnknownBaseError):
+ """Helper exception type to handle unknown nsCertType types."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "nsCertType"
+
+
+class UnknownTLSFeature(UnknownBaseError):
+ """Helper exception type to handle unknown TLS Features."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "TLSFeature"
+
+
+class UnknownDelegatedCredentialError(UnknownBaseError):
+ """Helper exception type to handle unknown Delegated Credential args."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "delegatedCredential"
+
+
+class InvalidSCTSpecification(Error):
+ """Helper exception type to handle invalid SCT specifications."""
+
+ def __init__(self, value):
+ super(InvalidSCTSpecification, self).__init__()
+ self.value = value
+
+ def __str__(self):
+ return repr('invalid SCT specification "{}"' % self.value)
+
+
+class InvalidSerialNumber(Error):
+ """Exception type to handle invalid serial numbers."""
+
+ def __init__(self, value):
+ super(InvalidSerialNumber, self).__init__()
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+
+def getASN1Tag(asn1Type):
+ """Helper function for returning the base tag value of a given
+ type from the pyasn1 package"""
+ return asn1Type.tagSet.baseTag.tagId
+
+
+def stringToAccessDescription(string):
+ """Helper function that takes a string representing a URI
+ presumably identifying an OCSP authority information access
+ location. Returns an AccessDescription usable by pyasn1."""
+ accessMethod = rfc2459.id_ad_ocsp
+ accessLocation = rfc2459.GeneralName()
+ accessLocation["uniformResourceIdentifier"] = string
+ sequence = univ.Sequence()
+ sequence.setComponentByPosition(0, accessMethod)
+ sequence.setComponentByPosition(1, accessLocation)
+ return sequence
+
+
+def stringToDN(string, tag=None):
+ """Takes a string representing a distinguished name or directory
+ name and returns a Name for use by pyasn1. See the documentation
+ for the issuer and subject fields for more details. Takes an
+ optional implicit tag in cases where the Name needs to be tagged
+ differently."""
+ if string and "/" not in string:
+ string = "/CN=%s" % string
+ rdns = rfc2459.RDNSequence()
+ pattern = "/(C|ST|L|O|OU|CN|emailAddress)="
+ split = re.split(pattern, string)
+ # split should now be [[encoding], <type>, <value>, <type>, <value>, ...]
+ if split[0]:
+ encoding = split[0]
+ else:
+ encoding = "utf8String"
+ for pos, (nameType, value) in enumerate(zip(split[1::2], split[2::2])):
+ ava = rfc2459.AttributeTypeAndValue()
+ if nameType == "C":
+ ava["type"] = rfc2459.id_at_countryName
+ nameComponent = rfc2459.X520countryName(value)
+ elif nameType == "ST":
+ ava["type"] = rfc2459.id_at_stateOrProvinceName
+ nameComponent = rfc2459.X520StateOrProvinceName()
+ elif nameType == "L":
+ ava["type"] = rfc2459.id_at_localityName
+ nameComponent = rfc2459.X520LocalityName()
+ elif nameType == "O":
+ ava["type"] = rfc2459.id_at_organizationName
+ nameComponent = rfc2459.X520OrganizationName()
+ elif nameType == "OU":
+ ava["type"] = rfc2459.id_at_organizationalUnitName
+ nameComponent = rfc2459.X520OrganizationalUnitName()
+ elif nameType == "CN":
+ ava["type"] = rfc2459.id_at_commonName
+ nameComponent = rfc2459.X520CommonName()
+ elif nameType == "emailAddress":
+ ava["type"] = rfc2459.emailAddress
+ nameComponent = rfc2459.Pkcs9email(value)
+ else:
+ raise UnknownDNTypeError(nameType)
+ if not nameType == "C" and not nameType == "emailAddress":
+ # The value may have things like '\0' (i.e. a slash followed by
+ # the number zero) that have to be decoded into the resulting
+ # '\x00' (i.e. a byte with value zero).
+ nameComponent[encoding] = six.ensure_binary(value).decode(
+ encoding="unicode_escape"
+ )
+ ava["value"] = nameComponent
+ rdn = rfc2459.RelativeDistinguishedName()
+ rdn.setComponentByPosition(0, ava)
+ rdns.setComponentByPosition(pos, rdn)
+ if tag:
+ name = rfc2459.Name().subtype(implicitTag=tag)
+ else:
+ name = rfc2459.Name()
+ name.setComponentByPosition(0, rdns)
+ return name
+
+
+def stringToAlgorithmIdentifiers(string):
+ """Helper function that converts a description of an algorithm
+ to a representation usable by the pyasn1 package and a hash
+ algorithm constant for use by pykey."""
+ algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+ algorithmType = None
+ algorithm = None
+ # We add Null parameters for RSA only
+ addParameters = False
+ if string == "sha1WithRSAEncryption":
+ algorithmType = pykey.HASH_SHA1
+ algorithm = rfc2459.sha1WithRSAEncryption
+ addParameters = True
+ elif string == "sha256WithRSAEncryption":
+ algorithmType = pykey.HASH_SHA256
+ algorithm = univ.ObjectIdentifier("1.2.840.113549.1.1.11")
+ addParameters = True
+ elif string == "md5WithRSAEncryption":
+ algorithmType = pykey.HASH_MD5
+ algorithm = rfc2459.md5WithRSAEncryption
+ addParameters = True
+ elif string == "ecdsaWithSHA256":
+ algorithmType = pykey.HASH_SHA256
+ algorithm = univ.ObjectIdentifier("1.2.840.10045.4.3.2")
+ elif string == "ecdsaWithSHA384":
+ algorithmType = pykey.HASH_SHA384
+ algorithm = univ.ObjectIdentifier("1.2.840.10045.4.3.3")
+ elif string == "ecdsaWithSHA512":
+ algorithmType = pykey.HASH_SHA512
+ algorithm = univ.ObjectIdentifier("1.2.840.10045.4.3.4")
+ else:
+ raise UnknownAlgorithmTypeError(string)
+ algorithmIdentifier["algorithm"] = algorithm
+ if addParameters:
+ # Directly setting parameters to univ.Null doesn't currently work.
+ nullEncapsulated = encoder.encode(univ.Null())
+ algorithmIdentifier["parameters"] = univ.Any(nullEncapsulated)
+ return (algorithmIdentifier, algorithmType)
+
+
+def datetimeToTime(dt):
+ """Takes a datetime object and returns an rfc2459.Time object with
+ that time as its value as a GeneralizedTime"""
+ time = rfc2459.Time()
+ time["generalTime"] = useful.GeneralizedTime(dt.strftime("%Y%m%d%H%M%SZ"))
+ return time
+
+
+def serialBytesToString(serialBytes):
+ """Takes a list of integers in the interval [0, 255] and returns
+ the corresponding serial number string."""
+ serialBytesLen = len(serialBytes)
+ if serialBytesLen > 127:
+ raise InvalidSerialNumber("{} bytes is too long".format(serialBytesLen))
+ # Prepend the ASN.1 INTEGER tag and length bytes.
+ stringBytes = [getASN1Tag(univ.Integer), serialBytesLen] + serialBytes
+ return bytes(stringBytes)
+
+
+class Certificate(object):
+ """Utility class for reading a certificate specification and
+ generating a signed x509 certificate"""
+
+ def __init__(self, paramStream):
+ self.versionValue = 2 # a value of 2 is X509v3
+ self.signature = "sha256WithRSAEncryption"
+ self.issuer = "Default Issuer"
+ actualNow = datetime.datetime.utcnow()
+ self.now = datetime.datetime.strptime(str(actualNow.year), "%Y")
+ aYearAndAWhile = datetime.timedelta(days=400)
+ self.notBefore = self.now - aYearAndAWhile
+ self.notAfter = self.now + aYearAndAWhile
+ self.subject = "Default Subject"
+ self.extensions = None
+ # The serial number can be automatically generated from the
+ # certificate specification. We need this value to depend in
+ # part of what extensions are present. self.extensions are
+ # pyasn1 objects. Depending on the string representation of
+ # these objects can cause the resulting serial number to change
+ # unexpectedly, so instead we depend on the original string
+ # representation of the extensions as specified.
+ self.extensionLines = None
+ self.savedEmbeddedSCTListData = None
+ self.subjectKey = pykey.keyFromSpecification("default")
+ self.issuerKey = pykey.keyFromSpecification("default")
+ self.serialNumber = None
+ self.decodeParams(paramStream)
+ # If a serial number wasn't specified, generate one based on
+ # the certificate contents.
+ if not self.serialNumber:
+ self.serialNumber = self.generateSerialNumber()
+ # This has to be last because the SCT signature depends on the
+ # contents of the certificate.
+ if self.savedEmbeddedSCTListData:
+ self.addEmbeddedSCTListData()
+
+ def generateSerialNumber(self):
+ """Generates a serial number for this certificate based on its
+ contents. Intended to be reproducible for compatibility with
+ the build system on OS X (see the comment above main, later in
+ this file)."""
+ hasher = hashlib.sha256()
+ hasher.update(six.ensure_binary(str(self.versionValue)))
+ hasher.update(six.ensure_binary(self.signature))
+ hasher.update(six.ensure_binary(self.issuer))
+ hasher.update(six.ensure_binary(str(self.notBefore)))
+ hasher.update(six.ensure_binary(str(self.notAfter)))
+ hasher.update(six.ensure_binary(self.subject))
+ if self.extensionLines:
+ for extensionLine in self.extensionLines:
+ hasher.update(six.ensure_binary(extensionLine))
+ if self.savedEmbeddedSCTListData:
+ # savedEmbeddedSCTListData is
+ # (embeddedSCTListSpecification, critical), where |critical|
+ # may be None
+ hasher.update(six.ensure_binary(self.savedEmbeddedSCTListData[0]))
+ if self.savedEmbeddedSCTListData[1]:
+ hasher.update(six.ensure_binary(self.savedEmbeddedSCTListData[1]))
+ serialBytes = [c for c in hasher.digest()[:20]]
+ # Ensure that the most significant bit isn't set (which would
+ # indicate a negative number, which isn't valid for serial
+ # numbers).
+ serialBytes[0] &= 0x7F
+ # Also ensure that the least significant bit on the most
+ # significant byte is set (to prevent a leading zero byte,
+ # which also wouldn't be valid).
+ serialBytes[0] |= 0x01
+ return serialBytesToString(serialBytes)
+
+ def decodeParams(self, paramStream):
+ for line in paramStream.readlines():
+ self.decodeParam(line.strip())
+
+ def decodeParam(self, line):
+ param = line.split(":")[0]
+ value = ":".join(line.split(":")[1:])
+ if param == "version":
+ self.setVersion(value)
+ elif param == "subject":
+ self.subject = value
+ elif param == "issuer":
+ self.issuer = value
+ elif param == "validity":
+ self.decodeValidity(value)
+ elif param == "extension":
+ self.decodeExtension(value)
+ elif param == "issuerKey":
+ self.setupKey("issuer", value)
+ elif param == "subjectKey":
+ self.setupKey("subject", value)
+ elif param == "signature":
+ self.signature = value
+ elif param == "serialNumber":
+ serialNumber = int(value)
+ # Ensure only serial numbers that conform to the rules listed in
+ # generateSerialNumber() are permitted.
+ if serialNumber < 1 or serialNumber > 127:
+ raise InvalidSerialNumber(value)
+ self.serialNumber = serialBytesToString([serialNumber])
+ else:
+ raise UnknownParameterTypeError(param)
+
+ def setVersion(self, version):
+ intVersion = int(version)
+ if intVersion >= 1 and intVersion <= 4:
+ self.versionValue = intVersion - 1
+ else:
+ raise UnknownVersionError(version)
+
+ def decodeValidity(self, duration):
+ match = re.search("([0-9]{8})-([0-9]{8})", duration)
+ if match:
+ self.notBefore = datetime.datetime.strptime(match.group(1), "%Y%m%d")
+ self.notAfter = datetime.datetime.strptime(match.group(2), "%Y%m%d")
+ else:
+ delta = datetime.timedelta(days=(int(duration) / 2))
+ self.notBefore = self.now - delta
+ self.notAfter = self.now + delta
+
+ def decodeExtension(self, extension):
+ match = re.search(r"([a-zA-Z]+)(\[critical\])?:(.*)", extension)
+ if not match:
+ raise UnknownExtensionTypeError(extension)
+ extensionType = match.group(1)
+ critical = match.group(2)
+ value = match.group(3)
+ if extensionType == "basicConstraints":
+ self.addBasicConstraints(value, critical)
+ elif extensionType == "keyUsage":
+ self.addKeyUsage(value, critical)
+ elif extensionType == "extKeyUsage":
+ self.addExtKeyUsage(value, critical)
+ elif extensionType == "subjectAlternativeName":
+ self.addSubjectAlternativeName(value, critical)
+ elif extensionType == "authorityInformationAccess":
+ self.addAuthorityInformationAccess(value, critical)
+ elif extensionType == "certificatePolicies":
+ self.addCertificatePolicies(value, critical)
+ elif extensionType == "nameConstraints":
+ self.addNameConstraints(value, critical)
+ elif extensionType == "nsCertType":
+ self.addNSCertType(value, critical)
+ elif extensionType == "TLSFeature":
+ self.addTLSFeature(value, critical)
+ elif extensionType == "embeddedSCTList":
+ self.savedEmbeddedSCTListData = (value, critical)
+ elif extensionType == "delegationUsage":
+ self.addDelegationUsage(critical)
+ else:
+ raise UnknownExtensionTypeError(extensionType)
+
+ if extensionType != "embeddedSCTList":
+ if not self.extensionLines:
+ self.extensionLines = []
+ self.extensionLines.append(extension)
+
+ def setupKey(self, subjectOrIssuer, value):
+ if subjectOrIssuer == "subject":
+ self.subjectKey = pykey.keyFromSpecification(value)
+ elif subjectOrIssuer == "issuer":
+ self.issuerKey = pykey.keyFromSpecification(value)
+ else:
+ raise UnknownKeyTargetError(subjectOrIssuer)
+
+ def addExtension(self, extensionType, extensionValue, critical):
+ if not self.extensions:
+ self.extensions = []
+ encapsulated = univ.OctetString(encoder.encode(extensionValue))
+ extension = rfc2459.Extension()
+ extension["extnID"] = extensionType
+ # critical is either the string '[critical]' or None.
+ # We only care whether or not it is truthy.
+ if critical:
+ extension["critical"] = True
+ extension["extnValue"] = encapsulated
+ self.extensions.append(extension)
+
+ def addBasicConstraints(self, basicConstraints, critical):
+ cA = basicConstraints.split(",")[0]
+ pathLenConstraint = basicConstraints.split(",")[1]
+ basicConstraintsExtension = rfc2459.BasicConstraints()
+ basicConstraintsExtension["cA"] = cA == "cA"
+ if pathLenConstraint:
+ pathLenConstraintValue = univ.Integer(int(pathLenConstraint)).subtype(
+ subtypeSpec=constraint.ValueRangeConstraint(0, float("inf"))
+ )
+ basicConstraintsExtension["pathLenConstraint"] = pathLenConstraintValue
+ self.addExtension(
+ rfc2459.id_ce_basicConstraints, basicConstraintsExtension, critical
+ )
+
+ def addKeyUsage(self, keyUsage, critical):
+ keyUsageExtension = rfc2459.KeyUsage(keyUsage)
+ self.addExtension(rfc2459.id_ce_keyUsage, keyUsageExtension, critical)
+
+ def keyPurposeToOID(self, keyPurpose):
+ if keyPurpose == "serverAuth":
+ return rfc2459.id_kp_serverAuth
+ if keyPurpose == "clientAuth":
+ return rfc2459.id_kp_clientAuth
+ if keyPurpose == "codeSigning":
+ return rfc2459.id_kp_codeSigning
+ if keyPurpose == "emailProtection":
+ return rfc2459.id_kp_emailProtection
+ if keyPurpose == "nsSGC":
+ return univ.ObjectIdentifier("2.16.840.1.113730.4.1")
+ if keyPurpose == "OCSPSigning":
+ return univ.ObjectIdentifier("1.3.6.1.5.5.7.3.9")
+ if keyPurpose == "timeStamping":
+ return rfc2459.id_kp_timeStamping
+ raise UnknownKeyPurposeTypeError(keyPurpose)
+
+ def addExtKeyUsage(self, extKeyUsage, critical):
+ extKeyUsageExtension = rfc2459.ExtKeyUsageSyntax()
+ for count, keyPurpose in enumerate(extKeyUsage.split(",")):
+ extKeyUsageExtension.setComponentByPosition(
+ count, self.keyPurposeToOID(keyPurpose)
+ )
+ self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension, critical)
+
+ def addSubjectAlternativeName(self, names, critical):
+ IPV4_PREFIX = "ip4:"
+
+ subjectAlternativeName = rfc2459.SubjectAltName()
+ for count, name in enumerate(names.split(",")):
+ generalName = rfc2459.GeneralName()
+ if "/" in name:
+ directoryName = stringToDN(
+ name, tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+ )
+ generalName["directoryName"] = directoryName
+ elif "@" in name:
+ generalName["rfc822Name"] = name
+ elif name.startswith(IPV4_PREFIX):
+ generalName["iPAddress"] = socket.inet_pton(
+ socket.AF_INET, name[len(IPV4_PREFIX) :]
+ )
+ else:
+ # The string may have things like '\0' (i.e. a slash
+ # followed by the number zero) that have to be decoded into
+ # the resulting '\x00' (i.e. a byte with value zero).
+ generalName["dNSName"] = six.ensure_binary(name).decode(
+ "unicode_escape"
+ )
+ subjectAlternativeName.setComponentByPosition(count, generalName)
+ self.addExtension(
+ rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical
+ )
+
+ def addAuthorityInformationAccess(self, ocspURI, critical):
+ sequence = univ.Sequence()
+ accessDescription = stringToAccessDescription(ocspURI)
+ sequence.setComponentByPosition(0, accessDescription)
+ self.addExtension(rfc2459.id_pe_authorityInfoAccess, sequence, critical)
+
+ def addCertificatePolicies(self, policyOIDs, critical):
+ policies = rfc2459.CertificatePolicies()
+ for pos, policyOID in enumerate(policyOIDs.split(",")):
+ if policyOID == "any":
+ policyOID = "2.5.29.32.0"
+ policy = rfc2459.PolicyInformation()
+ policyIdentifier = rfc2459.CertPolicyId(policyOID)
+ policy["policyIdentifier"] = policyIdentifier
+ policies.setComponentByPosition(pos, policy)
+ self.addExtension(rfc2459.id_ce_certificatePolicies, policies, critical)
+
+ def addNameConstraints(self, constraints, critical):
+ nameConstraints = rfc2459.NameConstraints()
+ if constraints.startswith("permitted:"):
+ (subtreesType, subtreesTag) = ("permittedSubtrees", 0)
+ elif constraints.startswith("excluded:"):
+ (subtreesType, subtreesTag) = ("excludedSubtrees", 1)
+ else:
+ raise UnknownNameConstraintsSpecificationError(constraints)
+ generalSubtrees = rfc2459.GeneralSubtrees().subtype(
+ implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, subtreesTag
+ )
+ )
+ subtrees = constraints[(constraints.find(":") + 1) :]
+ for pos, name in enumerate(subtrees.split(",")):
+ generalName = rfc2459.GeneralName()
+ if "/" in name:
+ directoryName = stringToDN(
+ name, tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)
+ )
+ generalName["directoryName"] = directoryName
+ else:
+ generalName["dNSName"] = name
+ generalSubtree = rfc2459.GeneralSubtree()
+ generalSubtree["base"] = generalName
+ generalSubtrees.setComponentByPosition(pos, generalSubtree)
+ nameConstraints[subtreesType] = generalSubtrees
+ self.addExtension(rfc2459.id_ce_nameConstraints, nameConstraints, critical)
+
+ def addNSCertType(self, certType, critical):
+ if certType != "sslServer":
+ raise UnknownNSCertTypeError(certType)
+ self.addExtension(
+ univ.ObjectIdentifier("2.16.840.1.113730.1.1"),
+ univ.BitString("'01'B"),
+ critical,
+ )
+
+ def addDelegationUsage(self, critical):
+ if critical:
+ raise UnknownDelegatedCredentialError(critical)
+ self.addExtension(
+ univ.ObjectIdentifier("1.3.6.1.4.1.44363.44"), univ.Null(), critical
+ )
+
+ def addTLSFeature(self, features, critical):
+ namedFeatures = {"OCSPMustStaple": 5}
+ featureList = [f.strip() for f in features.split(",")]
+ sequence = univ.Sequence()
+ for pos, feature in enumerate(featureList):
+ featureValue = 0
+ try:
+ featureValue = int(feature)
+ except ValueError:
+ try:
+ featureValue = namedFeatures[feature]
+ except Exception:
+ raise UnknownTLSFeature(feature)
+ sequence.setComponentByPosition(pos, univ.Integer(featureValue))
+ self.addExtension(
+ univ.ObjectIdentifier("1.3.6.1.5.5.7.1.24"), sequence, critical
+ )
+
+ def addEmbeddedSCTListData(self):
+ (scts, critical) = self.savedEmbeddedSCTListData
+ encodedSCTs = []
+ for sctSpec in scts.split(","):
+ match = re.search("(\w+):(\d{8})", sctSpec)
+ if not match:
+ raise InvalidSCTSpecification(sctSpec)
+ keySpec = match.group(1)
+ key = pykey.keyFromSpecification(keySpec)
+ time = datetime.datetime.strptime(match.group(2), "%Y%m%d")
+ tbsCertificate = self.getTBSCertificate()
+ tbsDER = encoder.encode(tbsCertificate)
+ sct = pyct.SCT(key, time, tbsDER, self.issuerKey)
+ signed = sct.signAndEncode()
+ lengthPrefix = pack("!H", len(signed))
+ encodedSCTs.append(lengthPrefix + signed)
+ encodedSCTBytes = b"".join(encodedSCTs)
+ lengthPrefix = pack("!H", len(encodedSCTBytes))
+ extensionBytes = lengthPrefix + encodedSCTBytes
+ self.addExtension(
+ univ.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2"),
+ univ.OctetString(extensionBytes),
+ critical,
+ )
+
+ def getVersion(self):
+ return rfc2459.Version(self.versionValue).subtype(
+ explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)
+ )
+
+ def getSerialNumber(self):
+ return decoder.decode(self.serialNumber)[0]
+
+ def getIssuer(self):
+ return stringToDN(self.issuer)
+
+ def getValidity(self):
+ validity = rfc2459.Validity()
+ validity["notBefore"] = self.getNotBefore()
+ validity["notAfter"] = self.getNotAfter()
+ return validity
+
+ def getNotBefore(self):
+ return datetimeToTime(self.notBefore)
+
+ def getNotAfter(self):
+ return datetimeToTime(self.notAfter)
+
+ def getSubject(self):
+ return stringToDN(self.subject)
+
+ def getTBSCertificate(self):
+ (signatureOID, _) = stringToAlgorithmIdentifiers(self.signature)
+ tbsCertificate = rfc2459.TBSCertificate()
+ tbsCertificate["version"] = self.getVersion()
+ tbsCertificate["serialNumber"] = self.getSerialNumber()
+ tbsCertificate["signature"] = signatureOID
+ tbsCertificate["issuer"] = self.getIssuer()
+ tbsCertificate["validity"] = self.getValidity()
+ tbsCertificate["subject"] = self.getSubject()
+ tbsCertificate[
+ "subjectPublicKeyInfo"
+ ] = self.subjectKey.asSubjectPublicKeyInfo()
+ if self.extensions:
+ extensions = rfc2459.Extensions().subtype(
+ explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3)
+ )
+ for count, extension in enumerate(self.extensions):
+ extensions.setComponentByPosition(count, extension)
+ tbsCertificate["extensions"] = extensions
+ return tbsCertificate
+
+ def toDER(self):
+ (signatureOID, hashAlgorithm) = stringToAlgorithmIdentifiers(self.signature)
+ certificate = rfc2459.Certificate()
+ tbsCertificate = self.getTBSCertificate()
+ certificate["tbsCertificate"] = tbsCertificate
+ certificate["signatureAlgorithm"] = signatureOID
+ tbsDER = encoder.encode(tbsCertificate)
+ certificate["signatureValue"] = self.issuerKey.sign(tbsDER, hashAlgorithm)
+ return encoder.encode(certificate)
+
+ def toPEM(self):
+ output = "-----BEGIN CERTIFICATE-----"
+ der = self.toDER()
+ b64 = six.ensure_text(base64.b64encode(der))
+ while b64:
+ output += "\n" + b64[:64]
+ b64 = b64[64:]
+ output += "\n-----END CERTIFICATE-----"
+ return output
+
+
+# The build harness will call this function with an output
+# file-like object and a path to a file containing a
+# specification. This will read the specification and output
+# the certificate as PEM.
+# This utility tries as hard as possible to ensure that two
+# runs with the same input will have the same output. This is
+# particularly important when building on OS X, where we
+# generate everything twice for unified builds. During the
+# unification step, if any pair of input files differ, the build
+# system throws an error.
+# The one concrete failure mode is if one run happens before
+# midnight on New Year's Eve and the next run happens after
+# midnight.
+def main(output, inputPath):
+ with open(inputPath) as configStream:
+ output.write(Certificate(configStream).toPEM() + "\n")
+
+
+# When run as a standalone program, this will read a specification from
+# stdin and output the certificate as PEM to stdout.
+if __name__ == "__main__":
+ print(Certificate(sys.stdin).toPEM())
diff --git a/security/manager/ssl/tests/unit/pycms.py b/security/manager/ssl/tests/unit/pycms.py
new file mode 100755
index 0000000000..befe68e346
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pycms.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Reads a specification from stdin and outputs a PKCS7 (CMS) message with
+the desired properties.
+
+The specification format is as follows:
+
+sha1:<hex string>
+sha256:<hex string>
+signer:
+<pycert specification>
+
+Eith or both of sha1 and sha256 may be specified. The value of
+each hash directive is what will be put in the messageDigest
+attribute of the SignerInfo that corresponds to the signature
+algorithm defined by the hash algorithm and key type of the
+default key. Together, these comprise the signerInfos field of
+the SignedData. If neither hash is specified, the signerInfos
+will be an empty SET (i.e. there will be no actual signature
+information).
+The certificate specification must come last.
+"""
+
+from pyasn1.codec.der import decoder
+from pyasn1.codec.der import encoder
+from pyasn1.type import tag, univ
+from pyasn1_modules import rfc2315, rfc2459
+import base64
+from io import StringIO
+import pycert
+import pykey
+import sys
+
+
+class Error(Exception):
+ """Base class for exceptions in this module."""
+
+ pass
+
+
+class UnknownDirectiveError(Error):
+ """Helper exception type to handle unknown specification
+ directives."""
+
+ def __init__(self, directive):
+ super(UnknownDirectiveError, self).__init__()
+ self.directive = directive
+
+ def __str__(self):
+ return "Unknown directive %s" % repr(self.directive)
+
+
+class CMS(object):
+ """Utility class for reading a CMS specification and
+ generating a CMS message"""
+
+ def __init__(self, paramStream):
+ self.sha1 = ""
+ self.sha256 = ""
+ signerSpecification = StringIO()
+ readingSignerSpecification = False
+ for line in paramStream.readlines():
+ if readingSignerSpecification:
+ print(line.strip(), file=signerSpecification)
+ elif line.strip() == "signer:":
+ readingSignerSpecification = True
+ elif line.startswith("sha1:"):
+ self.sha1 = line.strip()[len("sha1:") :]
+ elif line.startswith("sha256:"):
+ self.sha256 = line.strip()[len("sha256:") :]
+ else:
+ raise UnknownDirectiveError(line.strip())
+ signerSpecification.seek(0)
+ self.signer = pycert.Certificate(signerSpecification)
+ self.signingKey = pykey.keyFromSpecification("default")
+
+ def buildAuthenticatedAttributes(self, value, implicitTag=None):
+ """Utility function to build a pyasn1 AuthenticatedAttributes
+ object. Useful because when building a SignerInfo, the
+ authenticatedAttributes needs to be tagged implicitly, but when
+ signing an AuthenticatedAttributes, it needs the explicit SET
+ tag."""
+ if implicitTag:
+ authenticatedAttributes = rfc2315.Attributes().subtype(
+ implicitTag=implicitTag
+ )
+ else:
+ authenticatedAttributes = rfc2315.Attributes()
+ contentTypeAttribute = rfc2315.Attribute()
+ # PKCS#9 contentType
+ contentTypeAttribute["type"] = univ.ObjectIdentifier("1.2.840.113549.1.9.3")
+ contentTypeAttribute["values"] = univ.SetOf(rfc2459.AttributeValue())
+ # PKCS#7 data
+ contentTypeAttribute["values"][0] = univ.ObjectIdentifier(
+ "1.2.840.113549.1.7.1"
+ )
+ authenticatedAttributes[0] = contentTypeAttribute
+ hashAttribute = rfc2315.Attribute()
+ # PKCS#9 messageDigest
+ hashAttribute["type"] = univ.ObjectIdentifier("1.2.840.113549.1.9.4")
+ hashAttribute["values"] = univ.SetOf(rfc2459.AttributeValue())
+ hashAttribute["values"][0] = univ.OctetString(hexValue=value)
+ authenticatedAttributes[1] = hashAttribute
+ return authenticatedAttributes
+
+ def pykeyHashToDigestAlgorithm(self, pykeyHash):
+ """Given a pykey hash algorithm identifier, builds an
+ AlgorithmIdentifier for use with pyasn1."""
+ if pykeyHash == pykey.HASH_SHA1:
+ oidString = "1.3.14.3.2.26"
+ elif pykeyHash == pykey.HASH_SHA256:
+ oidString = "2.16.840.1.101.3.4.2.1"
+ else:
+ raise pykey.UnknownHashAlgorithmError(pykeyHash)
+ algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+ algorithmIdentifier["algorithm"] = univ.ObjectIdentifier(oidString)
+ # Directly setting parameters to univ.Null doesn't currently work.
+ nullEncapsulated = encoder.encode(univ.Null())
+ algorithmIdentifier["parameters"] = univ.Any(nullEncapsulated)
+ return algorithmIdentifier
+
+ def buildSignerInfo(self, certificate, pykeyHash, digestValue):
+ """Given a pyasn1 certificate, a pykey hash identifier
+ and a hash value, creates a SignerInfo with the
+ appropriate values."""
+ signerInfo = rfc2315.SignerInfo()
+ signerInfo["version"] = 1
+ issuerAndSerialNumber = rfc2315.IssuerAndSerialNumber()
+ issuerAndSerialNumber["issuer"] = self.signer.getIssuer()
+ issuerAndSerialNumber["serialNumber"] = certificate["tbsCertificate"][
+ "serialNumber"
+ ]
+ signerInfo["issuerAndSerialNumber"] = issuerAndSerialNumber
+ signerInfo["digestAlgorithm"] = self.pykeyHashToDigestAlgorithm(pykeyHash)
+ rsa = rfc2459.AlgorithmIdentifier()
+ rsa["algorithm"] = rfc2459.rsaEncryption
+ rsa["parameters"] = univ.Null()
+ authenticatedAttributes = self.buildAuthenticatedAttributes(
+ digestValue,
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0),
+ )
+ authenticatedAttributesTBS = self.buildAuthenticatedAttributes(digestValue)
+ signerInfo["authenticatedAttributes"] = authenticatedAttributes
+ signerInfo["digestEncryptionAlgorithm"] = rsa
+ authenticatedAttributesEncoded = encoder.encode(authenticatedAttributesTBS)
+ signature = self.signingKey.sign(authenticatedAttributesEncoded, pykeyHash)
+ # signature will be a hexified bit string of the form
+ # "'<hex bytes>'H". For some reason that's what BitString wants,
+ # but since this is an OCTET STRING, we have to strip off the
+ # quotation marks and trailing "H".
+ signerInfo["encryptedDigest"] = univ.OctetString(hexValue=signature[1:-2])
+ return signerInfo
+
+ def toDER(self):
+ contentInfo = rfc2315.ContentInfo()
+ contentInfo["contentType"] = rfc2315.signedData
+
+ signedData = rfc2315.SignedData()
+ signedData["version"] = rfc2315.Version(1)
+
+ digestAlgorithms = rfc2315.DigestAlgorithmIdentifiers()
+ digestAlgorithms[0] = self.pykeyHashToDigestAlgorithm(pykey.HASH_SHA1)
+ signedData["digestAlgorithms"] = digestAlgorithms
+
+ dataContentInfo = rfc2315.ContentInfo()
+ dataContentInfo["contentType"] = rfc2315.data
+ signedData["contentInfo"] = dataContentInfo
+
+ certificates = rfc2315.ExtendedCertificatesAndCertificates().subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)
+ )
+ extendedCertificateOrCertificate = rfc2315.ExtendedCertificateOrCertificate()
+ certificate = decoder.decode(
+ self.signer.toDER(), asn1Spec=rfc2459.Certificate()
+ )[0]
+ extendedCertificateOrCertificate["certificate"] = certificate
+ certificates[0] = extendedCertificateOrCertificate
+ signedData["certificates"] = certificates
+
+ signerInfos = rfc2315.SignerInfos()
+
+ if len(self.sha1) > 0:
+ signerInfos[len(signerInfos)] = self.buildSignerInfo(
+ certificate, pykey.HASH_SHA1, self.sha1
+ )
+ if len(self.sha256) > 0:
+ signerInfos[len(signerInfos)] = self.buildSignerInfo(
+ certificate, pykey.HASH_SHA256, self.sha256
+ )
+ signedData["signerInfos"] = signerInfos
+
+ encoded = encoder.encode(signedData)
+ anyTag = univ.Any(encoded).subtype(
+ explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)
+ )
+
+ contentInfo["content"] = anyTag
+ return encoder.encode(contentInfo)
+
+ def toPEM(self):
+ output = "-----BEGIN PKCS7-----"
+ der = self.toDER()
+ b64 = base64.b64encode(der)
+ while b64:
+ output += "\n" + b64[:64]
+ b64 = b64[64:]
+ output += "\n-----END PKCS7-----\n"
+ return output
+
+
+# When run as a standalone program, this will read a specification from
+# stdin and output the certificate as PEM to stdout.
+if __name__ == "__main__":
+ print(CMS(sys.stdin).toPEM())
diff --git a/security/manager/ssl/tests/unit/pyct.py b/security/manager/ssl/tests/unit/pyct.py
new file mode 100644
index 0000000000..125b626fc2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pyct.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Helper library for creating a Signed Certificate Timestamp given the
+details of a signing key, when to sign, and the certificate data to
+sign. Currently only supports precert_entry types. See RFC 6962.
+"""
+
+from pyasn1.codec.der import encoder
+from struct import pack
+import binascii
+import calendar
+import hashlib
+
+import pykey
+
+
+class InvalidKeyError(Exception):
+ """Helper exception to handle unknown key types."""
+
+ def __init__(self, key):
+ self.key = key
+
+ def __str__(self):
+ return 'Invalid key: "%s"' % str(self.key)
+
+
+class SCT(object):
+ """SCT represents a Signed Certificate Timestamp."""
+
+ def __init__(self, key, date, tbsCertificate, issuerKey):
+ self.key = key
+ self.timestamp = calendar.timegm(date.timetuple()) * 1000
+ self.tbsCertificate = tbsCertificate
+ self.issuerKey = issuerKey
+
+ def signAndEncode(self):
+ """Returns a signed and encoded representation of the SCT as a
+ string."""
+ # The signature is over the following data:
+ # sct_version (one 0 byte)
+ # signature_type (one 0 byte)
+ # timestamp (8 bytes, milliseconds since the epoch)
+ # entry_type (two bytes [0, 1] - currently only precert_entry is
+ # supported)
+ # signed_entry (bytes of PreCert)
+ # extensions (2-byte-length-prefixed, currently empty (so two 0
+ # bytes))
+ # A PreCert is:
+ # issuer_key_hash (32 bytes of SHA-256 hash of the issuing
+ # public key, as DER-encoded SPKI)
+ # tbs_certificate (3-byte-length-prefixed data)
+ timestamp = pack("!Q", self.timestamp)
+ hasher = hashlib.sha256()
+ hasher.update(encoder.encode(self.issuerKey.asSubjectPublicKeyInfo()))
+ issuer_key_hash = hasher.digest()
+ len_prefix = pack("!L", len(self.tbsCertificate))[1:]
+ data = (
+ b"\0\0"
+ + timestamp
+ + b"\0\1"
+ + issuer_key_hash
+ + len_prefix
+ + self.tbsCertificate
+ + b"\0\0"
+ )
+ if isinstance(self.key, pykey.ECCKey):
+ signatureByte = b"\3"
+ elif isinstance(self.key, pykey.RSAKey):
+ signatureByte = b"\1"
+ else:
+ raise InvalidKeyError(self.key)
+ # sign returns a hex string like "'<hex bytes>'H", but we want
+ # bytes here
+ hexSignature = self.key.sign(data, pykey.HASH_SHA256)
+ signature = binascii.unhexlify(hexSignature[1:-2])
+ # The actual data returned is the following:
+ # sct_version (one 0 byte)
+ # id (32 bytes of SHA-256 hash of the signing key, as
+ # DER-encoded SPKI)
+ # timestamp (8 bytes, milliseconds since the epoch)
+ # extensions (2-byte-length-prefixed data, currently
+ # empty)
+ # hash (one 4 byte representing sha256)
+ # signature (one byte - 1 for RSA and 3 for ECDSA)
+ # signature (2-byte-length-prefixed data)
+ hasher = hashlib.sha256()
+ hasher.update(encoder.encode(self.key.asSubjectPublicKeyInfo()))
+ key_id = hasher.digest()
+ signature_len_prefix = pack("!H", len(signature))
+ return (
+ b"\0"
+ + key_id
+ + timestamp
+ + b"\0\0\4"
+ + signatureByte
+ + signature_len_prefix
+ + signature
+ )
diff --git a/security/manager/ssl/tests/unit/pykey.py b/security/manager/ssl/tests/unit/pykey.py
new file mode 100755
index 0000000000..05163adc36
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pykey.py
@@ -0,0 +1,958 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Reads a key specification from stdin or a file and outputs a
+PKCS #8 file representing the (private) key. Also provides
+methods for signing data and representing the key as a subject
+public key info for use with pyasn1.
+
+The key specification format is as follows:
+
+default: a 2048-bit RSA key
+alternate: a different 2048-bit RSA key
+ev: a 2048-bit RSA key that, when combined with the right pycert
+ specification, results in a certificate that is enabled for
+ extended validation in debug Firefox (see ExtendedValidation.cpp).
+evRSA2040: a 2040-bit RSA key that, when combined with the right pycert
+ specification, results in a certificate that is enabled for
+ extended validation in debug Firefox.
+rsa2040: a 2040-bit RSA key
+rsa1024: a 1024-bit RSA key
+rsa1016: a 1016-bit RSA key
+secp256k1: an ECC key on the curve secp256k1
+secp244r1: an ECC key on the curve secp244r1
+secp256r1: an ECC key on the curve secp256r1
+secp384r1: an ECC key on the curve secp384r1
+secp521r1: an ECC key on the curve secp521r1
+"""
+
+from pyasn1.codec.der import encoder
+from pyasn1.type import univ, namedtype, tag
+from pyasn1_modules import rfc2459
+import base64
+import binascii
+import ecdsa
+import hashlib
+import math
+import rsa
+import six
+import sys
+
+# "constants" to make it easier for consumers to specify hash algorithms
+HASH_MD5 = "hash:md5"
+HASH_SHA1 = "hash:sha1"
+HASH_SHA256 = "hash:sha256"
+HASH_SHA384 = "hash:sha384"
+HASH_SHA512 = "hash:sha512"
+
+
+# NOTE: With bug 1621441 we migrated from one library for ecdsa to another.
+# These libraries differ somewhat in terms of functionality and interface. In
+# order to ensure there are no diffs and that the generated signatures are
+# exactly the same between the two libraries, we need to patch some stuff in.
+
+
+def _gen_k(curve):
+ # This calculation is arbitrary, but it matches what we were doing pre-
+ # bug 1621441 (see the above NOTE). Crucially, this generation of k is
+ # non-random; the ecdsa library exposes an option to deterministically
+ # generate a value of k for us, but it doesn't match up to what we were
+ # doing before so we have to inject a custom value.
+ num_bytes = int(math.log(curve.order - 1, 2) + 1) // 8 + 8
+ entropy = int.from_bytes(b"\04" * num_bytes, byteorder="big")
+ p = curve.curve.p()
+ return (entropy % (p - 1)) + 1
+
+
+# As above, the library has built-in logic for truncating digests that are too
+# large, but they use a slightly different technique than our previous library.
+# Re-implement that logic here.
+def _truncate_digest(digest, curve):
+ i = int.from_bytes(digest, byteorder="big")
+ p = curve.curve.p()
+ while i > p:
+ i >>= 1
+ return i.to_bytes(math.ceil(i.bit_length() / 8), byteorder="big")
+
+
+def byteStringToHexifiedBitString(string):
+ """Takes a string of bytes and returns a hex string representing
+ those bytes for use with pyasn1.type.univ.BitString. It must be of
+ the form "'<hex bytes>'H", where the trailing 'H' indicates to
+ pyasn1 that the input is a hex string."""
+ return "'%s'H" % six.ensure_binary(string).hex()
+
+
+class UnknownBaseError(Exception):
+ """Base class for handling unexpected input in this module."""
+
+ def __init__(self, value):
+ super(UnknownBaseError, self).__init__()
+ self.value = value
+ self.category = "input"
+
+ def __str__(self):
+ return 'Unknown %s type "%s"' % (self.category, repr(self.value))
+
+
+class UnknownKeySpecificationError(UnknownBaseError):
+ """Helper exception type to handle unknown key specifications."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "key specification"
+
+
+class UnknownHashAlgorithmError(UnknownBaseError):
+ """Helper exception type to handle unknown key specifications."""
+
+ def __init__(self, value):
+ UnknownBaseError.__init__(self, value)
+ self.category = "hash algorithm"
+
+
+class UnsupportedHashAlgorithmError(Exception):
+ """Helper exception type for unsupported hash algorithms."""
+
+ def __init__(self, value):
+ super(UnsupportedHashAlgorithmError, self).__init__()
+ self.value = value
+
+ def __str__(self):
+ return 'Unsupported hash algorithm "%s"' % repr(self.value)
+
+
+class RSAPublicKey(univ.Sequence):
+ """Helper type for encoding an RSA public key"""
+
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType("N", univ.Integer()),
+ namedtype.NamedType("E", univ.Integer()),
+ )
+
+
+class RSAPrivateKey(univ.Sequence):
+ """Helper type for encoding an RSA private key"""
+
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType("version", univ.Integer()),
+ namedtype.NamedType("modulus", univ.Integer()),
+ namedtype.NamedType("publicExponent", univ.Integer()),
+ namedtype.NamedType("privateExponent", univ.Integer()),
+ namedtype.NamedType("prime1", univ.Integer()),
+ namedtype.NamedType("prime2", univ.Integer()),
+ namedtype.NamedType("exponent1", univ.Integer()),
+ namedtype.NamedType("exponent2", univ.Integer()),
+ namedtype.NamedType("coefficient", univ.Integer()),
+ )
+
+
+class ECPrivateKey(univ.Sequence):
+ """Helper type for encoding an EC private key
+ ECPrivateKey ::= SEQUENCE {
+ version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ privateKey OCTET STRING,
+ parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
+ (NOTE: parameters field is not supported)
+ publicKey [1] BIT STRING OPTIONAL
+ }"""
+
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType("version", univ.Integer()),
+ namedtype.NamedType("privateKey", univ.OctetString()),
+ namedtype.OptionalNamedType(
+ "publicKey",
+ univ.BitString().subtype(
+ explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1)
+ ),
+ ),
+ )
+
+
+class ECPoint(univ.Sequence):
+ """Helper type for encoding a EC point"""
+
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType("x", univ.Integer()),
+ namedtype.NamedType("y", univ.Integer()),
+ )
+
+
+class PrivateKeyInfo(univ.Sequence):
+ """Helper type for encoding a PKCS #8 private key info"""
+
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType("version", univ.Integer()),
+ namedtype.NamedType("privateKeyAlgorithm", rfc2459.AlgorithmIdentifier()),
+ namedtype.NamedType("privateKey", univ.OctetString()),
+ )
+
+
+class RSAKey(object):
+ # For reference, when encoded as a subject public key info, the
+ # base64-encoded sha-256 hash of this key is
+ # VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=
+ sharedRSA_N = int(
+ "00ba8851a8448e16d641fd6eb6880636103d3c13d9eae4354ab4ecf56857"
+ "6c247bc1c725a8e0d81fbdb19c069b6e1a86f26be2af5a756b6a6471087a"
+ "a55aa74587f71cd5249c027ecd43fc1e69d038202993ab20c349e4dbb94c"
+ "c26b6c0eed15820ff17ead691ab1d3023a8b2a41eea770e00f0d8dfd660b"
+ "2bb02492a47db988617990b157903dd23bc5e0b8481fa837d38843ef2716"
+ "d855b7665aaa7e02902f3a7b10800624cc1c6c97ad96615bb7e29612c075"
+ "31a30c91ddb4caf7fcad1d25d309efb9170ea768e1b37b2f226f69e3b48a"
+ "95611dee26d6259dab91084e36cb1c24042cbf168b2fe5f18f991731b8b3"
+ "fe4923fa7251c431d503acda180a35ed8d",
+ 16,
+ )
+ sharedRSA_E = 65537
+ sharedRSA_D = int(
+ "009ecbce3861a454ecb1e0fe8f85dd43c92f5825ce2e997884d0e1a949da"
+ "a2c5ac559b240450e5ac9fe0c3e31c0eefa6525a65f0c22194004ee1ab46"
+ "3dde9ee82287cc93e746a91929c5e6ac3d88753f6c25ba5979e73e5d8fb2"
+ "39111a3cdab8a4b0cdf5f9cab05f1233a38335c64b5560525e7e3b92ad7c"
+ "7504cf1dc7cb005788afcbe1e8f95df7402a151530d5808346864eb370aa"
+ "79956a587862cb533791307f70d91c96d22d001a69009b923c683388c9f3"
+ "6cb9b5ebe64302041c78d908206b87009cb8cabacad3dbdb2792fb911b2c"
+ "f4db6603585be9ae0ca3b8e6417aa04b06e470ea1a3b581ca03a6781c931"
+ "5b62b30e6011f224725946eec57c6d9441",
+ 16,
+ )
+ sharedRSA_P = int(
+ "00dd6e1d4fffebf68d889c4d114cdaaa9caa63a59374286c8a5c29a717bb"
+ "a60375644d5caa674c4b8bc7326358646220e4550d7608ac27d55b6db74f"
+ "8d8127ef8fa09098b69147de065573447e183d22fe7d885aceb513d9581d"
+ "d5e07c1a90f5ce0879de131371ecefc9ce72e9c43dc127d238190de81177"
+ "3ca5d19301f48c742b",
+ 16,
+ )
+ sharedRSA_Q = int(
+ "00d7a773d9ebc380a767d2fec0934ad4e8b5667240771acdebb5ad796f47"
+ "8fec4d45985efbc9532968289c8d89102fadf21f34e2dd4940eba8c09d6d"
+ "1f16dcc29729774c43275e9251ddbe4909e1fd3bf1e4bedf46a39b8b3833"
+ "28ef4ae3b95b92f2070af26c9e7c5c9b587fedde05e8e7d86ca57886fb16"
+ "5810a77b9845bc3127",
+ 16,
+ )
+ sharedRSA_exp1 = int(
+ "0096472b41a610c0ade1af2266c1600e3671355ba42d4b5a0eb4e9d7eb35"
+ "81400ba5dd132cdb1a5e9328c7bbc0bbb0155ea192972edf97d12751d8fc"
+ "f6ae572a30b1ea309a8712dd4e33241db1ee455fc093f5bc9b592d756e66"
+ "21474f32c07af22fb275d340792b32ba2590bbb261aefb95a258eea53765"
+ "5315be9c24d191992d",
+ 16,
+ )
+ sharedRSA_exp2 = int(
+ "28b450a7a75a856413b2bda6f7a63e3d964fb9ecf50e3823ef6cc8e8fa26"
+ "ee413f8b9d1205540f12bbe7a0c76828b7ba65ad83cca4d0fe2a220114e1"
+ "b35d03d5a85bfe2706bd50fce6cfcdd571b46ca621b8ed47d605bbe765b0"
+ "aa4a0665ac25364da20154032e1204b8559d3e34fb5b177c9a56ff93510a"
+ "5a4a6287c151de2d",
+ 16,
+ )
+ sharedRSA_coef = int(
+ "28067b9355801d2ef52dfa96d8adb589673cf8ee8a9c6ff72aeeabe9ef6b"
+ "e58a4f4abf05f788947dc851fdaa34542147a71a246bfb054ee76aa346ab"
+ "cd2692cfc9e44c51e6f069c735e073ba019f6a7214961c91b26871caeabf"
+ "8f064418a02690e39a8d5ff3067b7cdb7f50b1f53418a703966c4fc774bf"
+ "7402af6c43247f43",
+ 16,
+ )
+
+ # For reference, when encoded as a subject public key info, the
+ # base64-encoded sha-256 hash of this key is
+ # MQj2tt1yGAfwFpWETYUCVrZxk2CD2705NKBQUlAaKJI=
+ alternateRSA_N = int(
+ "00c175c65266099f77082a6791f1b876c37f5ce538b06c4acd22b1cbd46f"
+ "a65ada2add41c8c2498ac4a3b3c1f61487f41b698941bd80a51c3c120244"
+ "c584a4c4483305e5138c0106cf08be9a862760bae6a2e8f36f23c5d98313"
+ "b9dfaf378345dace51d4d6dcd2a6cb3cc706ebcd3070ec98cce40aa591d7"
+ "295a7f71c5be66691d2b2dfec84944590bc5a3ea49fd93b1d753405f1773"
+ "7699958666254797ed426908880811422069988a43fee48ce68781dd22b6"
+ "a69cd28375131f932b128ce286fa7d251c062ad27ef016f187cdd54e832b"
+ "35b8930f74ba90aa8bc76167242ab1fd6d62140d18c4c0b8c68fc3748457"
+ "324ad7de86e6552f1d1e191d712168d3bb",
+ 16,
+ )
+ alternateRSA_E = 65537
+ alternateRSA_D = int(
+ "7e3f6d7cb839ef66ae5d7dd92ff5410bb341dc14728d39034570e1a37079"
+ "0f30f0681355fff41e2ad4e9a9d9fcebfbd127bdfab8c00affb1f3cea732"
+ "7ead47aa1621f2ac1ee14ca02f04b3b2786017980b181a449d03b03e69d1"
+ "12b83571e55434f012056575d2832ed6731dce799e37c83f6d51c55ab71e"
+ "b58015af05e1af15c747603ef7f27d03a6ff049d96bbf854c1e4e50ef5b0"
+ "58d0fb08180e0ac7f7be8f2ff1673d97fc9e55dba838077bbf8a7cff2962"
+ "857785269cd9d5bad2b57469e4afcd33c4ca2d2f699f11e7c8fbdcd484f0"
+ "8d8efb8a3cb8a972eb24bed972efaae4bb712093e48fe94a46eb629a8750"
+ "78c4021a9a2c93c9a70390e9d0a54401",
+ 16,
+ )
+ alternateRSA_P = int(
+ "00e63fc725a6ba76925a7ff8cb59c4f56dd7ec83fe85bf1f53e11cac9a81"
+ "258bcfc0ae819077b0f2d1477aaf868de6a8ecbeaf7bb22b196f2a9ad82d"
+ "3286f0d0cc29de719e5f2be8e509b7284d5963edd362f927887a4c4a8979"
+ "9d340d51b301ac7601ab27179024fcaadd38bf6522af63eb16461ec02a7f"
+ "27b06fe09ddda7c0a1",
+ 16,
+ )
+ alternateRSA_Q = int(
+ "00d718b1fe9f8f99f00e832ae1fbdc6fe2ab27f34e049c498010fa0eb708"
+ "4852182346083b5c96c3eee5592c014a410c6b930b165c13b5c26aa32eac"
+ "6e7c925a8551c25134f2f4a72c6421f19a73148a0edfaba5d3a6888b35cb"
+ "a18c00fd38ee5aaf0b545731d720761bbccdee744a52ca415e98e4de01cd"
+ "fe764c1967b3e8cadb",
+ 16,
+ )
+ alternateRSA_exp1 = int(
+ "01e5aca266c94a88d22e13c2b92ea247116c657a076817bdfd30db4b3a9d"
+ "3095b9a4b6749647e2f84e7a784fc7838b08c85971cf7a036fa30e3b91c3"
+ "c4d0df278f80c1b6e859d8456adb137defaa9f1f0ac5bac9a9184fd4ea27"
+ "9d722ea626f160d78aad7bc83845ccb29df115c83f61b7622b99bd439c60"
+ "9b5790a63c595181",
+ 16,
+ )
+ alternateRSA_exp2 = int(
+ "0080cc45d10d2484ee0d1297fc07bf80b3beff461ea27e1f38f371789c3a"
+ "f66b4a0edd2192c227791db4f1c77ae246bf342f31856b0f56581b58a95b"
+ "1131c0c5396db2a8c3c6f39ea2e336bc205ae6a2a0b36869fca98cbba733"
+ "cf01319a6f9bb26b7ca23d3017fc551cd8da8afdd17f6fa2e30d34868798"
+ "1cd6234d571e90b7df",
+ 16,
+ )
+ alternateRSA_coef = int(
+ "6f77c0c1f2ae7ac169561cca499c52bdfbe04cddccdbdc12aec5a85691e8"
+ "594b7ee29908f30e7b96aa6254b80ed4aeec9b993782bdfc79b69d8d58c6"
+ "8870fa4be1bc0c3527288c5c82bb4aebaf15edff110403fc78e6ace6a828"
+ "27bf42f0cfa751e507651c5638db9393dd23dd1f6b295151de44b77fe55a"
+ "7b0df271e19a65c0",
+ 16,
+ )
+
+ evRSA_N = int(
+ "00b549895c9d00108d11a1f99f87a9e3d1a5db5dfaecf188da57bf641368"
+ "8f2ce4722cff109038c17402c93a2a473dbd286aed3fdcd363cf5a291477"
+ "01bdd818d7615bf9356bd5d3c8336aaa8c0971368a06c3cd4461b93e5142"
+ "4e1744bb2eaad46aab38ce196821961f87714a1663693f09761cdf4d6ba1"
+ "25eacec7be270d388f789f6cdf78ae3144ed28c45e79293863a7a22a4898"
+ "0a36a40e72d579c9b925dff8c793362ffd6897a7c1754c5e97c967c3eadd"
+ "1aae8aa2ccce348a0169b80e28a2d70c1a960c6f335f2da09b9b643f5abf"
+ "ba49e8aaa981e960e27d87480bdd55dd9417fa18509fbb554ccf81a4397e"
+ "8ba8128a34bdf27865c189e5734fb22905",
+ 16,
+ )
+ evRSA_E = 65537
+ evRSA_D = int(
+ "00983d54f94d6f4c76eb23d6f93d78523530cf73b0d16254c6e781768d45"
+ "f55681d1d02fb2bd2aac6abc1c389860935c52a0d8f41482010394778314"
+ "1d864bff30803638a5c0152570ae9d18f3d8ca163efb475b0dddf32e7e16"
+ "ec7565e6bb5e025c41c5c66e57a03cede554221f83045347a2c4c451c3dc"
+ "e476b787ce0c057244be9e04ef13118dbbb3d5e0a6cc87029eafd4a69ed9"
+ "b14759b15e39d8a9884e56f54d2f9ab013f0d15f318a9ab6b2f73d1ec3c9"
+ "fe274ae89431a10640be7899b0011c5e5093a1834708689de100634dabde"
+ "60fbd6aaefa3a33df34a1f36f60c043036b748d1c9ee98c4031a0afec60e"
+ "fda0a990be524f5614eac4fdb34a52f951",
+ 16,
+ )
+ evRSA_P = int(
+ "00eadc2cb33e5ff1ca376bbd95bd6a1777d2cf4fac47545e92d11a6209b9"
+ "d5e4ded47834581c169b3c884742a09ea187505c1ca55414d8d25b497632"
+ "d5ec2aaa05233430fad49892777a7d68e038f561a3b8969e60b0a263defb"
+ "fda48a9b0ff39d95bc88b15267c8ade97b5107948e41e433249d87f7db10"
+ "9d5d74584d86bcc1d7",
+ 16,
+ )
+ evRSA_Q = int(
+ "00c59ae576a216470248d944a55b9e9bf93299da341ec56e558eba821abc"
+ "e1bf57b79cf411d2904c774f9dba1f15185f607b0574a08205d6ec28b66a"
+ "36d634232eaaf2fea37561abaf9d644b68db38c9964cb8c96ec0ac61eba6"
+ "4d05b446542f423976f5acde4ecc95536d2df578954f93f0cfd9c58fb78b"
+ "a2a76dd5ac284dc883",
+ 16,
+ )
+ evRSA_exp1 = int(
+ "00c1d2ef3906331c52aca64811f9fe425beb2898322fb3db51032ce8d7e9"
+ "fc32240be92019cf2480fcd5e329837127118b2a59a1bfe06c883e3a4447"
+ "f3f031cd9aebd0b8d368fc79740d2cce8eadb324df7f091eafe1564361d5"
+ "4920b01b0471230e5e47d93f8ed33963c517bc4fc78f6d8b1f9eba85bcce"
+ "db7033026508db6285",
+ 16,
+ )
+ evRSA_exp2 = int(
+ "008521b8db5694dfbe804a315f9efc9b65275c5490acf2a3456d65e6e610"
+ "bf9f647fc67501d4f5772f232ac70ccdef9fc2a6dfa415c7c41b6afc7af9"
+ "d07c3ca03f7ed93c09f0b99f2c304434322f1071709bbc1baa4c91575fa6"
+ "a959e07d4996956d95e22b57938b6e47c8d51ffedfc9bf888ce0d1a3e42b"
+ "65a89bed4b91d3e5f5",
+ 16,
+ )
+ evRSA_coef = int(
+ "00dc497b06b920c8be0b0077b798e977eef744a90ec2c5d7e6cbb22448fa"
+ "c72da81a33180e0d8a02e831460c7fc7fd3a612f7b9930b61b799f8e908e"
+ "632e9ba0409b6aa70b03a3ba787426263b5bd5843df8476edb5d14f6a861"
+ "3ebaf5b9cd5ca42f5fbd2802e08e4e49e5709f5151510caa5ab2c1c6eb3e"
+ "fe9295d16e8c25c916",
+ 16,
+ )
+
+ evRSA2040_N = int(
+ "00ca7020dc215f57914d343fae4a015111697af997a5ece91866499fc23f"
+ "1b88a118cbd30b10d91c7b9a0d4ee8972fcae56caf57f25fc1275a2a4dbc"
+ "b982428c32ef587bf2387410330a0ffb16b8029bd783969ef675f6de38c1"
+ "8f67193cb6c072f8b23d0b3374112627a57b90055771d9e62603f53788d7"
+ "f63afa724f5d108096df31f89f26b1eb5f7c4357980e008fcd55d827dd26"
+ "2395ca2f526a07897cc40c593b38716ebc0caa596719c6f29ac9b73a7a94"
+ "4748a3aa3e09e9eb4d461ea0027e540926614728b9d243975cf9a0541bef"
+ "d25e76b51f951110b0e7644fc7e38441791b6d2227384cb8004e23342372"
+ "b1cf5cc3e73e31b7bbefa160e6862ebb",
+ 16,
+ )
+ evRSA2040_E = 65537
+ evRSA2040_D = int(
+ "00b2db74bce92362abf72955a638ae8720ba3033bb7f971caf39188d7542"
+ "eaa1c1abb5d205b1e2111f4791c08911a2e141e8cfd7054702d23100b564"
+ "2c06e1a31b118afd1f9a2f396cced425c501d91435ca8656766ced2b93bb"
+ "b8669fce9bacd727d1dacb3dafabc3293e35389eef8ea0b58e1aeb1a20e6"
+ "a61f9fcd453f7567fe31d123b616a26fef4df1d6c9f7490111d028eefd1d"
+ "972045b1a242273dd7a67ebf111db2741a5a93c7b2289cc4a236f5a99a6e"
+ "c7a8206fdae1c1d04bdbb1980d4a298c5a17dae4186474a5f7835d882bce"
+ "f24aef4ed6f149f94d96c9f7d78e647fc778a9017ff208d3b4a1768b1821"
+ "62102cdab032fabbab38d5200a324649",
+ 16,
+ )
+ evRSA2040_P = int(
+ "0f3844d0d4d4d6a21acd76a6fc370b8550e1d7ec5a6234172e790f0029ae"
+ "651f6d5c59330ab19802b9d7a207de7a1fb778e3774fdbdc411750633d8d"
+ "1b3fe075006ffcfd1d10e763c7a9227d2d5f0c2dade1c9e659c350a159d3"
+ "6bb986f12636d4f9942b288bc0fe21da8799477173144249ca2e389e6c5c"
+ "25aa78c8cad7d4df",
+ 16,
+ )
+ evRSA2040_Q = int(
+ "0d4d0bedd1962f07a1ead6b23a4ed67aeaf1270f052a6d29ba074945c636"
+ "1a5c4f8f07bf859e067aed3f4e6e323ef2aa8a6acd340b0bdc7cfe4fd329"
+ "e3c97f870c7f7735792c6aa9d0f7e7542a28ed6f01b0e55a2b8d9c24a65c"
+ "6da314c95484f5c7c3954a81bb016b07ed17ee9b06039695bca059a79f8d"
+ "c2423d328d5265a5",
+ 16,
+ )
+ evRSA2040_exp1 = int(
+ "09f29a2ff05be8a96d614ba31b08935420a86c6bc42b99a6692ea0da5763"
+ "f01e596959b7ddce73ef9c2e4f6e5b40710887500d44ba0c3cd3132cba27"
+ "475f39c2df7552e2d123a2497a4f97064028769a48a3624657f72bf539f3"
+ "d0de234feccd3be8a0aa90c6bf6e9b0bed43070a24d061ff3ed1751a3ef2"
+ "ff7f6b90b9dbd5fb",
+ 16,
+ )
+ evRSA2040_exp2 = int(
+ "01a659e170cac120a03be1cf8f9df1caa353b03593bd7476e5853bd874c2"
+ "87388601c6c341ce9d1d284a5eef1a3a669d32b816a5eaecd8b7844fe070"
+ "64b9bca0c2b318d540277b3f7f1510d386bb36e03b04771e5d229e88893e"
+ "13b753bfb94518bb638e2404bd6e6a993c1668d93fc0b82ff08aaf34347d"
+ "3fe8397108c87ca5",
+ 16,
+ )
+ evRSA2040_coef = int(
+ "040257c0d4a21c0b9843297c65652db66304fb263773d728b6abfa06d37a"
+ "c0ca62c628023e09e37dc0a901e4ce1224180e2582a3aa4b6a1a7b98e2bd"
+ "70077aec14ac8ab66a755c71e0fc102471f9bbc1b46a95aa0b645f2c38e7"
+ "6450289619ea3f5e8ae61037bffcf8249f22aa4e76e2a01909f3feb290ce"
+ "93edf57b10ebe796",
+ 16,
+ )
+
+ rsa2040_N = int(
+ "00bac0652fdfbc0055882ffbaeaceec88fa2d083c297dd5d40664dd3d90f"
+ "52f9aa02bd8a50fba16e0fd991878ef475f9b350d9f8e3eb2abd717ce327"
+ "b09788531f13df8e3e4e3b9d616bb8a41e5306eed2472163161051180127"
+ "6a4eb66f07331b5cbc8bcae7016a8f9b3d4f2ac4553c624cf5263bcb348e"
+ "8840de6612870960a792191b138fb217f765cec7bff8e94f16b39419bf75"
+ "04c59a7e4f79bd6d173e9c7bf3d9d2a4e73cc180b0590a73d584fb7fc9b5"
+ "4fa544607e53fc685c7a55fd44a81d4142b6af51ea6fa6cea52965a2e8c5"
+ "d84f3ca024d6fbb9b005b9651ce5d9f2ecf40ed404981a9ffc02636e311b"
+ "095c6332a0c87dc39271b5551481774b",
+ 16,
+ )
+ rsa2040_E = 65537
+ rsa2040_D = int(
+ "603db267df97555cbed86b8df355034af28f1eb7f3e7829d239bcc273a7c"
+ "7a69a10be8f21f1b6c4b02c6bae3731c3158b5bbff4605f57ab7b7b2a0cb"
+ "a2ec005a2db5b1ea6e0aceea5bc745dcd2d0e9d6b80d7eb0ea2bc08127bc"
+ "e35fa50c42cc411871ba591e23ba6a38484a33eff1347f907ee9a5a92a23"
+ "11bb0b435510020f78e3bb00099db4d1182928096505fcba84f3ca1238fd"
+ "1eba5eea1f391bbbcc5424b168063fc17e1ca6e1912ccba44f9d0292308a"
+ "1fedb80612529b39f59d0a3f8180b5ba201132197f93a5815ded938df8e7"
+ "d93c9b15766588f339bb59100afda494a7e452d7dd4c9a19ce2ec3a33a18"
+ "b20f0b4dade172bee19f26f0dcbe41",
+ 16,
+ )
+ rsa2040_P = int(
+ "0ec3869cb92d406caddf7a319ab29448bc505a05913707873361fc5b986a"
+ "499fb65eeb815a7e37687d19f128087289d9bb8818e7bcca502c4900ad9a"
+ "ece1179be12ff3e467d606fc820ea8f07ac9ebffe2236e38168412028822"
+ "3e42dbe68dfd972a85a6447e51695f234da7911c67c9ab9531f33df3b994"
+ "32d4ee88c9a4efbb",
+ 16,
+ )
+ rsa2040_Q = int(
+ "0ca63934549e85feac8e0f5604303fd1849fe88af4b7f7e1213283bbc7a2"
+ "c2a509f9273c428c68de3db93e6145f1b400bd6d4a262614e9043ad362d4"
+ "eba4a6b995399c8934a399912199e841d8e8dbff0489f69e663796730b29"
+ "80530b31cb70695a21625ea2adccc09d930516fa872211a91e22dd89fd9e"
+ "b7da8574b72235b1",
+ 16,
+ )
+ rsa2040_exp1 = int(
+ "0d7d3a75e17f65f8a658a485c4095c10a4f66979e2b73bca9cf8ef21253e"
+ "1facac6d4791f58392ce8656f88f1240cc90c29653e3100c6d7a38ed44b1"
+ "63b339e5f3b6e38912126c69b3ceff2e5192426d9649b6ffca1abb75d2ba"
+ "2ed6d9a26aa383c5973d56216ff2edb90ccf887742a0f183ac92c94cf187"
+ "657645c7772d9ad7",
+ 16,
+ )
+ rsa2040_exp2 = int(
+ "03f550194c117f24bea285b209058032f42985ff55acebe88b16df9a3752"
+ "7b4e61dc91a68dbc9a645134528ce5f248bda2893c96cb7be79ee73996c7"
+ "c22577f6c2f790406f3472adb3b211b7e94494f32c5c6fcc0978839fe472"
+ "4c31b06318a2489567b4fca0337acb1b841227aaa5f6c74800a2306929f0"
+ "2ce038bad943df41",
+ 16,
+ )
+ rsa2040_coef = int(
+ "080a7dbfa8c2584814c71664c56eb62ce4caf16afe88d4499159d674774a"
+ "3a3ecddf1256c02fc91525c527692422d0aba94e5c41ee12dc71bb66f867"
+ "9fa17e096f28080851ba046eb31885c1414e8985ade599d907af17453d1c"
+ "caea2c0d06443f8367a6be154b125e390ee0d90f746f08801dd3f5367f59"
+ "fba2e5a67c05f375",
+ 16,
+ )
+
+ rsa1024_N = int(
+ "00d3a97440101eba8c5df9503e6f935eb52ffeb3ebe9d0dc5cace26f973c"
+ "a94cbc0d9c31d66c0c013bce9c82d0d480328df05fb6bcd7990a5312ddae"
+ "6152ad6ee61c8c1bdd8663c68bd36224a9882ae78e89f556dfdbe6f51da6"
+ "112cbfc27c8a49336b41afdb75321b52b24a7344d1348e646351a551c757"
+ "1ccda0b8fe35f61a75",
+ 16,
+ )
+ rsa1024_E = 65537
+ rsa1024_D = int(
+ "5b6708e185548fc07ff062dba3792363e106ff9177d60ee3227162391024"
+ "1813f958a318f26db8b6a801646863ebbc69190d6c2f5e7723433e99666d"
+ "76b3987892cd568f1f18451e8dc05477c0607ee348380ebb7f4c98d0c036"
+ "a0260bc67b2dab46cbaa4ce87636d839d8fddcbae2da3e02e8009a21225d"
+ "d7e47aff2f82699d",
+ 16,
+ )
+ rsa1024_P = int(
+ "00fcdee570323e8fc399dbfc63d8c1569546fb3cd6886c628668ab1e1d0f"
+ "ca71058febdf76d702970ad6579d80ac2f9521075e40ef8f3f39983bd819"
+ "07e898bad3",
+ 16,
+ )
+ rsa1024_Q = int(
+ "00d64801c955b4eb75fbae230faa8b28c9cc5e258be63747ff5ac8d2af25"
+ "3e9f6d6ce03ea2eb13ae0eb32572feb848c32ca00743635374338fedacd8"
+ "c5885f7897",
+ 16,
+ )
+ rsa1024_exp1 = int(
+ "76c0526d5b1b28368a75d5d42a01b9a086e20b9310241e2cd2d0b166a278"
+ "c694ff1e9d25d9193d47789b52bb0fa194de1af0b77c09007f12afdfeef9"
+ "58d108c3",
+ 16,
+ )
+ rsa1024_exp2 = int(
+ "008a41898d8b14217c4d782cbd15ef95d0a660f45ed09a4884f4e170367b"
+ "946d2f20398b907896890e88fe17b54bd7febe133ebc7720c86fe0649cca"
+ "7ca121e05f",
+ 16,
+ )
+ rsa1024_coef = int(
+ "22db133445f7442ea2a0f582031ee214ff5f661972986f172651d8d6b4ec"
+ "3163e99bff1c82fe58ec3d075c6d8f26f277020edb77c3ba821b9ba3ae18"
+ "ff8cb2cb",
+ 16,
+ )
+
+ rsa1016_N = int(
+ "00d29bb12fb84fddcd29b3a519cb66c43b8d8f8be545ba79384ce663ed03"
+ "df75991600eb920790d2530cece544db99a71f05896a3ed207165534aa99"
+ "057e47c47e3bc81ada6fa1e12e37268b5046a55268f9dad7ccb485d81a2e"
+ "19d50d4f0b6854acaf6d7be69d9a083136e15afa8f53c1c8c84fc6077279"
+ "dd0e55d7369a5bdd",
+ 16,
+ )
+ rsa1016_E = 65537
+ rsa1016_D = int(
+ "3c4965070bf390c251d5a2c5277c5b5fd0bdee85cad7fe2b27982bb28511"
+ "4a507004036ae1cf8ae54b25e4db39215abd7e903f618c2d8b2f08cc6cd1"
+ "2dbccd72205e4945b6b3df389e5e43de0a148bb2c84e2431fdbe5920b044"
+ "bb272f45ecff0721b7dfb60397fc613a9ea35c22300530cae8f9159c534d"
+ "f3bf0910951901",
+ 16,
+ )
+ rsa1016_P = int(
+ "0f9f17597c85b8051b9c69afb55ef576c996dbd09047d0ccde5b9d60ea5c"
+ "67fe4fac67be803f4b6ac5a3f050f76b966fb14f5cf105761e5ade6dd960"
+ "b183ba55",
+ 16,
+ )
+ rsa1016_Q = int(
+ "0d7b637112ce61a55168c0f9c9386fb279ab40cba0d549336bba65277263"
+ "aac782611a2c81d9b635cf78c40018859e018c5e9006d12e3d2ee6f346e7"
+ "9fa43369",
+ 16,
+ )
+ rsa1016_exp1 = int(
+ "09fd6c9a3ea6e91ae32070f9fc1c210ff9352f97be5d1eeb951bb39681e9"
+ "dc5b672a532221b3d8900c9a9d99b9d0a4e102dc450ca1b87b0b1389de65"
+ "16c0ae0d",
+ 16,
+ )
+ rsa1016_exp2 = int(
+ "0141b832491b7dd4a83308920024c79cae64bd447df883bb4c5672a96bab"
+ "48b7123b34f26324452cdceb17f21e570e347cbe2fd4c2d8f9910eac2cb6"
+ "d895b8c9",
+ 16,
+ )
+ rsa1016_coef = int(
+ "0458dd6aee18c88b2f9b81f1bc3075ae20dc1f9973d20724f20b06043d61"
+ "47c8789d4a07ae88bc82c8438c893e017b13947f62e0b18958a31eb664b1"
+ "9e64d3e0",
+ 16,
+ )
+
+ def __init__(self, specification):
+ if specification == "default":
+ self.RSA_N = self.sharedRSA_N
+ self.RSA_E = self.sharedRSA_E
+ self.RSA_D = self.sharedRSA_D
+ self.RSA_P = self.sharedRSA_P
+ self.RSA_Q = self.sharedRSA_Q
+ self.RSA_exp1 = self.sharedRSA_exp1
+ self.RSA_exp2 = self.sharedRSA_exp2
+ self.RSA_coef = self.sharedRSA_coef
+ elif specification == "alternate":
+ self.RSA_N = self.alternateRSA_N
+ self.RSA_E = self.alternateRSA_E
+ self.RSA_D = self.alternateRSA_D
+ self.RSA_P = self.alternateRSA_P
+ self.RSA_Q = self.alternateRSA_Q
+ self.RSA_exp1 = self.alternateRSA_exp1
+ self.RSA_exp2 = self.alternateRSA_exp2
+ self.RSA_coef = self.alternateRSA_coef
+ elif specification == "ev":
+ self.RSA_N = self.evRSA_N
+ self.RSA_E = self.evRSA_E
+ self.RSA_D = self.evRSA_D
+ self.RSA_P = self.evRSA_P
+ self.RSA_Q = self.evRSA_Q
+ self.RSA_exp1 = self.evRSA_exp1
+ self.RSA_exp2 = self.evRSA_exp2
+ self.RSA_coef = self.evRSA_coef
+ elif specification == "evRSA2040":
+ self.RSA_N = self.evRSA2040_N
+ self.RSA_E = self.evRSA2040_E
+ self.RSA_D = self.evRSA2040_D
+ self.RSA_P = self.evRSA2040_P
+ self.RSA_Q = self.evRSA2040_Q
+ self.RSA_exp1 = self.evRSA2040_exp1
+ self.RSA_exp2 = self.evRSA2040_exp2
+ self.RSA_coef = self.evRSA2040_coef
+ elif specification == "rsa2040":
+ self.RSA_N = self.rsa2040_N
+ self.RSA_E = self.rsa2040_E
+ self.RSA_D = self.rsa2040_D
+ self.RSA_P = self.rsa2040_P
+ self.RSA_Q = self.rsa2040_Q
+ self.RSA_exp1 = self.rsa2040_exp1
+ self.RSA_exp2 = self.rsa2040_exp2
+ self.RSA_coef = self.rsa2040_coef
+ elif specification == "rsa1024":
+ self.RSA_N = self.rsa1024_N
+ self.RSA_E = self.rsa1024_E
+ self.RSA_D = self.rsa1024_D
+ self.RSA_P = self.rsa1024_P
+ self.RSA_Q = self.rsa1024_Q
+ self.RSA_exp1 = self.rsa1024_exp1
+ self.RSA_exp2 = self.rsa1024_exp2
+ self.RSA_coef = self.rsa1024_coef
+ elif specification == "rsa1016":
+ self.RSA_N = self.rsa1016_N
+ self.RSA_E = self.rsa1016_E
+ self.RSA_D = self.rsa1016_D
+ self.RSA_P = self.rsa1016_P
+ self.RSA_Q = self.rsa1016_Q
+ self.RSA_exp1 = self.rsa1016_exp1
+ self.RSA_exp2 = self.rsa1016_exp2
+ self.RSA_coef = self.rsa1016_coef
+ else:
+ raise UnknownKeySpecificationError(specification)
+
+ def toDER(self):
+ privateKeyInfo = PrivateKeyInfo()
+ privateKeyInfo["version"] = 0
+ algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+ algorithmIdentifier["algorithm"] = rfc2459.rsaEncryption
+ # Directly setting parameters to univ.Null doesn't currently work.
+ nullEncapsulated = encoder.encode(univ.Null())
+ algorithmIdentifier["parameters"] = univ.Any(nullEncapsulated)
+ privateKeyInfo["privateKeyAlgorithm"] = algorithmIdentifier
+ rsaPrivateKey = RSAPrivateKey()
+ rsaPrivateKey["version"] = 0
+ rsaPrivateKey["modulus"] = self.RSA_N
+ rsaPrivateKey["publicExponent"] = self.RSA_E
+ rsaPrivateKey["privateExponent"] = self.RSA_D
+ rsaPrivateKey["prime1"] = self.RSA_P
+ rsaPrivateKey["prime2"] = self.RSA_Q
+ rsaPrivateKey["exponent1"] = self.RSA_exp1
+ rsaPrivateKey["exponent2"] = self.RSA_exp2
+ rsaPrivateKey["coefficient"] = self.RSA_coef
+ rsaPrivateKeyEncoded = encoder.encode(rsaPrivateKey)
+ privateKeyInfo["privateKey"] = univ.OctetString(rsaPrivateKeyEncoded)
+ return encoder.encode(privateKeyInfo)
+
+ def toPEM(self):
+ output = "-----BEGIN PRIVATE KEY-----"
+ der = self.toDER()
+ b64 = six.ensure_text(base64.b64encode(der))
+ while b64:
+ output += "\n" + b64[:64]
+ b64 = b64[64:]
+ output += "\n-----END PRIVATE KEY-----"
+ return output
+
+ def asSubjectPublicKeyInfo(self):
+ """Returns a subject public key info representing
+ this key for use by pyasn1."""
+ algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+ algorithmIdentifier["algorithm"] = rfc2459.rsaEncryption
+ # Directly setting parameters to univ.Null doesn't currently work.
+ nullEncapsulated = encoder.encode(univ.Null())
+ algorithmIdentifier["parameters"] = univ.Any(nullEncapsulated)
+ spki = rfc2459.SubjectPublicKeyInfo()
+ spki["algorithm"] = algorithmIdentifier
+ rsaKey = RSAPublicKey()
+ rsaKey["N"] = univ.Integer(self.RSA_N)
+ rsaKey["E"] = univ.Integer(self.RSA_E)
+ subjectPublicKey = univ.BitString(
+ byteStringToHexifiedBitString(encoder.encode(rsaKey))
+ )
+ spki["subjectPublicKey"] = subjectPublicKey
+ return spki
+
+ def sign(self, data, hashAlgorithm):
+ """Returns a hexified bit string representing a
+ signature by this key over the specified data.
+ Intended for use with pyasn1.type.univ.BitString"""
+ hashAlgorithmName = None
+ if hashAlgorithm == HASH_MD5:
+ hashAlgorithmName = "MD5"
+ elif hashAlgorithm == HASH_SHA1:
+ hashAlgorithmName = "SHA-1"
+ elif hashAlgorithm == HASH_SHA256:
+ hashAlgorithmName = "SHA-256"
+ elif hashAlgorithm == HASH_SHA384:
+ hashAlgorithmName = "SHA-384"
+ elif hashAlgorithm == HASH_SHA512:
+ hashAlgorithmName = "SHA-512"
+ else:
+ raise UnknownHashAlgorithmError(hashAlgorithm)
+ rsaPrivateKey = rsa.PrivateKey(
+ self.RSA_N, self.RSA_E, self.RSA_D, self.RSA_P, self.RSA_Q
+ )
+ signature = rsa.sign(data, rsaPrivateKey, hashAlgorithmName)
+ return byteStringToHexifiedBitString(signature)
+
+
+ecPublicKey = univ.ObjectIdentifier("1.2.840.10045.2.1")
+secp256k1 = univ.ObjectIdentifier("1.3.132.0.10")
+secp224r1 = univ.ObjectIdentifier("1.3.132.0.33")
+secp256r1 = univ.ObjectIdentifier("1.2.840.10045.3.1.7")
+secp384r1 = univ.ObjectIdentifier("1.3.132.0.34")
+secp521r1 = univ.ObjectIdentifier("1.3.132.0.35")
+
+
+def longToEvenLengthHexString(val):
+ h = format(val, "x")
+ if not len(h) % 2 == 0:
+ h = "0" + h
+ return h
+
+
+class ECCKey(object):
+ secp256k1KeyPair = (
+ "35ee7c7289d8fef7a86afe5da66d8bc2ebb6a8543fd2fead089f45ce7acd0fa6"
+ + "4382a9500c41dad770ffd4b511bf4b492eb1238800c32c4f76c73a3f3294e7c5",
+ "67cebc208a5fa3df16ec2bb34acc59a42ab4abb0538575ca99b92b6a2149a04f",
+ )
+
+ secp224r1KeyPair = (
+ "668d72cca6fd6a1b3557b5366104d84408ecb637f08e8c86bbff82cc"
+ + "00e88f0066d7af63c3298ba377348a1202b03b37fd6b1ff415aa311e",
+ "04389459926c3296c242b83e10a6cd2011c8fe2dae1b772ea5b21067",
+ )
+
+ secp256r1KeyPair = (
+ "4fbfbbbb61e0f8f9b1a60a59ac8704e2ec050b423e3cf72e923f2c4f794b455c"
+ + "2a69d233456c36c4119d0706e00eedc8d19390d7991b7b2d07a304eaa04aa6c0",
+ "2191403d5710bf15a265818cd42ed6fedf09add92d78b18e7a1e9feb95524702",
+ )
+
+ secp384r1KeyPair = (
+ "a1687243362b5c7b1889f379154615a1c73fb48dee863e022915db608e252de4b71"
+ + "32da8ce98e831534e6a9c0c0b09c8d639ade83206e5ba813473a11fa330e05da8c9"
+ + "6e4383fe27873da97103be2888cff002f05af71a1fddcc8374aa6ea9ce",
+ "035c7a1b10d9fafe837b64ad92f22f5ced0789186538669b5c6d872cec3d926122b"
+ + "393772b57602ff31365efe1393246",
+ )
+
+ secp521r1KeyPair = (
+ "014cdc9cacc47941096bc9cc66752ec27f597734fa66c62b792f88c519d6d37f0d1"
+ + "6ea1c483a1827a010b9128e3a08070ca33ef5f57835b7c1ba251f6cc3521dc42b01"
+ + "0653451981b445d343eed3782a35d6cff0ff484f5a883d209f1b9042b726703568b"
+ + "2f326e18b833bdd8aa0734392bcd19501e10d698a79f53e11e0a22bdd2aad90",
+ "014f3284fa698dd9fe1118dd331851cdfaac5a3829278eb8994839de9471c940b85"
+ + "8c69d2d05e8c01788a7d0b6e235aa5e783fc1bee807dcc3865f920e12cf8f2d29",
+ )
+
+ def __init__(self, specification):
+ if specification == "secp256k1":
+ key_pair = self.secp256k1KeyPair
+ self.keyOID = secp256k1
+ self.curve = ecdsa.SECP256k1
+ elif specification == "secp224r1":
+ key_pair = self.secp224r1KeyPair
+ self.keyOID = secp224r1
+ self.curve = ecdsa.NIST224p
+ elif specification == "secp256r1":
+ key_pair = self.secp256r1KeyPair
+ self.keyOID = secp256r1
+ self.curve = ecdsa.NIST256p
+ elif specification == "secp384r1":
+ key_pair = self.secp384r1KeyPair
+ self.keyOID = secp384r1
+ self.curve = ecdsa.NIST384p
+ elif specification == "secp521r1":
+ key_pair = self.secp521r1KeyPair
+ self.keyOID = secp521r1
+ self.curve = ecdsa.NIST521p
+ else:
+ raise UnknownKeySpecificationError(specification)
+
+ self.public_key, self.private_key = (
+ binascii.unhexlify(key_pair[0]),
+ binascii.unhexlify(key_pair[1]),
+ )
+ self.key = ecdsa.SigningKey.from_string(self.private_key, curve=self.curve)
+
+ def getPublicKeyHexifiedString(self):
+ """Returns the EC public key as a hex string using the uncompressed
+ point representation. This is intended to be used in the encoder
+ functions, as it surrounds the value with ''H to indicate its type."""
+ p1, p2 = (
+ self.public_key[: len(self.public_key) // 2],
+ self.public_key[len(self.public_key) // 2 :],
+ )
+ # We don't want leading zeroes.
+ p1, p2 = (p1.lstrip(b"\0"), p2.lstrip(b"\0"))
+ # '04' indicates that the points are in uncompressed form.
+ return byteStringToHexifiedBitString(b"\04" + p1 + p2)
+
+ def toPEM(self):
+ """Return the EC private key in PEM-encoded form."""
+ output = "-----BEGIN EC PRIVATE KEY-----"
+ der = self.toDER()
+ b64 = six.ensure_text(base64.b64encode(der))
+ while b64:
+ output += "\n" + b64[:64]
+ b64 = b64[64:]
+ output += "\n-----END EC PRIVATE KEY-----"
+ return output
+
+ def toDER(self):
+ """Return the EC private key in DER-encoded form, encoded per SEC 1
+ section C.4 format."""
+ privateKeyInfo = PrivateKeyInfo()
+ privateKeyInfo["version"] = 0
+ algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+ algorithmIdentifier["algorithm"] = ecPublicKey
+ algorithmIdentifier["parameters"] = self.keyOID
+ privateKeyInfo["privateKeyAlgorithm"] = algorithmIdentifier
+ ecPrivateKey = ECPrivateKey()
+ ecPrivateKey["version"] = 1
+ ecPrivateKey["privateKey"] = self.private_key
+ ecPrivateKey["publicKey"] = univ.BitString(
+ self.getPublicKeyHexifiedString()
+ ).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))
+ ecPrivateKeyEncoded = encoder.encode(ecPrivateKey)
+ privateKeyInfo["privateKey"] = univ.OctetString(ecPrivateKeyEncoded)
+ return encoder.encode(privateKeyInfo)
+
+ def asSubjectPublicKeyInfo(self):
+ """Returns a subject public key info representing
+ this key for use by pyasn1."""
+ algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+ algorithmIdentifier["algorithm"] = ecPublicKey
+ algorithmIdentifier["parameters"] = self.keyOID
+ spki = rfc2459.SubjectPublicKeyInfo()
+ spki["algorithm"] = algorithmIdentifier
+ spki["subjectPublicKey"] = univ.BitString(self.getPublicKeyHexifiedString())
+ return spki
+
+ def signRaw(self, data, hashAlgorithm):
+ """Performs the ECDSA signature algorithm over the given data.
+ The returned value is a string representing the bytes of the
+ resulting point when encoded by left-padding each of (r, s) to
+ the key size and concatenating them.
+ """
+ assert hashAlgorithm.startswith("hash:")
+ hashAlgorithm = hashAlgorithm[len("hash:") :]
+ k = _gen_k(self.curve)
+ digest = hashlib.new(hashAlgorithm, six.ensure_binary(data)).digest()
+ digest = _truncate_digest(digest, self.curve)
+ # NOTE: Under normal circumstances it's advisable to use
+ # sign_digest_deterministic. In this case we don't want the library's
+ # default generation of k, so we call the normal "sign" method and
+ # inject it here.
+ return self.key.sign_digest(digest, sigencode=ecdsa.util.sigencode_string, k=k)
+
+ def sign(self, data, hashAlgorithm):
+ """Returns a hexified bit string representing a
+ signature by this key over the specified data.
+ Intended for use with pyasn1.type.univ.BitString"""
+ # signRaw returns an encoded point, which is useful in some situations.
+ # However, for signatures on X509 certificates, we need to decode it so
+ # we can encode it as a BITSTRING consisting of a SEQUENCE of two
+ # INTEGERs.
+ raw = self.signRaw(data, hashAlgorithm)
+ point = ECPoint()
+ point["x"] = int.from_bytes(raw[: len(raw) // 2], byteorder="big")
+ point["y"] = int.from_bytes(raw[len(raw) // 2 :], byteorder="big")
+ return byteStringToHexifiedBitString(encoder.encode(point))
+
+
+def keyFromSpecification(specification):
+ """Pass in a specification, get the appropriate key back."""
+ if specification.startswith("secp"):
+ return ECCKey(specification)
+ return RSAKey(specification)
+
+
+# The build harness will call this function with an output file-like
+# object and a path to a file containing a specification. This will
+# read the specification and output the key as ASCII-encoded PKCS #8.
+
+
+def main(output, inputPath):
+ with open(inputPath) as configStream:
+ output.write(keyFromSpecification(configStream.read().strip()).toPEM() + "\n")
+
+
+# When run as a standalone program, this will read a specification from
+# stdin and output the certificate as PEM to stdout.
+if __name__ == "__main__":
+ print(keyFromSpecification(sys.stdin.read().strip()).toPEM())
diff --git a/security/manager/ssl/tests/unit/requirements.txt b/security/manager/ssl/tests/unit/requirements.txt
new file mode 100644
index 0000000000..095fcb04fc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/requirements.txt
@@ -0,0 +1,6 @@
+lxml
+pyasn1 == 0.3.7
+pyasn1_modules == 0.1.5
+ecc
+mock
+rsa
diff --git a/security/manager/ssl/tests/unit/sign_app.py b/security/manager/ssl/tests/unit/sign_app.py
new file mode 100755
index 0000000000..8c90376581
--- /dev/null
+++ b/security/manager/ssl/tests/unit/sign_app.py
@@ -0,0 +1,399 @@
+#!/usr/bin/env python3
+#
+# 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/.
+
+"""
+Given a directory of files, packages them up and signs the
+resulting zip file. Mainly for creating test inputs to the
+nsIX509CertDB.openSignedAppFileAsync API.
+"""
+from base64 import b64encode
+from cbor2 import dumps
+from cbor2.types import CBORTag
+from hashlib import sha1, sha256
+import argparse
+from io import StringIO
+import os
+import pycert
+import pycms
+import pykey
+import re
+import six
+import zipfile
+
+
+ES256 = -7
+ES384 = -35
+ES512 = -36
+KID = 4
+ALG = 1
+COSE_Sign = 98
+
+
+def coseAlgorithmToPykeyHash(algorithm):
+ """Helper function that takes one of (ES256, ES384, ES512)
+ and returns the corresponding pykey.HASH_* identifier."""
+ if algorithm == ES256:
+ return pykey.HASH_SHA256
+ if algorithm == ES384:
+ return pykey.HASH_SHA384
+ if algorithm == ES512:
+ return pykey.HASH_SHA512
+ raise UnknownCOSEAlgorithmError(algorithm)
+
+
+# COSE_Signature = [
+# protected : serialized_map,
+# unprotected : {},
+# signature : bstr
+# ]
+
+
+def coseSignature(payload, algorithm, signingKey, signingCertificate, bodyProtected):
+ """Returns a COSE_Signature structure.
+ payload is a string representing the data to be signed
+ algorithm is one of (ES256, ES384, ES512)
+ signingKey is a pykey.ECKey to sign the data with
+ signingCertificate is a byte string
+ bodyProtected is the serialized byte string of the protected body header
+ """
+ protected = {ALG: algorithm, KID: signingCertificate}
+ protectedEncoded = dumps(protected)
+ # Sig_structure = [
+ # context : "Signature"
+ # body_protected : bodyProtected
+ # sign_protected : protectedEncoded
+ # external_aad : nil
+ # payload : bstr
+ # ]
+ sigStructure = [u"Signature", bodyProtected, protectedEncoded, None, payload]
+ sigStructureEncoded = dumps(sigStructure)
+ pykeyHash = coseAlgorithmToPykeyHash(algorithm)
+ signature = signingKey.signRaw(sigStructureEncoded, pykeyHash)
+ return [protectedEncoded, {}, signature]
+
+
+# COSE_Sign = [
+# protected : serialized_map,
+# unprotected : {},
+# payload : nil,
+# signatures : [+ COSE_Signature]
+# ]
+
+
+def coseSig(payload, intermediates, signatures):
+ """Returns the entire (tagged) COSE_Sign structure.
+ payload is a string representing the data to be signed
+ intermediates is an array of byte strings
+ signatures is an array of (algorithm, signingKey,
+ signingCertificate) triplets to be passed to
+ coseSignature
+ """
+ protected = {KID: intermediates}
+ protectedEncoded = dumps(protected)
+ coseSignatures = []
+ for (algorithm, signingKey, signingCertificate) in signatures:
+ coseSignatures.append(
+ coseSignature(
+ payload, algorithm, signingKey, signingCertificate, protectedEncoded
+ )
+ )
+ tagged = CBORTag(COSE_Sign, [protectedEncoded, {}, None, coseSignatures])
+ return dumps(tagged)
+
+
+def walkDirectory(directory):
+ """Given a relative path to a directory, enumerates the
+ files in the tree rooted at that location. Returns a list
+ of pairs of paths to those files. The first in each pair
+ is the full path to the file. The second in each pair is
+ the path to the file relative to the directory itself."""
+ paths = []
+ for path, _dirs, files in os.walk(directory):
+ for f in files:
+ fullPath = os.path.join(path, f)
+ internalPath = re.sub(r"^/", "", fullPath.replace(directory, ""))
+ paths.append((fullPath, internalPath))
+ return paths
+
+
+def addManifestEntry(filename, hashes, contents, entries):
+ """Helper function to fill out a manifest entry.
+ Takes the filename, a list of (hash function, hash function name)
+ pairs to use, the contents of the file, and the current list
+ of manifest entries."""
+ entry = "Name: %s\n" % filename
+ for (hashFunc, name) in hashes:
+ base64hash = b64encode(hashFunc(contents).digest()).decode("ascii")
+ entry += "%s-Digest: %s\n" % (name, base64hash)
+ entries.append(entry)
+
+
+def getCert(subject, keyName, issuerName, ee, issuerKey=""):
+ """Helper function to create an X509 cert from a specification.
+ Takes the subject, the subject key name to use, the issuer name,
+ a bool whether this is an EE cert or not, and optionally an issuer key
+ name."""
+ certSpecification = (
+ "issuer:%s\n" % issuerName
+ + "subject:"
+ + subject
+ + "\n"
+ + "subjectKey:%s\n" % keyName
+ )
+ if ee:
+ certSpecification += "extension:keyUsage:digitalSignature"
+ else:
+ certSpecification += (
+ "extension:basicConstraints:cA,\n"
+ + "extension:keyUsage:cRLSign,keyCertSign"
+ )
+ if issuerKey:
+ certSpecification += "\nissuerKey:%s" % issuerKey
+ certSpecificationStream = StringIO()
+ print(certSpecification, file=certSpecificationStream)
+ certSpecificationStream.seek(0)
+ return pycert.Certificate(certSpecificationStream)
+
+
+def coseAlgorithmToSignatureParams(coseAlgorithm, issuerName):
+ """Given a COSE algorithm ('ES256', 'ES384', 'ES512') and an issuer
+ name, returns a (algorithm id, pykey.ECCKey, encoded certificate)
+ triplet for use with coseSig.
+ """
+ if coseAlgorithm == "ES256":
+ keyName = "secp256r1"
+ algId = ES256
+ elif coseAlgorithm == "ES384":
+ keyName = "secp384r1"
+ algId = ES384
+ elif coseAlgorithm == "ES512":
+ keyName = "secp521r1" # COSE uses the hash algorithm; this is the curve
+ algId = ES512
+ else:
+ raise UnknownCOSEAlgorithmError(coseAlgorithm)
+ key = pykey.ECCKey(keyName)
+ # The subject must differ to avoid errors when importing into NSS later.
+ ee = getCert(
+ "xpcshell signed app test signer " + keyName,
+ keyName,
+ issuerName,
+ True,
+ "default",
+ )
+ return (algId, key, ee.toDER())
+
+
+def signZip(
+ appDirectory,
+ outputFile,
+ issuerName,
+ rootName,
+ manifestHashes,
+ signatureHashes,
+ pkcs7Hashes,
+ coseAlgorithms,
+ emptySignerInfos,
+ headerPaddingFactor,
+):
+ """Given a directory containing the files to package up,
+ an output filename to write to, the name of the issuer of
+ the signing certificate, the name of trust anchor, a list of hash algorithms
+ to use in the manifest file, a similar list for the signature file,
+ a similar list for the pkcs#7 signature, a list of COSE signature algorithms
+ to include, whether the pkcs#7 signer info should be kept empty, and how
+ many MB to pad the manifests by (to test handling large manifest files),
+ packages up the files in the directory and creates the output as
+ appropriate."""
+ # The header of each manifest starts with the magic string
+ # 'Manifest-Version: 1.0' and ends with a blank line. There can be
+ # essentially anything after the first line before the blank line.
+ mfEntries = ["Manifest-Version: 1.0"]
+ if headerPaddingFactor > 0:
+ # In this format, each line can only be 72 bytes long. We make
+ # our padding 50 bytes per line (49 of content and one newline)
+ # so the math is easy.
+ singleLinePadding = "a" * 49
+ # 1000000 / 50 = 20000
+ allPadding = [singleLinePadding] * (headerPaddingFactor * 20000)
+ mfEntries.extend(allPadding)
+ # Append the blank line.
+ mfEntries.append("")
+
+ with zipfile.ZipFile(outputFile, "w", zipfile.ZIP_DEFLATED) as outZip:
+ for (fullPath, internalPath) in walkDirectory(appDirectory):
+ with open(fullPath, "rb") as inputFile:
+ contents = inputFile.read()
+ outZip.writestr(internalPath, contents)
+
+ # Add the entry to the manifest we're building
+ addManifestEntry(internalPath, manifestHashes, contents, mfEntries)
+
+ if len(coseAlgorithms) > 0:
+ coseManifest = "\n".join(mfEntries)
+ outZip.writestr("META-INF/cose.manifest", coseManifest)
+ coseManifest = six.ensure_binary(coseManifest)
+ addManifestEntry(
+ "META-INF/cose.manifest", manifestHashes, coseManifest, mfEntries
+ )
+ intermediates = []
+ coseIssuerName = issuerName
+ if rootName:
+ coseIssuerName = "xpcshell signed app test issuer"
+ intermediate = getCert(coseIssuerName, "default", rootName, False)
+ intermediate = intermediate.toDER()
+ intermediates.append(intermediate)
+ signatures = [
+ coseAlgorithmToSignatureParams(coseAlgorithm, coseIssuerName)
+ for coseAlgorithm in coseAlgorithms
+ ]
+ coseSignatureBytes = coseSig(coseManifest, intermediates, signatures)
+ outZip.writestr("META-INF/cose.sig", coseSignatureBytes)
+ addManifestEntry(
+ "META-INF/cose.sig", manifestHashes, coseSignatureBytes, mfEntries
+ )
+
+ if len(pkcs7Hashes) != 0 or emptySignerInfos:
+ mfContents = "\n".join(mfEntries)
+ sfContents = "Signature-Version: 1.0\n"
+ for (hashFunc, name) in signatureHashes:
+ hashed = hashFunc(six.ensure_binary(mfContents)).digest()
+ base64hash = b64encode(hashed).decode("ascii")
+ sfContents += "%s-Digest-Manifest: %s\n" % (name, base64hash)
+
+ cmsSpecification = ""
+ for name in pkcs7Hashes:
+ hashFunc, _ = hashNameToFunctionAndIdentifier(name)
+ cmsSpecification += "%s:%s\n" % (
+ name,
+ hashFunc(six.ensure_binary(sfContents)).hexdigest(),
+ )
+ cmsSpecification += (
+ "signer:\n"
+ + "issuer:%s\n" % issuerName
+ + "subject:xpcshell signed app test signer\n"
+ + "extension:keyUsage:digitalSignature"
+ )
+ cmsSpecificationStream = StringIO()
+ print(cmsSpecification, file=cmsSpecificationStream)
+ cmsSpecificationStream.seek(0)
+ cms = pycms.CMS(cmsSpecificationStream)
+ p7 = cms.toDER()
+ outZip.writestr("META-INF/A.RSA", p7)
+ outZip.writestr("META-INF/A.SF", sfContents)
+ outZip.writestr("META-INF/MANIFEST.MF", mfContents)
+
+
+class Error(Exception):
+ """Base class for exceptions in this module."""
+
+ pass
+
+
+class UnknownHashAlgorithmError(Error):
+ """Helper exception type to handle unknown hash algorithms."""
+
+ def __init__(self, name):
+ super(UnknownHashAlgorithmError, self).__init__()
+ self.name = name
+
+ def __str__(self):
+ return "Unknown hash algorithm %s" % repr(self.name)
+
+
+class UnknownCOSEAlgorithmError(Error):
+ """Helper exception type to handle unknown COSE algorithms."""
+
+ def __init__(self, name):
+ super(UnknownCOSEAlgorithmError, self).__init__()
+ self.name = name
+
+ def __str__(self):
+ return "Unknown COSE algorithm %s" % repr(self.name)
+
+
+def hashNameToFunctionAndIdentifier(name):
+ if name == "sha1":
+ return (sha1, "SHA1")
+ if name == "sha256":
+ return (sha256, "SHA256")
+ raise UnknownHashAlgorithmError(name)
+
+
+def main(outputFile, appPath, *args):
+ """Main entrypoint. Given an already-opened file-like
+ object, a path to the app directory to sign, and some
+ optional arguments, signs the contents of the directory and
+ writes the resulting package to the 'file'."""
+ parser = argparse.ArgumentParser(description="Sign an app.")
+ parser.add_argument(
+ "-i",
+ "--issuer",
+ action="store",
+ help="Issuer name",
+ default="xpcshell signed apps test root",
+ )
+ parser.add_argument("-r", "--root", action="store", help="Root name", default="")
+ parser.add_argument(
+ "-m",
+ "--manifest-hash",
+ action="append",
+ help="Hash algorithms to use in manifest",
+ default=[],
+ )
+ parser.add_argument(
+ "-s",
+ "--signature-hash",
+ action="append",
+ help="Hash algorithms to use in signature file",
+ default=[],
+ )
+ parser.add_argument(
+ "-c",
+ "--cose-sign",
+ action="append",
+ help="Append a COSE signature with the given "
+ + "algorithms (out of ES256, ES384, and ES512)",
+ default=[],
+ )
+ parser.add_argument(
+ "-z",
+ "--pad-headers",
+ action="store",
+ default=0,
+ help="Pad the header sections of the manifests "
+ + "with X MB of repetitive data",
+ )
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument(
+ "-p",
+ "--pkcs7-hash",
+ action="append",
+ help="Hash algorithms to use in PKCS#7 signature",
+ default=[],
+ )
+ group.add_argument(
+ "-e",
+ "--empty-signerInfos",
+ action="store_true",
+ help="Emit pkcs#7 SignedData with empty signerInfos",
+ )
+ parsed = parser.parse_args(args)
+ if len(parsed.manifest_hash) == 0:
+ parsed.manifest_hash.append("sha256")
+ if len(parsed.signature_hash) == 0:
+ parsed.signature_hash.append("sha256")
+ signZip(
+ appPath,
+ outputFile,
+ parsed.issuer,
+ parsed.root,
+ [hashNameToFunctionAndIdentifier(h) for h in parsed.manifest_hash],
+ [hashNameToFunctionAndIdentifier(h) for h in parsed.signature_hash],
+ parsed.pkcs7_hash,
+ parsed.cose_sign,
+ parsed.empty_signerInfos,
+ int(parsed.pad_headers),
+ )
diff --git a/security/manager/ssl/tests/unit/sss_readstate_child_worker.js b/security/manager/ssl/tests/unit/sss_readstate_child_worker.js
new file mode 100644
index 0000000000..851a4e26ce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/sss_readstate_child_worker.js
@@ -0,0 +1,66 @@
+/* import-globals-from head_psm.js */
+"use strict";
+
+function run_test() {
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+
+ ok(
+ !SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://expired.example.com"),
+ 0
+ )
+ );
+ ok(
+ SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://notexpired.example.com"),
+ 0
+ )
+ );
+ ok(
+ SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ !SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://incsubdomain.example.com"),
+ 0
+ )
+ );
+ ok(
+ SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.incsubdomain.example.com"),
+ 0
+ )
+ );
+ ok(
+ !SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains2.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ !SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.includesubdomains2.preloaded.test"),
+ 0
+ )
+ );
+ do_test_finished();
+}
diff --git a/security/manager/ssl/tests/unit/test_add_preexisting_cert.js b/security/manager/ssl/tests/unit/test_add_preexisting_cert.js
new file mode 100644
index 0000000000..1f1a7825f2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_add_preexisting_cert.js
@@ -0,0 +1,46 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// Tests that adding a certificate already present in the certificate database
+// with different trust bits than those stored in the database does not result
+// in the new trust bits being ignored.
+
+do_get_profile();
+var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function load_cert(cert, trust) {
+ let file = "test_intermediate_basic_usage_constraints/" + cert + ".pem";
+ return addCertFromFile(certDB, file, trust);
+}
+
+add_task(async function() {
+ load_cert("ca", "CTu,CTu,CTu");
+ let int_cert = load_cert("int-limited-depth", "CTu,CTu,CTu");
+ let file =
+ "test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem";
+ let cert_pem = readFile(do_get_file(file));
+ let ee = certDB.constructX509FromBase64(pemToBase64(cert_pem));
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ // Change the already existing intermediate certificate's trust using
+ // addCertFromBase64().
+ notEqual(int_cert, null, "Intermediate cert should be in the cert DB");
+ let base64_cert = int_cert.getBase64DERString();
+ let returnedEE = certDB.addCertFromBase64(base64_cert, "p,p,p");
+ notEqual(returnedEE, null, "addCertFromBase64 should return a certificate");
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageSSLServer
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_allow_all_cert_errors.js b/security/manager/ssl/tests/unit/test_allow_all_cert_errors.js
new file mode 100644
index 0000000000..e9f1b6c9db
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_allow_all_cert_errors.js
@@ -0,0 +1,25 @@
+/* -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+function run_test() {
+ do_get_profile();
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
+ true
+ );
+
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+ add_connection_test("expired.example.com", PRErrorCodeSuccess);
+ add_test(function() {
+ certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
+ false
+ );
+ run_next_test();
+ });
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem
new file mode 100644
index 0000000000..161ce88377
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUXwklABFZn09Yj1azSdQ4kpuizxYwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDUwMDEwMTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYD
+VR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCdpP06UqpAnAAak/i4+LShrIG/
+DdjHLQ8qLChBdPkT6Gxx1XVoNfG3GJsIGCoOf6isORMfBlBXiR5kmLFTE3kJhA/I
+pDxDGYDqt9DNb0fxeMpOJxwQ+mBMZIKLPu+nk4jTqUrOX8bLwDMiXTbFeY91SUr8
+4b43YzXVorVQSlYOfcsmrnEfmfJNauHBrzak5BQhsEXHqAI7qV9TCQA1+cGwH8jb
+Aw2SyVu1usAyHkM2wCisHXeidf3qR6PxmfLMAgHKXBLz3DsXY30xDI4jJS9MySu9
+lhrs1IJf5PdJk5z5viYqZQemuv0R+R7ItbpCHpreUXh2GutiAY4Xsgrhfcer
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec
new file mode 100644
index 0000000000..9c21e7adcf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:ca
+validity:20100101-20500101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build b/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build
new file mode 100644
index 0000000000..df0cef67d6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'no-san-old.pem',
+# 'no-san-older.pem',
+# 'no-san-recent.pem',
+# 'san-contains-no-hostnames-old.pem',
+# 'san-contains-no-hostnames-older.pem',
+# 'san-contains-no-hostnames-recent.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem
new file mode 100644
index 0000000000..a13b0cbcd1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUS00fexo4Y4FagP1oiKQiGCJKd/swDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNjA3MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBAGjMx1NAs2guKp3wRRAWdiAw9NCeX+qCsxxkr4jUdgQ14wZwU2fZ/2rK
+7Z/StpwlWN+G4dWwCRCjibKvjauQs+XLT9wMO3U/1VBcQda36duX70ss2SAF9crs
+dmZdISevFItTn+W6JNyr1Wt2sSiD4buXpW1UzNwuxnWGpo/a++bMEYaM8KDdz73t
+KA9VShYNbcrUZgf70bvCbc8BR3vYUwyIp3fP7TngD/dRLYJyv5ayo9VXVJxOzYU7
+/aRrBvAdAL2w8eQzzYm6uyHlOE+imWYix2QOGNFJIiRXFRTii9Rr0o4geW7x2wZJ
++TzO4XPVlax1h6KDPRLsmwhbJDAMuUU=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec
new file mode 100644
index 0000000000..7a34d0758f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:example.com
+validity:20160724-20160924
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem
new file mode 100644
index 0000000000..f2bb8c97d9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUZ3gdKZRvWFYArMRStT2zAGE6JDQwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNTA3MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBABERZmWOEZAI2dQHbpXo7BJFw8eigDs8xWGtnu5UbNFs7zGnXzta0L8T
+RBoaWeEbsaVpuGQ648eorgQRGwNdL3JkJb0qHtaSl5/raAOvv+YqmGzZhFWcj3ib
+WUOWODdFlY3oUzpPjA+IeRzULya6//s8DhEKfVi2mJXc/sS6fE9J234IhKBysyr1
+cRIApw6OCr0V78TbHzEPh1z0QuMKY8hH0lz3JvQqGD59oTEdSJ5VVbmDLxqqmVtA
+/i4j2lYkDos2HvHGP7a/LC20FI0lOcSqazSeKc+y2Mand9tDXCU/dEEYMj1IW5rM
+z2+96XzCbJBesYFEEfWXG6XysP3UtE0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem.certspec
new file mode 100644
index 0000000000..aa682a7afd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-older.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:example.com
+validity:20150724-20160924
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem
new file mode 100644
index 0000000000..7ac56a0689
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUWxGwhSb8roUQoLNpJajl0X8jk10wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNjA4MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBAKxEoWUrHht8zENUlC/3tj/KbDSeaIy5nVE49aJRqHwuSczG5PTDn7sD
+rjl6Qq2rx3Z3ftZxJNIdpS3BUeUJ+FsQ2fK3ckDyeWkzxADlWp2l2bgKDtnW5Xjw
+lfzFt1Z/4PUmz0JyNGqijvpT/YYM9uM5OOKmKvy6C/6HiIMI2f5pVHM2bFWLuHLI
+p9yqp056EKPWCEQbkqseXLS0O8ZyxAyW4kHezUQzn1KFq9IGZu5sBZ8Imop0xnal
+2wXCuEisuAIOQNb1l9t+hf/P53+oRLjPbJzuzLZGSNA5PzoTbmZOqOdJS+kCNGCX
+IahLlU3dPILDlvl8DLFhk+lz9mA8x90=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec
new file mode 100644
index 0000000000..e38478165c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:example.com
+validity:20160824-20160924
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem
new file mode 100644
index 0000000000..ba11dcc152
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5TCCAc2gAwIBAgIUOWkKeR6blD0zzhElEnCm4eSqLTAwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNjA3MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjMDAuMCwGA1UdEQQl
+MCOkITAfMR0wGwYDVQQKDBRFeGFtcGxlIE9yZ2FuaXphdGlvbjANBgkqhkiG9w0B
+AQsFAAOCAQEACPZ4Ed3iONrIIKtg3865taT5TUj4Ts3tnYn4riFqojr6h/CHEDPR
+LStAJ4yYGQoTTzQzaxYjEZXHIEI/bCZ1VpGNpZt8DXWsJWwJgt8QUNeYT0eFFE2M
+CjrLJC1OIQRvlR89WEbk/q45KBQC0faeizqkAn+YUSG8mjHHbmSO8PWbh4z0YYlg
+BjwhTRFWUmWfQKC+mHyWblbYyFKlsFWf6cGOd4qE8N/hIz7oPzI4IPN1EQ/IfaQy
+WOOyxPZeu+J5VPlPE10nV5afoRcPLh/6vTwH85tPGzxWU4Jo4NDtFOtZAZR/S7sO
+OB/ST8Dbvs7qQFfRtgCtpEMicG7kxO1F+Q==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec
new file mode 100644
index 0000000000..41817bde75
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:example.com
+validity:20160724-20160924
+extension:subjectAlternativeName:/O=Example Organization
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem
new file mode 100644
index 0000000000..6882ba0554
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5TCCAc2gAwIBAgIUG1q3+RCKIizPHadkamCYLL7cp2swDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNTA3MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjMDAuMCwGA1UdEQQl
+MCOkITAfMR0wGwYDVQQKDBRFeGFtcGxlIE9yZ2FuaXphdGlvbjANBgkqhkiG9w0B
+AQsFAAOCAQEAqa/ayP8zXODUJJmuZLTJsdT2VaqbOh2WWk2bC2A82feGvCevLuL0
+0gPk+001Zx49Y1erMmt2M1cKtf2Lob8SGLNGUlJf2K7SNJTk7nyog+4UIlK4Hsxo
+CQ9sqKGJjkwHTDK1rB9gSW4e27Taj6tudnsUfgKWVzERxipebtLsCvz8Jfa0YLRI
+bGr6L4LoWiN2RNiK7/IOycfJ8VLDlBRouHD2Xfu0pFcvymMvfOC7GG4oBAacCKGn
+muUfoODgKw2yCIOSe7jsJOQ9yOp03sRipISfO0TALFzJX0V5+7CYDLZYR8vs/F/4
+0cIqkhwyCfUWYsGI4gPPR/nVxNvTRZhiFw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem.certspec
new file mode 100644
index 0000000000..65acf0b024
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-older.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:example.com
+validity:20150724-20160924
+extension:subjectAlternativeName:/O=Example Organization
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem
new file mode 100644
index 0000000000..cd87acfe48
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5TCCAc2gAwIBAgIUD1kNqQ0aKQ2TJjGUYztUh8I7j4AwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNjA4MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjMDAuMCwGA1UdEQQl
+MCOkITAfMR0wGwYDVQQKDBRFeGFtcGxlIE9yZ2FuaXphdGlvbjANBgkqhkiG9w0B
+AQsFAAOCAQEAU9n5I/Hdfod2fPBYWHZA/5/SabHkOBUHfrg6UUQYeHagVbqmoTQD
+M5F/DcDna+w6nHagIC/GRHBHkgY3Syh8QK5LnL3zi5tC0u4dzysDUjWtEAEgIcWA
+/pYtp6qZwJzxvn68PTnYnFDL61+LDLxlUBa2iRieRkCUOokCLL4ce3jsSTuJ+mGk
+XoaRrRREgtG5loYK8hFXM1RDkzyCa82DF/qD+iYgUJS9LMXrsksIRHP7Lqzhnwba
+Q4N8rgsBDFkNTEAmGcnTLMTlfO+SyKdHZI4n9VHdUQ1n38qFn70jj1YjcqEOFKXb
+wldSrYMedEOFVWyXaVWmxMwMTiDIKWMoxg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec
new file mode 100644
index 0000000000..140c201434
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:example.com
+validity:20160824-20160924
+extension:subjectAlternativeName:/O=Example Organization
diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js b/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js
new file mode 100644
index 0000000000..56de79ae7c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js
@@ -0,0 +1,295 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// The preference security.pki.name_matching_mode controls whether or not
+// mozilla::pkix will fall back to using a certificate's subject common name
+// during name matching. If the Baseline Requirements are followed, fallback
+// should not be necessary (because any name information in the subject common
+// name should be present in the subject alternative name extension). Due to
+// compatibility concerns, the platform can be configured to fall back for
+// certificates that are valid before 23 August 2016. Note that for certificates
+// issued by an imported root, the platform will fall back if necessary,
+// regardless of the value of the preference.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function certFromFile(certName) {
+ return constructCertFromFile(`test_baseline_requirements/${certName}.pem`);
+}
+
+function loadCertWithTrust(certName, trustString) {
+ addCertFromFile(
+ gCertDB,
+ `test_baseline_requirements/${certName}.pem`,
+ trustString
+ );
+}
+
+function checkCertOn25August2016(cert, expectedResult) {
+ // (new Date("2016-08-25T00:00:00Z")).getTime() / 1000
+ const VALIDATION_TIME = 1472083200;
+ return checkCertErrorGenericAtTime(
+ gCertDB,
+ cert,
+ expectedResult,
+ certificateUsageSSLServer,
+ VALIDATION_TIME,
+ false,
+ "example.com"
+ );
+}
+
+add_task(async function() {
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.pki.name_matching_mode");
+ Services.prefs.clearUserPref("security.test.built_in_root_hash");
+ Services.prefs.clearUserPref("privacy.reduceTimerPrecision");
+ });
+
+ Services.prefs.setBoolPref("privacy.reduceTimerPrecision", false);
+
+ loadCertWithTrust("ca", "CTu,,");
+
+ // When verifying a certificate, if the trust anchor is not a built-in root,
+ // name matching will fall back to using the subject common name if necessary
+ // (i.e. if there is no subject alternative name extension or it does not
+ // contain any dNSName or iPAddress entries). Thus, since imported roots are
+ // not in general treated as built-ins, these should all successfully verify
+ // regardless of the value of the pref.
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 0);
+ info("current mode: always fall back, root not built-in");
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(certFromFile("no-san-old"), PRErrorCodeSuccess);
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ PRErrorCodeSuccess
+ );
+
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 1);
+ info(
+ "current mode: fall back for notBefore < August 23, 2016, root " +
+ "not built-in"
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(certFromFile("no-san-old"), PRErrorCodeSuccess);
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ PRErrorCodeSuccess
+ );
+
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 2);
+ info(
+ "current mode: fall back for notBefore < August 23, 2015, root " +
+ "not built-in"
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(certFromFile("no-san-old"), PRErrorCodeSuccess);
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ PRErrorCodeSuccess
+ );
+
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 3);
+ info("current mode: never fall back, root not built-in");
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(certFromFile("no-san-old"), PRErrorCodeSuccess);
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ PRErrorCodeSuccess
+ );
+
+ // In debug builds, we can treat an imported root as a built-in, and thus we
+ // can actually test the different values of the pref.
+ if (isDebugBuild) {
+ let root = certFromFile("ca");
+ Services.prefs.setCharPref(
+ "security.test.built_in_root_hash",
+ root.sha256Fingerprint
+ );
+
+ // Always fall back if necessary.
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 0);
+ info("current mode: always fall back, root built-in");
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ PRErrorCodeSuccess
+ );
+
+ // Only fall back if notBefore < 23 August 2016
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 1);
+ info(
+ "current mode: fall back for notBefore < August 23, 2016, root " +
+ "built-in"
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ PRErrorCodeSuccess
+ );
+
+ // Only fall back if notBefore < 23 August 2015
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 2);
+ info(
+ "current mode: fall back for notBefore < August 23, 2015, root " +
+ "built-in"
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-old"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ PRErrorCodeSuccess
+ );
+
+ // Never fall back.
+ Services.prefs.setIntPref("security.pki.name_matching_mode", 3);
+ info("current mode: never fall back, root built-in");
+ await checkCertOn25August2016(
+ certFromFile("no-san-recent"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-old"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("no-san-older"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-recent"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-old"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ await checkCertOn25August2016(
+ certFromFile("san-contains-no-hostnames-older"),
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ }
+});
diff --git a/security/manager/ssl/tests/unit/test_blocklist_onecrl.js b/security/manager/ssl/tests/unit/test_blocklist_onecrl.js
new file mode 100644
index 0000000000..388816417a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_blocklist_onecrl.js
@@ -0,0 +1,75 @@
+"use strict";
+
+do_get_profile();
+
+const { Utils } = ChromeUtils.import("resource://services-settings/Utils.jsm");
+const { RemoteSettings } = ChromeUtils.import(
+ "resource://services-settings/remote-settings.js"
+);
+const { RemoteSecuritySettings } = ChromeUtils.import(
+ "resource://gre/modules/psm/RemoteSecuritySettings.jsm"
+);
+const { OneCRLBlocklistClient } = RemoteSecuritySettings.init();
+
+const global = this;
+
+add_task(async function test_uses_a_custom_signer() {
+ Assert.notEqual(
+ OneCRLBlocklistClient.signerName,
+ RemoteSettings("not-specified").signerName
+ );
+});
+
+add_task(async function test_has_initial_dump() {
+ Assert.ok(
+ await Utils.hasLocalDump(
+ OneCRLBlocklistClient.bucketName,
+ OneCRLBlocklistClient.collectionName
+ )
+ );
+});
+
+add_task(async function test_default_jexl_filter_is_used() {
+ Assert.deepEqual(
+ OneCRLBlocklistClient.filterFunc,
+ RemoteSettings("not-specified").filterFunc
+ );
+});
+
+add_task(
+ async function test_revocations_are_updated_on_sync_with_cert_storage() {
+ const certList = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+ const has_revocations = () =>
+ new Promise(resolve => {
+ certList.hasPriorData(
+ Ci.nsICertStorage.DATA_TYPE_REVOCATION,
+ (rv, hasPriorData) => {
+ if (rv == Cr.NS_OK) {
+ return resolve(hasPriorData);
+ }
+ return resolve(false);
+ }
+ );
+ });
+
+ Assert.ok(!(await has_revocations()));
+
+ await OneCRLBlocklistClient.emit("sync", {
+ data: {
+ current: [],
+ created: [
+ {
+ issuerName: "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=",
+ serialNumber: "a0X7/7DlTaedpgrIJg25iBPOkIM=",
+ },
+ ],
+ updated: [],
+ deleted: [],
+ },
+ });
+
+ Assert.ok(await has_revocations());
+ }
+);
diff --git a/security/manager/ssl/tests/unit/test_blocklist_pinning.js b/security/manager/ssl/tests/unit/test_blocklist_pinning.js
new file mode 100644
index 0000000000..87fd836f45
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_blocklist_pinning.js
@@ -0,0 +1,127 @@
+"use strict";
+
+const { Utils } = ChromeUtils.import("resource://services-settings/Utils.jsm");
+const { RemoteSettings } = ChromeUtils.import(
+ "resource://services-settings/remote-settings.js"
+);
+const { RemoteSecuritySettings } = ChromeUtils.import(
+ "resource://gre/modules/psm/RemoteSecuritySettings.jsm"
+);
+
+const sss = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+);
+
+const { PinningBlocklistClient } = RemoteSecuritySettings.init();
+
+add_task(async function test_uses_a_custom_signer() {
+ Assert.notEqual(
+ PinningBlocklistClient.signerName,
+ RemoteSettings("not-specified").signerName
+ );
+});
+
+add_task(async function test_pinning_has_initial_dump() {
+ if (AppConstants.platform == "android") {
+ // Skip test: we don't ship pinning dumps on Android (see package-manifest).
+ return;
+ }
+ Assert.ok(
+ await Utils.hasLocalDump(
+ PinningBlocklistClient.bucketName,
+ PinningBlocklistClient.collectionName
+ )
+ );
+});
+
+add_task(async function test_default_jexl_filter_is_used() {
+ Assert.deepEqual(
+ PinningBlocklistClient.filterFunc,
+ RemoteSettings("not-specified").filterFunc
+ );
+});
+
+add_task(async function test_no_pins_by_default() {
+ // ensure our pins are all missing before we start
+ ok(
+ !sss.isSecureURI(
+ sss.HEADER_HSTS,
+ Services.io.newURI("https://four.example.com"),
+ 0
+ )
+ );
+ ok(
+ !sss.isSecureURI(
+ sss.HEADER_HSTS,
+ Services.io.newURI("https://five.example.com"),
+ 0
+ )
+ );
+});
+
+add_task(async function test_multiple_entries() {
+ const current = [
+ {
+ pinType: "STSPin",
+ hostName: "five.example.com",
+ includeSubdomains: false,
+ expires: new Date().getTime() + 1000000,
+ versions: [Services.appinfo.version, "some version that won't match"],
+ },
+ ];
+ await PinningBlocklistClient.emit("sync", { data: { current } });
+
+ // Check that the HSTS preload added to the collection works...
+ ok(
+ sss.isSecureURI(
+ sss.HEADER_HSTS,
+ Services.io.newURI("https://five.example.com"),
+ 0
+ )
+ );
+ // // ...and that includeSubdomains is honored
+ ok(
+ !sss.isSecureURI(
+ sss.HEADER_HSTS,
+ Services.io.newURI("https://subdomain.five.example.com"),
+ 0
+ )
+ );
+
+ // Overwrite existing entries.
+ current[current.length - 1].includeSubdomains = true;
+ await PinningBlocklistClient.emit("sync", { data: { current } });
+ // The STS entry for five.example.com now has includeSubdomains set;
+ // ensure that the new includeSubdomains value is honored.
+ ok(
+ sss.isSecureURI(
+ sss.HEADER_HSTS,
+ Services.io.newURI("https://subdomain.five.example.com"),
+ 0
+ )
+ );
+});
+
+add_task(async function test_bad_entries() {
+ const current = [
+ {
+ pinType: "STSPin",
+ hostName: "five.example.com",
+ includeSubdomains: true,
+ expires: new Date().getTime() + 1000000,
+ }, // missing versions.
+ ];
+ // The event listener will catch any error, and won't throw.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1554939
+ await PinningBlocklistClient.emit("sync", { data: { current } });
+
+ // Check that the HSTS preload overwrites existing entries...
+ // Version field is missing.
+ ok(
+ !sss.isSecureURI(
+ sss.HEADER_HSTS,
+ Services.io.newURI("https://five.example.com"),
+ 0
+ )
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_broken_fips.js b/security/manager/ssl/tests/unit/test_broken_fips.js
new file mode 100644
index 0000000000..964092e2db
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_broken_fips.js
@@ -0,0 +1,64 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests that if Firefox attempts and fails to load a PKCS#11 module DB that was
+// in FIPS mode, Firefox can still make use of keys in the key database.
+// secomd.db can be created via `certutil -N -d <dir>`. Putting it in FIPS mode
+// involves running `modutil -fips true -dbdir <dir>`. key4.db is from
+// test_sdr_preexisting/key4.db.
+
+function run_test() {
+ // Append a single quote and non-ASCII characters to the profile path.
+ let env = Cc["@mozilla.org/process/environment;1"].getService(
+ Ci.nsIEnvironment
+ );
+ let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(profd);
+ file.append("'÷1");
+ env.set("XPCSHELL_TEST_PROFILE_DIR", file.path);
+
+ let profile = do_get_profile(); // must be called before getting nsIX509CertDB
+ Assert.ok(
+ /[^\x20-\x7f]/.test(profile.path),
+ "the profile path should contain a non-ASCII character"
+ );
+
+ let keyDBName = "key4.db";
+ let keyDBFile = do_get_file(`test_broken_fips/${keyDBName}`);
+ keyDBFile.copyTo(profile, keyDBName);
+
+ let pkcs11modDBName = "pkcs11.txt";
+ let pkcs11modDBFile = do_get_file(`test_broken_fips/${pkcs11modDBName}`);
+ pkcs11modDBFile.copyTo(profile, pkcs11modDBName);
+
+ let moduleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ ok(!moduleDB.isFIPSEnabled, "FIPS should not be enabled");
+
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ const encrypted =
+ "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d";
+ const expectedResult = "password";
+ let decrypted = sdr.decryptString(encrypted);
+ equal(
+ decrypted,
+ expectedResult,
+ "decrypted ciphertext should match expected plaintext"
+ );
+
+ let pkcs11modDBFileFIPS = do_get_profile();
+ pkcs11modDBFileFIPS.append(`${pkcs11modDBName}.fips`);
+ ok(
+ pkcs11modDBFileFIPS.exists(),
+ "backed-up PKCS#11 module db should now exist"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_broken_fips/key4.db b/security/manager/ssl/tests/unit/test_broken_fips/key4.db
new file mode 100644
index 0000000000..8f320dfdbd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_broken_fips/key4.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_broken_fips/pkcs11.txt b/security/manager/ssl/tests/unit/test_broken_fips/pkcs11.txt
new file mode 100644
index 0000000000..78a11f5fa7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_broken_fips/pkcs11.txt
@@ -0,0 +1,5 @@
+library=
+name=NSS Internal FIPS PKCS #11 Module
+parameters=configdir='.' certPrefix='' keyPrefix='' secmod='' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
+NSS=slotParams={0x00000003=[slotFlags=RSA,RC4,RC2,DES,DH,SHA1,MD5,MD2,SSL,TLS,AES,SHA256,SHA512,Camellia,SEED,RANDOM ] } Flags=internal,FIPS,critical
+
diff --git a/security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js
new file mode 100644
index 0000000000..3bcbc52f2b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12.js
@@ -0,0 +1,57 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests exporting a certificate and key as a PKCS#12 blob and importing it
+// again with a new password set.
+
+do_get_profile();
+
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const PKCS12_FILE = "test_certDB_import/cert_from_windows.pfx";
+const CERT_COMMON_NAME = "test_cert_from_windows";
+const TEST_CERT_PASSWORD = "黒い";
+const TEST_OUTPUT_PASSWORD = "other password";
+
+function findCertByCommonName(commonName) {
+ for (let cert of gCertDB.getCerts()) {
+ if (cert.commonName == commonName) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+function run_test() {
+ // Import the certificate and key so we have something to export.
+ let cert = findCertByCommonName(CERT_COMMON_NAME);
+ equal(cert, null, "cert should not be found before import");
+ let certFile = do_get_file(PKCS12_FILE);
+ ok(certFile, `${PKCS12_FILE} should exist`);
+ let errorCode = gCertDB.importPKCS12File(certFile, TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should be imported");
+ cert = findCertByCommonName(CERT_COMMON_NAME);
+ notEqual(cert, null, "cert should be found now");
+
+ // Export the certificate and key.
+ let output = do_get_tempdir();
+ output.append("output.p12");
+ ok(!output.exists(), "output shouldn't exist before exporting PKCS12 file");
+ errorCode = gCertDB.exportPKCS12File(output, [cert], TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should be exported");
+ ok(output.exists(), "output should exist after exporting PKCS12 file");
+
+ // We should be able to import the exported blob again using the new password.
+ errorCode = gCertDB.importPKCS12File(output, TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should be imported");
+ output.remove(false /* not a directory; recursive doesn't apply */);
+
+ // Ideally there would be some way to confirm that this actually did anything.
+ // Unfortunately, since deleting a certificate currently doesn't actually do
+ // anything until the platform is restarted, we can't confirm that we
+ // successfully re-imported the certificate.
+}
diff --git a/security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js
new file mode 100644
index 0000000000..598d159c95
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_export_pkcs12_with_master_password.js
@@ -0,0 +1,117 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests exporting a certificate and key as a PKCS#12 blob if the user has a
+// master password set.
+
+do_get_profile();
+
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const PKCS12_FILE = "test_certDB_import/cert_from_windows.pfx";
+const CERT_COMMON_NAME = "test_cert_from_windows";
+const TEST_CERT_PASSWORD = "黒い";
+
+var gPrompt = {
+ password: "password",
+ clickOk: true,
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
+
+ // This intentionally does not use arrow function syntax to avoid an issue
+ // where in the context of the arrow function, |this != gPrompt| due to
+ // how objects get wrapped when going across xpcom boundaries.
+ alert(title, text) {
+ info(`alert('${text}')`);
+ ok(false, "not expecting alert() to be called");
+ },
+
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ equal(
+ text,
+ "Please enter your Primary Password.",
+ "password prompt text should be as expected"
+ );
+ equal(checkMsg, null, "checkMsg should be null");
+ password.value = this.password;
+ return this.clickOk;
+ },
+};
+
+const gPromptFactory = {
+ QueryInterface: ChromeUtils.generateQI(["nsIPromptFactory"]),
+ getPrompt: (aWindow, aIID) => gPrompt,
+};
+
+function findCertByCommonName(commonName) {
+ for (let cert of gCertDB.getCerts()) {
+ if (cert.commonName == commonName) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+function run_test() {
+ let promptFactoryCID = MockRegistrar.register(
+ "@mozilla.org/prompter;1",
+ gPromptFactory
+ );
+
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(promptFactoryCID);
+ });
+
+ // Set a master password.
+ let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
+ Ci.nsIPK11TokenDB
+ );
+ let token = tokenDB.getInternalKeyToken();
+ token.initPassword("password");
+ token.logoutSimple();
+
+ // Import the certificate and key so we have something to export.
+ let cert = findCertByCommonName(CERT_COMMON_NAME);
+ equal(cert, null, "cert should not be found before import");
+ let certFile = do_get_file(PKCS12_FILE);
+ ok(certFile, `${PKCS12_FILE} should exist`);
+ let errorCode = gCertDB.importPKCS12File(certFile, TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should import");
+ cert = findCertByCommonName(CERT_COMMON_NAME);
+ notEqual(cert, null, "cert should be found now");
+
+ // Log out so we're prompted for the password.
+ token.logoutSimple();
+
+ // Export the certificate and key (and don't cancel the password request
+ // dialog).
+ let output = do_get_tempdir();
+ output.append("output.p12");
+ ok(!output.exists(), "output shouldn't exist before exporting PKCS12 file");
+ errorCode = gCertDB.exportPKCS12File(output, [cert], TEST_CERT_PASSWORD);
+ equal(errorCode, Ci.nsIX509CertDB.Success, "cert should export");
+ ok(output.exists(), "output should exist after exporting PKCS12 file");
+ output.remove(false /* not a directory; recursive doesn't apply */);
+
+ // Log out again so we're prompted for the password.
+ token.logoutSimple();
+
+ // Attempt to export the certificate and key, but this time cancel the
+ // password request dialog. The export operation should also be canceled.
+ gPrompt.clickOk = false;
+ let output2 = do_get_tempdir();
+ output2.append("output2.p12");
+ ok(!output2.exists(), "output2 shouldn't exist before exporting PKCS12 file");
+ errorCode = gCertDB.exportPKCS12File(output, [cert], TEST_CERT_PASSWORD);
+ equal(
+ errorCode,
+ Ci.nsIX509CertDB.ERROR_PKCS12_BACKUP_FAILED,
+ "cert should not export"
+ );
+
+ ok(!output2.exists(), "output2 shouldn't exist after failing to export");
+}
diff --git a/security/manager/ssl/tests/unit/test_certDB_import.js b/security/manager/ssl/tests/unit/test_certDB_import.js
new file mode 100644
index 0000000000..76bf968970
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import.js
@@ -0,0 +1,217 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the various nsIX509CertDB import methods.
+
+do_get_profile();
+
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const CA_CERT_COMMON_NAME = "importedCA";
+const TEST_EMAIL_ADDRESS = "test@example.com";
+
+let gCACertImportDialogCount = 0;
+
+// Mock implementation of nsICertificateDialogs.
+const gCertificateDialogs = {
+ confirmDownloadCACert: (ctx, cert, trust) => {
+ gCACertImportDialogCount++;
+ equal(
+ cert.commonName,
+ CA_CERT_COMMON_NAME,
+ "CA cert to import should have the correct CN"
+ );
+ trust.value = Ci.nsIX509CertDB.TRUSTED_EMAIL;
+ return true;
+ },
+ setPKCS12FilePassword: (ctx, password) => {
+ // This is only relevant to exporting.
+ ok(false, "setPKCS12FilePassword() should not have been called");
+ },
+ getPKCS12FilePassword: (ctx, password) => {
+ // We don't test anything that calls this method yet.
+ ok(false, "getPKCS12FilePassword() should not have been called");
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsICertificateDialogs"]),
+};
+
+// Implements nsIInterfaceRequestor. Mostly serves to mock nsIPrompt.
+const gInterfaceRequestor = {
+ alert: (title, text) => {
+ // We don't test anything that calls this method yet.
+ ok(false, `alert() should not have been called: ${text}`);
+ },
+
+ getInterface: iid => {
+ if (iid.equals(Ci.nsIPrompt)) {
+ return this;
+ }
+
+ throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
+ },
+};
+
+function getCertAsByteArray(certPath) {
+ let certFile = do_get_file(certPath, false);
+ let certBytes = readFile(certFile);
+
+ let byteArray = [];
+ for (let i = 0; i < certBytes.length; i++) {
+ byteArray.push(certBytes.charCodeAt(i));
+ }
+
+ return byteArray;
+}
+
+function commonFindCertBy(propertyName, value) {
+ for (let cert of gCertDB.getCerts()) {
+ if (cert[propertyName] == value) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+function findCertByCommonName(commonName) {
+ return commonFindCertBy("commonName", commonName);
+}
+
+function findCertByEmailAddress(emailAddress) {
+ return commonFindCertBy("emailAddress", emailAddress);
+}
+
+function testImportCACert() {
+ // Sanity check the CA cert is missing.
+ equal(
+ findCertByCommonName(CA_CERT_COMMON_NAME),
+ null,
+ "CA cert should not be in the database before import"
+ );
+
+ // Import and check for success.
+ let caArray = getCertAsByteArray("test_certDB_import/importedCA.pem");
+ gCertDB.importCertificates(
+ caArray,
+ caArray.length,
+ Ci.nsIX509Cert.CA_CERT,
+ gInterfaceRequestor
+ );
+ equal(
+ gCACertImportDialogCount,
+ 1,
+ "Confirmation dialog for the CA cert should only be shown once"
+ );
+
+ let caCert = findCertByCommonName(CA_CERT_COMMON_NAME);
+ notEqual(caCert, null, "CA cert should now be found in the database");
+ ok(
+ gCertDB.isCertTrusted(
+ caCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_EMAIL
+ ),
+ "CA cert should be trusted for e-mail"
+ );
+}
+
+function testImportEmptyCertPackage() {
+ // Because this is an empty cert package, nothing will be imported. We know it succeeded if no errors are thrown.
+ let byteArray = [
+ 0x30,
+ 0x0f,
+ 0x06,
+ 0x09,
+ 0x60,
+ 0x86,
+ 0x48,
+ 0x01,
+ 0x86,
+ 0xf8,
+ 0x42,
+ 0x02,
+ 0x05,
+ 0xa0,
+ 0x02,
+ 0x30,
+ 0x00,
+ ];
+ gCertDB.importCertificates(
+ byteArray,
+ byteArray.length,
+ Ci.nsIX509Cert.CA_CERT,
+ gInterfaceRequestor
+ );
+}
+
+function testImportEmptyUserCert() {
+ // Because this is an empty cert package, nothing will be imported. We know it succeeded if no errors are thrown.
+ let byteArray = [
+ 0x30,
+ 0x0f,
+ 0x06,
+ 0x09,
+ 0x60,
+ 0x86,
+ 0x48,
+ 0x01,
+ 0x86,
+ 0xf8,
+ 0x42,
+ 0x02,
+ 0x05,
+ 0xa0,
+ 0x02,
+ 0x30,
+ 0x00,
+ ];
+ gCertDB.importUserCertificate(
+ byteArray,
+ byteArray.length,
+ gInterfaceRequestor
+ );
+}
+
+function run_test() {
+ let certificateDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsCertificateDialogs;1",
+ gCertificateDialogs
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(certificateDialogsCID);
+ });
+
+ // Sanity check the e-mail cert is missing.
+ equal(
+ findCertByEmailAddress(TEST_EMAIL_ADDRESS),
+ null,
+ "E-mail cert should not be in the database before import"
+ );
+
+ // Import the CA cert so that the e-mail import succeeds.
+ testImportCACert();
+ testImportEmptyCertPackage();
+ testImportEmptyUserCert();
+
+ // Import the e-mail cert and check for success.
+ let emailArray = getCertAsByteArray("test_certDB_import/emailEE.pem");
+ gCertDB.importEmailCertificate(
+ emailArray,
+ emailArray.length,
+ gInterfaceRequestor
+ );
+ let emailCert = findCertByEmailAddress(TEST_EMAIL_ADDRESS);
+ notEqual(emailCert, null, "E-mail cert should now be found in the database");
+ let bundle = Services.strings.createBundle(
+ "chrome://pipnss/locale/pipnss.properties"
+ );
+ equal(
+ emailCert.tokenName,
+ bundle.GetStringFromName("PrivateTokenDescription"),
+ "cert's tokenName should be the expected localized value"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows.pfx b/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows.pfx
new file mode 100644
index 0000000000..e969d672d7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows.pfx
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_emptypass.pfx b/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_emptypass.pfx
new file mode 100644
index 0000000000..879d424b85
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_emptypass.pfx
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_nopass.pfx b/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_nopass.pfx
new file mode 100644
index 0000000000..7dcd668121
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/cert_from_windows_nopass.pfx
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem b/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem
new file mode 100644
index 0000000000..136356c362
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAgIUZ0427vGPMGf5dB3ZIhW8mkoNr04wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaW1wb3J0ZWRDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIw
+MjIwMjA1MDAwMDAwWjAhMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29t
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+jQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBi4qlU7QLKNNAFYso7DWFZzptHNiR5
+5oOGSbUPu8pvi8YoJbyzefH7A9kiIyvdZNkBKPUZTwwFWKM+QwmZVrH8e7G3zI/G
+447HiUQ4VNeeAYkT3K126nxEhVsk/gWPfv7qv3jUQcii/3PlUhHNqVRZNcEe/u4D
+zwWp/j56ydzmQoWwBN/pal//uQOjum9PPpk+V1RYCDtekOHwOHQsuhKXTscMcamy
+SJmG06/O3JF8yhjq03X1cHtj3YF2PH0fTCcgZMv/3GY34kZ61A4VJWUWRZFoCujy
+Fx8jMEoMvJt/oIBggNJsDCBP9ksesfA5ImOqI5hziqo52oHG0Z9+0f1D
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem.certspec b/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem.certspec
new file mode 100644
index 0000000000..0528bc624a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/emailEE.pem.certspec
@@ -0,0 +1,2 @@
+issuer:importedCA
+subject:/emailAddress=test@example.com
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem b/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem
new file mode 100644
index 0000000000..6c8ab901ae
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICzDCCAbSgAwIBAgIUUQGnWi0czVZBST5XDA1wp0G74kowDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKaW1wb3J0ZWRDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIw
+MjIwMjA1MDAwMDAwWjAVMRMwEQYDVQQDDAppbXBvcnRlZENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1
+aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/we
+adA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSS
+pH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62W
+YVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauR
+CE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoxAwDjAM
+BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCkMue2IDPldrL6f/ox5CoU
+RPLnEPQyUDYIz4eFdAS/PX2A1/KEvYthVuVNbGZ1r5/3htt4cOKJCUYI/LWKc4eB
+6gC9uhcC6JBqRfsQB4zK9RZu860shuNPGuEWA1KYgy1bzi8iJ2kTaHO0y3yXL+ow
+guKrFJTK5NWPnxWQsdLE5lA9p67bNHjWxDzRHkDqeyIPiG/zmrudprxWXHk+EQgx
+1hKN0rTaSAZnCyeik11i7MCyAnvrAk0mr9j8jX1+yf5mbFeKk0XcjSRmzoAQZBSA
+9br5LUP1CvN2DbphDK1jkgP2Dx0dBuQH4HZBbY6lcKGfi4mdiJUkwiwqp346q7vo
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem.certspec b/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem.certspec
new file mode 100644
index 0000000000..b168253544
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/importedCA.pem.certspec
@@ -0,0 +1,3 @@
+issuer:importedCA
+subject:importedCA
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_certDB_import/moz.build b/security/manager/ssl/tests/unit/test_certDB_import/moz.build
new file mode 100644
index 0000000000..dfc75de914
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See Bug 1256495.
+# test_certificates = (
+# 'emailEE.pem',
+# 'importedCA.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js b/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
new file mode 100644
index 0000000000..a295d424ff
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import_pkcs12.js
@@ -0,0 +1,117 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests import PKCS12 file by nsIX509CertDB.
+
+do_get_profile();
+
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const PKCS12_FILE = "test_certDB_import/cert_from_windows.pfx";
+const PKCS12_FILE_EMPTY_PASS =
+ "test_certDB_import/cert_from_windows_emptypass.pfx";
+const PKCS12_FILE_NO_PASS = "test_certDB_import/cert_from_windows_nopass.pfx";
+const CERT_COMMON_NAME = "test_cert_from_windows";
+const TEST_CERT_PASSWORD = "黒い";
+
+// Has getPKCS12FilePassword been called since we last reset this?
+let gGetPKCS12FilePasswordCalled = false;
+let gCurrentTestcase = null;
+
+let gTestcases = [
+ // Test that importing a PKCS12 file with the wrong password fails.
+ {
+ name: "import using incorrect password",
+ filename: PKCS12_FILE,
+ passwordToUse: "this is the wrong password",
+ successExpected: false,
+ errorCode: Ci.nsIX509CertDB.ERROR_BAD_PASSWORD,
+ checkCertExist: true,
+ },
+ // Test that importing something that isn't a PKCS12 file fails.
+ {
+ name: "import non-PKCS12 file",
+ filename: "test_certDB_import_pkcs12.js",
+ passwordToUse: TEST_CERT_PASSWORD,
+ successExpected: false,
+ errorCode: Ci.nsIX509CertDB.ERROR_DECODE_ERROR,
+ checkCertExist: true,
+ },
+ // Test that importing a PKCS12 file with the correct password succeeds.
+ // This needs to be last because currently there isn't a way to delete the
+ // imported certificate (and thus reset the test state) that doesn't depend on
+ // the garbage collector running.
+ {
+ name: "import PKCS12 file",
+ filename: PKCS12_FILE,
+ passwordToUse: TEST_CERT_PASSWORD,
+ successExpected: true,
+ errorCode: Ci.nsIX509CertDB.Success,
+ checkCertExist: true,
+ },
+ // Same cert file protected with empty string password
+ {
+ name: "import PKCS12 file empty password",
+ filename: PKCS12_FILE_EMPTY_PASS,
+ passwordToUse: "",
+ successExpected: true,
+ errorCode: Ci.nsIX509CertDB.Success,
+ checkCertExist: false,
+ },
+ // Same cert file protected with no password
+ {
+ name: "import PKCS12 file no password",
+ filename: PKCS12_FILE_NO_PASS,
+ passwordToUse: null,
+ successExpected: true,
+ errorCode: Ci.nsIX509CertDB.Success,
+ checkCertExist: false,
+ },
+];
+
+function doesCertExist(commonName) {
+ let allCerts = gCertDB.getCerts();
+ for (let cert of allCerts) {
+ if (cert.isBuiltInRoot) {
+ continue;
+ }
+ if (cert.commonName == commonName) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function runOneTestcase(testcase) {
+ info(`running ${testcase.name}`);
+ if (testcase.checkCertExist) {
+ ok(
+ !doesCertExist(CERT_COMMON_NAME),
+ "cert should not be in the database before import"
+ );
+ }
+
+ // Import and check for failure.
+ let certFile = do_get_file(testcase.filename);
+ ok(certFile, `${testcase.filename} should exist`);
+ gGetPKCS12FilePasswordCalled = false;
+ gCurrentTestcase = testcase;
+ let errorCode = gCertDB.importPKCS12File(certFile, testcase.passwordToUse);
+ equal(errorCode, testcase.errorCode, `verifying error code`);
+ equal(
+ doesCertExist(CERT_COMMON_NAME),
+ testcase.successExpected,
+ `cert should${testcase.successExpected ? "" : " not"} be found now`
+ );
+}
+
+function run_test() {
+ for (let testcase of gTestcases) {
+ runOneTestcase(testcase);
+ }
+}
diff --git a/security/manager/ssl/tests/unit/test_certDB_import_with_master_password.js b/security/manager/ssl/tests/unit/test_certDB_import_with_master_password.js
new file mode 100644
index 0000000000..e31dc605b8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_certDB_import_with_master_password.js
@@ -0,0 +1,148 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that a CA certificate can still be imported if the user has a master
+// password set.
+
+do_get_profile();
+
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const CA_CERT_COMMON_NAME = "importedCA";
+
+let gCACertImportDialogCount = 0;
+
+// Mock implementation of nsICertificateDialogs.
+const gCertificateDialogs = {
+ confirmDownloadCACert: (ctx, cert, trust) => {
+ gCACertImportDialogCount++;
+ equal(
+ cert.commonName,
+ CA_CERT_COMMON_NAME,
+ "CA cert to import should have the correct CN"
+ );
+ trust.value = Ci.nsIX509CertDB.TRUSTED_EMAIL;
+ return true;
+ },
+ setPKCS12FilePassword: (ctx, password) => {
+ // This is only relevant to exporting.
+ ok(false, "setPKCS12FilePassword() should not have been called");
+ },
+ getPKCS12FilePassword: (ctx, password) => {
+ // We don't test anything that calls this method yet.
+ ok(false, "getPKCS12FilePassword() should not have been called");
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsICertificateDialogs"]),
+};
+
+var gMockPrompter = {
+ passwordToTry: "password",
+ numPrompts: 0,
+
+ // This intentionally does not use arrow function syntax to avoid an issue
+ // where in the context of the arrow function, |this != gMockPrompter| due to
+ // how objects get wrapped when going across xpcom boundaries.
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ this.numPrompts++;
+ if (this.numPrompts > 1) {
+ // don't keep retrying a bad password
+ return false;
+ }
+ equal(
+ text,
+ "Please enter your Primary Password.",
+ "password prompt text should be as expected"
+ );
+ equal(checkMsg, null, "checkMsg should be null");
+ ok(this.passwordToTry, "passwordToTry should be non-null");
+ password.value = this.passwordToTry;
+ return true;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
+
+ // Again with the arrow function issue.
+ getInterface(iid) {
+ if (iid.equals(Ci.nsIPrompt)) {
+ return this;
+ }
+
+ throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
+ },
+};
+
+function getCertAsByteArray(certPath) {
+ let certFile = do_get_file(certPath, false);
+ let certBytes = readFile(certFile);
+
+ let byteArray = [];
+ for (let i = 0; i < certBytes.length; i++) {
+ byteArray.push(certBytes.charCodeAt(i));
+ }
+
+ return byteArray;
+}
+
+function findCertByCommonName(commonName) {
+ for (let cert of gCertDB.getCerts()) {
+ if (cert.commonName == commonName) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+function run_test() {
+ let certificateDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsCertificateDialogs;1",
+ gCertificateDialogs
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(certificateDialogsCID);
+ });
+
+ // Set a master password.
+ let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
+ Ci.nsIPK11TokenDB
+ );
+ let token = tokenDB.getInternalKeyToken();
+ token.initPassword("password");
+ token.logoutSimple();
+
+ // Sanity check the CA cert is missing.
+ equal(
+ findCertByCommonName(CA_CERT_COMMON_NAME),
+ null,
+ "CA cert should not be in the database before import"
+ );
+
+ // Import and check for success.
+ let caArray = getCertAsByteArray("test_certDB_import/importedCA.pem");
+ gCertDB.importCertificates(
+ caArray,
+ caArray.length,
+ Ci.nsIX509Cert.CA_CERT,
+ gMockPrompter
+ );
+ equal(
+ gCACertImportDialogCount,
+ 1,
+ "Confirmation dialog for the CA cert should only be shown once"
+ );
+
+ let caCert = findCertByCommonName(CA_CERT_COMMON_NAME);
+ notEqual(caCert, null, "CA cert should now be found in the database");
+ ok(
+ gCertDB.isCertTrusted(
+ caCert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_EMAIL
+ ),
+ "CA cert should be trusted for e-mail"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_chains.js b/security/manager/ssl/tests/unit/test_cert_chains.js
new file mode 100644
index 0000000000..56c68a049a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_chains.js
@@ -0,0 +1,814 @@
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// 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/.
+
+"use strict";
+
+function test_cert_equals() {
+ let certA = constructCertFromFile("bad_certs/default-ee.pem");
+ let certB = constructCertFromFile("bad_certs/default-ee.pem");
+ let certC = constructCertFromFile("bad_certs/expired-ee.pem");
+
+ ok(
+ certA != certB,
+ "Cert objects constructed from the same file should not be equal" +
+ " according to the equality operators"
+ );
+ ok(
+ certA.equals(certB),
+ "equals() on cert objects constructed from the same cert file should" +
+ " return true"
+ );
+ ok(
+ !certA.equals(certC),
+ "equals() on cert objects constructed from files for different certs" +
+ " should return false"
+ );
+}
+
+// We hard-code the following certificates for the pkcs7 export tests so that we
+// don't have to change the test data when the certificates change each year.
+// Luckily these tests don't depend on the certificates being valid, so it's ok
+// to let them expire.
+const gDefaultEEPEM = `-----BEGIN CERTIFICATE-----
+MIIDiTCCAnGgAwIBAgIUDUo/9G0rz7fJiWTw0hY6TIyPRSIwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE3MTEyNzAwMDAwMFoYDzIwMjAw
+MjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcow
+gccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tghUqLnBp
+bm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcu
+ZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBs
+ZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxo
+b3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCkguNhMyVCYhyYXfE22wNvlaob
+K2YRb4OGMxySIKuQ80N0XlO+xpLJTs9YzFVY1+JTHNez1QfwP9KJeZznTzVzLh4s
+v0swx/+oUxCfLb0VIl/kdUqLkbGYrAmtjeOKZLaqVtRH0BnmbPowLak1pi6nQYOU
++aL9QOuvT/j3rXoimcdo6X3TK1SN2/64fGMyG/pwas+JXehbReUf4n1ewk84ADtb
++ew8tRAKf/uxzKUj5t/UgqDsnTWq5wUc5IJKwoHT41sQnNqPg12x4+WGWiAsWCpR
+/hKYHFGr7rb4JTGEPAJpWcv9WtZYAvwT78a2xpHp5XNglj16IjWEukvJuU1W
+-----END CERTIFICATE-----`;
+
+const gExpiredEEPEM = `-----BEGIN CERTIFICATE-----
+MIIDHDCCAgSgAwIBAgIUY9ERAIKj0js/YbhJoMrcLnj++uowDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDEzMDEwMTAwMDAwMFoYDzIwMTQw
+MTAxMDAwMDAwWjAiMSAwHgYDVQQDDBdFeHBpcmVkIFRlc3QgRW5kLWVudGl0eTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAAaNWMFQwHgYDVR0RBBcwFYITZXhwaXJlZC5leGFtcGxlLmNvbTAyBggrBgEF
+BQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJ
+KoZIhvcNAQELBQADggEBAImiFuy275T6b+Ud6gl/El6qpgWHUXeYiv2sp7d+HVzf
+T+ow5WVsxI/GMKhdA43JaKT9gfMsbnP1qiI2zel3U+F7IAMO1CEr5FVdCOVTma5h
+mu/81rkJLmZ8RQDWWOhZKyn/7aD7TH1C1e768yCt5E2DDl8mHil9zR8BPsoXwuS3
+L9zJ2JqNc60+hB8l297ZaSl0nbKffb47ukvn5kSJ7tI9n/fSXdj1JrukwjZP+74V
+kQyNobaFzDZ+Zr3QmfbejEsY2EYnq8XuENgIO4DuYrm80/p6bMO6laB0Uv5W6uXZ
+gBZdRTe1WMdYWGhmvnFFQmf+naeOOl6ryFwWwtnoK7I=
+-----END CERTIFICATE-----`;
+
+const gTestCAPEM = `-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUKaFwIwCwHXUgKRuOhAX4pjYsmbgwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE3MTEyNzAwMDAwMFoYDzIwMjAw
+MjA1MDAwMDAwWjASMRAwDgYDVQQDDAdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRME
+BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAIMgnywHFbPzJ
+BEcbpx/aWQOI2tUFlo7MUoPSoACHzoI/HOUTx25eKHlpNK2jSljLufhUd//eCCXg
++OQt4f2N/tRw8gumbs3YDF7+t3ZNGt+iQxZTwN7MKsGIZy+6R523XHw8lpzFX5iz
+XgIS+0APlX+XyZk7MRCcBWh6PSaSqEOOvUXVp6Omh3it034kBWnm809TEWmwiVw3
+ssPDmpUCArdDNMMdvQehzaH96cdjcSsguqpX9NcMDUmmiG7HLQ2iy+WSzek9S46S
+bKKDLw8Ebevfkl6PEpg+GDulq+EPXayN3AsFXkF8MaFLgfeprkENjN1g4jM+WSyN
+6DC7vCkj7A==
+-----END CERTIFICATE-----`;
+
+const gUnknownIssuerPEM = `
+-----BEGIN CERTIFICATE-----
+MIIDqTCCApGgAwIBAgIUMRiJ9TrwqTOoVFU+j5FDWDWS1X8wDQYJKoZIhvcNAQEL
+BQAwJjEkMCIGA1UEAwwbVGVzdCBJbnRlcm1lZGlhdGUgdG8gZGVsZXRlMCIYDzIw
+MTcxMTI3MDAwMDAwWhgPMjAyMDAyMDUwMDAwMDBaMC4xLDAqBgNVBAMMI1Rlc3Qg
+RW5kLWVudGl0eSBmcm9tIHVua25vd24gaXNzdWVyMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4HCMIG/MIGIBgNV
+HREEgYAwfoIZdW5rbm93bmlzc3Vlci5leGFtcGxlLmNvbYI0dW5rbm93bmlzc3Vl
+ci5pbmNsdWRlLXN1YmRvbWFpbnMucGlubmluZy5leGFtcGxlLmNvbYIrdW5rbm93
+bmlzc3Vlci50ZXN0LW1vZGUucGlubmluZy5leGFtcGxlLmNvbTAyBggrBgEFBQcB
+AQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZI
+hvcNAQELBQADggEBALAnJjBJ+MOc7kMRzmESYZRSxKak7A1K67xBXWzWmK3t3WXv
+e/RLjV/RhbyTN20h2ZjSVcuDzgNYC/RJ/z3Xd5Q9QEGoi1ly84HeaeHw/3kUSHxv
+J3JnbPu2lk96U5y7tXEVfbEVZYpx4Us72fuURPWriVldILH2lgrEg+iKZWbY/wcT
+vfu1j/flMkGEOpc1HytlmR9fkCDnqzFfcmv7Eh3X1BiSBOIemGnUHxONwlthSE68
+IItE5l3c82G8oQGmve6r0N9h7t6opIjH1koFWMck/pzDA01FmWey4ASdlmjE8NSJ
+Al1zsF8EiLOZeI1rvurcXwVOd0Olk9/QT5hwTkk=
+-----END CERTIFICATE-----`;
+
+const gOCSPEEWithIntermediatePEM = `
+-----BEGIN CERTIFICATE-----
+MIIDNTCCAh2gAwIBAgIUZ67hS7lHVnCQtXx7oXFlzihqh0cwDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUwIhgPMjAxNzExMjcwMDAw
+MDBaGA8yMDIwMDIwNTAwMDAwMFowLDEqMCgGA1UEAwwhVGVzdCBFbmQtZW50aXR5
+IHdpdGggSW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo1swWTAjBgNVHREEHDAagglsb2NhbGhv
+c3SCDSouZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZo
+dHRwOi8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQAo043hM4Gi
+UtoXKOQB2v0C8nF4Yyzpf+i0LlxQCFZkiLYu9pIuQu16I3TbLQRBwhCC0ml7TqJB
+AbryzILTorCQP8A1WQa1kt6cb30jCyXLcWnDA/ULPexn9cYm6I0YyLFlnkcVzMGL
+Fc+LyWTAPEW5rMauu5iOOp/6L5rBF0M9bg5yXSGNDv8gk3Jc+opJbBDTrAuKDNLp
+JSEp4rqovNFnirzlJWDS+ScAsWHtoLcrH6gnQRPsEV1WFQnYr3HkAakYQok9xs5A
+ikBS6mgz4/cFBts8bSGSuXxctkN2Ss7Y5l3YmTYKCxPz6retVfrhi/islH4W3z9H
+pu3ZqyACO6Lb
+-----END CERTIFICATE-----`;
+
+const gTestIntPEM = `
+-----BEGIN CERTIFICATE-----
+MIIC3TCCAcWgAwIBAgIUa0X7/7DlTaedpgrIJg25iBPOkIMwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE1MDEwMTAwMDAwMFoYDzIwMjUw
+MTAxMDAwMDAwWjAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1
+SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+
+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYL
+K7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwc
+bJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibW
+JZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMd
+MBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEB
+AILNZM9yT9ylMpjyi0tXaDORzpHiJ8vEoVKk98bC2BQF0kMEEB547p+Ms8zdJY00
+Bxe9qigT8rQwKprXq5RvgIZ32QLn/yMPiCp/e6zBdsx77TkfmnSnxvPi+0nlA+eM
+8JYN0UST4vWD4vPPX9GgZDVoGQTiF3hUivJ5R8sHb/ozcSukMKQQ22+AIU7w6wyA
+IbCAG7Pab4k2XFAeEnUZsl9fCym5jsPN9Pnv9rlBi6h8shHw1R2ROXjgxubjiMr3
+B456vFTJImLJjyA1iTSlr/+VXGUYg6Z0/HYnsO00+8xUKM71dPxGAfIFNaSscpyk
+rGFLvocT/kym6r8galxCJUo=
+-----END CERTIFICATE-----`;
+
+function build_cert_list_from_pem_list(pemList) {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let certList = [];
+ for (let pem of pemList) {
+ let cert = certdb.constructX509FromBase64(pemToBase64(pem));
+ certList.push(cert);
+ }
+ return certList;
+}
+
+function test_cert_pkcs7_export() {
+ // This was generated by running BadCertAndPinningServer locally on the bad_certs
+ // directory and visiting:
+ // https://good.include-subdomains.pinning.example.com:8443/
+ // and then viewing the certificate chain presented (in the page info dialog)
+ // and exporting it.
+ // (NB: test-ca must be imported and trusted for the connection to succeed)
+ const expectedPKCS7ForDefaultEE =
+ "MIAGCSqGSIb3DQEHAqCAMIACAQExADCABgkqhkiG9w0BBwEAAKCCBmQwggLTMIIBu6ADAgE" +
+ "CAhQpoXAjALAddSApG46EBfimNiyZuDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZX" +
+ "N0IENBMCIYDzIwMTcxMTI3MDAwMDAwWhgPMjAyMDAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB" +
+ "1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI" +
+ "BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xz" +
+ "VJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCy" +
+ "uwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW" +
+ "7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE" +
+ "LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8" +
+ "wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAgyCfLAcVs/MkERxunH9pZA4ja1Q" +
+ "WWjsxSg9KgAIfOgj8c5RPHbl4oeWk0raNKWMu5+FR3/94IJeD45C3h/Y3+1HDyC6ZuzdgMX" +
+ "v63dk0a36JDFlPA3swqwYhnL7pHnbdcfDyWnMVfmLNeAhL7QA+Vf5fJmTsxEJwFaHo9JpKo" +
+ "Q469RdWno6aHeK3TfiQFaebzT1MRabCJXDeyw8OalQICt0M0wx29B6HNof3px2NxKyC6qlf" +
+ "01wwNSaaIbsctDaLL5ZLN6T1LjpJsooMvDwRt69+SXo8SmD4YO6Wr4Q9drI3cCwVeQXwxoU" +
+ "uB96muQQ2M3WDiMz5ZLI3oMLu8KSPsMIIDiTCCAnGgAwIBAgIUDUo/9G0rz7fJiWTw0hY6T" +
+ "IyPRSIwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE3MTEyNzAw" +
+ "MDAwMFoYDzIwMjAwMjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggE" +
+ "iMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNU" +
+ "q07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0" +
+ "DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ" +
+ "sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJH" +
+ "dtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFz" +
+ "G4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcowgccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob" +
+ "3N0gg0qLmV4YW1wbGUuY29tghUqLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1z" +
+ "dWJkb21haW5zLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnB" +
+ "pbm5pbmcuZXhhbXBsZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi" +
+ "8vbG9jYWxob3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCkguNhMyVCYhyYXfE22wNvl" +
+ "aobK2YRb4OGMxySIKuQ80N0XlO+xpLJTs9YzFVY1+JTHNez1QfwP9KJeZznTzVzLh4sv0sw" +
+ "x/+oUxCfLb0VIl/kdUqLkbGYrAmtjeOKZLaqVtRH0BnmbPowLak1pi6nQYOU+aL9QOuvT/j" +
+ "3rXoimcdo6X3TK1SN2/64fGMyG/pwas+JXehbReUf4n1ewk84ADtb+ew8tRAKf/uxzKUj5t" +
+ "/UgqDsnTWq5wUc5IJKwoHT41sQnNqPg12x4+WGWiAsWCpR/hKYHFGr7rb4JTGEPAJpWcv9W" +
+ "tZYAvwT78a2xpHp5XNglj16IjWEukvJuU1WMQAAAAAAAAA=";
+ let certListDefaultEE = build_cert_list_from_pem_list([
+ gDefaultEEPEM,
+ gTestCAPEM,
+ ]);
+
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let pkcs7DefaultEE = certdb.asPKCS7Blob(certListDefaultEE);
+
+ equal(
+ btoa(pkcs7DefaultEE),
+ expectedPKCS7ForDefaultEE,
+ "PKCS7 export should work as expected for default-ee chain"
+ );
+
+ // This was generated by running BadCertAndPinningServer locally on the bad_certs
+ // directory and visiting:
+ // https://unknownissuer.example.com:8443/
+ // and then viewing the certificate presented (in the add certificate
+ // exception dialog) and exporting it.
+ const expectedPKCS7ForUnknownIssuer =
+ "MIAGCSqGSIb3DQEHAqCAMIACAQExADCABgkqhkiG9w0BBwEAAKCCA60wggOpMIICkaADAgE" +
+ "CAhQxGIn1OvCpM6hUVT6PkUNYNZLVfzANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtUZX" +
+ "N0IEludGVybWVkaWF0ZSB0byBkZWxldGUwIhgPMjAxNzExMjcwMDAwMDBaGA8yMDIwMDIwN" +
+ "TAwMDAwMFowLjEsMCoGA1UEAwwjVGVzdCBFbmQtZW50aXR5IGZyb20gdW5rbm93biBpc3N1" +
+ "ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTw" +
+ "T2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs" +
+ "1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkf" +
+ "bmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA" +
+ "dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/" +
+ "l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcIwgb8wgYgGA1UdEQSBgDB+ghl1bm" +
+ "tub3duaXNzdWVyLmV4YW1wbGUuY29tgjR1bmtub3duaXNzdWVyLmluY2x1ZGUtc3ViZG9tY" +
+ "Wlucy5waW5uaW5nLmV4YW1wbGUuY29tgit1bmtub3duaXNzdWVyLnRlc3QtbW9kZS5waW5u" +
+ "aW5nLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2x" +
+ "vY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAsCcmMEn4w5zuQxHOYRJhlFLEpq" +
+ "TsDUrrvEFdbNaYre3dZe979EuNX9GFvJM3bSHZmNJVy4POA1gL9En/Pdd3lD1AQaiLWXLzg" +
+ "d5p4fD/eRRIfG8ncmds+7aWT3pTnLu1cRV9sRVlinHhSzvZ+5RE9auJWV0gsfaWCsSD6Ipl" +
+ "Ztj/BxO9+7WP9+UyQYQ6lzUfK2WZH1+QIOerMV9ya/sSHdfUGJIE4h6YadQfE43CW2FITrw" +
+ "gi0TmXdzzYbyhAaa97qvQ32Hu3qikiMfWSgVYxyT+nMMDTUWZZ7LgBJ2WaMTw1IkCXXOwXw" +
+ "SIs5l4jWu+6txfBU53Q6WT39BPmHBOSTEAAAAAAAAA";
+ let certListUnknownIssuer = build_cert_list_from_pem_list([
+ gUnknownIssuerPEM,
+ ]);
+ let pkcs7UnknownIssuer = certdb.asPKCS7Blob(certListUnknownIssuer);
+ equal(
+ btoa(pkcs7UnknownIssuer),
+ expectedPKCS7ForUnknownIssuer,
+ "PKCS7 export should work as expected for unknown issuer"
+ );
+
+ // This was generated by running OCSPStaplingServer locally on the ocsp_certs
+ // directory and visiting:
+ // https://ocsp-stapling-with-intermediate.example.com:8443/
+ // and then viewing the certificate chain presented (in the page info dialog)
+ // and exporting it.
+ // (NB: test-ca must be imported and trusted for the connection to succeed)
+ const expectedPKCS7WithIntermediate =
+ "MIAGCSqGSIb3DQEHAqCAMIACAQExADCABgkqhkiG9w0BBwEAAKCCCPEwggLTMIIBu6ADAgE" +
+ "CAhQpoXAjALAddSApG46EBfimNiyZuDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZX" +
+ "N0IENBMCIYDzIwMTcxMTI3MDAwMDAwWhgPMjAyMDAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB" +
+ "1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI" +
+ "BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xz" +
+ "VJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCy" +
+ "uwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW" +
+ "7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE" +
+ "LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8" +
+ "wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAgyCfLAcVs/MkERxunH9pZA4ja1Q" +
+ "WWjsxSg9KgAIfOgj8c5RPHbl4oeWk0raNKWMu5+FR3/94IJeD45C3h/Y3+1HDyC6ZuzdgMX" +
+ "v63dk0a36JDFlPA3swqwYhnL7pHnbdcfDyWnMVfmLNeAhL7QA+Vf5fJmTsxEJwFaHo9JpKo" +
+ "Q469RdWno6aHeK3TfiQFaebzT1MRabCJXDeyw8OalQICt0M0wx29B6HNof3px2NxKyC6qlf" +
+ "01wwNSaaIbsctDaLL5ZLN6T1LjpJsooMvDwRt69+SXo8SmD4YO6Wr4Q9drI3cCwVeQXwxoU" +
+ "uB96muQQ2M3WDiMz5ZLI3oMLu8KSPsMIIC3TCCAcWgAwIBAgIUa0X7/7DlTaedpgrIJg25i" +
+ "BPOkIMwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE1MDEwMTAw" +
+ "MDAwMFoYDzIwMjUwMTAxMDAwMDAwWjAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTC" +
+ "CASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6u" +
+ "Q1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8H" +
+ "mnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh" +
+ "eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaM" +
+ "Mkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5" +
+ "kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EB" +
+ "AMCAQYwDQYJKoZIhvcNAQELBQADggEBAILNZM9yT9ylMpjyi0tXaDORzpHiJ8vEoVKk98bC" +
+ "2BQF0kMEEB547p+Ms8zdJY00Bxe9qigT8rQwKprXq5RvgIZ32QLn/yMPiCp/e6zBdsx77Tk" +
+ "fmnSnxvPi+0nlA+eM8JYN0UST4vWD4vPPX9GgZDVoGQTiF3hUivJ5R8sHb/ozcSukMKQQ22" +
+ "+AIU7w6wyAIbCAG7Pab4k2XFAeEnUZsl9fCym5jsPN9Pnv9rlBi6h8shHw1R2ROXjgxubji" +
+ "Mr3B456vFTJImLJjyA1iTSlr/+VXGUYg6Z0/HYnsO00+8xUKM71dPxGAfIFNaSscpykrGFL" +
+ "vocT/kym6r8galxCJUowggM1MIICHaADAgECAhRnruFLuUdWcJC1fHuhcWXOKGqHRzANBgk" +
+ "qhkiG9w0BAQsFADAcMRowGAYDVQQDDBFUZXN0IEludGVybWVkaWF0ZTAiGA8yMDE3MTEyNz" +
+ "AwMDAwMFoYDzIwMjAwMjA1MDAwMDAwWjAsMSowKAYDVQQDDCFUZXN0IEVuZC1lbnRpdHkgd" +
+ "2l0aCBJbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo" +
+ "RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHE" +
+ "IeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7q" +
+ "dw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCAB" +
+ "iTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd" +
+ "q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjWzBZMCMGA1U" +
+ "dEQQcMBqCCWxvY2FsaG9zdIINKi5leGFtcGxlLmNvbTAyBggrBgEFBQcBAQQmMCQwIgYIKw" +
+ "YBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJKoZIhvcNAQELBQADggEBACjTj" +
+ "eEzgaJS2hco5AHa/QLycXhjLOl/6LQuXFAIVmSIti72ki5C7XojdNstBEHCEILSaXtOokEB" +
+ "uvLMgtOisJA/wDVZBrWS3pxvfSMLJctxacMD9Qs97Gf1xibojRjIsWWeRxXMwYsVz4vJZMA" +
+ "8Rbmsxq67mI46n/ovmsEXQz1uDnJdIY0O/yCTclz6iklsENOsC4oM0uklISniuqi80WeKvO" +
+ "UlYNL5JwCxYe2gtysfqCdBE+wRXVYVCdivceQBqRhCiT3GzkCKQFLqaDPj9wUG2zxtIZK5f" +
+ "Fy2Q3ZKztjmXdiZNgoLE/Pqt61V+uGL+KyUfhbfP0em7dmrIAI7otsxAAAAAAAAAA==";
+ let certListWithIntermediate = build_cert_list_from_pem_list([
+ gOCSPEEWithIntermediatePEM,
+ gTestIntPEM,
+ gTestCAPEM,
+ ]);
+ let pkcs7WithIntermediate = certdb.asPKCS7Blob(certListWithIntermediate);
+ equal(
+ btoa(pkcs7WithIntermediate),
+ expectedPKCS7WithIntermediate,
+ "PKCS7 export should work as expected for chain with intermediate"
+ );
+}
+
+function test_cert_pkcs7_empty_array() {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+
+ throws(
+ () => certdb.asPKCS7Blob([]),
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "trying to convert an empty array to pkcs7 should throw"
+ );
+}
+
+function test_security_info_serialization(securityInfo, expectedErrorCode) {
+ // Serialize the securityInfo to a string
+ let serHelper = Cc["@mozilla.org/network/serialization-helper;1"].getService(
+ Ci.nsISerializationHelper
+ );
+ let serialized = serHelper.serializeToString(securityInfo);
+
+ // Deserialize from the string and compare to the original object
+ let deserialized = serHelper.deserializeObject(serialized);
+ deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
+ equal(
+ securityInfo.securityState,
+ deserialized.securityState,
+ "Original and deserialized security state should match"
+ );
+ equal(
+ securityInfo.errorMessage,
+ deserialized.errorMessage,
+ "Original and deserialized error message should match"
+ );
+ equal(
+ securityInfo.errorCode,
+ expectedErrorCode,
+ "Original and expected error code should match"
+ );
+ equal(
+ deserialized.errorCode,
+ expectedErrorCode,
+ "Deserialized and expected error code should match"
+ );
+}
+
+// In Bug 1580315, nsNSSCertList/nsIX509CertList was replaced by
+// Array<nsIX509Cert>, so the serialization of the certList changed. This
+// test is used to make sure we can still deserialize the transportSecurityInfo
+// binary string which has the old certList binary.
+function test_old_succeeded_certlist_deseralization_v1() {
+ // This was the serialized output of test "good.include-subdomains.pinning.example.com"
+ // in security/manager/ssl/tests/unit/test_cert_chains.js
+ // The serialized output was generated before we replace nsIX509CertList with
+ // Array<nsIX509Cert>, so it had the old version of transportSecurityInfo.
+ const serialized =
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAAAAgA" +
+ "AAAAAAAAAAAAAAAAAAAEAMQFmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAA" +
+ "AAAAONMIIDiTCCAnGgAwIBAgIUDUo/9G0rz7fJiWTw0hY6TIyPRSIwDQYJKoZIhvcNAQELB" +
+ "QAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE3MTEyNzAwMDAwMFoYDzIwMjAwMjA1MDAw" +
+ "MDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4I" +
+ "BDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZ" +
+ "wGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tF" +
+ "YIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n" +
+ "FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN" +
+ "7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe" +
+ "2NAgMBAAGjgcowgccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tg" +
+ "hUqLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcu" +
+ "ZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBsZS5jb20" +
+ "wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA" +
+ "0GCSqGSIb3DQEBCwUAA4IBAQCkguNhMyVCYhyYXfE22wNvlaobK2YRb4OGMxySIKuQ80N0X" +
+ "lO+xpLJTs9YzFVY1+JTHNez1QfwP9KJeZznTzVzLh4sv0swx/+oUxCfLb0VIl/kdUqLkbGY" +
+ "rAmtjeOKZLaqVtRH0BnmbPowLak1pi6nQYOU+aL9QOuvT/j3rXoimcdo6X3TK1SN2/64fGM" +
+ "yG/pwas+JXehbReUf4n1ewk84ADtb+ew8tRAKf/uxzKUj5t/UgqDsnTWq5wUc5IJKwoHT41" +
+ "sQnNqPg12x4+WGWiAsWCpR/hKYHFGr7rb4JTGEPAJpWcv9WtZYAvwT78a2xpHp5XNglj16I" +
+ "jWEukvJuU1WwC8AAwAAAAABAQAAAAAAAAZ4MjU1MTkAAAAOUlNBLVBTUy1TSEEyNTYBlZ+x" +
+ "ZWUXSH+rm9iRO+Uxl650zaXNL0c/lvXwt//2LGgAAAACZgoyJpFcT/u7IImFpjLfBb3Dl5p" +
+ "UIkzVhYlpa26W6oMAAAAAAAADjTCCA4kwggJxoAMCAQICFA1KP/RtK8+3yYlk8NIWOkyMj0" +
+ "UiMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwIhgPMjAxNzExMjcwMDAwM" +
+ "DBaGA8yMDIwMDIwNTAwMDAwMFowGjEYMBYGA1UEAwwPVGVzdCBFbmQtZW50aXR5MIIBIjAN" +
+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz" +
+ "1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4IC" +
+ "mTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXk" +
+ "D3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK" +
+ "9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP" +
+ "+SSP6clHEMdUDrNoYCjXtjQIDAQABo4HKMIHHMIGQBgNVHREEgYgwgYWCCWxvY2FsaG9zdI" +
+ "INKi5leGFtcGxlLmNvbYIVKi5waW5uaW5nLmV4YW1wbGUuY29tgigqLmluY2x1ZGUtc3ViZ" +
+ "G9tYWlucy5waW5uaW5nLmV4YW1wbGUuY29tgigqLmV4Y2x1ZGUtc3ViZG9tYWlucy5waW5u" +
+ "aW5nLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2x" +
+ "vY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEApILjYTMlQmIcmF3xNtsDb5WqGy" +
+ "tmEW+DhjMckiCrkPNDdF5TvsaSyU7PWMxVWNfiUxzXs9UH8D/SiXmc5081cy4eLL9LMMf/q" +
+ "FMQny29FSJf5HVKi5GxmKwJrY3jimS2qlbUR9AZ5mz6MC2pNaYup0GDlPmi/UDrr0/49616" +
+ "IpnHaOl90ytUjdv+uHxjMhv6cGrPiV3oW0XlH+J9XsJPOAA7W/nsPLUQCn/7scylI+bf1IK" +
+ "g7J01qucFHOSCSsKB0+NbEJzaj4NdsePlhlogLFgqUf4SmBxRq+62+CUxhDwCaVnL/VrWWA" +
+ "L8E+/GtsaR6eVzYJY9eiI1hLpLyblNVmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtul" +
+ "uqDAAAAAAAAAtcwggLTMIIBu6ADAgECAhQpoXAjALAddSApG46EBfimNiyZuDANBgkqhkiG" +
+ "9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0IENBMCIYDzIwMTcxMTI3MDAwMDAwWhgPMjAyMDA" +
+ "yMDUwMDAwMDBaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDw" +
+ "AwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm" +
+ "24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP" +
+ "8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFth" +
+ "Vt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7Ly" +
+ "JvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NA" +
+ "gMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IB" +
+ "AQAgyCfLAcVs/MkERxunH9pZA4ja1QWWjsxSg9KgAIfOgj8c5RPHbl4oeWk0raNKWMu5+FR" +
+ "3/94IJeD45C3h/Y3+1HDyC6ZuzdgMXv63dk0a36JDFlPA3swqwYhnL7pHnbdcfDyWnMVfmL" +
+ "NeAhL7QA+Vf5fJmTsxEJwFaHo9JpKoQ469RdWno6aHeK3TfiQFaebzT1MRabCJXDeyw8Oal" +
+ "QICt0M0wx29B6HNof3px2NxKyC6qlf01wwNSaaIbsctDaLL5ZLN6T1LjpJsooMvDwRt69+S" +
+ "Xo8SmD4YO6Wr4Q9drI3cCwVeQXwxoUuB96muQQ2M3WDiMz5ZLI3oMLu8KSPsAA==";
+
+ let serHelper = Cc["@mozilla.org/network/serialization-helper;1"].getService(
+ Ci.nsISerializationHelper
+ );
+ // deserialize from the string and compare to the original object
+ let deserialized = serHelper.deserializeObject(serialized);
+ deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
+
+ equal(
+ deserialized.failedCertChain.length,
+ 0,
+ "failedCertChain for a successful connection should be empty"
+ );
+ let certChain = build_cert_list_from_pem_list([gDefaultEEPEM, gTestCAPEM]);
+ ok(
+ areCertArraysEqual(certChain, deserialized.succeededCertChain),
+ "succeededCertChain should be deserialized correctly"
+ );
+}
+
+// Same as the above test, however, this is the v2 version of the
+// serialization.
+function test_old_succeeded_certlist_deseralization_v2() {
+ const serialized =
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAAAAgA" +
+ "AAAAAAAAAAAAAAAAAAAEAMgFmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAA" +
+ "AAAAONMIIDiTCCAnGgAwIBAgIUDUo/9G0rz7fJiWTw0hY6TIyPRSIwDQYJKoZIhvcNAQELB" +
+ "QAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE3MTEyNzAwMDAwMFoYDzIwMjAwMjA1MDAw" +
+ "MDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4I" +
+ "BDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZ" +
+ "wGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tF" +
+ "YIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n" +
+ "FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN" +
+ "7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe" +
+ "2NAgMBAAGjgcowgccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tg" +
+ "hUqLnBpbm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcu" +
+ "ZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBsZS5jb20" +
+ "wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxob3N0Ojg4ODgvMA" +
+ "0GCSqGSIb3DQEBCwUAA4IBAQCkguNhMyVCYhyYXfE22wNvlaobK2YRb4OGMxySIKuQ80N0X" +
+ "lO+xpLJTs9YzFVY1+JTHNez1QfwP9KJeZznTzVzLh4sv0swx/+oUxCfLb0VIl/kdUqLkbGY" +
+ "rAmtjeOKZLaqVtRH0BnmbPowLak1pi6nQYOU+aL9QOuvT/j3rXoimcdo6X3TK1SN2/64fGM" +
+ "yG/pwas+JXehbReUf4n1ewk84ADtb+ew8tRAKf/uxzKUj5t/UgqDsnTWq5wUc5IJKwoHT41" +
+ "sQnNqPg12x4+WGWiAsWCpR/hKYHFGr7rb4JTGEPAJpWcv9WtZYAvwT78a2xpHp5XNglj16I" +
+ "jWEukvJuU1WEwEABAAAAAABAQAAAAAAAAZ4MjU1MTkAAAAOUlNBLVBTUy1TSEEyNTYBlZ+x" +
+ "ZWUXSH+rm9iRO+Uxl650zaXNL0c/lvXwt//2LGgAAAACZgoyJpFcT/u7IImFpjLfBb3Dl5p" +
+ "UIkzVhYlpa26W6oMAAAAAAAADjTCCA4kwggJxoAMCAQICFA1KP/RtK8+3yYlk8NIWOkyMj0" +
+ "UiMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwIhgPMjAxNzExMjcwMDAwM" +
+ "DBaGA8yMDIwMDIwNTAwMDAwMFowGjEYMBYGA1UEAwwPVGVzdCBFbmQtZW50aXR5MIIBIjAN" +
+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz" +
+ "1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4IC" +
+ "mTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXk" +
+ "D3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK" +
+ "9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP" +
+ "+SSP6clHEMdUDrNoYCjXtjQIDAQABo4HKMIHHMIGQBgNVHREEgYgwgYWCCWxvY2FsaG9zdI" +
+ "INKi5leGFtcGxlLmNvbYIVKi5waW5uaW5nLmV4YW1wbGUuY29tgigqLmluY2x1ZGUtc3ViZ" +
+ "G9tYWlucy5waW5uaW5nLmV4YW1wbGUuY29tgigqLmV4Y2x1ZGUtc3ViZG9tYWlucy5waW5u" +
+ "aW5nLmV4YW1wbGUuY29tMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2x" +
+ "vY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEApILjYTMlQmIcmF3xNtsDb5WqGy" +
+ "tmEW+DhjMckiCrkPNDdF5TvsaSyU7PWMxVWNfiUxzXs9UH8D/SiXmc5081cy4eLL9LMMf/q" +
+ "FMQny29FSJf5HVKi5GxmKwJrY3jimS2qlbUR9AZ5mz6MC2pNaYup0GDlPmi/UDrr0/49616" +
+ "IpnHaOl90ytUjdv+uHxjMhv6cGrPiV3oW0XlH+J9XsJPOAA7W/nsPLUQCn/7scylI+bf1IK" +
+ "g7J01qucFHOSCSsKB0+NbEJzaj4NdsePlhlogLFgqUf4SmBxRq+62+CUxhDwCaVnL/VrWWA" +
+ "L8E+/GtsaR6eVzYJY9eiI1hLpLyblNVmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtul" +
+ "uqDAAAAAAAAAtcwggLTMIIBu6ADAgECAhQpoXAjALAddSApG46EBfimNiyZuDANBgkqhkiG" +
+ "9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0IENBMCIYDzIwMTcxMTI3MDAwMDAwWhgPMjAyMDA" +
+ "yMDUwMDAwMDBaMBIxEDAOBgNVBAMMB1Rlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDw" +
+ "AwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm" +
+ "24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP" +
+ "8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFth" +
+ "Vt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7Ly" +
+ "JvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NA" +
+ "gMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IB" +
+ "AQAgyCfLAcVs/MkERxunH9pZA4ja1QWWjsxSg9KgAIfOgj8c5RPHbl4oeWk0raNKWMu5+FR" +
+ "3/94IJeD45C3h/Y3+1HDyC6ZuzdgMXv63dk0a36JDFlPA3swqwYhnL7pHnbdcfDyWnMVfmL" +
+ "NeAhL7QA+Vf5fJmTsxEJwFaHo9JpKoQ469RdWno6aHeK3TfiQFaebzT1MRabCJXDeyw8Oal" +
+ "QICt0M0wx29B6HNof3px2NxKyC6qlf01wwNSaaIbsctDaLL5ZLN6T1LjpJsooMvDwRt69+S" +
+ "Xo8SmD4YO6Wr4Q9drI3cCwVeQXwxoUuB96muQQ2M3WDiMz5ZLI3oMLu8KSPsAAA=";
+
+ let serHelper = Cc["@mozilla.org/network/serialization-helper;1"].getService(
+ Ci.nsISerializationHelper
+ );
+ // deserialize from the string and compare to the original object
+ let deserialized = serHelper.deserializeObject(serialized);
+ deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
+
+ equal(
+ deserialized.failedCertChain.length,
+ [],
+ "failedCertChain for a successful connection should be empty"
+ );
+ let certChain = build_cert_list_from_pem_list([gDefaultEEPEM, gTestCAPEM]);
+ ok(
+ areCertArraysEqual(certChain, deserialized.succeededCertChain),
+ "succeededCertChain should be deserialized correctly"
+ );
+}
+
+// In Bug 1580315, nsNSSCertList/nsIX509CertList was replaced by
+// Array<nsIX509Cert>, so the serialization of the certList changed. This
+// test is used to make sure we can still deserialize the TransportSecurityInfo
+// binary string which has the old certList binary.
+function test_old_failed_certlist_deseralization_v1() {
+ // This was the serialized output of test "expired.example.com"
+ // in security/manager/ssl/tests/unit/test_cert_chains.js
+ // The serialized output was generated before we replace nsIX509CertList with
+ // Array<nsIX509Cert>, so it had the old version of transportSecurityInfo.
+ const serialized =
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAAABAA" +
+ "AAAAAAAAA///gCwAAAAEAMQFmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAA" +
+ "AAAAMgMIIDHDCCAgSgAwIBAgIUY9ERAIKj0js/YbhJoMrcLnj++uowDQYJKoZIhvcNAQELB" +
+ "QAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDEzMDEwMTAwMDAwMFoYDzIwMTQwMTAxMDAw" +
+ "MDAwWjAiMSAwHgYDVQQDDBdFeHBpcmVkIFRlc3QgRW5kLWVudGl0eTCCASIwDQYJKoZIhvc" +
+ "NAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wc" +
+ "clqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk2" +
+ "7lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhI" +
+ "H6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wn" +
+ "vuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxD" +
+ "HVA6zaGAo17Y0CAwEAAaNWMFQwHgYDVR0RBBcwFYITZXhwaXJlZC5leGFtcGxlLmNvbTAyB" +
+ "ggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJ" +
+ "KoZIhvcNAQELBQADggEBAImiFuy275T6b+Ud6gl/El6qpgWHUXeYiv2sp7d+HVzfT+ow5WV" +
+ "sxI/GMKhdA43JaKT9gfMsbnP1qiI2zel3U+F7IAMO1CEr5FVdCOVTma5hmu/81rkJLmZ8RQ" +
+ "DWWOhZKyn/7aD7TH1C1e768yCt5E2DDl8mHil9zR8BPsoXwuS3L9zJ2JqNc60+hB8l297Za" +
+ "Sl0nbKffb47ukvn5kSJ7tI9n/fSXdj1JrukwjZP+74VkQyNobaFzDZ+Zr3QmfbejEsY2EYn" +
+ "q8XuENgIO4DuYrm80/p6bMO6laB0Uv5W6uXZgBZdRTe1WMdYWGhmvnFFQmf+naeOOl6ryFw" +
+ "WwtnoK7IAAAAAAAEAAAEAAQAAAAAAAAAAAAAAAZWfsWVlF0h/q5vYkTvlMZeudM2lzS9HP5" +
+ "b18Lf/9ixoAAAAAmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAAAAAAAyAwg" +
+ "gMcMIICBKADAgECAhRj0REAgqPSOz9huEmgytwueP766jANBgkqhkiG9w0BAQsFADASMRAw" +
+ "DgYDVQQDDAdUZXN0IENBMCIYDzIwMTMwMTAxMDAwMDAwWhgPMjAxNDAxMDEwMDAwMDBaMCI" +
+ "xIDAeBgNVBAMMF0V4cGlyZWQgVGVzdCBFbmQtZW50aXR5MIIBIjANBgkqhkiG9w0BAQEFAA" +
+ "OCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngfv" +
+ "bGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO" +
+ "7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEP" +
+ "vJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naO" +
+ "Gzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYC" +
+ "jXtjQIDAQABo1YwVDAeBgNVHREEFzAVghNleHBpcmVkLmV4YW1wbGUuY29tMDIGCCsGAQUF" +
+ "BwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0" +
+ "BAQsFAAOCAQEAiaIW7LbvlPpv5R3qCX8SXqqmBYdRd5iK/aynt34dXN9P6jDlZWzEj8YwqF" +
+ "0DjclopP2B8yxuc/WqIjbN6XdT4XsgAw7UISvkVV0I5VOZrmGa7/zWuQkuZnxFANZY6FkrK" +
+ "f/toPtMfULV7vrzIK3kTYMOXyYeKX3NHwE+yhfC5Lcv3MnYmo1zrT6EHyXb3tlpKXSdsp99" +
+ "vju6S+fmRInu0j2f99Jd2PUmu6TCNk/7vhWRDI2htoXMNn5mvdCZ9t6MSxjYRierxe4Q2Ag" +
+ "7gO5iubzT+npsw7qVoHRS/lbq5dmAFl1FN7VYx1hYaGa+cUVCZ/6dp446XqvIXBbC2egrsm" +
+ "YKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAAAAAAAtcwggLTMIIBu6ADAgECA" +
+ "hQpoXAjALAddSApG46EBfimNiyZuDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0" +
+ "IENBMCIYDzIwMTcxMTI3MDAwMDAwWhgPMjAyMDAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB1R" +
+ "lc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBj" +
+ "YQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJ" +
+ "JwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw" +
+ "JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7f" +
+ "ilhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL" +
+ "8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wC" +
+ "wYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAgyCfLAcVs/MkERxunH9pZA4ja1QWW" +
+ "jsxSg9KgAIfOgj8c5RPHbl4oeWk0raNKWMu5+FR3/94IJeD45C3h/Y3+1HDyC6ZuzdgMXv6" +
+ "3dk0a36JDFlPA3swqwYhnL7pHnbdcfDyWnMVfmLNeAhL7QA+Vf5fJmTsxEJwFaHo9JpKoQ4" +
+ "69RdWno6aHeK3TfiQFaebzT1MRabCJXDeyw8OalQICt0M0wx29B6HNof3px2NxKyC6qlf01" +
+ "wwNSaaIbsctDaLL5ZLN6T1LjpJsooMvDwRt69+SXo8SmD4YO6Wr4Q9drI3cCwVeQXwxoUuB" +
+ "96muQQ2M3WDiMz5ZLI3oMLu8KSPs";
+
+ let serHelper = Cc["@mozilla.org/network/serialization-helper;1"].getService(
+ Ci.nsISerializationHelper
+ );
+ // Deserialize from the string and compare to the original object
+ let deserialized = serHelper.deserializeObject(serialized);
+ deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
+
+ equal(
+ deserialized.succeededCertChain.length,
+ 0,
+ "succeededCertChain should be empty"
+ );
+ let certChain = build_cert_list_from_pem_list([gExpiredEEPEM, gTestCAPEM]);
+ ok(
+ areCertArraysEqual(certChain, deserialized.failedCertChain),
+ "failedCertChain should be deserialized correctly"
+ );
+}
+
+// Same as the above test, however, this is the v2 version of the
+// serialization.
+function test_old_failed_certlist_deseralization_v2() {
+ const serialized =
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAAABAA" +
+ "AAAAAAAAA///gCwAAAAEAMgFmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAA" +
+ "AAAAMgMIIDHDCCAgSgAwIBAgIUY9ERAIKj0js/YbhJoMrcLnj++uowDQYJKoZIhvcNAQELB" +
+ "QAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDEzMDEwMTAwMDAwMFoYDzIwMTQwMTAxMDAw" +
+ "MDAwWjAiMSAwHgYDVQQDDBdFeHBpcmVkIFRlc3QgRW5kLWVudGl0eTCCASIwDQYJKoZIhvc" +
+ "NAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wc" +
+ "clqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk2" +
+ "7lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhI" +
+ "H6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wn" +
+ "vuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxD" +
+ "HVA6zaGAo17Y0CAwEAAaNWMFQwHgYDVR0RBBcwFYITZXhwaXJlZC5leGFtcGxlLmNvbTAyB" +
+ "ggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9sb2NhbGhvc3Q6ODg4OC8wDQYJ" +
+ "KoZIhvcNAQELBQADggEBAImiFuy275T6b+Ud6gl/El6qpgWHUXeYiv2sp7d+HVzfT+ow5WV" +
+ "sxI/GMKhdA43JaKT9gfMsbnP1qiI2zel3U+F7IAMO1CEr5FVdCOVTma5hmu/81rkJLmZ8RQ" +
+ "DWWOhZKyn/7aD7TH1C1e768yCt5E2DDl8mHil9zR8BPsoXwuS3L9zJ2JqNc60+hB8l297Za" +
+ "Sl0nbKffb47ukvn5kSJ7tI9n/fSXdj1JrukwjZP+74VkQyNobaFzDZ+Zr3QmfbejEsY2EYn" +
+ "q8XuENgIO4DuYrm80/p6bMO6laB0Uv5W6uXZgBZdRTe1WMdYWGhmvnFFQmf+naeOOl6ryFw" +
+ "WwtnoK7IAAAAAAAEAAAEAAQAAAAAAAAAAAAAAAZWfsWVlF0h/q5vYkTvlMZeudM2lzS9HP5" +
+ "b18Lf/9ixoAAAAAmYKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAAAAAAAyAwg" +
+ "gMcMIICBKADAgECAhRj0REAgqPSOz9huEmgytwueP766jANBgkqhkiG9w0BAQsFADASMRAw" +
+ "DgYDVQQDDAdUZXN0IENBMCIYDzIwMTMwMTAxMDAwMDAwWhgPMjAxNDAxMDEwMDAwMDBaMCI" +
+ "xIDAeBgNVBAMMF0V4cGlyZWQgVGVzdCBFbmQtZW50aXR5MIIBIjANBgkqhkiG9w0BAQEFAA" +
+ "OCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngfv" +
+ "bGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO" +
+ "7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEP" +
+ "vJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naO" +
+ "Gzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYC" +
+ "jXtjQIDAQABo1YwVDAeBgNVHREEFzAVghNleHBpcmVkLmV4YW1wbGUuY29tMDIGCCsGAQUF" +
+ "BwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4LzANBgkqhkiG9w0" +
+ "BAQsFAAOCAQEAiaIW7LbvlPpv5R3qCX8SXqqmBYdRd5iK/aynt34dXN9P6jDlZWzEj8YwqF" +
+ "0DjclopP2B8yxuc/WqIjbN6XdT4XsgAw7UISvkVV0I5VOZrmGa7/zWuQkuZnxFANZY6FkrK" +
+ "f/toPtMfULV7vrzIK3kTYMOXyYeKX3NHwE+yhfC5Lcv3MnYmo1zrT6EHyXb3tlpKXSdsp99" +
+ "vju6S+fmRInu0j2f99Jd2PUmu6TCNk/7vhWRDI2htoXMNn5mvdCZ9t6MSxjYRierxe4Q2Ag" +
+ "7gO5iubzT+npsw7qVoHRS/lbq5dmAFl1FN7VYx1hYaGa+cUVCZ/6dp446XqvIXBbC2egrsm" +
+ "YKMiaRXE/7uyCJhaYy3wW9w5eaVCJM1YWJaWtuluqDAAAAAAAAAtcwggLTMIIBu6ADAgECA" +
+ "hQpoXAjALAddSApG46EBfimNiyZuDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0" +
+ "IENBMCIYDzIwMTcxMTI3MDAwMDAwWhgPMjAyMDAyMDUwMDAwMDBaMBIxEDAOBgNVBAMMB1R" +
+ "lc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBj" +
+ "YQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJ" +
+ "JwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw" +
+ "JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7f" +
+ "ilhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL" +
+ "8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wC" +
+ "wYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAgyCfLAcVs/MkERxunH9pZA4ja1QWW" +
+ "jsxSg9KgAIfOgj8c5RPHbl4oeWk0raNKWMu5+FR3/94IJeD45C3h/Y3+1HDyC6ZuzdgMXv6" +
+ "3dk0a36JDFlPA3swqwYhnL7pHnbdcfDyWnMVfmLNeAhL7QA+Vf5fJmTsxEJwFaHo9JpKoQ4" +
+ "69RdWno6aHeK3TfiQFaebzT1MRabCJXDeyw8OalQICt0M0wx29B6HNof3px2NxKyC6qlf01" +
+ "wwNSaaIbsctDaLL5ZLN6T1LjpJsooMvDwRt69+SXo8SmD4YO6Wr4Q9drI3cCwVeQXwxoUuB" +
+ "96muQQ2M3WDiMz5ZLI3oMLu8KSPsAA==";
+
+ let serHelper = Cc["@mozilla.org/network/serialization-helper;1"].getService(
+ Ci.nsISerializationHelper
+ );
+ // Deserialize from the string and compare to the original object
+ let deserialized = serHelper.deserializeObject(serialized);
+ deserialized.QueryInterface(Ci.nsITransportSecurityInfo);
+
+ let certChain = build_cert_list_from_pem_list([gExpiredEEPEM, gTestCAPEM]);
+ ok(
+ areCertArraysEqual(certChain, deserialized.failedCertChain),
+ "failedCertChain should be deserialized correctly"
+ );
+}
+function run_test() {
+ do_get_profile();
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+ // Test nsIX509Cert.equals
+ add_test(function() {
+ test_cert_equals();
+ run_next_test();
+ });
+
+ add_test(function() {
+ test_cert_pkcs7_export();
+ run_next_test();
+ });
+
+ add_test(function() {
+ test_cert_pkcs7_empty_array();
+ run_next_test();
+ });
+
+ add_test(function() {
+ test_old_succeeded_certlist_deseralization_v2();
+ run_next_test();
+ });
+
+ add_test(function() {
+ test_old_failed_certlist_deseralization_v2();
+ run_next_test();
+ });
+
+ add_test(function() {
+ test_old_succeeded_certlist_deseralization_v1();
+ run_next_test();
+ });
+
+ add_test(function() {
+ test_old_failed_certlist_deseralization_v1();
+ run_next_test();
+ });
+
+ // Test successful connection (failedCertChain should be null)
+ add_connection_test(
+ // re-use pinning certs (keeler)
+ "good.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess,
+ null,
+ function withSecurityInfo(aTransportSecurityInfo) {
+ aTransportSecurityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ test_security_info_serialization(aTransportSecurityInfo, 0);
+ equal(
+ aTransportSecurityInfo.failedCertChain.length,
+ 0,
+ "failedCertChain for a successful connection should be null"
+ );
+ }
+ );
+
+ // Test overrideable connection failure (failedCertChain should be non-null)
+ add_connection_test(
+ "expired.example.com",
+ SEC_ERROR_EXPIRED_CERTIFICATE,
+ null,
+ function withSecurityInfo(securityInfo) {
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ test_security_info_serialization(
+ securityInfo,
+ SEC_ERROR_EXPIRED_CERTIFICATE
+ );
+ notEqual(
+ securityInfo.failedCertChain,
+ null,
+ "failedCertChain should not be null for an overrideable" +
+ " connection failure"
+ );
+ let originalCertChain = build_cert_chain(["expired-ee", "test-ca"]);
+ ok(
+ areCertArraysEqual(originalCertChain, securityInfo.failedCertChain),
+ "failedCertChain should equal the original cert chain for an" +
+ " overrideable connection failure"
+ );
+ }
+ );
+
+ // Test overrideable connection failure (failedCertChain should be non-null)
+ add_connection_test(
+ "unknownissuer.example.com",
+ SEC_ERROR_UNKNOWN_ISSUER,
+ null,
+ function withSecurityInfo(securityInfo) {
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ test_security_info_serialization(securityInfo, SEC_ERROR_UNKNOWN_ISSUER);
+ notEqual(
+ securityInfo.failedCertChain,
+ null,
+ "failedCertChain should not be null for an overrideable" +
+ " connection failure"
+ );
+ let originalCertChain = build_cert_chain(["unknownissuer"]);
+ ok(
+ areCertArraysEqual(originalCertChain, securityInfo.failedCertChain),
+ "failedCertChain should equal the original cert chain for an" +
+ " overrideable connection failure"
+ );
+ }
+ );
+
+ // Test non-overrideable error (failedCertChain should be non-null)
+ add_connection_test(
+ "inadequatekeyusage.example.com",
+ SEC_ERROR_INADEQUATE_KEY_USAGE,
+ null,
+ function withSecurityInfo(securityInfo) {
+ securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+ test_security_info_serialization(
+ securityInfo,
+ SEC_ERROR_INADEQUATE_KEY_USAGE
+ );
+ notEqual(
+ securityInfo.failedCertChain,
+ null,
+ "failedCertChain should not be null for a non-overrideable" +
+ " connection failure"
+ );
+ let originalCertChain = build_cert_chain([
+ "inadequatekeyusage-ee",
+ "test-ca",
+ ]);
+ ok(
+ areCertArraysEqual(originalCertChain, securityInfo.failedCertChain),
+ "failedCertChain should equal the original cert chain for a" +
+ " non-overrideable connection failure"
+ );
+ }
+ );
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_dbKey.js b/security/manager/ssl/tests/unit/test_cert_dbKey.js
new file mode 100644
index 0000000000..a45b638b8f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_dbKey.js
@@ -0,0 +1,263 @@
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// 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/.
+
+"use strict";
+
+// This test tests that the nsIX509Cert.dbKey and nsIX509CertDB.findCertByDBKey
+// APIs work as expected. That is, getting a certificate's dbKey and using it
+// in findCertByDBKey should return the same certificate. Also, for backwards
+// compatibility, findCertByDBKey should ignore any whitespace in its input
+// (even though now nsIX509Cert.dbKey will never have whitespace in it).
+
+function hexStringToBytes(hex) {
+ let bytes = [];
+ for (let hexByteStr of hex.split(":")) {
+ bytes.push(parseInt(hexByteStr, 16));
+ }
+ return bytes;
+}
+
+function encodeCommonNameAsBytes(commonName) {
+ // The encoding will look something like this (in hex):
+ // 30 (SEQUENCE) <length of contents>
+ // 31 (SET) <length of contents>
+ // 30 (SEQUENCE) <length of contents>
+ // 06 (OID) 03 (length)
+ // 55 04 03 (id-at-commonName)
+ // 0C (UTF8String) <length of common name>
+ // <common name bytes>
+ // To make things simple, it would be nice to have the length of each
+ // component be less than 128 bytes (so we can have single-byte lengths).
+ // For this to hold, the maximum length of the contents of the outermost
+ // SEQUENCE must be 127. Everything not in the contents of the common name
+ // will take up 11 bytes, so the value of the common name itself can be at
+ // most 116 bytes.
+ ok(
+ commonName.length <= 116,
+ "test assumption: common name can't be longer than 116 bytes (makes " +
+ "DER encoding easier)"
+ );
+ let commonNameOIDBytes = [0x06, 0x03, 0x55, 0x04, 0x03];
+ let commonNameBytes = [0x0c, commonName.length];
+ for (let i = 0; i < commonName.length; i++) {
+ commonNameBytes.push(commonName.charCodeAt(i));
+ }
+ let bytes = commonNameOIDBytes.concat(commonNameBytes);
+ bytes.unshift(bytes.length);
+ bytes.unshift(0x30); // SEQUENCE
+ bytes.unshift(bytes.length);
+ bytes.unshift(0x31); // SET
+ bytes.unshift(bytes.length);
+ bytes.unshift(0x30); // SEQUENCE
+ return bytes;
+}
+
+function testInvalidDBKey(certDB, dbKey) {
+ throws(
+ () => certDB.findCertByDBKey(dbKey),
+ /NS_ERROR_ILLEGAL_INPUT/,
+ `findCertByDBKey(${dbKey}) should raise NS_ERROR_ILLEGAL_INPUT`
+ );
+}
+
+function testDBKeyForNonexistentCert(certDB, dbKey) {
+ let cert = certDB.findCertByDBKey(dbKey);
+ ok(!cert, "shouldn't find cert for given dbKey");
+}
+
+function byteArrayToByteString(bytes) {
+ let byteString = "";
+ for (let b of bytes) {
+ byteString += String.fromCharCode(b);
+ }
+ return byteString;
+}
+
+function run_test() {
+ do_get_profile();
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let cert = constructCertFromFile("bad_certs/test-ca.pem");
+ equal(
+ cert.issuerName,
+ "CN=" + cert.issuerCommonName,
+ "test assumption: this certificate's issuer distinguished name " +
+ "consists only of a common name"
+ );
+ let issuerBytes = encodeCommonNameAsBytes(cert.issuerCommonName);
+ ok(
+ issuerBytes.length < 256,
+ "test assumption: length of encoded issuer is less than 256 bytes"
+ );
+ let serialNumberBytes = hexStringToBytes(cert.serialNumber);
+ ok(
+ serialNumberBytes.length < 256,
+ "test assumption: length of encoded serial number is less than 256 bytes"
+ );
+ let dbKeyHeader = [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ serialNumberBytes.length,
+ 0,
+ 0,
+ 0,
+ issuerBytes.length,
+ ];
+ let expectedDbKeyBytes = dbKeyHeader.concat(serialNumberBytes, issuerBytes);
+ let expectedDbKey = btoa(byteArrayToByteString(expectedDbKeyBytes));
+ equal(
+ cert.dbKey,
+ expectedDbKey,
+ "actual and expected dbKey values should match"
+ );
+
+ let certFromDbKey = certDB.findCertByDBKey(expectedDbKey);
+ ok(
+ certFromDbKey.equals(cert),
+ "nsIX509CertDB.findCertByDBKey should find the right certificate"
+ );
+
+ ok(
+ expectedDbKey.length > 64,
+ "test assumption: dbKey should be longer than 64 characters"
+ );
+ let expectedDbKeyWithCRLF = expectedDbKey.replace(/(.{64})/, "$1\r\n");
+ ok(
+ expectedDbKeyWithCRLF.indexOf("\r\n") == 64,
+ "test self-check: adding CRLF to dbKey should succeed"
+ );
+ certFromDbKey = certDB.findCertByDBKey(expectedDbKeyWithCRLF);
+ ok(
+ certFromDbKey.equals(cert),
+ "nsIX509CertDB.findCertByDBKey should work with dbKey with CRLF"
+ );
+
+ let expectedDbKeyWithSpaces = expectedDbKey.replace(/(.{64})/, "$1 ");
+ ok(
+ expectedDbKeyWithSpaces.indexOf(" ") == 64,
+ "test self-check: adding spaces to dbKey should succeed"
+ );
+ certFromDbKey = certDB.findCertByDBKey(expectedDbKeyWithSpaces);
+ ok(
+ certFromDbKey.equals(cert),
+ "nsIX509CertDB.findCertByDBKey should work with dbKey with spaces"
+ );
+
+ // Test some invalid dbKey values.
+ testInvalidDBKey(certDB, "AAAA"); // Not long enough.
+ // No header.
+ testInvalidDBKey(
+ certDB,
+ btoa(
+ byteArrayToByteString(
+ [0, 0, 0, serialNumberBytes.length, 0, 0, 0, issuerBytes.length].concat(
+ serialNumberBytes,
+ issuerBytes
+ )
+ )
+ )
+ );
+ testInvalidDBKey(
+ certDB,
+ btoa(
+ byteArrayToByteString([
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 255,
+ 255,
+ 255,
+ 255, // serial number length is way too long
+ 255,
+ 255,
+ 255,
+ 255, // issuer length is way too long
+ 0,
+ 0,
+ 0,
+ 0,
+ ])
+ )
+ );
+ // Truncated issuer.
+ testInvalidDBKey(
+ certDB,
+ btoa(
+ byteArrayToByteString([
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 10,
+ 1,
+ 1,
+ 2,
+ 3,
+ ])
+ )
+ );
+ // Issuer doesn't decode to valid common name.
+ testDBKeyForNonexistentCert(
+ certDB,
+ btoa(
+ byteArrayToByteString([
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 3,
+ 1,
+ 1,
+ 2,
+ 3,
+ ])
+ )
+ );
+
+ // zero-length serial number and issuer -> no such certificate
+ testDBKeyForNonexistentCert(
+ certDB,
+ btoa(
+ byteArrayToByteString([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+ )
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_eku.js b/security/manager/ssl/tests/unit/test_cert_eku.js
new file mode 100644
index 0000000000..d185407243
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku.js
@@ -0,0 +1,189 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests that the extended key usage extension is properly processed by the
+// platform when verifying certificates. There are already comprehensive tests
+// in mozilla::pkix itself, but these tests serve as integration tests to ensure
+// that the cases we're particularly concerned about are correctly handled.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function certFromFile(certName) {
+ return constructCertFromFile(`test_cert_eku/${certName}.pem`);
+}
+
+function loadCertWithTrust(certName, trustString) {
+ addCertFromFile(certdb, `test_cert_eku/${certName}.pem`, trustString);
+}
+
+function checkEndEntity(cert, expectedResult) {
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ expectedResult,
+ certificateUsageSSLServer
+ );
+}
+
+function checkCertOn25August2016(cert, expectedResult) {
+ // (new Date("2016-08-25T00:00:00Z")).getTime() / 1000
+ const VALIDATION_TIME = 1472083200;
+ return checkCertErrorGenericAtTime(
+ certdb,
+ cert,
+ expectedResult,
+ certificateUsageSSLServer,
+ VALIDATION_TIME
+ );
+}
+
+add_task(async function() {
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("privacy.reduceTimerPrecision");
+ });
+ Services.prefs.setBoolPref("privacy.reduceTimerPrecision", false);
+
+ loadCertWithTrust("ca", "CTu,,");
+ // end-entity has id-kp-serverAuth => success
+ await checkEndEntity(certFromFile("ee-SA"), PRErrorCodeSuccess);
+ // end-entity has id-kp-serverAuth => success
+ await checkEndEntity(certFromFile("ee-SA-CA"), PRErrorCodeSuccess);
+ // end-entity has extended key usage, but id-kp-serverAuth is not present =>
+ // failure
+ await checkEndEntity(certFromFile("ee-CA"), SEC_ERROR_INADEQUATE_CERT_TYPE);
+ // end-entity has id-kp-serverAuth => success
+ await checkEndEntity(certFromFile("ee-SA-nsSGC"), PRErrorCodeSuccess);
+
+ // end-entity has extended key usage, but id-kp-serverAuth is not present =>
+ // failure (in particular, Netscape Server Gated Crypto (also known as
+ // Netscape Step Up) is not an acceptable substitute for end-entity
+ // certificates).
+ // Verify this for all Netscape Step Up policy configurations.
+ // 0 = "always accept nsSGC in place of serverAuth for CA certificates"
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 0);
+ await checkEndEntity(
+ certFromFile("ee-nsSGC"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ // 1 = "accept nsSGC before 23 August 2016"
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 1);
+ await checkEndEntity(
+ certFromFile("ee-nsSGC"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ // 2 = "accept nsSGC before 23 August 2015"
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 2);
+ await checkEndEntity(
+ certFromFile("ee-nsSGC"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ // 3 = "never accept nsSGC"
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 3);
+ await checkEndEntity(
+ certFromFile("ee-nsSGC"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+
+ // end-entity has id-kp-OCSPSigning, which is not acceptable for end-entity
+ // certificates being verified as TLS server certificates => failure
+ await checkEndEntity(
+ certFromFile("ee-SA-OCSP"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+
+ // intermediate has id-kp-serverAuth => success
+ loadCertWithTrust("int-SA", ",,");
+ await checkEndEntity(certFromFile("ee-int-SA"), PRErrorCodeSuccess);
+ // intermediate has id-kp-serverAuth => success
+ loadCertWithTrust("int-SA-CA", ",,");
+ await checkEndEntity(certFromFile("ee-int-SA-CA"), PRErrorCodeSuccess);
+ // intermediate has extended key usage, but id-kp-serverAuth is not present
+ // => failure
+ loadCertWithTrust("int-CA", ",,");
+ await checkEndEntity(
+ certFromFile("ee-int-CA"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ // intermediate has id-kp-serverAuth => success
+ loadCertWithTrust("int-SA-nsSGC", ",,");
+ await checkEndEntity(certFromFile("ee-int-SA-nsSGC"), PRErrorCodeSuccess);
+
+ // Intermediate has Netscape Server Gated Crypto. Success will depend on the
+ // Netscape Step Up policy configuration and the notBefore property of the
+ // intermediate.
+ loadCertWithTrust("int-nsSGC-recent", ",,");
+ loadCertWithTrust("int-nsSGC-old", ",,");
+ loadCertWithTrust("int-nsSGC-older", ",,");
+ // 0 = "always accept nsSGC in place of serverAuth for CA certificates"
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 0);
+ info("Netscape Step Up policy: always accept");
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-recent"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-older"),
+ PRErrorCodeSuccess
+ );
+ // 1 = "accept nsSGC before 23 August 2016"
+ info("Netscape Step Up policy: accept before 23 August 2016");
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 1);
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-recent"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-old"),
+ PRErrorCodeSuccess
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-older"),
+ PRErrorCodeSuccess
+ );
+ // 2 = "accept nsSGC before 23 August 2015"
+ info("Netscape Step Up policy: accept before 23 August 2015");
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 2);
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-recent"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-old"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-older"),
+ PRErrorCodeSuccess
+ );
+ // 3 = "never accept nsSGC"
+ info("Netscape Step Up policy: never accept");
+ Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 3);
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-recent"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-old"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+ await checkCertOn25August2016(
+ certFromFile("ee-int-nsSGC-older"),
+ SEC_ERROR_INADEQUATE_CERT_TYPE
+ );
+
+ // intermediate has id-kp-OCSPSigning, which is acceptable for CA
+ // certificates => success
+ loadCertWithTrust("int-SA-OCSP", ",,");
+ await checkEndEntity(certFromFile("ee-int-SA-OCSP"), PRErrorCodeSuccess);
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ca.pem b/security/manager/ssl/tests/unit/test_cert_eku/ca.pem
new file mode 100644
index 0000000000..790e942fbd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvDCCAaSgAwIBAgIUbYeck7JVOWdVm1AGwsw/DzdRnaYwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNTAxMDEwMDAwMDBaGA8yMDM1MDEwMTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggEBAI0AO+ZBY1oImSrpifcmQZTE/C4xIu7Uu5GX/A7ApQU7
+8UAXivMgRDbtrjOie1HNH9DIxHBCFY/Y7f0VRXxWmPEmT+5LpHrLoi+YF0h2wh/1
+RuiJV1AfaEdVJyNCVDSQrS8BQG5O3LebBq00gjSJSQ4+DHu7YHWkyMIZk+lbBiO1
+GsD0FWBDlOtiMpL/CQWjyiskiqQjrDCs5m0NayqgzYAMtdlEd+pAKEMNO8Fr8xSI
+tAlcG4frvH0kLJ2scX9ayvKTZrAiAxJz9CjmmnXOyL78yyr/hkJbzYTvk0+1cClg
+J2aGnsFIHgUxEx7sApOqlmG8g1lqL7UPqpi8ItVNl48=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ca.pem.certspec
new file mode 100644
index 0000000000..c6e443f5d8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
+validity:20150101-20350101
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem
new file mode 100644
index 0000000000..b947abe69f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAgIUC5X2jRE2ClirTUZa/5o/butQdrgwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEDEOMAwGA1UEAwwFZWUtQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9
+sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5
+TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7
+xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHd
+tMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l
+8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsG
+AQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQCaHEnFif4rqZGKOtXrnXTCvFvkxr7i
+hBU/zInfRn+9Q1+k4/yyjdThNNLCuTVu3sMlWjIbZGvF/iudIN/m2ZkT4qOmi+LX
+fy7a+QEEFLPZy+CCAQ3KHX2p+LEf9rnuUVOiqub9/lxD/KJOBV6SOwVGU+D7bxq4
+/+Y1mVFpxlGqQgHgSroZCPDAquRVYJC43x+lrlPNnX5fOetd3ipbZDcEdLf6Vy1A
+RsWVyFgJhVR8Uw267iI9/j9rIbChLY4aQNxTmH6/Knz+KYussltkcotgEu5YAQSs
+y7VL3y2JzlLQaq/0WorvkjHjZ8F4eILSS1fGAHrK6HT1eJsDxJzYVayO
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem.certspec
new file mode 100644
index 0000000000..d49cabaa2f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-CA.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-CA
+extension:extKeyUsage:clientAuth
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem
new file mode 100644
index 0000000000..a9fc97c5b0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUFZM7fDUSi2PQ/ZLQ1JsWSrDyX7kwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEzERMA8GA1UEAwwIZWUtU0EtQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg
+2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ
+5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQ
+PdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGj
+DJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8W
+iy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjITAfMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAd6LFItpHZW0s
+kKR7utmFTcQmuSXg6OJF7DSbF0bJkVfE9L0ZrYc4ATp3M9zTxlZAVKJIFX8RPMp4
+tNPQrfsa3jQGWzRdIv6dtsHau3EUVrNQwdsErtko3LUr/W6anHy9VTOJuXWQ//qf
+eBiZHOfxf2WAZCjDcOP8k6/rttbrpka0Y1VjPzNNXJR83lLk0ADxt2R9R6/PYsq1
+uxuYbBbkmB3vlLrfpYw3yCEXkaAByTCvCALaEXzC9vS1gDxhPvucS122A9GdCe8j
+8wkHMNFpfeCafqTypD6KDS/6yl+GEiB1ErHM2pkwn9oromh9R9M1ILkBtICy+w/W
+4FqGVfffaQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem.certspec
new file mode 100644
index 0000000000..5250cc4a84
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-CA.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-SA-CA
+extension:extKeyUsage:serverAuth,clientAuth
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem
new file mode 100644
index 0000000000..de6b780718
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1TCCAb2gAwIBAgIUO/5lR94sxaHEzsErl8RFZ8quniAwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFTETMBEGA1UEAwwKZWUtU0EtT0NTUDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMhMB8wHQYDVR0lBBYw
+FAYIKwYBBQUHAwEGCCsGAQUFBwMJMA0GCSqGSIb3DQEBCwUAA4IBAQB+jNOQ3Nti
+BB0Vi9y2+E/TMcrR+A0YNJpGQfxbH2bPs1RONwq+b8/RXzTb5OXkh02gb94inOn6
+E8uvxwZeFNp6hRdJWrUMP/Fw0H5zn7F/X7h9yRGDV3aICULFK9G/c26jgiQY4+P8
+j61EnAaEI68KDXZ/QFSrH/nxlb+xOl+MZQfnD4jEbyGWknL6RX2uhOuc3c6mzaEI
+FkKRXtLuAnGaaBwnjPenNKO7wPKaaF5wo1JcO5uZqaEI/hkw3WXUF25HBhq8ntx4
+VYKQaKQwzwLvptLDxUsf4nhbyQYNSxUJq/L2hnZDuiLFQzWiK4cFnOiBeojZDoq4
+gVp7BdWxFL7A
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem.certspec
new file mode 100644
index 0000000000..3b3eff9ae4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-OCSP.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-SA-OCSP
+extension:extKeyUsage:serverAuth,OCSPSigning
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem
new file mode 100644
index 0000000000..70f5bd54b4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1zCCAb+gAwIBAgIUD6t9UhitEOD4JaK87FphFLXfkgowDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLZWUtU0EtbnNTR0MwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjIjAgMB4GA1UdJQQX
+MBUGCCsGAQUFBwMBBglghkgBhvhCBAEwDQYJKoZIhvcNAQELBQADggEBAIveO3ip
+QuHtl08d57tWw4yCLEvkm00QHiW45hvkB7sAc/yc5oQ6Ctas8kFeiQlFov0oc855
+H/m1/+q69AhjloTCRP56ETxQD8XOYpJ65Z+h7xLJ0ePb16KzFdP4D/RrrP88Q+IW
+P8y80sk79ZYFpXw/pQVWmeYlZ/2B+0lRG2Qr14Y87oJq58e2v3D8JVTYIaqvcUUV
+KL+vvz2VD2oIRVcHkPKRDziC71au/uGZNpaRFiJLrqMXp5tbSTnTfvEU8UQNvWsD
+up7hx5Sjnm1hih9NxNNYC0JqjqAe09fPLuKEpmhkMs+cQBhQkgGum82WOu/BVgTA
+bOzdhBReGuu6di4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem.certspec
new file mode 100644
index 0000000000..4c51425ce7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA-nsSGC.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-SA-nsSGC
+extension:extKeyUsage:serverAuth,nsSGC
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem
new file mode 100644
index 0000000000..72fdae0143
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAgIUO1FsmRgdyx0D0iMZXEesXr+RpxswDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEDEOMAwGA1UEAwwFZWUtU0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9
+sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5
+TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7
+xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHd
+tMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l
+8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsG
+AQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQBHY3Yp7JCSy1kJOdflxPplNuPlU5i/
+WTQbM3LKjibHKefWe22GAKIArymZWtE5/1OLUOyicB2bd4AdLA2dflTN2kZ0isVd
+K4Gk5z7o+i3rSULI5DeAhlAHPDMP9+NV1os+j/c5eK+CKAaWEJtcH+khDeb6kH83
+1IQDAgvYOpXrZTnoC+dKc/9esdmAzEWeP05F5PgfFCTAjKX2voMmnJOjGtSoV/UM
+76RHmB3Z4tLeJ85eXU2k6E+RwMrb6JLZe+xfeInN3EE/g4dfnh+/KAHhgLl7MTyn
+GRIlTXisaOYe27wGK4/O9vuvoaQu4A5QrZcUp9lFv7cD1Fz1E8zBC8oY
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem.certspec
new file mode 100644
index 0000000000..690f579afa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-SA.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-SA
+extension:extKeyUsage:serverAuth
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem
new file mode 100644
index 0000000000..cc1e10404d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtTCCAZ2gAwIBAgIUZaXG7JEsTaOaPw87/nUy+/eUmqYwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBQxEjAQBgNVBAMMCWVlLWludC1DQTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7
+wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCAp
+k6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh
+eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KW
+EsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONssc
+JAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0B
+AQsFAAOCAQEAigF8fmjv72/13vJXo6IeNjrY7B7Vo4ojlCP+3MjGBCgCXy1Bm34p
+X8t4DcJMZ9e4QPkGpA30jl/TymxaFUA/Hr2zezdJ+uU66qPWzzsiQCYZVXpkHgDL
+yd2MpiQZeIoX7t71BdxZbikajedXutn36U/ZIehZcSeNeaz/Fd8MqABVcvvjuTsn
+0Lbt8eubJml/x6WtQbDjPspI25oQwbQz3stXOGv4uBfuALKzO9AtlAXWpS/JFhhs
+4rqa8VEqHKGtP8fSieSsqhy4R98gR/PfYS/XvnRo71U+/m2iGxMizz7/FIuO13X+
+Xq1+S2fKey7Z3ZbA8+zdfHwlZpBydKAkMA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem.certspec
new file mode 100644
index 0000000000..670973930d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-CA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-CA
+subject:ee-int-CA
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem
new file mode 100644
index 0000000000..759812ff9f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuzCCAaOgAwIBAgIUY8AAXoT/5+XKoZB/qHd8n2QKXn0wDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJaW50LVNBLUNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAy
+MjAyMDUwMDAwMDBaMBcxFTATBgNVBAMMDGVlLWludC1TQS1DQTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs
+9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8
+HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7Ak
+kqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJet
+lmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2r
+kQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkq
+hkiG9w0BAQsFAAOCAQEAongard/cqAypTPlDgt8nFQQxFkNgZ0L0izzNhbZ2VuYM
+8+gxKgT76VhixF5fM4aojpTKpvfBjMMVfBeYc9zLjad4jIvroYEzRTuOVeHhsVyf
+sre2adRS1Z1rj45x5G4N6KriZimKJGgaZeBeTNA4rVO6Tf9mUzllaHG9e3VhPXCR
+Pzqhb29lpdIkqn3TZR+RU/gRHLgk5uAL5d3izvI2xmH16zWdtEULjPA6fATiLPDi
+36dmkUFNBQBQ74xZBBmj98R0FpVGCEPj3yX8/hjYT4R2ixhLLZXrybLwSbVD7MVq
+GOnF+ezXULHmbUkNUO1expuWLFnrOTqsNMFOse453A==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem.certspec
new file mode 100644
index 0000000000..bd012ab712
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-CA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-SA-CA
+subject:ee-int-SA-CA
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem
new file mode 100644
index 0000000000..0b442fb3ce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvzCCAaegAwIBAgIUG6TTZ+C/RpJ/4TcDy/SngGYRzAkwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLaW50LVNBLU9DU1AwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowGTEXMBUGA1UEAwwOZWUtaW50LVNBLU9DU1AwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erk
+NUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwC
+fs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1m
+CyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTM
+HGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m
+1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEw
+DQYJKoZIhvcNAQELBQADggEBAFPckRG2h/ZdBJmtCRQ0sLB76hwRRHxm6WMyCzUM
+V5kOmpgcIRIlbSJmYCVBzgcmVgT0q/6eK6qg+vCnrhBthv+lx+ZI42x0QEatyL0X
+FTqwEe5x0nnw6wqhDuFBQ5KGOyGfb5fRk3cT7m6Fya00X/87uPs/HuoYBJsnschJ
+2zCyP3yGQRiiQ8moBvQZvJBJ1wwlNPduMLF9WhQRxW6ijxZGiNctHmZU452ol+3/
+jOK2N7FvkK19f+fzMa0y1bWE8M68fIIIylWEpiYSrzCO04nf6TD6HK2Ss25qCoKS
+dzowS1bL9TdUmwbCXnL/CyXSDWHMaAhNWD/DEwL6t3altBc=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem.certspec
new file mode 100644
index 0000000000..2374d248f6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-OCSP.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-SA-OCSP
+subject:ee-int-SA-OCSP
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem
new file mode 100644
index 0000000000..ed4d21e745
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwTCCAamgAwIBAgIUQ+NMzLj5C9j8fDWZOGMQ0w7huRcwDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMaW50LVNBLW5zU0dDMCIYDzIwMTkxMTI4MDAwMDAwWhgP
+MjAyMjAyMDUwMDAwMDBaMBoxGDAWBgNVBAMMD2VlLWludC1TQS1uc1NHQzCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+ATANBgkqhkiG9w0BAQsFAAOCAQEAMQ6PCkVUC9IQH/ZQippzUv5UWQo+YagnX13p
+10m5+Qt4OT/qxaj1hKbSnZrgZfphDoQ/8DKmHKFuRH8sCNXloc1XNrgXDb959BH8
+1R/BreULnXn7avmSycVTW06XuctRfozB15YjG9V6EUQgPOyzWeK7qmtJWJSxlsMw
+JxRtbD8M+UOXHO8mmneRjz0N7kzEEOhr97sr/XCbcRuc81PU9n8X3gqITEyvBRld
+MqXNuVF/cvN8DJG9A7gKQ6NWlpks2k2U2k93je5lsofLmdQFQjc8AQ63aW8i9Fa8
+kBOWlHNVMK838BSR8DPT05Pc6f6kAT9biLMx8Z7z3/5AmbJXlQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem.certspec
new file mode 100644
index 0000000000..6c3cb64730
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA-nsSGC.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-SA-nsSGC
+subject:ee-int-SA-nsSGC
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem
new file mode 100644
index 0000000000..31833bde84
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtTCCAZ2gAwIBAgIUWSf0ySmH86e0yX7mdf5oq7s682cwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LVNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBQxEjAQBgNVBAMMCWVlLWludC1TQTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7
+wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCAp
+k6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh
+eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KW
+EsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONssc
+JAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0B
+AQsFAAOCAQEAfaFAdO8ATfh6LHVfh/R9TVdi+khwSItsGLXJO3XEJd5luGSb2bkH
+HM+9TWZmBC/LHhtnm5RDcQ9qSPSDoj7KRHPXccN0mLjTwr/nMnZaqG8P/khxsb1V
+8vQvHVz3XrNXbothv8TCidYPSain7Zr3j8iB8bArzwOL5gf0z7xyDtpnsRMkTd48
+/cOhY+YA5vx+mNE7NUVUSMk1jtfg7Q8FVSGVOzZlRFoZ7IxlDN7nt2fTepPjuYDM
+NTiJ1qLwkve2mig0ZPzov4eTH+i/gkFC08Kqc9IKI14VmFT72Lxu6pDTAeO4bEQm
+Jm5i9Z4ffgJmbL8nUC4IDTOau4eGoTjhig==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem.certspec
new file mode 100644
index 0000000000..72ddb78dfd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-SA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-SA
+subject:ee-int-SA
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem
new file mode 100644
index 0000000000..8e8d4c2c04
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwzCCAaugAwIBAgIUB/nCi+kTmIkobMVqQFtJnevhGwEwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNaW50LW5zU0dDLW9sZDAiGA8yMDE2MDcyNDAwMDAwMFoY
+DzIwMTYwOTI0MDAwMDAwWjAbMRkwFwYDVQQDDBBlZS1pbnQtbnNTR0Mtb2xkMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08
+E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc
+1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAP
+DY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQ
+gAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqV
+YR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQID
+AQABMA0GCSqGSIb3DQEBCwUAA4IBAQBcxzg5hx9tQ57Jv3wibAhPktjmqapB2suH
+eGE253Xn5haGq+0E3Qkmn7oF1ou6QyLvP+qbElhkx7+eEvhMhzRZFU8XmxzHRqbb
+dZ0/MQw+aLEOc4Utl+1n3pDbVhBjoG3yVxyErndHfyXg+hNPMBEPvtfMYnSkKHX1
+3w3pJSs8HtUNMG3jecwXOJic0kUu6V5npNC0KTeZq4OOzp48WCpEfsQ6W5MzR4PN
+mrCVU2t1GzCKRNA8E8a9unOcxnukz8FaY9RCkeW3cTwRNt0VaFprX32d1C4mz5rV
+vNz/TGA0jy2b4Mg/dpGXrxqyRy1mG3zfs7sDhEoCxLsd/7ytu/bs
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem.certspec
new file mode 100644
index 0000000000..a21b49b783
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-nsSGC-old
+subject:ee-int-nsSGC-old
+validity:20160724-20160924
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem
new file mode 100644
index 0000000000..4acdf7b59d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxzCCAa+gAwIBAgIUYfx/2lyIWKMRlK/zS6/kmkp+t/MwDQYJKoZIhvcNAQEL
+BQAwGjEYMBYGA1UEAwwPaW50LW5zU0dDLW9sZGVyMCIYDzIwMTUwNzI0MDAwMDAw
+WhgPMjAxNjA5MjQwMDAwMDBaMB0xGzAZBgNVBAMMEmVlLWludC1uc1NHQy1vbGRl
+cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogG
+NhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqn
+RYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHu
+p3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQ
+Lzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p
+47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo1
+7Y0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAbCqK5OcHMj28V1PKiL4CHeN2oTCj
+pObUJrAUqsHJbqS2Q+huzzoSsL+yT4uKeiuDMZnKOm8OIZMtM7M4zQb6b5H1Wqm+
+fM5384kUubmSG+CzUd4adK8NjSwgYhgdvAecbzhPS1GUlai9qDJU+elQVIrNZCL4
+6B634koCQkarYybibdXgoum9zzcla0TRXe9rHjb8VYe+BH7tLAnWGpUqw4x36Buz
+gv605ApVb+QDAB9NH4/EZ1TXuKby6I9I19EkfADHL5XUKHGFH2EE+6Fsqihgu3QG
+NRYMU2GL79bZcwOm2t2v+ZB2fp+UlEs8nykWY7PgwbJ+ObgSauNHhKi5DA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem.certspec
new file mode 100644
index 0000000000..93e1831a32
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-nsSGC-older
+subject:ee-int-nsSGC-older
+validity:20150724-20160924
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem
new file mode 100644
index 0000000000..557c8276e8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUE/PT07mikR8SeEYcqdLT8IL5vMIwDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQaW50LW5zU0dDLXJlY2VudDAiGA8yMDE2MDgyNDAwMDAw
+MFoYDzIwMTcwODI0MDAwMDAwWjAeMRwwGgYDVQQDDBNlZS1pbnQtbnNTR0MtcmVj
+ZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62
+iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHql
+WqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosq
+Qe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+
+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8i
+b2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoY
+CjXtjQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAKnYtenyuUSykB8EP4IkEeeVPo
+PeZpLKEHHr9CnjIC8b0lHhTsFQhIiNsCWt8xh+JS273QJi/A2rdo1M4KiMZyURne
+RZvTBpFu5F8E+VixDP9xmpAffrgqPqgNRyN+co7tUQZgsJHhb8LSKKMHQTMTBOgW
+nrVlMR+MwXBBQx7Yc7KkVob8DMWmIAAXlv0JY6ID2lRb4haBVS1gsPIj24mVF8XG
+dRJIgtVUvey49/PvErG6FNA32QPDCyv9UOkX9mtpnu9dT+v6mAkbRcSDYAsTTWh2
+seog8fOBA8WoqO+EtTIY2VLiO2G8pTp8MftYVc1Mhd5Kh/ZT+PtdngloPnDM
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem.certspec
new file mode 100644
index 0000000000..4a3ad8ec05
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-nsSGC-recent
+subject:ee-int-nsSGC-recent
+validity:20160824-20170824
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem b/security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem
new file mode 100644
index 0000000000..46f729df5c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyjCCAbKgAwIBAgIUOcdHr2fMC/woQI+w/G6U646FIE0wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowEzERMA8GA1UEAwwIZWUtbnNTR0MwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg
+2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ
+5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQ
+PdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGj
+DJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8W
+iy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjGDAWMBQGA1UdJQQNMAsG
+CWCGSAGG+EIEATANBgkqhkiG9w0BAQsFAAOCAQEATjPFb+Y7HWDeWq02niJ4Puxu
+T68/Q/P1M9BCeLMCaZAybxCJ1FDR/aOGfZMF8TQlx77wYz63j5s4BVjM1gLwl59w
+EQK1xaav37Jt5IxfTFVBkJovOnDZFkDa3s+ebZfFpXl5pUFCZc8XOzkV/dy5mbE+
+baYZUikhVIDrvRmWcbXwzhEJhl5qfzgFkiWP9vA0vkVt3NAMuPclh/6L9844d4TN
+uVo0C6X6I9jK3X7x2skLk00RT1MeigOE9o6Xuu0SYujDOXBAeOcjCE3mFJZQUr71
+FkD3Exrxo/UjbgIpLdBNLp2TEDR5bbwUIkk6bZy37vDa2xYCxe+Ret3ykpxshw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem.certspec
new file mode 100644
index 0000000000..43d58ab6d4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-nsSGC.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-nsSGC
+extension:extKeyUsage:nsSGC
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem
new file mode 100644
index 0000000000..ec83db25ec
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1TCCAb2gAwIBAgIULaosvYiVvbvMj07BsP9K65Ea2yMwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowETEPMA0GA1UEAwwGaW50LUNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/
+MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBRB7aEaMyA
+kIN82mO+ZICrTUqFqe/0LHF4N3c5hR/JqjHGnrYcyNnewYccQXMQhZhZu1aRfZ8o
+bs5Hz52teZsxjL8udA0c75bF48Dd2BHT++lHV4AHO8EZsW6SwF5NDGGJH6B8bRq9
+5FoiuOtWHan8MFTqC5FxHeaXsDiqS8MlEojSTvkh5Huq9HQAEAziJFfUyRZ1W8qw
+m0GNX+/iXvqj0g0vrIBkVnPnUgHuglQITcH8luYhBJlv7I9YE55wNMCIf/xcHJro
+htKzXzaJvdXeZrNlOLTzhEt8scv0sOF/tgiqVYpIHB7ku5x0Qnw7NRf6j9zDdzqm
+SlKm9uRBg4LX
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem.certspec
new file mode 100644
index 0000000000..e5bc18198c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-CA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-CA
+extension:basicConstraints:cA,
+extension:extKeyUsage:clientAuth
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem
new file mode 100644
index 0000000000..9ebd34f0df
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4jCCAcqgAwIBAgIUU53rn1mYUmAkdWEYl4H8CjVjAqUwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFDESMBAGA1UEAwwJaW50LVNBLUNBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo
+4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD
+SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX
+kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx
+owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/
+Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoy8wLTAMBgNVHRMEBTAD
+AQH/MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsF
+AAOCAQEANp5URW6l61bXiiNt7GQU/Q5bXrlHO31ipGzyo1t936xejdsnEZuRqc7i
+QCKsCDynX7z6+hqyjFiOx7hTxh3tek/LICciZfigatB0mGqK0SsqaDMPbPvJgPCT
+PDJUskEI92R4Qzelvol46F/4mZUQTOvoCASZRcr4RefqF/ClcE167XqZDn2DYxn/
+jpNY+Bs+VINWEgaqPbznEh3sQHksmweUWW49deIidAZGCwIHJFfFOegT0xBSRRdp
+BN2WV78zIr4LFsrpwSKD82LJ2jdeq86hrc9UXJ55+JffNXtsWxXr/vXxiNEW+qJ0
+g4OGIAY9XAF/sJ2iPJd5ynaCmpEqVw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem.certspec
new file mode 100644
index 0000000000..94e9a42d48
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-CA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-SA-CA
+extension:basicConstraints:cA,
+extension:extKeyUsage:serverAuth,clientAuth
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem
new file mode 100644
index 0000000000..e37d294074
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5DCCAcygAwIBAgIULf8za+LuAorq+vV7GCo4dV9SG8MwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLaW50LVNBLU9DU1AwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjLzAtMAwGA1UdEwQF
+MAMBAf8wHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMJMA0GCSqGSIb3DQEB
+CwUAA4IBAQBuugyWnIWv5eql67JO/rdh5JswvOfsmDql24twefbVILsHDMKHNMm7
+Xzq7N2lQ0Ye5hy1G0Tmpgd6GV8Yyi1lWwRmI0zZCsRWDYeodbXgDYbDoBBdzNUeH
+KpPqV7PCkdMgoJp8AAlYOJnNKw9ARAQBe6ut5mq1fNgpzRGEeHpl0WVFKF0c1pSd
+snNLv8LgtCOXdbpc+gHEZ4CLHffVic1cVWg2M+EivuZsFZRPUMFlfevlC5Uh9ea9
+UWvp3Ta3ipZX+XuCTkmzDMLRNm6pM/Je6yKo8zywxLgGt+kpK6itSLEMmQFql49A
+NWS74xUiOXJWsE0+YeZ0aaNSPe0tFyLX
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem.certspec
new file mode 100644
index 0000000000..c38a640b9b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-OCSP.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-SA-OCSP
+extension:basicConstraints:cA,
+extension:extKeyUsage:serverAuth,OCSPSigning
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem
new file mode 100644
index 0000000000..c149580a8f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5jCCAc6gAwIBAgIUVWq3GjxxzfgGFcE6ypKcX3YnKZEwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFzEVMBMGA1UEAwwMaW50LVNBLW5zU0dDMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozAwLjAMBgNVHRME
+BTADAQH/MB4GA1UdJQQXMBUGCCsGAQUFBwMBBglghkgBhvhCBAEwDQYJKoZIhvcN
+AQELBQADggEBAFUF48SjYDpRRNNlGynD/g4jHOW5TTdUueiEWNCYEayldMMe6Rhq
+kkEhY+3yC5Ys94YZFoiWaPmlR9FGFNdvjYV+abyElqYoSWO99qOJUldvm4xFwGQr
+TwalO2jqwy4qtos/qbw6M/dflrRQzEZ5Cf5yYLwm9E18ALbuiU+B2Jp3n5LQ6T1Q
+dDt+FkP3LlCWde+Y3aFOY+aLCkXgLwCOVCluwet8/IsKTGD6qzJhd3R2f1fnX8EU
+EBfhaw850FpB8Xn4OvDYd+rEHSURNsNGxLzMVxhoBZKTzbwim5Cwem/5n8epRheW
+L3eKTia8yBvTtvxDE3+iKkR+Oz9ozOGp6Ys=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem.certspec
new file mode 100644
index 0000000000..c84201d874
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA-nsSGC.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-SA-nsSGC
+extension:basicConstraints:cA,
+extension:extKeyUsage:serverAuth,nsSGC
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem
new file mode 100644
index 0000000000..5dac5b5c24
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1TCCAb2gAwIBAgIUV3pjDiG/4yqDPtPjv5HyVpDHrncwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowETEPMA0GA1UEAwwGaW50LVNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/
+MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQABgZe7+5NE
+2Ui9SdwymCBbH8KrZcPKWRCBb/iCxVMbt39k+MQPaQQgxEKqGiYYCE79yJo0HznF
++gN/Jc62tuY+oBUc+E3MvULqR6AMB1qjSs48uq57aunoE5gDgwoGU/gMA1Mfd22s
+X08jcbpWfsyDVglzeK9J/Ihc7QPnBk3iMnv10TuFq+8/C6E+2ZQmTYd9CjVAPyS0
+C1DVIBoPsRrcWlh5cKVXQIVBECGLNAIIXbPGcO6tirxOtNG6ehnROfQn6hBqZQrs
+LIrrRHHGuF+eT9PV3Eld4puoHQ2FYPo8lK41xqqbZJYW7a9kP0EEJVQYLrwcM/0i
+haT6qq3JwqKA
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem.certspec
new file mode 100644
index 0000000000..74bec2b21c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-SA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-SA
+extension:basicConstraints:cA,
+extension:extKeyUsage:serverAuth
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem
new file mode 100644
index 0000000000..e0deef7c3f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3TCCAcWgAwIBAgIUbXK8dZ2kTHVUtKZ1Uamdr5h27tQwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNjA3MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowGDEWMBQGA1UEAwwNaW50LW5zU0dDLW9sZDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7
+wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCAp
+k6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh
+eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KW
+EsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONssc
+JAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMmMCQwDAYDVR0T
+BAUwAwEB/zAUBgNVHSUEDTALBglghkgBhvhCBAEwDQYJKoZIhvcNAQELBQADggEB
+AK/99QYaWy5/SG7iKiGwSgJ49hobsVUf4DTN9+4FIesvXNUtXc+vmPVNwgy8S4Kn
+23zOUmp2LaRBQ3VxaiQ0o/RN3ZjULhPhGi3cHsjDTKXH3U0snAloUYONx1JCFOif
+RWwLeiyps1oW9ARfUQrbQRtw87ospU5aJ7JSPoVgbCMXubmEpRSeTMv0SHdOs6g7
+ahSaoT7BBRuHDnCb8+ZR509H9Dc4M2Dv4lQ9vlWGZjXnfM1ImIeUBTWQ0fngyoZW
+neTStGeL5MmCjjMMupJYbjNsenQXd9doD08+voOfmKgaMQDH0fgEACJHjmmt+eQ4
+j8WZP3lRZHZm/s23xNl+6S4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem.certspec
new file mode 100644
index 0000000000..35f61671ed
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-nsSGC-old
+extension:basicConstraints:cA,
+extension:extKeyUsage:nsSGC
+validity:20160724-20160924
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem
new file mode 100644
index 0000000000..dd709c343d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAcegAwIBAgIUITNR+z5zrXNAq+3y9u3et1fNp6YwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNTA3MjQwMDAwMDBaGA8yMDE2MDkyNDAw
+MDAwMFowGjEYMBYGA1UEAwwPaW50LW5zU0dDLW9sZGVyMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyYwJDAMBgNV
+HRMEBTADAQH/MBQGA1UdJQQNMAsGCWCGSAGG+EIEATANBgkqhkiG9w0BAQsFAAOC
+AQEAtkFQHiG8UeleNMgvJVneYQzIQt8wiZwudAH2FdiyeCtYUp95z19IzPlZeYq0
+FXlMJJ+SOn2wSZ9jHshV5VF8S4Rb3ou0EVJcu+X7cs3jOuLtV/KwFDKXrncWpF7L
+sH1yGVkAUNMQlE52MgPxg7P6yd+lbU9MlqD5rpifsAecrXzctzi8l3qb2UJvkHku
+BLE+ViTnn9RFDQFQ/1AAF9cbSJaUF0rwWznXpB0PAu+v9lBpn9a83/GXZc1SO2mE
+MIdgRpuB1Lr+wZq5ulXHAeBJ+kH8lrG2CIXSQPFQjPfiS2D5hM1cdd7GSEh4BoZv
+AbTKG12NXN9AG7RpG7upSBG4mQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem.certspec
new file mode 100644
index 0000000000..f7a870c0f1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-nsSGC-older
+extension:basicConstraints:cA,
+extension:extKeyUsage:nsSGC
+validity:20150724-20160924
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem
new file mode 100644
index 0000000000..c5c8e724c2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4DCCAcigAwIBAgIUec5AldPAXDY+y1Q7RfO4SfvteiEwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNjA4MjQwMDAwMDBaGA8yMDE3MDgyNDAw
+MDAwMFowGzEZMBcGA1UEAwwQaW50LW5zU0dDLXJlY2VudDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMmMCQwDAYD
+VR0TBAUwAwEB/zAUBgNVHSUEDTALBglghkgBhvhCBAEwDQYJKoZIhvcNAQELBQAD
+ggEBAJO1C5eYEk2/+xvgWsCsUZi7PMoZLhb5Jb2V61JMWREfCozmYepqlamR6IZV
+fn5Q9OClLtAzSybeia40nsW/xb/o+5zJn0rPzk6JIsMToJk07fqp+uG9LbAM82IV
+RnHzmCS4/3n4fl1k1GGL0A/NYBUsilY9oKhVl1zukB/z3ALp9LyProNLsEZmWkl7
+F07+lR9PPMOqA1SuDJmZMyZ4cKDyYYF7NYKGi57xpgpUXGq8IPOKKGq4XlYqQhOQ
+sVSuc+16A+NlPwMogczcgTgy7QD6DF4WNOCDGwuF0YB7uROEbYvh4lkKJfhnAtb7
+1/xnmTVssnCTsbppIiXtKs8zzu0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem.certspec b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem.certspec
new file mode 100644
index 0000000000..f421ddc1a8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-nsSGC-recent
+extension:basicConstraints:cA,
+extension:extKeyUsage:nsSGC
+validity:20160824-20170824
diff --git a/security/manager/ssl/tests/unit/test_cert_eku/moz.build b/security/manager/ssl/tests/unit/test_cert_eku/moz.build
new file mode 100644
index 0000000000..ede7f4b548
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/moz.build
@@ -0,0 +1,35 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'ee-CA.pem',
+# 'ee-SA-CA.pem',
+# 'ee-SA-OCSP.pem',
+# 'ee-SA-nsSGC.pem',
+# 'ee-SA.pem',
+# 'ee-int-CA.pem',
+# 'ee-int-SA-CA.pem',
+# 'ee-int-SA-OCSP.pem',
+# 'ee-int-SA-nsSGC.pem',
+# 'ee-int-SA.pem',
+# 'ee-int-nsSGC-old.pem',
+# 'ee-int-nsSGC-older.pem',
+# 'ee-int-nsSGC-recent.pem',
+# 'ee-nsSGC.pem',
+# 'int-CA.pem',
+# 'int-SA-CA.pem',
+# 'int-SA-OCSP.pem',
+# 'int-SA-nsSGC.pem',
+# 'int-SA.pem',
+# 'int-nsSGC-old.pem',
+# 'int-nsSGC-older.pem',
+# 'int-nsSGC-recent.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null.js b/security/manager/ssl/tests/unit/test_cert_embedded_null.js
new file mode 100644
index 0000000000..c1b20c1664
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null.js
@@ -0,0 +1,54 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests that a certificate with a clever subject common name like
+// 'www.bank1.com[NUL]www.bad-guy.com' (where [NUL] is a single byte with
+// value 0) will not be treated as valid for www.bank1.com.
+// Includes a similar test case but for the subject alternative name extension.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+async function do_testcase(certname, checkCommonName) {
+ let cert = constructCertFromFile(`test_cert_embedded_null/${certname}.pem`);
+ // Where applicable, check that the testcase is meaningful (i.e. that the
+ // certificate's subject common name has an embedded NUL in it).
+ if (checkCommonName) {
+ equal(
+ cert.commonName,
+ "www.bank1.com\\00www.bad-guy.com",
+ "certificate subject common name should have an embedded NUL byte"
+ );
+ }
+ await checkCertErrorGeneric(
+ certdb,
+ cert,
+ SSL_ERROR_BAD_CERT_DOMAIN,
+ certificateUsageSSLServer,
+ undefined,
+ "www.bank1.com"
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ cert,
+ SSL_ERROR_BAD_CERT_DOMAIN,
+ certificateUsageSSLServer,
+ undefined,
+ "www.bad-guy.com"
+ );
+}
+
+add_task(async function() {
+ addCertFromFile(certdb, "test_cert_embedded_null/ca.pem", "CTu,,");
+
+ await do_testcase("embeddedNull", true);
+ await do_testcase("embeddedNullSAN", false);
+ await do_testcase("embeddedNullCNAndSAN", true);
+ await do_testcase("embeddedNullSAN2", false);
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem b/security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem
new file mode 100644
index 0000000000..48031d478c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUb2Np/S2xMtQR+BvitAuzKtjWxMswDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYD
+VR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBPEDwwrdd6h+SHkc0J+p4Ik/yc
+aoLWNdGsVD4hJYa0IOgNh6Z/GyhTXM9hr/q0f7fewjfNhXi/nrXNcW5w4FJERhHp
+HvZO6Z08i8CrVYSiijFcLf6ClK4e163j0LXUutBWY2PCf4TDS+fnF9PJmle0kPLU
+IxqptfSSoCjLuWyYyRmjZIdNCtrNCU9g85SDaUO6l79vBm0TYOLhOiPBN0F7DWXo
+JlOZKWEco6qqS22yIM1r/5KNc5ekPTD+UJFh7qprAq0riEfR38DYJmUq3w07q5Tb
+HQdG8JHEtemi7dHr/WoJY18MLZaF7BSLAwBWmBWzx8Lj2V+/4TP4NtRWkL7y
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem.certspec
new file mode 100644
index 0000000000..6660f5d478
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem
new file mode 100644
index 0000000000..3ec195d7ad
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxTCCAa2gAwIBAgIUN3cis45zT7JXRJUjglyGLKsRNY4wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowKDEmMCQGA1UEAwwdd3d3LmJhbmsxLmNvbQB3d3cuYmFkLWd1eS5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ
+PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH
+9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw
+4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86
+exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0
+ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N
+AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEH79W5n+TiJob4LWvypQNMj4Fi44pwh
+L/Pcpe+vXd19sN/UyY4WAH4S6U+Fu+wNlItMWWFQnlkibutTQQLpJhXTSR2q/ttU
+CWo9MdCJ8JGFwTReHDoajxdD2NJ/C8vK/dJeFNtCUXLAnwR2bk+F+StDQt6I6iS4
+EXL/A+y1L3jw9j/qVO8ewNp8QJvA8jOe3AQyI3q8fHENExUk0opo1Nvorrlj4xWg
+MP+NTIHZeJMGUfBw9vRwIZvfsx5BVQ5aqWpzVR9wxv7UCbO5NKeva57P6s+UeqX5
+rNxjzk0t7MV/3TZSTkMSVCUDc//sGUHMGk0F5fFsZ4/7SM+dO9+PaFM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem.certspec b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem.certspec
new file mode 100644
index 0000000000..d1a32349a2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNull.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:www.bank1.com\0www.bad-guy.com
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem
new file mode 100644
index 0000000000..6a1dd23bbf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIUP8GnOjltE+C0ll5IYA6asvsZ+/MwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowKDEmMCQGA1UEAwwdd3d3LmJhbmsxLmNvbQB3d3cuYmFkLWd1eS5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ
+PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH
+9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw
+4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86
+exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0
+ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N
+AgMBAAGjLDAqMCgGA1UdEQQhMB+CHXd3dy5iYW5rMS5jb20Ad3d3LmJhZC1ndXku
+Y29tMA0GCSqGSIb3DQEBCwUAA4IBAQAF6YEQrh2rMu+HEcySofn4x3MStqoXrqwc
+mn4r4cm4Qvrlubg+vewRan9b8pMryixRG8obsiEp+KGEB74XoSJmwumv8Ov3WL8V
+njKT2vAfVLgyZp4p+ZD3firGJMdzZcRDAU8Sg+yM0Vru3fx40piusBqeMbc7lOga
+AF+JCMtHPEJc32C4Y+itY0bedXUcBALljTHkam5nQ6+ShPk8X7wazKs8gn3wIBSB
+XmLZtmqEjzeI1UuUoyP3/AymCgo90Xi27aA8GGsxsmaUk5Am07Sw9WobLpeRoNgp
+c4dV4+2tcDqVM7nK2V4LKNlH9/2oRXoWnQNoDBzRZis9C6SeNV29
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem.certspec b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem.certspec
new file mode 100644
index 0000000000..1029d6cdd0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullCNAndSAN.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:www.bank1.com\0www.bad-guy.com
+extension:subjectAlternativeName:www.bank1.com\0www.bad-guy.com
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem
new file mode 100644
index 0000000000..7a9140f31b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6TCCAdGgAwIBAgIUAy5rhhLOHnC6Vr7TTF+tKn9VA+UwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowHjEcMBoGA1UEAwwTZW1iZWRkZWQgTlVMIGluIFNBTjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs
+9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8
+HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7Ak
+kqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJet
+lmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2r
+kQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMsMCow
+KAYDVR0RBCEwH4Idd3d3LmJhbmsxLmNvbQB3d3cuYmFkLWd1eS5jb20wDQYJKoZI
+hvcNAQELBQADggEBAEmpR9Bldw/m1N5yODTW8J5E1vIy8UbxtcNJnGZaQNCeCbXO
+r5d1EwqhGIsrVrYk4kjPD3gLPJuwAGh4yRVTiHWM+dCGf0EyzY5Ytlw42J0ny0PW
+15dRoEf07hKxnloqwXy3pAQqtoRyKgIY38xrSYcIAaGlt/wbuO469nzyD7QoamxR
+VcSosi0YxSlD8yye+E8fiZyBzB+fiD9W+JHX6mhYEznvtdW3J6zTG7aCOBEswh9C
+kVBpMu7gAvGrIviIrtw7EFYxqwmNQg8/KMfeitOoBH7aznq0qsVajkXLo2QrX4CE
+VzOabf4X7rLlcdqGN3fl4aDcZNuGR8goj4dgRkY=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem.certspec b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem.certspec
new file mode 100644
index 0000000000..f224888eee
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:embedded NUL in SAN
+extension:subjectAlternativeName:www.bank1.com\0www.bad-guy.com
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem
new file mode 100644
index 0000000000..e08110f6bc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7jCCAdagAwIBAgIUQx7QA9Wf1gacnL1geoQjuSqMSx4wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLYmFkLWd1eS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjOTA3MDUGA1UdEQQu
+MCyCC2JhZC1ndXkuY29tgh13d3cuYmFuazEuY29tAHd3dy5iYWQtZ3V5LmNvbTAN
+BgkqhkiG9w0BAQsFAAOCAQEAWyk3hxMIPXpSeKlePbth7wYv0kUF0RmT6eERgDtz
+h/0GIVYx7kaCNQLuBAaRCP/50Tj17R/gYWiPN7YXa1kGfgUYy25mOsrccMR1wvYy
+KDzCv4Fip0dDkYAVFRmpTHzy1JQ64dnVlgOkCWuhSb93XOcOJ651BAgPjRwzjDZ4
+vHYidUF61uOU3i/UsLrJJXHdojIgYsyfHv2B7aCkuO4ea1WcmwrS2YYW7DoDBft1
+/X4vRkWhJRKBrertLXCzpC/0ERO0pvGAQp1CJJIddIoO+XwCKnvvgsQI0ao34pJp
+Bsk3ZkIyNeYL5o9MAEAr8R4KkpZqACFWoyGqlwP9GPkqOw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem.certspec b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem.certspec
new file mode 100644
index 0000000000..d352d034b6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/embeddedNullSAN2.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:bad-guy.com
+extension:subjectAlternativeName:bad-guy.com,www.bank1.com\0www.bad-guy.com
diff --git a/security/manager/ssl/tests/unit/test_cert_embedded_null/moz.build b/security/manager/ssl/tests/unit/test_cert_embedded_null/moz.build
new file mode 100644
index 0000000000..57c9013ddc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_embedded_null/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'embeddedNull.pem',
+# 'embeddedNullCNAndSAN.pem',
+# 'embeddedNullSAN.pem',
+# 'embeddedNullSAN2.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_expiration_canary.js b/security/manager/ssl/tests/unit/test_cert_expiration_canary.js
new file mode 100644
index 0000000000..4519dd3935
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_expiration_canary.js
@@ -0,0 +1,40 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Attempts to verify a certificate for a time a few weeks into the future in
+// the hopes of avoiding mass test failures when the certificates all expire.
+// If this test fails, the certificates probably need to be regenerated.
+// See bug 1525191.
+
+// If this test and only this test fails, do the following:
+// 1. Create a bug for the issue in "Core :: Security: PSM".
+// 2. Write a patch to temporarily disable the test.
+// 3. Land the patch.
+// 4. Write a patch to reenable the test but don't land it.
+// 5. Needinfo the triage owner of Bugzilla's "Core :: Security: PSM" component
+// in the bug.
+// 6. Patches to update certificates get created.
+// 7. Test the patches with a Try push.
+// 8. Land the patches on all trees whose code will still be used when the
+// certificates expire in 3 weeks.
+add_task(async function() {
+ do_get_profile();
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ addCertFromFile(certDB, "bad_certs/test-ca.pem", "CTu,,");
+ let threeWeeksFromNowInSeconds = Date.now() / 1000 + 3 * 7 * 24 * 60 * 60;
+ let ee = constructCertFromFile("bad_certs/default-ee.pem");
+ await checkCertErrorGenericAtTime(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ threeWeeksFromNowInSeconds,
+ false,
+ "test.example.com"
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot.js b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot.js
new file mode 100644
index 0000000000..d69ea5105c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot.js
@@ -0,0 +1,73 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests that nsIX509Cert.isBuiltInRoot works as expected.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+// This is a certificate that (currently) ships with the platform.
+// It should be considered a built-in root.
+const sGeoTrustBase64 =
+ "" +
+ "MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL" +
+ "MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj" +
+ "KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2" +
+ "MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0" +
+ "eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV" +
+ "BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw" +
+ "NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV" +
+ "BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH" +
+ "MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL" +
+ "So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal" +
+ "tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO" +
+ "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG" +
+ "CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT" +
+ "qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz" +
+ "rD6ogRLQy7rQkgu2npaqBA+K";
+
+// This is a certificate that does not ship with the platform.
+// It should not be considered a built-in root.
+const sLetsEncryptBase64 =
+ "" +
+ "MIIEqDCCA5CgAwIBAgIRAJgT9HUT5XULQ+dDHpceRL0wDQYJKoZIhvcNAQELBQAw" +
+ "PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD" +
+ "Ew5EU1QgUm9vdCBDQSBYMzAeFw0xNTEwMTkyMjMzMzZaFw0yMDEwMTkyMjMzMzZa" +
+ "MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD" +
+ "ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMTCCASIwDQYJKoZIhvcNAQEBBQAD" +
+ "ggEPADCCAQoCggEBAJzTDPBa5S5Ht3JdN4OzaGMw6tc1Jhkl4b2+NfFwki+3uEtB" +
+ "BaupnjUIWOyxKsRohwuj43Xk5vOnYnG6eYFgH9eRmp/z0HhncchpDpWRz/7mmelg" +
+ "PEjMfspNdxIknUcbWuu57B43ABycrHunBerOSuu9QeU2mLnL/W08lmjfIypCkAyG" +
+ "dGfIf6WauFJhFBM/ZemCh8vb+g5W9oaJ84U/l4avsNwa72sNlRZ9xCugZbKZBDZ1" +
+ "gGusSvMbkEl4L6KWTyogJSkExnTA0DHNjzE4lRa6qDO4Q/GxH8Mwf6J5MRM9LTb4" +
+ "4/zyM2q5OTHFr8SNDR1kFjOq+oQpttQLwNh9w5MCAwEAAaOCAZIwggGOMBIGA1Ud" +
+ "EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMH8GCCsGAQUFBwEBBHMwcTAy" +
+ "BggrBgEFBQcwAYYmaHR0cDovL2lzcmcudHJ1c3RpZC5vY3NwLmlkZW50cnVzdC5j" +
+ "b20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMv" +
+ "ZHN0cm9vdGNheDMucDdjMB8GA1UdIwQYMBaAFMSnsaR7LHH62+FLkHX/xBVghYkQ" +
+ "MFQGA1UdIARNMEswCAYGZ4EMAQIBMD8GCysGAQQBgt8TAQEBMDAwLgYIKwYBBQUH" +
+ "AgEWImh0dHA6Ly9jcHMucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcwPAYDVR0fBDUw" +
+ "MzAxoC+gLYYraHR0cDovL2NybC5pZGVudHJ1c3QuY29tL0RTVFJPT1RDQVgzQ1JM" +
+ "LmNybDATBgNVHR4EDDAKoQgwBoIELm1pbDAdBgNVHQ4EFgQUqEpqYwR93brm0Tm3" +
+ "pkVl7/Oo7KEwDQYJKoZIhvcNAQELBQADggEBANHIIkus7+MJiZZQsY14cCoBG1hd" +
+ "v0J20/FyWo5ppnfjL78S2k4s2GLRJ7iD9ZDKErndvbNFGcsW+9kKK/TnY21hp4Dd" +
+ "ITv8S9ZYQ7oaoqs7HwhEMY9sibED4aXw09xrJZTC9zK1uIfW6t5dHQjuOWv+HHoW" +
+ "ZnupyxpsEUlEaFb+/SCI4KCSBdAsYxAcsHYI5xxEI4LutHp6s3OT2FuO90WfdsIk" +
+ "6q78OMSdn875bNjdBYAqxUp2/LEIHfDBkLoQz0hFJmwAbYahqKaLn73PAAm1X2kj" +
+ "f1w8DdnkabOLGeOVcj9LQ+s67vBykx4anTjURkbqZslUEUsn2k5xeua2zUk=";
+
+function run_test() {
+ let builtInCert = certdb.constructX509FromBase64(sGeoTrustBase64);
+ ok(builtInCert, "should be able to decode base-64 of built-in cert");
+ ok(builtInCert.isBuiltInRoot, "cert should be considered built-in");
+
+ let notBuiltInCert = certdb.constructX509FromBase64(sLetsEncryptBase64);
+ ok(notBuiltInCert, "should be able to decode base-64 of built-in cert");
+ ok(!notBuiltInCert.isBuiltInRoot, "cert should not be considered built-in");
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload.js b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload.js
new file mode 100644
index 0000000000..a8c6d09f2b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload.js
@@ -0,0 +1,124 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests that nsIX509Cert.isBuiltInRoot works as expected. Differs from
+// test_cert_isBuiltInRoot.js in that this test uses a preexisting NSS
+// certificate DB that already contains some of the certificates in question.
+//
+// To create the necessary preexisting files, obtain the "GeoTrust Primary
+// Certification Authority - G2" certificate and the "Let's Encrypt Authority
+// X1" certificate (copied below for reference) and perform the following steps:
+//
+// `certutil -d . -N` (use an empty password)
+// `certutil -d . -A -n "GeoTrust Primary Certification Authority - G2" -t ,, \
+// -a -i GeoTrust.pem`
+// `certutil -d . -A -n "Let's Encrypt Authority X1" -t ,, -a \
+// -i LetsEncrypt.pem`
+//
+// This should create the cert9.db and key4.db files.
+//
+// (The crucial property of the first certificate is that it is a built-in trust
+// anchor, so any replacement must also have this property. The second
+// certificate is not a built-in trust anchor, so any replacement must not be a
+// built-in trust anchor.)
+//
+// GeoTrust Primary Certification Authority - G2:
+// -----BEGIN CERTIFICATE-----
+// MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL
+// MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
+// KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
+// MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+// eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV
+// BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw
+// NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV
+// BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
+// MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL
+// So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal
+// tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
+// BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG
+// CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT
+// qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz
+// rD6ogRLQy7rQkgu2npaqBA+K
+// -----END CERTIFICATE-----
+//
+// Let's Encrypt Authority X1:
+// -----BEGIN CERTIFICATE-----
+// MIIEqDCCA5CgAwIBAgIRAJgT9HUT5XULQ+dDHpceRL0wDQYJKoZIhvcNAQELBQAw
+// PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
+// Ew5EU1QgUm9vdCBDQSBYMzAeFw0xNTEwMTkyMjMzMzZaFw0yMDEwMTkyMjMzMzZa
+// MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
+// ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMTCCASIwDQYJKoZIhvcNAQEBBQAD
+// ggEPADCCAQoCggEBAJzTDPBa5S5Ht3JdN4OzaGMw6tc1Jhkl4b2+NfFwki+3uEtB
+// BaupnjUIWOyxKsRohwuj43Xk5vOnYnG6eYFgH9eRmp/z0HhncchpDpWRz/7mmelg
+// PEjMfspNdxIknUcbWuu57B43ABycrHunBerOSuu9QeU2mLnL/W08lmjfIypCkAyG
+// dGfIf6WauFJhFBM/ZemCh8vb+g5W9oaJ84U/l4avsNwa72sNlRZ9xCugZbKZBDZ1
+// gGusSvMbkEl4L6KWTyogJSkExnTA0DHNjzE4lRa6qDO4Q/GxH8Mwf6J5MRM9LTb4
+// 4/zyM2q5OTHFr8SNDR1kFjOq+oQpttQLwNh9w5MCAwEAAaOCAZIwggGOMBIGA1Ud
+// EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMH8GCCsGAQUFBwEBBHMwcTAy
+// BggrBgEFBQcwAYYmaHR0cDovL2lzcmcudHJ1c3RpZC5vY3NwLmlkZW50cnVzdC5j
+// b20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMv
+// ZHN0cm9vdGNheDMucDdjMB8GA1UdIwQYMBaAFMSnsaR7LHH62+FLkHX/xBVghYkQ
+// MFQGA1UdIARNMEswCAYGZ4EMAQIBMD8GCysGAQQBgt8TAQEBMDAwLgYIKwYBBQUH
+// AgEWImh0dHA6Ly9jcHMucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcwPAYDVR0fBDUw
+// MzAxoC+gLYYraHR0cDovL2NybC5pZGVudHJ1c3QuY29tL0RTVFJPT1RDQVgzQ1JM
+// LmNybDATBgNVHR4EDDAKoQgwBoIELm1pbDAdBgNVHQ4EFgQUqEpqYwR93brm0Tm3
+// pkVl7/Oo7KEwDQYJKoZIhvcNAQELBQADggEBANHIIkus7+MJiZZQsY14cCoBG1hd
+// v0J20/FyWo5ppnfjL78S2k4s2GLRJ7iD9ZDKErndvbNFGcsW+9kKK/TnY21hp4Dd
+// ITv8S9ZYQ7oaoqs7HwhEMY9sibED4aXw09xrJZTC9zK1uIfW6t5dHQjuOWv+HHoW
+// ZnupyxpsEUlEaFb+/SCI4KCSBdAsYxAcsHYI5xxEI4LutHp6s3OT2FuO90WfdsIk
+// 6q78OMSdn875bNjdBYAqxUp2/LEIHfDBkLoQz0hFJmwAbYahqKaLn73PAAm1X2kj
+// f1w8DdnkabOLGeOVcj9LQ+s67vBykx4anTjURkbqZslUEUsn2k5xeua2zUk=
+// -----END CERTIFICATE-----
+
+"use strict";
+
+function run_test() {
+ const certDBName = "cert9.db";
+ const keyDBName = "key4.db";
+ let profile = do_get_profile();
+ let certDBFile = do_get_file(`test_cert_isBuiltInRoot_reload/${certDBName}`);
+ certDBFile.copyTo(profile, certDBName);
+ let keyDBFile = do_get_file(`test_cert_isBuiltInRoot_reload/${keyDBName}`);
+ keyDBFile.copyTo(profile, keyDBName);
+
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+
+ // This is a built-in root, but not one that was added to the preexisting
+ // certificate DB.
+ // Verisign Class 1 Public Primary Certification Authority - G3
+ // Certificate fingerprint (SHA1): 204285DCF7EB764195578E136BD4B7D1E98E46A5
+ // https://crt.sh/?id=8984570
+ const veriSignCertDBKey = `AAAAAAAAAAAAAAARAAAAzQCLW3VWhFSFCwDPrzhI
+ zrGkMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdB
+ gNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IF
+ ZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAM
+ TPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+ dXRob3JpdHkgLSBHMw==`;
+ let veriSignCert = certdb.findCertByDBKey(veriSignCertDBKey);
+ ok(veriSignCert, "Should be able to find VeriSign root");
+ ok(veriSignCert.isBuiltInRoot, "VeriSign root is a built-in");
+
+ // This is a built-in root. It was added to the preexisting certificate DB. It
+ // should still be considered a built-in.
+ const geoTrustCertDBKey = `AAAAAAAAAAAAAAAQAAAAmzyy9EgK
+ AOL+6yQ7XmA+w2swgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJ
+ bmMuMTkwNwYDVQQLEzAoYykgMjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhv
+ cml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlm
+ aWNhdGlvbiBBdXRob3JpdHkgLSBHMg==`;
+ let geoTrustCert = certdb.findCertByDBKey(geoTrustCertDBKey);
+ ok(geoTrustCert, "Should be able to find GeoTrust root");
+ ok(geoTrustCert.isBuiltInRoot, "GeoTrust root is a built-in");
+
+ // This is not a built-in root. It was added to the preexisting certificate
+ // DB. It should not be considered a built-in root.
+ const letsEncryptCertDBKey = `AAAAAAAAAAAAAAARAAAAQQCYE
+ /R1E+V1C0PnQx6XHkS9MD8xJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRyd
+ XN0IENvLjEXMBUGA1UEAxMORFNUIFJvb3QgQ0EgWDM=`;
+ let letsEncryptCert = certdb.findCertByDBKey(letsEncryptCertDBKey);
+ ok(letsEncryptCert, "Should be able to find LetsEncrypt root");
+ ok(!letsEncryptCert.isBuiltInRoot, "LetsEncrypt root is not a built-in");
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/cert9.db b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/cert9.db
new file mode 100644
index 0000000000..b4567566d4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/cert9.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/key4.db b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/key4.db
new file mode 100644
index 0000000000..ed8747112d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_isBuiltInRoot_reload/key4.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage.js b/security/manager/ssl/tests/unit/test_cert_keyUsage.js
new file mode 100644
index 0000000000..a32be6152d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage.js
@@ -0,0 +1,76 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+var certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const caList = [
+ "ca-no-keyUsage-extension",
+ "ca-missing-keyCertSign",
+ "ca-all-usages",
+];
+const eeList = [
+ "ee-no-keyUsage-extension",
+ "ee-keyCertSign-only",
+ "ee-keyEncipherment-only",
+ "ee-keyCertSign-and-keyEncipherment",
+];
+
+const caUsage = [certificateUsageSSLCA];
+const allEEUsages = [
+ certificateUsageSSLClient,
+ certificateUsageSSLServer,
+ certificateUsageEmailSigner,
+ certificateUsageEmailRecipient,
+];
+const serverEEUsages = [
+ certificateUsageSSLServer,
+ certificateUsageEmailRecipient,
+];
+
+const expectedUsagesMap = {
+ "ca-no-keyUsage-extension": caUsage,
+ "ca-missing-keyCertSign": [],
+ "ca-all-usages": caUsage,
+
+ "ee-no-keyUsage-extension-ca-no-keyUsage-extension": allEEUsages,
+ "ee-no-keyUsage-extension-ca-missing-keyCertSign": [],
+ "ee-no-keyUsage-extension-ca-all-usages": allEEUsages,
+
+ "ee-keyCertSign-only-ca-no-keyUsage-extension": [],
+ "ee-keyCertSign-only-ca-missing-keyCertSign": [],
+ "ee-keyCertSign-only-ca-all-usages": [],
+
+ "ee-keyEncipherment-only-ca-no-keyUsage-extension": serverEEUsages,
+ "ee-keyEncipherment-only-ca-missing-keyCertSign": [],
+ "ee-keyEncipherment-only-ca-all-usages": serverEEUsages,
+
+ "ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension": serverEEUsages,
+ "ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign": [],
+ "ee-keyCertSign-and-keyEncipherment-ca-all-usages": serverEEUsages,
+};
+
+add_task(async function() {
+ for (let ca of caList) {
+ addCertFromFile(certdb, "test_cert_keyUsage/" + ca + ".pem", "CTu,CTu,CTu");
+ let caCert = constructCertFromFile("test_cert_keyUsage/" + ca + ".pem");
+ await asyncTestCertificateUsages(certdb, caCert, expectedUsagesMap[ca]);
+ for (let ee of eeList) {
+ let eeFullName = ee + "-" + ca;
+ let eeCert = constructCertFromFile(
+ "test_cert_keyUsage/" + eeFullName + ".pem"
+ );
+ await asyncTestCertificateUsages(
+ certdb,
+ eeCert,
+ expectedUsagesMap[eeFullName]
+ );
+ }
+ }
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem
new file mode 100644
index 0000000000..f59173e4d4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAcegAwIBAgIUJUeujAg8AwYGhKxQTX/25wMUlcUwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNY2EtYWxsLXVzYWdlczAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAYMRYwFAYDVQQDDA1jYS1hbGwtdXNhZ2VzMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq
+5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SSc
+An7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39
+ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk
+zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3u
+JtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQAB
+ox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC
+AQEAG8zr2oIAMopI4FXUoIcqnnqRafTg/URYGcBcuqpXQiCFufslaqIY2c1iQExS
+S6TcNM2BD2+XjudfRO4R3qSNWNHcq7MFGaiZEOIzIF4bSozXg4HWLkhhZFxPO6Sp
+zWd1wr3sHs+LpympZAB4Hs3BCkrYBPACKwTXSP8iqmZ5gN/vRMhyyZMNPSBE9YYO
+H7hl+eDeShTQZ8wrhMj2C1QRP8bLtANMpR1yzHNYBVaGFEGJi3ojgSlkZjt1OBu+
+AXOkTU72YycuV0Pin+5RPWGTpbQCBKKkDT4dWEKe9Ur04JdrVuFPHY1AA422Bg1k
+WpRsoqRh9fqXtwntrf3gRm/jIw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem.certspec
new file mode 100644
index 0000000000..2ca523c74e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-all-usages.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca-all-usages
+subject:ca-all-usages
+extension:basicConstraints:cA,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem
new file mode 100644
index 0000000000..49b7d2b962
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIUb5GhjzP0jcrSceWaX1M31KkwYIgwDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWY2EtbWlzc2luZy1rZXlDZXJ0U2lnbjAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAhMR8wHQYDVQQDDBZjYS1taXNzaW5n
+LWtleUNlcnRTaWduMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohR
+qESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+Kv
+WnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+
+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPv
+JxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5
+Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6
+clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB
++jANBgkqhkiG9w0BAQsFAAOCAQEAGWOD5t5lqzj8nk4TXD4wNPTlZYYpGqRNkQJ4
+LAgt8LhtH656c1OPKCxwJDQXF9XapnkqnPzpT4M3wiRfVBVZgEoO0TN0Fh4CWNJM
+Dkq+kNC+rCvb9bddoF9Kzw1MoGa8A5y/JLqAQHi7TL+GwkksdgMvjBkmbEUqc0bc
+JdZ5USOBJPEWcsyfxNleyLHtcvI0JcngAeHfb6mK+Tr3wHUeRq1o6llfHOz1nzwE
+Lf1rhl6+hY458nxJjIv4p7YzON6dmzkkbKn4Rq4PPppMy44w2jxNqqtbev2TGJBh
+lnEibCTgzwFs0xJaQpKqKpHy4lBgjAMwjTzS76UsI30sYV9QjQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem.certspec
new file mode 100644
index 0000000000..26e0158ebe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-missing-keyCertSign.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca-missing-keyCertSign
+subject:ca-missing-keyCertSign
+extension:basicConstraints:cA,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem
new file mode 100644
index 0000000000..e79801f9dc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6DCCAdCgAwIBAgIUd46cj+Dhtp73GVJRwiYAMXpO09EwDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYY2Etbm8ta2V5VXNhZ2UtZXh0ZW5zaW9uMCIYDzIwMTkx
+MTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCMxITAfBgNVBAMMGGNhLW5vLWtl
+eVVzYWdlLWV4dGVuc2lvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG
+8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0V
+gg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g3
+04hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l
+0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz
+/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG
+9w0BAQsFAAOCAQEAfo7G469It6DM1PTLY0lfxGdV+4uCJPAWNKKjDk4swLsNy1yz
+4zxv5HUwQPnZZqm5dZT22EPUHmycWzt/N/cnjfn10pxDvB41h+XmWgmLPltVXD6E
+66N+9sNHb+XIwZdFdGa+oHJI0MLLH+/Uym4t46HuSsjAjvSth40yf7D76WXRmPUn
+2bTuxqK+T2Ioq3z8DXx47fNPSqmFiFsj7wUdfQfxugjTbwDNlZ9quxEicYChnl4O
+mScxJ/cDoBgB2ZV3FdcSeIc+0aVd7Nxx5sNOifS12/O8m0pflK5dVUtoNXLFoa78
+udnWXJ0J9224khw25u5vabj/gWJGe8TaI61tNw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem.certspec
new file mode 100644
index 0000000000..d32e6a6496
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ca-no-keyUsage-extension.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-no-keyUsage-extension
+subject:ca-no-keyUsage-extension
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem
new file mode 100644
index 0000000000..0b391b9b35
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5jCCAc6gAwIBAgIUIQJpgukzHUBKie2RdSdPXFoY4LwwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNY2EtYWxsLXVzYWdlczAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAtMSswKQYDVQQDDCJlZS1rZXlDZXJ0U2lnbi1hbmQt
+a2V5RW5jaXBoZXJtZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+uohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGoby
+a+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWC
+D/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfT
+iEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXT
+Ce+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+
+SSP6clHEMdUDrNoYCjXtjQIDAQABow8wDTALBgNVHQ8EBAMCAiQwDQYJKoZIhvcN
+AQELBQADggEBAA69Kh2Wk+QjEnMqyClxxpodQa37MQqGkXAVgSKgWHYQSagQ0EEJ
+SD8oDeNdgeh2mb7lvBysQCw29jgoRj8f/9Pi1+8xkCGBocitM2Um8nYWJ70nHMgt
+YOY1io2vQnQmI/dTIrSn330Gf5aCByqIEczEB7Erv/wuZY4l3RPG/HI6e94DXLOt
+rSBW/bFM61PU8C/q31VLZ8IHebdaaRO3OL7T0BM4bzQafc3lPfMbqiVy9GzBfQ/a
+nRrJirwqNGrKRyAWQ3gfvC60eNTjryH+NSA2N7gPawr2HxMkBqbUXhQwRMRatNKx
+JwmoBQTlPI3GVL42qeWGO/ozgwQoRqwHqBs=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem.certspec
new file mode 100644
index 0000000000..0bb2721a31
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-all-usages
+subject:ee-keyCertSign-and-keyEncipherment
+extension:keyUsage:keyEncipherment,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem
new file mode 100644
index 0000000000..45427ccd17
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7zCCAdegAwIBAgIUG6KKRn9QfADWZAOAYBt7T0HRwxUwDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWY2EtbWlzc2luZy1rZXlDZXJ0U2lnbjAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAtMSswKQYDVQQDDCJlZS1rZXlDZXJ0
+U2lnbi1hbmQta2V5RW5jaXBoZXJtZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABow8wDTALBgNVHQ8EBAMCAiQw
+DQYJKoZIhvcNAQELBQADggEBAA3HxmNPBMOe9i3095PKNQU3OPoJ+iRM6cQLM6KQ
+PnfTAXPJFK4gdiY/xH3A69fq8h44F7VoHmxBf2VNurgI8GYiG2U6rLtTshPvDyHe
+4KKTIz7rqiCAjdU5jslTS56HYvf0NguwdnFumXybWMAq0ThKCbQMXK16umegtoVE
+RgtupUHDwdx8FA+GdcEYYqe7n+yO5ZuzoMlOdAaCl4sO/2XjpTcTNOauUVfrgJ9k
+WlKTF6kzL7jhQVYosbGNpE/NblUjRt2/gDbz9tbi/OOcRqq0bQ3l7g6k+lpX7yYf
+TKBnXzLhUoP+TNRF/MnO/Hj+HefiGXDtugH2e1CoXJOXluM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem.certspec
new file mode 100644
index 0000000000..567ab0ce2d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-missing-keyCertSign
+subject:ee-keyCertSign-and-keyEncipherment
+extension:keyUsage:keyEncipherment,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem
new file mode 100644
index 0000000000..54b5156753
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIUKX6X2shljk68mAen0V5Rvu+OMJgwDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYY2Etbm8ta2V5VXNhZ2UtZXh0ZW5zaW9uMCIYDzIwMTkx
+MTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMC0xKzApBgNVBAMMImVlLWtleUNl
+cnRTaWduLWFuZC1rZXlFbmNpcGhlcm1lbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg
+2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ
+5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQ
+PdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGj
+DJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8W
+iy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjDzANMAsGA1UdDwQEAwIC
+JDANBgkqhkiG9w0BAQsFAAOCAQEAW4N1dHXHynllzRvDdokzMD8vKAFcXyOSXP5N
+lFqigC5B7Gmt19WFwui/y+qGws1H1PeJPdt73ht/R0tRFMfptuswGlyPufT7t+yP
+bVH3JEPuBYBBNragxVsXtqkYIxJGNH96xGBhAbovL7Ie4iIhY+6tsn0j/DATn301
+9Sv+Q3aUTjmpAYo+X7G/TkxOM37vtJbCvZhEYy3NnFQliIdyneJoXRX7V7Yxu+Yv
+vAnoDbiuiaT4N/XnP2OTdiwi/Obm7ITujbhwf2QOJvB8gGBIcGQ864opOmqIOn6i
+gx6ZpIB0bXeRun3j7qQdm/NeunBBG/EGlK1bcobxYB0lEthxUg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem.certspec
new file mode 100644
index 0000000000..c48ef66126
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-no-keyUsage-extension
+subject:ee-keyCertSign-and-keyEncipherment
+extension:keyUsage:keyEncipherment,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem
new file mode 100644
index 0000000000..649c6b2744
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1zCCAb+gAwIBAgIUS7/18rlCU549DxhuSleCZnLydSUwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNY2EtYWxsLXVzYWdlczAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAeMRwwGgYDVQQDDBNlZS1rZXlDZXJ0U2lnbi1vbmx5
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
+ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
+h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
+cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
+OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
+tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
+jQIDAQABow8wDTALBgNVHQ8EBAMCAgQwDQYJKoZIhvcNAQELBQADggEBAHGZLv0V
+Jx+wMdx0BJkZWUpJq2rKTepW7b9zsGKO1OFo3Y9S+jCHobanWqmz2bwiEThQ4Ebx
+wy6eXK9/ex46HNMU/R2OmqjNRJ+fn8NIbSLRK1UTGBnUNFMJgwFqoDGKCHmSGIA/
+DPR0dCFn9VFSVet7vSLSLbUurvTgfSR9GlyXw3abJ1BWsxP+3iCXr5NBNRVepZte
+NGIajiG8gnm+JVhAAG1XSnd+/Q99IcLwkJK6QQ8rYQgsg86yF7f1PF/+1jMWo/3E
+irkb0oBiyx3aitcPcci3yHcBAHqB/BIooJiQmt9EhvnVOKxFycw1yh3bpbzp0sIr
+MBu/rdb+5QSUqCc=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem.certspec
new file mode 100644
index 0000000000..c495ca6d0e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-all-usages.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-all-usages
+subject:ee-keyCertSign-only
+extension:keyUsage:keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem
new file mode 100644
index 0000000000..a818a38d1a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4DCCAcigAwIBAgIUW6rkcglhshQOePFgTRd3FZiywHgwDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWY2EtbWlzc2luZy1rZXlDZXJ0U2lnbjAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAeMRwwGgYDVQQDDBNlZS1rZXlDZXJ0
+U2lnbi1vbmx5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESO
+FtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVr
+amRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWka
+sdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbY
+VbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6n
+aOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHE
+MdUDrNoYCjXtjQIDAQABow8wDTALBgNVHQ8EBAMCAgQwDQYJKoZIhvcNAQELBQAD
+ggEBAAkfffPLkCJ7QwlVv19U2I69EEG7R8dfPcckXdxRQvRBSrnvFpq4xP+mS+b/
+cPzkCF7XK2JbKILWdpLl1Gl0fqIMdqAo3CUgAh2EgFXb+dDaTNF50vIKhIEnnVBt
+xWVQDOxNtFRrwtoUleeV7gOdY8oII+ehCIAqWSSN0tIHdYaoD84ngoIOYD7qGytK
+KN7AIIsZ9Ka+2WVGaWloE81ubJK68VThgrjW4eOHGjiHt38cEqge5zco2xj1rSE5
+G7Szdi+gcIOMoqJon3mXq/bkqaUZyTJs7BSm/tic/qID9cIeX2ZpuKCu1HWJqhSF
+Jdx3G8WPh5xnKwwKtDmdZoS5NgE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem.certspec
new file mode 100644
index 0000000000..23ddd0eb8a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-missing-keyCertSign.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-missing-keyCertSign
+subject:ee-keyCertSign-only
+extension:keyUsage:keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem
new file mode 100644
index 0000000000..f95886bfb5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4jCCAcqgAwIBAgIUHwJqP3oKeHdRO4h+n18uLiTGuvswDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYY2Etbm8ta2V5VXNhZ2UtZXh0ZW5zaW9uMCIYDzIwMTkx
+MTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMB4xHDAaBgNVBAMME2VlLWtleUNl
+cnRTaWduLW9ubHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjDzANMAsGA1UdDwQEAwICBDANBgkqhkiG9w0BAQsF
+AAOCAQEAGT9yKiHNhaNqa+QCuBKgMYN6cthRl7cXmB6rbgSl6x4+LGLFC/mpVx2n
+2ON9I9weQOcdqdA/s6LaT4sdk3V+BR7NDUBN0mHehxxYvMdOKWirMBGQsU/yspEh
+asD9RVHBBiX4AaCBfz+k+a0xGg1WNX+XQr7OGARPBclvN4gK/C3EnqWvcInd6dKh
+L11X5V8+O76coeii1pRTuEJTgJhttlFurr10YQM59xcJCvbNnDF4NP0ithumm3TX
+iH6h29vnr9Xuyhz6yBo8Izvw2mSKtn619U4nxqD0kT11ovfOLbBfS+0D67i7uGUe
+9xnFnXmaC7x/UPZ40Ba2bpm5KIo22w==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem.certspec
new file mode 100644
index 0000000000..a5a2d62a7d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyCertSign-only-ca-no-keyUsage-extension.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-no-keyUsage-extension
+subject:ee-keyCertSign-only
+extension:keyUsage:keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem
new file mode 100644
index 0000000000..94d5b8376d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC2zCCAcOgAwIBAgIUD4en2K6uMB5YpGMfwGgTtM+cZMMwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNY2EtYWxsLXVzYWdlczAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAiMSAwHgYDVQQDDBdlZS1rZXlFbmNpcGhlcm1lbnQt
+b25seTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1u
+togGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6
+pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqL
+KkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3Zlqq
+fgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3sv
+Im9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6za
+GAo17Y0CAwEAAaMPMA0wCwYDVR0PBAQDAgUgMA0GCSqGSIb3DQEBCwUAA4IBAQCD
+FhkKA16rSKyeeyWbKV0GLm0leHDTHD1lLq9OMFJTnM8oIV4wTxkNlM8GBvmXrbJ9
+bR2XBZADefQ2/hYBWCQJHBONOss+G3OF4xYYYQT0gDnhZuABWpT97FLMOsajPaAJ
+GcCy4CXFiWmBR3Z70duV7B7AbYM18SOstghaOHCT6dLzA00C/MkeuSwwmylJ0jjX
+VE5kmrh8RMy4o9BT2RGLNCAyJehCik0DqHyQjq61Q2pE/eVwSaDIFH29Q57ianyB
+fsVGxWjebixLkFm+GWqTbTsGyACwL2tDgE9zA7FcuQ+bUsmNt14+9Sf9j+DGcMXn
+wroXpu9cJ2hUBGH/1kya
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem.certspec
new file mode 100644
index 0000000000..08154a53ee
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-all-usages.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-all-usages
+subject:ee-keyEncipherment-only
+extension:keyUsage:keyEncipherment
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem
new file mode 100644
index 0000000000..413eec8073
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5DCCAcygAwIBAgIUR9LRIE9OxiebeLBW62zXCGMQD/4wDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWY2EtbWlzc2luZy1rZXlDZXJ0U2lnbjAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAiMSAwHgYDVQQDDBdlZS1rZXlFbmNp
+cGhlcm1lbnQtb25seTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqI
+UahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvi
+r1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/x
+fq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD
+7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnv
+uRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj
++nJRxDHVA6zaGAo17Y0CAwEAAaMPMA0wCwYDVR0PBAQDAgUgMA0GCSqGSIb3DQEB
+CwUAA4IBAQAgn7t4QKh0bvoLuwFaZMhTEG1aeC/UH8CAi9InsjNFFKsrqKNMFXIB
+BHMwsNX0pd92UVQmcxOelJGKh4Ssebqwi3CegvUxjkoc0XJmJIsmGnta7aliFgYq
+8yS9CzxQAsQBS8L/NgJx+kYL5OyyYcOdKkDH2MnHvMl66JwSKoerp0xxK+senJmM
+UjvR5bZgR+4L/IZ9G1J70u5EIKOKusrHHnD95y9OtwJSXriErMDaeTMK7C+HSqdK
+6+d7OSj/l7sBWgvvIRBAOqIOe3WBnNw5HVAfEf+RkzL9ry0llF93z429BxnV17r0
+DecPXRH8iTA11aPezhblHImD9I+LnEnx
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem.certspec
new file mode 100644
index 0000000000..9bdcf4b7b8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-missing-keyCertSign.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-missing-keyCertSign
+subject:ee-keyEncipherment-only
+extension:keyUsage:keyEncipherment
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem
new file mode 100644
index 0000000000..42d173b89a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5jCCAc6gAwIBAgIUUb8T3+6DgXwOgUnl93BH2znvH7swDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYY2Etbm8ta2V5VXNhZ2UtZXh0ZW5zaW9uMCIYDzIwMTkx
+MTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCIxIDAeBgNVBAMMF2VlLWtleUVu
+Y2lwaGVybWVudC1vbmx5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+uohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGoby
+a+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWC
+D/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfT
+iEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXT
+Ce+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+
+SSP6clHEMdUDrNoYCjXtjQIDAQABow8wDTALBgNVHQ8EBAMCBSAwDQYJKoZIhvcN
+AQELBQADggEBADPdx2NPP/lnfhFONgim1PT1z9715fnVBUOovdbOFrX4o5bAW9A4
+5EL+4jWXFEgSDCWbaVZIunaExT0clbrdP+96KSu0eCmO8UtWuEmLvWBpwnLNSJmV
+V07kpv4cF3ju7m6FMVn2yAWC3MwUVwtG3rc1cTEaQb8mRSP168L+c4bgbDfjT3SH
+cjc0Xku8+0qEF8mvDkEjEQ6DXciMQMEA+mdnhjLY7pCxEeMeYn3wbc02j0sDg9mc
+P5r7KFALon3lvNNvfgX8ksqs/FxvtkMJuJfxuqqw0JKbTUnCSWDUQLLpH5vTSNwS
+J6HaMBk4cBmm7RiMSNHb4q4DD2KPQaR3ia0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem.certspec
new file mode 100644
index 0000000000..a2383ecfdd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-keyEncipherment-only-ca-no-keyUsage-extension.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca-no-keyUsage-extension
+subject:ee-keyEncipherment-only
+extension:keyUsage:keyEncipherment
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem
new file mode 100644
index 0000000000..f9c0e98abf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyzCCAbOgAwIBAgIUdb+6dAvNPrDSi4t1Mh0VWTH9+PwwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNY2EtYWxsLXVzYWdlczAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAjMSEwHwYDVQQDDBhlZS1uby1rZXlVc2FnZS1leHRl
+bnNpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAOmZY9qkWrF1fxh37r8uzeI
+DIliysuC/r8Zv2PH1uXNQ/Xc2/YRevoLlXYFjPSv+dDLBTLU8gaKoc4A8VbScMXj
+RD/4Dgg5P8zp8tYhC6IC6lJU+BsYdE4bVEfxYIuFsj9t3IWaV6dfBGV6tZr5+hma
+fd132i0EB6j9tHdWxyjRjbLlpS+Ebws1YxpxhFthtDXia8g4jowh1SRYpK3a+Feh
+f/uZAVVDUia9iLK17JwQCWSvIAoMz5A1GyYzfjL6CnhIpXO77G1crCGoUeWbJ94j
+5IX1zQfT0YQhdiIQEoD11478aok3BhIIE1KO6iCSXj4o+OVAlFWZexPcutqnTFA=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem.certspec
new file mode 100644
index 0000000000..6d2e672961
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-all-usages.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca-all-usages
+subject:ee-no-keyUsage-extension
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem
new file mode 100644
index 0000000000..4fd1085a57
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAgIUQSQmSr1VH8nomKY2NVNJ+HN8y+0wDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWY2EtbWlzc2luZy1rZXlDZXJ0U2lnbjAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAjMSEwHwYDVQQDDBhlZS1uby1rZXlV
+c2FnZS1leHRlbnNpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA36Vps+r3Ao
+jobxXMLVlFswo/qOC6/G1BtF8l0UPuOuSf7xpKDWVsorWY/FmsHAdcauHm0m5MUP
+nCVhPfH8xYP6TBQw2wWrGk/RBnWDNLewrdEIlWSTFUALY6SKcvRO1adWGJ/uJZr3
+O6KCvrg22O4ZlOFLpb5B8XhUVcCPiH30vdo2MeiaA/IOs0dqMZAfzH5s25WRjwnJ
+byYST/yVutDdexECWVibIFwi1N+APduacEyDXvao5y3VXz96kblSLtZaSX/2mHDc
+fhKTLWw4ykHqBIWmDYs2RJrSqFM45bNShLYGCqTPmYFmCU5SY9e+elcfReLcbJzv
+FNK2ofnYhm4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem.certspec
new file mode 100644
index 0000000000..3cba2f0d81
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-missing-keyCertSign.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca-missing-keyCertSign
+subject:ee-no-keyUsage-extension
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem
new file mode 100644
index 0000000000..4bf7250fee
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1jCCAb6gAwIBAgIUOXIHlX23XCDj+TpQN+GOUIa0wl0wDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYY2Etbm8ta2V5VXNhZ2UtZXh0ZW5zaW9uMCIYDzIwMTkx
+MTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCMxITAfBgNVBAMMGGVlLW5vLWtl
+eVVzYWdlLWV4dGVuc2lvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG
+8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0V
+gg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g3
+04hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l
+0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz
+/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAkKUtc+iJ
+0yBV2Y3xKmTIDe3he3Cy3VSembzQTpc+FBfQ4n9gXZSw5O8PllaWD4pjz7ovDYNp
+rl422x6vkfmm7ebZb1PqVXZDkONXYn22a/76j7EFl+LlG6EMUw8OJ+iMTiCrDS2j
+MbTpyRKLbgPMcT7GxZZDmqRJKva/VFVJ92XuIndZrltQ/RAr5QAg1+ijo2VpowWn
+etUhR/GZ6WbDS85KQLEOFeWHS9ez02asCjQcanWgMYPqZXG1wQQbEAgfJLT1Bqar
+k+TRt7NRlJPbxI8R5UfXJfCdXYrT17jhO52b7UZcCmCe/kyQPwtir5axOupFy17i
+NHOBFw1PVX4KfQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem.certspec b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem.certspec
new file mode 100644
index 0000000000..c850725a63
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca-no-keyUsage-extension
+subject:ee-no-keyUsage-extension
diff --git a/security/manager/ssl/tests/unit/test_cert_keyUsage/moz.build b/security/manager/ssl/tests/unit/test_cert_keyUsage/moz.build
new file mode 100644
index 0000000000..a16fff4b94
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage/moz.build
@@ -0,0 +1,27 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca-all-usages.pem',
+# 'ca-missing-keyCertSign.pem',
+# 'ca-no-keyUsage-extension.pem',
+# 'ee-keyCertSign-and-keyEncipherment-ca-all-usages.pem',
+# 'ee-keyCertSign-and-keyEncipherment-ca-missing-keyCertSign.pem',
+# 'ee-keyCertSign-and-keyEncipherment-ca-no-keyUsage-extension.pem',
+# 'ee-keyCertSign-only-ca-all-usages.pem',
+# 'ee-keyCertSign-only-ca-missing-keyCertSign.pem',
+# 'ee-keyCertSign-only-ca-no-keyUsage-extension.pem',
+# 'ee-keyEncipherment-only-ca-all-usages.pem',
+# 'ee-keyEncipherment-only-ca-missing-keyCertSign.pem',
+# 'ee-keyEncipherment-only-ca-no-keyUsage-extension.pem',
+# 'ee-no-keyUsage-extension-ca-all-usages.pem',
+# 'ee-no-keyUsage-extension-ca-missing-keyCertSign.pem',
+# 'ee-no-keyUsage-extension-ca-no-keyUsage-extension.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_override_bits_mismatches.js b/security/manager/ssl/tests/unit/test_cert_override_bits_mismatches.js
new file mode 100644
index 0000000000..4ac8820515
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_override_bits_mismatches.js
@@ -0,0 +1,116 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Test that when an override exists for a (host, certificate) pair,
+// connections will succeed only if the set of error bits in the override
+// contains each bit in the set of encountered error bits.
+// Strangely, it is possible to store an override with an empty set of error
+// bits, so this tests that too.
+
+do_get_profile();
+
+function add_override_bits_mismatch_test(
+ host,
+ certPath,
+ expectedBits,
+ expectedError
+) {
+ const MAX_BITS =
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_TIME;
+ let cert = constructCertFromFile(certPath);
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ for (let overrideBits = 0; overrideBits <= MAX_BITS; overrideBits++) {
+ add_test(function() {
+ certOverrideService.clearValidityOverride(host, 8443);
+ certOverrideService.rememberValidityOverride(
+ host,
+ 8443,
+ cert,
+ overrideBits,
+ true
+ );
+ run_next_test();
+ });
+ // The override will succeed only if the set of error bits in the override
+ // contains each bit in the set of expected error bits.
+ let successExpected = (overrideBits | expectedBits) == overrideBits;
+ add_connection_test(
+ host,
+ successExpected ? PRErrorCodeSuccess : expectedError
+ );
+ }
+}
+
+function run_test() {
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+ let fakeOCSPResponder = new HttpServer();
+ fakeOCSPResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ });
+ fakeOCSPResponder.start(8888);
+
+ add_override_bits_mismatch_test(
+ "unknownissuer.example.com",
+ "bad_certs/unknownissuer.pem",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_override_bits_mismatch_test(
+ "mismatch.example.com",
+ "bad_certs/mismatch.pem",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ add_override_bits_mismatch_test(
+ "expired.example.com",
+ "bad_certs/expired-ee.pem",
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_EXPIRED_CERTIFICATE
+ );
+
+ add_override_bits_mismatch_test(
+ "mismatch-untrusted.example.com",
+ "bad_certs/mismatch-untrusted.pem",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_override_bits_mismatch_test(
+ "untrusted-expired.example.com",
+ "bad_certs/untrusted-expired.pem",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_override_bits_mismatch_test(
+ "mismatch-expired.example.com",
+ "bad_certs/mismatch-expired.pem",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+
+ add_override_bits_mismatch_test(
+ "mismatch-untrusted-expired.example.com",
+ "bad_certs/mismatch-untrusted-expired.pem",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+
+ add_test(function() {
+ fakeOCSPResponder.stop(run_next_test);
+ });
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js
new file mode 100644
index 0000000000..f160f00562
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -0,0 +1,718 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Tests the certificate overrides we allow.
+// add_cert_override_test will queue a test that does the following:
+// 1. Attempt to connect to the given host. This should fail with the
+// given error and override bits.
+// 2. Add an override for that host/port/certificate/override bits.
+// 3. Connect again. This should succeed.
+
+do_get_profile();
+
+function check_telemetry() {
+ let histogram = Services.telemetry
+ .getHistogramById("SSL_CERT_ERROR_OVERRIDES")
+ .snapshot();
+ equal(histogram.values[0], 0, "Should have 0 unclassified values");
+ equal(
+ histogram.values[2],
+ 9,
+ "Actual and expected SEC_ERROR_UNKNOWN_ISSUER values should match"
+ );
+ equal(
+ histogram.values[3],
+ 1,
+ "Actual and expected SEC_ERROR_CA_CERT_INVALID values should match"
+ );
+ equal(
+ histogram.values[4] || 0,
+ 0,
+ "Actual and expected SEC_ERROR_UNTRUSTED_ISSUER values should match"
+ );
+ equal(
+ histogram.values[5],
+ 1,
+ "Actual and expected SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE values should match"
+ );
+ equal(
+ histogram.values[6] || 0,
+ 0,
+ "Actual and expected SEC_ERROR_UNTRUSTED_CERT values should match"
+ );
+ equal(
+ histogram.values[7] || 0,
+ 0,
+ "Actual and expected SEC_ERROR_INADEQUATE_KEY_USAGE values should match"
+ );
+ equal(
+ histogram.values[8],
+ 2,
+ "Actual and expected SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED values should match"
+ );
+ equal(
+ histogram.values[9],
+ 13,
+ "Actual and expected SSL_ERROR_BAD_CERT_DOMAIN values should match"
+ );
+ equal(
+ histogram.values[10],
+ 5,
+ "Actual and expected SEC_ERROR_EXPIRED_CERTIFICATE values should match"
+ );
+ equal(
+ histogram.values[11],
+ 2,
+ "Actual and expected MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY values should match"
+ );
+ equal(
+ histogram.values[12],
+ 1,
+ "Actual and expected MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA values should match"
+ );
+ equal(
+ histogram.values[13],
+ 1,
+ "Actual and expected MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE values should match"
+ );
+ equal(
+ histogram.values[14],
+ 2,
+ "Actual and expected MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE values should match"
+ );
+ equal(
+ histogram.values[15],
+ 1,
+ "Actual and expected MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE values should match"
+ );
+ equal(
+ histogram.values[16],
+ 2,
+ "Actual and expected SEC_ERROR_INVALID_TIME values should match"
+ );
+ equal(
+ histogram.values[17],
+ 1,
+ "Actual and expected MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME values should match"
+ );
+ equal(
+ histogram.values[19],
+ 3,
+ "Actual and expected MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT values should match"
+ );
+ equal(
+ histogram.values[20],
+ 1,
+ "Actual and expected MOZILLA_PKIX_ERROR_MITM_DETECTED values should match"
+ );
+
+ let keySizeHistogram = Services.telemetry
+ .getHistogramById("CERT_CHAIN_KEY_SIZE_STATUS")
+ .snapshot();
+ equal(
+ keySizeHistogram.values[0],
+ 0,
+ "Actual and expected unchecked key size values should match"
+ );
+ equal(
+ keySizeHistogram.values[1],
+ 16,
+ "Actual and expected successful verifications of 2048-bit keys should match"
+ );
+ equal(
+ keySizeHistogram.values[2] || 0,
+ 0,
+ "Actual and expected successful verifications of 1024-bit keys should match"
+ );
+ equal(
+ keySizeHistogram.values[3],
+ 68,
+ "Actual and expected verification failures unrelated to key size should match"
+ );
+
+ run_next_test();
+}
+
+// Internally, specifying "port" -1 is the same as port 443. This tests that.
+function run_port_equivalency_test(inPort, outPort) {
+ Assert.ok(
+ (inPort == 443 && outPort == -1) || (inPort == -1 && outPort == 443),
+ "The two specified ports must be -1 and 443 (in any order)"
+ );
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ let cert = constructCertFromFile("bad_certs/default-ee.pem");
+ let expectedBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED;
+ let expectedTemporary = true;
+ certOverrideService.rememberValidityOverride(
+ "example.com",
+ inPort,
+ cert,
+ expectedBits,
+ expectedTemporary
+ );
+ let actualBits = {};
+ let actualTemporary = {};
+ Assert.ok(
+ certOverrideService.hasMatchingOverride(
+ "example.com",
+ outPort,
+ cert,
+ actualBits,
+ actualTemporary
+ ),
+ `override set on port ${inPort} should match port ${outPort}`
+ );
+ equal(
+ actualBits.value,
+ expectedBits,
+ "input override bits should match output bits"
+ );
+ equal(
+ actualTemporary.value,
+ expectedTemporary,
+ "input override temporary value should match output temporary value"
+ );
+ Assert.ok(
+ !certOverrideService.hasMatchingOverride("example.com", 563, cert, {}, {}),
+ `override set on port ${inPort} should not match port 563`
+ );
+ certOverrideService.clearValidityOverride("example.com", inPort);
+ Assert.ok(
+ !certOverrideService.hasMatchingOverride(
+ "example.com",
+ outPort,
+ cert,
+ actualBits,
+ {}
+ ),
+ `override cleared on port ${inPort} should match port ${outPort}`
+ );
+ equal(actualBits.value, 0, "should have no bits set if there is no override");
+}
+
+function run_test() {
+ run_port_equivalency_test(-1, 443);
+ run_port_equivalency_test(443, -1);
+
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+ let fakeOCSPResponder = new HttpServer();
+ fakeOCSPResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ });
+ fakeOCSPResponder.start(8888);
+
+ add_simple_tests();
+ add_localhost_tests();
+ add_combo_tests();
+ add_distrust_tests();
+
+ add_test(function() {
+ fakeOCSPResponder.stop(check_telemetry);
+ });
+
+ run_next_test();
+}
+
+function add_simple_tests() {
+ add_cert_override_test(
+ "expired.example.com",
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_EXPIRED_CERTIFICATE
+ );
+ add_cert_override_test(
+ "notyetvalid.example.com",
+ Ci.nsICertOverrideService.ERROR_TIME,
+ MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE
+ );
+ add_cert_override_test(
+ "before-epoch.example.com",
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_INVALID_TIME
+ );
+ add_cert_override_test(
+ "selfsigned.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
+ );
+ add_cert_override_test(
+ "unknownissuer.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_cert_override_test(
+ "expiredissuer.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE
+ );
+ add_cert_override_test(
+ "notyetvalidissuer.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE
+ );
+ add_cert_override_test(
+ "before-epoch-issuer.example.com",
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_INVALID_TIME
+ );
+ add_cert_override_test(
+ "md5signature.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ add_cert_override_test(
+ "emptyissuername.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME
+ );
+ // This has name information in the subject alternative names extension,
+ // but not the subject common name.
+ add_cert_override_test(
+ "mismatch.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ // This has name information in the subject common name but not the subject
+ // alternative names extension.
+ add_cert_override_test(
+ "mismatch-CN.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+
+ // A Microsoft IIS utility generates self-signed certificates with
+ // properties similar to the one this "host" will present.
+ add_cert_override_test(
+ "selfsigned-inadequateEKU.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
+ );
+
+ add_prevented_cert_override_test(
+ "inadequatekeyusage.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_INADEQUATE_KEY_USAGE
+ );
+
+ // Test triggering the MitM detection. We don't set-up a proxy here. Just
+ // set the pref. Without the pref set we expect an unkown issuer error.
+ add_cert_override_test(
+ "mitm.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_test(function() {
+ Services.prefs.setStringPref(
+ "security.pki.mitm_canary_issuer",
+ "CN=Test MITM Root"
+ );
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride("mitm.example.com", 8443);
+ run_next_test();
+ });
+ add_cert_override_test(
+ "mitm.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_MITM_DETECTED
+ );
+ add_test(function() {
+ Services.prefs.setStringPref(
+ "security.pki.mitm_canary_issuer",
+ "CN=Other MITM Root"
+ );
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride("mitm.example.com", 8443);
+ run_next_test();
+ });
+ // If the canary issuer doesn't match the one we see, we exepct and unknown
+ // issuer error.
+ add_cert_override_test(
+ "mitm.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ // If security.pki.mitm_canary_issuer.enabled is false, there should always
+ // be an unknown issuer error.
+ add_test(function() {
+ Services.prefs.setBoolPref(
+ "security.pki.mitm_canary_issuer.enabled",
+ false
+ );
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride("mitm.example.com", 8443);
+ run_next_test();
+ });
+ add_cert_override_test(
+ "mitm.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_test(function() {
+ Services.prefs.clearUserPref("security.pki.mitm_canary_issuer");
+ run_next_test();
+ });
+
+ // This is intended to test the case where a verification has failed for one
+ // overridable reason (e.g. unknown issuer) but then, in the process of
+ // reporting that error, a non-overridable error is encountered. The
+ // non-overridable error should be prioritized.
+ add_test(function() {
+ let rootCert = constructCertFromFile("bad_certs/test-ca.pem");
+ setCertTrust(rootCert, ",,");
+ run_next_test();
+ });
+ add_prevented_cert_override_test(
+ "nsCertTypeCritical.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
+ );
+ add_test(function() {
+ let rootCert = constructCertFromFile("bad_certs/test-ca.pem");
+ setCertTrust(rootCert, "CTu,,");
+ run_next_test();
+ });
+
+ // Bug 990603: Apache documentation has recommended generating a self-signed
+ // test certificate with basic constraints: CA:true. For compatibility, this
+ // is a scenario in which an override is allowed.
+ add_cert_override_test(
+ "self-signed-end-entity-with-cA-true.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
+ );
+
+ add_cert_override_test(
+ "ca-used-as-end-entity.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
+ );
+
+ // If an X.509 version 1 certificate is not a trust anchor, we will
+ // encounter an overridable error.
+ add_cert_override_test(
+ "end-entity-issued-by-v1-cert.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA
+ );
+ // If we make that certificate a trust anchor, the connection will succeed.
+ add_test(function() {
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride(
+ "end-entity-issued-by-v1-cert.example.com",
+ 8443
+ );
+ let v1Cert = constructCertFromFile("bad_certs/v1Cert.pem");
+ setCertTrust(v1Cert, "CTu,,");
+ clearSessionCache();
+ run_next_test();
+ });
+ add_connection_test(
+ "end-entity-issued-by-v1-cert.example.com",
+ PRErrorCodeSuccess
+ );
+ // Reset the trust for that certificate.
+ add_test(function() {
+ let v1Cert = constructCertFromFile("bad_certs/v1Cert.pem");
+ setCertTrust(v1Cert, ",,");
+ clearSessionCache();
+ run_next_test();
+ });
+
+ // Due to compatibility issues, we allow overrides for certificates issued by
+ // certificates that are not valid CAs.
+ add_cert_override_test(
+ "end-entity-issued-by-non-CA.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ // This host presents a 1016-bit RSA key.
+ add_cert_override_test(
+ "inadequate-key-size-ee.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE
+ );
+
+ add_cert_override_test(
+ "ipAddressAsDNSNameInSAN.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ add_cert_override_test(
+ "noValidNames.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ add_cert_override_test(
+ "badSubjectAltNames.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+
+ add_cert_override_test(
+ "bug413909.xn--hxajbheg2az3al.xn--jxalpdlp",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_test(function() {
+ // At this point, the override for bug413909.xn--hxajbheg2az3al.xn--jxalpdlp
+ // is still valid. Do some additional tests relating to IDN handling.
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ let uri = Services.io.newURI(
+ "https://bug413909.xn--hxajbheg2az3al.xn--jxalpdlp"
+ );
+ let cert = constructCertFromFile("bad_certs/idn-certificate.pem");
+ Assert.ok(
+ certOverrideService.hasMatchingOverride(
+ uri.asciiHost,
+ 8443,
+ cert,
+ {},
+ {}
+ ),
+ "IDN certificate should have matching override using ascii host"
+ );
+ Assert.throws(
+ () =>
+ !certOverrideService.hasMatchingOverride(
+ uri.displayHost,
+ 8443,
+ cert,
+ {},
+ {}
+ ),
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "IDN certificate should not have matching override using (non-ascii) host"
+ );
+ let invalidHost = uri.asciiHost.replace(/./g, c =>
+ String.fromCharCode(c.charCodeAt(0) | 0x100)
+ );
+ Assert.throws(
+ () =>
+ !certOverrideService.hasMatchingOverride(
+ invalidHost,
+ 8443,
+ cert,
+ {},
+ {}
+ ),
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "hasMatchingOverride should not truncate high-bytes"
+ );
+ run_next_test();
+ });
+
+ add_test(function() {
+ // Add a bunch of overrides...
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ let cert = constructCertFromFile("bad_certs/default-ee.pem");
+ let expectedBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED;
+ certOverrideService.rememberValidityOverride(
+ "example.com",
+ 443,
+ cert,
+ expectedBits,
+ false
+ );
+ Assert.ok(
+ certOverrideService.hasMatchingOverride("example.com", 443, cert, {}, {}),
+ "Should have added override for example.com:443"
+ );
+ certOverrideService.rememberValidityOverride(
+ "example.com",
+ 80,
+ cert,
+ expectedBits,
+ false
+ );
+ Assert.ok(
+ certOverrideService.hasMatchingOverride("example.com", 80, cert, {}, {}),
+ "Should have added override for example.com:80"
+ );
+ certOverrideService.rememberValidityOverride(
+ "example.org",
+ 443,
+ cert,
+ expectedBits,
+ false
+ );
+ Assert.ok(
+ certOverrideService.hasMatchingOverride("example.org", 443, cert, {}, {}),
+ "Should have added override for example.org:443"
+ );
+ certOverrideService.rememberValidityOverride(
+ "example.org",
+ 80,
+ cert,
+ expectedBits,
+ true
+ );
+ Assert.ok(
+ certOverrideService.hasMatchingOverride("example.org", 80, cert, {}, {}),
+ "Should have added override for example.org:80"
+ );
+
+ // Clear them all...
+ certOverrideService.clearAllOverrides();
+
+ // And ensure they're all gone.
+ Assert.ok(
+ !certOverrideService.hasMatchingOverride(
+ "example.com",
+ 443,
+ cert,
+ {},
+ {}
+ ),
+ "Should have removed override for example.com:443"
+ );
+ Assert.ok(
+ !certOverrideService.hasMatchingOverride("example.com", 80, cert, {}, {}),
+ "Should have removed override for example.com:80"
+ );
+ Assert.ok(
+ !certOverrideService.hasMatchingOverride(
+ "example.org",
+ 443,
+ cert,
+ {},
+ {}
+ ),
+ "Should have removed override for example.org:443"
+ );
+ Assert.ok(
+ !certOverrideService.hasMatchingOverride("example.org", 80, cert, {}, {}),
+ "Should have removed override for example.org:80"
+ );
+
+ run_next_test();
+ });
+}
+
+function add_localhost_tests() {
+ add_cert_override_test(
+ "localhost",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_cert_override_test(
+ "127.0.0.1",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ add_cert_override_test(
+ "::1",
+ Ci.nsICertOverrideService.ERROR_MISMATCH,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+}
+
+function add_combo_tests() {
+ add_cert_override_test(
+ "mismatch-expired.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ add_cert_override_test(
+ "mismatch-notYetValid.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SSL_ERROR_BAD_CERT_DOMAIN
+ );
+ add_cert_override_test(
+ "mismatch-untrusted.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_cert_override_test(
+ "untrusted-expired.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_cert_override_test(
+ "mismatch-untrusted-expired.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+
+ add_cert_override_test(
+ "md5signature-expired.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+
+ add_cert_override_test(
+ "ca-used-as-end-entity-name-mismatch.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
+ );
+}
+
+function add_distrust_tests() {
+ // Before we specifically distrust this certificate, it should be trusted.
+ add_connection_test("untrusted.example.com", PRErrorCodeSuccess);
+
+ add_distrust_test(
+ "bad_certs/default-ee.pem",
+ "untrusted.example.com",
+ SEC_ERROR_UNTRUSTED_CERT
+ );
+
+ add_distrust_test(
+ "bad_certs/other-test-ca.pem",
+ "untrustedissuer.example.com",
+ SEC_ERROR_UNTRUSTED_ISSUER
+ );
+
+ add_distrust_test(
+ "bad_certs/test-ca.pem",
+ "ca-used-as-end-entity.example.com",
+ SEC_ERROR_UNTRUSTED_ISSUER
+ );
+}
+
+function add_distrust_test(certFileName, hostName, expectedResult) {
+ let certToDistrust = constructCertFromFile(certFileName);
+
+ add_test(function() {
+ // Add an entry to the NSS certDB that says to distrust the cert
+ setCertTrust(certToDistrust, "pu,,");
+ clearSessionCache();
+ run_next_test();
+ });
+ add_prevented_cert_override_test(
+ hostName,
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ expectedResult
+ );
+ add_test(function() {
+ setCertTrust(certToDistrust, "u,,");
+ run_next_test();
+ });
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides_read_only.js b/security/manager/ssl/tests/unit/test_cert_overrides_read_only.js
new file mode 100644
index 0000000000..bf7104eaaa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_overrides_read_only.js
@@ -0,0 +1,126 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Tests that permanent certificate error overrides can be added even if the
+// certificate/key databases are in read-only mode.
+
+// Helper function for add_read_only_cert_override_test. Probably doesn't need
+// to be called directly.
+function add_read_only_cert_override(aHost, aExpectedBits, aSecurityInfo) {
+ let bits =
+ (aSecurityInfo.isUntrusted
+ ? Ci.nsICertOverrideService.ERROR_UNTRUSTED
+ : 0) |
+ (aSecurityInfo.isDomainMismatch
+ ? Ci.nsICertOverrideService.ERROR_MISMATCH
+ : 0) |
+ (aSecurityInfo.isNotValidAtThisTime
+ ? Ci.nsICertOverrideService.ERROR_TIME
+ : 0);
+
+ Assert.equal(
+ bits,
+ aExpectedBits,
+ "Actual and expected override bits should match"
+ );
+ let cert = aSecurityInfo.serverCert;
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ // Setting the last argument to false here ensures that we attempt to store a
+ // permanent override (which is what was failing in bug 1427273).
+ certOverrideService.rememberValidityOverride(
+ aHost,
+ 8443,
+ cert,
+ aExpectedBits,
+ false
+ );
+}
+
+// Given a host, expected error bits (see nsICertOverrideService.idl), and an
+// expected error code, tests that an initial connection to the host fails with
+// the expected errors and that adding an override results in a subsequent
+// connection succeeding.
+function add_read_only_cert_override_test(
+ aHost,
+ aExpectedBits,
+ aExpectedError
+) {
+ add_connection_test(
+ aHost,
+ aExpectedError,
+ null,
+ add_read_only_cert_override.bind(this, aHost, aExpectedBits)
+ );
+ add_connection_test(aHost, PRErrorCodeSuccess, null, aSecurityInfo => {
+ Assert.ok(
+ aSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
+ "Cert override flag should be set on the security state"
+ );
+ });
+}
+
+function run_test() {
+ let profile = do_get_profile();
+ const KEY_DB_NAME = "key4.db";
+ const CERT_DB_NAME = "cert9.db";
+ let srcKeyDBFile = do_get_file(
+ `test_cert_overrides_read_only/${KEY_DB_NAME}`
+ );
+ srcKeyDBFile.copyTo(profile, KEY_DB_NAME);
+ let srcCertDBFile = do_get_file(
+ `test_cert_overrides_read_only/${CERT_DB_NAME}`
+ );
+ srcCertDBFile.copyTo(profile, CERT_DB_NAME);
+
+ // set the databases to read-only
+ let keyDBFile = do_get_profile();
+ keyDBFile.append(KEY_DB_NAME);
+ keyDBFile.permissions = 0o400;
+ let certDBFile = do_get_profile();
+ certDBFile.append(CERT_DB_NAME);
+ certDBFile.permissions = 0o400;
+
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ // Specifying false as the last argument means we don't try to add the default
+ // test root CA (which would fail).
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs", false);
+
+ let fakeOCSPResponder = new HttpServer();
+ fakeOCSPResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ });
+ fakeOCSPResponder.start(8888);
+
+ // Since we can't add the root CA to the (read-only) trust db, all of these
+ // will result in an "unknown issuer error" and need the "untrusted" error bit
+ // set in addition to whatever other specific error bits are necessary.
+ add_read_only_cert_override_test(
+ "expired.example.com",
+ Ci.nsICertOverrideService.ERROR_TIME |
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_read_only_cert_override_test(
+ "selfsigned.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
+ );
+ add_read_only_cert_override_test(
+ "mismatch.example.com",
+ Ci.nsICertOverrideService.ERROR_MISMATCH |
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+
+ add_test(function() {
+ fakeOCSPResponder.stop(run_next_test);
+ });
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides_read_only/cert9.db b/security/manager/ssl/tests/unit/test_cert_overrides_read_only/cert9.db
new file mode 100644
index 0000000000..3d452f335c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_overrides_read_only/cert9.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides_read_only/key4.db b/security/manager/ssl/tests/unit/test_cert_overrides_read_only/key4.db
new file mode 100644
index 0000000000..44d0cb1728
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_overrides_read_only/key4.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1.js b/security/manager/ssl/tests/unit/test_cert_sha1.js
new file mode 100644
index 0000000000..371945bf85
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1.js
@@ -0,0 +1,187 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests the rejection of SHA-1 certificates based on the preference
+// security.pki.sha1_enforcement_level.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+// We need to test as if we are at a fixed time, so that we are testing the
+// 2016 checks on SHA-1, not the notBefore checks.
+//
+// (new Date("2016-03-01")).getTime() / 1000
+const VALIDATION_TIME = 1456790400;
+
+function certFromFile(certName) {
+ return constructCertFromFile("test_cert_sha1/" + certName + ".pem");
+}
+
+function loadCertWithTrust(certName, trustString) {
+ addCertFromFile(certdb, "test_cert_sha1/" + certName + ".pem", trustString);
+}
+
+function checkEndEntity(cert, expectedResult) {
+ return checkCertErrorGenericAtTime(
+ certdb,
+ cert,
+ expectedResult,
+ certificateUsageSSLServer,
+ VALIDATION_TIME
+ );
+}
+
+add_task(async function() {
+ loadCertWithTrust("ca", "CTu,,");
+ loadCertWithTrust("int-pre", ",,");
+ loadCertWithTrust("int-post", ",,");
+
+ // Test cases per pref setting
+ //
+ // root intermed. end entity
+ // ===========================
+ // root
+ // |
+ // +--- pre-2016
+ // | |
+ // | +----- pre-2016 <--- (a)
+ // | +----- post-2016 <--- (b)
+ // |
+ // +--- post-2016
+ // |
+ // +----- post-2016 <--- (c)
+ //
+ // Expected outcomes (accept / reject):
+ //
+ // a b c
+ // Allowed (0) Accept Accept Accept
+ // Forbidden (1) Reject Reject Reject
+ // (2) is no longer available and is treated as Forbidden (1) internally.
+ // ImportedRoot (3) Reject Reject Reject (for built-in roots)
+ // ImportedRoot Accept Accept Accept (for non-built-in roots)
+ // ImportedRootOrBefore2016 (4) Accept Reject Reject (for built-in roots)
+ // ImportedRootOrBefore2016 Accept Accept Accept (for non-built-in roots)
+ //
+ // The pref setting of ImportedRoot accepts usage of SHA-1 only for
+ // certificates issued by non-built-in roots. By default, the testing
+ // certificates are all considered issued by a non-built-in root. However, we
+ // have the ability to treat a given non-built-in root as built-in. We test
+ // both of these situations below.
+ //
+ // As a historical note, a policy option (Before2016) was previously available
+ // that only allowed SHA-1 for certificates with a notBefore before 2016.
+ // However, to enable the policy of only allowing SHA-1 from non-built-in
+ // roots in the most straightforward way (while still having a time-based
+ // policy that users could enable if this new policy were problematic),
+ // Before2016 was shifted to also allow SHA-1 from non-built-in roots, hence
+ // ImportedRootOrBefore2016.
+ //
+ // A note about intermediate certificates: the certificate verifier has the
+ // ability to directly verify a given certificate for the purpose of issuing
+ // TLS web server certificates. However, when asked to do so, the certificate
+ // verifier does not take into account the currently configured SHA-1 policy.
+ // This is in part due to implementation complexity and because this isn't
+ // actually how TLS web server certificates are verified in the TLS handshake
+ // (which makes a full implementation that supports heeding the SHA-1 policy
+ // unnecessary).
+
+ // SHA-1 allowed
+ Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 0);
+ await checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-post_int-pre"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-post_int-post"), PRErrorCodeSuccess);
+
+ // SHA-1 forbidden
+ Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 1);
+ await checkEndEntity(
+ certFromFile("ee-pre_int-pre"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ await checkEndEntity(
+ certFromFile("ee-post_int-pre"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ await checkEndEntity(
+ certFromFile("ee-post_int-post"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+
+ // SHA-1 forbidden (test the case where the pref has been set to 2)
+ Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 2);
+ await checkEndEntity(
+ certFromFile("ee-pre_int-pre"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ await checkEndEntity(
+ certFromFile("ee-post_int-pre"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ await checkEndEntity(
+ certFromFile("ee-post_int-post"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+
+ // SHA-1 allowed only when issued by an imported root. First test with the
+ // test root considered a built-in (on debug only - this functionality is
+ // disabled on non-debug builds).
+ Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 3);
+ if (isDebugBuild) {
+ let root = certFromFile("ca");
+ Services.prefs.setCharPref(
+ "security.test.built_in_root_hash",
+ root.sha256Fingerprint
+ );
+ await checkEndEntity(
+ certFromFile("ee-pre_int-pre"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ await checkEndEntity(
+ certFromFile("ee-post_int-pre"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ await checkEndEntity(
+ certFromFile("ee-post_int-post"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ Services.prefs.clearUserPref("security.test.built_in_root_hash");
+ }
+
+ // SHA-1 still allowed only when issued by an imported root.
+ // Now test with the test root considered a non-built-in.
+ await checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-post_int-pre"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-post_int-post"), PRErrorCodeSuccess);
+
+ // SHA-1 allowed before 2016 or when issued by an imported root. First test
+ // with the test root considered a built-in.
+ Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 4);
+ if (isDebugBuild) {
+ let root = certFromFile("ca");
+ Services.prefs.setCharPref(
+ "security.test.built_in_root_hash",
+ root.sha256Fingerprint
+ );
+ await checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
+ await checkEndEntity(
+ certFromFile("ee-post_int-pre"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ await checkEndEntity(
+ certFromFile("ee-post_int-post"),
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
+ );
+ Services.prefs.clearUserPref("security.test.built_in_root_hash");
+ }
+
+ // SHA-1 still only allowed before 2016 or when issued by an imported root.
+ // Now test with the test root considered a non-built-in.
+ await checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-post_int-pre"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-post_int-post"), PRErrorCodeSuccess);
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ca.pem b/security/manager/ssl/tests/unit/test_cert_sha1/ca.pem
new file mode 100644
index 0000000000..60140056de
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUUbC4F7yPobFDd+B73iWKejQ3THkwDQYJKoZIhvcNAQEF
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDUwMDEwMTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAsGA1UdDwQEAwIBBjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCU+Y0AeMXFIfX4/L2aX37uSR70
+pGpPrh0FPOwBO2ETUc4j7Whol9wOSPnUwuPIpiDhar5qRuhY6aCPGTIJbRZrVeXv
+T5uFQXq+3CQdIyI55AkFDh9tO7wX7p3pRgzma47mBxIH082Uwy7+eEeQfhuJ5cU4
+e/zyHf6FEdkSrDjgwDip+dn8Q7tnjdaN3WYQjOFRXkHyYCIFkORDPTbYSYZ6DAqq
+Q/loKTdrcbyeEwFVBZxQu4Nb6mjhkfk8U+8TIGMCTQXhoQhgMWMeMo2E0kWvYbjc
+YDiRzmnsdvPu2LKnTvH28M9ODi3ZzcOuLs6jAKAiXISq0CbmwaddV/oAMoF2
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_sha1/ca.pem.certspec
new file mode 100644
index 0000000000..7e65e9ee30
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ca.pem.certspec
@@ -0,0 +1,6 @@
+issuer:ca
+subject:ca
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+signature:sha1WithRSAEncryption
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem
new file mode 100644
index 0000000000..8fb93e69e3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtTCCAZ2gAwIBAgIUJWQl8gkrLL7gPn47YYMdN7qBP5wwDQYJKoZIhvcNAQEF
+BQAwEzERMA8GA1UEAwwIaW50LXBvc3QwIhgPMjAxNjAxMDIwMDAwMDBaGA8yMDE3
+MDIwMTAwMDAwMFowEjEQMA4GA1UEAwwHZWUtcG9zdDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7
+wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCAp
+k6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh
+eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KW
+EsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONssc
+JAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0B
+AQUFAAOCAQEAVOPvLS8zwos/RdClnabdxMh5g1WN5T1BsIMExE6oU6sJ7n4HyIXt
+RnFTFe0t2CdXBCQPK6qG8ymeLQFNKykYlQxVZb8m5YgUK3k4IeMS/EoX4g7taREI
+IwK9/n7+oKZYlz2Q/ro/R2HFmLXsCIUrsxV3vAWQCm8rSeCKzEVNlDaQ5FEMVAM7
+VlNhNNKtUnXkzZ3SRj6O4eOq3G4azr5DNo5kAQPaIbAI3k/3AHyPqIjHcoSiG1Ug
+aqzIK6fNNCIAxIKAY2ERJfxA4fPlBZXono2sCOgCdFfF7QAo+o9SO8v3B+djuHgb
+flbfdyEnN+y32UpYe3qV8LnFmRqGAe/vbQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem.certspec b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem.certspec
new file mode 100644
index 0000000000..76834f8447
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-post
+subject:ee-post
+validity:20160102-20170201
+signature:sha1WithRSAEncryption
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem
new file mode 100644
index 0000000000..2385322a64
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIUIVsTCuql4BDO2r4O5zePBEoHg3QwDQYJKoZIhvcNAQEF
+BQAwEjEQMA4GA1UEAwwHaW50LXByZTAiGA8yMDE2MDEwMTAwMDAwMFoYDzIwMTcw
+MjAxMDAwMDAwWjASMRAwDgYDVQQDDAdlZS1wb3N0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3DQEB
+BQUAA4IBAQCW5zeJg+LzGn07GtdBprRLarL7aLNFSr+s+3yzphq9kN43prElX6eD
+gdggPWicZozTm3IFLfJfsuhiodBZjWmkF/dsvvT3W77AcKzNWjShCnnA/Vf5IF6k
+U01ROfjmgcX0mMuhVUB7b9Fl6G5DFxJgny2jYehZcJIzWUBLiwu41TIxj5Cv5F9p
++XIHEyygqm8rYzbW8F49FRbsDD9nvhmdVqXsoTaKxY8bsKKu1EpBSBXozRbLKcAn
+/zrLy0HHS4qfXTHm0UKt+RCYVylL5I5YGR6rapXHChkBTBHFXdkPVsW5uHKBdBqY
+98zoAAMWRvJ3xOuqAbtKG6DKadiOpz6l
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem.certspec b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem.certspec
new file mode 100644
index 0000000000..1e8bb35b34
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-pre
+subject:ee-post
+validity:20160101-20170201
+signature:sha1WithRSAEncryption
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem b/security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem
new file mode 100644
index 0000000000..11a5e41e99
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUOU+CyYnf3GRRgGHvbeJB8HTUvJ4wDQYJKoZIhvcNAQEF
+BQAwEjEQMA4GA1UEAwwHaW50LXByZTAiGA8yMDE1MDEwMTAwMDAwMFoYDzIwMTcw
+MjAxMDAwMDAwWjARMQ8wDQYDVQQDDAZlZS1wcmUwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEF
+BQADggEBACWLcVnT0nNyU/qFZNDZPGDFPuZCXuodBf3sgz1XDmcp5wlH+IjW3J8H
+3HH6ZwIUpTtppqVxuvo2y9GP+GNyeXhxmokHTVdKDj8HqYl5lV+reO1UmzEH0AU8
+x3hd2Fkzv/h3N3EPVETWuPiFSB0oAN/xwsXC/7Yi4AY0s/I/4q/vkS76Oa2RyL3f
+gbaa80+nR73BX+0wRqyg+Sgo2hOzjkCQchtZPUFYsRLhsHHBnokD8GJlT7NBKSN1
+TFt6uXpfEmqDICoyZoAw6rtnFfdEsZNo+PU6NDN3T0fkolBQPvuzfqcqAl85dpJK
+ow8mFKkb2t6qQH98yabi/172l03/P34=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem.certspec b/security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem.certspec
new file mode 100644
index 0000000000..0f4a6ec257
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-pre
+subject:ee-pre
+validity:20150101-20170201
+signature:sha1WithRSAEncryption
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem b/security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem
new file mode 100644
index 0000000000..94ab4f5b15
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzzCCAbegAwIBAgIUYQ6xulHM0FrQ0sKMdbPI2ecC3PcwDQYJKoZIhvcNAQEF
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxNjAxMDEwMDAwMDBaGA8yMDI2MDEwMTAw
+MDAwMFowEzERMA8GA1UEAwwIaW50LXBvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg
+2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ
+5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQ
+PdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGj
+DJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8W
+iy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAsGA1UdDwQEAwIB
+BjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCJN8/XexXMusXYbhVW
+uYA1YpaSTrz96CXlckIuABnzCrGD5iHkfFoB0LPKd+EZ80fdHIvS1zQTYohy6O95
+JageOaTY9HsOEqsgr1BA9VKZW4QlGp7csQzNMa52WRetimv6XY/lrfPf4qAhomWG
+/ImmLJpGTVPhEdz6Pl4Kvmf9zNf/BcXtBTWLSGWUC5UItC58WTopqcr5kLg3DmXB
+Qr7DjqA7DT92N6qefFkTYspDZJzv0nL9OfqkdCj/s6bm3iisTQq3aek6IP6OEkXF
+4TWUF3RDBsyRpG8jt/XsOsrwJDOGZGFPmMTz2uZPsZkeZhU+sfFDWL7wojl7kHV1
+c7f5
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem.certspec b/security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem.certspec
new file mode 100644
index 0000000000..50156c9f6e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem.certspec
@@ -0,0 +1,6 @@
+issuer:ca
+subject:int-post
+validity:20160101-20260101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+signature:sha1WithRSAEncryption
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem b/security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem
new file mode 100644
index 0000000000..0b916d5755
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbagAwIBAgIUYW27TGzVOIbjpG1ADx7m7mZnrNQwDQYJKoZIhvcNAQEF
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDIwMDEwMTAw
+MDAwMFowEjEQMA4GA1UEAwwHaW50LXByZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODY
+H72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk
+27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9
+0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMM
+kd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaL
+L+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswCwYDVR0PBAQDAgEG
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAESNgcKkxBwkaBykKo2Q
+GzgimnI3eNw8O8GrhefL+r3Ek/CH4/oBHGvfvrz6D19uemdOyHxo5QNW2iWEq0No
+pE6Hhm504P1fdkfQvjqjIhu/h3y5QjO3zdMGeVE/39TWAGrGsNFKE+jSxm8IbycF
+Ue6165agasf+PhQdorjFca48iLcowKYs5Df0SAhY7zbw1fM1HTr1YGAXc1K9aCA5
+fTmu8Nd0fNKc1NcbNDpdCG2YEj1nox1iMN5A4nY1ve88zJsnlpfsoJkHJqo2Cy+M
+mpQSnkTlf3Gfpl8NO3UW9FTcnK8L4Ix2DSNBDe8Yg2YL5w/VIxecFwlmwV0wWdg6
+vl0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem.certspec b/security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem.certspec
new file mode 100644
index 0000000000..9f0a59ee99
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem.certspec
@@ -0,0 +1,6 @@
+issuer:ca
+subject:int-pre
+validity:20100101-20200101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+signature:sha1WithRSAEncryption
diff --git a/security/manager/ssl/tests/unit/test_cert_sha1/moz.build b/security/manager/ssl/tests/unit/test_cert_sha1/moz.build
new file mode 100644
index 0000000000..93cba24554
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'int-pre.pem',
+# 'ee-pre_int-pre.pem',
+# 'ee-post_int-pre.pem',
+# 'int-post.pem',
+# 'ee-post_int-post.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures.js b/security/manager/ssl/tests/unit/test_cert_signatures.js
new file mode 100644
index 0000000000..e92b5a4ab8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures.js
@@ -0,0 +1,140 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests that certificates cannot be tampered with without being detected.
+// Tests a combination of cases: RSA signatures, ECDSA signatures, certificate
+// chains where the intermediate has been tampered with, chains where the
+// end-entity has been tampered, tampering of the signature, and tampering in
+// the rest of the certificate.
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+var certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+// Reads a PEM-encoded certificate, modifies the nth byte (0-indexed), and
+// returns the base64-encoded bytes of the certificate. Negative indices may be
+// specified to modify a byte from the end of the certificate.
+function readAndTamperWithNthByte(certificatePath, n) {
+ let pem = readFile(do_get_file(certificatePath, false));
+ let der = atob(pemToBase64(pem));
+ if (n < 0) {
+ // remember, n is negative at this point
+ n = der.length + n;
+ }
+ let replacement = "\x22";
+ if (der.charCodeAt(n) == replacement) {
+ replacement = "\x23";
+ }
+ der = der.substring(0, n) + replacement + der.substring(n + 1);
+ return btoa(der);
+}
+
+// The signature on certificates appears last. This should modify the contents
+// of the signature such that it no longer validates correctly while still
+// resulting in a structurally valid certificate.
+const BYTE_IN_SIGNATURE = -8;
+function addSignatureTamperedCertificate(certificatePath) {
+ let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SIGNATURE);
+ certdb.addCertFromBase64(base64, ",,");
+}
+
+function ensureSignatureVerificationFailure(certificatePath) {
+ let cert = constructCertFromFile(certificatePath);
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ SEC_ERROR_BAD_SIGNATURE,
+ certificateUsageSSLServer
+ );
+}
+
+function tamperWithSignatureAndEnsureVerificationFailure(certificatePath) {
+ let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SIGNATURE);
+ let cert = certdb.constructX509FromBase64(base64);
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ SEC_ERROR_BAD_SIGNATURE,
+ certificateUsageSSLServer
+ );
+}
+
+// The beginning of a certificate looks like this (in hex, using DER):
+// 30 XX XX XX [the XX encode length - there are probably 3 bytes here]
+// 30 XX XX XX [length again]
+// A0 03
+// 02 01
+// 02
+// 02 XX [length again - 1 byte as long as we're using pycert]
+// XX XX ... [serial number - 20 bytes as long as we're using pycert]
+// Since we want to modify the serial number, we need to change something from
+// byte 15 to byte 34 (0-indexed). If it turns out that the two length sections
+// we assumed were 3 bytes are shorter (they can't be longer), modifying
+// something from byte 15 to byte 30 will still get us what we want. Since the
+// serial number is a DER INTEGER and because it must be positive, it's best to
+// skip the first two bytes of the serial number so as to not run into any
+// issues there. Thus byte 17 is a good byte to modify.
+const BYTE_IN_SERIAL_NUMBER = 17;
+function addSerialNumberTamperedCertificate(certificatePath) {
+ let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SERIAL_NUMBER);
+ certdb.addCertFromBase64(base64, ",,");
+}
+
+function tamperWithSerialNumberAndEnsureVerificationFailure(certificatePath) {
+ let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SERIAL_NUMBER);
+ let cert = certdb.constructX509FromBase64(base64);
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ SEC_ERROR_BAD_SIGNATURE,
+ certificateUsageSSLServer
+ );
+}
+
+add_task(async function() {
+ addCertFromFile(certdb, "test_cert_signatures/ca-rsa.pem", "CTu,,");
+ addCertFromFile(certdb, "test_cert_signatures/ca-secp384r1.pem", "CTu,,");
+
+ // Tamper with the signatures on intermediate certificates and ensure that
+ // end-entity certificates issued by those intermediates do not validate
+ // successfully.
+ addSignatureTamperedCertificate("test_cert_signatures/int-rsa.pem");
+ addSignatureTamperedCertificate("test_cert_signatures/int-secp384r1.pem");
+ await ensureSignatureVerificationFailure("test_cert_signatures/ee-rsa.pem");
+ await ensureSignatureVerificationFailure(
+ "test_cert_signatures/ee-secp384r1.pem"
+ );
+
+ // Tamper with the signatures on end-entity certificates and ensure that they
+ // do not validate successfully.
+ await tamperWithSignatureAndEnsureVerificationFailure(
+ "test_cert_signatures/ee-rsa-direct.pem"
+ );
+ await tamperWithSignatureAndEnsureVerificationFailure(
+ "test_cert_signatures/ee-secp384r1-direct.pem"
+ );
+
+ // Tamper with the serial numbers of intermediate certificates and ensure
+ // that end-entity certificates issued by those intermediates do not validate
+ // successfully.
+ addSerialNumberTamperedCertificate("test_cert_signatures/int-rsa.pem");
+ addSerialNumberTamperedCertificate("test_cert_signatures/int-secp384r1.pem");
+ await ensureSignatureVerificationFailure("test_cert_signatures/ee-rsa.pem");
+ await ensureSignatureVerificationFailure(
+ "test_cert_signatures/ee-secp384r1.pem"
+ );
+
+ // Tamper with the serial numbers of end-entity certificates and ensure that
+ // they do not validate successfully.
+ await tamperWithSerialNumberAndEnsureVerificationFailure(
+ "test_cert_signatures/ee-rsa-direct.pem"
+ );
+ await tamperWithSerialNumberAndEnsureVerificationFailure(
+ "test_cert_signatures/ee-secp384r1-direct.pem"
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem b/security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem
new file mode 100644
index 0000000000..290b01e0b2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUJaZFyIe9qjfQ+Uofk9CZqYEHlTgwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGY2EtcnNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBExDzANBgNVBAMMBmNhLXJzYTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswDAYDVR0TBAUw
+AwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBALa6fRuvA3KLtAMN
+jeo0kNekw4UJ4eZK2m1XPHOEbrPXIZ9tTRAPPioOuNCLHYsj0038iu2tjpHmorgs
+sCYSjvVxVxu9jWu2Ka99X/o8j1bH137Rrr9JpD1/Ur5/TvqjS4YmL+vhmogb9fr6
+1Duw6H9/L0E+KROhAQcxjAxe3kGHhBeIMLPFo4jtpTQo5rFUA2N1z9BHd7J3/zAv
+T72inci5G739Uo2l2EeAWB3VtqrMCZeSLw4WbBJiRS9sO7Q3weIRH+k5qqBlKcB9
+hQnP+MXADWkphfe5AucI8aKY5fK5vCn2SlWQ+/6J3rnPkNUYVHkjKTnqkPBeoCeq
+S3FICGE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem.certspec
new file mode 100644
index 0000000000..5890d2db60
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ca-rsa.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca-rsa
+subject:ca-rsa
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem b/security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem
new file mode 100644
index 0000000000..fd92a46fef
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBjjCCARSgAwIBAgIUOyTEDH+eecqyzh2AucmMGj9fTLAwCgYIKoZIzj0EAwIw
+FzEVMBMGA1UEAwwMY2Etc2VjcDM4NHIxMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAy
+MjAyMDUwMDAwMDBaMBcxFTATBgNVBAMMDGNhLXNlY3AzODRyMTB2MBAGByqGSM49
+AgEGBSuBBAAiA2IABKFockM2K1x7GInzeRVGFaHHP7SN7oY+AikV22COJS3ktxMt
+qM6Y6DFTTmqcDAsJyNY5regyBuW6gTRzoR+jMOBdqMluQ4P+J4c9qXEDviiIz/AC
+8Fr3Gh/dzIN0qm6pzqMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwCgYI
+KoZIzj0EAwIDaAAwZQIxAO0GJz6haDpUtNgaQ3SESJY85j6+gRcD7Nc9cvCiVAZZ
+1OxFRuhW515lVbeTqfcA8wIwEy0o836FaKqjhXZB1FUAVV+3Qr3iStR9Be83+S6N
+9SZg7pExVwePnv4bYkrcja5s
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem.certspec
new file mode 100644
index 0000000000..0701c23c1e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ca-secp384r1.pem.certspec
@@ -0,0 +1,7 @@
+issuer:ca-secp384r1
+subject:ca-secp384r1
+issuerKey:secp384r1
+subjectKey:secp384r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem
new file mode 100644
index 0000000000..3e1eef7d68
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuTCCAaGgAwIBAgIUfab+E3B6QwSzRFe7us5sNYBJa10wDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGY2EtcnNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBgxFjAUBgNVBAMMDWVlLXJzYS1kaXJlY3QwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
+V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
+0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
+fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
+W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
+TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZI
+hvcNAQELBQADggEBAJMWIw3o4w2U4Nvw2R0QZ41JZDGMSRSj88Jc3rqU9R0RrmuJ
+xWj1/PidCJGo0ZhKsBxgleezXqyxPrnm/2nIK6GValEbHQh5lybnGugnu8sjjHv0
+XRifYbr+Hw1IIs6ZCZbqt6X4BsbinS8Kj/SArptw45dnXYO+xQVkcZVXRj8tRuWo
+9H3uWpnspur3Jod2fhhyEWTYYHiH/B7XtD81slgWrNwPFvsdDiiLqBgsZ7O/vDAO
+zE7VowW8qcs7R3sNOZrPx5bW+uAp2LSpZVToLdInMzLuLA9DdUqppyE5uBQ+MgEy
+s052+WQ549Z8LN0l8iLYs9Ir9p437jNw9sty12k=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem.certspec
new file mode 100644
index 0000000000..4e25ddcf94
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa-direct.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca-rsa
+subject:ee-rsa-direct
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem
new file mode 100644
index 0000000000..7b368c2554
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUG5+/xrLNQFse7iMoGWpBJsrHx84wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHaW50LXJzYTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjARMQ8wDQYDVQQDDAZlZS1yc2EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBAFuOB6tFv6DrMoDrZcBwqptvrUE152IoCVT7M+p3VPIgTA4jyAm3tgog
+ID3CZJmoYJrd+7HNDhFsjudGOF21k8z8snqSCCYBDETlLz5EKq5XmVGLw+hdKyZB
+MtCmXXDr4qBFupN4D7x/1boqiB7L/AvDPOTk3H+UOF4xPLRFFSCZda0pAcbuPkAn
+M07E1P9/qN6qBNp0/AT8tOHvcw5L2oSnfSJyYDCB+fStuh3MGj8crwq5sOprEWXp
+5xxFiGM+0RyVTj6CqkJxb7UuY1g+rqKHxQo0CauF3g6pWFYz68NfHdOxiq17T5Ze
+cc/o2Bl0HfldhPykzcdafPe4sEF0gxQ=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem.certspec
new file mode 100644
index 0000000000..b974a0a0a8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-rsa.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-rsa
+subject:ee-rsa
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem
new file mode 100644
index 0000000000..9a09340159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBdTCB/KADAgECAhRfE+Et8NOs0+Akxjdd6jKTDpPw6jAKBggqhkjOPQQDAjAX
+MRUwEwYDVQQDDAxjYS1zZWNwMzg0cjEwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIy
+MDIwNTAwMDAwMFowHjEcMBoGA1UEAwwTZWUtc2VjcDM4NHIxLWRpcmVjdDB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABKFockM2K1x7GInzeRVGFaHHP7SN7oY+AikV22CO
+JS3ktxMtqM6Y6DFTTmqcDAsJyNY5regyBuW6gTRzoR+jMOBdqMluQ4P+J4c9qXED
+viiIz/AC8Fr3Gh/dzIN0qm6pzjAKBggqhkjOPQQDAgNoADBlAjEA7QYnPqFoOlS0
+2BpDdIRIljzmPr6BFwPs1z1y8KJUBlnU7EVG6FbnXmVVt5Op9wDzAjAYYo+yN543
+lFkKtNGxnw22Jtgv0oA8co0+1n9dOxz8zTR0msc+9jzshYa7VZQ0hNA=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem.certspec
new file mode 100644
index 0000000000..386ab95f78
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1-direct.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca-secp384r1
+subject:ee-secp384r1-direct
+issuerKey:secp384r1
+subjectKey:secp384r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem
new file mode 100644
index 0000000000..1baaad5156
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBcDCB9qADAgECAhRPYP/LpKATb8Eee6XdLyXiGnwgJzAKBggqhkjOPQQDAjAY
+MRYwFAYDVQQDDA1pbnQtc2VjcDM4NHIxMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAy
+MjAyMDUwMDAwMDBaMBcxFTATBgNVBAMMDGVlLXNlY3AzODRyMTB2MBAGByqGSM49
+AgEGBSuBBAAiA2IABKFockM2K1x7GInzeRVGFaHHP7SN7oY+AikV22COJS3ktxMt
+qM6Y6DFTTmqcDAsJyNY5regyBuW6gTRzoR+jMOBdqMluQ4P+J4c9qXEDviiIz/AC
+8Fr3Gh/dzIN0qm6pzjAKBggqhkjOPQQDAgNpADBmAjEA7QYnPqFoOlS02BpDdIRI
+ljzmPr6BFwPs1z1y8KJUBlnU7EVG6FbnXmVVt5Op9wDzAjEAx9LL4IWV8HF8MrGQ
+ZdFqG+sVSQ3QCUcEsVjGG/gZ6RcMlEnjW21iGqxnIZpKtiTN
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem.certspec
new file mode 100644
index 0000000000..b8f7993be8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/ee-secp384r1.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int-secp384r1
+subject:ee-secp384r1
+issuerKey:secp384r1
+subjectKey:secp384r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem b/security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem
new file mode 100644
index 0000000000..49f8879ed2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0jCCAbqgAwIBAgIURzatuu7JX78XkY/w99jKWmox0howDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGY2EtcnNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBIxEDAOBgNVBAMMB2ludC1yc2EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQF
+MAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAQ64v/9Z3EylV0
+lsRQI0PevDlAIEL8YHGBJWToq++WJGC3eemcoxyvvw1b9rvZgdkrrjx/+bjtOJci
+7kloRJ2xiZOYQlqvnpwFcNqTlMDZnuWkPX50ZBUelqk4R6cwm0HB1VFIN+GOwCMD
+zCC7t4a66HhAiqaejMDVcWMzw9VqytWgwm/isui/uBYQW84NGI1GKy1cIk770S6Q
+J6HTKE6LM526ytMjIlGBtN9D7hl7K8AMuupKySA2QASodOrAJqoT5U2hlCl5OviF
+OjmxnbgvveU6kdKN8a7z7y3uTkjhZpef81ImbF1jj8E1KtkC+b7Lm/CbK+IiEXxL
+u9pvfao3
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem.certspec
new file mode 100644
index 0000000000..a86d28b44a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/int-rsa.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca-rsa
+subject:int-rsa
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem b/security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem
new file mode 100644
index 0000000000..ab896dbead
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBjzCCARWgAwIBAgIUMSRDv+0O5jNq1Obl2F8M+xzDNGUwCgYIKoZIzj0EAwIw
+FzEVMBMGA1UEAwwMY2Etc2VjcDM4NHIxMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAy
+MjAyMDUwMDAwMDBaMBgxFjAUBgNVBAMMDWludC1zZWNwMzg0cjEwdjAQBgcqhkjO
+PQIBBgUrgQQAIgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcT
+LajOmOgxU05qnAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/w
+AvBa9xof3cyDdKpuqc6jHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoG
+CCqGSM49BAMCA2gAMGUCMQDtBic+oWg6VLTYGkN0hEiWPOY+voEXA+zXPXLwolQG
+WdTsRUboVudeZVW3k6n3APMCMCNZB4rLFK+Njz93hs7gAv/HPo9isW3PSdPN7Dt6
+5lZVYZzti2Ram/Fr4ki+/pP+/g==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem.certspec b/security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem.certspec
new file mode 100644
index 0000000000..e002a1569a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/int-secp384r1.pem.certspec
@@ -0,0 +1,7 @@
+issuer:ca-secp384r1
+subject:int-secp384r1
+issuerKey:secp384r1
+subjectKey:secp384r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_cert_signatures/moz.build b/security/manager/ssl/tests/unit/test_cert_signatures/moz.build
new file mode 100644
index 0000000000..f2bbe7a375
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_signatures/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca-rsa.pem',
+# 'ca-secp384r1.pem',
+# 'ee-rsa-direct.pem',
+# 'ee-rsa.pem',
+# 'ee-secp384r1-direct.pem',
+# 'ee-secp384r1.pem',
+# 'int-rsa.pem',
+# 'int-secp384r1.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_storage.js b/security/manager/ssl/tests/unit/test_cert_storage.js
new file mode 100644
index 0000000000..ff0d1f24c8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage.js
@@ -0,0 +1,286 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// This test checks a number of things:
+// * it ensures that data loaded from revocations.txt on startup is present
+// * it ensures that data served from OneCRL are persisted correctly
+// * it ensures that items in the CertBlocklist are seen as revoked by the
+// cert verifier
+// * it does a sanity check to ensure other cert verifier behavior is
+// unmodified
+
+const { setTimeout } = ChromeUtils.import(
+ "resource://gre/modules/Timer.jsm",
+ {}
+);
+const { RemoteSecuritySettings } = ChromeUtils.import(
+ "resource://gre/modules/psm/RemoteSecuritySettings.jsm"
+);
+
+// First, we need to setup appInfo for the blocklist service to work
+var id = "xpcshell@tests.mozilla.org";
+var appName = "XPCShell";
+var version = "1";
+var platformVersion = "1.9.2";
+ChromeUtils.import("resource://testing-common/AppInfo.jsm", this); // Imported via AppInfo.jsm.
+/* global updateAppInfo:false */ updateAppInfo({
+ name: appName,
+ ID: id,
+ version,
+ platformVersion: platformVersion ? platformVersion : "1.0",
+ crashReporter: true,
+});
+
+// we need to ensure we setup revocation data before certDB, or we'll start with
+// no revocation.txt in the profile
+var gProfile = do_get_profile();
+
+var gRevocations = gProfile.clone();
+gRevocations.append("revocations.txt");
+if (!gRevocations.exists()) {
+ let existing = do_get_file("test_onecrl/sample_revocations.txt", false);
+ existing.copyTo(gProfile, "revocations.txt");
+}
+
+var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const certBlocklist = [
+ // test with some bad data ...
+ {
+ issuerName: "Some nonsense in issuer",
+ serialNumber: "AkHVNA==",
+ },
+ {
+ issuerName: "MA0xCzAJBgNVBAMMAmNh",
+ serialNumber: "some nonsense in serial",
+ },
+ {
+ issuerName: "and serial",
+ serialNumber: "some nonsense in both issuer",
+ },
+ // some mixed
+ // In these case, the issuer name and the valid serialNumber correspond
+ // to test-int.pem in bad_certs/
+ {
+ issuerName: "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=",
+ serialNumber: "oops! more nonsense.",
+ },
+ {
+ issuerName: "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=",
+ serialNumber: "a0X7/7DlTaedpgrIJg25iBPOkIM=",
+ },
+ // ... and some good
+ // In this case, the issuer name and the valid serialNumber correspond
+ // to other-test-ca.pem in bad_certs/ (for testing root revocation)
+ {
+ issuerName: "MBgxFjAUBgNVBAMMDU90aGVyIHRlc3QgQ0E=",
+ serialNumber: "Rym6o+VN9xgZXT/QLrvN/nv1ZN4=",
+ },
+ // These items correspond to an entry in sample_revocations.txt where:
+ // isser name is the base-64 encoded subject DN for the shared Test
+ // Intermediate and the serialNumbers are base-64 encoded 78 and 31,
+ // respectively.
+ // We need this to ensure that existing items are retained if they're
+ // also in the blocklist
+ {
+ issuerName: "MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl",
+ serialNumber: "Tg==",
+ },
+ {
+ issuerName: "MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl",
+ serialNumber: "Hw==",
+ },
+ // This item revokes same-issuer-ee.pem by subject and pubKeyHash.
+ {
+ subject: "MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5",
+ pubKeyHash: "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=",
+ },
+];
+
+function verify_cert(file, expectedError) {
+ let ee = constructCertFromFile(file);
+ return checkCertErrorGeneric(
+ certDB,
+ ee,
+ expectedError,
+ certificateUsageSSLServer
+ );
+}
+
+// The certificate blocklist currently only applies to TLS server certificates.
+async function verify_non_tls_usage_succeeds(file) {
+ let ee = constructCertFromFile(file);
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certDB,
+ ee,
+ PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+}
+
+function load_cert(cert, trust) {
+ let file = "bad_certs/" + cert + ".pem";
+ addCertFromFile(certDB, file, trust);
+}
+
+async function update_blocklist() {
+ const { OneCRLBlocklistClient } = RemoteSecuritySettings.init();
+
+ const fakeEvent = {
+ current: certBlocklist, // with old .txt revocations.
+ deleted: [],
+ created: certBlocklist, // with new cert storage.
+ updated: [],
+ };
+ await OneCRLBlocklistClient.emit("sync", { data: fakeEvent });
+ // Save the last check timestamp, used by cert_storage to assert
+ // if the blocklist is «fresh».
+ Services.prefs.setIntPref(
+ OneCRLBlocklistClient.lastCheckTimePref,
+ Math.floor(Date.now() / 1000)
+ );
+}
+
+function* generate_revocations_txt_lines() {
+ let profile = do_get_profile();
+ let revocations = profile.clone();
+ revocations.append("revocations.txt");
+ ok(revocations.exists(), "the revocations file should exist");
+ let inputStream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+ inputStream.init(revocations, -1, -1, 0);
+ inputStream.QueryInterface(Ci.nsILineInputStream);
+ let hasmore = false;
+ do {
+ let line = {};
+ hasmore = inputStream.readLine(line);
+ yield line.value;
+ } while (hasmore);
+}
+
+function run_test() {
+ // import the certificates we need
+ load_cert("test-ca", "CTu,CTu,CTu");
+ load_cert("test-int", ",,");
+ load_cert("other-test-ca", "CTu,CTu,CTu");
+
+ let certList = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+
+ add_task(async function() {
+ // check some existing items in revocations.txt are blocked.
+ // This test corresponds to:
+ // issuer: MBIxEDAOBgNVBAMMB1Rlc3QgQ0E= (CN=Test CA)
+ // serial: Kg== (42)
+ let file = "test_onecrl/ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // This test corresponds to:
+ // issuer: MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl (CN=Test Intermediate)
+ // serial: Tg== (78)
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // And this test corresponds to:
+ // issuer: MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl (CN=Test Intermediate)
+ // serial: Hw== (31)
+ // (we test this issuer twice to ensure we can read multiple serials)
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // Test that a certificate revoked by subject and public key hash in
+ // revocations.txt is revoked
+ // subject: MCsxKTAnBgNVBAMMIEVFIFJldm9rZWQgQnkgU3ViamVjdCBhbmQgUHViS2V5
+ // (CN=EE Revoked By Subject and PubKey)
+ // pubkeyhash: VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8 (this is the
+ // shared RSA SPKI)
+ file = "test_onecrl/ee-revoked-by-subject-and-pubkey.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // Soon we'll load a blocklist which revokes test-int.pem, which issued
+ // test-int-ee.pem.
+ // Check the cert validates before we load the blocklist
+ file = "test_onecrl/test-int-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+
+ // The blocklist also revokes other-test-ca.pem, which issued
+ // other-ca-ee.pem. Check the cert validates before we load the blocklist
+ file = "bad_certs/other-issuer-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+
+ // The blocklist will revoke same-issuer-ee.pem via subject / pubKeyHash.
+ // Check the cert validates before we load the blocklist
+ file = "test_onecrl/same-issuer-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+ });
+
+ // blocklist load is async so we must use add_test from here
+ add_task(update_blocklist);
+
+ add_task(async function() {
+ // The blocklist will be loaded now. Let's check the data is sane.
+ // In particular, we should still have the revoked issuer / serial pair
+ // that was in revocations.txt but not the blocklist.
+ let file = "test_onecrl/ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // We should also still have the revoked issuer / serial pairs that were in
+ // revocations.txt and are also in the blocklist.
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ file = "test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // The cert revoked by subject and pubkeyhash should still be revoked.
+ file = "test_onecrl/ee-revoked-by-subject-and-pubkey.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+
+ // Check the blocklisted intermediate now causes a failure
+ file = "test_onecrl/test-int-ee.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ await verify_non_tls_usage_succeeds(file);
+
+ // Check the ee with the blocklisted root also causes a failure
+ file = "bad_certs/other-issuer-ee.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ await verify_non_tls_usage_succeeds(file);
+
+ // Check the ee blocked by subject / pubKey causes a failure
+ file = "test_onecrl/same-issuer-ee.pem";
+ await verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
+ await verify_non_tls_usage_succeeds(file);
+
+ // Check a non-blocklisted chain still validates OK
+ file = "bad_certs/default-ee.pem";
+ await verify_cert(file, PRErrorCodeSuccess);
+
+ // Check a bad cert is still bad (unknown issuer)
+ file = "bad_certs/unknownissuer.pem";
+ await verify_cert(file, SEC_ERROR_UNKNOWN_ISSUER);
+ });
+
+ add_task(async function() {
+ ok(certList.isBlocklistFresh(), "Blocklist should be fresh.");
+ });
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_broken_db.js b/security/manager/ssl/tests/unit/test_cert_storage_broken_db.js
new file mode 100644
index 0000000000..070482f236
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_broken_db.js
@@ -0,0 +1,95 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// This file tests cert_storage's automatic database recreation mechanism. If
+// opening the database for the first time fails, cert_storage will re-create
+// it.
+
+function call_has_prior_data(certStorage, type) {
+ return new Promise(resolve => {
+ certStorage.hasPriorData(type, (rv, hasPriorData) => {
+ Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
+ resolve(hasPriorData);
+ });
+ });
+}
+
+async function check_has_prior_revocation_data(certStorage, expectedResult) {
+ let hasPriorRevocationData = await call_has_prior_data(
+ certStorage,
+ Ci.nsICertStorage.DATA_TYPE_REVOCATION
+ );
+ Assert.equal(
+ hasPriorRevocationData,
+ expectedResult,
+ `should ${expectedResult ? "have" : "not have"} prior revocation data`
+ );
+}
+
+async function check_has_prior_cert_data(certStorage, expectedResult) {
+ let hasPriorCertData = await call_has_prior_data(
+ certStorage,
+ Ci.nsICertStorage.DATA_TYPE_CERTIFICATE
+ );
+ Assert.equal(
+ hasPriorCertData,
+ expectedResult,
+ `should ${expectedResult ? "have" : "not have"} prior cert data`
+ );
+}
+
+async function check_has_prior_crlite_data(certStorage, expectedResult) {
+ let hasPriorCRLiteData = await call_has_prior_data(
+ certStorage,
+ Ci.nsICertStorage.DATA_TYPE_CRLITE
+ );
+ Assert.equal(
+ hasPriorCRLiteData,
+ expectedResult,
+ `should ${expectedResult ? "have" : "not have"} prior CRLite data`
+ );
+}
+
+add_task(async function() {
+ // Create an invalid database.
+ let fileToCopy = do_get_file("test_cert_storage_broken_db.js");
+ let dbDirectory = do_get_profile();
+ dbDirectory.append("security_state");
+ fileToCopy.copyTo(dbDirectory, "data.mdb");
+
+ let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+ check_has_prior_revocation_data(certStorage, false);
+ check_has_prior_cert_data(certStorage, false);
+ check_has_prior_crlite_data(certStorage, false);
+
+ let result = await new Promise(resolve => {
+ certStorage.setRevocations([], resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "setRevocations should succeed");
+
+ check_has_prior_revocation_data(certStorage, true);
+ check_has_prior_cert_data(certStorage, false);
+ check_has_prior_crlite_data(certStorage, false);
+
+ result = await new Promise(resolve => {
+ certStorage.addCerts([], resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "addCerts should succeed");
+
+ check_has_prior_revocation_data(certStorage, true);
+ check_has_prior_cert_data(certStorage, true);
+ check_has_prior_crlite_data(certStorage, false);
+
+ result = await new Promise(resolve => {
+ certStorage.setCRLiteState([], resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "setCRLiteState should succeed");
+ check_has_prior_revocation_data(certStorage, true);
+ check_has_prior_cert_data(certStorage, true);
+ check_has_prior_crlite_data(certStorage, true);
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct.js b/security/manager/ssl/tests/unit/test_cert_storage_direct.js
new file mode 100644
index 0000000000..09d8a5bd17
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct.js
@@ -0,0 +1,533 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// This file consists of unit tests for cert_storage (whereas test_cert_storage.js is more of an
+// integration test).
+
+do_get_profile();
+
+this.certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+);
+
+async function addCerts(certInfos) {
+ let result = await new Promise(resolve => {
+ certStorage.addCerts(certInfos, resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "addCerts should succeed");
+}
+
+async function removeCertsByHashes(hashesBase64) {
+ let result = await new Promise(resolve => {
+ certStorage.removeCertsByHashes(hashesBase64, resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "removeCertsByHashes should succeed");
+}
+
+function getLongString(uniquePart, length) {
+ return String(uniquePart).padStart(length, "0");
+}
+
+class CertInfo {
+ constructor(cert, subject) {
+ this.cert = btoa(cert);
+ this.subject = btoa(subject);
+ this.trust = Ci.nsICertStorage.TRUST_INHERIT;
+ }
+}
+CertInfo.prototype.QueryInterface = ChromeUtils.generateQI(["nsICertInfo"]);
+
+add_task(async function test_common_subject() {
+ let someCert1 = new CertInfo(
+ "some certificate bytes 1",
+ "some common subject"
+ );
+ let someCert2 = new CertInfo(
+ "some certificate bytes 2",
+ "some common subject"
+ );
+ let someCert3 = new CertInfo(
+ "some certificate bytes 3",
+ "some common subject"
+ );
+ await addCerts([someCert1, someCert2, someCert3]);
+ let storedCerts = certStorage.findCertsBySubject(
+ stringToArray("some common subject")
+ );
+ let storedCertsAsStrings = storedCerts.map(arrayToString);
+ let expectedCerts = [
+ "some certificate bytes 1",
+ "some certificate bytes 2",
+ "some certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should find expected certs"
+ );
+
+ await addCerts([
+ new CertInfo("some other certificate bytes", "some other subject"),
+ ]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("some common subject")
+ );
+ storedCertsAsStrings = storedCerts.map(arrayToString);
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should still find expected certs"
+ );
+
+ let storedOtherCerts = certStorage.findCertsBySubject(
+ stringToArray("some other subject")
+ );
+ let storedOtherCertsAsStrings = storedOtherCerts.map(arrayToString);
+ let expectedOtherCerts = ["some other certificate bytes"];
+ Assert.deepEqual(
+ storedOtherCertsAsStrings,
+ expectedOtherCerts,
+ "should have other certificate"
+ );
+});
+
+add_task(async function test_many_entries() {
+ const NUM_CERTS = 500;
+ const CERT_LENGTH = 3000;
+ const SUBJECT_LENGTH = 40;
+ let certs = [];
+ for (let i = 0; i < NUM_CERTS; i++) {
+ certs.push(
+ new CertInfo(
+ getLongString(i, CERT_LENGTH),
+ getLongString(i, SUBJECT_LENGTH)
+ )
+ );
+ }
+ await addCerts(certs);
+ for (let i = 0; i < NUM_CERTS; i++) {
+ let subject = stringToArray(getLongString(i, SUBJECT_LENGTH));
+ let storedCerts = certStorage.findCertsBySubject(subject);
+ Assert.equal(
+ storedCerts.length,
+ 1,
+ "should have 1 certificate (lots of data test)"
+ );
+ let storedCertAsString = arrayToString(storedCerts[0]);
+ Assert.equal(
+ storedCertAsString,
+ getLongString(i, CERT_LENGTH),
+ "certificate should be as expected (lots of data test)"
+ );
+ }
+});
+
+add_task(async function test_removal() {
+ // As long as cert_storage is given valid base64, attempting to delete some nonexistent
+ // certificate will "succeed" (it'll do nothing).
+ await removeCertsByHashes([btoa("thishashisthewrongsize")]);
+
+ let removalCert1 = new CertInfo(
+ "removal certificate bytes 1",
+ "common subject to remove"
+ );
+ let removalCert2 = new CertInfo(
+ "removal certificate bytes 2",
+ "common subject to remove"
+ );
+ let removalCert3 = new CertInfo(
+ "removal certificate bytes 3",
+ "common subject to remove"
+ );
+ await addCerts([removalCert1, removalCert2, removalCert3]);
+
+ let storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ let storedCertsAsStrings = storedCerts.map(arrayToString);
+ let expectedCerts = [
+ "removal certificate bytes 1",
+ "removal certificate bytes 2",
+ "removal certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should find expected certs before removing them"
+ );
+
+ // echo -n "removal certificate bytes 2" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes(["2nUPHwl5TVr1mAD1FU9FivLTlTb0BAdnVUhsYgBccN4="]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ storedCertsAsStrings = storedCerts.map(arrayToString);
+ expectedCerts = [
+ "removal certificate bytes 1",
+ "removal certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should only have first and third certificates now"
+ );
+
+ // echo -n "removal certificate bytes 1" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes(["8zoRqHYrklr7Zx6UWpzrPuL+ol8KL1Ml6XHBQmXiaTY="]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ storedCertsAsStrings = storedCerts.map(arrayToString);
+ expectedCerts = ["removal certificate bytes 3"];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should only have third certificate now"
+ );
+
+ // echo -n "removal certificate bytes 3" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes(["vZn7GwDSabB/AVo0T+N26nUsfSXIIx4NgQtSi7/0p/w="]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("common subject to remove")
+ );
+ Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now");
+
+ // echo -n "removal certificate bytes 3" | sha256sum | xxd -r -p | base64
+ // Again, removing a nonexistent certificate should "succeed".
+ await removeCertsByHashes(["vZn7GwDSabB/AVo0T+N26nUsfSXIIx4NgQtSi7/0p/w="]);
+});
+
+add_task(async function test_batched_removal() {
+ let removalCert1 = new CertInfo(
+ "batch removal certificate bytes 1",
+ "batch subject to remove"
+ );
+ let removalCert2 = new CertInfo(
+ "batch removal certificate bytes 2",
+ "batch subject to remove"
+ );
+ let removalCert3 = new CertInfo(
+ "batch removal certificate bytes 3",
+ "batch subject to remove"
+ );
+ await addCerts([removalCert1, removalCert2, removalCert3]);
+ let storedCerts = certStorage.findCertsBySubject(
+ stringToArray("batch subject to remove")
+ );
+ let storedCertsAsStrings = storedCerts.map(arrayToString);
+ let expectedCerts = [
+ "batch removal certificate bytes 1",
+ "batch removal certificate bytes 2",
+ "batch removal certificate bytes 3",
+ ];
+ Assert.deepEqual(
+ storedCertsAsStrings.sort(),
+ expectedCerts.sort(),
+ "should find expected certs before removing them"
+ );
+ // echo -n "batch removal certificate bytes 1" | sha256sum | xxd -r -p | base64
+ // echo -n "batch removal certificate bytes 2" | sha256sum | xxd -r -p | base64
+ // echo -n "batch removal certificate bytes 3" | sha256sum | xxd -r -p | base64
+ await removeCertsByHashes([
+ "EOEEUTuanHZX9NFVCoMKVT22puIJC6g+ZuNPpJgvaa8=",
+ "Xz6h/Kvn35cCLJEZXkjPqk1GG36b56sreLyAXpO+0zg=",
+ "Jr7XdiTT8ZONUL+ogNNMW2oxKxanvYOLQPKBPgH/has=",
+ ]);
+ storedCerts = certStorage.findCertsBySubject(
+ stringToArray("batch subject to remove")
+ );
+ Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now");
+});
+
+class CRLiteState {
+ constructor(subject, spkiHash, state) {
+ this.subject = btoa(subject);
+ this.spkiHash = spkiHash;
+ this.state = state;
+ }
+}
+CRLiteState.prototype.QueryInterface = ChromeUtils.generateQI([
+ "nsICRLiteState",
+]);
+
+async function addCRLiteState(state) {
+ let result = await new Promise(resolve => {
+ certStorage.setCRLiteState(state, resolve);
+ });
+ Assert.equal(result, Cr.NS_OK, "setCRLiteState should succeed");
+}
+
+add_task(async function test_crlite_state() {
+ // echo -n "some spki 1" | sha256sum | xxd -r -p | base64
+ let crliteState1 = new CRLiteState(
+ "some subject 1",
+ "bDlKlhR5ptlvuxclnZ3RQHznG8/3pgIybrRJ/Zvn9L8=",
+ Ci.nsICertStorage.STATE_ENFORCE
+ );
+ // echo -n "some spki 2" | sha256sum | xxd -r -p | base64
+ let crliteState2 = new CRLiteState(
+ "some subject 2",
+ "ZlXvlHhtdx4yKwkhZqg7Opv5T1ofwzorlsCoLf0wnlY=",
+ Ci.nsICertStorage.STATE_UNSET
+ );
+ // echo -n "some spki 3" | sha256sum | xxd -r -p | base64
+ let crliteState3 = new CRLiteState(
+ "some subject 3",
+ "pp1SRn6njaHX/c+b2uf82JPeBkWhPfTBp/Mxb3xkjRM=",
+ Ci.nsICertStorage.STATE_ENFORCE
+ );
+ await addCRLiteState([crliteState1, crliteState2, crliteState3]);
+
+ let state1 = certStorage.getCRLiteState(
+ stringToArray("some subject 1"),
+ stringToArray("some spki 1")
+ );
+ Assert.equal(state1, Ci.nsICertStorage.STATE_ENFORCE);
+ let state2 = certStorage.getCRLiteState(
+ stringToArray("some subject 2"),
+ stringToArray("some spki 2")
+ );
+ Assert.equal(state2, Ci.nsICertStorage.STATE_UNSET);
+ let state3 = certStorage.getCRLiteState(
+ stringToArray("some subject 3"),
+ stringToArray("some spki 3")
+ );
+ Assert.equal(state3, Ci.nsICertStorage.STATE_ENFORCE);
+
+ // Check that if we never set the state of a particular subject/spki pair, we get "unset" when we
+ // look it up.
+ let stateNeverSet = certStorage.getCRLiteState(
+ stringToArray("some unknown subject"),
+ stringToArray("some unknown spki")
+ );
+ Assert.equal(stateNeverSet, Ci.nsICertStorage.STATE_UNSET);
+
+ // "some subject 1"/"some spki 1" and "some subject 3"/"some spki 3" both have their CRLite state
+ // set. However, the combination of "some subject 3"/"some spki1" should not.
+ let stateDifferentSubjectSPKI = certStorage.getCRLiteState(
+ stringToArray("some subject 3"),
+ stringToArray("some spki 1")
+ );
+ Assert.equal(stateDifferentSubjectSPKI, Ci.nsICertStorage.STATE_UNSET);
+
+ let anotherStateDifferentSubjectSPKI = certStorage.getCRLiteState(
+ stringToArray("some subject 1"),
+ stringToArray("some spki 2")
+ );
+ Assert.equal(anotherStateDifferentSubjectSPKI, Ci.nsICertStorage.STATE_UNSET);
+ let yetAnotherStateDifferentSubjectSPKI = certStorage.getCRLiteState(
+ stringToArray("some subject 2"),
+ stringToArray("some spki 1")
+ );
+ Assert.equal(
+ yetAnotherStateDifferentSubjectSPKI,
+ Ci.nsICertStorage.STATE_UNSET
+ );
+
+ crliteState3 = new CRLiteState(
+ "some subject 3",
+ "pp1SRn6njaHX/c+b2uf82JPeBkWhPfTBp/Mxb3xkjRM=",
+ Ci.nsICertStorage.STATE_UNSET
+ );
+ await addCRLiteState([crliteState3]);
+ state3 = certStorage.getCRLiteState(
+ stringToArray("some subject 3"),
+ stringToArray("some spki 3")
+ );
+ Assert.equal(state3, Ci.nsICertStorage.STATE_UNSET);
+
+ crliteState2 = new CRLiteState(
+ "some subject 2",
+ "ZlXvlHhtdx4yKwkhZqg7Opv5T1ofwzorlsCoLf0wnlY=",
+ Ci.nsICertStorage.STATE_ENFORCE
+ );
+ await addCRLiteState([crliteState2]);
+ state2 = certStorage.getCRLiteState(
+ stringToArray("some subject 2"),
+ stringToArray("some spki 2")
+ );
+ Assert.equal(state2, Ci.nsICertStorage.STATE_ENFORCE);
+
+ // Inserting a subject/spki pair with a state value outside of our expected
+ // values will succeed. However, since our data type is a signed 16-bit value,
+ // values outside that range will be truncated. The least significant 16 bits
+ // of 2013003773 are FFFD, which when interpreted as a signed 16-bit integer
+ // comes out to -3.
+ // echo -n "some spki 4" | sha256sum | xxd -r -p | base64
+ let bogusValueState = new CRLiteState(
+ "some subject 4",
+ "1eA0++hCqzt8vpzREYSqHAqpEOLchZca1Gx8viCVYzc=",
+ 2013003773
+ );
+ await addCRLiteState([bogusValueState]);
+ let bogusValueStateValue = certStorage.getCRLiteState(
+ stringToArray("some subject 4"),
+ stringToArray("some spki 4")
+ );
+ Assert.equal(bogusValueStateValue, -3);
+});
+
+async function enrollCertForCRLite(nsCert) {
+ let { subjectString, spkiHashString } = getSubjectAndSPKIHash(nsCert);
+ let crliteState = new CRLiteState(
+ subjectString,
+ spkiHashString,
+ Ci.nsICertStorage.STATE_ENFORCE
+ );
+ await addCRLiteState([crliteState]);
+}
+
+add_task(async function test_crlite_filter() {
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let validCertIssuer = constructCertFromFile(
+ "test_cert_storage_direct/valid-cert-issuer.pem"
+ );
+ await enrollCertForCRLite(validCertIssuer);
+ let validCert = constructCertFromFile(
+ "test_cert_storage_direct/valid-cert.pem"
+ );
+ let revokedCertIssuer = constructCertFromFile(
+ "test_cert_storage_direct/revoked-cert-issuer.pem"
+ );
+ await enrollCertForCRLite(revokedCertIssuer);
+ let revokedCert = constructCertFromFile(
+ "test_cert_storage_direct/revoked-cert.pem"
+ );
+
+ let filterFile = do_get_file(
+ "test_cert_storage_direct/test-filter.crlite",
+ false
+ );
+ ok(filterFile.exists(), "test filter file should exist");
+ let filterBytes = stringToArray(readFile(filterFile));
+ // First simulate the filter being from before the certificates being tested are valid. With
+ // CRLite enabled, none of the certificates should appear to be revoked.
+ let setFullCRLiteFilterResult = await new Promise(resolve => {
+ certStorage.setFullCRLiteFilter(
+ filterBytes,
+ new Date("2017-10-28T00:00:00Z").getTime() / 1000,
+ resolve
+ );
+ });
+ Assert.equal(
+ setFullCRLiteFilterResult,
+ Cr.NS_OK,
+ "setFullCRLiteFilter should succeed"
+ );
+
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeEnforcePrefValue
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+
+ // Now "replace" the filter with a more recent one. The revoked certificate should be revoked.
+ setFullCRLiteFilterResult = await new Promise(resolve => {
+ certStorage.setFullCRLiteFilter(
+ filterBytes,
+ new Date("2019-10-28T00:00:00Z").getTime() / 1000,
+ resolve
+ );
+ });
+ Assert.equal(
+ setFullCRLiteFilterResult,
+ Cr.NS_OK,
+ "setFullCRLiteFilter should succeed"
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+
+ // If we're only collecting telemetry, none of the certificates should appear to be revoked.
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeTelemetryOnlyPrefValue
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+
+ // If CRLite is disabled, none of the certificates should appear to be revoked.
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeDisabledPrefValue
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem
new file mode 100644
index 0000000000..d775817b33
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert-issuer.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEoDCCA4igAwIBAgIQBpaPlkroI1bHThfCtTZbADANBgkqhkiG9w0BAQsFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTE3MTEwNjEyMjI1N1oXDTI3MTEwNjEyMjI1N1owXzEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTEeMBwGA1UEAxMVVGhhd3RlIEVWIFJTQSBDQSAyMDE4MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0Cu52zmdJFnSezXMKvL0rso
+WgA/1X7OxjMQHsAllID1eDG836ptJXSTPg+DoEenHfkKyw++wXobgahr0cU/2v8R
+WR3fID53ZDhEGHzS+Ol7V+HRtZG5teMWCY7gldtBQH0r7xUEp/3ISVsZUVBqtUmL
+VJlf9nxJD6Cxp4LBlcJJ8+N6kSkV+fA+WdQc0HYhXSg3PxJP7XSU28Wc7gf6y9kZ
+zQhK4WrZLRrHHbHC2QXdqQYUxR927QV+UCNXnlbTcZy2QpxWTPLzK+/cKXX4cwP6
+MGF7+8RnUgHlij/5V2k/tIF9ep4B72ucqaS/UhEPpIN/T7A3OAw995yrB38glQID
+AQABo4IBSTCCAUUwHQYDVR0OBBYEFOcB/AwWGMp9sozshyejb2GBO4Q5MB8GA1Ud
+IwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9jZCvDMA4GA1UdDwEB/wQEAwIBhjAdBgNV
+HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0
+BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
+LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v
+RGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2MDQwMgYE
+VR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
+MA0GCSqGSIb3DQEBCwUAA4IBAQAWGka+5ffLpfFuzT+WlwDRwhyTZSunnvecZWZT
+PPKXipynjpXx5dK8YG+2XoH74285GR1UABuvHMFV94XeDET9Pzz5s/NHS1/eAr5e
+GdwfBl80XwPkwXaYqzRtw6J4RAxeLqcbibhUQv9Iev9QcP0kNPyJu413Xov76mSu
+JlGThKzcurJPive2eLmwmoIgTPH11N/IIO9nHLVe8KTkt+FGgZCOWHA3kbFBZR39
+Mn2hFS974rhUkM+VS9KbCiQQ5OwkfbZ/6BINkE1CMtiESZ2WkbxJKPsF3dN7p9DF
+YWiQSbYjFP+rCT0/MkaHHYUkEvLNPgyJ6z29eMf0DjLu/SXJ
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem
new file mode 100644
index 0000000000..81e01bd783
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/revoked-cert.pem
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIHOzCCBiOgAwIBAgIQBi31aKBRMQgg1+xDJ+G6/TANBgkqhkiG9w0BAQsFADBf
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMR4wHAYDVQQDExVUaGF3dGUgRVYgUlNBIENBIDIwMTgw
+HhcNMTgwNTI4MDAwMDAwWhcNMjAwNTIxMTIwMDAwWjCB6zEdMBsGA1UEDwwUUHJp
+dmF0ZSBPcmdhbml6YXRpb24xEzARBgsrBgEEAYI3PAIBAxMCREUxFjAUBgsrBgEE
+AYI3PAIBAhMFSGVzc2UxGDAWBgsrBgEEAYI3PAIBAQwHR2llw59lbjERMA8GA1UE
+BRMISFJCIDY5MDIxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIZXNzZW4xFDASBgNV
+BAcTC0hldWNoZWxoZWltMRQwEgYDVQQKEwtTY2h1bmsgR21iSDELMAkGA1UECxMC
+SVQxGTAXBgNVBAMTEHNjaHVuay1ncm91cC5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCvkuQZz2ExPv9paJb622OOk+o4bWnjDe1zHGK6qnK25mMT
+Zldk74sXF+Wfr9lbwqHTcjGhQFwmVDqvtr55KVX8FOv0CSqNaewOrnNrFz8Xg4rn
+OlIs3+MmqD5CIK+el0rA+xltEY8WvNlwZKG7yeJYrdsr+5DAThDuwCVe8bU7it4h
+sjsMsof5ocee9zDkFThNVGR4sMk5EgBxb1Gt4n9wXUj4OBT78whhlkLH/pVZrrhs
+tQwC3q90MOPC5RJcEolSCNjGdHCKRbexmRqJgbJj/qZ9JT+fQ+Ko6a+UAWvc2BUc
+POnzGV2GzCdFFGOubJb6RjU0nuPG4Lmdc/BuS9kFAgMBAAGjggNkMIIDYDAfBgNV
+HSMEGDAWgBTnAfwMFhjKfbKM7Icno29hgTuEOTAdBgNVHQ4EFgQUNb1SY8Bkil98
+tD8zoxE30jBA1NIwZwYDVR0RBGAwXoIQc2NodW5rLWdyb3VwLmNvbYIUd3d3LnNj
+aHVuay1ncm91cC5jb22CG3NjaHVuay1jYXJib250ZWNobm9sb2d5LmNvbYIXc2No
+dW5rLXNpbnRlcm1ldGFscy5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY2Rw
+LnRoYXd0ZS5jb20vVGhhd3RlRVZSU0FDQTIwMTguY3JsMEsGA1UdIAREMEIwNwYJ
+YIZIAYb9bAIBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv
+bS9DUFMwBwYFZ4EMAQEwcQYIKwYBBQUHAQEEZTBjMCQGCCsGAQUFBzABhhhodHRw
+Oi8vc3RhdHVzLnRoYXd0ZS5jb20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9jYWNlcnRz
+LnRoYXd0ZS5jb20vVGhhd3RlRVZSU0FDQTIwMTguY3J0MAkGA1UdEwQCMAAwggF7
+BgorBgEEAdZ5AgQCBIIBawSCAWcBZQB1AKS5CZC0GFgUh7sTosxncAo8NZgE+Rvf
+uON3zQ7IDdwQAAABY6Ze9XAAAAQDAEYwRAIgNUeXL3GwlpGQtTS/wKBlOkHJvHR5
+knSop0OPumeCfQECIEdxY7qr/WRVbWkQFvP48fgWkZHkd4vTq70Y0aaSZTbPAHUA
+VhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFjpl71tQAABAMARjBE
+AiAtxKdc/wum3TE7r9BoRd/gkrjYLWyqeLuL/opRBRy9xwIgPF6uEZxyhEoLZ+9G
+AFBAP+X89zjZphVALjIXu0RRea4AdQC72d+8H4pxtZOUI5eqkntHOFeVCqtS6BqQ
+lmQ2jh7RhQAAAWOmXvY1AAAEAwBGMEQCIHc04ERlUbIkVrlC+I89C9xtugvRCwbR
+a7qZzSdqHltUAiBRVwTacf1dnO9AgLSgrxft5LV32DvH3qNT7pWYh8dFNjANBgkq
+hkiG9w0BAQsFAAOCAQEARJ+tbnM+yS6chgpyzfB3e7IWPq2Den46Ja1H6/4qaKrd
+nsbElcvd4cCQf1zYY6jlQkO6qtfMUChKrEar5aqqnyX8x/8T9PkpHp8XyUxgGlmT
+hrnHML0gDJFS8O4MB5pFnGkgoOQa+OIQokWCXr4/a4AwsTG3Ms+lC+R+vRYz90lg
+TEJLNHB2fSvQyvpXDUL9aAjACBp/9pKxfM9iq06MFO5jP483xJUfdqtVteHMw75w
+1mb8IrM9R1dP47GsblTrf2rZYdaoxdyLjtJQG2aaOdU5unE6QeFrXbz0qeTPePs8
+ftuXSW9xb053HjAkCcVo48j07b2cHfU1hxzGGptVbQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlite b/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlite
new file mode 100644
index 0000000000..34ced4b840
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.crlite
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.stash b/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.stash
new file mode 100644
index 0000000000..1dbea0be47
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/test-filter.stash
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem
new file mode 100644
index 0000000000..705827a85e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert-issuer.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEkDCCA3igAwIBAgIJIrmxUyPlrv3NMA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV
+BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw
+JQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTIwHhcNMTgwODIy
+MDczMjI0WhcNMjgwODIyMDczMjI0WjBQMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
+U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEaMBgGA1UEAxMRQ3Jvc3NUcnVz
+dCBEViBDQTUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnTNi5Kgrt
+FL8qBuEmpL2gvLFY7f9MEgjzClvic/45ebM+DxZ2CMuqtMtImgf8XPIpLaFFbozx
+3VgqH41cmGHbpAoDRKpwfF1f53peHYhRxpOVgcnsiVCPZJPBPCUM9St+cuEjfo0d
+YGbr3aG5urdT2zeKIFyxKbggdkU0LVRHwvLFsIpXCn/YK/8Rmx87yW9VB80OXkzf
+IQoZop83+aebq1VwzjNCN3u4bWSFLYDyJGqE40WlZ53NZh+TwBsa6gld9YXPGQfx
+k8x38zkFXberlMQOYhX9KyuTOMdlFkbx6LfIUqVKJavpcr54+XPzVyeroNPpKxtZ
+mEqUYiFjAqUVAgMBAAGjggFeMIIBWjAdBgNVHQ4EFgQUT4twz6lAHJbllF13rNZv
+TS2b8ncwHwYDVR0jBBgwFoAUCoWpd2UFmHxAgfgPlyw48QrsPM8wEgYDVR0TAQH/
+BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwSQYDVR0fBEIwQDA+oDygOoY4aHR0
+cDovL3JlcG9zaXRvcnkuc2Vjb210cnVzdC5uZXQvU0MtUm9vdDIvU0NSb290MkNS
+TC5jcmwwUgYDVR0gBEswSTBHBgoqgwiMmxtkhwUEMDkwNwYIKwYBBQUHAgEWK2h0
+dHBzOi8vcmVwb3NpdG9yeS5zZWNvbXRydXN0Lm5ldC9TQy1Sb290Mi8wQAYIKwYB
+BQUHAQEENDAyMDAGCCsGAQUFBzABhiRodHRwOi8vc2Nyb290Y2EyLm9jc3Auc2Vj
+b210cnVzdC5uZXQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQAD
+ggEBABEDSrrhhR+Js5q45yih2Ne4cMLZmrH0AZwU3eM+7HZplzi1EhppgvcYk/2k
+LM9haQGWnAZ5wiixLqKu7WlWrHgblZbXyCxALmMBK1rqeP0omxXExqKVqWNHU8KZ
+t3jahH1wDYSzfetM7guWR+PAPpb9oQCtAx8DVyI/3Ocswvti/uWb517Bdo6Nd0+9
+mf0LiphNKcSzSFX0s1Cb47cJROYHGBe2J6NUSWR7wE0asPtKsznGyNO+NJCUR+0h
+OLN2cA2KJwPhZjYJt8UkucAF/EE7qC0Fc8B9Q/gttQ52en5BZxdkDrHCi4qnsSvi
+gueQme/RzYkEaQlNT1WCZ9AIgVE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem
new file mode 100644
index 0000000000..195d2d8ca2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_direct/valid-cert.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF4DCCBMigAwIBAgIQC3d196+a5UJlyc0yVxB3jjANBgkqhkiG9w0BAQsFADBQ
+MQswCQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4s
+TFRELjEaMBgGA1UEAxMRQ3Jvc3NUcnVzdCBEViBDQTUwHhcNMTkwNjExMDUyMjEy
+WhcNMjEwNjMwMTQ1OTU5WjAUMRIwEAYDVQQDEwlza3luZXcuanAwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeciw7C297026HA4oIwc29vL2h29GVrRF7
+HGdeXzAJA7kh+qwo8rTBFfdX7sgHy6nnE1+flEtFt91Ss8i3BZMEqoFUZFb1jGXd
+DbQtmIWxz7O5skkjR1gdKwt9GImy1hEPt8dwU52mwVsSUEKvlZlsjeofUPAEbnYY
++iA/nYaYXiXyCxJzk6Y09VlzghyMIhkLwDa7rL3S9FgUQI6tSUwsiNYNoQzlYgXF
+yPfQfd57LbBwZJtqVPC6rjPOZZd0sw7uvrDNuxnAM2k2mzlML9Vwt8EvSlZX60xD
+oGQCsQQ/ZgjEQTA8WRZ+fxW/LqQNYYm70KU/1M+e8o4MKmA9xkH5AgMBAAGjggLw
+MIIC7DAfBgNVHSMEGDAWgBRPi3DPqUAcluWUXXes1m9NLZvydzA8BggrBgEFBQcB
+AQQwMC4wLAYIKwYBBQUHMAGGIGh0dHA6Ly9kdmNhNS5vY3NwLnNlY29tdHJ1c3Qu
+bmV0MCMGA1UdEQQcMBqCCXNreW5ldy5qcIINd3d3LnNreW5ldy5qcDBaBgNVHSAE
+UzBRMEUGCiqDCIybG26BVQIwNzA1BggrBgEFBQcCARYpaHR0cHM6Ly9yZXBvMS5z
+ZWNvbXRydXN0Lm5ldC9zcHBjYS94dGR2NS8wCAYGZ4EMAQIBMBMGA1UdJQQMMAoG
+CCsGAQUFBwMBMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9yZXBvMS5zZWNvbXRy
+dXN0Lm5ldC9zcHBjYS94dGR2NS9mdWxsY3JsLmNybDAdBgNVHQ4EFgQUuj9305tQ
+JIeVAQtsz9JHx3PTqaQwDgYDVR0PAQH/BAQDAgWgMIIBfgYKKwYBBAHWeQIEAgSC
+AW4EggFqAWgAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWtF
+Bb6pAAAEAwBHMEUCIHUQkmFzUh01r1Px/zWMZSL21dNNQwM+rN1z0gutxV3JAiEA
+jSb2/GAm4+2qiNWDtx1EkHsMXjNW+5S4GhJePexjJR8AdgDuS723dc5guuFCaR+r
+4Z5mow9+X7By2IMAxHuJeqj9ywAAAWtFBcdeAAAEAwBHMEUCIDVHdfP9wnVgz45l
+eX80DpRCRNEV/OCDwfW+B0g/dveYAiEArbpLQb5Z9hul3r00kF2LrivNuI7kwEBy
+MpkYsLtSPJoAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAAAWtF
+BdCQAAAEAwBHMEUCIE2GUo6x3qDrIhacnCmjikBCHF2yT6Fv5GAehZB569YCAiEA
+vXwMXV8+y3xNFys+A6u9EjKiy8CTKv+SQxqsJ4s6jK0wDQYJKoZIhvcNAQELBQAD
+ggEBAAzlm9W+N5fviTJ9wDsc5nXKYur3744V/cm75+8dUM61Rko1isK6IZt5aNPN
+wOfhBsTzHHSYmAFMR9Xjoq8iDYZtIk01IGI6LEWuls9F2hVcERiHMWJOLTiH35xN
+vRNTG0AbBdIpTX2sURsoCPJ+8DTnVUr3pTzXnIY4EQ4UXfANuYwceOHShF6UJo/L
+PK0uRdHcd5SmMa03gFUdkTc9gU6PIEO/UgubazGh9xDBHtHECeleL+gpSfOP3SkF
+7W1RgmbE6WJdVPlto7FRQtl2xIzHs/gNaPezqNKPHgFlx4c+ECTjPLqoW8LdeXu+
+N8dueJg1+h+lQifkmgl23DqEIiI=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting.js b/security/manager/ssl/tests/unit/test_cert_storage_preexisting.js
new file mode 100644
index 0000000000..153fdc56f6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting.js
@@ -0,0 +1,59 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// This file tests that cert_storage correctly persists its "has prior data"
+// information across runs of the browser.
+// (The test DB files for this test were created by running the test
+// `test_cert_storage_broken_db.js` and copying them from that test's profile
+// directory.)
+
+/* eslint-disable no-unused-vars */
+add_task(async function() {
+ let dbDirectory = do_get_profile();
+ dbDirectory.append("security_state");
+ let dbFile = do_get_file("test_cert_storage_preexisting/data.safe.bin");
+ dbFile.copyTo(dbDirectory, "data.safe.bin");
+
+ let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+ let hasPriorRevocationData = await new Promise(resolve => {
+ certStorage.hasPriorData(
+ Ci.nsICertStorage.DATA_TYPE_REVOCATION,
+ (rv, hasPriorData) => {
+ Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
+ resolve(hasPriorData);
+ }
+ );
+ });
+ Assert.equal(
+ hasPriorRevocationData,
+ true,
+ "should have prior revocation data"
+ );
+
+ let hasPriorCertData = await new Promise(resolve => {
+ certStorage.hasPriorData(
+ Ci.nsICertStorage.DATA_TYPE_CERTIFICATE,
+ (rv, hasPriorData) => {
+ Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
+ resolve(hasPriorData);
+ }
+ );
+ });
+ Assert.equal(hasPriorCertData, true, "should have prior cert data");
+
+ let hasPriorCRLiteData = await new Promise(resolve => {
+ certStorage.hasPriorData(
+ Ci.nsICertStorage.DATA_TYPE_CRLITE,
+ (rv, hasPriorData) => {
+ Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
+ resolve(hasPriorData);
+ }
+ );
+ });
+ Assert.equal(hasPriorCRLiteData, true, "should have prior cert data");
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.mdb b/security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.mdb
new file mode 100644
index 0000000000..df4cb182a7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.mdb
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.safe.bin b/security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.safe.bin
new file mode 100644
index 0000000000..011ed93484
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting/data.safe.bin
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting/lock.mdb b/security/manager/ssl/tests/unit/test_cert_storage_preexisting/lock.mdb
new file mode 100644
index 0000000000..dc4b50fdfc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting/lock.mdb
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js
new file mode 100644
index 0000000000..0c6ffbd459
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js
@@ -0,0 +1,71 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// This file tests that cert_storage correctly persists its information across
+// runs of the browser specifically in the case of CRLite.
+// (The test DB files for this test were created by running the test
+// `test_cert_storage_direct.js` and copying them from that test's profile
+// directory.)
+
+/* eslint-disable no-unused-vars */
+add_task(async function() {
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeEnforcePrefValue
+ );
+
+ let dbDirectory = do_get_profile();
+ dbDirectory.append("security_state");
+ let dbFile = do_get_file(
+ "test_cert_storage_preexisting_crlite/data.safe.bin"
+ );
+ dbFile.copyTo(dbDirectory, "data.safe.bin");
+ let crliteFile = do_get_file(
+ "test_cert_storage_preexisting_crlite/crlite.filter"
+ );
+ crliteFile.copyTo(dbDirectory, "crlite.filter");
+
+ let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let validCertIssuer = constructCertFromFile(
+ "test_cert_storage_direct/valid-cert-issuer.pem"
+ );
+ let validCert = constructCertFromFile(
+ "test_cert_storage_direct/valid-cert.pem"
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2019-10-28T00:00:00Z").getTime() / 1000,
+ false,
+ "skynew.jp",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+
+ let revokedCertIssuer = constructCertFromFile(
+ "test_cert_storage_direct/revoked-cert-issuer.pem"
+ );
+ let revokedCert = constructCertFromFile(
+ "test_cert_storage_direct/revoked-cert.pem"
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2019-11-04T00:00:00Z").getTime() / 1000,
+ false,
+ "schunk-group.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/crlite.filter b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/crlite.filter
new file mode 100644
index 0000000000..34ced4b840
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/crlite.filter
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/data.safe.bin b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/data.safe.bin
new file mode 100644
index 0000000000..d96571f128
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/data.safe.bin
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_cert_storage_prefs.js b/security/manager/ssl/tests/unit/test_cert_storage_prefs.js
new file mode 100644
index 0000000000..ca7e06ece9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_storage_prefs.js
@@ -0,0 +1,34 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// Tests that cert_storage properly handles its preference values.
+
+function run_test() {
+ let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+ // Since none of our prefs start with values, looking them up will fail. cert_storage should use
+ // safe fallbacks.
+ ok(
+ !certStorage.isBlocklistFresh(),
+ "checking blocklist freshness shouldn't crash"
+ );
+
+ // If we set nonsensical values, cert_storage should still use safe fallbacks.
+ Services.prefs.setIntPref("services.blocklist.onecrl.checked", -2);
+ Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds", -7);
+ ok(
+ !certStorage.isBlocklistFresh(),
+ "checking blocklist freshness still shouldn't crash"
+ );
+
+ // Clearing prefs shouldn't cause failures.
+ Services.prefs.clearUserPref("services.blocklist.onecrl.checked");
+ ok(
+ !certStorage.isBlocklistFresh(),
+ "checking blocklist freshness again shouldn't crash"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_trust.js b/security/manager/ssl/tests/unit/test_cert_trust.js
new file mode 100644
index 0000000000..43fbaa6174
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust.js
@@ -0,0 +1,324 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function load_cert(cert_name, trust_string) {
+ let cert_filename = cert_name + ".pem";
+ return addCertFromFile(
+ certdb,
+ "test_cert_trust/" + cert_filename,
+ trust_string
+ );
+}
+
+function setup_basic_trusts(ca_cert, int_cert) {
+ certdb.setCertTrust(
+ ca_cert,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_SSL | Ci.nsIX509CertDB.TRUSTED_EMAIL
+ );
+
+ certdb.setCertTrust(int_cert, Ci.nsIX509Cert.CA_CERT, 0);
+}
+
+async function test_ca_distrust(ee_cert, cert_to_modify_trust, isRootCA) {
+ // On reset most usages are successful
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_CA_CERT_INVALID,
+ certificateUsageSSLCA
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+
+ // Test of active distrust. No usage should pass.
+ setCertTrust(cert_to_modify_trust, "p,p,p");
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageSSLServer
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_CA_CERT_INVALID,
+ certificateUsageSSLCA
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageEmailRecipient
+ );
+
+ // Trust set to T - trusted CA to issue client certs, where client cert is
+ // usageSSLClient.
+ setCertTrust(cert_to_modify_trust, "T,T,T");
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+
+ // XXX(Bug 982340)
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_CA_CERT_INVALID,
+ certificateUsageSSLCA
+ );
+
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+
+ // Now tests on the SSL trust bit
+ setCertTrust(cert_to_modify_trust, "p,C,C");
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageSSLServer
+ );
+
+ // XXX(Bug 982340)
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_CA_CERT_INVALID,
+ certificateUsageSSLCA
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+
+ // Inherited trust SSL
+ setCertTrust(cert_to_modify_trust, ",C,C");
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ // XXX(Bug 982340)
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_CA_CERT_INVALID,
+ certificateUsageSSLCA
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+
+ // Now tests on the EMAIL trust bit
+ setCertTrust(cert_to_modify_trust, "C,p,C");
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_CA_CERT_INVALID,
+ certificateUsageSSLCA
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_UNTRUSTED_ISSUER,
+ certificateUsageEmailRecipient
+ );
+
+ // inherited EMAIL Trust
+ setCertTrust(cert_to_modify_trust, "C,,C");
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ SEC_ERROR_CA_CERT_INVALID,
+ certificateUsageSSLCA
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ isRootCA ? SEC_ERROR_UNKNOWN_ISSUER : PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+}
+
+add_task(async function() {
+ let certList = ["ca", "int", "ee"];
+ let loadedCerts = [];
+ for (let certName of certList) {
+ loadedCerts.push(load_cert(certName, ",,"));
+ }
+
+ let ca_cert = loadedCerts[0];
+ notEqual(ca_cert, null, "CA cert should have successfully loaded");
+ let int_cert = loadedCerts[1];
+ notEqual(int_cert, null, "Intermediate cert should have successfully loaded");
+ let ee_cert = loadedCerts[2];
+ notEqual(ee_cert, null, "EE cert should have successfully loaded");
+
+ setup_basic_trusts(ca_cert, int_cert);
+ await test_ca_distrust(ee_cert, ca_cert, true);
+
+ setup_basic_trusts(ca_cert, int_cert);
+ await test_ca_distrust(ee_cert, int_cert, false);
+
+ // Reset trust to default ("inherit trust")
+ setCertTrust(ca_cert, ",,");
+ setCertTrust(int_cert, ",,");
+
+ // End-entities can be trust anchors for interoperability with users who
+ // prefer not to build a hierarchy and instead directly trust a particular
+ // server certificate.
+ setCertTrust(ee_cert, "CTu,CTu,CTu");
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLClient
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailSigner
+ );
+ await checkCertErrorGeneric(
+ certdb,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageEmailRecipient
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_trust/ca.pem b/security/manager/ssl/tests/unit/test_cert_trust/ca.pem
new file mode 100644
index 0000000000..45de962523
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUVSmns5EZzUHKobrhxdpy/ZtJMucwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYD
+VR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQATPLfx55bklmCWgjlOjZjD8EpY
+fenI0HAkE0044+KmVlFSpsETog4OK0aZKYKwDysClM/ZipzBpmcZ4ZczM0TbtC7/
+d8nfUmT5R/sXBuMmeNIioAn6J5JaSctKQbsVta9GwIyJUBuYIvccL1c5wUuxV8xf
+CaTrFgKWkDWkhOEY+TN/95l+Cm0Gwc3l+jom5UrPRyw9Cb5AhRiRc+x3HhgJ2hGe
+kKo4K/AfH/jE7U0u4VM+VZTQf5K92aih3f54vPW+yFfMkgFgzHtwIrj1WStknR7u
+yRjBCHfjuoOgQLm1svKtpgnMbRhucCkW+mifY+ZbE51vYMiRlK3sHj9hz3ol
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_trust/ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_trust/ca.pem.certspec
new file mode 100644
index 0000000000..d809dbd635
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust/ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_cert_trust/ee.pem b/security/manager/ssl/tests/unit/test_cert_trust/ee.pem
new file mode 100644
index 0000000000..37db649572
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust/ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4jCCAcqgAwIBAgIUKXfW0gwCmh3GP4O2Z+Kd/CYbINgwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMA0xCzAJBgNVBAMMAmVlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
+BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
+a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
+uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
+9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
+mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozUwMzAxBgNVHSUEKjAoBggrBgEF
+BQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMEBggrBgEFBQcDAzANBgkqhkiG9w0BAQsF
+AAOCAQEArrxwO26t+coTY3AKAoLTFj8Syt469PatjuQA911/QB64+YYrIEs4KTCZ
+HHx4/WvWAttXNtYV7NYiLXJOTfJUYKr9MdoM6S4C9HHh3oBzkKvNyg7hM6RKr1va
+qd/OVJMulsJ1oP2x2tHeK3k0GkG70yplUxFiaQZ4KVCHsfUo5GPergooQoM64jXC
+ILBF/YJ3vtox2AiNCAA8Vv742CBavsq3bOVN2VxZ/B8yBd5D366UBP39TSRdWHH5
+X3dJWkJ1vkJMmWSbP03zNbqG4H0Qimq5Pywr4uUkmckx6ZclEoGbxozutdMx8u9B
+erZH6etD3bj9ExPVUcf4qHfcJUrmdQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_trust/ee.pem.certspec b/security/manager/ssl/tests/unit/test_cert_trust/ee.pem.certspec
new file mode 100644
index 0000000000..9666c18062
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust/ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:ee
+extension:extKeyUsage:serverAuth,clientAuth,emailProtection,codeSigning
diff --git a/security/manager/ssl/tests/unit/test_cert_trust/int.pem b/security/manager/ssl/tests/unit/test_cert_trust/int.pem
new file mode 100644
index 0000000000..44e6a36efa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust/int.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyjCCAbKgAwIBAgIUNbjVwdBjmiBHgijAyjyGIHuhJqQwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDjEMMAoGA1UEAwwDaW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
+BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
+a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
+uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
+9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
+mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsG
+A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAO35vcl2OA9fAarEgTCrS42CL
+5BeNfQLO7QCIdMZBfcIrQ6v5yw7i07pYGptagz/76OwYOsKO1lqb7sr2nTCNovVp
+Co4dCAGQI460C6x3BOG7qyCFgZvf17xXl5filhMZw93LkWaeEIp8JmOTb511i1Bv
+1akdFJ+xW40AuHE7EswLXzN0eu+tVAMJcsuxibK3jTkEdIjFCVwMD/7LC1rcoKcg
+V18MHbh0zcDrXzo/jk8JfjqkOH5eylBtX2DH/9BjFcKzZDvZQFYlhsmADRHOmqvi
+YoEXJj2A6fRzONsNKtQQbmVGvW/rHP7/YIiaAvn0kNb/FDtSqmTcLazSYpRewQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_trust/int.pem.certspec b/security/manager/ssl/tests/unit/test_cert_trust/int.pem.certspec
new file mode 100644
index 0000000000..a7f6d81419
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust/int.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_cert_trust/moz.build b/security/manager/ssl/tests/unit/test_cert_trust/moz.build
new file mode 100644
index 0000000000..8338b13334
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_trust/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'ee.pem',
+# 'int.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_utf8.js b/security/manager/ssl/tests/unit/test_cert_utf8.js
new file mode 100644
index 0000000000..caeddd8158
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_utf8.js
@@ -0,0 +1,79 @@
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// 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/.
+
+"use strict";
+
+do_get_profile();
+
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function run_test() {
+ // This certificate has a number of placeholder byte sequences that we can
+ // replace with invalid UTF-8 to ensure that we handle these cases safely.
+ let certificateToAlterFile = do_get_file(
+ "test_cert_utf8/certificateToAlter.pem",
+ false
+ );
+ let certificateBytesToAlter = atob(
+ pemToBase64(readFile(certificateToAlterFile))
+ );
+ testUTF8InField("issuerName", "ISSUER CN", certificateBytesToAlter);
+ testUTF8InField("issuerOrganization", "ISSUER O", certificateBytesToAlter);
+ testUTF8InField(
+ "issuerOrganizationUnit",
+ "ISSUER OU",
+ certificateBytesToAlter
+ );
+ testUTF8InField("issuerCommonName", "ISSUER CN", certificateBytesToAlter);
+ testUTF8InField("organization", "SUBJECT O", certificateBytesToAlter);
+ testUTF8InField("organizationalUnit", "SUBJECT OU", certificateBytesToAlter);
+ testUTF8InField("subjectName", "SUBJECT CN", certificateBytesToAlter);
+ testUTF8InField("displayName", "SUBJECT CN", certificateBytesToAlter);
+ testUTF8InField("commonName", "SUBJECT CN", certificateBytesToAlter);
+ testUTF8InField(
+ "emailAddress",
+ "SUBJECT EMAILADDRESS",
+ certificateBytesToAlter
+ );
+}
+
+// Every (issuer, serial number) pair must be unique. If NSS ever encounters two
+// different (in terms of encoding) certificates with the same values for this
+// pair, it will refuse to import it (even as a temporary certificate). Since
+// we're creating a number of different certificates, we need to ensure this
+// pair is always unique. The easiest way to do this is to change the issuer
+// distinguished name each time. To make sure this doesn't introduce additional
+// UTF8 issues, always use a printable ASCII value.
+var gUniqueIssuerCounter = 32;
+
+function testUTF8InField(field, replacementPrefix, certificateBytesToAlter) {
+ let toReplace = `${replacementPrefix} REPLACE ME`;
+ let replacement = "";
+ for (let i = 0; i < toReplace.length; i++) {
+ replacement += "\xEB";
+ }
+ let bytes = certificateBytesToAlter.replace(toReplace, replacement);
+ let uniqueIssuerReplacement =
+ "ALWAYS MAKE ME UNIQU" + String.fromCharCode(gUniqueIssuerCounter);
+ bytes = bytes.replace("ALWAYS MAKE ME UNIQUE", uniqueIssuerReplacement);
+ ok(
+ gUniqueIssuerCounter < 127,
+ "should have enough ASCII replacements to make a unique issuer DN"
+ );
+ gUniqueIssuerCounter++;
+ let cert = gCertDB.constructX509(stringToArray(bytes));
+ notEqual(cert[field], null, `accessing nsIX509Cert.${field} shouldn't fail`);
+ notEqual(
+ cert.getEmailAddresses(),
+ null,
+ "calling nsIX509Cert.getEmailAddresses() shouldn't assert"
+ );
+ ok(
+ !cert.containsEmailAddress("test@test.test"),
+ "calling nsIX509Cert.containsEmailAddress() shouldn't assert"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem b/security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem
new file mode 100644
index 0000000000..63ce6adb95
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID7zCCAtegAwIBAgIUD0km5IZjROSFHzbVwqApAdud/iAwDQYJKoZIhvcNAQEL
+BQAwfDEcMBoGA1UECgwTSVNTVUVSIE8gUkVQTEFDRSBNRTEdMBsGA1UECwwUSVNT
+VUVSIE9VIFJFUExBQ0UgTUUxHTAbBgNVBAMMFElTU1VFUiBDTiBSRVBMQUNFIE1F
+MR4wHAYDVQQHDBVBTFdBWVMgTUFLRSBNRSBVTklRVUUwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowgY8xHTAbBgNVBAoMFFNVQkpFQ1QgTyBSRVBM
+QUNFIE1FMR4wHAYDVQQLDBVTVUJKRUNUIE9VIFJFUExBQ0UgTUUxHjAcBgNVBAMM
+FVNVQkpFQ1QgQ04gUkVQTEFDRSBNRTEuMCwGCSqGSIb3DQEJARYfU1VCSkVDVCBF
+TUFJTEFERFJFU1MgUkVQTEFDRSBNRTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNRME8wTQYDVR0RBEYwRIIeU1VC
+SkVDVCBBTFQgRE5TTkFNRSBSRVBMQUNFIE1FgSJTVUJKRUNUIEFMVCBSRkM4MjJA
+TkFNRSBSRVBMQUNFIE1FMA0GCSqGSIb3DQEBCwUAA4IBAQA1ZJfPm9pINBPkJ/hu
+5we0GtnKeTaIQOvtVnOXIA8NQBDny3pAaGNEQTaf48PL7RViFmUNGubPVzx27ZC4
+HIsUV+fT6ASdkgj9HU3cV7vOIXTDWiZOm8pFoqwV9sbkFToUuEomIHAJ5qXnF7Fv
+3O87ePTPf1s4bf8URK3Dn3cLY5N9P0o2wdUKj5Ey94ZMfGnqH7lZ0PSoDTRdzb8j
+lHp1bskW/s9jsYqF7zSExur2OZ3DBwQptmoiK6jYu3h5M6+lV/euRjsJLqEIBSu9
+krgKiuGeiG2F8aR+Vi8agulxZnm3zmZPCgIxHOe2O85+Ak1GctL6mRopr4nYgc+P
+dfBK
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem.certspec b/security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem.certspec
new file mode 100644
index 0000000000..6579ac5550
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_utf8/certificateToAlter.pem.certspec
@@ -0,0 +1,3 @@
+issuer:/O=ISSUER O REPLACE ME/OU=ISSUER OU REPLACE ME/CN=ISSUER CN REPLACE ME/L=ALWAYS MAKE ME UNIQUE
+subject:/O=SUBJECT O REPLACE ME/OU=SUBJECT OU REPLACE ME/CN=SUBJECT CN REPLACE ME/emailAddress=SUBJECT EMAILADDRESS REPLACE ME
+extension:subjectAlternativeName:SUBJECT ALT DNSNAME REPLACE ME,SUBJECT ALT RFC822@NAME REPLACE ME
diff --git a/security/manager/ssl/tests/unit/test_cert_utf8/moz.build b/security/manager/ssl/tests/unit/test_cert_utf8/moz.build
new file mode 100644
index 0000000000..f96c97f0e6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_utf8/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'certificateToAlter.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_version.js b/security/manager/ssl/tests/unit/test_cert_version.js
new file mode 100644
index 0000000000..73b0f458f7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version.js
@@ -0,0 +1,304 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests the interaction between the basic constraints extension and the
+// certificate version field. In general, the testcases consist of verifying
+// certificate chains of the form:
+//
+// end-entity (issued by) intermediate (issued by) trusted X509v3 root
+//
+// where the intermediate is one of X509 v1, v2, v3, or v4, and either does or
+// does not have the basic constraints extension. If it has the extension, it
+// either does or does not specify that it is a CA.
+//
+// To test cases where the trust anchor has a different version and/or does or
+// does not have the basic constraint extension, there are testcases where the
+// intermediate is trusted as an anchor and the verification is repeated.
+// (Loading a certificate with trust "CTu,," means that it is a trust anchor
+// for SSL. Loading a certificate with trust ",," means that it inherits its
+// trust.)
+//
+// There are also testcases for end-entities issued by a trusted X509v3 root
+// where the end-entities similarly cover the range of versions and basic
+// constraint extensions.
+//
+// Finally, there are testcases for self-signed certificates that, again, cover
+// the range of versions and basic constraint extensions.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function certFromFile(certName) {
+ return constructCertFromFile("test_cert_version/" + certName + ".pem");
+}
+
+function loadCertWithTrust(certName, trustString) {
+ addCertFromFile(
+ certdb,
+ "test_cert_version/" + certName + ".pem",
+ trustString
+ );
+}
+
+function checkEndEntity(cert, expectedResult) {
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ expectedResult,
+ certificateUsageSSLServer
+ );
+}
+
+function checkIntermediate(cert, expectedResult) {
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ expectedResult,
+ certificateUsageSSLCA
+ );
+}
+
+add_task(async function() {
+ loadCertWithTrust("ca", "CTu,,");
+
+ // Section for CAs lacking the basicConstraints extension entirely:
+ loadCertWithTrust("int-v1-noBC_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v1-noBC_ca"),
+ MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v1-noBC"),
+ MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA
+ );
+ // A v1 certificate with no basicConstraints extension may issue certificates
+ // if it is a trust anchor.
+ loadCertWithTrust("int-v1-noBC_ca", "CTu,,");
+ await checkIntermediate(certFromFile("int-v1-noBC_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v1-noBC"), PRErrorCodeSuccess);
+
+ loadCertWithTrust("int-v2-noBC_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v2-noBC_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v2-noBC"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ loadCertWithTrust("int-v2-noBC_ca", "CTu,,");
+ await checkIntermediate(
+ certFromFile("int-v2-noBC_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v2-noBC"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ loadCertWithTrust("int-v3-noBC_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v3-noBC_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v3-noBC"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ loadCertWithTrust("int-v3-noBC_ca", "CTu,,");
+ await checkIntermediate(
+ certFromFile("int-v3-noBC_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v3-noBC"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ loadCertWithTrust("int-v4-noBC_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v4-noBC_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v4-noBC"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ loadCertWithTrust("int-v4-noBC_ca", "CTu,,");
+ await checkIntermediate(
+ certFromFile("int-v4-noBC_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v4-noBC"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ // Section for CAs with basicConstraints not specifying cA:
+ loadCertWithTrust("int-v1-BC-not-cA_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v1-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v1-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ loadCertWithTrust("int-v1-BC-not-cA_ca", "CTu,,");
+ await checkIntermediate(
+ certFromFile("int-v1-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v1-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ loadCertWithTrust("int-v2-BC-not-cA_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v2-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v2-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ loadCertWithTrust("int-v2-BC-not-cA_ca", "CTu,,");
+ await checkIntermediate(
+ certFromFile("int-v2-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v2-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ loadCertWithTrust("int-v3-BC-not-cA_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v3-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v3-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ loadCertWithTrust("int-v3-BC-not-cA_ca", "CTu,,");
+ await checkIntermediate(
+ certFromFile("int-v3-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v3-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ loadCertWithTrust("int-v4-BC-not-cA_ca", ",,");
+ await checkIntermediate(
+ certFromFile("int-v4-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v4-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ loadCertWithTrust("int-v4-BC-not-cA_ca", "CTu,,");
+ await checkIntermediate(
+ certFromFile("int-v4-BC-not-cA_ca"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+ await checkEndEntity(
+ certFromFile("ee_int-v4-BC-not-cA"),
+ SEC_ERROR_CA_CERT_INVALID
+ );
+
+ // Section for CAs with basicConstraints specifying cA:
+ loadCertWithTrust("int-v1-BC-cA_ca", ",,");
+ await checkIntermediate(certFromFile("int-v1-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v1-BC-cA"), PRErrorCodeSuccess);
+ loadCertWithTrust("int-v1-BC-cA_ca", "CTu,,");
+ await checkIntermediate(certFromFile("int-v1-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v1-BC-cA"), PRErrorCodeSuccess);
+
+ loadCertWithTrust("int-v2-BC-cA_ca", ",,");
+ await checkIntermediate(certFromFile("int-v2-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v2-BC-cA"), PRErrorCodeSuccess);
+ loadCertWithTrust("int-v2-BC-cA_ca", "CTu,,");
+ await checkIntermediate(certFromFile("int-v2-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v2-BC-cA"), PRErrorCodeSuccess);
+
+ loadCertWithTrust("int-v3-BC-cA_ca", ",,");
+ await checkIntermediate(certFromFile("int-v3-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v3-BC-cA"), PRErrorCodeSuccess);
+ loadCertWithTrust("int-v3-BC-cA_ca", "CTu,,");
+ await checkIntermediate(certFromFile("int-v3-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v3-BC-cA"), PRErrorCodeSuccess);
+
+ loadCertWithTrust("int-v4-BC-cA_ca", ",,");
+ await checkIntermediate(certFromFile("int-v4-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v4-BC-cA"), PRErrorCodeSuccess);
+ loadCertWithTrust("int-v4-BC-cA_ca", "CTu,,");
+ await checkIntermediate(certFromFile("int-v4-BC-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee_int-v4-BC-cA"), PRErrorCodeSuccess);
+
+ // Section for end-entity certificates with various basicConstraints:
+ await checkEndEntity(certFromFile("ee-v1-noBC_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-v2-noBC_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-v3-noBC_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-v4-noBC_ca"), PRErrorCodeSuccess);
+
+ await checkEndEntity(certFromFile("ee-v1-BC-not-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-v2-BC-not-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-v3-BC-not-cA_ca"), PRErrorCodeSuccess);
+ await checkEndEntity(certFromFile("ee-v4-BC-not-cA_ca"), PRErrorCodeSuccess);
+
+ await checkEndEntity(
+ certFromFile("ee-v1-BC-cA_ca"),
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
+ );
+ await checkEndEntity(
+ certFromFile("ee-v2-BC-cA_ca"),
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
+ );
+ await checkEndEntity(
+ certFromFile("ee-v3-BC-cA_ca"),
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
+ );
+ await checkEndEntity(
+ certFromFile("ee-v4-BC-cA_ca"),
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
+ );
+
+ // Section for self-signed certificates:
+ await checkEndEntity(certFromFile("ss-v1-noBC"), SEC_ERROR_UNKNOWN_ISSUER);
+ await checkEndEntity(certFromFile("ss-v2-noBC"), SEC_ERROR_UNKNOWN_ISSUER);
+ await checkEndEntity(certFromFile("ss-v3-noBC"), SEC_ERROR_UNKNOWN_ISSUER);
+ await checkEndEntity(certFromFile("ss-v4-noBC"), SEC_ERROR_UNKNOWN_ISSUER);
+
+ await checkEndEntity(
+ certFromFile("ss-v1-BC-not-cA"),
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ await checkEndEntity(
+ certFromFile("ss-v2-BC-not-cA"),
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ await checkEndEntity(
+ certFromFile("ss-v3-BC-not-cA"),
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ await checkEndEntity(
+ certFromFile("ss-v4-BC-not-cA"),
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+
+ await checkEndEntity(certFromFile("ss-v1-BC-cA"), SEC_ERROR_UNKNOWN_ISSUER);
+ await checkEndEntity(certFromFile("ss-v2-BC-cA"), SEC_ERROR_UNKNOWN_ISSUER);
+ await checkEndEntity(certFromFile("ss-v3-BC-cA"), SEC_ERROR_UNKNOWN_ISSUER);
+ await checkEndEntity(certFromFile("ss-v4-BC-cA"), SEC_ERROR_UNKNOWN_ISSUER);
+});
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ca.pem
new file mode 100644
index 0000000000..1126badb9e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUX0wQbjf3LQAHIgB14ftnKbI1mekwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAsGA1UdDwQEAwIBBjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA3q3li09bMd9Y57E2xPY9NdMiG
+SA7DSsGnxOchtT06MfkP6lH4PlKDLATIEv2CSq2z25Thq6yYWyZnWXC+tOptACjQ
+FDS5scNAI2KDwtK7pqx1+Tr606DQI9QUF1ZeaKo7DP1ibmwmDu3cY16RaowbgaCh
+bU4ADBkZNAlRCvrRGCtBkdUl0hqVT6sW/eFkOK9vA5pDHZryQv/DZSg2bDeJIQBL
+ElArPKv3DhETpuf580rkI84+w7HBiUSNHB2KPq7ctn4Zvsou/zxJ9x2px/7t+pLx
+TPoYXl9QOgv2uqKqMr5dWMAgZXrFqvXVC01JHPbu4BJDKi5qyKcdZNcdl5pq
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ca.pem.certspec
new file mode 100644
index 0000000000..8689ef9ea1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem
new file mode 100644
index 0000000000..b39391ce2c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwDCCAagCFEmH4RSzGlM8deH3Cyvvc8BTUkK4MA0GCSqGSIb3DQEBCwUAMA0x
+CzAJBgNVBAMMAmNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBa
+MBYxFDASBgNVBAMMC2VlLXYxLUJDLWNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoxAwDjAMBgNVHRMEBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4IBAQCe6J0atonkAikXwwKyUEier2uTl/M5H1JoJKQT
+8FDYIgDKzZi2I0mt1OBd98r+SxCFEoF/r0Je1vwTzFnK9R460VdZY7kXzPOrQ3nP
+3XCXS/sOK+c6BoWF1NsFBzVohgatObe5zMgV9s5xYZqXlmPFDBJicNyO2DkyWHbk
+7sLMavGanKa1au1Zpn8GoAattNJW9ihqEZ1ZsXM8zgYzuRN850myynAYcIl9rsON
+6mMFe1d1UiEplhAJGGD91A8RtokJlq32uGMWIw76lNbrCEjN6oB6XFS57PcOF+3b
+5rVmgfY7IHbB2gjFSKcj8hFF8D0ISZn4mFRsJof62RwM8QCV
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..4570e6e3ff
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v1-BC-cA
+version:1
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..f099356266
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwTCCAakCFDvg2eVC0vnuGk65tm8Vzurs8Uj1MA0GCSqGSIb3DQEBCwUAMA0x
+CzAJBgNVBAMMAmNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBa
+MBoxGDAWBgNVBAMMD2VlLXYxLUJDLW5vdC1jQTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMNMAswCQYDVR0TBAIw
+ADANBgkqhkiG9w0BAQsFAAOCAQEAUnEgkyfbh+pY2ZAIpqUSRkMYv+NIXdcnTJZV
+GmqhFJEakZ2hMBxELneaxbv49Lj7JyK09CVfBhWFEA0CjnmS+APb3rVozt9uN6yE
+KCjGsmdUnthMG6E2D0VbLsNKq57m3naDVgDDedOhheAvsJyQvKubsQiMtuoJCdZO
+obx+lYE+16m6+SzJPFt1yh5vn9+hBcTq1zmYQSKbAJXkrDMMQnjywzb77kBKYgyr
+uQ3/SlwjzGojQFlWOhkRsQjcC+zz6lobziUeAQ1vXy8t8xaQQjTmwpVi+vVH7w+8
+ATJKEfIE4cl58aAEYHV7qv1fQ6NUQO0lsRumsgoR+E8PhPQKrQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..f4257841fe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v1-BC-not-cA
+version:1
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem
new file mode 100644
index 0000000000..08fce562e7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrTCCAZUCFEmbMXhUIOtpbMU4ZOUHBaPfFSv9MA0GCSqGSIb3DQEBCwUAMA0x
+CzAJBgNVBAMMAmNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBa
+MBUxEzARBgNVBAMMCmVlLXYxLW5vQkMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9
+sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5
+TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7
+xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHd
+tMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l
+8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQELBQADggEB
+AJkjYBJWNRU/lCHANJUmnuRt7ignqJyGsYR/q5z31B6kWUA1TGX15iyWnmgtRqR3
+jDiZqh3ejnozXtbAHkGykndeY3jvHCMzCNNpAQZKd9hoCb9Ix9yK1zbqtGAuDgT6
+t0nfkvGK9qPulRZ/2cHHHEdryceX2B1aVxv8D2z4M1Fg2Ajplp269EFhd0T5sZL3
+9zlgv9JlcoUVdjlDQ95Kagv4jt0gG3mGci/Oj3pYwhe8bVUE3/vwetR+1akOaBcE
+3YSvUW2EbQkGq5ghhIolF2K5DRGjsXkGrJQszRNRPTiEaexL3CV9r7lW0Re6z8Fs
+c2TT9q6PeqCvNoM0K4ji8Ag=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..48fe9e5416
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v1-noBC_ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-v1-noBC
+version:1
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem
new file mode 100644
index 0000000000..25c6102473
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxTCCAa2gAwIBAQIUTUkREc279H3wFGALzwJDTUzA5XYwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLZWUtdjItQkMtY0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAOMAwGA1UdEwQF
+MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAG0viSnuppjnIHdiuApCFM55eBf71+Tc
+g68DIa0yYtVylF6KAhFMCe7JPg+9viImNGeUktRSpwLtCc8w63gILGF8HhAcOScM
+2uMCQMX0HqQqDxZdl1PmQv7oj0pPK4FWiyEXjYaHOaHO+qzo5hV3KUe2iaK0VHbk
+vtyAhTrhkHrhLdpDGYtGErAj5E4hySfNW8tNdvrOge9n2SEshDiSr1G3NSQZwlGd
+ZOPrOGbDNFGlqzKRwIdn8wz1NC8JUjMWZRI7opnqA59e6RXoosT6Ruycnx6qC6eu
+gBKRYMtbVtYmaxHmTR7jKZLdmx+siIkt6nZZJ36sAiQvAd7pWdZrctU=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..f714725d2b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v2-BC-cA
+version:2
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..c100e8d026
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAQIUZUYdrYFQ0vpgwxOwG9WoaOfZlCQwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGjEYMBYGA1UEAwwPZWUtdjItQkMtbm90LWNBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABow0wCzAJBgNV
+HRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCcsL4DBcZ9blKekKo3s0mhsNm056g4
+/QPMHfZ3V3Wtidz2VDp09LTuXaLDbfMYSlsR6p828rJmHrO1BhIvKKJ7SAfN3QXp
+lntK2RzY0G53huTqs8u34DdHdubrG8TSFbq/pqa/2So1YfvVLMQYrkz1p9AxtFqN
+/Ym1G33+1RIB5Shji8I8PyzKVL1ymlzfVLPP4jBkxopytZUykc6wPNRZl1FnbZV4
+0+ZFB01fOHmitHiLuPjIm5YYA48jArq6h589PUg3+WyNccd9MzRQgVdXNg5j88d5
+Ta2oXKAPmzTt0L+vkqkPyTYFT1gNAKg4eD6PdjxXtWDYwNtOqG827jfm
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..db72288814
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v2-BC-not-cA
+version:2
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem
new file mode 100644
index 0000000000..74085ee898
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICsjCCAZqgAwIBAQIUY4fMtZjVWcwhTGK7pGcExmXLBzAwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFTETMBEGA1UEAwwKZWUtdjItbm9CQzCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0BAQsF
+AAOCAQEAG+pHmRMmLdRIWHbG+CiU4jBFaTfjRHVOjt2diO4p60dZ5up2tp2KAnHy
+U7OJmN0FcTl+7ydDWXyR3gRrR/rOGdx5inq3817+CotMkeai3dnKVjjLxDBCA8Ch
+2mqDXR6wYyXccIMokJgVam2b/OHAfpWlphecReKr7kYA8j6Kg52IFZl7iX0l2tL6
+34TJnRdPyGz0AM5uP46pLn7P1zi1M/6+l0y8EnX7KL/k2Bxj2BWwWLJ8L7IXIret
+f/zEM4EiHJHSC+HLq9r/XJFJGVXS6bNGPETQ96ySEvNPPQTRWLe8JBj5pUpe5LBj
+hrv7fb16fTx/eVdVFmbmdFH6q9nNHA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..cc304ab87e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v2-noBC_ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-v2-noBC
+version:2
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem
new file mode 100644
index 0000000000..480bed0f40
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxTCCAa2gAwIBAgIUGxHIJJyv/iJrELEMIQD1aphjDaowDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLZWUtdjMtQkMtY0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAOMAwGA1UdEwQF
+MAMBAf8wDQYJKoZIhvcNAQELBQADggEBABwWiSkeHa9tiZRe2vCYFRgR9OEHPr+z
+uN1PFpmLbofm4HTQ+FYD91QrYuDJPoO6+zBLRbZqkBZ8J3X6ts1uRr7qk6x9Cgq5
+ZJNUre5gW8wjZ/WiX2IoY7ucZXuTYKePPyd5RyfPYm+9p9960elo2uNLLG0Nmy1A
+YlqjkhaFn6q2eNh/4c0tlRKIKpiUDnBa/ibsJPRzbTAhmIm6ooiphX6E6CG4sIWP
+HhWvldTPETvF8PGe4BxqZRBbAzh892yh8FCGos8RLNOoFrr4k4Vysb94n4E1SKrv
+ORfes12d6FmFdYlB4mjl50FJGh0hjPcP1tq78EbkipRGTJO/6Q3vcVE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..6f69c35743
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v3-BC-cA
+version:3
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..574ef68d5f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAgIUb4dXJa+wzvKsmCT/HB8Xu0Cp8YcwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGjEYMBYGA1UEAwwPZWUtdjMtQkMtbm90LWNBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABow0wCzAJBgNV
+HRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQB0pTw1CI81i2uK8HfcfuljDfI1/thC
+9QmV+nRLMic+82XNPHYjUBAS9r2kZvNEeSoQQThkefEzcRPP33hl4TbYXCKuFzpT
+k0ssjzLAUIWUCaVWnV3p0aryGv/MBITNu4TWTnRwOIinWZ/cExO42iLTDiMbuhQp
+b5IubLsj2+15aF56zuTFEqwB+O6D0L+r5uka7Q6I3T3kCrbDFqcjlosKxY67AqLG
+dKMahrfX2D1hFIeVz8yMtYCAH0ZWt+ooJE1ntUksbwsQ2pA7AQwKl2ojiqI6FMRm
+6jD0ydcYAGdNboNkL9fCC1oC7J66slt5a172GE8IGXVKqNZYLv2+0P+/
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..4a9de06358
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v3-BC-not-cA
+version:3
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem
new file mode 100644
index 0000000000..88b8996d5b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICsjCCAZqgAwIBAgIUH6a1yPknNm4Zd5xhXtDsj5JWTVIwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFTETMBEGA1UEAwwKZWUtdjMtbm9CQzCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0BAQsF
+AAOCAQEADZVMW87EF24I8XiCc+t0MG+vu8rcR1msyWAwtjxmYNbkt2Up+etwR0U/
+54zFmJiRagPnKZhd9pyp1rpej9nj1liL8mV5hLeX0Mlc6QnCo7gNIY0wWcwtsMXe
+NDWBO2i1mTlTBLjJrrcmTd8dwKy4Pru2oQ2+6O3/m58oWtBqsAJp47o8qOTmjJvZ
+IF2U6cOgoVNGeOmzq3QGGQnM3/9rXhD5DL3Trz2lCuy91fW0gMyFC3neaKdSDgoP
+2cZtvs5plLwBNXMu6W89BZI08mrD/flo3VkUgkxVBDr3PUKdY60Xvpp+sJwEw0Ef
+Yr9Zmb1FmDlwTjmAAZVo9OiAg7r2Jw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..9d385900f0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v3-noBC_ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-v3-noBC
+version:3
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem
new file mode 100644
index 0000000000..ebb27c0de3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxTCCAa2gAwIBAwIUN2t/Wc6xThY3A8WTT8g4qc3BJccwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLZWUtdjQtQkMtY0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAOMAwGA1UdEwQF
+MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGcf/zc6xWE/038gQYOrWnTw5B65PsEi
+iGWJQgeUOSCwADHJ3DYzf239OmBqhe30/fbT4Z5F9y4OjSGqzKhiAwq6rRCeybFQ
+d/tQvT5F197q5b0ZgMSSbu9luGD8kNSrpzmIvqwEVrABbKTFUqaFpSumZKbCRuQ/
+2ReQ2W+nfnL/EDekytU9nDoIMULcCc5cPj8q+gEVPaMm94bDwYe+9vVRwf2JycWt
+ldZbH6brAZ4dOpa+hXK4W4RqY0baTRYU/8wrihezQtVElJmXszOzcWveGg5cOGFC
+vr1EgdFx4d/dj+Eh7HiMqSJqUlVQ9D84mR1C1NBJdDZESflVT26kB0k=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..1f44c1dd27
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v4-BC-cA
+version:4
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..adb901d1d2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxjCCAa6gAwIBAwIUXX5wI9ZLvqMcMEKuZ6ShXJ0XfY4wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGjEYMBYGA1UEAwwPZWUtdjQtQkMtbm90LWNBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABow0wCzAJBgNV
+HRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAoqp9OCrEoIKeQG+2mtReILd2OW21v
+sgvxqDP6MGYtdQ7HfZaHZuv1PZiVzxysqk/0nuUYWzbPh+7/xUWTa21YVjt2fhDV
+wotDiE3iNOumE3Pbeg70MIRgQa00pYvjRse9748y4rLMSpZu9Dx8vPS6T2efrjI/
+JwMh0lqjsqGNdMEotfNFeGCrsjr3bjBj43RtRHpL00Cq3A276PmSyfs/r+raxJEU
+BkKqjmry3AzOMTIyTxI7qe5/CMAm8GvfGdhr1tj7sh9lwS5oe8II4u4D/9tirHF9
+wTNAhSaTiAsUoynaJtBYTA/MxYvwJ6/qY69Q7oUkX/Gs7Pl9fryyDo05
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..e9659153e5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ee-v4-BC-not-cA
+version:4
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem
new file mode 100644
index 0000000000..e7c778415d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICsjCCAZqgAwIBAwIUB2z1TvAGiocHPPLxLlCE2W4kKrUwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFTETMBEGA1UEAwwKZWUtdjQtbm9CQzCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0BAQsF
+AAOCAQEARifsn/PYmP30WdyRw3EBXEolSO+kqeY2fWM/4Qji7jUGLsgXRUt4zXbs
+Wgwrvxk+iHlrDwN8ux6OZ0UteKBlWxN6vWUZO3QqCI2prkdzBtHkiBB/sbp6kJPR
+IIBLPoXn8IOeV7xP7bjNHDqSqwDy4Ltm+0OmOBaONxhAjAyQPKZtuzLRUQzljLOZ
+9vE4CXM3Q/ZugkKOFq4YcJ8fTRXT08Tqtfxn0IIYBsezTPw3kX4qcgiyaSLhCSKC
+w1eb46+Kqe9FVC9pApbRFSsYm0guPnRRMASQSiHW3w+7KB35EFl4G5LlxzuZdZh5
+I3pv2YB2zXurIJ7IOizgX9GMVbOzjg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..19229ba766
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee-v4-noBC_ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ee-v4-noBC
+version:4
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem
new file mode 100644
index 0000000000..c9fe2a1deb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIUYXRFk++ngkM7pfne+aGZuKsxTYUwDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMaW50LXYxLUJDLWNBMCIYDzIwMTkxMTI4MDAwMDAwWhgP
+MjAyMjAyMDUwMDAwMDBaMA0xCzAJBgNVBAMMAmVlMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQCwvmF6hChs9K1PKjkg2daU5Lz2zAPhh1q7urmJu8gCWSTGz+PeJvKP
+zFptbQNPqFnDG9FBXX1IDkMJuZinwyQb1zcLxUqycX4ZKd09BcRIeS/9BXnOy+Cp
+1QFDj0R/wdMYnvh+yfMFOSLVBSFkGtHkvkDoaf7Sm9vzw8xVG8L2785tkfGVsyd4
+oqhDbRJ/RQu4wOjS8NmIRp8bAUg69YA1+wcc9cTbsp772sA529XzNDT6+IUV4H3b
+TcDW8/Z/A28616soIN6F1KXmQrj9y1lcj7o4uuHPhlpoF+CQScZNCKE94QqHqHvY
+yPuMZS8TZz0F31Jnb9sSncDWtK/MOyia
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem.certspec
new file mode 100644
index 0000000000..6a7dc5c436
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v1-BC-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem
new file mode 100644
index 0000000000..1cd6f09ca2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuDCCAaCgAwIBAgIUT5zJeVS8mAKQvZ177AdPjbMcSZkwDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQaW50LXYxLUJDLW5vdC1jQTAiGA8yMDE5MTEyODAwMDAw
+MFoYDzIwMjIwMjA1MDAwMDAwWjANMQswCQYDVQQDDAJlZTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEAjr1UeC96xOI/hocWQtnEJxO9+qxnlRzSuM0gZKvjRlaSsGyn
+QmN+/hiYSl71Jq4IkLTxkDy+Fn6F/zb353+f0JMZDVjbBuBfr4HPMTdCHKLnNyoH
+XkDwIc08bhoeVFj67OA0E/DxyDjoxZNOfvVALC/8xWlvfrik/N6NBNUS/CPCEFa6
+G/R2RPpea9Ftx5op5SMApctxGT5ZiYTSER44EFLJ2GVQWm8Tt/PNNXt+sYAaAESF
+vuOYwiRp17TRPq7+ddE3omKB5xA8e51y/NSAtK8SAsE6nDnjJUmM8sBaitfX6vDY
+ji8NYlIWVEMLkEPmg3dWiUz0WYG7Z7RkZ9yT2g==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..639f83d660
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-BC-not-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v1-BC-not-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem
new file mode 100644
index 0000000000..22b86450e7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUc2AKlc7qreFiWgePu0YLMlgs/YwwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLaW50LXYxLW5vQkMwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowDTELMAkGA1UEAwwCZWUwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBACgJObqHt39+EYoxXhZWuYTAWF7Kjt5jRxluishYoqZ2SKOvhxe0UJAK
+ufWLYDbkE0rRW3SNmFxipkXO8ERztDIjc1tbmFJMQa/mC1A3N7YiCLHO8k7t97ie
+diw7nEF6kxNp+OkOwZqWiH4siyTiFcAGArnh7j2FZt9dF03FgjmtqAtpB02fON0G
+YXuIms0yWHfQv8kFuOmJ9ScBoDZu0layvueWnCfnejIEMyqynRR+ZkPeb5cGqRpL
+HGjZnXYXTR2//DV71uUskCz3SGYwttthlDdaBM/AIg7GZIU4mL7iLSq3hxdpVQoM
+QXbqnL6xZrDlSn3OmT7/cNrIxdf9qUU=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem.certspec
new file mode 100644
index 0000000000..d704f3086d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v1-noBC.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v1-noBC
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem
new file mode 100644
index 0000000000..f113c5d965
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIUATfzH/PSKv0xC89ayKzWGTWeIYQwDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMaW50LXYyLUJDLWNBMCIYDzIwMTkxMTI4MDAwMDAwWhgP
+MjAyMjAyMDUwMDAwMDBaMA0xCzAJBgNVBAMMAmVlMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQAiY5jZpo5IV7FF4xLax2uq+WdCxUDJgYQeaTiu9RlJ84vEcHIuERwu
+CaweD8T/4fE372XyivZlopfJ3QR1owqfjGBA67H50mJAurVIBpb+cBnHSxiSYezG
+NNTrHwosfb1G8H/oT1/AQtjB4tMSuD8RkTQMwN2G0N9JAhoKL0EnNxKFzuW3IZrW
+A9B5mxEe9LLo4JhiNFUtm2Cyh7KiOgOC4IqZfh+A2yrJYaJoBlc3KJx9EfhC/nnm
+jsT4W2xxAXn3mNNKaaxnqF/e1jMSlxuKvXTYu1CssaixqQ3XvdmtGRcFoMSgS3RS
+MBnffZz/68BbniBcHIgqUp5M7jMEx6lH
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem.certspec
new file mode 100644
index 0000000000..5d3a65e650
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v2-BC-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem
new file mode 100644
index 0000000000..b5768a0c01
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuDCCAaCgAwIBAgIUfU0RGDUqR251hZR7j7dGSVvLOCEwDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQaW50LXYyLUJDLW5vdC1jQTAiGA8yMDE5MTEyODAwMDAw
+MFoYDzIwMjIwMjA1MDAwMDAwWjANMQswCQYDVQQDDAJlZTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEAUzqCpvNWzcDl+DVO0zNcJ1YHLwCTGzD62ejKfjvSUFNloW1n
+VYfWP3FN77vtfHdPWZOWcaPGOgvIAjtrovv8PAqE3zusM8ml+2XHYckXReOv+3xp
+qMN+8uGgI/KjyYuuxUOcNnWjwEHs9aB5A2QznNfb73vorBoMGOsp/eRRu/88SIsm
+Lb8faovBW/M82DddY01u4pQ7NRdk1lRJP7NDwyeJi0sp0Tt8vkqQmaWwVxJcECQ9
+pjyHdcV6I8X/0unB5aTDVNPk59FQ2mBnL49uhwDLPw/CfvIFUk5t4e5Y8fYENL1o
+bCmQwdRtv2uvglGbduN14jNeWi2BrR97CwieaQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..591a16aca0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-BC-not-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v2-BC-not-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem
new file mode 100644
index 0000000000..e63e7811e4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUH3fF+CwxPCO0p6f03bj8udeTCqMwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLaW50LXYyLW5vQkMwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowDTELMAkGA1UEAwwCZWUwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBADV11pKOwjVQe19NU0Dm+mxFwfr3JtigZJTuqLkDuI1ZzoiLdy5Gm7N2
+HEzx+5A6XoV+HxAA3vgDXjcxqNoBlUFZSKcO3dME/YAwtE06y5R1o8xU2ySH6/Dp
+N4Cg5hVPNDo/gomEsqDou3IlLXVQO64fBGFT/eK4fnv96jEMl9hkCbHUjrz9XODI
+iEkz2wJSiBmrqKBG+ZB66HqvoqoCNTSfV/IHE85QEb9YanJOc+DR9CYN5S1FILEa
+LMi/nVB2Ldn42kBFZjXxEdkQFIWPyKZIk4wFAJwsw6fNGUY6UAb7exLTEhkvP1nD
+cgozZt5aezZOz8N+xKwseT66ebSnBhM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem.certspec
new file mode 100644
index 0000000000..7f99393cf2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v2-noBC.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v2-noBC
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem
new file mode 100644
index 0000000000..6c1a22262c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIUc1r1zrsB1TOy5rvPR+6sCmW3WLkwDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMaW50LXYzLUJDLWNBMCIYDzIwMTkxMTI4MDAwMDAwWhgP
+MjAyMjAyMDUwMDAwMDBaMA0xCzAJBgNVBAMMAmVlMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQB8cqiLjiCQsWyha7QOnGpJVDbrZ050OuDNgg0RZ4h4+vtzvADZFr9T
+bbDS5lBUwCipjySqJezUVAbjXLwFPVilc9HW/dZYQCrTHq1KsKma/hbS4+evrW32
+EbEIWLlSc/Dqh8TIwdPtFCtkaBDibIEw4NRjjr18qckN/QQRDRUTjWPFE3nSsSL8
+ywlfQN22LFZpz9mLDsR7hgHGVQ7lKSn7aFspCguu3Z4H3IotMZKd8HmoItayeOwT
+5r/tid/uBWUMEV/8IevoJ97arusRZxpCkgrJLK0lRjYfpdO46G9xyEa9NV6R3I1L
+OYG6w/XbJYyJujCE1G9tDGVVB88lR41Z
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem.certspec
new file mode 100644
index 0000000000..13af934f29
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v3-BC-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem
new file mode 100644
index 0000000000..5490b63b25
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuDCCAaCgAwIBAgIUMWZpljmNpyGng0Q4Nt3ZY0BTjjAwDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQaW50LXYzLUJDLW5vdC1jQTAiGA8yMDE5MTEyODAwMDAw
+MFoYDzIwMjIwMjA1MDAwMDAwWjANMQswCQYDVQQDDAJlZTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEAGx9iRA2sOhzEzfbU7jg502lVEAfj0HwJFRN+DVQNyVuNl4ik
+QMVFHJvoMCFGu4loJRLqF/OuF/3tvUp4BC8QjbLqUj4wtSUCDZTF8o+Tjb9kTj3J
+60VqqL5/HU1G6FaKPDOU/mvmGkiVUjOcrrUghv8urTpYvx8/7Z+u0WySrxSCPCZZ
+mYv4LfCv4oEVpzpUBb4078qkntbezDQ5GAyzhpWHSo8qx0FTGnilyZ6Ql2RXItnf
+ube5HxTETDYRqHu9E9GcraGKIgYY7vbG2nNOvl87zxU4lHIwpu1vZftpe3raGLqL
+7nDqDqHC+9v64RDYH3U2yrl2SLzwsMR0by1otg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..8539715a03
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-BC-not-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v3-BC-not-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem
new file mode 100644
index 0000000000..c6a4f4e8c5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUb5/nrEpBnFWFYMGAHbxheeinOLwwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLaW50LXYzLW5vQkMwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowDTELMAkGA1UEAwwCZWUwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBAFgzXwCceIuZFJ7jB2Fls1D1XvYiwhxeuSVZHsNz1kpCbpmZi1hnDYOY
+gfr3FEpcFI3P93b3hHpl8w7DGJ6bvJDqnNxe/idDDg60jeghcmCsJKYUgyoqoYbo
+kjHdg1ce+6xsf4n3of3Cr6xDCjpvTupXTf5zrUGtTAvE6XmQPTx1vOEXK/mOdsBb
+6ZiVv726yukaCsBN+zuhIU+0eOj8+2kXeKc4C5S3OZ/48LydMRNxjPT4pZtsHfAe
+NWy4hyTqfcKZfU31F5GzdXMgn0vvA/v2fssj+ke9sXhTnpNdI48Rp/l5koEBQBNQ
+D2rvAdgdZEEQ9xrl5RkLjAXDpVbSoI4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem.certspec
new file mode 100644
index 0000000000..a65e41cea5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v3-noBC.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v3-noBC
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem
new file mode 100644
index 0000000000..dcb3b722aa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtDCCAZygAwIBAgIUG/xdmUZCt7ps5TMTVbvfWjJHuxMwDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMaW50LXY0LUJDLWNBMCIYDzIwMTkxMTI4MDAwMDAwWhgP
+MjAyMjAyMDUwMDAwMDBaMA0xCzAJBgNVBAMMAmVlMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQCrG/sSj1dbSebCi92T47mRiq8Pm1ovwfqvcuOAcdOELdpLocJMGlDn
+7ywQFXRjWvSYHpnk6ekg1XrkHG/N8rUYijRJ0SyR5kpeqGfxiMxSGjxHtmA0sCuK
+inqpZAa58v8nsUsFNH4iBWlRYxOAJ+KjwboTar59A5+gKXkxxmoy+4f+LgbyBBJM
+EHp2lMg4Nce0/l7tBtNTw5kuozQ7ds0iRl0h++Aw0SjhpyWGP3WVGNa0z3WlBULx
+FRqHLPV7yS0WN8A2j2rPanzIzNpka4QsnPQuDznnI3295k1aaTKXacMDnj10JNHh
+VSNkPAii9GM0xubnEwvgioNrIePpRpPn
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem.certspec
new file mode 100644
index 0000000000..35a3a9ff36
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v4-BC-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem
new file mode 100644
index 0000000000..417e004719
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuDCCAaCgAwIBAgIUPbBY1V9XpHrNz4pW9hdLlHaE8wowDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQaW50LXY0LUJDLW5vdC1jQTAiGA8yMDE5MTEyODAwMDAw
+MFoYDzIwMjIwMjA1MDAwMDAwWjANMQswCQYDVQQDDAJlZTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEAbcIplVCPKWXW3FSoCRZE9gLtlnKN0aWmsqn9mbZij23ZbVXA
+i8t+wu0SSbgeJ99YE36Z/NkKCx3pRIGbjdE1r08rTAhoHxbTdeIzD8K3P6JOSqY2
+7HHsNQxkroWB3Mf3iPW/P8/53Q8o3FYfZEmLO18i2PHw+Y9ZVwHSE7pbdwvTrlGq
+MrGeSR57JJLHdZteMhr0A8w2qA2Cv+2NIKNLz0Tztvynmmu+laIMvfTpq9WJesmG
+Y1CPXrC7y+Z2720LoxtXpxtVKxi88WFFOKVsBdEBfH9E34TSw5B84RWiaQaZfrDJ
+Yd3E+Su7BkyfA89q1J8ITgHMEQdkceafCiQ0pA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..7627d3a8a2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-BC-not-cA.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v4-BC-not-cA
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem
new file mode 100644
index 0000000000..238514cffe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICszCCAZugAwIBAgIUM3CPGo7kDa/S240PLKd0jQ2d/SAwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLaW50LXY0LW5vQkMwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowDTELMAkGA1UEAwwCZWUwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBAJBYwR2i3Y5sJp1o6Eo7Ue1eOpBLFGSuOz0HmQHrZG4dal1dfVrRkv8S
+YV//loHyjmp80patejR3QylK1Y6ARQQm6Wn/fOSsUcy4KyWU+DQ5xSrWpRL1EA/7
+yMvK3uwKpoRGUCplLqpTnboRBuFvd6sqU2badeVBpOx/cJ5CBbTiHzudtoWs+O3x
+Pv6rjRS8KPDh0qENyKi0JKALXWmO0eHGKPAlRKjx96/4wfj4nvnj6i+OlIbLNZJ3
+Ff8+KCt2xc6cToClZ7jD68eGRDhBd4r5HcUO9y85BOKLPepTM7gRd1FSVXjL21W2
+H+SGy+jOUZOMSiFpQW7Gohv47xa3s4M=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem.certspec
new file mode 100644
index 0000000000..a780337539
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ee_int-v4-noBC.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-v4-noBC
+subject:ee
diff --git a/security/manager/ssl/tests/unit/test_cert_version/generate.py b/security/manager/ssl/tests/unit/test_cert_version/generate.py
new file mode 100755
index 0000000000..2afaace3b9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/generate.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python
+
+# 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/.
+
+# This file generates the certspec files for test_cert_version.js. The naming
+# convention for those files is generally of the form
+# "<subject-description>_<issuer-description>.pem.certspec". End-entity
+# certificates are generally called "ee". Intermediates are called
+# "int". The root CA is called "ca" and self-signed certificates are called
+# "ss".
+# In the case that the subject and issuer are the same, the redundant part is
+# not repeated.
+# If there is nothing particularly special about a certificate, it has no
+# description ("nothing particularly special" meaning the certificate is X509v3
+# and has or does not have the basic constraints extension as expected by where
+# it is in the hierarchy). Otherwise, the description includes its version and
+# details about the extension. If the extension is not present, the string
+# "noBC" is used. If it is present but the cA bit is not asserted, the string
+# "BC-not-cA" is used. If it is present with the cA bit asserted, the string
+# "BC-cA" is used.
+# For example, a v1 intermediate that does not have the extension that was
+# issued by the root CA has the name "int-v1-noBC_ca.pem.certspec".
+# A v4 end-entity that does have the extension but does not assert the cA bit
+# that was issued by the root CA has the name
+# "ee-v4-BC-not-cA_ca.pem.certspec".
+# An end-entity issued by a v3 intermediate with the extension that asserts the
+# cA bit has the name "ee_int-v3-BC-cA.pem.certspec".
+
+versions = {"v1": 1, "v2": 2, "v3": 3, "v4": 4}
+
+basicConstraintsTypes = {
+ "noBC": "",
+ "BC-not-cA": "extension:basicConstraints:,",
+ "BC-cA": "extension:basicConstraints:cA,",
+}
+
+
+def writeCertspec(issuer, subject, fields):
+ filename = "%s_%s.pem.certspec" % (subject, issuer)
+ if issuer == subject:
+ filename = "%s.pem.certspec" % subject
+ with open(filename, "w") as f:
+ f.write("issuer:%s\n" % issuer)
+ f.write("subject:%s\n" % subject)
+ for field in fields:
+ if len(field) > 0:
+ f.write("%s\n" % field)
+
+
+keyUsage = "extension:keyUsage:keyCertSign,cRLSign"
+basicConstraintsCA = "extension:basicConstraints:cA,"
+
+writeCertspec("ca", "ca", [keyUsage, basicConstraintsCA])
+
+for versionStr, versionVal in versions.iteritems():
+ # intermediates
+ versionText = "version:%s" % versionVal
+ for (
+ basicConstraintsType,
+ basicConstraintsExtension,
+ ) in basicConstraintsTypes.iteritems():
+ intermediateName = "int-%s-%s" % (versionStr, basicConstraintsType)
+ writeCertspec(
+ "ca", intermediateName, [keyUsage, versionText, basicConstraintsExtension]
+ )
+ writeCertspec(intermediateName, "ee", [])
+
+ # end-entities
+ versionText = "version:%s" % versionVal
+ for (
+ basicConstraintsType,
+ basicConstraintsExtension,
+ ) in basicConstraintsTypes.iteritems():
+ writeCertspec(
+ "ca",
+ "ee-%s-%s" % (versionStr, basicConstraintsType),
+ [versionText, basicConstraintsExtension],
+ )
+
+ # self-signed certificates
+ versionText = "version:%s" % versionVal
+ for (
+ basicConstraintsType,
+ basicConstraintsExtension,
+ ) in basicConstraintsTypes.iteritems():
+ selfSignedName = "ss-%s-%s" % (versionStr, basicConstraintsType)
+ writeCertspec(
+ selfSignedName, selfSignedName, [versionText, basicConstraintsExtension]
+ )
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem
new file mode 100644
index 0000000000..8b635b1c33
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbYCFE3k+54zZHCkzie8w4rzHPcWCvpHMA0GCSqGSIb3DQEBCwUAMA0x
+CzAJBgNVBAMMAmNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBa
+MBcxFTATBgNVBAMMDGludC12MS1CQy1jQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODY
+H72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk
+27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9
+0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMM
+kd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaL
+L+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswCwYDVR0PBAQDAgEG
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACtj7FH0rKA0rkR4jE7D
+IoflYPE7meH6vbDEb7m0kALj69+TH6pnBuKiaiWQwyhx4anAbu13JEEpACJoaNER
+FtxsKL8y9j+tSbU5RFHx+6f7OOcz5SawscGEF+P3t0lJqU5d5sDs/frd9DZWu3Ho
+lzTlbP2YQsqiRAT2Ryx1AN6hrZEDoQ8TEwXqSC3slSeDvC+pkSZbhSg/VlZ9vwjz
+zZ/ukePXRg65AcBrlWhMuJoCCzkQk7lVt/oOHcrGl2AYzN+t04FXs+92/5Gr4Qcd
+iCweQYIyHm8UKGsA8gNpFzt8jP5cDUNx11FRPA+N7lImdCas9GHwKTKIunsqW8zC
+1lQ=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..77f3ae9147
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v1-BC-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:1
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..45db11b6be
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzzCCAbcCFE3ZspZvBnOAmWIh05fgdovmSbLrMA0GCSqGSIb3DQEBCwUAMA0x
+CzAJBgNVBAMMAmNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBa
+MBsxGTAXBgNVBAMMEGludC12MS1CQy1ub3QtY0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjGjAYMAsGA1UdDwQE
+AwIBBjAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAbIDFm/7bUqz+GPgQj
+m5pCmxph7tF1Hfz1JMW/3Yi7bthN9ih9As18qgDOPVD3qrZFAUNM6mpvBo/UPb5V
+vW8IgtvoNffhUKUjUiYDBcPNmhc2XPCQ9CyhCAF2T1dXftp2H+kQ1cu7N/Yq3vz/
+XkadGQphSs+IWNxYf5hAFU12ZnY5L5BJ3Sr0fjzKYdiyv8MbNqxf1NNlg6+gH0aR
+qH+EjqJarw+VeGtwbQjiNCIqf60swE7Fj8hlnGXNlv3ISV0rY8Clfmpr9pG6YBZa
+30y/XrwECMiCGlGyQKjKHmijFUF6h49fI1M94jhGDWCslhtvpusdZkaTwBfDEf7U
+/5Ze
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..2a366535b6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v1-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v1-BC-not-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:1
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem
new file mode 100644
index 0000000000..a3a7d3b154
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvzCCAacCFGUeQFCueydOeMy6qL1r3OWc72slMA0GCSqGSIb3DQEBCwUAMA0x
+CzAJBgNVBAMMAmNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBa
+MBYxFDASBgNVBAMMC2ludC12MS1ub0JDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABow8wDTALBgNVHQ8EBAMCAQYw
+DQYJKoZIhvcNAQELBQADggEBABvfOrDhyQkVROM6IwTPmetz5M2MjZeQjOVYErgE
+fUCE+z7EW5jXaMrcIbjpR+FnnJ3nB6d5vwx6Bzw2q127dUQZkWcI/soiL0ERr+Kv
+e97+tPdLb5gL+6trnJb5enkfnGp+Ooo6NyBzUJT8EI326JCxkD3SrGBgLtxzd5hz
+rkyjp7y6ypK8ihHGgL7fp4kCeM1NjAt3IPLym7yQOkFFivcKN3Wp7+9oN8t3yo3U
+is9yEOmYE/VNRdm6nFnLbe+QQqAZwWQitadVVBHOcW9nIdB1e8VPO+1Dja0muX9K
+cLt3twV8Yjk+aaAC3T/4ZTUWNdH80gEZE5Esy1PYeoV16iY=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..63bf6ed737
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v1-noBC_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-v1-noBC
+extension:keyUsage:keyCertSign,cRLSign
+version:1
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem
new file mode 100644
index 0000000000..6250abda7a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAQIUf9E2zqRqLx+VENz/4+SpW+a1PeAwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFzEVMBMGA1UEAwwMaW50LXYyLUJDLWNBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzALBgNVHQ8E
+BAMCAQYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANmkUyFb9kU1z
+kMHtqom8a2oY76fmNcSyk2VbhodFyj4B9nl/wnHIUxFdL34lMHdxpkEKf2WlSWez
+fIYU2JHwPA+iuRE0K0bFqyYt0Bpjt2r5faUzk0ELxYoxIRW7KOHutLBhk/gotgWa
+7+HiVJrqfBXQlfWIcb+BNEMc+bzGkQDCdFuzxhUTdIY5monTTbHjF/PUhQ3l9Yql
+GQ8IfFQYzaTvZN7kHetuG/gl2dcHA3Yv8IjfSr3/ElrVtF9Tp/c30m3i0Z5YMa9M
+zyFYUxqNq5btZYqU7E5/9MXt1A45oet6QHO12E3m0816ooT7g9swJnIQ9CV21LYL
+HEVn3xw7sg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..ca1bf67a7d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v2-BC-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:2
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..8deb1b0eaf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAQIUC1qc7IY2m7UMNztbSNKbdkpXSF0wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGzEZMBcGA1UEAwwQaW50LXYyLUJDLW5vdC1jQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMaMBgwCwYD
+VR0PBAQDAgEGMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAFSFOD3w2yEL
+hJBjs6WtkQGAOLJ5Ip/2DI0W+XJAyCRF5nRVpakj8ub/Y/cGG1dLbXuOFoQFR0Hc
+6m9hoNSn4Z0v84K5WEsKHtjawQ7DnXSYVyt3JVbtqtQLlVPgXhGqeuP4geF9Zu5+
+2hb6nkwg4bGkIumcx9WFDt0NI++UU9q7/Pw9CTjk86XGhwz1LlVA/RIfotsG3IeZ
+IBoV6qT5URErFpOj1b96VnMQNtnjo2HMq6GhrtOqBxRzkjySJ+2JHRe4cf/Phg7h
+DIkHTo+ueLrT9toHciMW5NXssUwuBtvhGyeCuzVTtYSnEKGHdnXcVUx6gB4F0PFI
+JsD87eWyB5U=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..fc28ff150e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v2-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v2-BC-not-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:2
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem
new file mode 100644
index 0000000000..273f23a8ba
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxDCCAaygAwIBAQIUVYXvMaxdKZ4YI73r4J7jv8+sslwwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLaW50LXYyLW5vQkMwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjDzANMAsGA1UdDwQE
+AwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAsNqQCwIN/u9TAVkh8Hv4IcfsY8u7Ex+t
+Z8kfW0rUz666kTRVGFQbM9gzgb+rqyou14vxHDuBx67kbJLKdJ8tUDoe8bcpWbGB
+jLx/f6elA7D0eSArpEAheRgoqOJS05f6S9pOA9RnNySfFyV+ypZxBOEbrGOzdzNd
+guwoBYS/CAx+TH/f4o92cdqBtm0psRkTp+Fj/qi6f2zetxNfLjFpy3Zog8GopCWK
+Mhg0W8j9zeKDLf3NVFt8XMSW8DfwJrsY9enK6ISrdBjIdDqaFdzsXEykeiZgHemf
+jBPdogIQD6ZsdlaFFWOq03DHLw4v9Sj0wgc9C8gtQDrL6K5fmfLXGA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..2d3fe59ffa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v2-noBC_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-v2-noBC
+extension:keyUsage:keyCertSign,cRLSign
+version:2
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem
new file mode 100644
index 0000000000..d4b8eee198
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUH8RrlVV1Arzvq9vwD37aGxKswKMwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFzEVMBMGA1UEAwwMaW50LXYzLUJDLWNBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzALBgNVHQ8E
+BAMCAQYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAFLbj9FU+Oa8R
+/9oGqUDa519Et6mg/euJ5tACGLozVoEGCGZN6raLMGlrGNSKLFZBKerg6CdvAuJj
+EUNU1utJhH+XGz6ZCIr9SAsDQ3y19cF31WJ9sSamjmChoAsB0zl9EDIrurg5RPHD
+wUaTGPLLieLzmAXYynsxIrKZbCCEmR0be2fPqGdkby3iuQnk/Punzluax/6d7pOV
+wNbXlRVH9Tcagjcfv/VqPy+8Vt+iOySaAQSj+Mq4chu7wnDYpsDjtAiR6lou3gvt
+tjYy66d0Az4SjJeE22r3Xbdz5fgcWyzkiTg0McHpzJqBjPHwONAgy711NOVUNrbM
+3RRmkWzpmQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..56fcb21a03
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v3-BC-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:3
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..48b81adcf5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAgIUY/xRC7wQthFDkmcRdLMOkyv+UdwwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGzEZMBcGA1UEAwwQaW50LXYzLUJDLW5vdC1jQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMaMBgwCwYD
+VR0PBAQDAgEGMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAKvTbOiPSNbY
+tvjAcbZPsk+XMNJgOsZ9Wv9RGl+Pc2uZQOKLeysL9bhxqI3Hfylma55t4HI2G7Z/
+AhzgkYBr8H0XPF5q0h66up4YJrDCY2goWjv1W2TmVxleQNQO+eXpP4PJqz/bZnB1
+cXj+olt4CjUtbJ99+RnPU2/7pjQ7D0XMnH5KEqoOrqZr4jVrb86kps9OVmvDr8bW
+P7uzZywMNJfAuO9SYtSsqQMGz1SA/fDUK2Uhc3AeGyTU3ePAecABCVNaLnMlqR4L
+mugayqLdU0OLuNPpo/bNdtyU5GC+wpSFF2jIkASK+4a7KFJq5SViOCT1BV1NoWMN
+DzxXo1ODQjw=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..a2def8d323
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v3-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v3-BC-not-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:3
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem
new file mode 100644
index 0000000000..8a97eaafc8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxDCCAaygAwIBAgIUC26qMXLsjLi4NxcXvpWLTZI5pyUwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLaW50LXYzLW5vQkMwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjDzANMAsGA1UdDwQE
+AwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAkdjTbxvps6efTz63F1cnOMKU0NTgR+Qy
+IrfrbMX00GOuABe4IoEiq5uuV0Lj0jkd0sKSc516oy79ZujJnow0sQp6igv3fBHz
+6ZsQcHjULxd/PTsTGPp/NwLBsn49kXa/hwt1Us+59qH1oFtRPnQb4Hpr2QhXBR03
+DlhgsNHgaStKcfG37q1PIfDNENC4yM7mgQMvtqQX8QOmIEGAfnwBDLZAuD1GEZ5V
+qxprydaCHommBoUKUOAxMnZudco9SDDHPI5IiM1kN/cQL+WDHVIJgWMURqS+Xrod
+nkM5lFfdWGpU/79QLiCQC3mBpkyJ8Yu3bXCh4W4qxarC3n8QaCadjg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..b336397ab6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v3-noBC_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-v3-noBC
+extension:keyUsage:keyCertSign,cRLSign
+version:3
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem
new file mode 100644
index 0000000000..000898b869
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAwIUD7Qul1mX5757Z6c1xfzbff8Bq3kwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFzEVMBMGA1UEAwwMaW50LXY0LUJDLWNBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzALBgNVHQ8E
+BAMCAQYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAX1ogR2JROTKS
+BzUzJ8dsfds1qRR4THa+4+1YcuuTJ1P66dnhnRXJVAdJmJyeqezQf23M5O56h4fL
+ilt20gptvf7bFd+DZCnV9iHQidQnukj7CweiHOs5xCdjz6+ozZfFpQZaRWX9/462
+VxYOLIWw8ft23liLUmBP/BhPn7W+ILpwvvqLamO3xm68yAGLOlGfz9FH65wupkq2
+wuXjh2XOVEKZ5RdUzXrF40nzVUBBWPwvQTLge+2wD07jXuDegMVrBKkbZDara9TO
+HNXcfbT1+9BcRJXlQDJGPyw6oFWHqH+FilAtbXryw3lc9forDWy6nLFvBGW+Ed7a
+lpGnStAJ4A==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem.certspec
new file mode 100644
index 0000000000..12b94e63a5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v4-BC-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:4
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem
new file mode 100644
index 0000000000..46401840ab
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAwIULzzqCIST7DXQ9SzN6d/zzUSfj+UwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGzEZMBcGA1UEAwwQaW50LXY0LUJDLW5vdC1jQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMaMBgwCwYD
+VR0PBAQDAgEGMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAKlQRoxIy8+V
+EtYKZJGAsUvfB4+3LRWZy+eD89bhPUzEgh77u2EZS8byT9/I2+1kv1jJ7I6+G2m2
+cOLGILJBinEB8J3tOZHoUdiwAuqD0+zNBq+6sbItvNlJ84fh66Q1PTNnLWiKaQmr
+oIQJ+Nu7crZHkfUrCn8DxHgfaZqI+FV3mWie1AQYrsU9g5ZevWokQr4AH9X/jPrt
+ECbGoD5tPF0D3bAy3HxS1kCvwFSh0/GSszffYIaPrcUgOcUGYNdQSstE0y+lKZ2M
+ncYUxXd6JI/3OFDTMKRh37JcyPAozQyVecJQLjRc4ycB5uy99zdZzHB2DsMHu3aB
+gC2zpGY6iL8=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem.certspec
new file mode 100644
index 0000000000..43a04f70fd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v4-BC-not-cA_ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-v4-BC-not-cA
+extension:keyUsage:keyCertSign,cRLSign
+version:4
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem b/security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem
new file mode 100644
index 0000000000..c77ad4f7d4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxDCCAaygAwIBAwIUL9fvs2uc+o6yxIOieQDCERYiO0YwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFjEUMBIGA1UEAwwLaW50LXY0LW5vQkMwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjDzANMAsGA1UdDwQE
+AwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAe6Q1mwKjfxiOiyvpExwNTAo48PGfDIAF
+L9vF94d6fO7Z04Q5LeIMJwfXqb8sTJBBcsV51IYSMnK/EaVVf9vX7abcya6ku8VT
+EF9THjT6+8gXKPj1v1te77kvu0tYRpDQeZ1Nhv7fRm8LsZFZPTKeT+ADR2qZo24J
+K3073nn13fod1TnUYIWg5vjDezDUdgqWfRO35rPusy8DtIg3CqkYgXbQJQUnvEj+
+VDWbbmqc7tw3O0xFJVOPyo+/GAIqbSzmqJTzS9o3gBIzicBy8Q3B+s1F0+7qZELX
+P8ZOSdY4L0iHKSVEZhFnEmcz/cySwTwt5ou90CdVaNCeOntga0e19A==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem.certspec
new file mode 100644
index 0000000000..4970d1e945
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/int-v4-noBC_ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-v4-noBC
+extension:keyUsage:keyCertSign,cRLSign
+version:4
diff --git a/security/manager/ssl/tests/unit/test_cert_version/moz.build b/security/manager/ssl/tests/unit/test_cert_version/moz.build
new file mode 100644
index 0000000000..2e2c294961
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/moz.build
@@ -0,0 +1,61 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'ee_int-v1-BC-cA.pem',
+# 'ee_int-v1-BC-not-cA.pem',
+# 'ee_int-v1-noBC.pem',
+# 'ee_int-v2-BC-cA.pem',
+# 'ee_int-v2-BC-not-cA.pem',
+# 'ee_int-v2-noBC.pem',
+# 'ee_int-v3-BC-cA.pem',
+# 'ee_int-v3-BC-not-cA.pem',
+# 'ee_int-v3-noBC.pem',
+# 'ee_int-v4-BC-cA.pem',
+# 'ee_int-v4-BC-not-cA.pem',
+# 'ee_int-v4-noBC.pem',
+# 'ee-v1-BC-cA_ca.pem',
+# 'ee-v1-BC-not-cA_ca.pem',
+# 'ee-v1-noBC_ca.pem',
+# 'ee-v2-BC-cA_ca.pem',
+# 'ee-v2-BC-not-cA_ca.pem',
+# 'ee-v2-noBC_ca.pem',
+# 'ee-v3-BC-cA_ca.pem',
+# 'ee-v3-BC-not-cA_ca.pem',
+# 'ee-v3-noBC_ca.pem',
+# 'ee-v4-BC-cA_ca.pem',
+# 'ee-v4-BC-not-cA_ca.pem',
+# 'ee-v4-noBC_ca.pem',
+# 'int-v1-BC-cA_ca.pem',
+# 'int-v1-BC-not-cA_ca.pem',
+# 'int-v1-noBC_ca.pem',
+# 'int-v2-BC-cA_ca.pem',
+# 'int-v2-BC-not-cA_ca.pem',
+# 'int-v2-noBC_ca.pem',
+# 'int-v3-BC-cA_ca.pem',
+# 'int-v3-BC-not-cA_ca.pem',
+# 'int-v3-noBC_ca.pem',
+# 'int-v4-BC-cA_ca.pem',
+# 'int-v4-BC-not-cA_ca.pem',
+# 'int-v4-noBC_ca.pem',
+# 'ss-v1-BC-cA.pem',
+# 'ss-v1-BC-not-cA.pem',
+# 'ss-v1-noBC.pem',
+# 'ss-v2-BC-cA.pem',
+# 'ss-v2-BC-not-cA.pem',
+# 'ss-v2-noBC.pem',
+# 'ss-v3-BC-cA.pem',
+# 'ss-v3-BC-not-cA.pem',
+# 'ss-v3-noBC.pem',
+# 'ss-v4-BC-cA.pem',
+# 'ss-v4-BC-not-cA.pem',
+# 'ss-v4-noBC.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem
new file mode 100644
index 0000000000..cb9ec2ab97
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbECFHd1Crm+6DaFhkTjrm8a2lNZn96/MA0GCSqGSIb3DQEBCwUAMBYx
+FDASBgNVBAMMC3NzLXYxLUJDLWNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBYxFDASBgNVBAMMC3NzLXYxLUJDLWNBMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoxAwDjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCbtlhHVoAs3xPSRAoXxOuUpyTB
+MzQWcIvsS61mkueLRnhBVzkpR+fVlmLUObLpZkVvZpTwlzhutw8A18ALBTfme9vY
+8zdzMR/sNDRNe5kFDFWyqRAw7MWKKT70ViM9jJflEpN58wuboB6trwWoGuep5YSG
+Tzl9vnxxAPwlvUbMzktFHUYyVfXes5mvA1IjujgexuSLAQP494QWvgjUIIcvd+Vj
+FNBhDgj+bt7fhzRc4PF123lFn1bDpl7qxavC6+e7xx0AL1OPbifHTJD9M2sjrHe8
+Jqbq31/YoFQqgI+CTKH2IVnvioacB+rV4AxODP+unfIwNzjef5Z0UM/aXlMO
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem.certspec
new file mode 100644
index 0000000000..731396f034
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v1-BC-cA
+subject:ss-v1-BC-cA
+version:1
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem
new file mode 100644
index 0000000000..0a9be9ba44
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbYCFHt8jLZUapQcsSi0Kh9mqYUtCnd8MA0GCSqGSIb3DQEBCwUAMBox
+GDAWBgNVBAMMD3NzLXYxLUJDLW5vdC1jQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIw
+MjIwMjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9zcy12MS1CQy1ub3QtY0EwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erk
+NUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwC
+fs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1m
+CyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTM
+HGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m
+1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGj
+DTALMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAFjtTHvdBSPcrUxP3PMM
+dnBxm/idtTvwhRcoskIXF/uHw39W9Md4lu9PT5AaSRFy+DCPG6WGuZW7z8qo9ECc
+2GbeoBCroJ7yYLQL0avK5SfWsuT79kBBuFXCD8NbDXQGle3RkXg+MrkCsvagoXqt
+8vexfNXMwPu6g+iAxZ7LdNjLVmHo8Q8vQKiAxDvjjmscxxrlGc6/biSEychTeEDK
+/JQMsD6uSadFCLBcKnZvAox05AxDd08yvlFGtGUx4NFToixkBUJZ8vPL9c0pHXVm
+YDKdib/UsN940D+chT7EI4fsolX4F4Ti2c3XbIHqF5yFbySODDKm9eVRCFxfLwrs
+7U8=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..20d716d494
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-BC-not-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v1-BC-not-cA
+subject:ss-v1-BC-not-cA
+version:1
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem
new file mode 100644
index 0000000000..ca4d8e167a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtTCCAZ0CFCmTa/0HEBZWaC3cI4nBeSBnQgArMA0GCSqGSIb3DQEBCwUAMBUx
+EzARBgNVBAMMCnNzLXYxLW5vQkMwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIw
+NTAwMDAwMFowFTETMBEGA1UEAwwKc3MtdjEtbm9CQzCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7
+wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCAp
+k6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh
+eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KW
+EsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONssc
+JAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAATANBgkqhkiG9w0B
+AQsFAAOCAQEAGsjqQY3SWLufSRTl3mGq0Wv4rI5mSYOJlsZKCMtVXkMMs3QUNCcD
+1a7Fnae6tFjcpBElxgFFISLdmQ8lZXa4Et7flcwwYiQO9Hfzwpz/onlq1Cl150a0
+okDieDL5cklYlCoR2KGgq5T56K9PRKsAHWNys5Uhxfo+ysK/R8oqEVOUpm3k/Nsh
+k9EMx40a+T7idLj3z+D3hAOm9sus+TVSd0JXT0IFyJKf9+h7A6OCQasO9sTN7vpN
+O9nJf2aCU2rFPoTxOShDGJRqIFPLK2F1Efib8VqfZ85VmkepxyyzSpYnLYYtPLkK
+bpSqCfUSchX8syla2zeOldxUHWA2RjWYVw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem.certspec
new file mode 100644
index 0000000000..58d2f0d7f5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v1-noBC.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ss-v1-noBC
+subject:ss-v1-noBC
+version:1
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem
new file mode 100644
index 0000000000..1442072cd4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbagAwIBAQIUQQKlrDt19yvMQsPwETMYCCytugwwDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLc3MtdjItQkMtY0EwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowFjEUMBIGA1UEAwwLc3MtdjItQkMtY0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAO
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHG8xQ7meUSvopihSREz
+/svytvDvWFrlANJTBuUTDQUE/RR6uOgkJkSf3+je2Ngg4zCVkxqq+hSkbo+rQ+nA
+MhcDpNDxqu0waYeujbFIPMQ5iapebD7GoGKYZjv8HOHcg+PDDClP5ANIfUJBuTEF
+R9PePsHgnlF9wupRywfdd4E98psZmKhiHA7/rMiCmaf3lbxASVbqo2eWNMsBIzCb
+FQXab4rplzcQ+B76IzTNWRv08x+K32dApNfirepO5yCE5aLXn3XrChzV8tHgPaco
+gta9/I538U1xZOh3Z6qpQzksqL6r3inamvqvI10ZNB+/Mi0jUexPxyoj7MLCbtO1
+lI4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem.certspec
new file mode 100644
index 0000000000..8dbb3a65ad
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v2-BC-cA
+subject:ss-v2-BC-cA
+version:2
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem
new file mode 100644
index 0000000000..f78ceabdeb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAQIUNbZQDstJLaaiUaQfs27GbUHZ38UwDQYJKoZIhvcNAQEL
+BQAwGjEYMBYGA1UEAwwPc3MtdjItQkMtbm90LWNBMCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMBoxGDAWBgNVBAMMD3NzLXYyLUJDLW5vdC1jQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAAaMNMAswCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAJWGrrP8uHTXB
+pIl5CU+IUk88BswSpETWnKenUqwyLfdQlr0fCzWPzZjZ+tbbtKLzHTdIhjAFqpwm
+Q7fefDO/CNmmRb49OajJE8HO3cHltcWK8mbIUgMsdcWpCCNyUBmwuxsAMj4ggJHE
+2p9KTvRorOj2Jzgvt2PktM7hV3Mh/hGP2VETMr4g1m87mSanFtmOUAX8BJ8Aemjj
+HHwPzJMCjnnfDap6YpIzgQQoRK+f/X9PiAfxM1vzjVezdiXjB1WWxqKMf7AcyTbz
+1QGA1bfMmJ1pRazpGqzeZmnADjvtcnmYKU0/MY5eAcmAVWRqbffg17Bgoj3ZKgc/
+sIiWXjciJA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..2b8f4bcc55
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-BC-not-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v2-BC-not-cA
+subject:ss-v2-BC-not-cA
+version:2
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem
new file mode 100644
index 0000000000..46909ed0f8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICujCCAaKgAwIBAQIUFaBZ6C5ChVgFJTuYCY+Fv2HlV9UwDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKc3MtdjItbm9CQzAiGA8yMDE5MTEyODAwMDAwMFoYDzIw
+MjIwMjA1MDAwMDAwWjAVMRMwEQYDVQQDDApzcy12Mi1ub0JDMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1
+aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/we
+adA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSS
+pH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62W
+YVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauR
+CE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqG
+SIb3DQEBCwUAA4IBAQBnZXBfG8G0KE0SXdrsOA1pD/WGg0vnlbEZyL2/NCBoyuNb
+l57Ktb2ZtmSVgiAAtEnDDmeqUxkLKK0EVxyO1rRfWfgMf9C5+YwZvglnev0fZLN5
+k3+hQp3y4d15fURD70DbVajH4zk3S61GdlvUL4ic2qU4J6B1NudQSaDa8gn9sCBq
+rLrwmozkBOqL47QrFzXqigakuV+6b7ZU3Dh3c5xs2LtNMMlblMU2Ib0o/wgF7J6g
+bouwR3fbrBTmIwPCSsw/zM98H5v0xWYiKpGd8RKrazVpPnEmKzmud8aYhasRpTeB
+yQ+yfqGfJjrEJ/TAz++I/0/lAu63V1pFQRsTejAW
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem.certspec
new file mode 100644
index 0000000000..7656115a84
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v2-noBC.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ss-v2-noBC
+subject:ss-v2-noBC
+version:2
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem
new file mode 100644
index 0000000000..075d1f7071
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbagAwIBAgIUETSep9lFvjlmLDhUIYgZJ9C1AZ4wDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLc3MtdjMtQkMtY0EwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowFjEUMBIGA1UEAwwLc3MtdjMtQkMtY0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAO
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAJA2yM4He8/hSksOiLp
+3WCPkSxarm9MIbj/XbbKpiDw5OZLsbwZzth6d1KJ38q57j0boNHNEPM5AJwW0jep
+izBQzVS+8Xy0x66MxEIoaxF/tVWNBnWnr6WPci8XpJl60XUcj/T8kPrncZ4jfu8I
+99qeMHMsv8LG4BzCZGkiHdmxGFbKvn9Fl1Incq2yTu4jR5nn9AeoyksYbbtbEFIB
+YUgWpmSj9Eu2g9JT1nq35ovOZ7wgcDXxnFiCHcULiMOw2ZrMViWhMRKOo5o93kZn
+34b4sPJ0dOnKVnLM09wICdvq2c4aDGKy6lCVoZPBV8yjxeqGdUeTVeHMZBJHAZUV
+FmQ=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem.certspec
new file mode 100644
index 0000000000..df822e99b9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v3-BC-cA
+subject:ss-v3-BC-cA
+version:3
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem
new file mode 100644
index 0000000000..897001600f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUCxOdFiOnJjGWpdtzdSkevnFmtiUwDQYJKoZIhvcNAQEL
+BQAwGjEYMBYGA1UEAwwPc3MtdjMtQkMtbm90LWNBMCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMBoxGDAWBgNVBAMMD3NzLXYzLUJDLW5vdC1jQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAAaMNMAswCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAsXgT1HoCwbXS
+yEW8j3hk7f8XrtC+l5oXby/NtEEh0UqG5/gjkkoPUXKh3kU/EsmwzadFtr9j8H5i
+Vy8+zr2rIkNzc8WkpXRLyvuwzRW04sIVmEnvvHWVnWIKoILk5XQ9wKKrXO40H4Jy
+6Fva9+f31D5y1hsPbYfmfKZlNxPrM9Mvd+NG5N/PEkBDzHkqMTh0jTmUs99DcoyR
+QVIta5ctnV8O+xWVTmKIte7hPGLnAUiDC54LNYkIXhevO6KbqpMvk74sKRPJRryt
+2NiTaSKu9YxL+Nv5wWqh2quv8hp6y4kzGt/dp9CrE3GQXkCe/KrTJr4Z6EBQfglL
+fy9XXKPejg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..0b2b575573
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-BC-not-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v3-BC-not-cA
+subject:ss-v3-BC-not-cA
+version:3
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem
new file mode 100644
index 0000000000..41c6121a97
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICujCCAaKgAwIBAgIUK8t7xNHS43H4akDzvAdNmXRk9l0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKc3MtdjMtbm9CQzAiGA8yMDE5MTEyODAwMDAwMFoYDzIw
+MjIwMjA1MDAwMDAwWjAVMRMwEQYDVQQDDApzcy12My1ub0JDMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1
+aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/we
+adA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSS
+pH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62W
+YVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauR
+CE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqG
+SIb3DQEBCwUAA4IBAQCh6MJjtVoXMs9CqIvFGOtC5xZBzApCyVVlO0sWRgdZ85Wr
+n0vdkUsRk7TPTeRmxC9aNtUHAYv7PznW59b8Kt3eOC2datSCu/d4HLLZasuwk/ti
+7ILH4wnLJx1NQJ/luax74pHmFR/ws+OomzL0IXu+nVsB/rv4OuNVZ2R4BlTsKdma
+conhammBSvpgCwOAXnT8lZAY0z9jYZfWYqKFgTz3wj9kyocrlJZNshf5JULEK4KZ
+tbJaVhFMv4BQK2ONaWj3fzjSVKW/lqUvd1P0c2Of/x48RXQlkZu5l87K/NQ99Nr5
+VprqZiNgNWMlOlMs+I2ooDjjdHLq/g5tlgF/kK48
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem.certspec
new file mode 100644
index 0000000000..96314e51a7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v3-noBC.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ss-v3-noBC
+subject:ss-v3-noBC
+version:3
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem
new file mode 100644
index 0000000000..b00a723c6c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbagAwIBAwIUGR1OiiW80EkYCQaIVEB2xPm51J8wDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLc3MtdjQtQkMtY0EwIhgPMjAxOTExMjgwMDAwMDBaGA8y
+MDIyMDIwNTAwMDAwMFowFjEUMBIGA1UEAwwLc3MtdjQtQkMtY0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAO
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABE/8s/iuB1DHRKW7Odz
+uv1MVeVgCYeWfVGJVZ9w5dMVvmng8/7tkSffzVmuKdbvcfugQ6QzTAXFaPeMzFa/
+djSrxTghal5rH36truQrNZKHkcMxzDkD1G7FuEYomZVuODWhRIQ1mZWS/AnyVoA0
+B4Dr7u1ULdDqaaWroxZX0i9UG2QtDX6DAytzeNJwxgJ0UhnHJ3b4uRJN92rObzQ9
+HwBwfWwFGfrjnO/DGn60NxDr6zdjNmK8pgzmEjgGxPE9lHsOkF4skq/9XnyUMh5N
+41EXZL3PC7PwgG2nYeFEkUd0rP2HahxkBEIcO2iVWorqhfBWGLRQUzYtfd8V/5QU
+B90=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem.certspec
new file mode 100644
index 0000000000..54269184ed
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v4-BC-cA
+subject:ss-v4-BC-cA
+version:4
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem
new file mode 100644
index 0000000000..b5aa017f80
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAwIUISiwRjGQW0zDa8BcggTNw+Gw6EAwDQYJKoZIhvcNAQEL
+BQAwGjEYMBYGA1UEAwwPc3MtdjQtQkMtbm90LWNBMCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMBoxGDAWBgNVBAMMD3NzLXY0LUJDLW5vdC1jQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAAaMNMAswCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEAl20D6S5AemWE
+GJMgprIXINyeCpH7K2umFjmzoVk73HosNjOaPDZPgk+v4n8Wf2CVg21uU4U3v5LK
+1EHmNicbnI23fV/J/PPb1yy2YZ7+YPVolx8DwsxxYWH6N7aTGCdLwe6L44O91uQk
+zCXNXD/mHyRraYb1xPFLIg1GzzIz8LVHrax9V7Jz/u3TlhSeSRORaUbvJgrCm9nX
+V/7Ozc8czgtKfnTaeLz6791rK1my+gymoB01wbirqwxEzpWTlTFXUMCyzeLc/vuu
+L9+vHAWT4pglrPMRCa4GqGlmIdXpTj65kiYMTVHOdP8xKZL9q6jfDYLB1buYt+pz
+Nvh82338Bw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem.certspec
new file mode 100644
index 0000000000..3d65335316
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-BC-not-cA.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ss-v4-BC-not-cA
+subject:ss-v4-BC-not-cA
+version:4
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem
new file mode 100644
index 0000000000..275306dc28
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICujCCAaKgAwIBAwIUAWcyAUceQlTM7+BoK8oA8uak6q0wDQYJKoZIhvcNAQEL
+BQAwFTETMBEGA1UEAwwKc3MtdjQtbm9CQzAiGA8yMDE5MTEyODAwMDAwMFoYDzIw
+MjIwMjA1MDAwMDAwWjAVMRMwEQYDVQQDDApzcy12NC1ub0JDMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1
+aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/we
+adA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSS
+pH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62W
+YVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauR
+CE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqG
+SIb3DQEBCwUAA4IBAQCiyfMdjGJ6o4Yw2Q3L5QbFRBijGCTVn1547HXPibTewg8G
+Z0/2twX36MKwbUGvq/rWMhsKKMrdbf72W9pCvyPcRQKfxO2XFqfxUF8TDUI0DKw7
+Wj1+vmfsxu99bArAk6ESUgYoYHij+Y5OLIKjWYgqXQEOVx+/Ywtv4kQv+Hlc1kzg
+0H3rQcLboya8NxX6zNqO+l7xpKw+eoiEyF+hqjrjbAzKgbQbOA1t9ts1xlFcfTJ1
+36DqE4eWsP+lNBHPAOXSMXUtFEjgXw7UA73zOxWJUqnPRvKjqpMEE8P84bt7PoEX
+riT1RslyhX2zDCn4KhLzbqrHk+9hZQ+K+HB+E9r3
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem.certspec b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem.certspec
new file mode 100644
index 0000000000..d02e04de51
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_version/ss-v4-noBC.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ss-v4-noBC
+subject:ss-v4-noBC
+version:4
diff --git a/security/manager/ssl/tests/unit/test_constructX509FromBase64.js b/security/manager/ssl/tests/unit/test_constructX509FromBase64.js
new file mode 100644
index 0000000000..7a4f3915af
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_constructX509FromBase64.js
@@ -0,0 +1,90 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Checks that ConstructX509FromBase64() accepts valid input and rejects invalid
+// input.
+
+do_get_profile(); // Must be called before getting nsIX509CertDB
+const certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function excMessage(e) {
+ if (e.message) {
+ let msg = e.message;
+ if (e.data) {
+ msg = msg + ": " + e.data;
+ }
+ return msg;
+ }
+
+ return e.toString();
+}
+
+function testGood(data) {
+ try {
+ let cert = certDB.constructX509FromBase64(data.cert);
+ equal(
+ cert.commonName,
+ data.cn,
+ "Actual and expected commonName should match"
+ );
+ } catch (e) {
+ info(`Exception: ${excMessage(e)}`);
+ ok(false, `Should not have gotten an exception for "CN=${data.cn}"`);
+ }
+}
+
+function testBad(data) {
+ throws(
+ () => certDB.constructX509FromBase64(data.input),
+ data.result,
+ `Should get "${data.result}" for "${data.input}"`
+ );
+}
+
+function run_test() {
+ const badCases = [
+ // Wrong type or too short
+ { input: null, result: /NS_ERROR_ILLEGAL_VALUE/ },
+ { input: "", result: /NS_ERROR_ILLEGAL_VALUE/ },
+ { input: "=", result: /NS_ERROR_ILLEGAL_VALUE/ },
+ { input: "==", result: /NS_ERROR_ILLEGAL_VALUE/ },
+ // Not base64
+ { input: "forty-four dead stone lions", result: /NS_ERROR_ILLEGAL_VALUE/ },
+ // Not a cert
+ {
+ input: "Zm9ydHktZm91ciBkZWFkIHN0b25lIGxpb25z",
+ result: /NS_ERROR_FAILURE/,
+ },
+ ];
+
+ // Real certs with all three padding levels
+ const goodCases = [
+ {
+ cn: "A",
+ cert:
+ "MIHhMIGcAgEAMA0GCSqGSIb3DQEBBQUAMAwxCjAIBgNVBAMTAUEwHhcNMTEwMzIzMjMyNTE3WhcNMTEwNDIyMjMyNTE3WjAMMQowCAYDVQQDEwFBMEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxANFm7ZCfYNJViaDWTFuMClX3+9u18VFGiyLfM6xJrxir4QVtQC7VUC/WUGoBUs9COQIDAQABMA0GCSqGSIb3DQEBBQUAAzEAx2+gIwmuYjJO5SyabqIm4lB1MandHH1HQc0y0tUFshBOMESTzQRPSVwPn77a6R9t",
+ },
+ {
+ cn: "Bo",
+ cert:
+ "MIHjMIGeAgEAMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAMTAkJvMB4XDTExMDMyMzIzMjYwMloXDTExMDQyMjIzMjYwMlowDTELMAkGA1UEAxMCQm8wTDANBgkqhkiG9w0BAQEFAAM7ADA4AjEA1FoSl9w9HqMqVgk2K0J3OTiRsgHeNsQdPUl6S82ME33gH+E56PcWZA3nse+fpS3NAgMBAAEwDQYJKoZIhvcNAQEFBQADMQAo/e3BvQAmygiATljQ68tWPoWcbMwa1xxAvpWTEc1LOvMqeDBinBUqbAbSmPhGWb4=",
+ },
+ {
+ cn: "Cid",
+ cert:
+ "MIHlMIGgAgEAMA0GCSqGSIb3DQEBBQUAMA4xDDAKBgNVBAMTA0NpZDAeFw0xMTAzMjMyMzI2MzJaFw0xMTA0MjIyMzI2MzJaMA4xDDAKBgNVBAMTA0NpZDBMMA0GCSqGSIb3DQEBAQUAAzsAMDgCMQDUUxlF5xKN+8KCSsR83sN+SRwJmZdliXsnBB7PU0OgbmOWN0u8yehRkmu39kN9tzcCAwEAATANBgkqhkiG9w0BAQUFAAMxAJ3UScNqRcjHFrNu4nuwRldZLJlVJvRYXp982V4/kYodQEGN4gJ+Qyj+HTsaXy5x/w==",
+ },
+ ];
+
+ for (let badCase of badCases) {
+ testBad(badCase);
+ }
+ for (let goodCase of goodCases) {
+ testGood(goodCase);
+ }
+}
diff --git a/security/manager/ssl/tests/unit/test_content_signing.js b/security/manager/ssl/tests/unit/test_content_signing.js
new file mode 100644
index 0000000000..e96db850b9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing.js
@@ -0,0 +1,435 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// These tests ensure content signatures are working correctly.
+
+// First, we need to set up some data
+const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
+
+const TEST_DATA_DIR = "test_content_signing/";
+
+const ONECRL_NAME = "oneCRL-signer.mozilla.org";
+const ABOUT_NEWTAB_NAME = "remotenewtab.content-signature.mozilla.org";
+var VERIFICATION_HISTOGRAM = Services.telemetry.getHistogramById(
+ "CONTENT_SIGNATURE_VERIFICATION_STATUS"
+);
+var ERROR_HISTOGRAM = Services.telemetry.getKeyedHistogramById(
+ "CONTENT_SIGNATURE_VERIFICATION_ERRORS"
+);
+
+function getSignatureVerifier() {
+ return Cc["@mozilla.org/security/contentsignatureverifier;1"].getService(
+ Ci.nsIContentSignatureVerifier
+ );
+}
+
+function setRoot(filename) {
+ let cert = constructCertFromFile(filename);
+ Services.prefs.setCharPref(PREF_SIGNATURE_ROOT, cert.sha256Fingerprint);
+}
+
+function getCertHash(name) {
+ let cert = constructCertFromFile(`test_content_signing/${name}.pem`);
+ return cert.sha256Fingerprint.replace(/:/g, "");
+}
+
+function loadChain(prefix, names) {
+ let chain = [];
+ for (let name of names) {
+ let filename = `${prefix}_${name}.pem`;
+ chain.push(readFile(do_get_file(filename)));
+ }
+ return chain;
+}
+
+function check_telemetry(expected_index, expected, expectedId) {
+ for (let i = 0; i < 10; i++) {
+ let expected_value = 0;
+ if (i == expected_index) {
+ expected_value = expected;
+ }
+ let errorSnapshot = ERROR_HISTOGRAM.snapshot();
+ for (let k in errorSnapshot) {
+ // We clear the histogram every time so there should be only this one
+ // category.
+ equal(k, expectedId);
+ equal(errorSnapshot[k].values[i] || 0, expected_value);
+ }
+ equal(
+ VERIFICATION_HISTOGRAM.snapshot().values[i] || 0,
+ expected_value,
+ "count " +
+ i +
+ ": " +
+ VERIFICATION_HISTOGRAM.snapshot().values[i] +
+ " expected " +
+ expected_value
+ );
+ }
+ VERIFICATION_HISTOGRAM.clear();
+ ERROR_HISTOGRAM.clear();
+}
+
+add_task(async function run_test() {
+ // set up some data
+ const DATA = readFile(do_get_file(TEST_DATA_DIR + "test.txt"));
+ const GOOD_SIGNATURE =
+ "p384ecdsa=" +
+ readFile(do_get_file(TEST_DATA_DIR + "test.txt.signature")).trim();
+
+ const BAD_SIGNATURE =
+ "p384ecdsa=WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2r" +
+ "UWM4GJke4pE8ecHiXoi-7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1G" +
+ "q25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L";
+
+ let remoteNewTabChain = loadChain(TEST_DATA_DIR + "content_signing", [
+ "remote_newtab_ee",
+ "int",
+ "root",
+ ]);
+
+ let oneCRLChain = loadChain(TEST_DATA_DIR + "content_signing", [
+ "onecrl_ee",
+ "int",
+ "root",
+ ]);
+
+ let oneCRLBadKeyChain = loadChain(TEST_DATA_DIR + "content_signing", [
+ "onecrl_wrong_key_ee",
+ "int",
+ "root",
+ ]);
+
+ let noSANChain = loadChain(TEST_DATA_DIR + "content_signing", [
+ "onecrl_no_SAN_ee",
+ "int",
+ "root",
+ ]);
+
+ let expiredOneCRLChain = loadChain(TEST_DATA_DIR + "content_signing", [
+ "onecrl_ee_expired",
+ "int",
+ "root",
+ ]);
+
+ let notValidYetOneCRLChain = loadChain(TEST_DATA_DIR + "content_signing", [
+ "onecrl_ee_not_valid_yet",
+ "int",
+ "root",
+ ]);
+
+ // Check signature verification works without error before the root is set
+ VERIFICATION_HISTOGRAM.clear();
+ let chain1 = oneCRLChain.join("\n");
+ let verifier = getSignatureVerifier();
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ ONECRL_NAME
+ )),
+ "Before the root is set, signatures should fail to verify but not throw."
+ );
+ // Check for generic chain building error.
+ check_telemetry(6, 1, getCertHash("content_signing_onecrl_ee"));
+
+ setRoot(TEST_DATA_DIR + "content_signing_root.pem");
+
+ // Check good signatures from good certificates with the correct SAN
+ ok(
+ await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ ONECRL_NAME
+ ),
+ "A OneCRL signature should verify with the OneCRL chain"
+ );
+ let chain2 = remoteNewTabChain.join("\n");
+ ok(
+ await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain2,
+ ABOUT_NEWTAB_NAME
+ ),
+ "A newtab signature should verify with the newtab chain"
+ );
+ // Check for valid signature
+ check_telemetry(0, 2, getCertHash("content_signing_remote_newtab_ee"));
+
+ // Check a bad signature when a good chain is provided
+ chain1 = oneCRLChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ BAD_SIGNATURE,
+ chain1,
+ ONECRL_NAME
+ )),
+ "A bad signature should not verify"
+ );
+ // Check for invalid signature
+ check_telemetry(1, 1, getCertHash("content_signing_onecrl_ee"));
+
+ // Check a good signature from cert with good SAN but a different key than the
+ // one used to create the signature
+ let badKeyChain = oneCRLBadKeyChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ badKeyChain,
+ ONECRL_NAME
+ )),
+ "A signature should not verify if the signing key is wrong"
+ );
+ // Check for wrong key in cert.
+ check_telemetry(9, 1, getCertHash("content_signing_onecrl_wrong_key_ee"));
+
+ // Check a good signature from cert with good SAN but a different key than the
+ // one used to create the signature (this time, an RSA key)
+ let rsaKeyChain = oneCRLBadKeyChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ rsaKeyChain,
+ ONECRL_NAME
+ )),
+ "A signature should not verify if the signing key is wrong (RSA)"
+ );
+ // Check for wrong key in cert.
+ check_telemetry(9, 1, getCertHash("content_signing_onecrl_wrong_key_ee"));
+
+ // Check a good signature from cert with good SAN but with chain missing root
+ let missingRoot = [oneCRLChain[0], oneCRLChain[1]].join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ missingRoot,
+ ONECRL_NAME
+ )),
+ "A signature should not verify if the chain is incomplete (missing root)"
+ );
+ // Check for generic chain building error.
+ check_telemetry(6, 1, getCertHash("content_signing_onecrl_ee"));
+
+ // Check a good signature from cert with good SAN but with no path to root
+ let missingInt = [oneCRLChain[0], oneCRLChain[2]].join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ missingInt,
+ ONECRL_NAME
+ )),
+ "A signature should not verify if the chain is incomplete (missing int)"
+ );
+ // Check for generic chain building error.
+ check_telemetry(6, 1, getCertHash("content_signing_onecrl_ee"));
+
+ // Check good signatures from good certificates with the wrong SANs
+ chain1 = oneCRLChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ ABOUT_NEWTAB_NAME
+ )),
+ "A OneCRL signature should not verify if we require the newtab SAN"
+ );
+ // Check for invalid EE cert.
+ check_telemetry(7, 1, getCertHash("content_signing_onecrl_ee"));
+
+ chain2 = remoteNewTabChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain2,
+ ONECRL_NAME
+ )),
+ "A newtab signature should not verify if we require the OneCRL SAN"
+ );
+ // Check for invalid EE cert.
+ check_telemetry(7, 1, getCertHash("content_signing_remote_newtab_ee"));
+
+ // Check good signatures with good chains with some other invalid names
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ ""
+ )),
+ "A signature should not verify if the SANs do not match an empty name"
+ );
+ // Check for invalid EE cert.
+ check_telemetry(7, 1, getCertHash("content_signing_onecrl_ee"));
+
+ // Test expired certificate.
+ let chainExpired = expiredOneCRLChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chainExpired,
+ ""
+ )),
+ "A signature should not verify if the signing certificate is expired"
+ );
+ // Check for expired cert.
+ check_telemetry(4, 1, getCertHash("content_signing_onecrl_ee_expired"));
+
+ // Test not valid yet certificate.
+ let chainNotValidYet = notValidYetOneCRLChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chainNotValidYet,
+ ""
+ )),
+ "A signature should not verify if the signing certificate is not valid yet"
+ );
+ // Check for not yet valid cert.
+ check_telemetry(5, 1, getCertHash("content_signing_onecrl_ee_not_valid_yet"));
+
+ let relatedName = "subdomain." + ONECRL_NAME;
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ relatedName
+ )),
+ "A signature should not verify if the SANs do not match a related name"
+ );
+
+ let randomName =
+ "\xb1\x9bU\x1c\xae\xaa3\x19H\xdb\xed\xa1\xa1\xe0\x81\xfb" +
+ "\xb2\x8f\x1cP\xe5\x8b\x9c\xc2s\xd3\x1f\x8e\xbbN";
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ randomName
+ )),
+ "A signature should not verify if the SANs do not match a random name"
+ );
+
+ // check good signatures with chains that have strange or missing SANs
+ chain1 = noSANChain.join("\n");
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ ONECRL_NAME
+ )),
+ "A signature should not verify if the SANs do not match a supplied name"
+ );
+
+ // Check malformed signature data
+ chain1 = oneCRLChain.join("\n");
+ let bad_signatures = [
+ // wrong length
+ "p384ecdsa=WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2rUWM4GJke4pE8ecHiXoi-" +
+ "7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1Gq25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L==",
+ // incorrectly encoded
+ "p384ecdsa='WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2rUWM4GJke4pE8ecHiXoi" +
+ "-7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1Gq25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L=",
+ // missing directive
+ "other_directive=WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2rUWM4GJke4pE8ec" +
+ "HiXoi-7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1Gq25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L",
+ // actually sha256 with RSA
+ "p384ecdsa=XS_jiQsS5qlzQyUKaA1nAnQn_OvxhvDfKybflB8Xe5gNH1wNmPGK1qN-jpeTfK" +
+ "6ob3l3gCTXrsMnOXMeht0kPP3wLfVgXbuuO135pQnsv0c-ltRMWLe56Cm4S4Z6E7WWKLPWaj" +
+ "jhAcG5dZxjffP9g7tuPP4lTUJztyc4d1z_zQZakEG7R0vN7P5_CaX9MiMzP4R7nC3H4Ba6yi" +
+ "yjlGvsZwJ_C5zDQzWWs95czUbMzbDScEZ_7AWnidw91jZn-fUK3xLb6m-Zb_b4GAqZ-vnXIf" +
+ "LpLB1Nzal42BQZn7i4rhAldYdcVvy7rOMlsTUb5Zz6vpVW9LCT9lMJ7Sq1xbU-0g==",
+ ];
+ for (let badSig of bad_signatures) {
+ await Assert.rejects(
+ verifier.asyncVerifyContentSignature(DATA, badSig, chain1, ONECRL_NAME),
+ /NS_ERROR/,
+ `Bad or malformed signature "${badSig}" should be rejected`
+ );
+ }
+
+ // Check malformed and missing certificate chain data
+ let chainSuffix = [oneCRLChain[1], oneCRLChain[2]].join("\n");
+ let badChains = [
+ // no data
+ "",
+ // completely wrong data
+ "blah blah \n blah",
+ ];
+
+ let badSections = [
+ // data that looks like PEM but isn't
+ "-----BEGIN CERTIFICATE-----\nBSsPRlYp5+gaFMRIczwUzaioRfteCjr94xyz0g==\n",
+ // data that will start to parse but won't base64decode
+ "-----BEGIN CERTIFICATE-----\nnon-base64-stuff\n-----END CERTIFICATE-----",
+ // data with garbage outside of PEM sections
+ "this data is garbage\n-----BEGIN CERTIFICATE-----\nnon-base64-stuff\n" +
+ "-----END CERTIFICATE-----",
+ ];
+
+ for (let badSection of badSections) {
+ // ensure we test each bad section on its own...
+ badChains.push(badSection);
+ // ... and as part of a chain with good certificates
+ badChains.push(badSection + "\n" + chainSuffix);
+ }
+
+ for (let badChain of badChains) {
+ await Assert.rejects(
+ verifier.asyncVerifyContentSignature(
+ DATA,
+ GOOD_SIGNATURE,
+ badChain,
+ ONECRL_NAME
+ ),
+ /NS_ERROR/,
+ `Bad chain data starting "${badChain.substring(0, 80)}" ` +
+ "should be rejected"
+ );
+ }
+
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA + "appended data",
+ GOOD_SIGNATURE,
+ chain1,
+ ONECRL_NAME
+ )),
+ "A good signature should not verify if the data is tampered with (append)"
+ );
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ "prefixed data" + DATA,
+ GOOD_SIGNATURE,
+ chain1,
+ ONECRL_NAME
+ )),
+ "A good signature should not verify if the data is tampered with (prefix)"
+ );
+ ok(
+ !(await verifier.asyncVerifyContentSignature(
+ DATA.replace(/e/g, "i"),
+ GOOD_SIGNATURE,
+ chain1,
+ ONECRL_NAME
+ )),
+ "A good signature should not verify if the data is tampered with (modify)"
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem
new file mode 100644
index 0000000000..e3d6133226
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1TCCAb2gAwIBAgIUfypncvlf0ZBX1MQ7mXPauUSiPVMwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowETEPMA0GA1UEAwwGaW50LUNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/
+MBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQA/uUqk6o66
+Hfkdkn/XZA7Wszvsz4k+eQEf/0Y15EqvjGPm1pS1qj/aQXhKqwA6kDaIP8urgUOu
+53ikXeiSE1LDFYHlduem8MgHg7tJ0QjzSAzdgjslHUuF0umbemwnXAbwGYfMlJOQ
+Pc2898QJoev+6YWC0Mb3nV86X0NwH9BTKVCqaxrQi0edqndxKv6VggfAyhPaw+TN
+jO8z3Hvv9APY184Spg/ndPhZPJ3hGCve8eus0hRhpSoRpGH6cmCw0IedUVhDpR1R
+StT5bZ2/XEGaKFGEIjYKzrreMIgdOfNQlsBB1iD/40qi+Mi1/m9ctYJv2RJaEjpw
+gu6XceM2TJaJ
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem.certspec
new file mode 100644
index 0000000000..4134a73c6f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-CA
+extension:basicConstraints:cA,
+extension:extKeyUsage:codeSigning
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem
new file mode 100644
index 0000000000..0908caae85
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIUETmtBD+18aLFBIz4293Yn084a+MwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBExDzANBgNVBAMMBmVlLVJTQTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaM9MDswEwYDVR0lBAww
+CgYIKwYBBQUHAwMwJAYDVR0RBB0wG4IZb25lQ1JMLXNpZ25lci5tb3ppbGxhLm9y
+ZzANBgkqhkiG9w0BAQsFAAOCAQEAEnk3u7e+n8mGwAbfy3Wj3RiBhXkJEN31ClBG
+/NsXUeRBv89rPb2m8DyLBcEWvyNgQ0woyUPmrP5vsIUYQ64mshWuLgSzEk5/lY2+
+Quro14NKCTWo4F/DNgra5lq7Q3S8tXhmM+mBlsxK5QN2kWQM0ODlZtMp3P9821DH
+OqaRTDvqd9dO2lFwlZ3vW5w5Uq/HQxciSkea4D2l1PcoWaqI7Zze66jj3H5kBoMY
+SN4n9+RqPXUyBuF1A0d3W/jIzSnW7f3F6Gwe8fzhNsiyVE71tRHNForx4KtDR6BG
+lyapz8MMoaTCgaS6gbv6RshJkRusqBQxvBY6JVgYBbkXw/IWBw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem.certspec
new file mode 100644
index 0000000000..7b2a02bdaf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-CA
+subject:ee-RSA
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem
new file mode 100644
index 0000000000..7dba148b87
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICPzCCASegAwIBAgIUKVuGEG5eiGwlJfVnv/ULDdeFL0gwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMA0xCzAJBgNVBAMMAmVlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE
+oWhyQzYrXHsYifN5FUYVocc/tI3uhj4CKRXbYI4lLeS3Ey2ozpjoMVNOapwMCwnI
+1jmt6DIG5bqBNHOhH6Mw4F2oyW5Dg/4nhz2pcQO+KIjP8ALwWvcaH93Mg3SqbqnO
+oz0wOzATBgNVHSUEDDAKBggrBgEFBQcDAzAkBgNVHREEHTAbghlvbmVDUkwtc2ln
+bmVyLm1vemlsbGEub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQA/Lumx62bij+HfehIY
+6+jXkePuTOsaRpL1W5jk3ai78rWYRAVzDgM389kTBYJYCViHglgZQmQUeU8+sS46
+L/PO17BboOsCgqZt5QPSGPqSFvmqFgNOT2bl6rKtg6F5pM+orlCU816noIOCFOTJ
+AHQHvK6qcy6SBGOn7BqvNMsRhGc7zqn2tTMt0FzjO7ULroR68aOLDCTzTgiDpz3A
+DELgbpWFFE+W8flGnO1NRXwQDX3kEVlgVlOGrw5RsryE9HTYFhV4KXymxluKjL7C
+oJFn115lsgbmbK38DKhjU4Sk0EHgFjC3K28fGaYtIBWoHAG3XGu1sBhWNCGCWqVH
+Q7un
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem.certspec
new file mode 100644
index 0000000000..ab22807b9e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int-CA
+subject:ee
+subjectKey:secp384r1
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem
new file mode 100644
index 0000000000..ef22669f4d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICTjCCATagAwIBAgIUOQNrYQz01j0SirgoHMLKbtGL9RowDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwMTMwMTAxMDAwMDAwWhgPMjAxNDAx
+MDEwMDAwMDBaMBwxGjAYBgNVBAMMEWVlLWludC1DQS1leHBpcmVkMHYwEAYHKoZI
+zj0CAQYFK4EEACIDYgAEoWhyQzYrXHsYifN5FUYVocc/tI3uhj4CKRXbYI4lLeS3
+Ey2ozpjoMVNOapwMCwnI1jmt6DIG5bqBNHOhH6Mw4F2oyW5Dg/4nhz2pcQO+KIjP
+8ALwWvcaH93Mg3SqbqnOoz0wOzATBgNVHSUEDDAKBggrBgEFBQcDAzAkBgNVHREE
+HTAbghlvbmVDUkwtc2lnbmVyLm1vemlsbGEub3JnMA0GCSqGSIb3DQEBCwUAA4IB
+AQBZJPo4llgMe5588+BnRLnFguspIiwMWmTeqCfi8VQBx/tUwRiTizbU7J2Yh9bo
+yZEPKfPSP2o8J0eSUgvXdVOxU1fNRuocsVfXUlveq5x10ddjXBT9X4AY1mtR7HJw
+hl/7269N8b4itfrfvZmCBToJayjv0I2N84bqjpOnXJ/iB5YVdk8oZIJDXWi4SR3B
+E9IejwA1fikpt++RjpJSZ1BSNU7FfiyGGUonxHDoP/29znaOJnpAqaH5LVJCRkfN
+H12vePBbunZd+ay5r+mMJPaXR+V2sY8OaOfcrPSHQLa8Eb/EEhBuITMKkOucohjx
+zqvM6S2iOI9GbwHClybEHRO7
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem.certspec
new file mode 100644
index 0000000000..48fd9c8cc7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_expired.pem.certspec
@@ -0,0 +1,6 @@
+issuer:int-CA
+subject:ee-int-CA-expired
+subjectKey:secp384r1
+validity:20130101-20140101
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem
new file mode 100644
index 0000000000..6cf5c6a312
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVDCCATygAwIBAgIUbV+rBAfhGRv/bU22A92xneoAy3owDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwNTAwMTAxMDAwMDAwWhgPMjA1MTAx
+MDEwMDAwMDBaMCIxIDAeBgNVBAMMF2VlLWludC1DQS1ub3QteWV0LXZhbGlkMHYw
+EAYHKoZIzj0CAQYFK4EEACIDYgAEoWhyQzYrXHsYifN5FUYVocc/tI3uhj4CKRXb
+YI4lLeS3Ey2ozpjoMVNOapwMCwnI1jmt6DIG5bqBNHOhH6Mw4F2oyW5Dg/4nhz2p
+cQO+KIjP8ALwWvcaH93Mg3SqbqnOoz0wOzATBgNVHSUEDDAKBggrBgEFBQcDAzAk
+BgNVHREEHTAbghlvbmVDUkwtc2lnbmVyLm1vemlsbGEub3JnMA0GCSqGSIb3DQEB
+CwUAA4IBAQAjXmLNn2kLa/FzNp7F3PqcSXuAO2jT31Y2g4pZnVqCDfMqplsl2ZFn
+oam3wyQnepm3q9DD4BOAW9JFYR3wqnl9cBRNHlSGyjGM4qBpuSD6WxAz7EdFcRO6
+fcA50245fAuB45UJeYJ58QvIBv7AwoBGnqAI7ZDN3eIGopZIL56jiH7vO9WyQPWj
+XZAWrXTG68rEf0RxXRtjUv9coFiuInT8+oyXB3NwK2EbaI5IeR+x3qIDEgNKk+t+
+PlE3NrtaAiK19p0s9RtQQilBKNmo+5irrUq/OD2H1aurDaAXpLTM5vLUpfyN3/qD
+HzuZujaUIeMsRiXsIRDNql1S+nq4oNRy
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem.certspec
new file mode 100644
index 0000000000..b2926dfc42
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee_not_valid_yet.pem.certspec
@@ -0,0 +1,6 @@
+issuer:int-CA
+subject:ee-int-CA-not-yet-valid
+subjectKey:secp384r1
+validity:20500101-20510101
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem
new file mode 100644
index 0000000000..defa2dca5d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICIDCCAQigAwIBAgIUQQefsZHHyGIzVRjWiIp+2Sojjt8wDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBQxEjAQBgNVBAMMCWVlLW5vLVNBTjB2MBAGByqGSM49AgEGBSuB
+BAAiA2IABKFockM2K1x7GInzeRVGFaHHP7SN7oY+AikV22COJS3ktxMtqM6Y6DFT
+TmqcDAsJyNY5regyBuW6gTRzoR+jMOBdqMluQ4P+J4c9qXEDviiIz/AC8Fr3Gh/d
+zIN0qm6pzqMXMBUwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQAD
+ggEBAKT/ZTZsIyuxQFbK8CcN/DwFVxYfSDVLxDdv1hytINxuAHjpOF2kkcxHvUDf
+pjXG6pMSFip2+dJp61qDrDzeb68K0bzYjGvalDbv9a8JDKUI+mhGTfm7wh2ZmxRm
+SIZEP0fQ9TVB+h29bYqdBaanfWBAcvWKTKDIYE2visis8Jo29fBoeK6TC0fZpzzP
+Y5tNZgbwHvMWW39lqTZj0FtKAN7jdRTM0O/KYNAqlbzmtf9gSDNCklxFLRZRlbqO
+Z42M6FRc8k2ctbeFAGLZTF7ezolx8HlAUVbaptJPux7oj3dsCiq73aXExDPtiZj6
+9IX27OM4XsJhpS+RmI8GWqVSULI=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem.certspec
new file mode 100644
index 0000000000..4a9b9a3ceb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_no_SAN_ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-CA
+subject:ee-no-SAN
+subjectKey:secp384r1
+extension:extKeyUsage:codeSigning
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem
new file mode 100644
index 0000000000..ce04094e82
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICLDCCARSgAwIBAgIUeYPUd9Pk1MZC6Pv1MwwnCAybLMQwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBcxFTATBgNVBAMMDGVlLXdyb25nLWtleTBZMBMGByqGSM49AgEG
+CCqGSM49AwEHA0IABE+/u7th4Pj5saYKWayHBOLsBQtCPjz3LpI/LE95S0VcKmnS
+M0VsNsQRnQcG4A7tyNGTkNeZG3stB6ME6qBKpsCjPTA7MBMGA1UdJQQMMAoGCCsG
+AQUFBwMDMCQGA1UdEQQdMBuCGW9uZUNSTC1zaWduZXIubW96aWxsYS5vcmcwDQYJ
+KoZIhvcNAQELBQADggEBAAsDCFfikCk7qQayhqET9q5qHWiXv+0NuLW9j3zpqwQY
+uRXfiY34JsSpNtR+AVICUcierAoKfqXe6HTtDtQfjef8viCaer40dfLI9P5gx1l7
+nwybFl0oRNSBo6CZTcLHEdWHXbQvogj27wt75pRP66e9hFnH9FdOqp5WtP1nBVJr
+wF8tdRZHHSLlsrLPtCBXMBtZxCBB6grqeIiuP5qz/+ayRvv+be4D6rqJrizHYgnW
+q4y0pxHZzOYwrv+cqzRYaVOlwH0oU6mm0d8QDiry4yIGdNSOtO8/a8cdmbQakFXI
+XK62f8bnkuYvn6wp/81lyK5DzFQLVobPnNnwloCyjII=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem.certspec
new file mode 100644
index 0000000000..fbc8e603f4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int-CA
+subject:ee-wrong-key
+subjectKey:secp256r1
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem
new file mode 100644
index 0000000000..a66b345a72
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICUDCCATigAwIBAgIUMX7ouKJot3CcYRs5Frqu4NZFy/wwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGaW50LUNBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMA0xCzAJBgNVBAMMAmVlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE
+oWhyQzYrXHsYifN5FUYVocc/tI3uhj4CKRXbYI4lLeS3Ey2ozpjoMVNOapwMCwnI
+1jmt6DIG5bqBNHOhH6Mw4F2oyW5Dg/4nhz2pcQO+KIjP8ALwWvcaH93Mg3SqbqnO
+o04wTDATBgNVHSUEDDAKBggrBgEFBQcDAzA1BgNVHREELjAsgipyZW1vdGVuZXd0
+YWIuY29udGVudC1zaWduYXR1cmUubW96aWxsYS5vcmcwDQYJKoZIhvcNAQELBQAD
+ggEBAAgkFNZeQNsGbNCAAWxVpWDdSgvCOYemduVhNJQTLZiZJkDia6hR+tgGGcoB
+dkCOjK38ipFeTttdeGTAGW7XwfM7ViB41sRTZnmkuPfAbiQfd4PjZQQ/KqfWipsZ
+UQbsXnoCNA9OfeE3RHK+79n8F+Wx7l0ISskYfMxoFE2HF42FuaZ0zh3CnLKQG+gn
+l8tiB1a1XgSBRNvXIgpqbyKD5YlHyhj7mW9SkhKfaFRnmktpQWqpmxczRFRNpMnd
+BcZRsmkQ+Kye+blgiBLudWrU18yDArYDHWD8ObhlVq9DSBqq1ClWdhxmMUxE95D0
+Bare3XT1RUSTBIuSH9gJFiiUpUw=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec
new file mode 100644
index 0000000000..81e1eefe1d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int-CA
+subject:ee
+subjectKey:secp384r1
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:remotenewtab.content-signature.mozilla.org
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem b/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem
new file mode 100644
index 0000000000..d35153e96d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUG3upUKWDjbYJ8e8IBkIMf7SrvMMwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjJTAjMAwGA1UdEwQFMAMBAf8wEwYD
+VR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAKaz+RKqMBowh2H1
+HBun9kma1zWzkKb4ZNjke2eza3NDWCcDipnpYGB+iNhtzWPc2SA53ZF/yn/xDpWW
+zF0UyKsswTxRIS/FmOkXqqTu0bHU7kmdIb+cqSuuDZDshdONGRDspQD/aYPva499
+eKc/X9E96ICCyTAVaL0YQz6UMjPgiTrrP1O5cpYb2uGbny5+okw/Ir3rlKhw0SGb
+QqHuXmixawRj3PNzO0B63B0HsMdn+hbybHaZIkgNFrZ4pRmjnbiywMQNsvFzETZc
+mgX9vf9puBLgn/3qBpJrObuZ4xux4stfBumKXWQ2S/7HaGn8W1mjOV82TQSCvx4X
+Re529ME=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem.certspec b/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem.certspec
new file mode 100644
index 0000000000..f5e387b0c4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
+extension:extKeyUsage:codeSigning
diff --git a/security/manager/ssl/tests/unit/test_content_signing/moz.build b/security/manager/ssl/tests/unit/test_content_signing/moz.build
new file mode 100644
index 0000000000..0e09e729e5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'content_signing_int.pem',
+# 'content_signing_onecrl_RSA_ee.pem',
+# 'content_signing_onecrl_ee.pem',
+# 'content_signing_onecrl_ee_expired.pem',
+# 'content_signing_onecrl_ee_not_valid_yet.pem',
+# 'content_signing_onecrl_no_SAN_ee.pem',
+# 'content_signing_onecrl_wrong_key_ee.pem',
+# 'content_signing_remote_newtab_ee.pem',
+# 'content_signing_root.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_content_signing/pysign.py b/security/manager/ssl/tests/unit/test_content_signing/pysign.py
new file mode 100644
index 0000000000..2d04cc40ff
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/pysign.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Create an ECDSA signature on the P-384 curve using the SHA-384 hash of data from
+stdin. The key used for the signature is the secp384r1Encoded key used in pykey
+and pycert.
+
+The certificates for the content signature tests make use of this program.
+You can use pysign.py like this:
+
+cat test.txt | python pysign.py > test.txt.signature
+"""
+
+import base64
+import binascii
+import hashlib
+import os
+import six
+import sys
+
+import ecdsa
+
+# For pykey
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+import pykey
+
+data = sys.stdin.buffer.read()
+
+key = pykey.ECCKey("secp384r1")
+sig = key.signRaw(b"Content-Signature:\00" + data, pykey.HASH_SHA384)
+print base64.b64encode(sig).replace("+", "-").replace("/", "_")
diff --git a/security/manager/ssl/tests/unit/test_content_signing/test.txt b/security/manager/ssl/tests/unit/test_content_signing/test.txt
new file mode 100644
index 0000000000..2daac1cb00
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/test.txt
@@ -0,0 +1 @@
+This is a test file to test content-signature verification with a PKI.
diff --git a/security/manager/ssl/tests/unit/test_content_signing/test.txt.signature b/security/manager/ssl/tests/unit/test_content_signing/test.txt.signature
new file mode 100644
index 0000000000..e613981473
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/test.txt.signature
@@ -0,0 +1 @@
+hSvmvvA7_QLedDsjRJGBevqLwjPILx1EtWSPP4A0fepaWWPuuZRB8VfDT2j07bKDacRsbmJjmvg_R4CpKmnoWF8-2w5lSszlFFDqYSvQVQxpKhu-HMM_qquu_l0KecQ2
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters.js b/security/manager/ssl/tests/unit/test_crlite_filters.js
new file mode 100644
index 0000000000..17d65773cb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters.js
@@ -0,0 +1,697 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests that CRLite filter downloading works correctly.
+
+"use strict";
+do_get_profile(); // must be called before getting nsIX509CertDB
+
+const { RemoteSettings } = ChromeUtils.import(
+ "resource://services-settings/remote-settings.js"
+);
+const { RemoteSecuritySettings } = ChromeUtils.import(
+ "resource://gre/modules/psm/RemoteSecuritySettings.jsm"
+);
+const { TestUtils } = ChromeUtils.import(
+ "resource://testing-common/TestUtils.jsm"
+);
+
+const {
+ CRLiteFiltersClient,
+ IntermediatePreloadsClient,
+} = RemoteSecuritySettings.init();
+
+const CRLITE_FILTERS_ENABLED_PREF =
+ "security.remote_settings.crlite_filters.enabled";
+const INTERMEDIATES_ENABLED_PREF =
+ "security.remote_settings.intermediates.enabled";
+const INTERMEDIATES_DL_PER_POLL_PREF =
+ "security.remote_settings.intermediates.downloads_per_poll";
+
+function getHashCommon(aStr, useBase64) {
+ let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hasher.init(Ci.nsICryptoHash.SHA256);
+ let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
+ Ci.nsIStringInputStream
+ );
+ stringStream.data = aStr;
+ hasher.updateFromStream(stringStream, -1);
+
+ return hasher.finish(useBase64);
+}
+
+// Get a hexified SHA-256 hash of the given string.
+function getHash(aStr) {
+ return hexify(getHashCommon(aStr, false));
+}
+
+// Get the name of the file in the test directory to serve as the attachment
+// for the given filter.
+function getFilenameForFilter(filter) {
+ if (filter.type == "full") {
+ return "20201017-0-filter";
+ }
+ if (filter.id == "0001") {
+ return "20201017-1-filter.stash";
+ }
+ // The addition of another stash file was written more than a month after
+ // other parts of this test. As such, the second stash file for October 17th,
+ // 2020 was not readily available. Since the structure of stash files don't
+ // depend on each other, though, any two stash files are compatible, and so
+ // this stash from December 1st is used instead.
+ return "20201201-3-filter.stash";
+}
+
+/**
+ * Simulate a Remote Settings synchronization by filling up the local data with
+ * fake records.
+ *
+ * @param {*} filters List of filters for which we will create records.
+ * @param {Boolean} clear Whether or not to clear the local DB first. Defaults
+ * to true.
+ */
+async function syncAndDownload(filters, clear = true) {
+ const localDB = await CRLiteFiltersClient.client.db;
+ if (clear) {
+ await localDB.clear();
+ }
+
+ for (let filter of filters) {
+ const filename = getFilenameForFilter(filter);
+ const file = do_get_file(`test_crlite_filters/${filename}`);
+ const fileBytes = readFile(file);
+
+ const record = {
+ details: {
+ name: `${filter.timestamp}-${filter.type}`,
+ },
+ attachment: {
+ hash: getHash(fileBytes),
+ size: fileBytes.length,
+ filename,
+ location: `security-state-workspace/cert-revocations/test_crlite_filters/${filename}`,
+ mimetype: "application/octet-stream",
+ },
+ incremental: filter.type == "diff",
+ effectiveTimestamp: new Date(filter.timestamp).getTime(),
+ parent: filter.type == "diff" ? filter.parent : undefined,
+ id: filter.id,
+ };
+
+ await localDB.create(record);
+ }
+ // This promise will wait for the end of downloading.
+ let promise = TestUtils.topicObserved(
+ "remote-security-settings:crlite-filters-downloaded"
+ );
+ // Simulate polling for changes, trigger the download of attachments.
+ Services.obs.notifyObservers(null, "remote-settings:changes-poll-end");
+ let results = await promise;
+ return results[1]; // topicObserved gives back a 2-array
+}
+
+add_task(async function test_crlite_filters_disabled() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, false);
+
+ let result = await syncAndDownload([
+ { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" },
+ ]);
+ equal(result, "disabled", "CRLite filter download should not have run");
+});
+
+add_task(async function test_crlite_no_filters() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([]);
+ equal(
+ result,
+ "unavailable",
+ "CRLite filter download should have run, but nothing was available"
+ );
+});
+
+add_task(async function test_crlite_only_incremental_filters() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([
+ {
+ timestamp: "2019-01-01T06:00:00Z",
+ type: "diff",
+ id: "0001",
+ parent: "0000",
+ },
+ {
+ timestamp: "2019-01-01T18:00:00Z",
+ type: "diff",
+ id: "0002",
+ parent: "0001",
+ },
+ {
+ timestamp: "2019-01-01T12:00:00Z",
+ type: "diff",
+ id: "0003",
+ parent: "0002",
+ },
+ ]);
+ equal(
+ result,
+ "unavailable",
+ "CRLite filter download should have run, but no full filters were available"
+ );
+});
+
+add_task(async function test_crlite_incremental_filters_with_wrong_parent() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([
+ { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" },
+ {
+ timestamp: "2019-01-01T06:00:00Z",
+ type: "diff",
+ id: "0001",
+ parent: "0000",
+ },
+ {
+ timestamp: "2019-01-01T12:00:00Z",
+ type: "diff",
+ id: "0003",
+ parent: "0002",
+ },
+ {
+ timestamp: "2019-01-01T18:00:00Z",
+ type: "diff",
+ id: "0004",
+ parent: "0003",
+ },
+ ]);
+ let [status, filters] = result.split(";");
+ equal(status, "finished", "CRLite filter download should have run");
+ let filtersSplit = filters.split(",");
+ deepEqual(
+ filtersSplit,
+ ["2019-01-01T00:00:00Z-full", "2019-01-01T06:00:00Z-diff"],
+ "Should have downloaded the expected CRLite filters"
+ );
+});
+
+add_task(async function test_crlite_incremental_filter_too_early() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([
+ { timestamp: "2019-01-02T00:00:00Z", type: "full", id: "0000" },
+ {
+ timestamp: "2019-01-01T00:00:00Z",
+ type: "diff",
+ id: "0001",
+ parent: "0000",
+ },
+ ]);
+ equal(
+ result,
+ "finished;2019-01-02T00:00:00Z-full",
+ "CRLite filter download should have run"
+ );
+});
+
+add_task(async function test_crlite_filters_basic() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([
+ { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" },
+ ]);
+ equal(
+ result,
+ "finished;2019-01-01T00:00:00Z-full",
+ "CRLite filter download should have run"
+ );
+});
+
+add_task(async function test_crlite_filters_full_and_incremental() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([
+ // These are deliberately listed out of order.
+ {
+ timestamp: "2019-01-01T06:00:00Z",
+ type: "diff",
+ id: "0001",
+ parent: "0000",
+ },
+ { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" },
+ {
+ timestamp: "2019-01-01T18:00:00Z",
+ type: "diff",
+ id: "0003",
+ parent: "0002",
+ },
+ {
+ timestamp: "2019-01-01T12:00:00Z",
+ type: "diff",
+ id: "0002",
+ parent: "0001",
+ },
+ ]);
+ let [status, filters] = result.split(";");
+ equal(status, "finished", "CRLite filter download should have run");
+ let filtersSplit = filters.split(",");
+ deepEqual(
+ filtersSplit,
+ [
+ "2019-01-01T00:00:00Z-full",
+ "2019-01-01T06:00:00Z-diff",
+ "2019-01-01T12:00:00Z-diff",
+ "2019-01-01T18:00:00Z-diff",
+ ],
+ "Should have downloaded the expected CRLite filters"
+ );
+});
+
+add_task(async function test_crlite_filters_multiple_days() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([
+ // These are deliberately listed out of order.
+ {
+ timestamp: "2019-01-02T06:00:00Z",
+ type: "diff",
+ id: "0011",
+ parent: "0010",
+ },
+ {
+ timestamp: "2019-01-03T12:00:00Z",
+ type: "diff",
+ id: "0022",
+ parent: "0021",
+ },
+ {
+ timestamp: "2019-01-02T12:00:00Z",
+ type: "diff",
+ id: "0012",
+ parent: "0011",
+ },
+ {
+ timestamp: "2019-01-03T18:00:00Z",
+ type: "diff",
+ id: "0023",
+ parent: "0022",
+ },
+ {
+ timestamp: "2019-01-02T18:00:00Z",
+ type: "diff",
+ id: "0013",
+ parent: "0012",
+ },
+ { timestamp: "2019-01-02T00:00:00Z", type: "full", id: "0010" },
+ { timestamp: "2019-01-03T00:00:00Z", type: "full", id: "0020" },
+ {
+ timestamp: "2019-01-01T06:00:00Z",
+ type: "diff",
+ id: "0001",
+ parent: "0000",
+ },
+ {
+ timestamp: "2019-01-01T18:00:00Z",
+ type: "diff",
+ id: "0003",
+ parent: "0002",
+ },
+ {
+ timestamp: "2019-01-01T12:00:00Z",
+ type: "diff",
+ id: "0002",
+ parent: "0001",
+ },
+ { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" },
+ {
+ timestamp: "2019-01-03T06:00:00Z",
+ type: "diff",
+ id: "0021",
+ parent: "0020",
+ },
+ ]);
+ let [status, filters] = result.split(";");
+ equal(status, "finished", "CRLite filter download should have run");
+ let filtersSplit = filters.split(",");
+ deepEqual(
+ filtersSplit,
+ [
+ "2019-01-03T00:00:00Z-full",
+ "2019-01-03T06:00:00Z-diff",
+ "2019-01-03T12:00:00Z-diff",
+ "2019-01-03T18:00:00Z-diff",
+ ],
+ "Should have downloaded the expected CRLite filters"
+ );
+});
+
+function getCRLiteEnrollmentRecordFor(nsCert) {
+ let { subjectString, spkiHashString } = getSubjectAndSPKIHash(nsCert);
+ return {
+ subjectDN: btoa(subjectString),
+ pubKeyHash: spkiHashString,
+ crlite_enrolled: true,
+ };
+}
+
+add_task(async function test_crlite_filters_and_check_revocation() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+ Services.prefs.setIntPref(
+ "security.pki.crlite_mode",
+ CRLiteModeEnforcePrefValue
+ );
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let issuerCert = constructCertFromFile("test_crlite_filters/issuer.pem");
+ let noSCTCertIssuer = constructCertFromFile(
+ "test_crlite_filters/no-sct-issuer.pem"
+ );
+
+ let crliteEnrollmentRecords = [
+ getCRLiteEnrollmentRecordFor(issuerCert),
+ getCRLiteEnrollmentRecordFor(noSCTCertIssuer),
+ ];
+
+ await IntermediatePreloadsClient.onSync({
+ data: {
+ current: crliteEnrollmentRecords,
+ created: crliteEnrollmentRecords,
+ updated: [],
+ deleted: [],
+ },
+ });
+
+ let result = await syncAndDownload([
+ { timestamp: "2020-10-17T00:00:00Z", type: "full", id: "0000" },
+ ]);
+ equal(
+ result,
+ "finished;2020-10-17T00:00:00Z-full",
+ "CRLite filter download should have run"
+ );
+
+ let validCert = constructCertFromFile("test_crlite_filters/valid.pem");
+ // NB: by not specifying Ci.nsIX509CertDB.FLAG_LOCAL_ONLY, this tests that
+ // the implementation does not fall back to OCSP fetching, because if it
+ // did, the implementation would attempt to connect to a server outside the
+ // test infrastructure, which would result in a crash in the test
+ // environment, which would be treated as a test failure.
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "vpn.worldofspeed.org",
+ 0
+ );
+
+ let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem");
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "us-datarecovery.com",
+ 0
+ );
+
+ // Before any stashes are downloaded, this should verify successfully.
+ let revokedInStashCert = constructCertFromFile(
+ "test_crlite_filters/revoked-in-stash.pem"
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedInStashCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "stokedmoto.com",
+ 0
+ );
+
+ result = await syncAndDownload(
+ [
+ {
+ timestamp: "2020-10-17T03:00:00Z",
+ type: "diff",
+ id: "0001",
+ parent: "0000",
+ },
+ ],
+ false
+ );
+ equal(
+ result,
+ "finished;2020-10-17T03:00:00Z-diff",
+ "Should have downloaded the expected CRLite filters"
+ );
+
+ // After downloading the first stash, this should be revoked.
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedInStashCert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "stokedmoto.com",
+ 0
+ );
+
+ // Before downloading the second stash, this should not be revoked.
+ let revokedInStash2Cert = constructCertFromFile(
+ "test_crlite_filters/revoked-in-stash-2.pem"
+ );
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedInStash2Cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "icsreps.com",
+ 0
+ );
+
+ result = await syncAndDownload(
+ [
+ {
+ timestamp: "2020-10-17T06:00:00Z",
+ type: "diff",
+ id: "0002",
+ parent: "0001",
+ },
+ ],
+ false
+ );
+ equal(
+ result,
+ "finished;2020-10-17T06:00:00Z-diff",
+ "Should have downloaded the expected CRLite filters"
+ );
+
+ // After downloading the second stash, this should be revoked.
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedInStash2Cert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "icsreps.com",
+ 0
+ );
+
+ // The other certificates should still get the same results as they did before.
+ await checkCertErrorGenericAtTime(
+ certdb,
+ validCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "vpn.worldofspeed.org",
+ 0
+ );
+
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "us-datarecovery.com",
+ 0
+ );
+
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedInStashCert,
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "stokedmoto.com",
+ 0
+ );
+
+ // This certificate has no embedded SCTs, so it is not guaranteed to be in
+ // CT, so CRLite can't be guaranteed to give the correct answer, so it is
+ // not consulted, and the implementation falls back to OCSP. Since the real
+ // OCSP responder can't be reached, this results in a
+ // SEC_ERROR_OCSP_SERVER_ERROR.
+ let noSCTCert = constructCertFromFile("test_crlite_filters/no-sct.pem");
+ // NB: this will cause an OCSP request to be sent to localhost:80, but
+ // since an OCSP responder shouldn't be running on that port, this should
+ // fail safely.
+ Services.prefs.setCharPref("network.dns.localDomains", "ocsp.digicert.com");
+ Services.prefs.setBoolPref("security.OCSP.require", true);
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ await checkCertErrorGenericAtTime(
+ certdb,
+ noSCTCert,
+ SEC_ERROR_OCSP_SERVER_ERROR,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "mail233.messagelabs.com",
+ 0
+ );
+ Services.prefs.clearUserPref("network.dns.localDomains");
+ Services.prefs.clearUserPref("security.OCSP.require");
+ Services.prefs.clearUserPref("security.OCSP.enabled");
+
+ // If the earliest certificate timestamp is within the merge delay of the
+ // logs for the filter we have, it won't be looked up, and thus won't be
+ // revoked.
+ // The earliest timestamp in this certificate is in August 2020, whereas
+ // the filter timestamp is in October 2020, so setting the merge delay to
+ // this large value simluates the situation being tested.
+ Services.prefs.setIntPref(
+ "security.pki.crlite_ct_merge_delay_seconds",
+ 60 * 60 * 24 * 60
+ );
+ // Since setting the merge delay parameter this way effectively makes this
+ // certificate "too new" to be covered by the filter, the implementation
+ // would fall back to OCSP fetching. Since this would result in a crash and
+ // test failure, the Ci.nsIX509CertDB.FLAG_LOCAL_ONLY is used.
+ await checkCertErrorGenericAtTime(
+ certdb,
+ revokedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ new Date("2020-10-20T00:00:00Z").getTime() / 1000,
+ false,
+ "us-datarecovery.com",
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
+ );
+ Services.prefs.clearUserPref("security.pki.crlite_ct_merge_delay_seconds");
+});
+
+add_task(async function test_crlite_filters_avoid_reprocessing_filters() {
+ Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
+
+ let result = await syncAndDownload([
+ { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" },
+ {
+ timestamp: "2019-01-01T06:00:00Z",
+ type: "diff",
+ id: "0001",
+ parent: "0000",
+ },
+ {
+ timestamp: "2019-01-01T12:00:00Z",
+ type: "diff",
+ id: "0002",
+ parent: "0001",
+ },
+ {
+ timestamp: "2019-01-01T18:00:00Z",
+ type: "diff",
+ id: "0003",
+ parent: "0002",
+ },
+ ]);
+ let [status, filters] = result.split(";");
+ equal(status, "finished", "CRLite filter download should have run");
+ let filtersSplit = filters.split(",");
+ deepEqual(
+ filtersSplit,
+ [
+ "2019-01-01T00:00:00Z-full",
+ "2019-01-01T06:00:00Z-diff",
+ "2019-01-01T12:00:00Z-diff",
+ "2019-01-01T18:00:00Z-diff",
+ ],
+ "Should have downloaded the expected CRLite filters"
+ );
+ // This simulates another poll without clearing the database first. The
+ // filter and stashes should not be re-downloaded.
+ result = await syncAndDownload([], false);
+ equal(result, "finished;");
+
+ // If a new stash is added, only it should be downloaded.
+ result = await syncAndDownload(
+ [
+ {
+ timestamp: "2019-01-02T00:00:00Z",
+ type: "diff",
+ id: "0004",
+ parent: "0003",
+ },
+ ],
+ false
+ );
+ equal(result, "finished;2019-01-02T00:00:00Z-diff");
+});
+
+let server;
+
+function run_test() {
+ server = new HttpServer();
+ server.start(-1);
+ registerCleanupFunction(() => server.stop(() => {}));
+
+ server.registerDirectory(
+ "/cdn/security-state-workspace/cert-revocations/",
+ do_get_file(".")
+ );
+
+ server.registerPathHandler("/v1/", (request, response) => {
+ response.write(
+ JSON.stringify({
+ capabilities: {
+ attachments: {
+ base_url: `http://localhost:${server.identity.primaryPort}/cdn/`,
+ },
+ },
+ })
+ );
+ response.setHeader("Content-Type", "application/json; charset=UTF-8");
+ response.setStatusLine(null, 200, "OK");
+ });
+
+ Services.prefs.setCharPref(
+ "services.settings.server",
+ `http://localhost:${server.identity.primaryPort}/v1`
+ );
+
+ // Set intermediate preloading to download 0 intermediates at a time.
+ Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 0);
+
+ Services.prefs.setCharPref("browser.policies.loglevel", "debug");
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/20201017-0-filter b/security/manager/ssl/tests/unit/test_crlite_filters/20201017-0-filter
new file mode 100644
index 0000000000..4da0a2ec14
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/20201017-0-filter
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/20201017-1-filter.stash b/security/manager/ssl/tests/unit/test_crlite_filters/20201017-1-filter.stash
new file mode 100644
index 0000000000..d43193a78c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/20201017-1-filter.stash
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/20201201-3-filter.stash b/security/manager/ssl/tests/unit/test_crlite_filters/20201201-3-filter.stash
new file mode 100644
index 0000000000..52c9ee8d51
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/20201201-3-filter.stash
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/issuer.pem b/security/manager/ssl/tests/unit/test_crlite_filters/issuer.pem
new file mode 100644
index 0000000000..ead19e3c14
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/issuer.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
+EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
+ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAwMFoXDTMxMDUwMzA3
+MDAwMFowgbQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
+EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UE
+CxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQD
+EypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC54MsQ1K92vdSTYuswZLiBCGzD
+BNliF44v/z5lz4/OYuY8UhzaFkVLVat4a2ODYpDOD2lsmcgaFItMzEUz6ojcnqOv
+K/6AYZ15V8TPLvQ/MDxdR/yaFrzDN5ZBUY4RS1T4KL7QjL7wMDge87Am+GZHY23e
+cSZHjzhHU9FGHbTj3ADqRay9vHHZqm8A29vNMDp5T19MR/gd71vCxJ1gO7GyQ5HY
+pDNO6rPWJ0+tJYqlxvTV0KaudAVkV4i1RFXULSo6Pvi4vekyCgKUZMQWOlDxSq7n
+eTOvDCAHf+jfBDnCaQJsY1L6d8EbyHSHyLmTGFBUNUtpTrw700kuH9zB0lL7AgMB
+AAGjggEaMIIBFjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
+HQ4EFgQUQMK9J47MNIMwojPX+2yz8LQsgM4wHwYDVR0jBBgwFoAUOpqFBxBnKLbv
+9r0FQW4gwZTaD94wNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC5nb2RhZGR5LmNvbS8wNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2NybC5n
+b2RhZGR5LmNvbS9nZHJvb3QtZzIuY3JsMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEG
+CCsGAQUFBwIBFiVodHRwczovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQAIfmyTEMg4uJapkEv/oV9PBO9sPpyIBslQj6Zz
+91cxG7685C/b+LrTW+C05+Z5Yg4MotdqY3MxtfWoSKQ7CC2iXZDXtHwlTxFWMMS2
+RJ17LJ3lXubvDGGqv+QqG+6EnriDfcFDzkSnE3ANkR/0yBOtg2DZ2HKocyQetawi
+DsoXiWJYRBuriSUBAA/NxBti21G00w9RKpv0vHP8ds42pM3Z2Czqrpv1KrKQ0U11
+GIo/ikGQI31bS/6kA1ibRrLDYGCD+H1QQc7CoZDDu+8CL9IVVO5EFdkKrqeKM+2x
+LXY2JtwE65/3YR8V3Idv7kaWKK2hJn0KCacuBKONvPi8BDAB
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/no-sct-issuer.pem b/security/manager/ssl/tests/unit/test_crlite_filters/no-sct-issuer.pem
new file mode 100644
index 0000000000..70b86dfd71
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/no-sct-issuer.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg
+U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83
+nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd
+KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f
+/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX
+kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0
+/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY
+aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6
+Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1
+oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD
+QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
+d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh
+xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB
+CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl
+5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA
+8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC
+2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit
+c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0
+j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/no-sct.pem b/security/manager/ssl/tests/unit/test_crlite_filters/no-sct.pem
new file mode 100644
index 0000000000..a690a0ad0d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/no-sct.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFpDCCBIygAwIBAgIQDVHBpbd6yyk2LgPoPr9QyjANBgkqhkiG9w0BAQsFADBN
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
+aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTkxMTE4MDAwMDAwWhcN
+MjExMTE4MTIwMDAwWjCBlDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
+aWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxHTAbBgNVBAoTFFN5bWFudGVjIENv
+cnBvcmF0aW9uMRcwFQYDVQQLEw5TeW1hbnRlYy5jbG91ZDEgMB4GA1UEAxMXbWFp
+bDIzMy5tZXNzYWdlbGFicy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCyM1Fy9hAlahRqqeEnPKDWgUmsxofivWEWKNeSMEKcnXX3TCQOGbLQTthN
+xfNU7IWY8ViTPwQ8JBWWDxNhd6dTYLNnytKrNRG8qDQ3rFMKJY4p0dZImMp55X3W
+1xcKMxSOkPv0YUCGp7qlAHq6+N3YY1ILw6MRdJ75Njh4Kw8qe5F3rHLwD+AyYQmx
+3WsMCRp5NZtWUcU5Vbc9ca/osrh9xBF7U3ZYR6GoPXQlizrNjXv7/BaKWWO5ChbD
+iRI4Nj8d3HhWUHsJoGvYDof5Iudgtbubz3c5cwp6+VNNMas7izpvbixqW8zXdUug
+8v5v47IkRNYnlma/zvv2IDC1dVlxAgMBAAGjggI2MIICMjAfBgNVHSMEGDAWgBQP
+gGEcgjFh1S8o541GOLQs4cbZ4jAdBgNVHQ4EFgQUZnBdWwGQjkPX/+A2ZEYdUdnw
+lN8wfQYDVR0RBHYwdIIbY2x1c3RlcjguZXUubWVzc2FnZWxhYnMuY29tgh5jbHVz
+dGVyOG91dC5ldS5tZXNzYWdlbGFicy5jb22CHGNsdXN0ZXI4YS5ldS5tZXNzYWdl
+bGFicy5jb22CF21haWwyMzMubWVzc2FnZWxhYnMuY29tMA4GA1UdDwEB/wQEAwIF
+oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwawYDVR0fBGQwYjAvoC2g
+K4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NzY2Etc2hhMi1nNi5jcmwwL6At
+oCuGKWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLXNoYTItZzYuY3JsMEwG
+A1UdIARFMEMwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
+LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQICMHwGCCsGAQUFBwEBBHAwbjAkBggr
+BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEYGCCsGAQUFBzAChjpo
+dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyU2VjdXJlU2Vy
+dmVyQ0EuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQADggEBAK7vS/qDcGKb
+QYu26+jGtBemopT3+2YJjtALeR62eNhF9LoHu+mnmNLvPI0M0NMhz56Ss/6sUHOz
+hJgB98SLAQ5ElSWXrnZThLIjsiH5X5MYTD0Y8MqzoJSi2Lf2Muy/UpyrD3wB14E1
+kUYhvUnaWDDPIN81DCFzEosBmnsRqr5zlcZSKs0e1LVQ8cNkt8svVkiwFgeOIhwo
+QF22GJAZPtRceSGlbRTFBYKh+u3KN8eNS/X+C935y+F4J/grufDCzRSGtRRseTcd
+1QW49+QME/rx1mBb7id4iXNKxvGuJTivBlxaHWBQLh/RGk39DSdHfjAhYvt2gmxh
+C3gxXMNrymE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash-2.pem b/security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash-2.pem
new file mode 100644
index 0000000000..9550453493
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash-2.pem
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIGNTCCBR2gAwIBAgIIFn0oGKdatdgwDQYJKoZIhvcNAQELBQAwgbQxCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRow
+GAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRz
+LmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1
+cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwHhcNMjAwOTI3MTE0MzI0WhcN
+MjExMDI5MTE0MzI0WjA5MSEwHwYDVQQLExhEb21haW4gQ29udHJvbCBWYWxpZGF0
+ZWQxFDASBgNVBAMTC2ljc3JlcHMuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAydMKPhCqRWjy5h1aPUNdRb80kt0hP/g55ytjlSF04PG+1CbIcId1
+GJg1qkbTR1vqZyvaI8wjv3zdfvsvYbka9OQFJiOasJfeqmiA+sDd9AvFiD2EF5zN
+D9uKHi+sF8Ut4JMl7jqaRAu/gbjBvY/9ammkz4sUiTlp1x4rteda3tuX9O7yMO7U
+ldnyfabHgGmmm8KU3nvRAjNbHCq3J/V/zw8YeolXU5OpOeZMhI8KAGKpxk8tRiIb
+LkdSdCTWoKgXO60extYcGTxIT8c8zfY6OoN0VaQY8HA1VyBZQIw2RTWivmI8le0J
+ypXYxNcUtInS0ivO4ymiNnCjYR9pgQ6+tQIDAQABo4ICwzCCAr8wDAYDVR0TAQH/
+BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQD
+AgWgMDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6Ly9jcmwuZ29kYWRkeS5jb20vZ2Rp
+ZzJzMS0yMzM0LmNybDBdBgNVHSAEVjBUMEgGC2CGSAGG/W0BBxcBMDkwNwYIKwYB
+BQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9y
+eS8wCAYGZ4EMAQIBMHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDov
+L29jc3AuZ29kYWRkeS5jb20vMEAGCCsGAQUFBzAChjRodHRwOi8vY2VydGlmaWNh
+dGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RpZzIuY3J0MB8GA1UdIwQYMBaA
+FEDCvSeOzDSDMKIz1/tss/C0LIDOMCcGA1UdEQQgMB6CC2ljc3JlcHMuY29tgg93
+d3cuaWNzcmVwcy5jb20wHQYDVR0OBBYEFEgqFQnM5zH3cKFGtFanJxxD9zdPMIIB
+BAYKKwYBBAHWeQIEAgSB9QSB8gDwAHUA9lyUL9F3MCIUVBgIMJRWjuNNExkzv98M
+LyALzE7xZOMAAAF0z1/40gAABAMARjBEAiADSOnqg/I15y+dJDSWta8NzBwE6Xti
+UzyBKMT2OYHCYwIgb1aFpZxlkhx6XCCuniBLTcr5JbhigoM/lAfUmvvUqrIAdwBc
+3EOS/uarRUSxXprUVuYQN/vV+kfcoXOUsl7m9scOygAAAXTPX/oBAAAEAwBIMEYC
+IQD6WvavTba+Lydf6uhaaxcRmhuuPeddzSmC8t4+tBLGGgIhANJPxhRl1pGYm6WX
+ay9jPCM2SqtBraOJYncaV6k37zGYMA0GCSqGSIb3DQEBCwUAA4IBAQA0IknIoU51
+FCBqpksgo7zR9OJj5MoQmlsQbzSFppdRKgyHhk8rW6IrBi3yrtWjo3HxcwihZlJQ
+2AbinRTNnHvBpiiiXXxR5u9yVly+9l3KfF/uHIGMnIqsahaKXNOy5h98uq4o4N0+
+YCGu9wSeDwtaCzdT+V447Fq63nmW629pjwin8FYCz2S8RdztranPZuOgwYqBNlWu
+u3Mi9DWIV2hP6eFwMZh7BWgbvnhWiI37TE74YQ+3cEVaLxLHJH9jkBjsel+ZwVIo
+9xeE6kGsh5+D0uHS5NWf9zu+fvW0iTgsB8J4VX5OihAa1v4FS4IaF3snkaMa0PCd
+MJMOgY5VkZiw
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash.pem b/security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash.pem
new file mode 100644
index 0000000000..1073159662
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/revoked-in-stash.pem
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIGPTCCBSWgAwIBAgIJAJeW47AXop8NMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYD
+VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEa
+MBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0
+cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2Vj
+dXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTIwMDgyMjIzMDI0NVoX
+DTIxMDkyMTIxMTYxMVowPDEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRh
+dGVkMRcwFQYDVQQDEw5zdG9rZWRtb3RvLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBANLX8D4Cin6x2bmoSdgnuw9O+tGcD1x1S++3110NJVXUlpSc
+3uSUwqpU4rMOkJlRnuaBtHXYUFG8NMMnbTJYY9JuuhAlnHzVqoRWmcTNRp72fpDA
+XoD3U20spJeGQZXuQnGfe/k9EouvXt5du029YqItkFbjdbub4FP5MbIz1CWeelEn
+fEwpe/peLVYEfKSyYf325tFI9wZhuIM/zTe9DE6lauznM8hg1ioiBujxzeWWSstZ
+K2uJKaI8nlWkSr6vwPqJEBvvoShcWHFEmG8SWqPBy1tsNsLNjJkaHfD7gvG/J1Rc
+G21D0XO2IA0rEo6lo8MNEkWoHZA/oHO1eHrCjNkCAwEAAaOCAscwggLDMAwGA1Ud
+EwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA4GA1UdDwEB
+/wQEAwIFoDA4BgNVHR8EMTAvMC2gK6AphidodHRwOi8vY3JsLmdvZGFkZHkuY29t
+L2dkaWcyczEtMjIzNy5jcmwwXQYDVR0gBFYwVDBIBgtghkgBhv1tAQcXATA5MDcG
+CCsGAQUFBwIBFitodHRwOi8vY2VydGlmaWNhdGVzLmdvZGFkZHkuY29tL3JlcG9z
+aXRvcnkvMAgGBmeBDAECATB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0
+dHA6Ly9vY3NwLmdvZGFkZHkuY29tLzBABggrBgEFBQcwAoY0aHR0cDovL2NlcnRp
+ZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5L2dkaWcyLmNydDAfBgNVHSME
+GDAWgBRAwr0njsw0gzCiM9f7bLPwtCyAzjAtBgNVHREEJjAkgg5zdG9rZWRtb3Rv
+LmNvbYISd3d3LnN0b2tlZG1vdG8uY29tMB0GA1UdDgQWBBTP8YI0WwVBgvg5GABQ
+fUksbk6evjCCAQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1APZclC/RdzAiFFQYCDCU
+Vo7jTRMZM7/fDC8gC8xO8WTjAAABdBhpJtAAAAQDAEYwRAIgcrebTXyuMk/ciVyg
+LMbbd6qbkyB3yRAY1QnBs5sGK1ICIGw6EpH/NMXcOfYdkuSmhSYgUx0AIDdT9QWH
+rRIT50D3AHUAXNxDkv7mq0VEsV6a1FbmEDf71fpH3KFzlLJe5vbHDsoAAAF0GGko
+AwAABAMARjBEAiBtzQQHVVML3yZhZRsLLhJkYcc11nXeU/S/rouOZCCZQQIgVYKY
+jgjCU5HQyY6R5PZQTsCQGWiaOm1VguAIgPbvKzYwDQYJKoZIhvcNAQELBQADggEB
+AH6jCZU9+5TMaR5XThGL/z8EYQ1uFVN0hlXXB+gP3IDQLmyxkqrk9cOSf1D6fLRT
+5T4tGzPLvmReBLfrzQEkuqkXpUieortbpi116V82K+zBDT5s9Dol6+MxhwIZyZ7K
+DkHSbcFyiV9hkr2bf8JZzjvpCOfw9kYZTcYv8M8kheIOQsONdfe/rqNQnMRy56UZ
+OGZmxqDT2RwbewHEMAEb5ZsZ55/UHq08vZSVSA8qIqX/BU+7frwwt4vmv4WQ7AoJ
+YNml0TdV4R27NlnvhbAclucuqEMBcBg8gNylvKD3Mid7O4c5gCX5Wuml6rLpOr05
+vpJJqSf0uy73EtNcyTqfwUQ=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/revoked.pem b/security/manager/ssl/tests/unit/test_crlite_filters/revoked.pem
new file mode 100644
index 0000000000..ece7360def
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/revoked.pem
@@ -0,0 +1,42 @@
+-----BEGIN CERTIFICATE-----
+MIIHTjCCBjagAwIBAgIILTXKZQP7G6MwDQYJKoZIhvcNAQELBQAwgbQxCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRow
+GAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRz
+LmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMwMQYDVQQDEypHbyBEYWRkeSBTZWN1
+cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwHhcNMjAwODIyMjM0ODE1WhcN
+MjEwMTE0MjEwMzAxWjBDMSEwHwYDVQQLExhEb21haW4gQ29udHJvbCBWYWxpZGF0
+ZWQxHjAcBgNVBAMMFSoudXMtZGF0YXJlY292ZXJ5LmNvbTCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBAKzuSr8LaH2Aw/G1aKN4URo0RwaFv4+7MzM0RVTB
+189BdE8rgNRfxskoAVfte+iftO7P5qjg+1Jx8SCE1ZCNT2TGtcYwlsjyNLnbP3xV
+Cq514bODlwimOKLhpUFH1/ofO4/enbU8E4hlxS4DPtOGbsjouTiRHAOLsi7D+WT3
+3pZelFa5Hgmed5dL/CCJTiwbF10lbTXNwLgI7efUqiwvHRwf/CXEW9IvKJ9HG2We
+tT9ouQGOHfz4fyGOMN268dHqP89K+auCcf9b3BzzWOsknhmDisl+06WaIJe/W7Dn
++eG07nAuJLhE6nH9KLYiU2X5CHnLPvBTOPDdm/hzH84hnHkX2pSbst4d1aNtU4uP
+hyQcE5aMfnV7Yr1ZQUlLreCKzwIAb6BKqF/gHMASXUX7Fw5OFUHxV1xcPbcHQLs6
+D3XAoLBzopTR4YjxIDqhS3pkxHu5u9OEzFfaeP5zYnrj69Bke3lrdbrKV8aXIxHn
+xh9xJV4zAaPoHe8ze03zvdBXfu69DU+TTQkRfzJSZx/sXV6BHe15WGSHZxe0i61z
+wA1Oi7QgySMKIKSs6dsPOqppmePc3seMJy7y0FSxF1Kq6a1tR+W3j2WT9nY1c4Wb
+4LWc+0gDK3nvibJbFqCfnPznq/Q+q0V6m9BqBNjvqyyWXbgzCVn7qcT4apiP47IV
++MiBAgMBAAGjggLSMIICzjAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCBaAwOAYDVR0fBDEwLzAtoCugKYYn
+aHR0cDovL2NybC5nb2RhZGR5LmNvbS9nZGlnMnMxLTIyMzcuY3JsMF0GA1UdIARW
+MFQwSAYLYIZIAYb9bQEHFwEwOTA3BggrBgEFBQcCARYraHR0cDovL2NlcnRpZmlj
+YXRlcy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzAIBgZngQwBAgEwdgYIKwYBBQUH
+AQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nb2RhZGR5LmNvbS8wQAYI
+KwYBBQUHMAKGNGh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3Np
+dG9yeS9nZGlnMi5jcnQwHwYDVR0jBBgwFoAUQMK9J47MNIMwojPX+2yz8LQsgM4w
+NQYDVR0RBC4wLIIVKi51cy1kYXRhcmVjb3ZlcnkuY29tghN1cy1kYXRhcmVjb3Zl
+cnkuY29tMB0GA1UdDgQWBBTooRcQfblnnD7pJFCZfGGcf4kD4jCCAQUGCisGAQQB
+1nkCBAIEgfYEgfMA8QB3APZclC/RdzAiFFQYCDCUVo7jTRMZM7/fDC8gC8xO8WTj
+AAABdBiSoGUAAAQDAEgwRgIhAI4Cd+FhKsWdUZkBugaburD9dzdlVHSxKq6oYNyI
+vmIIAiEAwlmkvAiPb9nwDirqe7MZud5nu/lmq/Ip7M1xSKuraykAdgBc3EOS/uar
+RUSxXprUVuYQN/vV+kfcoXOUsl7m9scOygAAAXQYkqGUAAAEAwBHMEUCIQCpYqq+
+Uc881tR+ikvsR97FRl6jBfxG50Sum+cdHEQkYQIgZJbGaeNoDS9+LAKI88NNRiCK
+vtQZkwWigDYr+2dWguYwDQYJKoZIhvcNAQELBQADggEBAD+sj44+86AvdVUrAN9h
+cU6kt4I6K1TM0KBmKg3rG8JEY7+Ec4Rztls3uviLR0ajH5tkQPwD7vRBVrLVDtQS
+Ndt2StR38AXiBRWwewy/sPMz11YzOPLyHaTl4pJVfyzHJ+rPdWuFZLtpTras/MIK
+IFnnbInlh5XtRhDCv6UEkAmGu5BeftA+9XxTuZlbwQlO5U1yg9Hqor9zANMqX9Ad
+3omIZrPtBkTdVOwHRU8SoaS6XxQ9jxcmWRgNTVAhU1/J7Bgvg3CPSpcHyV78sXkS
+D9bb1f4jdadaMKuJc0mTHBAxZenr3IFV8upf2FRTJnrCiS0jX8kKobN/+04Gbev6
+Tr0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters/valid.pem b/security/manager/ssl/tests/unit/test_crlite_filters/valid.pem
new file mode 100644
index 0000000000..6769ba3d37
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_crlite_filters/valid.pem
@@ -0,0 +1,39 @@
+-----BEGIN CERTIFICATE-----
+MIIGyTCCBbGgAwIBAgIJANpPOSv9i86oMA0GCSqGSIb3DQEBCwUAMIG0MQswCQYD
+VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEa
+MBgGA1UEChMRR29EYWRkeS5jb20sIEluYy4xLTArBgNVBAsTJGh0dHA6Ly9jZXJ0
+cy5nb2RhZGR5LmNvbS9yZXBvc2l0b3J5LzEzMDEGA1UEAxMqR28gRGFkZHkgU2Vj
+dXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTE4MTAxNTE5NTQxNFoX
+DTIwMTAyNDE4MzczOFowQjEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRh
+dGVkMR0wGwYDVQQDExR2cG4ud29ybGRvZnNwZWVkLm9yZzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAKb1cjhtYv6yiyZa1feabWaSBOCmN0JuqXIMkX3r
+4g+5heKAwPFlPFbZ3vZ3DQXiliV7jb5nhlx0O6nYG+MoGFmj6hEHDptCASAfdd3j
+4tCxg0FlilXgrgMkJCg+SjW0npZ86jMkfc0WzufUyMjxv2pUUicPNXyWbaQr+PCq
+zs6AsOkmuQ8RUUAqZ+Q0EJfQnjuhql7NCdByNui9S2LmrPcV6TAHHeTwKX733edv
+zsNzaLNgE6TLGXSSRvsW/eZ/uNScPHLybE4wdxxCDwSYCPwaQq34csc3a8SUTWfS
+4UkdbOn7j5sZPx7Jj0uUlm20ZDsj2FUi/0SXNvz5flFQbVsCAwEAAaOCA00wggNJ
+MAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA4G
+A1UdDwEB/wQEAwIFoDA3BgNVHR8EMDAuMCygKqAohiZodHRwOi8vY3JsLmdvZGFk
+ZHkuY29tL2dkaWcyczEtODc4LmNybDBdBgNVHSAEVjBUMEgGC2CGSAGG/W0BBxcB
+MDkwNwYIKwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20v
+cmVwb3NpdG9yeS8wCAYGZ4EMAQIBMHYGCCsGAQUFBwEBBGowaDAkBggrBgEFBQcw
+AYYYaHR0cDovL29jc3AuZ29kYWRkeS5jb20vMEAGCCsGAQUFBzAChjRodHRwOi8v
+Y2VydGlmaWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RpZzIuY3J0MB8G
+A1UdIwQYMBaAFEDCvSeOzDSDMKIz1/tss/C0LIDOMDkGA1UdEQQyMDCCFHZwbi53
+b3JsZG9mc3BlZWQub3Jnghh3d3cudnBuLndvcmxkb2ZzcGVlZC5vcmcwHQYDVR0O
+BBYEFGjmOgodY5yPX19UPVcACFpVExz1MIIBfQYKKwYBBAHWeQIEAgSCAW0EggFp
+AWcAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWZ5SxEeAAAE
+AwBHMEUCIEL5OLHwBpIALbEnLFQntFlGBe8Oko6U/arr15jU4sLDAiEA6uy8DEnh
+PFLOxXDC3ZaeOlK2+cdx76IAr6M6As74ETEAdgDuS723dc5guuFCaR+r4Z5mow9+
+X7By2IMAxHuJeqj9ywAAAWZ5SxMzAAAEAwBHMEUCIFWIeePa6jdf6y9o/YYxoqWr
+Je3j8W94e4f5bixaGPiyAiEA7aJRZI+aWCE/zz5DpvcyRgkIoSKS3+dKS2irf/mp
+qx0AdQBep3P531bA57U2SH3QSeAyepGaDIShEhKEGHWWgXFFWAAAAWZ5SxPjAAAE
+AwBGMEQCIEkYw6g/cIZBdOUh+ETwl2XX2S0Bv8iGGiaOKOoXqVK6AiAh3eqABfMc
+9b/wLJZo186YgbzmbZB0N3y5TUJKK1oMYDANBgkqhkiG9w0BAQsFAAOCAQEAPEwx
+d597oqiP9/TN8RDrZqhn4uLZ9K5mXOD93RorUN8T8O1kV0B4UcXM1CkU2zxv4a9S
+tG3diHjmfJgcrhpa4i19sZD7+QpTU8j0e82JlsB3MpbtuaiBwqb979c5qNPixQlJ
+kDs4DWf8kV+4B9/DWWLDKvs+FtL8ST8n+LfwstZqi2EyK+1ZyM0p9hkUAzrT/M2h
+Ou1DYELbCt2HaKMHMPSlrkESG7Q9v9Ba23EXElG+oFXxgnPwk4n84rYG8lI8pPyA
+clunJNqc2cISUTDMdgGctCFdSVGyEq+8VVG5Y6Od5LMwtOLKcv+VPi17+iGfxNt/
++dAMbzGIlDuJELwn0g==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ct.js b/security/manager/ssl/tests/unit/test_ct.js
new file mode 100644
index 0000000000..59db48f858
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct.js
@@ -0,0 +1,75 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function expectCT(value) {
+ return securityInfo => {
+ Assert.equal(
+ securityInfo.certificateTransparencyStatus,
+ value,
+ "actual and expected CT status should match"
+ );
+ };
+}
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.pki.certificate_transparency.mode");
+ let cert = constructCertFromFile("test_ct/ct-valid.example.com.pem");
+ setCertTrust(cert, ",,");
+});
+
+function run_test() {
+ Services.prefs.setIntPref("security.pki.certificate_transparency.mode", 1);
+ add_tls_server_setup("BadCertAndPinningServer", "test_ct");
+ // These certificates have a validity period of 800 days, which is a little
+ // over 2 years and 2 months. This gets rounded down to 2 years (since it's
+ // less than 2 years and 3 months). Our policy requires N + 1 embedded SCTs,
+ // where N is 2 in this case. So, a policy-compliant certificate would have at
+ // least 3 SCTs.
+ add_connection_test(
+ "ct-valid.example.com",
+ PRErrorCodeSuccess,
+ null,
+ expectCT(
+ Ci.nsITransportSecurityInfo.CERTIFICATE_TRANSPARENCY_POLICY_COMPLIANT
+ )
+ );
+ // This certificate has only 2 embedded SCTs, and so is not policy-compliant.
+ add_connection_test(
+ "ct-insufficient-scts.example.com",
+ PRErrorCodeSuccess,
+ null,
+ expectCT(
+ Ci.nsITransportSecurityInfo
+ .CERTIFICATE_TRANSPARENCY_POLICY_NOT_ENOUGH_SCTS
+ )
+ );
+
+ // Test that if an end-entity is marked as a trust anchor, CT verification
+ // returns a "not enough SCTs" result.
+ add_test(() => {
+ let cert = constructCertFromFile("test_ct/ct-valid.example.com.pem");
+ setCertTrust(cert, "CTu,,");
+ clearSessionCache();
+ run_next_test();
+ });
+ add_connection_test(
+ "ct-valid.example.com",
+ PRErrorCodeSuccess,
+ null,
+ expectCT(
+ Ci.nsITransportSecurityInfo
+ .CERTIFICATE_TRANSPARENCY_POLICY_NOT_ENOUGH_SCTS
+ )
+ );
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem b/security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem
new file mode 100644
index 0000000000..c015087e85
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEsjCCA5qgAwIBAgIUD7pA2hQ6aiqRWzwjIb6zStU4wxMwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjArMSkwJwYDVQQDDCBjdC1pbnN1ZmZpY2llbnQtc2N0cy5leGFt
+cGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaOCAeEwggHdMBgGA1UdEQQRMA+CDSouZXhhbXBsZS5jb20w
+ggG/BgorBgEEAdZ5AgQCBIIBrwSCAasBqQB2ACq4MEQzuRTe0vMeQgfyUcF6N6CS
+aFLZCAIG+F5XORYqAAABUfp73AAAAAQDAEcwRQIgXHVRnxMRUM1dit4go7wGMJH/
+snN1XzFk7P3LQoAKcOYCIQD0nA7JqwaCdhzWfXxdSrCk6FesmzZ8JJ9CHVN7JqEr
+JAEvADEI9rbdchgH8BaVhE2FAla2cZNgg9u9OTSgUFJQGiiSAAABUfp73AAAAAQB
+AQAJQ/auHYB8PHgYp6ApXGqdIPM6hRnYlX292tOXymT3atbxcBbrwCaW01Y3mU7F
+ndLHBxnfcUdGK314D/GF+8Ez5k+/kWsKYkxH+pVFLcHuyJ6bVREfRkqEgwrenDlz
+3NvxXGXUoWgYKfT9fWoMJui1IuRIbDjci0qBq+vT6GhcNJHdXHm41Yze+NjmsmKx
+5XEwSQIqg/NeUwuDrsPU9LOcjZ3OVEAH1aVWY8cy5EhVA+Zojk9mTXVE/iuBCp/n
+90V9dXyK+E7+ougZ+jnhrlcPCmULgW/Q8LNtRtR0KKexWMom2PSjs1GmbU/l8Wm3
+sv7vmhihg5SQsitmM4CHjxWtMA0GCSqGSIb3DQEBCwUAA4IBAQAeXJGA3UeqCgu4
+VXi8ZvPAmz2BXIfEiwtOXwvuvK1u0SsuwkLoSkKwk2Qr5/p69qn8mpQQmd5zSQ2v
+sAAviWu0rl4ugfQr8DOHX1DUF0o82xNMhR/xvz4cpNr3SPrVJOgFqf2oKquo7dzI
+kE5vYqACL9w/pRht2EUTJ1eWwNDoz9FCY9zmsvFAiCc2WLWWomKW0VmhxJ+8zLCO
+Yk3PBHhipjjGobt6UcG4/QViwKZlRIUKyDMHJ16NhXmicmKWz7LVfkN1XtOpOPGh
+v7AX58qHA4ZjBg3AOzfvew+pDdnrAIgrUqcatMqu31xieJVLHDGsg2c5ID3xPx+P
+AHOmzcCa
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem.certspec b/security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem.certspec
new file mode 100644
index 0000000000..c40f26d5d8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/ct-insufficient-scts.example.com.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:ct-insufficient-scts.example.com
+extension:subjectAlternativeName:*.example.com
+extension:embeddedSCTList:secp256r1:20160101,alternate:20160101
diff --git a/security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem b/security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem
new file mode 100644
index 0000000000..f7c6ab8b0d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF1zCCBL+gAwIBAgIUF5bjHB2T8yqxNGQJmDc0Cm6FVTYwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAfMR0wGwYDVQQDDBRjdC12YWxpZC5leGFtcGxlLmNvbTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+AaOCAxIwggMOMBgGA1UdEQQRMA+CDSouZXhhbXBsZS5jb20wggLwBgorBgEEAdZ5
+AgQCBIIC4ASCAtwC2gEvAFQiJZjzPTZIBULa7ODmuU3hXA7ujFkUykjXXjxIToA/
+AAABUfp73AAAAAQBAQANJRHiZJgVmreqeqGKEvgLaZbuVZyz5Vp1IfzawYVV2J5/
+mbvIxX9yQcJVQhHHdWgyc2WzYvgQTQ8XwUg12Xx2za7c8y0YvEbVfb0bPVDXRvWY
+r8YaKgJJuAJO1SqR9lB8iDEz/6BahLGfM9C8ypXoCTtpQ14oV/qSsFzJhys9jlLI
+p6AjXgNSqv/NVVMnvTWJ5AOzhIdJJQmVNZM0NXdDZmqlC4Wk8r8rJKYzGswm6CkC
+JJ7+WCzuM0ufP5JyLGOp8AsEJvURajm19v09uifDieYysAlBAukPCur3ni8SIxS6
+12iFjawXIeQMkobGanZyKMFYsfeFnQCUBi6Kr1a4AHYAKrgwRDO5FN7S8x5CB/JR
+wXo3oJJoUtkIAgb4Xlc5FioAAAFR+nvcAAAABAMARzBFAiBcdVGfExFQzV2K3iCj
+vAYwkf+yc3VfMWTs/ctCgApw5gIhAJGsOl/pvErK6FvgTc4TNRBDdX2x37AhywuA
+NtqY3PZhAS8AMQj2tt1yGAfwFpWETYUCVrZxk2CD2705NKBQUlAaKJIAAAFR+nvc
+AAAABAEBAIAsvMMHiK9UjMgfOjdtLP2bJwLxQ6qHHgJ6IooQgq95gvXpZmhDhSrB
+WzHtjSf0G8NrIYQLfGNqeUFjVq7Yby8FkOMrE/VbQrvSHOADJ65hqPesNfRLjWuD
+1u9IjlTZFyIxZcrN2OqjWnBfsAylivHAHGj/1vbZqvOKWmQbBpuNW2P5p35uArEX
+r5jhBxfM0HO07UgDJzt8oG3fK+iDV/wv3R6Y4LvO+SebnHiXg9HygwG0wi8bGMYK
+2wXNUsQ3tdBq4WeJr9cxoH8Ke9l+sLUc9wZ1N7lV7p7hmZeN+nSt6aJny/DfCLoo
+rG62y/At1y4l0FmXXvPftkv1QunvWj0wDQYJKoZIhvcNAQELBQADggEBAEFe0sch
+6JG7g3uiNpomqOqNaBHnoe5qsYONRTRajDRl8/qyn9NwkMEks0bR6xwBQmBOpN5X
++0TLa43nv28V7B66Mfnpv9ISKdhOytQYN39us0LMBEP8dR/YK5Ux2JH/+iI6IHl0
+syErYIki6Wuz6NxVKAaQN7iB+Qw+pCSJLkbRojXCqSKUwP1uBrhMW1Oe3N2o/eZr
+ZpS9IWYtBeb3zPKlMIQHLEUxOBq6SvrNrxcylNKPBXHWTfvFvZYVgLmqzIyti1mw
+yC1fr2sMriB6cFd/7i+Mnh4vK31yH/c1KwxDnpOUCEToryH6z+1ydWxyoLgnO6Za
+Vx9PmW79Rj2mSFM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem.certspec b/security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem.certspec
new file mode 100644
index 0000000000..0ecf46d89c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/ct-valid.example.com.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:ct-valid.example.com
+extension:subjectAlternativeName:*.example.com
+extension:embeddedSCTList:default:20160101,secp256r1:20160101,alternate:20160101
diff --git a/security/manager/ssl/tests/unit/test_ct/default-ee.key b/security/manager/ssl/tests/unit/test_ct/default-ee.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/default-ee.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_ct/default-ee.key.keyspec b/security/manager/ssl/tests/unit/test_ct/default-ee.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_ct/default-ee.pem b/security/manager/ssl/tests/unit/test_ct/default-ee.pem
new file mode 100644
index 0000000000..c85c051004
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/default-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDiTCCAnGgAwIBAgIUUwG2e1zCLPYPQc2aSZ4UsI/GiK0wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcow
+gccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tghUqLnBp
+bm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcu
+ZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBs
+ZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxo
+b3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCbcbjTQmzRu++LJ/R1KjA99THZ
+aRGG7u0knPs40bz+rIOAR7SllYTvZ1g5HanNG3GZ5+DExVmVtixcrqJFTV0BJsi0
+rv8XR4F3Cdict+rJ+hCSBqu6BGNWdptsaSPiSm+eL//tgjGY1zm9ln1B/OvTYA/n
+f+OV07v44pwRBUe8C9Awb2J3KMHATPciKTk0Pwmh0jXi4FN9ehG1rXZMY2daHoKq
+hzbBc8EaGzPPAyFumHd6wNqWX+/chEtT00SlcJw/lbQZnK8XvUSOhRuUeRdCM5wX
+3w+Gy4P/FrI5tePoR9606GR6plC8QZxT3+Z6lTyCHz3I05+PNXwfmZH3ABSg
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ct/default-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ct/default-ee.pem.certspec
new file mode 100644
index 0000000000..554339ff52
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/default-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/test_ct/moz.build b/security/manager/ssl/tests/unit/test_ct/moz.build
new file mode 100644
index 0000000000..d0fc04f747
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/moz.build
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ct-valid.example.com.pem',
+# 'ct-insufficient-scts.example.com.pem',
+# 'default-ee.pem',
+# 'test-ca.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'default-ee.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_ct/test-ca.pem b/security/manager/ssl/tests/unit/test_ct/test-ca.pem
new file mode 100644
index 0000000000..40515addbd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/test-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0zCCAbugAwIBAgIUZekW+kmMLmUyacMUQAExM6vEFpswDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjASMRAwDgYDVQQDDAdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRME
+BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAoabyPFisrr0c
+kmPrDCY8pyDZ6ScxE/ziG+5RG4yJzW0QLrnt6rusMdKuxwaLiTISsNI14vh9QQyG
+XQsVeiGNGIwyJ9k2ZbLvw6pjdUeaswkthSqUDYwYgCljBwViuFkK4SAwya0Vb0p8
+pErDX8pzSF78inMB/7f0+DPdEQgtAGPYfB0gMRWOliyJSVoJXTgJ2B6PTMMvIcIO
+OXcqjDvVYY4o9+YtsDfmzGOSa/YmbM4hO6lv/cO3zv3aT+szyQK8X44koSkvct2P
+QL4cY3/incY0l0I12PaScOJDuvITiRNca7gxbUiT2jz1eqzGPIyVS6ku2cO0v9tP
+TIiD5XDdFg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ct/test-ca.pem.certspec b/security/manager/ssl/tests/unit/test_ct/test-ca.pem.certspec
new file mode 100644
index 0000000000..5d2435d7bb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ct/test-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test CA
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_db_format_pref_new.js b/security/manager/ssl/tests/unit/test_db_format_pref_new.js
new file mode 100644
index 0000000000..b242ad0832
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_db_format_pref_new.js
@@ -0,0 +1,22 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Tests that when PSM initializes, we create the sqlite-backed certificate and
+// key databases.
+
+function run_test() {
+ let profileDir = do_get_profile();
+ let certificateDBFile = profileDir.clone();
+ certificateDBFile.append("cert9.db");
+ ok(!certificateDBFile.exists(), "cert9.db should not exist beforehand");
+ let keyDBFile = profileDir.clone();
+ keyDBFile.append("key4.db");
+ ok(!keyDBFile.exists(), "key4.db should not exist beforehand");
+ // This should start PSM.
+ Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+ ok(certificateDBFile.exists(), "cert9.db should exist in the profile");
+ ok(keyDBFile.exists(), "key4.db should exist in the profile");
+}
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials.js b/security/manager/ssl/tests/unit/test_delegated_credentials.js
new file mode 100644
index 0000000000..a1623ddcb2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials.js
@@ -0,0 +1,91 @@
+/* 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/. */
+"use strict";
+
+// Tests handling of certificates marked as permitting delegated credentials
+
+function shouldBeDelegatedCredential(aTransportSecurityInfo) {
+ Assert.ok(
+ aTransportSecurityInfo.isDelegatedCredential,
+ "This host should have used a delegated credential"
+ );
+}
+
+function shouldNotBeDelegatedCredential(aTransportSecurityInfo) {
+ Assert.ok(
+ !aTransportSecurityInfo.isDelegatedCredential,
+ "This host should not have used a delegated credential"
+ );
+}
+
+do_get_profile();
+
+add_tls_server_setup(
+ "DelegatedCredentialsServer",
+ "test_delegated_credentials"
+);
+
+// Test:
+// Server certificate supports DC
+// Server DC support enabled
+// Client DC support disabled
+// Result: Successful connection without DC
+add_test(function() {
+ clearSessionCache();
+ Services.prefs.setBoolPref(
+ "security.tls.enable_delegated_credentials",
+ false
+ );
+ run_next_test();
+});
+add_connection_test(
+ "delegated-enabled.example.com",
+ PRErrorCodeSuccess,
+ null,
+ shouldNotBeDelegatedCredential
+);
+
+// Test:
+// Server certificate does not support DC
+// Server DC support enabled
+// Client DC support enabled
+// Result: SSL_ERROR_DC_INVALID_KEY_USAGE from client when
+// checking DC against EE cert, no DC in aTransportSecurityInfo.
+add_test(function() {
+ clearSessionCache();
+ Services.prefs.setBoolPref("security.tls.enable_delegated_credentials", true);
+ run_next_test();
+});
+add_connection_test(
+ "standard-enabled.example.com",
+ SSL_ERROR_DC_INVALID_KEY_USAGE,
+ null,
+ // We'll never |mHaveCipherSuiteAndProtocol|,
+ // and therefore can't check IsDelegatedCredential
+ null
+);
+
+// Test:
+// Server certificate supports DC
+// Server DC support disabled
+// Client DC support enabled
+// Result: Successful connection without DC
+add_connection_test(
+ "delegated-disabled.example.com",
+ PRErrorCodeSuccess,
+ null,
+ shouldNotBeDelegatedCredential
+);
+
+// Test:
+// Server certificate supports DC
+// Server DC support enabled
+// Client DC support enabled
+// Result: Successful connection with DC
+add_connection_test(
+ "delegated-enabled.example.com",
+ PRErrorCodeSuccess,
+ null,
+ shouldBeDelegatedCredential
+);
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key
new file mode 100644
index 0000000000..a926a54efb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIZFAPVcQvxWiZYGM
+1C7W/t8JrdkteLGOeh6f65VSRwKhRANCAARPv7u7YeD4+bGmClmshwTi7AULQj48
+9y6SPyxPeUtFXCpp0jNFbDbEEZ0HBuAO7cjRk5DXmRt7LQejBOqgSqbA
+-----END EC PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key.keyspec b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key.keyspec
new file mode 100644
index 0000000000..03c3ce198f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.key.keyspec
@@ -0,0 +1 @@
+secp256r1
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem
new file mode 100644
index 0000000000..332793b89e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSDCCATCgAwIBAgIUNeh3F5ZVYezEJKXRBQTRiDY/isMwDQYJKoZIhvcNAQEL
+BQAwLDEqMCgGA1UEAwwhZGVsZWdhdGVkLWNyZWRlbnRpYWwtaW50ZXJtZWRpYXRl
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMBUxEzARBgNVBAMM
+CmRlZmF1bHQtZWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARPv7u7YeD4+bGm
+ClmshwTi7AULQj489y6SPyxPeUtFXCpp0jNFbDbEEZ0HBuAO7cjRk5DXmRt7LQej
+BOqgSqbAo0AwPjATBgNVHSUEDDAKBggrBgEFBQcDATAnBgNVHREEIDAeghxzdGFu
+ZGFyZC1lbmFibGVkLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBqh2DB
+lbCuX6H9CPsdgDLX3z4gS5ab/1g7J4az29EQuFgb2z6gHOOuMDkLgDPpq3Vz0mrk
+emsoXToPdHsRZ4S57KeELRLT42bdyYkDR8yWTm588UhDNo3n492RD+dYHILmJdj0
+d9MWlMnl/jpXo8/a36ZJ2QgjSUyzwE/Nyxd8864Pk5ipUwyabSFjiEGcHsrfsSIQ
+fqkBXG0UudjjWEPInSQIBNIwCZL3HDwoeV2SO3gjxDwyume2S0K6GGXWs8a+mLBn
+Kks7nmQ8pEipNyHDt9TUS+Ywj6txK4cOp4WTh86q0h9mLWqqvg/DdTHLyFyzPWPa
+LZl7VR1aKXr9IzE0
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem.certspec b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem.certspec
new file mode 100644
index 0000000000..5ebe9b5ba7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/default-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:delegated-credential-intermediate
+subjectKey:secp256r1
+subject:default-ee
+extension:extKeyUsage:serverAuth
+extension:subjectAlternativeName:standard-enabled.example.com
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem
new file mode 100644
index 0000000000..ece3ae8834
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICiTCCAXGgAwIBAgIUV0N3VFQpvGVw2qP62b/L1yTchOIwDQYJKoZIhvcNAQEL
+BQAwLDEqMCgGA1UEAwwhZGVsZWdhdGVkLWNyZWRlbnRpYWwtaW50ZXJtZWRpYXRl
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMBcxFTATBgNVBAMM
+DGRlbGVnYXRlZC1lZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE+/u7th4Pj5
+saYKWayHBOLsBQtCPjz3LpI/LE95S0VcKmnSM0VsNsQRnQcG4A7tyNGTkNeZG3st
+B6ME6qBKpsCjfzB9MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDBI
+BgNVHREEQTA/gh1kZWxlZ2F0ZWQtZW5hYmxlZC5leGFtcGxlLmNvbYIeZGVsZWdh
+dGVkLWRpc2FibGVkLmV4YW1wbGUuY29tMA8GCSsGAQQBgtpLLAQCBQAwDQYJKoZI
+hvcNAQELBQADggEBAIxXv51QQR9JwM+Q7kQnRpK41Xe4WCDSwoyD+eOgbKznTJvz
+v3YDZKkO9cdQDGdd2GgMA/6/a0vc1wK1IO5eI2bAisIbublJ8xkuTeLyhdisGxMb
+k0XLlcQ/KnXid3R6dccHguQPpdtDrf2kgKE8Wy9Gvg9LF3A6hIx5BtruInflvgp6
+jtUGlKrftPMhRIEP5FWy5KOJE8VW+ja4zBH6P36kDaNeWK3SrssyNAZmoDrxslJa
+LDls1xZ4+ElGUjoywmEG0Fbbrv4mUiwCQ+4flHoopBGqs+3J0gY8SJndTZniSjSh
+i7ivXTPlrU0Sm8jt1e/RYlnA6IyvQp8YbR+zX34=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem.certspec b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem.certspec
new file mode 100644
index 0000000000..e90fa3b646
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated-ee.pem.certspec
@@ -0,0 +1,7 @@
+issuer:delegated-credential-intermediate
+subject:delegated-ee
+subjectKey:secp256r1
+extension:extKeyUsage:serverAuth
+extension:keyUsage:digitalSignature,keyEncipherment
+extension:subjectAlternativeName:delegated-enabled.example.com,delegated-disabled.example.com
+extension:delegationUsage:
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key
new file mode 100644
index 0000000000..1c1af40bda
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDADXHobENn6/oN7ZK2S
+8i9c7QeJGGU4ZptcbYcs7D2SYSKzk3crV2Av8xNl7+E5MkahZANiAAShaHJDNitc
+exiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05qnAwLCcjWOa3oMgbl
+uoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyDdKpuqc4=
+-----END EC PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key.keyspec b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key.keyspec
new file mode 100644
index 0000000000..11f041d996
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/delegated.key.keyspec
@@ -0,0 +1 @@
+secp384r1
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/moz.build b/security/manager/ssl/tests/unit/test_delegated_credentials/moz.build
new file mode 100644
index 0000000000..e6722917e1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'test-ca.pem',
+# 'test-int.pem',
+# 'delegated-ee.pem',
+# 'default-ee.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'default-ee.key',
+# 'delegated.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem b/security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem
new file mode 100644
index 0000000000..52e77a4498
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIUXeumyyPJUORUNVVSf/2pTjEDxS4wDQYJKoZIhvcNAQEL
+BQAwIjEgMB4GA1UEAwwXZGVsZWdhdGVkLWNyZWRlbnRpYWwtY2EwIhgPMjAxOTEx
+MjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowIjEgMB4GA1UEAwwXZGVsZWdhdGVk
+LWNyZWRlbnRpYWwtY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQD
+AgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCvQNfsGAcHMiKfrbxHpJVnmxTAzWBSqJnp
+V924Ofy80m03jI+99SEi9blzfHhJxRFe3cZYK3Dh4lhR3/RMHfZsepsAMzplS+to
+m1T7D9zwFpVuqfAOnlkPPneVf3hTvgmkQqiLWZUb/pWZ2fGWAKtDXPM5HHf6gBKY
++8VbTUpPHhwDH1MRauoK18QysMlBm90OwM6mnzCsJ6xwTDQkFAt4vQ+jS3AglhPd
+D5xqKbHwHi//h1EeV6u9mIJuX8HIZl5AM0Be4XaYb9gZFriflIjWxqmgh1alhKUM
+H2gTyrzG8NmD+J4CCLEygYdaFlj3QfBypdW/QpdOaV77u7dhmxzg
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem.certspec b/security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem.certspec
new file mode 100644
index 0000000000..91227f5da0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/test-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:delegated-credential-ca
+subject:delegated-credential-ca
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem b/security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem
new file mode 100644
index 0000000000..2425254722
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAeWgAwIBAgIUAcAYRaw4AL7AX+7V+/SNR/7bZ0QwDQYJKoZIhvcNAQEL
+BQAwIjEgMB4GA1UEAwwXZGVsZWdhdGVkLWNyZWRlbnRpYWwtY2EwIhgPMjAxOTEx
+MjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowLDEqMCgGA1UEAwwhZGVsZWdhdGVk
+LWNyZWRlbnRpYWwtaW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/
+MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAgnn4xLqeQUJDkL2Vo5Xl
+WYgCcmCa0n01OwenTjTtWyIkbXCLDvyA5F7E79pW+8RevUJkM/JUzo/ZR6qr2cnR
+H7lg4EEjVUC8zI60jvFeFZAelCoz6W4C/A4Y9pqLkDgJMWfdOstAe2cescqQPwsY
+AK0OtxhdB8x371vwL0hBDL1u6CV+s2TGBc5Pj9lTY6UEDSAXIww2DzipVzalctg9
+oiihe6KDJuN0vTTt46ImksVo05EWT+uq2mJvVMgpcOXShJCmjl/zSM0KVT2VFc6v
+xyeWCmmyaem/m9Do7Gnq+DVPII153dWmy1Xr0vX31PhroRETFnY5eyyc8umDD4Qu
+uA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem.certspec b/security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem.certspec
new file mode 100644
index 0000000000..64cc4e5693
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_delegated_credentials/test-int.pem.certspec
@@ -0,0 +1,4 @@
+issuer:delegated-credential-ca
+subject:delegated-credential-intermediate
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_der.js b/security/manager/ssl/tests/unit/test_der.js
new file mode 100644
index 0000000000..bae18e98e3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_der.js
@@ -0,0 +1,380 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests DER.jsm functionality.
+
+// Until DER.jsm is actually used in production code, this is where we have to
+// import it from.
+var { DER } = ChromeUtils.import("resource://gre/modules/psm/DER.jsm");
+
+function run_simple_tests() {
+ throws(
+ () => new DER.DERDecoder("this is not an array"),
+ /invalid input/,
+ "should throw given non-array input"
+ );
+ throws(
+ () => new DER.DERDecoder([0, "invalid input", 1]),
+ /invalid input/,
+ "should throw given non-byte data (string case)"
+ );
+ throws(
+ () => new DER.DERDecoder([31, 1, {}]),
+ /invalid input/,
+ "should throw given non-byte data (object case)"
+ );
+ throws(
+ () => new DER.DERDecoder([0.1, 3, 1]),
+ /invalid input/,
+ "should throw given non-byte data (non-integer case)"
+ );
+ throws(
+ () => new DER.DERDecoder([1, 3, -1]),
+ /invalid input/,
+ "should throw given non-byte data (negative integer case)"
+ );
+ throws(
+ () => new DER.DERDecoder([1, 300, 79]),
+ /invalid input/,
+ "should throw given non-byte data (large integer case)"
+ );
+
+ let testReadByte = new DER.DERDecoder([0x0a, 0x0b]);
+ equal(testReadByte.readByte(), 0x0a, "should read 0x0a");
+ equal(testReadByte.readByte(), 0x0b, "should read 0x0b");
+ throws(
+ () => testReadByte.readByte(),
+ /data truncated/,
+ "reading more data than is available should fail"
+ );
+
+ let testReadBytes = new DER.DERDecoder([0x0c, 0x0d, 0x0e]);
+ deepEqual(
+ testReadBytes.readBytes(3),
+ [0x0c, 0x0d, 0x0e],
+ "should read correct sequence of bytes"
+ );
+
+ let testReadNegativeBytes = new DER.DERDecoder([0xff, 0xaf]);
+ throws(
+ () => testReadNegativeBytes.readBytes(-4),
+ /invalid length/,
+ "reading a negative number of bytes should fail"
+ );
+
+ let testReadZeroBytes = new DER.DERDecoder([]);
+ equal(
+ testReadZeroBytes.readBytes(0).length,
+ 0,
+ "reading zero bytes should result in a zero-length array"
+ );
+
+ let testReadTooManyBytes = new DER.DERDecoder([0xab, 0xcd, 0xef]);
+ throws(
+ () => testReadTooManyBytes.readBytes(4),
+ /data truncated/,
+ "reading too many bytes should fail"
+ );
+
+ let testSEQUENCE = new DER.DERDecoder([0x30, 0x01, 0x01]);
+ let content = testSEQUENCE.readTagAndGetContents(DER.SEQUENCE);
+ equal(content.length, 1, "content should have length 1");
+ equal(content[0], 1, "value of content should be [1]");
+ ok(testSEQUENCE.atEnd(), "testSEQUENCE should be at the end of its input");
+ testSEQUENCE.assertAtEnd();
+
+ // The length purports to be 4 bytes, but there are only 2 available.
+ let truncatedSEQUENCE = new DER.DERDecoder([0x30, 0x04, 0x00, 0x00]);
+ throws(
+ () => truncatedSEQUENCE.readTagAndGetContents(DER.SEQUENCE),
+ /data truncated/,
+ "should get 'data truncated' error"
+ );
+
+ // With 2 bytes of content, there is 1 remaining after reading the content.
+ let extraDataSEQUENCE = new DER.DERDecoder([0x30, 0x02, 0xab, 0xcd, 0xef]);
+ content = extraDataSEQUENCE.readTagAndGetContents(DER.SEQUENCE);
+ equal(content.length, 2, "content should have length 2");
+ deepEqual(content, [0xab, 0xcd], "value of content should be [0xab, 0xcd]");
+ ok(
+ !extraDataSEQUENCE.atEnd(),
+ "extraDataSEQUENCE should not be at the end of its input"
+ );
+ throws(
+ () => extraDataSEQUENCE.assertAtEnd(),
+ /extra data/,
+ "should get 'extra data' error"
+ );
+
+ // The length of 0x81 0x01 is invalid because it could be encoded as just
+ // 0x01, which is shorter.
+ let invalidLengthSEQUENCE1 = new DER.DERDecoder([0x30, 0x81, 0x01, 0x00]);
+ throws(
+ () => invalidLengthSEQUENCE1.readTagAndGetContents(DER.SEQUENCE),
+ /invalid length/,
+ "should get 'invalid length' error"
+ );
+
+ // Similarly, 0x82 0x00 0x01 could be encoded as just 0x01, which is shorter.
+ let invalidLengthSEQUENCE2 = new DER.DERDecoder([
+ 0x30,
+ 0x82,
+ 0x00,
+ 0x01,
+ 0x00,
+ ]);
+ throws(
+ () => invalidLengthSEQUENCE2.readTagAndGetContents(DER.SEQUENCE),
+ /invalid length/,
+ "should get 'invalid length' error"
+ );
+
+ // Lengths requiring 4 bytes to encode are not supported.
+ let unsupportedLengthSEQUENCE = new DER.DERDecoder([
+ 0x30,
+ 0x83,
+ 0x01,
+ 0x01,
+ 0x01,
+ ]);
+ throws(
+ () => unsupportedLengthSEQUENCE.readTagAndGetContents(DER.SEQUENCE),
+ /unsupported length/,
+ "should get 'unsupported length' error"
+ );
+
+ // Indefinite lengths are not supported (and aren't DER anyway).
+ let unsupportedASN1SEQUENCE = new DER.DERDecoder([
+ 0x30,
+ 0x80,
+ 0x01,
+ 0x00,
+ 0x00,
+ ]);
+ throws(
+ () => unsupportedASN1SEQUENCE.readTagAndGetContents(DER.SEQUENCE),
+ /unsupported asn.1/,
+ "should get 'unsupported asn.1' error"
+ );
+
+ let unexpectedTag = new DER.DERDecoder([0x31, 0x01, 0x00]);
+ throws(
+ () => unexpectedTag.readTagAndGetContents(DER.SEQUENCE),
+ /unexpected tag/,
+ "should get 'unexpected tag' error"
+ );
+
+ let readTLVTestcase = new DER.DERDecoder([0x02, 0x03, 0x45, 0x67, 0x89]);
+ let bytes = readTLVTestcase.readTLV();
+ deepEqual(
+ bytes,
+ [0x02, 0x03, 0x45, 0x67, 0x89],
+ "bytes read with readTLV should be equal to expected value"
+ );
+
+ let peekTagTestcase = new DER.DERDecoder([0x30, 0x01, 0x00]);
+ ok(
+ peekTagTestcase.peekTag(DER.SEQUENCE),
+ "peekTag should return true for peeking with a SEQUENCE at a SEQUENCE"
+ );
+ ok(
+ !peekTagTestcase.peekTag(DER.SET),
+ "peekTag should return false for peeking with a SET at a SEQUENCE"
+ );
+ peekTagTestcase.readTLV();
+ ok(
+ !peekTagTestcase.peekTag(DER.SEQUENCE),
+ "peekTag should return false for peeking at a DER with no more data"
+ );
+
+ let tlvChoiceTestcase = new DER.DERDecoder([0x31, 0x02, 0xaa, 0xbb]);
+ let tlvChoiceContents = tlvChoiceTestcase.readTLVChoice([DER.NULL, DER.SET]);
+ deepEqual(
+ tlvChoiceContents,
+ [0x31, 0x02, 0xaa, 0xbb],
+ "readTLVChoice should return expected bytes"
+ );
+
+ let tlvChoiceNoMatchTestcase = new DER.DERDecoder([0x30, 0x01, 0xff]);
+ throws(
+ () => tlvChoiceNoMatchTestcase.readTLVChoice([DER.NULL, DER.SET]),
+ /unexpected tag/,
+ "readTLVChoice should throw if no matching tag is found"
+ );
+}
+
+function run_bit_string_tests() {
+ let bitstringDER = new DER.DERDecoder([0x03, 0x04, 0x03, 0x01, 0x02, 0xf8]);
+ let bitstring = bitstringDER.readBIT_STRING();
+ equal(bitstring.unusedBits, 3, "BIT STRING should have 3 unused bits");
+ deepEqual(
+ bitstring.contents,
+ [0x01, 0x02, 0xf8],
+ "BIT STRING should have expected contents"
+ );
+
+ let bitstringTooManyUnusedBits = new DER.DERDecoder([0x03, 0x02, 0x08, 0x00]);
+ throws(
+ () => bitstringTooManyUnusedBits.readBIT_STRING(),
+ /invalid BIT STRING encoding/,
+ "BIT STRING with too many unused bits should throw"
+ );
+
+ // A BIT STRING must have the unused bits byte, and so its length must be at
+ // least one.
+ let bitstringMissingUnusedBits = new DER.DERDecoder([0x03, 0x00]);
+ throws(
+ () => bitstringMissingUnusedBits.readBIT_STRING(),
+ /invalid BIT STRING encoding/,
+ "BIT STRING with missing unused bits (and no contents) should throw"
+ );
+
+ // The minimal BIT STRING is 03 01 00 (zero bits of padding and zero bytes of
+ // content).
+ let minimalBitstringDER = new DER.DERDecoder([0x03, 0x01, 0x00]);
+ let minimalBitstring = minimalBitstringDER.readBIT_STRING();
+ equal(
+ minimalBitstring.unusedBits,
+ 0,
+ "minimal BIT STRING should have 0 unused bits"
+ );
+ equal(
+ minimalBitstring.contents.length,
+ 0,
+ "minimal BIT STRING should have empty contents"
+ );
+
+ // However, a BIT STRING with zero bytes of content can't have any padding,
+ // because that makes no sense.
+ let noContentsPaddedBitstringDER = new DER.DERDecoder([0x03, 0x01, 0x03]);
+ throws(
+ () => noContentsPaddedBitstringDER.readBIT_STRING(),
+ /invalid BIT STRING encoding/,
+ "BIT STRING with no contents with non-zero padding should throw"
+ );
+}
+
+function run_compound_tests() {
+ let derBytes = [
+ 0x30,
+ 0x1a, // SEQUENCE
+ 0x02,
+ 0x02,
+ 0x77,
+ 0xff, // INTEGER
+ 0x06,
+ 0x03,
+ 0x2b,
+ 0x01,
+ 0x01, // OBJECT IDENTIFIER
+ 0x30,
+ 0x07, // SEQUENCE
+ 0x05,
+ 0x00, // NULL
+ 0x02,
+ 0x03,
+ 0x45,
+ 0x46,
+ 0x47, // INTEGER
+ 0x30,
+ 0x06, // SEQUENCE
+ 0x02,
+ 0x02,
+ 0x00,
+ 0xff, // INTEGER
+ 0x05,
+ 0x00,
+ ]; // NULL
+ let der = new DER.DERDecoder(derBytes);
+ let contents = new DER.DERDecoder(der.readTagAndGetContents(DER.SEQUENCE));
+ let firstINTEGER = contents.readTagAndGetContents(DER.INTEGER);
+ deepEqual(
+ firstINTEGER,
+ [0x77, 0xff],
+ "first INTEGER should have expected value"
+ );
+ let oid = contents.readTagAndGetContents(DER.OBJECT_IDENTIFIER);
+ deepEqual(
+ oid,
+ [0x2b, 0x01, 0x01],
+ "OBJECT IDENTIFIER should have expected value"
+ );
+
+ let firstNested = new DER.DERDecoder(
+ contents.readTagAndGetContents(DER.SEQUENCE)
+ );
+ let firstNestedNULL = firstNested.readTagAndGetContents(DER.NULL);
+ equal(
+ firstNestedNULL.length,
+ 0,
+ "first nested NULL should have expected value (empty array)"
+ );
+ let firstNestedINTEGER = firstNested.readTagAndGetContents(DER.INTEGER);
+ deepEqual(
+ firstNestedINTEGER,
+ [0x45, 0x46, 0x47],
+ "first nested INTEGER should have expected value"
+ );
+ firstNested.assertAtEnd();
+
+ let secondNested = new DER.DERDecoder(
+ contents.readTagAndGetContents(DER.SEQUENCE)
+ );
+ let secondNestedINTEGER = secondNested.readTagAndGetContents(DER.INTEGER);
+ deepEqual(
+ secondNestedINTEGER,
+ [0x00, 0xff],
+ "second nested INTEGER should have expected value"
+ );
+ let secondNestedNULL = secondNested.readTagAndGetContents(DER.NULL);
+ equal(
+ secondNestedNULL.length,
+ 0,
+ "second nested NULL should have expected value (empty array)"
+ );
+ secondNested.assertAtEnd();
+
+ contents.assertAtEnd();
+ der.assertAtEnd();
+
+ let invalidDERBytes = [
+ 0x30,
+ 0x06, // SEQUENCE
+ 0x30,
+ 0x02, // SEQUENCE
+ 0x02,
+ 0x01, // INTEGER (missing data)
+ 0x05,
+ 0x00, // NULL
+ 0x00,
+ 0x00,
+ ]; // (extra data)
+ let invalidDER = new DER.DERDecoder(invalidDERBytes);
+ let invalidContents = new DER.DERDecoder(
+ invalidDER.readTagAndGetContents(DER.SEQUENCE)
+ );
+ let invalidContentsContents = new DER.DERDecoder(
+ invalidContents.readTagAndGetContents(DER.SEQUENCE)
+ );
+ throws(
+ () => invalidContentsContents.readTagAndGetContents(DER.INTEGER),
+ /data truncated/,
+ "should throw due to missing data"
+ );
+ let nestedNULL = invalidContents.readTagAndGetContents(DER.NULL);
+ equal(nestedNULL.length, 0, "nested NULL should have expected value");
+ invalidContents.assertAtEnd();
+ throws(
+ () => invalidDER.assertAtEnd(),
+ /extra data/,
+ "should throw due to extra data"
+ );
+}
+
+function run_test() {
+ run_simple_tests();
+ run_bit_string_tests();
+ run_compound_tests();
+}
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello.js b/security/manager/ssl/tests/unit/test_encrypted_client_hello.js
new file mode 100644
index 0000000000..69d3df8645
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello.js
@@ -0,0 +1,93 @@
+/* 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/. */
+"use strict";
+
+// Tests handling of Encrypted Client Hello. These ECHConfigs
+// can be regenerated by running EncryptedClientHelloServer
+// and dumping the output of SSL_EncodeEchConfig. They do not
+// expire. An update here is only needed if the host or ECH
+// ciphersuite configuration changes, or if the keypair in
+// EncryptedClientHelloServer.cpp is modified.
+
+// Public name: ech-public.example.com
+const ECH_CONFIG_FIXED =
+ "AEr+CABGABZlY2gtcHVibGljLmV4YW1wbGUuY29tACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAgAAQAAQADADIAAA==";
+
+// Public name: ech-public.example.com, Unsupported AEAD to prompt retry_configs from a trusted host.
+const ECH_CONFIG_TRUSTED_RETRY =
+ "AEr+CABGABZlY2gtcHVibGljLmV4YW1wbGUuY29tACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAgAAQAAQABADIAAA==";
+
+// Public name: selfsigned.example.com. Unsupported AEAD to prompt retry_configs from an untrusted host.
+const ECH_CONFIG_UNTRUSTED_RETRY =
+ "AEr+CABGABZzZWxmc2lnbmVkLmV4YW1wbGUuY29tACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAgAAQAAQABADIAAA==";
+
+function shouldBeAcceptedEch(aTransportSecurityInfo) {
+ Assert.ok(
+ aTransportSecurityInfo.isAcceptedEch,
+ "This host should have accepted ECH"
+ );
+}
+
+function shouldBeRejectedEch(aTransportSecurityInfo) {
+ Assert.ok(
+ !aTransportSecurityInfo.isAcceptedEch,
+ "This host should have rejected ECH"
+ );
+}
+
+do_get_profile();
+
+add_tls_server_setup(
+ "EncryptedClientHelloServer",
+ "test_encrypted_client_hello"
+);
+
+// Connect directly without ECH first
+add_connection_test(
+ "ech-public.example.com",
+ PRErrorCodeSuccess,
+ null,
+ shouldBeRejectedEch
+);
+
+// Connect with ECH
+add_connection_test(
+ "ech-private.example.com",
+ PRErrorCodeSuccess,
+ null,
+ shouldBeAcceptedEch,
+ null,
+ null,
+ ECH_CONFIG_FIXED
+);
+
+// Trigger retry_configs by setting an ECHConfig with a different.
+// AEAD than the server supports.
+add_connection_test(
+ "ech-private.example.com",
+ SSL_ERROR_ECH_RETRY_WITH_ECH,
+ null,
+ null,
+ null,
+ null,
+ ECH_CONFIG_TRUSTED_RETRY
+);
+
+// Trigger retry_configs, but from a host that is untrusted
+// (due to a self-signed certificate for the public name).
+// Retry_configs must not be used or reported as available.
+add_connection_test(
+ "ech-private.example.com",
+ MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT,
+ null,
+ null,
+ null,
+ null,
+ ECH_CONFIG_UNTRUSTED_RETRY
+);
+
+// A client-only (retry_without_ech) test is located in
+// test_encrypted_client_hello_client_only.js We can't easily restart
+// a different server (one without ECHConfigs) here, so put that
+// test in a different file that launches a non-ECH server.
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key.keyspec b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem
new file mode 100644
index 0000000000..c71005fc19
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4DCCAcigAwIBAgIUP5hH78XA0upQ7cmqNASOW6WqkoUwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZWNoLWNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBgxFjAUBgNVBAMMDWVjaC1wdWJsaWMtZWUwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
+V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
+0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
+fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
+W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
+TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjJTAjMCEG
+A1UdEQQaMBiCFmVjaC1wdWJsaWMuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQAD
+ggEBAIgqt32ojY+McIFUq57FIpI0FsDnY/L+GsmtUziUjM0rfNTuDP0r+CF/1emA
+sTukf+YIiohoGjfRMebYdeWtpUjdG9O/Z1ygmGQNms6LP+xy6dPUM4G0sBpkfNdb
+u74ymuru0kMopYpVXuDsaYOPTNj2W6a7gLpn3llk8K4SVFEogUTlzzOBtYobf8L5
+9tSDpsMrTPINVbyi8SQHONMrsuqMfMDSTCkQnZJDcbRW56T2+PgrWJnQ78NDM0GB
+/KLRW6tIyyTco1IBrFC9+0j5jyHh4+3em0bT7sdcwidjZKipWsvcKK9DuycSSM6O
+nL8vxqnekennEVLV7zm6rjB/wns=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem.certspec b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem.certspec
new file mode 100644
index 0000000000..d5c332ceec
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/default-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ech-ca
+subject:ech-public-ee
+extension:subjectAlternativeName:ech-public.example.com
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/moz.build b/security/manager/ssl/tests/unit/test_encrypted_client_hello/moz.build
new file mode 100644
index 0000000000..a3755e779b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'default-ee.pem',
+# 'private-ee.pem',
+# 'selfsigned.pem',
+# 'test-ca.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'public-ee.key',
+# 'private-ee.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key.keyspec b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem
new file mode 100644
index 0000000000..258b0fc043
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4jCCAcqgAwIBAgIUSXmHaEjlH8Iry6lZ5kbx/TBwgI8wDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZWNoLWNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBkxFzAVBgNVBAMMDmVjaC1wcml2YXRlLWVlMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1
+aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/we
+adA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSS
+pH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62W
+YVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauR
+CE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyYwJDAi
+BgNVHREEGzAZghdlY2gtcHJpdmF0ZS5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsF
+AAOCAQEAI63cvfxAvxMKJBDTIle1hHjW9wf1FgRyus37hqdLatN3fa4o2vNRLfX7
+MITXlLcmqehrBj/Wi7thMOC77HGE3wGvUNog2Avk4exps/bR1ksTraY5cxM386he
+FG2QJ8im2VavNyqwmu2IcbPpZk5MKYolptmRFvz/6XGtLQsRjdtABKoUBB1tXlT2
+go6VULbEQn62fAdBvb/1CO6+jDWIpPzbpFSdWTBSpY1IfhAjsEP6W5kkDaKYwf8G
+IJslTTh+4v8Fq06ySdV1T8IYu29YuCye+sbWyWfcfPQqssaQzWpqAyBOO2kV3r3B
+XsaWXLsuXMnzBDJ5PM6Wsp8dLFXFzg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem.certspec b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem.certspec
new file mode 100644
index 0000000000..c152462f0d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/private-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ech-ca
+subject:ech-private-ee
+extension:subjectAlternativeName:ech-private.example.com
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem b/security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem
new file mode 100644
index 0000000000..f7f7c6f2df
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAeugAwIBAgIUKyuH4MWSNUsG95g0ExZZWo1i0tkwDQYJKoZIhvcNAQEL
+BQAwJjEkMCIGA1UEAwwbU2VsZi1zaWduZWQgVGVzdCBFbmQtZW50aXR5MCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCYxJDAiBgNVBAMMG1NlbGYt
+c2lnbmVkIFRlc3QgRW5kLWVudGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwIQYDVR0RBBowGIIWc2Vs
+ZnNpZ25lZC5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAqCxzMj+ECM64
+aG9XqCBHkrR45i6UO0jZkGGBRhUJunfSpK8g5jCuRH0wv4SPZG3tl7VgOtFHFhm5
+1SkEOstkShslHCWwEXoMuO9uvRGzjXl3bxES70ERHmcncp0yWXDT+QDyXM489X1M
+UtEFzWLMv22BX6J6xrvyhJK3zodHXtBSphG2NUEARPsgxb3sMiQA9jTacsQHC9yZ
+dmknp5zhYgCHntO8jFVc04O21wwMlzMB7IR/WFHs67opimHlQlBIWvRDOSsVvxdY
+y8ybGmymQ8g3hxHlQ5fc+oX64SDuHQa6kw2ZWQEz3e4It+VU5Y93QhCLgKqijfBr
+HRt0AadMpg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem.certspec b/security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem.certspec
new file mode 100644
index 0000000000..438a08eba2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/selfsigned.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Self-signed Test End-entity
+subject:Self-signed Test End-entity
+extension:subjectAlternativeName:selfsigned.example.com
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem b/security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem
new file mode 100644
index 0000000000..00b43c3d66
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUYc9/sZ6jdK4/ErAKn9/aEFPSgxowDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZWNoLWNhMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBExDzANBgNVBAMMBmVjaC1jYTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswDAYDVR0TBAUw
+AwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFHcA3r+hl4tWZnO
+tG1AsddSxeqxhFsZTNTwzLfnYRlFgxytmgE+r3pp5Aost6ZLhcagQT2uIklEBOnw
+se2evClYtC4yVd/FOLaGdd9YCGCggYpoNgNhNMHXI5qS28kslZLnbcD4HScYan66
+KcxGDSBRPggdOkOjhBHax3nni1+kWbzNIFQ2SWR0V/C2pxZJXDGlkE/sggD0nqG+
+iQWJytyJQJsKskNdUOh4R0sANDMTYEQfMrDVuNS6poqRj1C98JyP8qP+jsZZ33WQ
+brR7zbYDhBHdIi4MLZMgGCN+0VZ5PD26nT+9XekV8A/7+U29PxdviW6oZqpMT+sD
+ne3P1JM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem.certspec b/security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem.certspec
new file mode 100644
index 0000000000..1735a15075
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello/test-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ech-ca
+subject:ech-ca
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_encrypted_client_hello_client_only.js b/security/manager/ssl/tests/unit/test_encrypted_client_hello_client_only.js
new file mode 100644
index 0000000000..eb6cd40e26
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello_client_only.js
@@ -0,0 +1,33 @@
+/* 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/. */
+"use strict";
+
+// Public Name = delegated-enabled.example.com
+const ECH_CONFIG_FIXED =
+ "AFH+CABNAB1kZWxlZ2F0ZWQtZW5hYmxlZC5leGFtcGxlLmNvbQAgigdWOUn6xiMpNu1vNsT6c1kw7N6u9nNOMUrqw1pW/QoAIAAEAAEAAwAyAAA=";
+
+do_get_profile();
+
+// An arbitrary, non-ECH server.
+add_tls_server_setup(
+ "DelegatedCredentialsServer",
+ "test_delegated_credentials"
+);
+
+add_test(function() {
+ clearSessionCache();
+ run_next_test();
+});
+
+// Connect, sending ECH. The server is not configured for it,
+// but *is* authoritative for the public name.
+add_connection_test(
+ "delegated-disabled.example.com",
+ SSL_ERROR_ECH_RETRY_WITHOUT_ECH,
+ null,
+ null,
+ null,
+ null,
+ ECH_CONFIG_FIXED
+);
diff --git a/security/manager/ssl/tests/unit/test_enterprise_roots.js b/security/manager/ssl/tests/unit/test_enterprise_roots.js
new file mode 100644
index 0000000000..24537ccc41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_enterprise_roots.js
@@ -0,0 +1,82 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests enterprise root certificate support. When configured to do so, the
+// platform will attempt to find and import enterprise root certificates. This
+// feature is specific to Windows.
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+
+const { TestUtils } = ChromeUtils.import(
+ "resource://testing-common/TestUtils.jsm"
+);
+
+async function check_no_enterprise_roots_imported(
+ nssComponent,
+ certDB,
+ dbKey = undefined
+) {
+ let enterpriseRoots = nssComponent.getEnterpriseRoots();
+ notEqual(enterpriseRoots, null, "enterprise roots list should not be null");
+ equal(
+ enterpriseRoots.length,
+ 0,
+ "should not have imported any enterprise roots"
+ );
+ if (dbKey) {
+ let cert = certDB.findCertByDBKey(dbKey);
+ // If the garbage-collector hasn't run, there may be reachable copies of
+ // imported enterprise root certificates. If so, they shouldn't be trusted
+ // to issue TLS server auth certificates.
+ if (cert) {
+ await asyncTestCertificateUsages(certDB, cert, []);
+ }
+ }
+}
+
+async function check_some_enterprise_roots_imported(nssComponent, certDB) {
+ let enterpriseRoots = nssComponent.getEnterpriseRoots();
+ notEqual(enterpriseRoots, null, "enterprise roots list should not be null");
+ notEqual(
+ enterpriseRoots.length,
+ 0,
+ "should have imported some enterprise roots"
+ );
+ let foundNonBuiltIn = false;
+ let savedDBKey = null;
+ for (let certDer of enterpriseRoots) {
+ let cert = certDB.constructX509(certDer);
+ notEqual(cert, null, "should be able to decode cert from DER");
+ if (!cert.isBuiltInRoot && !savedDBKey) {
+ foundNonBuiltIn = true;
+ savedDBKey = cert.dbKey;
+ info("saving dbKey from " + cert.commonName);
+ await asyncTestCertificateUsages(certDB, cert, [certificateUsageSSLCA]);
+ break;
+ }
+ }
+ ok(foundNonBuiltIn, "should have found non-built-in root");
+ return savedDBKey;
+}
+
+add_task(async function run_test() {
+ let nssComponent = Cc["@mozilla.org/psm;1"].getService(Ci.nsINSSComponent);
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ nssComponent.getEnterpriseRoots(); // blocks until roots are loaded
+ Services.prefs.setBoolPref("security.enterprise_roots.enabled", false);
+ await check_no_enterprise_roots_imported(nssComponent, certDB);
+ Services.prefs.setBoolPref("security.enterprise_roots.enabled", true);
+ await TestUtils.topicObserved("psm:enterprise-certs-imported");
+ let savedDBKey = await check_some_enterprise_roots_imported(
+ nssComponent,
+ certDB
+ );
+ Services.prefs.setBoolPref("security.enterprise_roots.enabled", false);
+ await check_no_enterprise_roots_imported(nssComponent, certDB, savedDBKey);
+});
diff --git a/security/manager/ssl/tests/unit/test_ev_certs.js b/security/manager/ssl/tests/unit/test_ev_certs.js
new file mode 100644
index 0000000000..937e4509f3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs.js
@@ -0,0 +1,437 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests that end-entity certificates that should successfully verify as EV
+// (Extended Validation) do so and that end-entity certificates that should not
+// successfully verify as EV do not. Also tests related situations (e.g. that
+// failure to fetch an OCSP response results in no EV treatment).
+//
+// A quick note about the certificates in these tests: generally, an EV
+// certificate chain will have an end-entity with a specific policy OID followed
+// by an intermediate with the anyPolicy OID chaining to a root with no policy
+// OID (since it's a trust anchor, it can be omitted). In these tests, the
+// specific policy OID is 1.3.6.1.4.1.13769.666.666.666.1.500.9.1 and is
+// referred to as the test OID. In order to reflect what will commonly be
+// encountered, the end-entity of any given test path will have the test OID
+// unless otherwise specified in the name of the test path. Similarly, the
+// intermediate will have the anyPolicy OID, again unless otherwise specified.
+// For example, for the path where the end-entity does not have an OCSP URI
+// (referred to as "no-ocsp-ee-path-{ee,int}", the end-entity has the test OID
+// whereas the intermediate has the anyPolicy OID.
+// For another example, for the test OID path ("test-oid-path-{ee,int}"), both
+// the end-entity and the intermediate have the test OID.
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("network.dns.localDomains");
+ Services.prefs.clearUserPref("security.OCSP.enabled");
+});
+
+Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
+Services.prefs.setIntPref("security.OCSP.enabled", 1);
+const evroot = addCertFromFile(certdb, "test_ev_certs/evroot.pem", "CTu,,");
+addCertFromFile(certdb, "test_ev_certs/non-evroot-ca.pem", "CTu,,");
+
+const SERVER_PORT = 8888;
+
+function failingOCSPResponder() {
+ return getFailingHttpServer(SERVER_PORT, ["www.example.com"]);
+}
+
+class EVCertVerificationResult {
+ constructor(
+ testcase,
+ expectedPRErrorCode,
+ expectedEV,
+ resolve,
+ ocspResponder
+ ) {
+ this.testcase = testcase;
+ this.expectedPRErrorCode = expectedPRErrorCode;
+ this.expectedEV = expectedEV;
+ this.resolve = resolve;
+ this.ocspResponder = ocspResponder;
+ }
+
+ verifyCertFinished(prErrorCode, verifiedChain, hasEVPolicy) {
+ equal(
+ prErrorCode,
+ this.expectedPRErrorCode,
+ `${this.testcase} should have expected error code`
+ );
+ equal(
+ hasEVPolicy,
+ this.expectedEV,
+ `${this.testcase} should result in expected EV status`
+ );
+ this.ocspResponder.stop(this.resolve);
+ }
+}
+
+function asyncTestEV(
+ cert,
+ expectedPRErrorCode,
+ expectedEV,
+ expectedOCSPRequestPaths,
+ ocspResponseTypes = undefined
+) {
+ let now = Date.now() / 1000;
+ return new Promise((resolve, reject) => {
+ let ocspResponder =
+ expectedOCSPRequestPaths.length > 0
+ ? startOCSPResponder(
+ SERVER_PORT,
+ "www.example.com",
+ "test_ev_certs",
+ expectedOCSPRequestPaths,
+ expectedOCSPRequestPaths.slice(),
+ null,
+ ocspResponseTypes
+ )
+ : failingOCSPResponder();
+ let result = new EVCertVerificationResult(
+ cert.subjectName,
+ expectedPRErrorCode,
+ expectedEV,
+ resolve,
+ ocspResponder
+ );
+ certdb.asyncVerifyCertAtTime(
+ cert,
+ certificateUsageSSLServer,
+ 0,
+ "ev-test.example.com",
+ now,
+ result
+ );
+ });
+}
+
+function ensureVerifiesAsEV(testcase) {
+ let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
+ addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
+ let expectedOCSPRequestPaths = gEVExpected
+ ? [`${testcase}-int`, `${testcase}-ee`]
+ : [`${testcase}-ee`];
+ return asyncTestEV(
+ cert,
+ PRErrorCodeSuccess,
+ gEVExpected,
+ expectedOCSPRequestPaths
+ );
+}
+
+function ensureVerifiesAsEVWithNoOCSPRequests(testcase) {
+ let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
+ addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
+ return asyncTestEV(cert, PRErrorCodeSuccess, gEVExpected, []);
+}
+
+function ensureVerifiesAsDV(testcase, expectedOCSPRequestPaths = undefined) {
+ let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
+ addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
+ return asyncTestEV(
+ cert,
+ PRErrorCodeSuccess,
+ false,
+ expectedOCSPRequestPaths ? expectedOCSPRequestPaths : [`${testcase}-ee`]
+ );
+}
+
+function ensureVerificationFails(testcase, expectedPRErrorCode) {
+ let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
+ addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
+ return asyncTestEV(cert, expectedPRErrorCode, false, []);
+}
+
+function verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, expectSuccess) {
+ let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
+ addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
+ let now = Date.now() / 1000;
+ let expectedErrorCode = SEC_ERROR_POLICY_VALIDATION_FAILED;
+ if (expectSuccess && gEVExpected) {
+ expectedErrorCode = PRErrorCodeSuccess;
+ }
+ return new Promise((resolve, reject) => {
+ let ocspResponder = failingOCSPResponder();
+ let result = new EVCertVerificationResult(
+ cert.subjectName,
+ expectedErrorCode,
+ expectSuccess && gEVExpected,
+ resolve,
+ ocspResponder
+ );
+ let flags =
+ Ci.nsIX509CertDB.FLAG_LOCAL_ONLY | Ci.nsIX509CertDB.FLAG_MUST_BE_EV;
+ certdb.asyncVerifyCertAtTime(
+ cert,
+ certificateUsageSSLServer,
+ flags,
+ "ev-test.example.com",
+ now,
+ result
+ );
+ });
+}
+
+function ensureNoOCSPMeansNoEV(testcase) {
+ return verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, false);
+}
+
+function ensureVerifiesAsEVWithFLAG_LOCAL_ONLY(testcase) {
+ return verifyWithFlags_LOCAL_ONLY_and_MUST_BE_EV(testcase, true);
+}
+
+function ensureOneCRLSkipsOCSPForIntermediates(testcase) {
+ let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
+ addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
+ return asyncTestEV(cert, PRErrorCodeSuccess, gEVExpected, [`${testcase}-ee`]);
+}
+
+function verifyWithDifferentOCSPResponseTypes(testcase, responses, expectEV) {
+ let cert = constructCertFromFile(`test_ev_certs/${testcase}-ee.pem`);
+ addCertFromFile(certdb, `test_ev_certs/${testcase}-int.pem`, ",,");
+ let expectedOCSPRequestPaths = gEVExpected
+ ? [`${testcase}-int`, `${testcase}-ee`]
+ : [`${testcase}-ee`];
+ let ocspResponseTypes = gEVExpected ? responses : responses.slice(1);
+ return asyncTestEV(
+ cert,
+ PRErrorCodeSuccess,
+ gEVExpected && expectEV,
+ expectedOCSPRequestPaths,
+ ocspResponseTypes
+ );
+}
+
+function ensureVerifiesAsEVWithOldIntermediateOCSPResponse(testcase) {
+ return verifyWithDifferentOCSPResponseTypes(
+ testcase,
+ ["longvalidityalmostold", "good"],
+ true
+ );
+}
+
+function ensureVerifiesAsDVWithOldEndEntityOCSPResponse(testcase) {
+ return verifyWithDifferentOCSPResponseTypes(
+ testcase,
+ ["good", "longvalidityalmostold"],
+ false
+ );
+}
+
+function ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse(testcase) {
+ return verifyWithDifferentOCSPResponseTypes(
+ testcase,
+ ["good", "ancientstillvalid"],
+ false
+ );
+}
+
+// These should all verify as EV.
+add_task(async function plainExpectSuccessEVTests() {
+ await ensureVerifiesAsEV("anyPolicy-int-path");
+ await ensureVerifiesAsEV("test-oid-path");
+ await ensureVerifiesAsEV("cabforum-oid-path");
+ await ensureVerifiesAsEV("cabforum-and-test-oid-ee-path");
+ await ensureVerifiesAsEV("test-and-cabforum-oid-ee-path");
+ await ensureVerifiesAsEV("reverse-order-oids-path");
+ // In this case, the end-entity has both the CA/B Forum OID and the test OID
+ // (in that order). The intermediate has the CA/B Forum OID. Since the
+ // implementation uses the first EV policy it encounters in the end-entity as
+ // the required one, this successfully verifies as EV.
+ await ensureVerifiesAsEV("cabforum-and-test-oid-ee-cabforum-oid-int-path");
+});
+
+// These fail for various reasons to verify as EV, but fallback to DV should
+// succeed.
+add_task(async function expectDVFallbackTests() {
+ await ensureVerifiesAsDV("anyPolicy-ee-path");
+ await ensureVerifiesAsDV("non-ev-root-path");
+ await ensureVerifiesAsDV(
+ "no-ocsp-ee-path",
+ gEVExpected ? ["no-ocsp-ee-path-int"] : []
+ );
+ await ensureVerifiesAsDV("no-ocsp-int-path");
+ // In this case, the end-entity has the test OID and the intermediate has the
+ // CA/B Forum OID. Since the CA/B Forum OID is not treated the same as the
+ // anyPolicy OID, this will not verify as EV.
+ await ensureVerifiesAsDV("test-oid-ee-cabforum-oid-int-path");
+ // In this case, the end-entity has both the test OID and the CA/B Forum OID
+ // (in that order). The intermediate has only the CA/B Forum OID. Since the
+ // implementation uses the first EV policy it encounters in the end-entity as
+ // the required one, this fails to verify as EV.
+ await ensureVerifiesAsDV("test-and-cabforum-oid-ee-cabforum-oid-int-path");
+});
+
+// Test that removing the trust bits from an EV root causes verifications
+// relying on that root to fail (and then test that adding back the trust bits
+// causes the verifications to succeed again).
+add_task(async function evRootTrustTests() {
+ clearOCSPCache();
+ info("untrusting evroot");
+ certdb.setCertTrust(
+ evroot,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.UNTRUSTED
+ );
+ await ensureVerificationFails("test-oid-path", SEC_ERROR_UNKNOWN_ISSUER);
+ info("re-trusting evroot");
+ certdb.setCertTrust(
+ evroot,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_SSL
+ );
+ await ensureVerifiesAsEV("test-oid-path");
+});
+
+// Test that if FLAG_LOCAL_ONLY and FLAG_MUST_BE_EV are specified, that no OCSP
+// requests are made (this also means that nothing will verify as EV).
+add_task(async function localOnlyMustBeEVTests() {
+ clearOCSPCache();
+ await ensureNoOCSPMeansNoEV("anyPolicy-ee-path");
+ await ensureNoOCSPMeansNoEV("anyPolicy-int-path");
+ await ensureNoOCSPMeansNoEV("non-ev-root-path");
+ await ensureNoOCSPMeansNoEV("no-ocsp-ee-path");
+ await ensureNoOCSPMeansNoEV("no-ocsp-int-path");
+ await ensureNoOCSPMeansNoEV("test-oid-path");
+});
+
+// Under certain conditions, OneCRL allows us to skip OCSP requests for
+// intermediates.
+add_task(async function oneCRLTests() {
+ clearOCSPCache();
+
+ // enable OneCRL OCSP skipping - allow staleness of up to 30 hours
+ Services.prefs.setIntPref(
+ "security.onecrl.maximum_staleness_in_seconds",
+ 108000
+ );
+ // set the blocklist-background-update-timer value to the recent past
+ Services.prefs.setIntPref(
+ "services.settings.security.onecrl.checked",
+ Math.floor(Date.now() / 1000) - 1
+ );
+ Services.prefs.setIntPref(
+ "app.update.lastUpdateTime.blocklist-background-update-timer",
+ Math.floor(Date.now() / 1000) - 1
+ );
+
+ await ensureOneCRLSkipsOCSPForIntermediates("anyPolicy-int-path");
+ await ensureOneCRLSkipsOCSPForIntermediates("no-ocsp-int-path");
+ await ensureOneCRLSkipsOCSPForIntermediates("test-oid-path");
+
+ clearOCSPCache();
+ // disable OneCRL OCSP Skipping (no staleness allowed)
+ Services.prefs.setIntPref("security.onecrl.maximum_staleness_in_seconds", 0);
+ await ensureVerifiesAsEV("anyPolicy-int-path");
+ // Because the intermediate in this case is missing an OCSP URI, it will not
+ // validate as EV, but it should fall back to DV.
+ await ensureVerifiesAsDV("no-ocsp-int-path");
+ await ensureVerifiesAsEV("test-oid-path");
+
+ clearOCSPCache();
+ // enable OneCRL OCSP skipping - allow staleness of up to 30 hours
+ Services.prefs.setIntPref(
+ "security.onecrl.maximum_staleness_in_seconds",
+ 108000
+ );
+ // set the blocklist-background-update-timer value to the more distant past
+ Services.prefs.setIntPref(
+ "services.settings.security.onecrl.checked",
+ Math.floor(Date.now() / 1000) - 108080
+ );
+ Services.prefs.setIntPref(
+ "app.update.lastUpdateTime.blocklist-background-update-timer",
+ Math.floor(Date.now() / 1000) - 108080
+ );
+ await ensureVerifiesAsEV("anyPolicy-int-path");
+ await ensureVerifiesAsDV("no-ocsp-int-path");
+ await ensureVerifiesAsEV("test-oid-path");
+
+ clearOCSPCache();
+ // test the OCSP behavior when services.settings.security.onecrl.checked is in the
+ // distant past and blacklist-background-update-timer is recent
+ // enable OneCRL OCSP skipping - allow staleness of up to 30 hours
+ Services.prefs.setIntPref(
+ "security.onecrl.maximum_staleness_in_seconds",
+ 108000
+ );
+ // set the blocklist-background-update-timer value to the recent past
+ // (services.settings.security.onecrl.checked defaults to 0)
+ Services.prefs.setIntPref(
+ "app.update.lastUpdateTime.blocklist-background-update-timer",
+ Math.floor(Date.now() / 1000) - 1
+ );
+
+ await ensureVerifiesAsEV("anyPolicy-int-path");
+ await ensureVerifiesAsDV("no-ocsp-int-path");
+ await ensureVerifiesAsEV("test-oid-path");
+
+ clearOCSPCache();
+ // test the OCSP behavior when services.settings.security.onecrl.checked is recent
+ // enable OneCRL OCSP skipping - allow staleness of up to 30 hours
+ Services.prefs.setIntPref(
+ "security.onecrl.maximum_staleness_in_seconds",
+ 108000
+ );
+ // now set services.settings.security.onecrl.checked to a recent value
+ Services.prefs.setIntPref(
+ "services.settings.security.onecrl.checked",
+ Math.floor(Date.now() / 1000) - 1
+ );
+ await ensureOneCRLSkipsOCSPForIntermediates("anyPolicy-int-path");
+ await ensureOneCRLSkipsOCSPForIntermediates("no-ocsp-int-path");
+ await ensureOneCRLSkipsOCSPForIntermediates("test-oid-path");
+
+ Services.prefs.clearUserPref("security.onecrl.maximum_staleness_in_seconds");
+ Services.prefs.clearUserPref("services.settings.security.onecrl.checked");
+ Services.prefs.clearUserPref(
+ "app.update.lastUpdateTime.blocklist-background-update-timer"
+ );
+});
+
+// Prime the OCSP cache and then ensure that we can validate certificates as EV
+// without hitting the network. There's two cases here: one where we simply
+// validate like normal and then check that the network was never accessed and
+// another where we use flags to mandate that the network not be used.
+add_task(async function ocspCachingTests() {
+ clearOCSPCache();
+
+ await ensureVerifiesAsEV("anyPolicy-int-path");
+ await ensureVerifiesAsEV("test-oid-path");
+
+ await ensureVerifiesAsEVWithNoOCSPRequests("anyPolicy-int-path");
+ await ensureVerifiesAsEVWithNoOCSPRequests("test-oid-path");
+
+ await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("anyPolicy-int-path");
+ await ensureVerifiesAsEVWithFLAG_LOCAL_ONLY("test-oid-path");
+});
+
+// Old-but-still-valid OCSP responses are accepted for intermediates but not
+// end-entity certificates (because of OCSP soft-fail this results in DV
+// fallback).
+add_task(async function oldOCSPResponseTests() {
+ clearOCSPCache();
+
+ await ensureVerifiesAsEVWithOldIntermediateOCSPResponse("anyPolicy-int-path");
+ await ensureVerifiesAsEVWithOldIntermediateOCSPResponse("test-oid-path");
+
+ clearOCSPCache();
+ await ensureVerifiesAsDVWithOldEndEntityOCSPResponse("anyPolicy-int-path");
+ await ensureVerifiesAsDVWithOldEndEntityOCSPResponse("test-oid-path");
+
+ clearOCSPCache();
+ await ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse(
+ "anyPolicy-int-path"
+ );
+ await ensureVerifiesAsDVWithVeryOldEndEntityOCSPResponse("test-oid-path");
+});
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem
new file mode 100644
index 0000000000..7326ef6d4e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVzCCAj+gAwIBAgIUQx+5x/tjsPB8QKeRGMqa5Xa/prYwDQYJKoZIhvcNAQEL
+BQAwIDEeMBwGA1UEAwwVYW55UG9saWN5LWVlLXBhdGgtaW50MCIYDzIwMTkxMTI4
+MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMB8xHTAbBgNVBAMMFGFueVBvbGljeS1l
+ZS1wYXRoLWVlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESO
+FtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVr
+amRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWka
+sdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbY
+VbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6n
+aOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHE
+MdUDrNoYCjXtjQIDAQABo4GFMIGCME0GCCsGAQUFBwEBBEEwPzA9BggrBgEFBQcw
+AYYxaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L2FueVBvbGljeS1lZS1wYXRo
+LWVlLzARBgNVHSAECjAIMAYGBFUdIAAwHgYDVR0RBBcwFYITZXYtdGVzdC5leGFt
+cGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAM5dSETXj42Kai/sDKzZOaltmATbf
+RDm9U8Pa1luk+m1Y2wgPDQfjcklPIkbyxsAwhdy1OCAUSVjWttAKs5w23TroT5Dq
+tJAt823UD2xjwA3x1tTTmielwhc5cRL9uddrKAZ+EnRNLsI4wlTvjX1OBS61BgA6
+V/4//SJgS72Yj1Ebl9D5mA6iVHyUZblAwhsrogTwzWKMPzcAVgGp7YQK4b1yV4vc
+zmcx9HrlsJuwpNwPQvysOhtOcbX1CbCdq9qzj6cCdIOZ3H5qzrr9swoZreVUsbic
+xeHxhXGcp4+Bk+djBqlKvSGuw9FSlOSgpaB/K3ceDRjewje2rzTgAzSg7Q==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem.certspec
new file mode 100644
index 0000000000..a9175c32ed
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:anyPolicy-ee-path-int
+subject:anyPolicy-ee-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/anyPolicy-ee-path-ee/
+extension:certificatePolicies:any
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem
new file mode 100644
index 0000000000..faaea5d176
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRDCCAiygAwIBAgIUR7Qq6/A9VAD2EfTAFB5di93IJx8wDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCAxHjAcBgNVBAMMFWFueVBvbGljeS1lZS1wYXRoLWludDCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+AaOBgDB+MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGME4GCCsGAQUFBwEBBEIw
+QDA+BggrBgEFBQcwAYYyaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L2FueVBv
+bGljeS1lZS1wYXRoLWludC8wEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEB
+CwUAA4IBAQCCOVv8KxGwhAsLedhqkwyLkg7MnmFKYdGJo5LHXU5eszyRTHNy5L5/
+nvy6uCwA4h/FBL/XZb+VYIvbMpL8i7Oduf8kvWGWkfNPfUNgXK2ZdX3y1uBQda89
+kX2FTuP2ajdjZHkHPuA2wzpce8pDzlv0ULvIV4anOGgxuYqxKM1ihddYs/6LT/vs
+cFjLJDLIvqv7heucPJ3ungJY4IEaYRv3zEXwrS3FY/zakfpEGLqTNlLOppp82LPB
+7McNBxcxXrFdSfWqbLTl8U93Xe48utWl/qmUA/0+QBB7MvnaABss8pmzPGnC/FSV
+AhS442Jz7DkpyTFzKuWarWdQN4ggoTIg
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem.certspec
new file mode 100644
index 0000000000..c49f6c4bcc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-ee-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:anyPolicy-ee-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/anyPolicy-ee-path-int/
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem
new file mode 100644
index 0000000000..8b2f95b556
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIUCeueEj6OFftd56Jw0VZ9VzHQVcowDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWYW55UG9saWN5LWludC1wYXRoLWludDAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAgMR4wHAYDVQQDDBVhbnlQb2xpY3kt
+aW50LXBhdGgtZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjgZQwgZEwTgYIKwYBBQUHAQEEQjBAMD4GCCsGAQUF
+BzABhjJodHRwOi8vd3d3LmV4YW1wbGUuY29tOjg4ODgvYW55UG9saWN5LWludC1w
+YXRoLWVlLzAfBgNVHSAEGDAWMBQGEisGAQQB60mFGoUahRoBg3QJATAeBgNVHREE
+FzAVghNldi10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAROBgq
+JsnJtkdWyOTrG4jVhQ6WU6tmiUXEHGqDr8ARrRImX9+oekgKcGN7gyiDkvGq74rm
+uqaeD4AlLkx9kXMKAd9Imr+efoOhzBbmZapCzeyrDFu343RIis2yjCSsjyLrNASZ
+JWmuwsL44WOY/vSem7NIkbVEw6po0nxYpsNA94fgfPJA8Ut82vixr7+dnrCWlWM1
+7boWU8S56HuDi9TjiE4ADqmbJ+j+UZcEktCIFo9tVMh7tbd9KbO54/nDG+qgAfJU
+yk8VJrYucrJa+Vtsa3CrCG640NcUD17618iwYw1c2Rseo/3yixTIQY8ujjSqC4j/
+oBiqhI8Wl7QOmX4h
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem.certspec
new file mode 100644
index 0000000000..1c643c2f95
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:anyPolicy-int-path-int
+subject:anyPolicy-int-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/anyPolicy-int-path-ee/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem
new file mode 100644
index 0000000000..bbb407b7d1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRjCCAi6gAwIBAgIUMw+R5DkD+4bOA7KD8UDYcG3RD2IwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCExHzAdBgNVBAMMFmFueVBvbGljeS1pbnQtcGF0aC1pbnQwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT
+2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzV
+JJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8N
+jf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCA
+BiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVh
+He4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMB
+AAGjgYEwfzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjBPBggrBgEFBQcBAQRD
+MEEwPwYIKwYBBQUHMAGGM2h0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4OC9hbnlQ
+b2xpY3ktaW50LXBhdGgtaW50LzARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcN
+AQELBQADggEBAEHWJ58QNHayFGL5fxbvN66qffLJVpO5L+NM5MIaeEykSj7Weaem
+8Fx6V8b+j6TKs6DD4yy7o/5PODbjc7v62a25XbTE4VrlG9T58hnxRpOXfMeHFu3L
+oJajIIrJI4Am23rvHyQ/achk+cKqGIgBC6eBaEb5bV13OeM4dWM34Qo0VDmOAyo5
+Rgfeh0W5XBPi7fcnDhbIyJ3JQBhlHotR3krBv98nhy5mCi4bxTjERKMAgYaLvxUb
+UOAT87BxNgHAiP4vvXbxl5T/TD1KLOFuypa0Q2LpUONbW6c4fcfKRoWkyA1u3+sE
+RB/0RTleOS8Ekwzex8FgQMp9D5BrIA7JDbg=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem.certspec
new file mode 100644
index 0000000000..5f5adacc7f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/anyPolicy-int-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:anyPolicy-int-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/anyPolicy-int-path-int/
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem
new file mode 100644
index 0000000000..c3a64d8890
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIUAXnU/fLGVanzfXZztetMUPvcgM0wDQYJKoZIhvcNAQEL
+BQAwPTE7MDkGA1UEAwwyY2FiZm9ydW0tYW5kLXRlc3Qtb2lkLWVlLWNhYmZvcnVt
+LW9pZC1pbnQtcGF0aC1pbnQwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowPDE6MDgGA1UEAwwxY2FiZm9ydW0tYW5kLXRlc3Qtb2lkLWVlLWNhYmZv
+cnVtLW9pZC1pbnQtcGF0aC1lZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAab
+bhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmts
+Du0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhI
+H6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8
+rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kX
+Mbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaOBuTCBtjBqBggrBgEFBQcBAQReMFww
+WgYIKwYBBQUHMAGGTmh0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4OC9jYWJmb3J1
+bS1hbmQtdGVzdC1vaWQtZWUtY2FiZm9ydW0tb2lkLWludC1wYXRoLWVlLzAoBgNV
+HSAEITAfMAcGBWeBDAEBMBQGEisGAQQB60mFGoUahRoBg3QJATAeBgNVHREEFzAV
+ghNldi10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBAyAiPnxab
+CxbHi47hZ8xWcfpmyItTzPCzsKx9VGkbnFM7hA7p0CP116YlFny9wJoOi49+Qm3D
+qyrnKPWme1tyW9DuDe0jz5eiUcBZ72jL5CZbXkQ1LfPKt1sGnwHoAu0NDNPeE/lQ
+0RDSp/XW1kgjBDPDlTJ6k1MMdy9A0fEQosUH4bOpTOEeeTl3ZGVxJZmc2PcZebdV
+Z/fizZ1JiKalKpYcAvgXGLw5HE+/OvGFlPrqxcQRa2f+MZFFDS/X7ZAiIFp7qqZg
+h314bFrrPpB/v2oLMz2tdvTrIxJfj3+VXw19zkxuWavl6a/V5jiEZUQsJLgjLGdS
+zQ9m/Jf6yLjU
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem.certspec
new file mode 100644
index 0000000000..c72237e453
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:cabforum-and-test-oid-ee-cabforum-oid-int-path-int
+subject:cabforum-and-test-oid-ee-cabforum-oid-int-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/cabforum-and-test-oid-ee-cabforum-oid-int-path-ee/
+extension:certificatePolicies:2.23.140.1.1,1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem
new file mode 100644
index 0000000000..b4728a8ea6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDgDCCAmigAwIBAgIUIejtPWmffl6VTrZjY+grxp67lQAwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMD0xOzA5BgNVBAMMMmNhYmZvcnVtLWFuZC10ZXN0LW9pZC1lZS1j
+YWJmb3J1bS1vaWQtaW50LXBhdGgtaW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GfMIGcMAwGA1UdEwQFMAMB
+Af8wCwYDVR0PBAQDAgEGMGsGCCsGAQUFBwEBBF8wXTBbBggrBgEFBQcwAYZPaHR0
+cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L2NhYmZvcnVtLWFuZC10ZXN0LW9pZC1l
+ZS1jYWJmb3J1bS1vaWQtaW50LXBhdGgtaW50LzASBgNVHSAECzAJMAcGBWeBDAEB
+MA0GCSqGSIb3DQEBCwUAA4IBAQB/zhtz6FPxar2eL9imF0/5lchKCYPLoHtMkMPo
+YPmyfsOODBw0MMTKlzvzYdy4hORU8zQ8JiKJa28GlyIf08fdtaIWPjf+PmNGhMex
+pD0KTCfJrmOeDVNwf3lKwM5TrWnH7nNTQ4SfRQCPBR91H6XuwgmuD8vTe1oebuX5
+ziPsjHgKp6qR707qlNQ54JSkTEnY7iLSB8O033Nrzqbz1LtFTX1kGLy0h4F/DKIJ
+q2yzixcU5mHwQHKKIiirjy6ZW25Acl5xsTc0Wj9zppbEItd+Jkma2rn4EvHeYAHa
+YUDwnsCqVp0WIG4QFhjzJsxoHqCnJek83E/5Av9vZX1JTwMs
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem.certspec
new file mode 100644
index 0000000000..92ebdb37fd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:cabforum-and-test-oid-ee-cabforum-oid-int-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/cabforum-and-test-oid-ee-cabforum-oid-int-path-int/
+extension:certificatePolicies:2.23.140.1.1
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem
new file mode 100644
index 0000000000..51ee5e9861
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnqgAwIBAgIUOXwNT5oauz67TgsKQiAb5GqLxy4wDQYJKoZIhvcNAQEL
+BQAwLDEqMCgGA1UEAwwhY2FiZm9ydW0tYW5kLXRlc3Qtb2lkLWVlLXBhdGgtaW50
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCsxKTAnBgNVBAMM
+IGNhYmZvcnVtLWFuZC10ZXN0LW9pZC1lZS1wYXRoLWVlMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GoMIGlMFkG
+CCsGAQUFBwEBBE0wSzBJBggrBgEFBQcwAYY9aHR0cDovL3d3dy5leGFtcGxlLmNv
+bTo4ODg4L2NhYmZvcnVtLWFuZC10ZXN0LW9pZC1lZS1wYXRoLWVlLzAoBgNVHSAE
+ITAfMAcGBWeBDAEBMBQGEisGAQQB60mFGoUahRoBg3QJATAeBgNVHREEFzAVghNl
+di10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCY5y+AbntV59z+
+5tR6RV3Qs1ibjC/83zG3iPXLv6QqTGTB9C93d0i/cN0f2j8QE11TpPUU7pHMmblk
+ZDOVLpaZtv26viXCYHJaS7OMhusmVruOJ2AoSBRdX47Z7aZB8vbmGIdSB4p6ESet
+eID6Wd73189xakvUAoxFB8+zEZo1pmuTJ11tQlyIDWKz6XDrO0cIHTjF+EAAeKnv
+AanOis7N/UwYEau72cErZCAwsih3V3WZ6TIwA3BCt2XmrYKtTEtFMaJNg5pR3feI
+e9ZqKKT2UO1YZykFLMEYUkCAUQMUdA13MSWiA2EjnYdVRieI5nHajNhmm8hipgnf
+OBEt0Zw4
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem.certspec
new file mode 100644
index 0000000000..36f80e017b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:cabforum-and-test-oid-ee-path-int
+subject:cabforum-and-test-oid-ee-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/cabforum-and-test-oid-ee-path-ee/
+extension:certificatePolicies:2.23.140.1.1,1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem
new file mode 100644
index 0000000000..7a5a0914d8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXTCCAkWgAwIBAgIUTSLwbOxOBuzeSe9coAOylouA6sEwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCwxKjAoBgNVBAMMIWNhYmZvcnVtLWFuZC10ZXN0LW9pZC1lZS1w
+YXRoLWludDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaOBjTCBijAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjBa
+BggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAGGPmh0dHA6Ly93d3cuZXhhbXBsZS5j
+b206ODg4OC9jYWJmb3J1bS1hbmQtdGVzdC1vaWQtZWUtcGF0aC1pbnQvMBEGA1Ud
+IAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsFAAOCAQEArzFPBw43mrcgEhmCcGAy
+1eglq5KnMgtDY4lTYTfVLxrx4tOqQTDoKyMp63o6//KJxhPvEzuD6O4Gq+FMzlVu
+MrGcRo78esgIkkp/I77L8YAoSALhmfBIQdiv1bC5xLWkk7MWyvHCUhQ91ZXXAOo9
+7kE/Jah0PqHZQ8GFZ50sJWaO7t1Werxw+H3uX0edUix9BhqUXt3eWxQ7f1rlcueE
+poiqEhc4ECG5XVt77GswtIfTvlfXZ0iELV3BtWqgDUxBGNgmHf+k8XtN9uEIB1Lg
+wJQ/zclk56ZrquaQLGm2GVIQIHQMHqkZQqWGr0SllsSXq72xX9k9WxHSGXqgfgRH
+kQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem.certspec
new file mode 100644
index 0000000000..79ae7ae801
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-and-test-oid-ee-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:cabforum-and-test-oid-ee-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/cabforum-and-test-oid-ee-path-int/
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem
new file mode 100644
index 0000000000..dd103dcb7f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWDCCAkCgAwIBAgIUQzfIanDdubWVWC4810gYEPO4i0swDQYJKoZIhvcNAQEL
+BQAwIDEeMBwGA1UEAwwVY2FiZm9ydW0tb2lkLXBhdGgtaW50MCIYDzIwMTkxMTI4
+MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMB8xHTAbBgNVBAMMFGNhYmZvcnVtLW9p
+ZC1wYXRoLWVlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESO
+FtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVr
+amRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWka
+sdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbY
+VbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6n
+aOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHE
+MdUDrNoYCjXtjQIDAQABo4GGMIGDME0GCCsGAQUFBwEBBEEwPzA9BggrBgEFBQcw
+AYYxaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L2NhYmZvcnVtLW9pZC1wYXRo
+LWVlLzASBgNVHSAECzAJMAcGBWeBDAEBMB4GA1UdEQQXMBWCE2V2LXRlc3QuZXhh
+bXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAJ9Md1ZCs0/Hr6gqayb9vBSGdX1i
+vT8ufJef6ViOlW0L5RrH7cYU5tIDHIRTFy+KeOhL/CWbL7zVJKofw7RUYskPucwa
+ll4SeMGFMT0vDADhec9WLd+F9D8YCVhbWkb+OxeoOs98nlGhrl3ZEVP+aH55DtqF
+ufiUsl8cjHY2ye1pxkfv1IlNpjORi+xtsYkkCQvgY1B5KzGg9Fe5wIC8VtyslrR5
+f7EkfSnZjrQYEHNcJTvGojRniDXhKS/AGsyDYFMgUTd/Ur3l9Wn2WfzhyyeAO+vX
+6YGyLnghtcx4pvEHaKpkhCoQskjkcGOQhlq3Fq48p12IyFMCQP1LdWnHzZA=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem.certspec
new file mode 100644
index 0000000000..86fd9aca39
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:cabforum-oid-path-int
+subject:cabforum-oid-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/cabforum-oid-path-ee/
+extension:certificatePolicies:2.23.140.1.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem
new file mode 100644
index 0000000000..2476d73cde
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDRTCCAi2gAwIBAgIUHxkuPQNGVMkABbeiiiNkn5pyPdgwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCAxHjAcBgNVBAMMFWNhYmZvcnVtLW9pZC1wYXRoLWludDCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+AaOBgTB/MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGME4GCCsGAQUFBwEBBEIw
+QDA+BggrBgEFBQcwAYYyaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L2NhYmZv
+cnVtLW9pZC1wYXRoLWludC8wEgYDVR0gBAswCTAHBgVngQwBATANBgkqhkiG9w0B
+AQsFAAOCAQEAfOjhFxR9ILmgEhFc0QtS1QxamGbs4aEPLfJpIf//IOMubqpSVr+7
+nS8nL+Fnglx4eeql6+LZNL6NUHY4xGVH9a9+RVZcfsFeJxUI9KCKBPrkD5Xetat4
+GpoYqjKT41UdJq+dLmXAs1YmF7XAVJY4vjYpFUbjML4JKlOw79jO72VRQC7MKl7t
+gxGq0/IPB3/zEVGtVdIWU73aFhvJ+2H3L4sFaVY5d9BTfYvOcQtebF3SWaVIDOlw
+13OdogmB1kzb2gaLs6tZAXCCtG+mxkd/+mrUXXDCsnfY7IEGf82z5cmEFJorgwEH
+uw/YpAPooZCMfvp5BNE6MIi6EYR/nHjCGw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem.certspec
new file mode 100644
index 0000000000..343307164b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/cabforum-oid-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:cabforum-oid-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/cabforum-oid-path-int/
+extension:certificatePolicies:2.23.140.1.1
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/evroot.key b/security/manager/ssl/tests/unit/test_ev_certs/evroot.key
new file mode 100644
index 0000000000..1d88a930d5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/evroot.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQC1SYlcnQAQjRGh
++Z+HqePRpdtd+uzxiNpXv2QTaI8s5HIs/xCQOMF0Ask6Kkc9vShq7T/c02PPWikU
+dwG92BjXYVv5NWvV08gzaqqMCXE2igbDzURhuT5RQk4XRLsuqtRqqzjOGWghlh+H
+cUoWY2k/CXYc301roSXqzse+Jw04j3ifbN94rjFE7SjEXnkpOGOnoipImAo2pA5y
+1XnJuSXf+MeTNi/9aJenwXVMXpfJZ8Pq3RquiqLMzjSKAWm4Diii1wwalgxvM18t
+oJubZD9av7pJ6Kqpgelg4n2HSAvdVd2UF/oYUJ+7VUzPgaQ5fouoEoo0vfJ4ZcGJ
+5XNPsikFAgMBAAECggEBAJg9VPlNb0x26yPW+T14UjUwz3Ow0WJUxueBdo1F9VaB
+0dAvsr0qrGq8HDiYYJNcUqDY9BSCAQOUd4MUHYZL/zCANjilwBUlcK6dGPPYyhY+
++0dbDd3zLn4W7HVl5rteAlxBxcZuV6A87eVUIh+DBFNHosTEUcPc5Ha3h84MBXJE
+vp4E7xMRjbuz1eCmzIcCnq/Upp7ZsUdZsV452KmITlb1TS+asBPw0V8xipq2svc9
+HsPJ/idK6JQxoQZAvniZsAEcXlCToYNHCGid4QBjTaveYPvWqu+joz3zSh829gwE
+MDa3SNHJ7pjEAxoK/sYO/aCpkL5ST1YU6sT9s0pS+VECgYEA6twssz5f8co3a72V
+vWoXd9LPT6xHVF6S0RpiCbnV5N7UeDRYHBabPIhHQqCeoYdQXBylVBTY0ltJdjLV
+7CqqBSM0MPrUmJJ3en1o4Dj1YaO4lp5gsKJj3vv9pIqbD/OdlbyIsVJnyK3pe1EH
+lI5B5DMknYf32xCdXXRYTYa8wdcCgYEAxZrldqIWRwJI2USlW56b+TKZ2jQexW5V
+jrqCGrzhv1e3nPQR0pBMd0+duh8VGF9gewV0oIIF1uwotmo21jQjLqry/qN1Yauv
+nWRLaNs4yZZMuMluwKxh66ZNBbRGVC9COXb1rN5OzJVTbS31eJVPk/DP2cWPt4ui
+p23VrChNyIMCgYEAwdLvOQYzHFKspkgR+f5CW+somDIvs9tRAyzo1+n8MiQL6SAZ
+zySA/NXjKYNxJxGLKlmhv+BsiD46REfz8DHNmuvQuNNo/Hl0DSzOjq2zJN9/CR6v
+4VZDYdVJILAbBHEjDl5H2T+O0zljxRe8T8ePbYsfnrqFvM7bcDMCZQjbYoUCgYEA
+hSG421aU376ASjFfnvybZSdcVJCs8qNFbWXm5hC/n2R/xnUB1PV3LyMqxwzN75/C
+pt+kFcfEG2r8evnQfDygP37ZPAnwuZ8sMEQ0Mi8QcXCbvBuqTJFXX6apWeB9SZaV
+bZXiK1eTi25HyNUf/t/Jv4iM4NGj5CtlqJvtS5HT5fUCgYEA3El7BrkgyL4LAHe3
+mOl37vdEqQ7Cxdfmy7IkSPrHLagaMxgODYoC6DFGDH/H/TphL3uZMLYbeZ+OkI5j
+LpugQJtqpwsDo7p4dCYmO1vVhD34R27bXRT2qGE+uvW5zVykL1+9KALgjk5J5XCf
+UVFRDKpassHG6z7+kpXRbowlyRY=
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/evroot.key.keyspec b/security/manager/ssl/tests/unit/test_ev_certs/evroot.key.keyspec
new file mode 100644
index 0000000000..1a3d76a550
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/evroot.key.keyspec
@@ -0,0 +1 @@
+ev
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/evroot.pem b/security/manager/ssl/tests/unit/test_ev_certs/evroot.pem
new file mode 100644
index 0000000000..13c3031905
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/evroot.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUIZSHsVgzcvhPgdfrgdMGlpSfMegwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTUwMTAxMDAwMDAwWhgPMjAzNTAx
+MDEwMDAwMDBaMBExDzANBgNVBAMMBmV2cm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALVJiVydABCNEaH5n4ep49Gl21367PGI2le/ZBNojyzkciz/
+EJA4wXQCyToqRz29KGrtP9zTY89aKRR3Ab3YGNdhW/k1a9XTyDNqqowJcTaKBsPN
+RGG5PlFCThdEuy6q1GqrOM4ZaCGWH4dxShZjaT8JdhzfTWuhJerOx74nDTiPeJ9s
+33iuMUTtKMReeSk4Y6eiKkiYCjakDnLVecm5Jd/4x5M2L/1ol6fBdUxel8lnw+rd
+Gq6KoszONIoBabgOKKLXDBqWDG8zXy2gm5tkP1q/uknoqqmB6WDifYdIC91V3ZQX
++hhQn7tVTM+BpDl+i6gSijS98nhlwYnlc0+yKQUCAwEAAaMdMBswDAYDVR0TBAUw
+AwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBABTOHA9XbfLv/C7+
+5KycYXToOIBRSjQ0j2nsiqFda4Jx+aKsvdpdrrbLHvhrpfsA3ZgB2+eKHunVc4fo
+UHNqZllAs2nx+AEinq4GX8iya5BpiyTIxXWu8v06siGgz1GxlJw1cJ/ZnFEQ9IBf
+cCAr5fCoZ4RC+2OVhiSTnYPCKM+zCyw3YpISjNOg1VVkp46Htp+831Eh12YfwvdY
+Fgh1fc5ohYC5GCLRuXKc9PGTsr3gp7Y0liYbK7v0RBjd+GivNQ3dS3W+lB3Ow0LH
+z/fc3qvrhsd58jHpb1QZQzd9bQjuIIM6Gij7TNdNNarEVZfSJjPYLfXosNdYh5fH
+HmbOwao=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/evroot.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/evroot.pem.certspec
new file mode 100644
index 0000000000..3121f3486e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/evroot.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:evroot
+subjectKey:ev
+issuerKey:ev
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/moz.build b/security/manager/ssl/tests/unit/test_ev_certs/moz.build
new file mode 100644
index 0000000000..927fadf1d0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/moz.build
@@ -0,0 +1,48 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'anyPolicy-ee-path-ee.pem',
+# 'anyPolicy-ee-path-int.pem',
+# 'anyPolicy-int-path-ee.pem',
+# 'anyPolicy-int-path-int.pem',
+# 'cabforum-and-test-oid-ee-cabforum-oid-int-path-ee.pem',
+# 'cabforum-and-test-oid-ee-cabforum-oid-int-path-int.pem',
+# 'cabforum-and-test-oid-ee-path-ee.pem',
+# 'cabforum-and-test-oid-ee-path-int.pem',
+# 'cabforum-oid-path-ee.pem',
+# 'cabforum-oid-path-int.pem',
+# 'evroot.pem',
+# 'no-ocsp-ee-path-ee.pem',
+# 'no-ocsp-ee-path-int.pem',
+# 'no-ocsp-int-path-ee.pem',
+# 'no-ocsp-int-path-int.pem',
+# 'non-ev-root-path-ee.pem',
+# 'non-ev-root-path-int.pem',
+# 'non-evroot-ca.pem',
+# 'reverse-order-oids-path-ee.pem',
+# 'reverse-order-oids-path-int.pem',
+# 'test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem',
+# 'test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem',
+# 'test-and-cabforum-oid-ee-path-ee.pem',
+# 'test-and-cabforum-oid-ee-path-int.pem',
+# 'test-oid-ee-cabforum-oid-int-path-ee.pem',
+# 'test-oid-ee-cabforum-oid-int-path-int.pem',
+# 'test-oid-path-ee.pem',
+# 'test-oid-path-int.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'evroot.key',
+# 'test-oid-path-int.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem
new file mode 100644
index 0000000000..1eb17e0d4a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDEDCCAfigAwIBAgIUEVaHlHZqtF7YUMSMX768HxJ2o6QwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTbm8tb2NzcC1lZS1wYXRoLWludDAiGA8yMDE5MTEyODAw
+MDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAdMRswGQYDVQQDDBJuby1vY3NwLWVlLXBh
+dGgtZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAGjQzBBMB8GA1UdIAQYMBYwFAYSKwYBBAHrSYUahRqFGgGDdAkB
+MB4GA1UdEQQXMBWCE2V2LXRlc3QuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQAD
+ggEBAEOYKTjhumr1vgG+VTbI09KXlkdy40/PKEKx6qqgcPVzJdwv9zcrcC0diJGm
+bgReRgaw7CauoL9iY/BaBI+awYfLeQR7+Sasyg3M7QPLHXTAvJYzjYWQrcI2Bq9f
+FpF6vGdN3O6OEifA+cdNWMAXQi2KnFdxCrwV1isLFSnonk+2qhmQmGBUCqVzSC+I
+09IzhxcZPtWYKPJL0Cfnx4r/+tLt/A+tfhUUcDxT3H6TgfhmN4oeMq01MQ/H/und
+ARclew0opuHEUQg1UL5cYJUTO/+rbslcRbwDkJNbw2CN6u2CCtaYEjkZQlQ+rfd8
+5m9OsORT8NhDwyGkCNoqU0eTYPY=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem.certspec
new file mode 100644
index 0000000000..ece1cf816f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:no-ocsp-ee-path-int
+subject:no-ocsp-ee-path-ee
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem
new file mode 100644
index 0000000000..7eea57927f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPzCCAiegAwIBAgIUO1Rn0qhER6ecygoAUilco/BzuwUwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMB4xHDAaBgNVBAMME25vLW9jc3AtZWUtcGF0aC1pbnQwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erk
+NUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwC
+fs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1m
+CyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTM
+HGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m
+1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGj
+fjB8MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMEwGCCsGAQUFBwEBBEAwPjA8
+BggrBgEFBQcwAYYwaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L25vLW9jc3At
+ZWUtcGF0aC1pbnQvMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsFAAOC
+AQEAkwxxKtURPe9v3BjIpNuig5QVEf5NXSoxqbScpEK/fumMsegQekfAqomKbmCn
+O5B3Qhn146UclkoeKaJJQ+DpWcRDqg9pMkwWbJDd7SpBGqzo34juHR2q7mnBEXQM
+jte6jJnV5XTNG0RSaBL523CcSByk0H6AtBI+vqYJlF2Ks7/9S94QD7fW7gvqBDPQ
+zXMbO4qXl34sA8rpaDQBjOmwMl0JXtt0/CrbOTkb7GcaNGaoNORKey6MxMQCmVbD
+KkMoU98lJwO5PMZkid03vN4ooTNoOFLevyDjP6dGBf3/+x7IASr7NQsdtXdWeSOs
+W31qevNBQqgSY/vA2graLBoKGQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem.certspec
new file mode 100644
index 0000000000..5eb952a9a7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-ee-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:no-ocsp-ee-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/no-ocsp-ee-path-int/
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem
new file mode 100644
index 0000000000..24c1a97670
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDYjCCAkqgAwIBAgIUU2GeukGa6dcsNWCnTyp5K0jKQO8wDQYJKoZIhvcNAQEL
+BQAwHzEdMBsGA1UEAwwUbm8tb2NzcC1pbnQtcGF0aC1pbnQwIhgPMjAxOTExMjgw
+MDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowHjEcMBoGA1UEAwwTbm8tb2NzcC1pbnQt
+cGF0aC1lZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaOBkjCBjzBMBggrBgEFBQcBAQRAMD4wPAYIKwYBBQUHMAGG
+MGh0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4OC9uby1vY3NwLWludC1wYXRoLWVl
+LzAfBgNVHSAEGDAWMBQGEisGAQQB60mFGoUahRoBg3QJATAeBgNVHREEFzAVghNl
+di10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAYiSEHOmStRPXW
+y5vUwnM98/F1YLNse2fC8PSsG3i+74YRVmtCi+A+KSIwVH6bRVSZCAXn0Ie86sQh
+UIty2qzWyn/nFrX3KS1N660GsSpvQmtnn31dMGGbEbEoLH5MOm1EPD/ydAdTZc9H
++Jg+0SSlELBTsdOI6mUj84JL4p8piYtIvgVWDUqn3d2Q4qp5m2IWsWJn/x/YrtJ9
+qcTHEYD1B3WGSg6LeUlqWCCmz/2CaYzd0SRSXPqOZpR5RCckoY5f7o1Lev+/yqLK
+ExfJrMIg+encVBPvPxIZrNLa+8GT705rVvX4MbKP0tIpTzPv1DimxaKauytRNkgR
+sN4lGvFc
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem.certspec
new file mode 100644
index 0000000000..623057e9e9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:no-ocsp-int-path-int
+subject:no-ocsp-int-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/no-ocsp-int-path-ee/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem
new file mode 100644
index 0000000000..51fbc28ed2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8jCCAdqgAwIBAgIUPy5yIOPguO2ZrWK+N5gQKt6P8vQwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMB8xHTAbBgNVBAMMFG5vLW9jc3AtaW50LXBhdGgtaW50MIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq
+5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SSc
+An7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39
+ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk
+zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3u
+JtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQAB
+ozAwLjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjARBgNVHSAECjAIMAYGBFUd
+IAAwDQYJKoZIhvcNAQELBQADggEBAHLIQ3h5ckWiymYNqo255k6T/8bIupCPn6Z0
+qVoNmlb3s0XZfTy/64GRCqdRDMMhT80po7IVInalhfrxlBsoG4ASQ1Rc1HI3I1c5
+4umDOTxDrTIiCxJlMFGodb7ZWsrsIO0a9FpDAuuXRkl2wdqEo2UpfJBq3/LPhq4w
+Uzv5KUFDo5frBytVJFR+qpnrSEeekORLqszUw5eYy50YegC1qlXca2Vk/m2oQGKC
+4yAT+dS7i7WYrZ20aoAngBLrBr0EJuBjjDCpIGM3z41RcT7VOsezfmteGzoRoVBh
+GjHgM7gesSuC8oOOr8i9aT1X5MQMhfVo/MdS/UDtLlSt5bCVwQo=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem.certspec
new file mode 100644
index 0000000000..548241de3a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/no-ocsp-int-path-int.pem.certspec
@@ -0,0 +1,6 @@
+issuer:evroot
+subject:no-ocsp-int-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem
new file mode 100644
index 0000000000..93139ba9db
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDYjCCAkqgAwIBAgIUd59Z/7YSXGFqK6Mbt0Q0cLiJV94wDQYJKoZIhvcNAQEL
+BQAwHzEdMBsGA1UEAwwUbm9uLWV2LXJvb3QtcGF0aC1pbnQwIhgPMjAxOTExMjgw
+MDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowHjEcMBoGA1UEAwwTbm9uLWV2LXJvb3Qt
+cGF0aC1lZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaOBkjCBjzBMBggrBgEFBQcBAQRAMD4wPAYIKwYBBQUHMAGG
+MGh0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4OC9ub24tZXYtcm9vdC1wYXRoLWVl
+LzAfBgNVHSAEGDAWMBQGEisGAQQB60mFGoUahRoBg3QJATAeBgNVHREEFzAVghNl
+di10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAlsnI1h5nJcbp2
+0qwOoxUZZhmztKVGp5Oy9ae0TbkEgqVYYRTnNSSVd82cULRrMY4p3kEaAwbU7ej7
+YhxsNIBf1f9pRozRIdY/SZv20nh2/r3N6RfjvxHXRcoav9dpEti8kLFsRDaEbxo9
+yxS+9vbEmg9iuVkvMHAd3tZ0lBg5lYFXVk6BKrJF9nqrOiZhl5//0+tUfCenvleJ
+ngoB8MLZGR5Y25Vov0rSzj6HM3XLBoV5MIeFcB3n/JcBCXPPFwikQgIHTzInF4+H
+UgQOhv7L1nzSrIAZjiGHbD3R4RNwMlkIAlt6nzWse7e2zwBedYjpcFy4qFN3sweU
+91MM5I3h
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem.certspec
new file mode 100644
index 0000000000..9895732b15
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:non-ev-root-path-int
+subject:non-ev-root-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/non-ev-root-path-ee/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem
new file mode 100644
index 0000000000..74e5066cec
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSDCCAjCgAwIBAgIUCa1ByR+IaXRuP5HPyIyTz7ZjGSAwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNbm9uLWV2cm9vdC1jYTAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAfMR0wGwYDVQQDDBRub24tZXYtcm9vdC1wYXRoLWlu
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogG
+NhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqn
+RYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHu
+p3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQ
+Lzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p
+47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo1
+7Y0CAwEAAaN/MH0wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwTQYIKwYBBQUH
+AQEEQTA/MD0GCCsGAQUFBzABhjFodHRwOi8vd3d3LmV4YW1wbGUuY29tOjg4ODgv
+bm9uLWV2LXJvb3QtcGF0aC1pbnQvMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG
+9w0BAQsFAAOCAQEAZU5zl2ENgMl0Y+Yvstp/aDqF1s8OTSEQgaYdRd5z5lh2tZKc
+vQdj/eH9EuT9ao3HsYFLao79ukbvHBPmtU6snsbO82kf7fFO/LtaGoKYKWnTJVyF
+1jOlQ1YCpntdkEdrdgSH1dbxQCqffvPQUxlHUYuhPXM/OyC6ZPuH3409bH7NFE8d
+d9FzlOZu7wX+aIttXJUeH+QklPomOp2Ef0aZwj9TMi9yjRdomfD/amYPYKmvpXlN
+Vem+Lx3O9sN6zsTcDs3tmY0pQ9GRAWPO9n8wv4drUVyhtF4LU87bShJ66zxW/lN4
+50jdCVPBizdvAbDB1iNVHBiagWX1rL4unY8g5w==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem.certspec
new file mode 100644
index 0000000000..5ce035ae1e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/non-ev-root-path-int.pem.certspec
@@ -0,0 +1,6 @@
+issuer:non-evroot-ca
+subject:non-ev-root-path-int
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/non-ev-root-path-int/
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem b/security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem
new file mode 100644
index 0000000000..c987c4826c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAcegAwIBAgIUGVWjVL2eG3LHefW8SbN9qKKBkR8wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNbm9uLWV2cm9vdC1jYTAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAYMRYwFAYDVQQDDA1ub24tZXZyb290LWNhMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq
+5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SSc
+An7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39
+ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk
+zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3u
+JtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQAB
+ox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC
+AQEAIL1Lg6t9nhLv8OMt5Jupz2pz7r2l+OQOeVklrV4defOgh5GNA6Ib27iqbp3u
+WiKHKHwUIjNUCbjYulnDqyQOugU7c3MCq4iy+rJhPECmTkrwTW8D3Rk0HtaABG3I
+hlsf3ol+5TWP/84+eu3Re9GELv3ot9j9BGH29lW8wF6K2lw4Q61uXxTS1FZFfBw0
+fXxyQYeKLc/NXnde1I05/vgieogPJa8klb6IOhHgdnfyfFUyTSKRUTuchCNJWlua
+cxuMEYDkbusVVN3mNaBanqSuP4o8LnyOXvxUhDIm9I8dpcYD45db+2Et5SwPq1jB
+9Bm5lW6mnQkcBeSDAhXs8WjYWA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem.certspec
new file mode 100644
index 0000000000..7b61447a80
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/non-evroot-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:non-evroot-ca
+subject:non-evroot-ca
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem
new file mode 100644
index 0000000000..80720f9856
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDgDCCAmigAwIBAgIUEXmqqsX9kzkkT1kcgsKRmjz4I3owDQYJKoZIhvcNAQEL
+BQAwJjEkMCIGA1UEAwwbcmV2ZXJzZS1vcmRlci1vaWRzLXBhdGgtaW50MCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCUxIzAhBgNVBAMMGnJldmVy
+c2Utb3JkZXItb2lkcy1wYXRoLWVlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
+BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
+a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
+uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
+9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
+mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GiMIGfMFMGCCsGAQUFBwEBBEcw
+RTBDBggrBgEFBQcwAYY3aHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L3JldmVy
+c2Utb3JkZXItb2lkcy1wYXRoLWVlLzAoBgNVHSAEITAfMAcGBWeBDAEBMBQGEisG
+AQQB60mFGoUahRoBg3QJATAeBgNVHREEFzAVghNldi10ZXN0LmV4YW1wbGUuY29t
+MA0GCSqGSIb3DQEBCwUAA4IBAQBUE1VSHa5kqgpWz91KR8zawKi+hFSszqXN8bFU
+H5WEl7j7W0fCBmpqKTlz6V3HFuphFaE6kDctZ33H8TLVTROPusCxY28dgphJPZQG
+LaOwdU0YcdCH20wEXgkoujM/cgnyNkTgBkRG5psvtAvTg28bsOWbqpgxiSaKBo0z
+xYPMq+J+schGpK02zLlkbANZQdBYg37jBVFWCTW5/tGS4rPEDC/agn33y6Pvv6mq
+XUrxOP0E8sZ3XPnjMBIXRI9NRm6urNMPQsteEHdhDVSo9LXV3d6GmlSW8xoGTmEA
+G4HYR1A6+D7PGJDVyvWaGENkGEfJUi7ngfiMEvDTwbQLy08V
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem.certspec
new file mode 100644
index 0000000000..31e3e69e53
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:reverse-order-oids-path-int
+subject:reverse-order-oids-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/reverse-order-oids-path-ee/
+extension:certificatePolicies:2.23.140.1.1,1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem
new file mode 100644
index 0000000000..17ccdcd549
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIUVyTXYnGBHJHjCi2C6eJVXi/iBbUwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCYxJDAiBgNVBAMMG3JldmVyc2Utb3JkZXItb2lkcy1wYXRoLWlu
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogG
+NhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqn
+RYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHu
+p3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQ
+Lzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p
+47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo1
+7Y0CAwEAAaOBnjCBmzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjBUBggrBgEF
+BQcBAQRIMEYwRAYIKwYBBQUHMAGGOGh0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4
+OC9yZXZlcnNlLW9yZGVyLW9pZHMtcGF0aC1pbnQvMCgGA1UdIAQhMB8wFAYSKwYB
+BAHrSYUahRqFGgGDdAkBMAcGBWeBDAEBMA0GCSqGSIb3DQEBCwUAA4IBAQAWdeBD
+aq3w2mJQWpFQLh9ilFWk0wR1wPTEu/G2sJlZiaj3hoQmjtAb2hzuoKorSDSv0z6l
+mTrvT1CLztFJmTosimY+aoDolDJ07cqMQ5LrzSRMw48vhzeeyy13DX5uCYUxRK27
+EHAovqpg9AoPBlA0oSEM1FHxxzWmnh3eJRsos6TBIeryzX3U3j9yVkXALprWc26W
+DK1SeAZjpALx2rapYRMKIypFmWmyYzOf2Ix5np6QwGCBnQyTvYSK2a8I4f4fGS2R
+y+ZSyd1JwVMD7ok6zfG3+KjXRgbAL3UFkT45h/BghBfavAuJbbjzbnUwHzy8NwQh
+e0303QOYHHq0vJIh
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem.certspec
new file mode 100644
index 0000000000..a2b523073e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/reverse-order-oids-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:reverse-order-oids-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/reverse-order-oids-path-int/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1,2.23.140.1.1
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem
new file mode 100644
index 0000000000..3ef4e6ab5e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDxTCCAq2gAwIBAgIUY4rG8NqK821vmnElwuFYW26zGUEwDQYJKoZIhvcNAQEL
+BQAwPTE7MDkGA1UEAwwydGVzdC1hbmQtY2FiZm9ydW0tb2lkLWVlLWNhYmZvcnVt
+LW9pZC1pbnQtcGF0aC1pbnQwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowPDE6MDgGA1UEAwwxdGVzdC1hbmQtY2FiZm9ydW0tb2lkLWVlLWNhYmZv
+cnVtLW9pZC1pbnQtcGF0aC1lZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAab
+bhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmts
+Du0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhI
+H6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8
+rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kX
+Mbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaOBuTCBtjBqBggrBgEFBQcBAQReMFww
+WgYIKwYBBQUHMAGGTmh0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4OC90ZXN0LWFu
+ZC1jYWJmb3J1bS1vaWQtZWUtY2FiZm9ydW0tb2lkLWludC1wYXRoLWVlLzAoBgNV
+HSAEITAfMBQGEisGAQQB60mFGoUahRoBg3QJATAHBgVngQwBATAeBgNVHREEFzAV
+ghNldi10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAd+Daw/djP
+TluuhjaKDlybSeAF7k3Y7ndQXGkXHnnjyDSzVUsgaqZ5snUkYVLVFV5dG+IsYhhu
+hhkXok4nq+6Jc9O08/Uu7kIpjf7Ouv5Z0OZBBojAvcaYDH98bjxxx5Lec0nV+GTP
+PTh6BgJosCOTXEan6QzPHOV3xyNuDzrXMVDglyVFvBgOj8AavNUPiqi9lB9gN23w
+AjzxSo3i5J0oEYfwUjnPlwI5zQ9yOKG1pnno4lBI7XMhDs5J7BEMt9fz+W5U+/2p
+MAUkku6bPT94uOH4Lj6Chu2ppFo+herNAdNO1UznNWOcOkOap+SUQJubPqTwdf8c
+BJ4WinbiDmDf
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem.certspec
new file mode 100644
index 0000000000..edac2fc1ad
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:test-and-cabforum-oid-ee-cabforum-oid-int-path-int
+subject:test-and-cabforum-oid-ee-cabforum-oid-int-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/test-and-cabforum-oid-ee-cabforum-oid-int-path-ee/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1,2.23.140.1.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem
new file mode 100644
index 0000000000..dc2d66256c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDgDCCAmigAwIBAgIUB38LWV7K1ZtRLJtMguF5H4CVwXAwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMD0xOzA5BgNVBAMMMnRlc3QtYW5kLWNhYmZvcnVtLW9pZC1lZS1j
+YWJmb3J1bS1vaWQtaW50LXBhdGgtaW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4Ngf
+vbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTb
+uUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3S
+O8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR
+3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv
+5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GfMIGcMAwGA1UdEwQFMAMB
+Af8wCwYDVR0PBAQDAgEGMGsGCCsGAQUFBwEBBF8wXTBbBggrBgEFBQcwAYZPaHR0
+cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L3Rlc3QtYW5kLWNhYmZvcnVtLW9pZC1l
+ZS1jYWJmb3J1bS1vaWQtaW50LXBhdGgtaW50LzASBgNVHSAECzAJMAcGBWeBDAEB
+MA0GCSqGSIb3DQEBCwUAA4IBAQA2ga4887r55Lcv0LUHtIAd9DqcgqzY59nXDXOX
+PnS5gR58r1LF/u/7jPSJ6K3qzON6ug+PDoJhUf1UXBWzXhWusm8Eb5Fxo9OJV521
+GqZVsPdt8c1rnRLlrNi2GsLmywhgAy1w3mAR33laYeuT9PPQokn1W8hnU7hdWEwV
+V24CVsAK5YPH/BoSmnxVqG22/ZPbetqRKRzUanuOEOAuDBgPrt7NRm5djMpKyH4D
+k3drl+ZM0HFy3zH5XHN+a93E+iWrLy9/+kDq/7/2BBnXFSa7bTq+d/aWFrIXfW57
+Qx0bLWxz1SbqIElXnUKWGwWHoWObMRw7nP6orVKBqefyqRLL
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem.certspec
new file mode 100644
index 0000000000..68dfd00573
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-cabforum-oid-int-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:test-and-cabforum-oid-ee-cabforum-oid-int-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/test-and-cabforum-oid-ee-cabforum-oid-int-path-int/
+extension:certificatePolicies:2.23.140.1.1
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem
new file mode 100644
index 0000000000..6c746a4773
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkjCCAnqgAwIBAgIUTezLjnCdM9De3GXzBHXuGsNnVKowDQYJKoZIhvcNAQEL
+BQAwLDEqMCgGA1UEAwwhdGVzdC1hbmQtY2FiZm9ydW0tb2lkLWVlLXBhdGgtaW50
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCsxKTAnBgNVBAMM
+IHRlc3QtYW5kLWNhYmZvcnVtLW9pZC1lZS1wYXRoLWVlMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GoMIGlMFkG
+CCsGAQUFBwEBBE0wSzBJBggrBgEFBQcwAYY9aHR0cDovL3d3dy5leGFtcGxlLmNv
+bTo4ODg4L3Rlc3QtYW5kLWNhYmZvcnVtLW9pZC1lZS1wYXRoLWVlLzAoBgNVHSAE
+ITAfMBQGEisGAQQB60mFGoUahRoBg3QJATAHBgVngQwBATAeBgNVHREEFzAVghNl
+di10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAYzsZA6rEPjGaO
+3B9F7YLtYBFxtstq1++z+MRuLFXsXnaGgS65bP8C+phNNQQUYcTzjp/+nbA2HIVL
+OOtf0Z0Ciqxb6ALXhuHnPYX8pSby7afY1I4NAOuEKA844TZ2hwf6hGxMGFYC1Khv
++k0iFMLLzYA0gvVRMnhRvhoPqVQL0CxO0TRiYpf0pmPPiTkop37eNbaHorEAnCtf
+gZpfbDiYgh9USBGcfehKmwXjNRM0M+uVUdcsdzOwTHMeAUV5teRtf82D3j3moWhg
+HUjGRNpVDZjSp+Wn7psAWtKLETJhmfWPu5/b6X91+N2L20oWYk4ONCEsKezqDMxS
+Z5Ix+EJT
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem.certspec
new file mode 100644
index 0000000000..affbd87458
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:test-and-cabforum-oid-ee-path-int
+subject:test-and-cabforum-oid-ee-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/test-and-cabforum-oid-ee-path-ee/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1,2.23.140.1.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem
new file mode 100644
index 0000000000..3579205224
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXTCCAkWgAwIBAgIUIQbkixAb+1Y/Mab1Sh8NPYs0GLgwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCwxKjAoBgNVBAMMIXRlc3QtYW5kLWNhYmZvcnVtLW9pZC1lZS1w
+YXRoLWludDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaOBjTCBijAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjBa
+BggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAGGPmh0dHA6Ly93d3cuZXhhbXBsZS5j
+b206ODg4OC90ZXN0LWFuZC1jYWJmb3J1bS1vaWQtZWUtcGF0aC1pbnQvMBEGA1Ud
+IAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsFAAOCAQEAk6yOHhUbvCaulXNXvAUc
+X97+o1CkeFk1QCavnZUURyGZQlGppyKXU+RuEITsiGbYU/eqOrTGsxmdbLQ3QsPc
+hjiToDnXD7Bt7Nr98PDUFmIdh2sd++Wcjo9qjxoKrmxVvpxo8bdIRY3gVQaI/3Wy
+Hz+/iBKYKO07b3ql3cApqnnl+DEi4KFIfHOca4iBricDWWUBuJUmhwIiKqxsP/yv
+Y96xgMmO2/zVBG9bAwpbqnqygKoiaxZDEpFR7/4w0CKkgS85k6fVsE1Ozz8K8BZZ
+xuLfgn9A6I4qjfCxCGfH9Gojh61BwlHIUIJ8Rjbpn5kt7E9V3WMGTcopQISRoKEN
+vg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem.certspec
new file mode 100644
index 0000000000..11630b4b4f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-and-cabforum-oid-ee-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:test-and-cabforum-oid-ee-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/test-and-cabforum-oid-ee-path-int/
+extension:certificatePolicies:any
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem
new file mode 100644
index 0000000000..fa64ee0636
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlTCCAn2gAwIBAgIUV330em+9Xec1DGZ4wJhdv0RjswMwDQYJKoZIhvcNAQEL
+BQAwMDEuMCwGA1UEAwwldGVzdC1vaWQtZWUtY2FiZm9ydW0tb2lkLWludC1wYXRo
+LWludDAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAvMS0wKwYD
+VQQDDCR0ZXN0LW9pZC1lZS1jYWJmb3J1bS1vaWQtaW50LXBhdGgtZWUwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erk
+NUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwC
+fs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1m
+CyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTM
+HGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m
+1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGj
+gaMwgaAwXQYIKwYBBQUHAQEEUTBPME0GCCsGAQUFBzABhkFodHRwOi8vd3d3LmV4
+YW1wbGUuY29tOjg4ODgvdGVzdC1vaWQtZWUtY2FiZm9ydW0tb2lkLWludC1wYXRo
+LWVlLzAfBgNVHSAEGDAWMBQGEisGAQQB60mFGoUahRoBg3QJATAeBgNVHREEFzAV
+ghNldi10ZXN0LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCh1PqRvUqb
+ZLTd7bxaQ//insQtmlqcflBQkPEkFLkyiyKOuBIeO5B+sgr2l8GsUfrVhqd6e2Xe
+qL/6+RNlLGm5tKaTLgmXo9iG/oADW8WKyf6oCXuFMU7AiZH6cS4//ewcWdZxO7xz
+vRfu5M6TDJMMEFk71wQSzx163/aidZ3u6tcPC+PSkCVD8vzQcJPBKcF+/hFyVOPT
+PJgF/NvUY3CBykH54zm12SEFh+iOkrbQRlazlJfUTXX+dGD3iohZ+KlpQUZ8Yibk
+OzHbgM5rggesRSvcu7Ud5e42XDdTq+gDVVbwEmYer6+h+qdT4WJ6bF3qNt7gKKf/
+FqqTAPIlcQFx
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem.certspec
new file mode 100644
index 0000000000..bd0f955ada
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:test-oid-ee-cabforum-oid-int-path-int
+subject:test-oid-ee-cabforum-oid-int-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/test-oid-ee-cabforum-oid-int-path-ee/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem
new file mode 100644
index 0000000000..a2c85f5efc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIUacgXv3Tmh0nRe3yh82VGGbTiqtkwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMDAxLjAsBgNVBAMMJXRlc3Qtb2lkLWVlLWNhYmZvcnVtLW9pZC1p
+bnQtcGF0aC1pbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjgZIwgY8wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMC
+AQYwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzABhkJodHRwOi8vd3d3LmV4YW1w
+bGUuY29tOjg4ODgvdGVzdC1vaWQtZWUtY2FiZm9ydW0tb2lkLWludC1wYXRoLWlu
+dC8wEgYDVR0gBAswCTAHBgVngQwBATANBgkqhkiG9w0BAQsFAAOCAQEAFRoD650v
+OtuTmJQO9NepvUZl8M3FqcQhrzl5u1qH9fKhpWaK2oXCsxirquCTitnFvIHwRARW
+2UUgGgngwKDVJ+c7ohRudQIrBaS4utKpIv6ljZQ/oF6W0Z+4SSkbv3wj1Aw0ThbQ
+am4Jhpf7KsxSwwrFwXn+8vCWahQvk31vZTw0S9EGdEL9P8xyor/pcG4RDB7+WYDP
+dHNdexGSYrnQINgQthPUSHfWJPI44CxyyACgpsu7zqd8UZ3BSP51wJARWBky5xhs
+RoVs9yVmXeah7ejpFSdI+fAS9ZAfHs0owrD+fEa2+2jdhpPEyay5DdLsGn/mMJ+Q
+K54zhyttXeyMtQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem.certspec
new file mode 100644
index 0000000000..37d4d133a1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-ee-cabforum-oid-int-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:test-oid-ee-cabforum-oid-int-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/test-oid-ee-cabforum-oid-int-path-int/
+extension:certificatePolicies:2.23.140.1.1
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem
new file mode 100644
index 0000000000..6d9acef5b1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWTCCAkGgAwIBAgIUZWC2u4FJNGtmdv2n5Yk6Q7xQ8OQwDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRdGVzdC1vaWQtcGF0aC1pbnQwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowGzEZMBcGA1UEAwwQdGVzdC1vaWQtcGF0aC1l
+ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogG
+NhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqn
+RYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHu
+p3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQ
+Lzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p
+47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo1
+7Y0CAwEAAaOBjzCBjDBJBggrBgEFBQcBAQQ9MDswOQYIKwYBBQUHMAGGLWh0dHA6
+Ly93d3cuZXhhbXBsZS5jb206ODg4OC90ZXN0LW9pZC1wYXRoLWVlLzAfBgNVHSAE
+GDAWMBQGEisGAQQB60mFGoUahRoBg3QJATAeBgNVHREEFzAVghNldi10ZXN0LmV4
+YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQB3iZ8RGk7s3jwZY1cgJyJfjWIb
+A6DpKtL6KcvL/lcAF2VbrTdAHkEQ7TbSmDMZM484hRxckG7gs2C7rb+lUTDNk4JS
+WGm+WhTGd98DDM2ZIY3UPrGEKycqfrFqdjaJhMwd/BcgQgw5DgHUWlfKO1utSDLh
+vv212o2uzldYaGnVgyr6QK+kzpm+zLuveSxmiZSw3PE1+fZ8+yS5Ku6bo5yfdSg+
+jC9j6BZDiaf6B73offVyNlOMqD6LHczWENKCl7bCeLkH9OEl2eF7KMf3SL17b+fw
+QXvNBwHcKkEd1ElqyZOadUtAtRvaF0bWjOJ5QirH3czhPaq4XZJI8KVQyeWz
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem.certspec
new file mode 100644
index 0000000000..a9d62c65e9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:test-oid-path-int
+subject:test-oid-path-ee
+extension:authorityInformationAccess:http://www.example.com:8888/test-oid-path-ee/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
+extension:subjectAlternativeName:ev-test.example.com
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key.keyspec b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem
new file mode 100644
index 0000000000..2be083fca1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSzCCAjOgAwIBAgIUEcfv2VQb1qigZ7waPro3Nb2Ury8wDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMBwxGjAYBgNVBAMMEXRlc3Qtb2lkLXBhdGgtaW50MIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVK
+tOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7N
+Q/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39Zgsr
+sCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxs
+l62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYl
+nauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GL
+MIGIMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMEoGCCsGAQUFBwEBBD4wPDA6
+BggrBgEFBQcwAYYuaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L3Rlc3Qtb2lk
+LXBhdGgtaW50LzAfBgNVHSAEGDAWMBQGEisGAQQB60mFGoUahRoBg3QJATANBgkq
+hkiG9w0BAQsFAAOCAQEAJCZU0IPgdXwzNObNGVAa5SRNx3J9CZ9j6++cfitpaYc9
+sG6mdyyAjUbumaIgo84aH6dER+FxICt124AkAWFNPZtL+RVBV6/Y54KXSYKViXGl
+DRWOYqrbF+RGELQr5OW2kTr/u1Fsd7DWBjPBRvxLHzgSXvu3+knjXo9Mfsw+tpkH
+av8FGvxHvrj1Ad4tBXPL2MvwfT/KNtDLDVpWbkUW7sHhcEiVlitD6JONopiAmPEU
+8yNUERTw2eTDO16UMmh205wI4Z2f4SnZqrsueCQv86Nr5i12V6TWiipVvNKJLjQU
+UiBVkuFLI6o/rZMjJwgJrYgeeBmJa3E1Xa4cXf3Ehw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem.certspec b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem.certspec
new file mode 100644
index 0000000000..53534eb526
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ev_certs/test-oid-path-int.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:test-oid-path-int
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/test-oid-path-int/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js b/security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
new file mode 100644
index 0000000000..cf697d94e7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
@@ -0,0 +1,205 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * 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/. */
+
+"use strict";
+
+// Ensures that HSTS (HTTP Strict Transport Security) information is cleared
+// when using "Forget About This Site".
+
+const { ForgetAboutSite } = ChromeUtils.import(
+ "resource://gre/modules/ForgetAboutSite.jsm"
+);
+
+do_get_profile(); // must be done before instantiating nsIX509CertDB
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
+});
+
+const GOOD_MAX_AGE_SECONDS = 69403;
+const GOOD_MAX_AGE = `max-age=${GOOD_MAX_AGE_SECONDS};`;
+
+const sss = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+);
+const uri = Services.io.newURI("https://a.pinning.example.com");
+
+function add_tests() {
+ let secInfo = null;
+
+ add_connection_test(
+ "a.pinning.example.com",
+ PRErrorCodeSuccess,
+ undefined,
+ aSecInfo => {
+ secInfo = aSecInfo;
+ }
+ );
+
+ // Test the normal case of processing HSTS headers for a.pinning.example.com,
+ // using "Forget About Site" on a.pinning2.example.com, and then checking
+ // that the platform doesn't consider a.pinning.example.com to be HSTS any
+ // longer.
+ add_task(async function() {
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ GOOD_MAX_AGE,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+
+ Assert.ok(
+ sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
+ "a.pinning.example.com should be HSTS"
+ );
+
+ await ForgetAboutSite.removeDataFromDomain("a.pinning.example.com");
+
+ Assert.ok(
+ !sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
+ "a.pinning.example.com should not be HSTS now"
+ );
+ });
+
+ // Test the case of processing HSTS headers for a.pinning.example.com, using
+ // "Forget About Site" on example.com, and then checking that the platform
+ // doesn't consider the subdomain to be HSTS any longer. Also test that
+ // unrelated sites don't also get removed.
+ add_task(async function() {
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ GOOD_MAX_AGE,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+
+ Assert.ok(
+ sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
+ "a.pinning.example.com should be HSTS (subdomain case)"
+ );
+
+ // Add an unrelated site to HSTS.
+ let unrelatedURI = Services.io.newURI("https://example.org");
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ unrelatedURI,
+ GOOD_MAX_AGE,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ Assert.ok(
+ sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, unrelatedURI, 0),
+ "example.org should be HSTS"
+ );
+
+ await ForgetAboutSite.removeDataFromDomain("example.com");
+
+ Assert.ok(
+ !sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
+ "a.pinning.example.com should not be HSTS now (subdomain case)"
+ );
+
+ Assert.ok(
+ sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, unrelatedURI, 0),
+ "example.org should still be HSTS"
+ );
+ });
+
+ // Test the case of processing HSTS headers for a.pinning.example.com with
+ // various originAttributes, using "Forget About Site" on example.com, and
+ // then checking that the platform doesn't consider the subdomain to be HSTS
+ // for any originAttributes any longer. Also test that unrelated sites don't
+ // also get removed.
+ add_task(async function() {
+ let originAttributesList = [
+ {},
+ { userContextId: 1 },
+ { firstPartyDomain: "foo.com" },
+ { userContextId: 1, firstPartyDomain: "foo.com" },
+ ];
+
+ let unrelatedURI = Services.io.newURI("https://example.org");
+
+ for (let originAttributes of originAttributesList) {
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ GOOD_MAX_AGE,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
+ originAttributes
+ );
+
+ Assert.ok(
+ sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes
+ ),
+ "a.pinning.example.com should be HSTS (originAttributes case)"
+ );
+
+ // Add an unrelated site to HSTS.
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ unrelatedURI,
+ GOOD_MAX_AGE,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
+ originAttributes
+ );
+ Assert.ok(
+ sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ unrelatedURI,
+ 0,
+ originAttributes
+ ),
+ "example.org should be HSTS (originAttributes case)"
+ );
+ }
+
+ await ForgetAboutSite.removeDataFromDomain("example.com");
+
+ for (let originAttributes of originAttributesList) {
+ Assert.ok(
+ !sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes
+ ),
+ "a.pinning.example.com should not be HSTS now " +
+ "(originAttributes case)"
+ );
+
+ Assert.ok(
+ sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ unrelatedURI,
+ 0,
+ originAttributes
+ ),
+ "example.org should still be HSTS (originAttributes case)"
+ );
+ }
+ });
+}
+
+function run_test() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+ add_tests();
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_hash_algorithms.js b/security/manager/ssl/tests/unit/test_hash_algorithms.js
new file mode 100644
index 0000000000..ccdaf831e3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_hash_algorithms.js
@@ -0,0 +1,158 @@
+"use strict";
+
+// This file tests various aspects of the nsICryptoHash implementation for all
+// of the supported algorithms.
+
+const messages = ["The quick brown fox jumps over the lazy dog", ""];
+const ALGORITHMS = [
+ {
+ initString: "md5",
+ initConstant: Ci.nsICryptoHash.MD5,
+ hexHashes: [
+ "9e107d9d372bb6826bd81d3542a419d6",
+ "d41d8cd98f00b204e9800998ecf8427e",
+ ],
+ b64Hashes: ["nhB9nTcrtoJr2B01QqQZ1g==", "1B2M2Y8AsgTpgAmY7PhCfg=="],
+ },
+ {
+ initString: "sha1",
+ initConstant: Ci.nsICryptoHash.SHA1,
+ hexHashes: [
+ "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+ ],
+ b64Hashes: ["L9ThxnotKPzthJ7hu3bnORuT6xI=", "2jmj7l5rSw0yVb/vlWAYkK/YBwk="],
+ },
+ {
+ initString: "sha256",
+ initConstant: Ci.nsICryptoHash.SHA256,
+ hexHashes: [
+ "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592",
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ ],
+ b64Hashes: [
+ "16j7swfXgJRpypq8sAguT41WUeRtPNt2LQLQvzfJ5ZI=",
+ "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
+ ],
+ },
+ {
+ initString: "sha384",
+ initConstant: Ci.nsICryptoHash.SHA384,
+ hexHashes: [
+ "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1",
+ "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
+ ],
+ b64Hashes: [
+ "ynN/EBSkj0wLbdQ8sXewr9nlFpNnVExJQBHjMX2/mlCcseXcHoWpQbvuPX8q+8mx",
+ "OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb",
+ ],
+ },
+ {
+ initString: "sha512",
+ initConstant: Ci.nsICryptoHash.SHA512,
+ hexHashes: [
+ "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6",
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+ ],
+ b64Hashes: [
+ "B+VH2VhvanP3P7rAQ17XaVEhj7fQyNeIownXhUNru2Quk6JSqVTyORJUfR6KO17W4b/XCXghIz+gU489uFT+5g==",
+ "z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==",
+ ],
+ },
+];
+
+function doHash(algo, value, cmp) {
+ let hash = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hash.initWithString(algo);
+
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "utf8";
+ value = converter.convertToByteArray(value);
+ hash.update(value, value.length);
+ equal(
+ hexify(hash.finish(false)),
+ cmp,
+ `Actual and expected hash for ${algo} should match`
+ );
+
+ hash.initWithString(algo);
+ hash.update(value, value.length);
+ equal(
+ hexify(hash.finish(false)),
+ cmp,
+ `Actual and expected hash for ${algo} should match after re-init`
+ );
+}
+
+function doHashStream(algo, value, cmp) {
+ // TODO(Bug 459835): Make updateFromStream() accept zero length streams.
+ if (value.length == 0) {
+ return;
+ }
+
+ let hash = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hash.initWithString(algo);
+
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "utf8";
+ let stream = converter.convertToInputStream(value);
+ hash.updateFromStream(stream, stream.available());
+ equal(
+ hexify(hash.finish(false)),
+ cmp,
+ `Actual and expected hash for ${algo} should match updating from stream`
+ );
+}
+
+function testInitConstantAndBase64(
+ initConstant,
+ algoName,
+ message,
+ expectedOutput
+) {
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "utf8";
+ let value = converter.convertToByteArray(message);
+
+ let hash = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hash.init(initConstant);
+ hash.update(value, value.length);
+ equal(
+ hash.finish(true),
+ expectedOutput,
+ `Actual and expected base64 hash for ${algoName} should match`
+ );
+}
+
+function run_test() {
+ for (let algo of ALGORITHMS) {
+ algo.hexHashes.forEach((hash, i) => {
+ doHash(algo.initString, messages[i], hash);
+ doHashStream(algo.initString, messages[i], hash);
+ });
+ algo.b64Hashes.forEach((hash, i) => {
+ testInitConstantAndBase64(
+ algo.initConstant,
+ algo.initString,
+ messages[i],
+ hash
+ );
+ });
+ }
+
+ // Our buffer size for working with streams is 4096 bytes. This tests we
+ // handle larger inputs.
+ doHashStream("md5", " ".repeat(4100), "59f337d82f9ef5c9571bec4d78d66641");
+}
diff --git a/security/manager/ssl/tests/unit/test_hash_algorithms_wrap.js b/security/manager/ssl/tests/unit/test_hash_algorithms_wrap.js
new file mode 100644
index 0000000000..f2b7016c05
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_hash_algorithms_wrap.js
@@ -0,0 +1,5 @@
+"use strict";
+
+function run_test() {
+ run_test_in_child("test_hash_algorithms.js");
+}
diff --git a/security/manager/ssl/tests/unit/test_hmac.js b/security/manager/ssl/tests/unit/test_hmac.js
new file mode 100644
index 0000000000..8bb6139133
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_hmac.js
@@ -0,0 +1,157 @@
+"use strict";
+
+// This file tests various aspects of the nsICryptoHMAC implementation for all
+// of the supported algorithms.
+
+function getHMAC(data, key, alg, returnBase64) {
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "utf8";
+ let dataArray = converter.convertToByteArray(data);
+
+ let keyObject = Cc["@mozilla.org/security/keyobjectfactory;1"]
+ .getService(Ci.nsIKeyObjectFactory)
+ .keyFromString(Ci.nsIKeyObject.HMAC, key);
+
+ let cryptoHMAC = Cc["@mozilla.org/security/hmac;1"].createInstance(
+ Ci.nsICryptoHMAC
+ );
+
+ cryptoHMAC.init(alg, keyObject);
+ cryptoHMAC.update(dataArray, dataArray.length);
+ let digest1 = cryptoHMAC.finish(returnBase64);
+
+ cryptoHMAC.reset();
+ cryptoHMAC.update(dataArray, dataArray.length);
+ let digest2 = cryptoHMAC.finish(returnBase64);
+
+ let stream = converter.convertToInputStream(data);
+ cryptoHMAC.reset();
+ cryptoHMAC.updateFromStream(stream, stream.available());
+ let digestFromStream = cryptoHMAC.finish(returnBase64);
+
+ equal(
+ digest1,
+ digest2,
+ "Initial digest and digest after calling reset() should match"
+ );
+
+ equal(
+ digest1,
+ digestFromStream,
+ "Digest from buffer and digest from stream should match"
+ );
+
+ return digest1;
+}
+
+function testHMAC(alg) {
+ const key1 = "MyKey_ABCDEFGHIJKLMN";
+ const key2 = "MyKey_01234567890123";
+
+ const dataA = "Secret message";
+ const dataB = "Secres message";
+
+ let digest1a = getHMAC(key1, dataA, alg, false);
+ let digest2 = getHMAC(key2, dataA, alg, false);
+ let digest1b = getHMAC(key1, dataA, alg, false);
+
+ equal(
+ digest1a,
+ digest1b,
+ "The digests for the same key, data and algorithm should match"
+ );
+ notEqual(
+ digest1a,
+ digest2,
+ "The digests for different keys should not match"
+ );
+
+ let digest1 = getHMAC(key1, dataA, alg, false);
+ digest2 = getHMAC(key1, dataB, alg, false);
+
+ notEqual(digest1, digest2, "The digests for different data should not match");
+}
+
+function testVectors() {
+ const keyTestVector = "Jefe";
+ const dataTestVector = "what do ya want for nothing?";
+
+ // The base 64 values aren't in either of the RFCs below; they were just
+ // calculated using the hex vectors.
+ const vectors = [
+ // RFC 2202 section 2 test case 2.
+ {
+ algoID: Ci.nsICryptoHMAC.MD5,
+ algoName: "MD5",
+ expectedDigest: "750c783e6ab0b503eaa86e310a5db738",
+ expectedBase64: "dQx4PmqwtQPqqG4xCl23OA==",
+ },
+ // RFC 2202 section 2 test case 3.
+ {
+ algoID: Ci.nsICryptoHMAC.SHA1,
+ algoName: "SHA-1",
+ expectedDigest: "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
+ expectedBase64: "7/zfauXrL6LSdBbV8YTfnCWafHk=",
+ },
+ // RFC 4231 section 4.3.
+ {
+ algoID: Ci.nsICryptoHMAC.SHA256,
+ algoName: "SHA-256",
+ expectedDigest:
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843",
+ expectedBase64: "W9zBRr9gdU5qBCQmCJV1x1oAPwidJzmDnexYuWTsOEM=",
+ },
+ {
+ algoID: Ci.nsICryptoHMAC.SHA384,
+ algoName: "SHA-384",
+ expectedDigest:
+ "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649",
+ expectedBase64:
+ "r0XS43ZIQDFhf3jStYprG5x+9GT1oBtH5C7Dc2MiRF6OIkDKXmnix4syOez6shZJ",
+ },
+ {
+ algoID: Ci.nsICryptoHMAC.SHA512,
+ algoName: "SHA-512",
+ expectedDigest:
+ "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737",
+ expectedBase64:
+ "Fkt6e/z4GeLjlfvnO1bgo4e9ZCIugx/WECcM1+olBVSXWL91wFqZSm0DT2X48Ob9yuqxo01Ka0tjbgcKOLznNw==",
+ },
+ ];
+
+ for (let vector of vectors) {
+ let digest = getHMAC(dataTestVector, keyTestVector, vector.algoID, false);
+ equal(
+ hexify(digest),
+ vector.expectedDigest,
+ `Actual and expected ${vector.algoName} digests should match`
+ );
+ let b64Digest = getHMAC(dataTestVector, keyTestVector, vector.algoID, true);
+ equal(
+ b64Digest,
+ vector.expectedBase64,
+ `Actual and expected ${vector.algoName} base64 digest should match`
+ );
+ }
+}
+
+function run_test() {
+ testVectors();
+
+ testHMAC(Ci.nsICryptoHMAC.MD5);
+ testHMAC(Ci.nsICryptoHMAC.SHA1);
+ testHMAC(Ci.nsICryptoHMAC.SHA256);
+ testHMAC(Ci.nsICryptoHMAC.SHA384);
+ testHMAC(Ci.nsICryptoHMAC.SHA512);
+
+ // Our buffer size for working with streams is 4096 bytes. This tests we
+ // handle larger inputs.
+ let digest = getHMAC(" ".repeat(4100), "test", Ci.nsICryptoHMAC.MD5, false);
+ equal(
+ hexify(digest),
+ "befbc875f73a088cf04e77f2b1286010",
+ "Actual and expected digest for large stream should match"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_imminent_distrust.js b/security/manager/ssl/tests/unit/test_imminent_distrust.js
new file mode 100644
index 0000000000..861269e784
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_imminent_distrust.js
@@ -0,0 +1,39 @@
+/* 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/. */
+"use strict";
+
+// Tests handling of certificates that are selected to emit a distrust warning
+// to the console.
+
+function shouldBeImminentlyDistrusted(aTransportSecurityInfo) {
+ let isDistrust =
+ aTransportSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_DISTRUST_IMMINENT;
+ Assert.ok(isDistrust, "This host should be imminently distrusted");
+}
+
+function shouldNotBeImminentlyDistrusted(aTransportSecurityInfo) {
+ let isDistrust =
+ aTransportSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_DISTRUST_IMMINENT;
+ Assert.ok(!isDistrust, "This host should not be imminently distrusted");
+}
+
+do_get_profile();
+
+add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+add_connection_test(
+ "imminently-distrusted.example.com",
+ PRErrorCodeSuccess,
+ null,
+ shouldBeImminentlyDistrusted
+);
+
+add_connection_test(
+ "include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess,
+ null,
+ shouldNotBeImminentlyDistrusted
+);
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
new file mode 100644
index 0000000000..5bb4b1cecb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
@@ -0,0 +1,138 @@
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function load_cert(name, trust) {
+ let filename = "test_intermediate_basic_usage_constraints/" + name + ".pem";
+ addCertFromFile(certdb, filename, trust);
+}
+
+function test_cert_for_usages(certChainNicks, expected_usages) {
+ let certs = [];
+ for (let i in certChainNicks) {
+ let certNick = certChainNicks[i];
+ let certPEM = readFile(
+ do_get_file(
+ "test_intermediate_basic_usage_constraints/" + certNick + ".pem"
+ ),
+ false
+ );
+ certs.push(certdb.constructX509FromBase64(pemToBase64(certPEM)));
+ }
+
+ let cert = certs[0];
+ return asyncTestCertificateUsages(certdb, cert, expected_usages);
+}
+
+add_task(async function() {
+ let ee_usages = [
+ certificateUsageSSLClient,
+ certificateUsageSSLServer,
+ certificateUsageEmailSigner,
+ certificateUsageEmailRecipient,
+ ];
+ let ca_usages = [certificateUsageSSLCA];
+ let eku_usages = [certificateUsageSSLClient, certificateUsageSSLServer];
+
+ // Load the ca into mem
+ let ca_name = "ca";
+ load_cert(ca_name, "CTu,CTu,CTu");
+ await test_cert_for_usages([ca_name], ca_usages);
+
+ // A certificate with no basicConstraints extension is considered an EE.
+ await test_cert_for_usages(["int-no-extensions"], ee_usages);
+
+ // int-no-extensions is an EE (see previous case), so no certs can chain to
+ // it.
+ await test_cert_for_usages(["ee-int-no-extensions", "int-no-extensions"], []);
+
+ // a certificate with basicConstraints.cA==false is considered an EE.
+ await test_cert_for_usages(["int-not-a-ca"], ee_usages);
+
+ // int-not-a-ca is an EE (see previous case), so no certs can chain to it.
+ await test_cert_for_usages(["ee-int-not-a-ca", "int-not-a-ca"], []);
+
+ // a certificate with basicConstraints.cA==false but with the keyCertSign
+ // key usage may not act as a CA (it can act like an end-entity).
+ await test_cert_for_usages(["int-cA-FALSE-asserts-keyCertSign"], ee_usages);
+ await test_cert_for_usages(
+ ["ee-int-cA-FALSE-asserts-keyCertSign", "int-cA-FALSE-asserts-keyCertSign"],
+ []
+ );
+
+ // int-limited-depth has cA==true and a path length constraint of zero.
+ await test_cert_for_usages(["int-limited-depth"], ca_usages);
+
+ // path length constraints do not affect the ability of a non-CA cert to
+ // chain to to the CA cert.
+ await test_cert_for_usages(
+ ["ee-int-limited-depth", "int-limited-depth"],
+ ee_usages
+ );
+
+ // ca
+ // int-limited-depth (cA==true, pathLenConstraint==0)
+ // int-limited-depth-invalid (cA==true)
+ //
+ await test_cert_for_usages(
+ ["int-limited-depth-invalid", "int-limited-depth"],
+ []
+ );
+ await test_cert_for_usages(
+ [
+ "ee-int-limited-depth-invalid",
+ "int-limited-depth-invalid",
+ "int-limited-depth",
+ ],
+ []
+ );
+
+ // int-valid-ku-no-eku has keyCertSign
+ await test_cert_for_usages(["int-valid-ku-no-eku"], ca_usages);
+ await test_cert_for_usages(
+ ["ee-int-valid-ku-no-eku", "int-valid-ku-no-eku"],
+ ee_usages
+ );
+
+ // int-bad-ku-no-eku has basicConstraints.cA==true and has a KU extension
+ // but the KU extension is missing keyCertSign. Note that mozilla::pkix
+ // doesn't validate certificates with basicConstraints.Ca==true for non-CA
+ // uses.
+ await test_cert_for_usages(["int-bad-ku-no-eku"], []);
+ await test_cert_for_usages(["ee-int-bad-ku-no-eku", "int-bad-ku-no-eku"], []);
+
+ // int-no-ku-no-eku has basicConstraints.cA==true and no KU extension.
+ // We treat a missing KU as "any key usage is OK".
+ await test_cert_for_usages(["int-no-ku-no-eku"], ca_usages);
+ await test_cert_for_usages(
+ ["ee-int-no-ku-no-eku", "int-no-ku-no-eku"],
+ ee_usages
+ );
+
+ // int-valid-ku-server-eku has basicConstraints.cA==true, keyCertSign in KU,
+ // and EKU=={id-kp-serverAuth,id-kp-clientAuth}.
+ await test_cert_for_usages(["int-valid-ku-server-eku"], ca_usages);
+ await test_cert_for_usages(
+ ["ee-int-valid-ku-server-eku", "int-valid-ku-server-eku"],
+ eku_usages
+ );
+
+ // int-bad-ku-server-eku has basicConstraints.cA==true, a KU without
+ // keyCertSign, and EKU=={id-kp-serverAuth,id-kp-clientAuth}.
+ await test_cert_for_usages(["int-bad-ku-server-eku"], []);
+ await test_cert_for_usages(
+ ["ee-int-bad-ku-server-eku", "int-bad-ku-server-eku"],
+ []
+ );
+
+ // int-bad-ku-server-eku has basicConstraints.cA==true, no KU, and
+ // EKU=={id-kp-serverAuth,id-kp-clientAuth}.
+ await test_cert_for_usages(["int-no-ku-server-eku"], ca_usages);
+ await test_cert_for_usages(
+ ["ee-int-no-ku-server-eku", "int-no-ku-server-eku"],
+ eku_usages
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem
new file mode 100644
index 0000000000..a42c7532c4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvDCCAaSgAwIBAgIUPbAn5aKDD+kIa7hReaL37pIRoqQwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggEBAByxcmVDqkg7zc2gVsSg2E+gTXDjMWp/DvVlbjOQoek1
+eRR6SmEEYQ8yYC4FMemiy2b1BXHkbcu4RlKJ/sr5n3qd+eKF1hSiekl8IAVO46lk
+I22qHCs3eRPkhSTXKfUqR8mOqLAMHNwmZ+4/S5xqrnSpq3+8q9o0a/A+osy1aj4K
+JNA+/Ey9HyxjOwTeq44JiMWbotqFAtrr4yQO8BVh6Kdskcp81UtRbm2gRjXtbIjO
+FSREK358HkIkcOYNT/qNgijwyJpQiWBUJ99dv3dgVXQpm+acsIgjQ8axT6Np+3+0
+f/kIT6Xl/umiMes/Q6auaRjdQMtok9nPTPFtWB0AlsM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem.certspec
new file mode 100644
index 0000000000..eb7c4b4bee
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem
new file mode 100644
index 0000000000..b78901e441
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAc+gAwIBAgIUJb+8nRpsie/LSW/e1iNkPRuYeGswDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRaW50LWJhZC1rdS1uby1la3UwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowHzEdMBsGA1UEAwwUZWUtaW50LWJhZC1rdS1u
+by1la3UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgP4MA0GCSqGSIb3
+DQEBCwUAA4IBAQBOyG/keiC1S06CjERcBFMpKlu4iPclxxZi8kscxlhpCfqUUW/v
+8bxR6AV7B5llfxW7fjxP1cx2tUzFMmipC6ShlO2M3Vfc4bxUy2plp0CVtt8099dR
+uXcuLxohJNL+hMs2Unt3rC+S4sZUypnWvDXcwCYhrsBZNq7IQ5oXy6m2oMxwHq29
+qUdIIFCmJ/wci6q2EaZttmLD3BW0IuPt7Z14oeAODYnIpnAjCIlPYUNRkFFT/2Tw
+cyEQlSeHqAc10dDuc7wLI8UMf0VOkxE5srTd3LPcXzrqBzeURwctZUFrwToE6Xk7
+g8czZIjwInmhjdES2r/iCLAlZX7/bl7tmDDS
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem.certspec
new file mode 100644
index 0000000000..390adf2344
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-bad-ku-no-eku
+subject:ee-int-bad-ku-no-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem
new file mode 100644
index 0000000000..4c2423e772
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7zCCAdegAwIBAgIUY4lZzPdovquC7Mbz72zTaN4o1y4wDQYJKoZIhvcNAQEL
+BQAwIDEeMBwGA1UEAwwVaW50LWJhZC1rdS1zZXJ2ZXItZWt1MCIYDzIwMTkxMTI4
+MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCMxITAfBgNVBAMMGGVlLWludC1iYWQt
+a3Utc2VydmVyLWVrdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqI
+UahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvi
+r1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/x
+fq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD
+7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnv
+uRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj
++nJRxDHVA6zaGAo17Y0CAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gw
+DQYJKoZIhvcNAQELBQADggEBAEbel/uwJK/uVtGY33gMAcUi1M7dFeCrxbo51f3M
+dWf+qG4rMpZ9Jh9oUAk2OUuhkDfu52Vh4y17hjcPirOxWcy05UutefuEqUTLsiZ/
+Ykg+2RUMLtMPCjMgIv6OxWzwiN7hjuHW3MDmkEkT7YNzJnTE0isLwtd3gKiRILsM
+WtQ3AFdR1KK+CUj6pGht+yDYR0kTI7a/1d5fCcdWD6usuIWtJlzGTtA5X9KFaiB+
+40UZs0p5NAVq2jS6W+DMUjHfrH07e94R6cYHM0DFXQLqDJucpeOrBX1Pd6//Jl2h
+NtimaXJoTWiUOqclQstkhPrSLtT1GWj9S9MjDmB9G3hsLYc=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem.certspec
new file mode 100644
index 0000000000..32bb6c2485
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-bad-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-bad-ku-server-eku
+subject:ee-int-bad-ku-server-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem
new file mode 100644
index 0000000000..7c670abd58
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBTCCAe2gAwIBAgIUd8ffH/82BvEHZUunrKaA0Zk6FG4wDQYJKoZIhvcNAQEL
+BQAwKzEpMCcGA1UEAwwgaW50LWNBLUZBTFNFLWFzc2VydHMta2V5Q2VydFNpZ24w
+IhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowLjEsMCoGA1UEAwwj
+ZWUtaW50LWNBLUZBTFNFLWFzc2VydHMta2V5Q2VydFNpZ24wggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
+V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
+0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
+fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
+W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
+TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjGjAYMAkG
+A1UdEwQCMAAwCwYDVR0PBAQDAgP4MA0GCSqGSIb3DQEBCwUAA4IBAQAVF9pewrYN
+HHFzPaQg07aY2le/pM3MgH2srjfHC+AZxrz0BeN0HFgmOcE/gnbN0hMPayVld+bh
+JvFFiHLTQ8/HyvcmGkHagHdcgjo+5NiD2baBWFPZ49Bj8N6XJyQKCyjF7YkOlpx1
+l6mte8APVgundg2iKXXVljIuJS+g3U6V6p7z5pkW/yU/Xbl+3GKYgPtGe/uIQA1S
+3fchFO11b/XWHw4PBQGMRxQKhS+e3Ih1TXJq5SwFp50RotkG/fLLw/q++W42bgcQ
+k+Eyww1HtI54wAC6AQyZ66pSkusk5OuQbs+dV83k5WEUnLFexImKUNIItCvM/2qq
+rEfSyd/jN5A8
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem.certspec
new file mode 100644
index 0000000000..9e0fb65fd6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-cA-FALSE-asserts-keyCertSign.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-cA-FALSE-asserts-keyCertSign
+subject:ee-int-cA-FALSE-asserts-keyCertSign
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem
new file mode 100644
index 0000000000..53160a7800
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9zCCAd+gAwIBAgIUPzIc4Q/DpFWUx8qcM7GOe4M/RF0wDQYJKoZIhvcNAQEL
+BQAwJDEiMCAGA1UEAwwZaW50LWxpbWl0ZWQtZGVwdGgtaW52YWxpZDAiGA8yMDE5
+MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAnMSUwIwYDVQQDDBxlZS1pbnQt
+bGltaXRlZC1kZXB0aC1pbnZhbGlkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
+BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
+a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
+uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
+9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
+mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoxowGDAJBgNVHRMEAjAAMAsGA1Ud
+DwQEAwID+DANBgkqhkiG9w0BAQsFAAOCAQEAil4HrKZ8ZItNn8riFIoViEPt1aif
+ATej/kVDhGRNOqqXIuR1+blS8VTbmKB4YA+HxyG4d2g4fZLPNbqRrNdFDIwRI08h
+EPk0ScoS+xBks5MS6EVqy507nR1XYHXRMjr9xD1L+hhMr+I42gFNDOm6BUfY2eb1
+11BttnNXk3PSakwAkt3sg8aZFLrZs1eW0EPjdbdmJPNV52ToUo0exydabJDrrFl1
+t2QJZCoz9MR+N/QPZkxpvOxjzmOCEFVHU8I4FIH2ddJVacg9LOZgh4CdLson8ug7
+mehOK8yk+QnfjoC0hlXi4FJdsNQFSL6SiCzuFlxk5AGs08pEfZQ75CvfIQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem.certspec
new file mode 100644
index 0000000000..f00b4d1591
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth-invalid.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-limited-depth-invalid
+subject:ee-int-limited-depth-invalid
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem
new file mode 100644
index 0000000000..d109bcd492
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAc+gAwIBAgIUV/tRlNSDvu7BruxM6b8StzTZACUwDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRaW50LWxpbWl0ZWQtZGVwdGgwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowHzEdMBsGA1UEAwwUZWUtaW50LWxpbWl0ZWQt
+ZGVwdGgwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgP4MA0GCSqGSIb3
+DQEBCwUAA4IBAQATRYSRBITskOkSFUKBg9DCA4bHR63pNBlEwb4L+aJYZnSdd2kc
+Cgz0x5GdoVdETz878x9O9yvNESspoASqwiobxFHsbWeW0h6eqt3LoVFI3Njt8OaG
+1kxynlyBvx3Q8E/b6ZT0hAbhum8GLqTxE10PZecvOjfDgNovCIhHk4PXpeXZNKo+
+p1yaXB4brxwVBEhv8iSBXnT60bt7vbqbJWCE2ASF2bgXTuARX1onkJz5+LWuGSF1
+ng9glpgB10r2XmOb52Hdlb9K1P4MM+Nofsb5XZ5WgdSEQnBb1b3pHLAwHXkNto3T
+E2AqOkV105ic8bkKGIMi/oCd06R0gZ7QO/u2
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem.certspec
new file mode 100644
index 0000000000..df85342d98
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-limited-depth.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-limited-depth
+subject:ee-int-limited-depth
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem
new file mode 100644
index 0000000000..9bfb1203c5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAc+gAwIBAgIUX0rT0GAv2W7gbUOUYEYetQADH1wwDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRaW50LW5vLWV4dGVuc2lvbnMwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowHzEdMBsGA1UEAwwUZWUtaW50LW5vLWV4dGVu
+c2lvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgP4MA0GCSqGSIb3
+DQEBCwUAA4IBAQAw74cGcE+TWUij1up8e6PZJpTX5JHgpYmtZcSVKR7gFKlfLxg0
+6oB6JKkN7EIfJ85FnG0vYu0yFAHZG2bZJrqE3vUCAkRe2Q3Q39g1CBrlTkNy13pm
+U41KqojfAGCGR6iS0pjDbDqu5bIpPh/sX9bmiLBq3nv+xIHYNdRU36KaRAdJ9l6+
+VAjq1b7WvPN7mF0rWN5C35pqxXu50i2gejOxlsxM2hUEODCCZoAKAweSVO3qKGCv
+093AQ/WiHpvSjvMdFO0aA+WvnIya7KRYI1lpERJltIJPBS2CNgkKOnKonGgVpBoh
+nZm+5PMjIiDU/cIrgXU3DzY9hAXmlwuCOAzL
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem.certspec
new file mode 100644
index 0000000000..c5279046d8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-extensions.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-no-extensions
+subject:ee-int-no-extensions
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem
new file mode 100644
index 0000000000..2087990cc3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5TCCAc2gAwIBAgIUcw4XUsGVtCZtRXKmHHReaOglg8kwDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQaW50LW5vLWt1LW5vLWVrdTAiGA8yMDE5MTEyODAwMDAw
+MFoYDzIwMjIwMjA1MDAwMDAwWjAeMRwwGgYDVQQDDBNlZS1pbnQtbm8ta3Utbm8t
+ZWt1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62
+iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHql
+WqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosq
+Qe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+
+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8i
+b2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoY
+CjXtjQIDAQABoxowGDAJBgNVHRMEAjAAMAsGA1UdDwQEAwID+DANBgkqhkiG9w0B
+AQsFAAOCAQEAG2jH13dTldiSOyQAtgKNJ8kv7zVK278ge0Dcihn5B1Q4JWgPCRn8
+d/RDDVajlBo3fIkQwlAnGU4YjSKDnjpSZoiU/qVmW7TGUZxY61nijgqgNMGBVO1z
+/81w17YNSHI1r6TEFAYo/NNRDCBpR4J+u3tKgQ8ZKWUv7ZdJbhHTPD+LwcoY8G07
+s9QgXisyB1KTofQAvWb4p+arjtjciTPfLg0QIf6dXOowg1+29IaGimnpb2P7yKB8
++Drg3VL+lnhDhDrTsS+VVbP7YRfI3nzTXmRTObukb9HCuT7AXacw0GbJvcNh7rFX
+2LPgZelDL5Xy/+l0osIbklL+fkcRb41xcA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem.certspec
new file mode 100644
index 0000000000..92ee3cc6d6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-no-ku-no-eku
+subject:ee-int-no-ku-no-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem
new file mode 100644
index 0000000000..4ee9fa145b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIUSydkZNHcPpeuo7P9vZIuqBGKd6YwDQYJKoZIhvcNAQEL
+BQAwHzEdMBsGA1UEAwwUaW50LW5vLWt1LXNlcnZlci1la3UwIhgPMjAxOTExMjgw
+MDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowIjEgMB4GA1UEAwwXZWUtaW50LW5vLWt1
+LXNlcnZlci1la3UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGo
+RI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9a
+dWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6t
+aRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8n
+FthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kX
+Dqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/py
+UcQx1QOs2hgKNe2NAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgP4MA0G
+CSqGSIb3DQEBCwUAA4IBAQBUaqs/EWro+OEULUWHRLBiLJCi1gA9tAHdxpisa0uT
+/tyodyY4L1xbgQWwlA13R5AjaVn1RfDnPAn6o1q/Xqbf53CGmyOCDkxTSXZifJbJ
+D4/4xF6ewsNPnERMkTjzBcrchIh8EbppoS9Bd1L3RVq1s6GOU6gVTF2BAXfpIwZo
+9bqTBc+DrCbT8+PoaZY8UJl4M7oy11Ye/GbwNd2Gm0TZU2zWdi2tsVTb82nr9wET
+BgDrwM63AdrrUQCaUC2I+vlOhuxWWboLl+W4XME22A+5t3S/+DBYyM8vf2S0SLyd
+h7NX2YzfBY9WI0r/rMJ0ubH7mbk1hypPjldbsygel/vw
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem.certspec
new file mode 100644
index 0000000000..c148896710
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-no-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-no-ku-server-eku
+subject:ee-int-no-ku-server-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem
new file mode 100644
index 0000000000..2392d94115
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3TCCAcWgAwIBAgIUUQAgWvrw7svP/y7EUHKfSCcVkjMwDQYJKoZIhvcNAQEL
+BQAwFzEVMBMGA1UEAwwMaW50LW5vdC1hLWNhMCIYDzIwMTkxMTI4MDAwMDAwWhgP
+MjAyMjAyMDUwMDAwMDBaMBoxGDAWBgNVBAMMD2VlLWludC1ub3QtYS1jYTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+AaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwDQYJKoZIhvcNAQELBQADggEB
+AAB8UNQ3JeAdPqtNhn1VFnZZzpLYcUGq1c+C8nQEOCmvy8s+QmdKsCKaO449t0fM
+Q8JyMuuH/ObHE54YzSEg/FWNrnO6hzTf05k8bxoUsFutL+xXvsLa7VGftz0VL2IC
+8xlYMvGDIZ3TGdU8B9D0B72Usn3nZvf+vbm/wZ5Wj3Bl//Fk8eqcosGJ6j5cQ1Bk
+DUQceGm6pRYtBERqkOPct9G+FOrd51tZ0+vTelJKC8v3OfK8pGwYqSjonhI146bp
+djCKAZsU9KynW1BZ61E0iU+D8/8F1ELi4SxTQtHeOWBkYq31mYnZO2ze3XYAE78C
+kkbfT9Qy0fIb9EpNn9FqOtU=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem.certspec
new file mode 100644
index 0000000000..a95b0dc260
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-not-a-ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-not-a-ca
+subject:ee-int-not-a-ca
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem
new file mode 100644
index 0000000000..c30fbe0dc2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6zCCAdOgAwIBAgIUFTa2zYscnDR5ILK/7lqO+cxtDUcwDQYJKoZIhvcNAQEL
+BQAwHjEcMBoGA1UEAwwTaW50LXZhbGlkLWt1LW5vLWVrdTAiGA8yMDE5MTEyODAw
+MDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAhMR8wHQYDVQQDDBZlZS1pbnQtdmFsaWQt
+a3Utbm8tZWt1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESO
+FtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVr
+amRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWka
+sdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbY
+VbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6n
+aOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHE
+MdUDrNoYCjXtjQIDAQABoxowGDAJBgNVHRMEAjAAMAsGA1UdDwQEAwID+DANBgkq
+hkiG9w0BAQsFAAOCAQEAmTBGZeCTRdr342Ff+rQuMiGwBl9prO45VGzGcb+QPQVF
+w1bubYX7lk9UlKNZZE4ZB8nBFDSNf7kMLn8T6MI8YYihOrUF1KxGCKmWgcSttmg0
+kZS02nrd+yVe+iYaWEK/VN8jQkyUyM5u8MJK0k0EEnljjnNs168I/pAWgcNf1zls
+1S9D3azqB0MFHe1CQzRtK6iOjE2deZHyYBb6T1uH3Gm2nm5kkpRgJ9mPGbccJS7p
+q903lUvxT7fmAj0ixaDABMggEU/hLNxNrCiV3XhxpNalNnBc1UiGJiPZBkHdSlAl
+nA0Ov7XuvCDmty7h8vWyUfZo0gchx43WTZImokBz8g==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem.certspec
new file mode 100644
index 0000000000..89a66b9f97
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-valid-ku-no-eku
+subject:ee-int-valid-ku-no-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem
new file mode 100644
index 0000000000..5c10a70b41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIUR3XzgtGCKrGxY8HRXSHlPF7GxWUwDQYJKoZIhvcNAQEL
+BQAwIjEgMB4GA1UEAwwXaW50LXZhbGlkLWt1LXNlcnZlci1la3UwIhgPMjAxOTEx
+MjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowJTEjMCEGA1UEAwwaZWUtaW50LXZh
+bGlkLWt1LXNlcnZlci1la3UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24a
+hvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7t
+FYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+o
+N9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0d
+JdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4
+s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQD
+AgP4MA0GCSqGSIb3DQEBCwUAA4IBAQBE2wXCNaiIiuKCv3PV/jHaYR1VDcc6Ypsp
+GrClISVncIlQ+vav7bRCBfNvog8YycfP/roevVWvvoBwryoHWBNj/g6N3Skrn3+P
+510oo0GDJrh/X6ITRk3uqu+6DIGtK4FJ3IcKL51zJpyuOi/YbH9m3RC5zb74EdNJ
+pT1AVdLkwn5JxKNQ+ydXuIskYtvyNQt1xcv3k/HgqBKx3A14MsKBfuMoJBkifxVh
+EPgX4Q5rrSb9Ztdmhux13UsVvt2ECuF2MGW8E/VgKh/XcXBh2WNZ6xNIiPTBM6S8
+RGefE1lVOeVlH5a5h43mI09JWxISQr0wTFvaHrJhWgQ1gsIRgwFj
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem.certspec
new file mode 100644
index 0000000000..43e83a336e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/ee-int-valid-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-valid-ku-server-eku
+subject:ee-int-valid-ku-server-eku
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem
new file mode 100644
index 0000000000..b41eb0ef1b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC2DCCAcCgAwIBAgIUZzy02i7tvAs/6RTdJfLYIt87D9cwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowHDEaMBgGA1UEAwwRaW50LWJhZC1rdS1uby1la3UwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
+V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
+0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
+fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
+W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
+TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwG
+A1UdEwQFMAMBAf8wCwYDVR0PBAQDAgH6MA0GCSqGSIb3DQEBCwUAA4IBAQAAHnJc
+VICzmnlR5N8AZlWrRHq3cye+ScZEQUXZRwxg3nLPIDwoNu0bgVAcoY2slrQEQ3d6
+Jq5UyBh6E6gjy0p3dVXO5bVlTTfdpoN3TmLUNERAqCyqRUw+tZUqqqOeLi8CT3jI
+JlW8usKSP4NaFZODWifLPQQ2DeQoBZbn6Em7YwalGqnA3Izb8zmMxPWZrwgWmYEa
+PG50Q3oh9j2dnkR4EOU0saT+gen86NwbvHZLSyAiIGH9/IJw9b5YvetZNRQ+wVuQ
+U7im69QqlZSTeDLq80YA2CCcVL617oKB+H+ChquMwnmTvviO2qnM7WcZK0Zqy9F3
+LpP0Hmpv067ExIdk
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem.certspec
new file mode 100644
index 0000000000..f6525449b4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-bad-ku-no-eku
+extension:basicConstraints:cA,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem
new file mode 100644
index 0000000000..3bd7dea894
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+zCCAeOgAwIBAgIUT+TsShZ32vzo05KpLbrZpGyUbCkwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowIDEeMBwGA1UEAwwVaW50LWJhZC1rdS1zZXJ2ZXItZWt1MIIBIjANBgkq
+hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVK
+tOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7N
+Q/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39Zgsr
+sCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxs
+l62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYl
+nauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozww
+OjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB+jAdBgNVHSUEFjAUBggrBgEFBQcD
+AQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBADETpYdt9P1ael7B2UilTCIu
+zaXfSBUZHAKidpNMENdqcYJScyLTrUSOsbHKzignTCoDN3NHVd7o/YJmV457yRKl
+nvtBaJQFd/9ycLtEiiksxvca1Zp7QfIeRRzspW23/n+bPa6CYEudbIp5KTzgi0uL
+1bEg52LoEM+ikrpT1QdgQ+q+v1Ao/BaybTumC+7nC3jvGj4itLsYTIskaacWEf/K
+WSFZ/K6cWIgKS5iEI+fAhx2IyJp4kK1jdIvmvtCiOG61r2f4GkxDkrQ5I2lvLEPF
+wS+S3scFNprbybPOpI7GAqSsewHWu+Ge81G2M+Z1ndBH9oV7qWhVlpvf2U/yXvg=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem.certspec
new file mode 100644
index 0000000000..2d324508d4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-bad-ku-server-eku.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-bad-ku-server-eku
+extension:basicConstraints:cA,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,cRLSign
+extension:extKeyUsage:serverAuth,clientAuth
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem
new file mode 100644
index 0000000000..efc707bb5e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5DCCAcygAwIBAgIUWz8ZXOO6fSSbmQau3JwsY5zTrDMwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowKzEpMCcGA1UEAwwgaW50LWNBLUZBTFNFLWFzc2VydHMta2V5Q2VydFNp
+Z24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
+BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
+p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
+7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
+kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
+aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
+Ne2NAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgH+MA0GCSqGSIb3DQEB
+CwUAA4IBAQCv+2MLK5Sm5TWC9WmEbavSz9bW315LIy/09r6NFaCQksbZzZ01b0n7
+jsuq6ZIhVfaMepUF1Fc7K5avfCzyJlPiOGsm9SmfT4XFIX5Ugu5mEAGva8jYDeOn
+3ie/VzwvQRPmb7fIDQ/in5siiQ3OcL3f7KaOZQM/zP34pxh2C2oZYz+xu/byaLhb
+qPXoTM8wnqVeNP7wqZDmv7ZZFXDT0dEKqCRs+G3YzPmfk/9tBokFla7jrrtG1vHt
+vWXmeRfJPzJq+bDDJo9BmzHjPWYTYajfDFTOs24AbNacZdyNdNnRLgXqKIn5slk7
+F2mjSU/KIeiQB9nGxC/nfqe1uAR5MdtY
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem.certspec
new file mode 100644
index 0000000000..39785d8a48
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-cA-FALSE-asserts-keyCertSign.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-cA-FALSE-asserts-keyCertSign
+extension:basicConstraints:,
+extension:keyUsage:digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem
new file mode 100644
index 0000000000..5eaf98add4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4jCCAcqgAwIBAgIULycRMzIhSjpkHsE2X111MeoH++4wDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRaW50LWxpbWl0ZWQtZGVwdGgwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowJDEiMCAGA1UEAwwZaW50LWxpbWl0ZWQtZGVw
+dGgtaW52YWxpZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahE
+jhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1
+a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1p
+GrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW
+2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcO
+p2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJR
+xDHVA6zaGAo17Y0CAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsF
+AAOCAQEAQ20RfnhWErcr9Bvu6mjwrmB7WGXO+vvwODj2IeG+r9y+25ZClHYp/87p
+dKUknyhmQ7O+lgKDp+lzNFkt92JaEW2im1gBPFN/ZZi/WCOS6y+s3r/2Ji/+6BHr
+R+SXvrizmpyNEzlAHOiG6bKv4UuECM4kK656tJmH+dH0sGiYSdmhf9lwM1m6X3r6
+kTToN0G8rUJc6hS3WuVRoKNrtBZxq7f0Z+lcBtnnCH7PQc94Sm06bauo1WBGE0E+
+1CXmL8M/22hDDfyI1Ve0NMmrK1YGRCY3YD+A9dCnc6zM60YtXAXhsGA8Mh8mdIfV
+8y8gFBiMrWlNnWB/AC4bea8HBguxqA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem.certspec
new file mode 100644
index 0000000000..9fdb2a248a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth-invalid.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-limited-depth
+subject:int-limited-depth-invalid
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem
new file mode 100644
index 0000000000..39c00d26ce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbagAwIBAgIUcylHQcmSUhRsK5erE0SNaPg0+okwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowHDEaMBgGA1UEAwwRaW50LWxpbWl0ZWQtZGVwdGgwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
+V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
+0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
+fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
+W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
+TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjEzARMA8G
+A1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAHHwYSdzz1ScKu0ShKs/
+9CVPkIuGIYrMpyBYCarzkQOs+bQIV91JPpoUt2W6enHixZ54M8bdACKYC3CnBiYe
+wbPcZP0thQB7Sd5yHI3uNT8JSvinVv2EXcCNlZSei2Y+agc8d/9KMYvBYMG7SMHX
+Egp5L4xrwkKrWOQ88DnokFMHf650Ddq5A1b1NFBHAjkRIQGhjRnwlxuVGf1cAle+
+C6tDK7ptHQM5CtAEdzGmf51MoErFRH0RCxJmUcbGnHTiq7HBMWw6Q/8PMTUsndS6
+W9igwATnCJqLpynZ0SvaETMa42JFJ+goSOYKVZTy2/uhvRlW81ZaR2E3lqj4W6jB
+Svc=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem.certspec
new file mode 100644
index 0000000000..64f54b0441
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-limited-depth.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:int-limited-depth
+extension:basicConstraints:cA,0
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem
new file mode 100644
index 0000000000..90a2f26b95
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuTCCAaGgAwIBAgIUYcfJhXqUXKOGw+E5MNKdPih6hZ8wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowHDEaMBgGA1UEAwwRaW50LW5vLWV4dGVuc2lvbnMwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
+V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
+0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
+fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
+W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
+TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwDQYJKoZI
+hvcNAQELBQADggEBAI7R3HB8uiokTZA8E4dciZE6IQXm+cwTwwC41KXk8gKwIdXN
+wuJiYmpFJS+ckS2azp8prxw9oGk1rv5V3v1b3yQ1js1MAEOcGlhFG02OMoEegOVe
+RI5p1WN0A6S8sf4HwvbyxBWgM0J+uFez+GKpX2X/UVGtG+YI9yZ0x5BGdUsdKbvS
+KJVgYmAjOg9wfl2vM71mfzMTVSHk7Nva7c0qjC3tNtGpILWrZH9tyccdJqSRTZzO
+Xge+BUCe5hiTpT+sAGij6ESR1QBe1F8U2QmQRUhy3K2s43JFxWu5gep0XBY/p6kR
+6EXpbTn9mWFuRg7nLEcOwYtbAXA1mNtH9QXw6PE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem.certspec
new file mode 100644
index 0000000000..c99626bd5a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-extensions.pem.certspec
@@ -0,0 +1,2 @@
+issuer:ca
+subject:int-no-extensions
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem
new file mode 100644
index 0000000000..00f1d1ddfe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyjCCAbKgAwIBAgIUQXSt+CjpOfyWdZcfjuLpGReDI5QwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowGzEZMBcGA1UEAwwQaW50LW5vLWt1LW5vLWVrdTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMQMA4wDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEASpnks6qV+XZPFh2HPqCPcm9H
+M4zZafwvx+OvwALtsu3goai06A2z2lWt/uGDNa773kkQI8rRBieuwjk7mUgAZFhm
+HbcGmVaih01VYVrPzr5iKhoY0p3RVDxtPJusy8OXv4+jPEpQ8SnIUIPzpoSUhpA4
+ejH/rZNOUuKnz5aI01mmAq2Q0eUlQLFvjJPy4qfJ7R2vuhDi7T/Op3Fbd78a1A2I
+/411XZ7rpjbh2tJVHI71kpaHGXGeppciYxPwjRG+hlebKFD9NVXldCcvVzy8DNzu
+FjCMqGOsHm87uu6R6ZDekW+Zxy30Ui2qW74b06nxlxphHgO0LglFhwwKVhs3Dw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem.certspec
new file mode 100644
index 0000000000..306a218db9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-no-eku.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:int-no-ku-no-eku
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem
new file mode 100644
index 0000000000..e2c2941f73
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIUe0/iqtgXpUCqSBbTkk3j6cHrF40wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowHzEdMBsGA1UEAwwUaW50LW5vLWt1LXNlcnZlci1la3UwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjLzAt
+MAwGA1UdEwQFMAMBAf8wHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0G
+CSqGSIb3DQEBCwUAA4IBAQA66A/sfTMBP0yPBfI4TtxMv1Af0gpOchsekkY4lUxg
+IgsSvqjrlW/r7oBVpucIqH03wlHZkoiY+rl2hokh5fVNmztaBSaSQSdzrwW7qpsS
+0OFmUmPPcV4Kjy4EaaFXk2diNP/xCENwci6CN6iVvTrHt1Nu5DVEAbEwQVjN2Lme
+f1prvxAcqkyoJqHNQyS5nr31Bc4OdBFEJJ4ZjMvJV0UfRH0fkwhutIW4kgYgHp6o
+6BVpnDyqEN0yZaNHIQ8VP1hl+gSoHYmhX6BuF6vI739ACKlZXMCtGEEOi0o/oZb8
+GMUgv22QZFC5JMiWwhbcN6LJC5XpGEl9X/FwDndrv2JZ
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem.certspec
new file mode 100644
index 0000000000..1482b627c7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-no-ku-server-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-no-ku-server-eku
+extension:basicConstraints:cA,
+extension:extKeyUsage:serverAuth,clientAuth
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem
new file mode 100644
index 0000000000..9857f050c3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwzCCAaugAwIBAgIULcyFCPC5AqS4vb2uiw2vfP5d52swDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowFzEVMBMGA1UEAwwMaW50LW5vdC1hLWNhMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABow0wCzAJBgNVHRME
+AjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAib9DQGbyyJ/RSZet5VQiZ/1WLmOZNDU63
+1tmavPbUWtoV6ac2nvObfMBxiMfog51CyS9kys9umj5i4wBOFc07ADPnZvZ4JzCH
+DMJqe83Q9DNMRqiUFgoioLmZM4+t6gOFdfv0Hcm1/dPrT7PAX/68aa2OP95vPrec
+LTKHirbpV9BxgH2ZFWmiI/iMYlEJpb9BcJfpAV9VzmOAoXwuP87qqxdWUhMuE3V/
+Jopnz32iH/j6HEsmhG5cixmiDOoVcuKo0EpsZSAV6QjQJWZ0KYBfnFwqSkaNLAzd
+Tsql/80wHPgrlb5G0na1VWqfzijcWrMM9GwYu2nliE0b8Erl70Wc
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem.certspec
new file mode 100644
index 0000000000..3161680b1c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-not-a-ca.pem.certspec
@@ -0,0 +1,3 @@
+issuer:ca
+subject:int-not-a-ca
+extension:basicConstraints:,
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem
new file mode 100644
index 0000000000..b426d19e30
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC2jCCAcKgAwIBAgIUAQgTd7cj4mynq7/pBIjhOMglEsUwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowHjEcMBoGA1UEAwwTaW50LXZhbGlkLWt1LW5vLWVrdTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs
+9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8
+HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7Ak
+kqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJet
+lmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2r
+kQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBsw
+DAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAgQwDQYJKoZIhvcNAQELBQADggEBAI+T
+Z5fdjBIEKwA7tLJHWAkXLYYX2/NIjc3ZS5J1+LMpQXA+Rmw7T8zZ5Tecf7wvHJNd
+qsjNFkOx9IkV3tiGPsv7J3B53Kk5qJ1LR3sNj99YW6zs0SpETYbVc0IY49ZP8eOA
+BMS6eGrkSYgXXTWN6fu83gr+/QB6vCri8xkOw7NM0gnfoZhOAXmKkWILW3S1Arj0
+E6FpW88Pww6miceSa8ecdc4QL8qRIFKDZTXCIwykjeOLZgw3JTOr0YYGO0uBjspj
+ASheUaiNfEGbLIU+PySMvgX3RRheSDMIxeoKCL8Wg5nyIg/6yE5vXipiTexqk9rR
+0z/z588qvYn2g5AqUB0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem.certspec
new file mode 100644
index 0000000000..d7f9b0387a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-no-eku.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-valid-ku-no-eku
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem
new file mode 100644
index 0000000000..834ea6ef65
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAeWgAwIBAgIUWWZayMVWHspFSBkAYaO7s8p84NAwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowIjEgMB4GA1UEAwwXaW50LXZhbGlkLWt1LXNlcnZlci1la3UwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erk
+NUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwC
+fs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1m
+CyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTM
+HGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m
+1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGj
+PDA6MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgIEMB0GA1UdJQQWMBQGCCsGAQUF
+BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAud9bDsVnUs48CsoNie24
+ZnxZPn6RJe6JjZUDdAYwVVYVzJz4UqZF2kwwjXm0Dzs758WdbqgP22bBrs6sZZBI
+39Rw74d2apnh8PpVzrsKVQSQqh8RAk38c6MDDRhYo859sTW2/Lvyo2PHr4wIR2jH
++awmkP3EEq7c1kt23q2c0u+rxKR20pYjJJ0br92b/buiOfjdA9q0pF/rRdm6/U4L
+ApUHYzyXiuEoekLbLf5tIL3Xel/OkaD7jBrrEWll/vWj+RBI4B33LViDJ9lMjavP
+KJHDM7PbzM1XKogapqmfqxYXzyY+ZtxurIXTkyWuuNfINd+OMOzRkvwDYkATNEGD
+OQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem.certspec
new file mode 100644
index 0000000000..84314bfa40
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/int-valid-ku-server-eku.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-valid-ku-server-eku
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign
+extension:extKeyUsage:serverAuth,clientAuth
diff --git a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build
new file mode 100644
index 0000000000..e2b0dd7599
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build
@@ -0,0 +1,35 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'ee-int-bad-ku-no-eku.pem',
+# 'ee-int-bad-ku-server-eku.pem',
+# 'ee-int-cA-FALSE-asserts-keyCertSign.pem',
+# 'ee-int-limited-depth.pem',
+# 'ee-int-limited-depth-invalid.pem',
+# 'ee-int-no-extensions.pem',
+# 'ee-int-no-ku-no-eku.pem',
+# 'ee-int-no-ku-server-eku.pem',
+# 'ee-int-not-a-ca.pem',
+# 'ee-int-valid-ku-no-eku.pem',
+# 'ee-int-valid-ku-server-eku.pem',
+# 'int-bad-ku-no-eku.pem',
+# 'int-bad-ku-server-eku.pem',
+# 'int-cA-FALSE-asserts-keyCertSign.pem',
+# 'int-limited-depth.pem',
+# 'int-limited-depth-invalid.pem',
+# 'int-no-extensions.pem',
+# 'int-no-ku-no-eku.pem',
+# 'int-no-ku-server-eku.pem',
+# 'int-not-a-ca.pem',
+# 'int-valid-ku-no-eku.pem',
+# 'int-valid-ku-server-eku.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads.js b/security/manager/ssl/tests/unit/test_intermediate_preloads.js
new file mode 100644
index 0000000000..1f8b289eb3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads.js
@@ -0,0 +1,645 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+do_get_profile(); // must be called before getting nsIX509CertDB
+
+const { RemoteSettings } = ChromeUtils.import(
+ "resource://services-settings/remote-settings.js"
+);
+const { RemoteSecuritySettings } = ChromeUtils.import(
+ "resource://gre/modules/psm/RemoteSecuritySettings.jsm"
+);
+const { TestUtils } = ChromeUtils.import(
+ "resource://testing-common/TestUtils.jsm"
+);
+const { TelemetryTestUtils } = ChromeUtils.import(
+ "resource://testing-common/TelemetryTestUtils.jsm"
+);
+const { IntermediatePreloadsClient } = RemoteSecuritySettings.init();
+
+let server;
+
+let intermediate1Data;
+let intermediate2Data;
+
+const INTERMEDIATES_DL_PER_POLL_PREF =
+ "security.remote_settings.intermediates.downloads_per_poll";
+const INTERMEDIATES_ENABLED_PREF =
+ "security.remote_settings.intermediates.enabled";
+
+function getHashCommon(aStr, useBase64) {
+ let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ hasher.init(Ci.nsICryptoHash.SHA256);
+ let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
+ Ci.nsIStringInputStream
+ );
+ stringStream.data = aStr;
+ hasher.updateFromStream(stringStream, -1);
+
+ return hasher.finish(useBase64);
+}
+
+// Get a hexified SHA-256 hash of the given string.
+function getHash(aStr) {
+ return hexify(getHashCommon(aStr, false));
+}
+
+function countTelemetryReports(histogram) {
+ let count = 0;
+ for (let x in histogram.values) {
+ count += histogram.values[x];
+ }
+ return count;
+}
+
+function clearTelemetry() {
+ Services.telemetry.getHistogramById("INTERMEDIATE_PRELOADING_ERRORS").clear();
+ Services.telemetry
+ .getHistogramById("INTERMEDIATE_PRELOADING_UPDATE_TIME_MS")
+ .clear();
+ Services.telemetry.clearScalars();
+}
+
+function getSubjectBytes(certDERString) {
+ let bytes = stringToArray(certDERString);
+ let cert = new X509.Certificate();
+ cert.parse(bytes);
+ return arrayToString(cert.tbsCertificate.subject._der._bytes);
+}
+
+function getSPKIBytes(certDERString) {
+ let bytes = stringToArray(certDERString);
+ let cert = new X509.Certificate();
+ cert.parse(bytes);
+ return arrayToString(cert.tbsCertificate.subjectPublicKeyInfo._der._bytes);
+}
+
+/**
+ * Simulate a Remote Settings synchronization by filling up the
+ * local data with fake records.
+ *
+ * @param {*} filenames List of pem files for which we will create
+ * records.
+ * @param {*} options Options for records to generate.
+ */
+async function syncAndDownload(filenames, options = {}) {
+ const {
+ hashFunc = getHash,
+ lengthFunc = arr => arr.length,
+ clear = true,
+ } = options;
+
+ const localDB = await IntermediatePreloadsClient.client.db;
+ if (clear) {
+ await localDB.clear();
+ }
+
+ let count = 1;
+ for (const filename of filenames) {
+ const file = do_get_file(`test_intermediate_preloads/${filename}`);
+ const certBytes = readFile(file);
+ const certDERBytes = atob(pemToBase64(certBytes));
+
+ const record = {
+ details: {
+ who: "",
+ why: "",
+ name: "",
+ created: "",
+ },
+ derHash: getHashCommon(certDERBytes, true),
+ subject: "",
+ subjectDN: btoa(getSubjectBytes(certDERBytes)),
+ attachment: {
+ hash: hashFunc(certBytes),
+ size: lengthFunc(certBytes),
+ filename: `intermediate certificate #${count}.pem`,
+ location: `security-state-workspace/intermediates/${filename}`,
+ mimetype: "application/x-pem-file",
+ },
+ whitelist: false,
+ pubKeyHash: getHashCommon(getSPKIBytes(certDERBytes), true),
+ crlite_enrolled: true,
+ };
+
+ await localDB.create(record);
+ count++;
+ }
+ // This promise will wait for the end of downloading.
+ const updatedPromise = TestUtils.topicObserved(
+ "remote-security-settings:intermediates-updated"
+ );
+ // Simulate polling for changes, trigger the download of attachments.
+ Services.obs.notifyObservers(null, "remote-settings:changes-poll-end");
+ const results = await updatedPromise;
+ return results[1]; // topicObserved gives back a 2-array
+}
+
+/**
+ * Return the list of records whose attachment was downloaded.
+ */
+async function locallyDownloaded() {
+ return IntermediatePreloadsClient.client.get({
+ filters: { cert_import_complete: true },
+ syncIfEmpty: false,
+ });
+}
+
+add_task(async function test_preload_empty() {
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+
+ // load the first root and end entity, ignore the initial intermediate
+ addCertFromFile(certDB, "test_intermediate_preloads/ca.pem", "CTu,,");
+
+ let ee_cert = constructCertFromFile(
+ "test_intermediate_preloads/default-ee.pem"
+ );
+ notEqual(ee_cert, null, "EE cert should have successfully loaded");
+
+ equal(
+ await syncAndDownload([]),
+ "success",
+ "Preloading update should have run"
+ );
+
+ equal(
+ (await locallyDownloaded()).length,
+ 0,
+ "There should have been no downloads"
+ );
+
+ // check that ee cert 1 is unknown
+ await checkCertErrorGeneric(
+ certDB,
+ ee_cert,
+ SEC_ERROR_UNKNOWN_ISSUER,
+ certificateUsageSSLServer
+ );
+});
+
+add_task(async function test_preload_disabled() {
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, false);
+
+ equal(
+ await syncAndDownload(["int.pem"]),
+ "disabled",
+ "Preloading update should not have run"
+ );
+
+ equal(
+ (await locallyDownloaded()).length,
+ 0,
+ "There should have been no downloads"
+ );
+});
+
+add_task(async function test_preload_invalid_hash() {
+ // Enable the collection (during test) for all products so even products
+ // that don't collect the data will be able to run the test without failure.
+ Services.prefs.setBoolPref(
+ "toolkit.telemetry.testing.overrideProductsCheck",
+ true
+ );
+
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+ const invalidHash =
+ "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d";
+
+ clearTelemetry();
+
+ const result = await syncAndDownload(["int.pem"], {
+ hashFunc: () => invalidHash,
+ });
+ equal(result, "success", "Preloading update should have run");
+
+ let errors_histogram = Services.telemetry
+ .getHistogramById("INTERMEDIATE_PRELOADING_ERRORS")
+ .snapshot();
+
+ equal(
+ countTelemetryReports(errors_histogram),
+ 1,
+ "There should be one error report"
+ );
+ equal(
+ errors_histogram.values[7],
+ 1,
+ "There should be one invalid hash error"
+ );
+
+ equal(
+ (await locallyDownloaded()).length,
+ 0,
+ "There should be no local entry"
+ );
+
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+
+ // load the first root and end entity, ignore the initial intermediate
+ addCertFromFile(certDB, "test_intermediate_preloads/ca.pem", "CTu,,");
+
+ let ee_cert = constructCertFromFile(
+ "test_intermediate_preloads/default-ee.pem"
+ );
+ notEqual(ee_cert, null, "EE cert should have successfully loaded");
+
+ // We should still have a missing intermediate.
+ await checkCertErrorGeneric(
+ certDB,
+ ee_cert,
+ SEC_ERROR_UNKNOWN_ISSUER,
+ certificateUsageSSLServer
+ );
+});
+
+add_task(async function test_preload_invalid_length() {
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+
+ clearTelemetry();
+
+ const result = await syncAndDownload(["int.pem"], {
+ lengthFunc: () => 42,
+ });
+ equal(result, "success", "Preloading update should have run");
+
+ let errors_histogram = Services.telemetry
+ .getHistogramById("INTERMEDIATE_PRELOADING_ERRORS")
+ .snapshot();
+
+ equal(
+ countTelemetryReports(errors_histogram),
+ 1,
+ "There should be only one error report"
+ );
+ equal(
+ errors_histogram.values[7],
+ 1,
+ "There should be one invalid content hash error"
+ );
+
+ equal(
+ (await locallyDownloaded()).length,
+ 0,
+ "There should be no local entry"
+ );
+
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+
+ // load the first root and end entity, ignore the initial intermediate
+ addCertFromFile(certDB, "test_intermediate_preloads/ca.pem", "CTu,,");
+
+ let ee_cert = constructCertFromFile(
+ "test_intermediate_preloads/default-ee.pem"
+ );
+ notEqual(ee_cert, null, "EE cert should have successfully loaded");
+
+ // We should still have a missing intermediate.
+ await checkCertErrorGeneric(
+ certDB,
+ ee_cert,
+ SEC_ERROR_UNKNOWN_ISSUER,
+ certificateUsageSSLServer
+ );
+});
+
+add_task(async function test_preload_basic() {
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+ Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 100);
+
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+
+ // load the first root and end entity, ignore the initial intermediate
+ addCertFromFile(certDB, "test_intermediate_preloads/ca.pem", "CTu,,");
+
+ let ee_cert = constructCertFromFile(
+ "test_intermediate_preloads/default-ee.pem"
+ );
+ notEqual(ee_cert, null, "EE cert should have successfully loaded");
+
+ // load the second end entity, ignore both intermediate and root
+ let ee_cert_2 = constructCertFromFile("test_intermediate_preloads/ee2.pem");
+ notEqual(ee_cert_2, null, "EE cert 2 should have successfully loaded");
+
+ // check that the missing intermediate causes an unknown issuer error, as
+ // expected, in both cases
+ await checkCertErrorGeneric(
+ certDB,
+ ee_cert,
+ SEC_ERROR_UNKNOWN_ISSUER,
+ certificateUsageSSLServer
+ );
+ await checkCertErrorGeneric(
+ certDB,
+ ee_cert_2,
+ SEC_ERROR_UNKNOWN_ISSUER,
+ certificateUsageSSLServer
+ );
+
+ let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+ let intermediateBytes = readFile(
+ do_get_file("test_intermediate_preloads/int.pem")
+ );
+ let intermediateDERBytes = atob(pemToBase64(intermediateBytes));
+ let intermediateCert = new X509.Certificate();
+ intermediateCert.parse(stringToArray(intermediateDERBytes));
+ let crliteStateBefore = certStorage.getCRLiteState(
+ intermediateCert.tbsCertificate.subject._der._bytes,
+ intermediateCert.tbsCertificate.subjectPublicKeyInfo._der._bytes
+ );
+ equal(
+ crliteStateBefore,
+ Ci.nsICertStorage.STATE_UNSET,
+ "crlite state should be unset before"
+ );
+
+ const result = await syncAndDownload(["int.pem", "int2.pem"]);
+ equal(result, "success", "Preloading update should have run");
+
+ equal(
+ (await locallyDownloaded()).length,
+ 2,
+ "There should have been 2 downloads"
+ );
+
+ // check that ee cert 1 verifies now the update has happened and there is
+ // an intermediate
+
+ // First verify by connecting to a server that uses that end-entity
+ // certificate but doesn't send the intermediate.
+ await asyncStartTLSTestServer(
+ "BadCertAndPinningServer",
+ "test_intermediate_preloads"
+ );
+ // This ensures the test server doesn't include the intermediate in the
+ // handshake.
+ let certDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ certDir.append("test_intermediate_preloads");
+ Assert.ok(certDir.exists(), "test_intermediate_preloads should exist");
+ let args = ["-D", "-n", "int"];
+ // If the certdb is cached from a previous run, the intermediate will have
+ // already been deleted, so this may "fail".
+ run_certutil_on_directory(certDir.path, args, false);
+ let certsCachedPromise = TestUtils.topicObserved(
+ "psm:intermediate-certs-cached"
+ );
+ await asyncConnectTo("ee.example.com", PRErrorCodeSuccess);
+ let subjectAndData = await certsCachedPromise;
+ Assert.equal(subjectAndData.length, 2, "expecting [subject, data]");
+ // Since the intermediate is preloaded, we don't save it to the profile's
+ // certdb.
+ Assert.equal(subjectAndData[1], "0", `expecting "0" certs imported`);
+
+ await checkCertErrorGeneric(
+ certDB,
+ ee_cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+
+ let localDB = await IntermediatePreloadsClient.client.db;
+ let data = await localDB.list();
+ ok(data.length > 0, "should have some entries");
+ // simulate a sync (syncAndDownload doesn't actually... sync.)
+ await IntermediatePreloadsClient.client.emit("sync", {
+ data: {
+ current: data,
+ created: data,
+ deleted: [],
+ updated: [],
+ },
+ });
+
+ let crliteStateAfter = certStorage.getCRLiteState(
+ intermediateCert.tbsCertificate.subject._der._bytes,
+ intermediateCert.tbsCertificate.subjectPublicKeyInfo._der._bytes
+ );
+ equal(
+ crliteStateAfter,
+ Ci.nsICertStorage.STATE_ENFORCE,
+ "crlite state should be set after"
+ );
+
+ // check that ee cert 2 does not verify - since we don't know the issuer of
+ // this certificate
+ await checkCertErrorGeneric(
+ certDB,
+ ee_cert_2,
+ SEC_ERROR_UNKNOWN_ISSUER,
+ certificateUsageSSLServer
+ );
+});
+
+add_task(async function test_preload_200() {
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+ Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 100);
+
+ const files = [];
+ for (let i = 0; i < 200; i++) {
+ files.push(["int.pem", "int2.pem"][i % 2]);
+ }
+
+ clearTelemetry();
+
+ let result = await syncAndDownload(files);
+ equal(result, "success", "Preloading update should have run");
+
+ equal(
+ (await locallyDownloaded()).length,
+ 100,
+ "There should have been only 100 downloaded"
+ );
+
+ const scalars = TelemetryTestUtils.getProcessScalars("parent");
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "security.intermediate_preloading_num_preloaded",
+ 100,
+ "Should have preloaded 100 certs"
+ );
+ TelemetryTestUtils.assertScalar(
+ scalars,
+ "security.intermediate_preloading_num_pending",
+ 100,
+ "Should report 100 pending"
+ );
+
+ let time_histogram = Services.telemetry
+ .getHistogramById("INTERMEDIATE_PRELOADING_UPDATE_TIME_MS")
+ .snapshot();
+ let errors_histogram = Services.telemetry
+ .getHistogramById("INTERMEDIATE_PRELOADING_ERRORS")
+ .snapshot();
+ equal(countTelemetryReports(time_histogram), 1, "Should report time once");
+ equal(
+ countTelemetryReports(errors_histogram),
+ 0,
+ "There should be no error reports"
+ );
+
+ // Re-run
+ result = await syncAndDownload([], { clear: false });
+ equal(result, "success", "Preloading update should have run");
+
+ equal(
+ (await locallyDownloaded()).length,
+ 200,
+ "There should have been 200 downloaded"
+ );
+});
+
+add_task(async function test_delete() {
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+ Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 100);
+
+ let syncResult = await syncAndDownload(["int.pem", "int2.pem"]);
+ equal(syncResult, "success", "Preloading update should have run");
+
+ equal(
+ (await locallyDownloaded()).length,
+ 2,
+ "There should have been 2 downloads"
+ );
+
+ let localDB = await IntermediatePreloadsClient.client.db;
+ let data = await localDB.list();
+ ok(data.length > 0, "should have some entries");
+ let subject = data[0].subjectDN;
+ let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
+ Ci.nsICertStorage
+ );
+ let resultsBefore = certStorage.findCertsBySubject(
+ stringToArray(atob(subject))
+ );
+ equal(
+ resultsBefore.length,
+ 1,
+ "should find the intermediate in cert storage before"
+ );
+ // simulate a sync where we deleted the entry
+ await IntermediatePreloadsClient.client.emit("sync", {
+ data: {
+ current: [],
+ created: [],
+ deleted: [data[0]],
+ updated: [],
+ },
+ });
+ let resultsAfter = certStorage.findCertsBySubject(
+ stringToArray(atob(subject))
+ );
+ equal(
+ resultsAfter.length,
+ 0,
+ "shouldn't find intermediate in cert storage now"
+ );
+});
+
+function findCertByCommonName(certDB, commonName) {
+ for (let cert of certDB.getCerts()) {
+ if (cert.commonName == commonName) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+add_task(async function test_healer() {
+ Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
+ Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 100);
+
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ // Add an intermediate as if it had previously been cached.
+ addCertFromFile(certDB, "test_intermediate_preloads/int.pem", ",,");
+ // Add an intermediate with non-default trust settings as if it had been added by the user.
+ addCertFromFile(certDB, "test_intermediate_preloads/int2.pem", "CTu,,");
+
+ let syncResult = await syncAndDownload(["int.pem", "int2.pem"]);
+ equal(syncResult, "success", "Preloading update should have run");
+
+ equal(
+ (await locallyDownloaded()).length,
+ 2,
+ "There should have been 2 downloads"
+ );
+
+ let healerRanPromise = TestUtils.topicObserved(
+ "psm:intermediate-preloading-healer-ran"
+ );
+ Services.prefs.setIntPref(
+ "security.intermediate_preloading_healer.timer_interval_ms",
+ 500
+ );
+ Services.prefs.setBoolPref(
+ "security.intermediate_preloading_healer.enabled",
+ true
+ );
+ await healerRanPromise;
+ Services.prefs.setBoolPref(
+ "security.intermediate_preloading_healer.enabled",
+ false
+ );
+
+ let intermediate = findCertByCommonName(
+ certDB,
+ "intermediate-preloading-intermediate"
+ );
+ equal(intermediate, null, "should not find intermediate in NSS");
+ let intermediate2 = findCertByCommonName(
+ certDB,
+ "intermediate-preloading-intermediate2"
+ );
+ notEqual(intermediate2, null, "should find second intermediate in NSS");
+});
+
+function run_test() {
+ server = new HttpServer();
+ server.start(-1);
+ registerCleanupFunction(() => server.stop(() => {}));
+
+ server.registerDirectory(
+ "/cdn/security-state-workspace/intermediates/",
+ do_get_file("test_intermediate_preloads")
+ );
+
+ server.registerPathHandler("/v1/", (request, response) => {
+ response.write(
+ JSON.stringify({
+ capabilities: {
+ attachments: {
+ base_url: `http://localhost:${server.identity.primaryPort}/cdn/`,
+ },
+ },
+ })
+ );
+ response.setHeader("Content-Type", "application/json; charset=UTF-8");
+ response.setStatusLine(null, 200, "OK");
+ });
+
+ Services.prefs.setCharPref(
+ "services.settings.server",
+ `http://localhost:${server.identity.primaryPort}/v1`
+ );
+
+ Services.prefs.setCharPref("browser.policies.loglevel", "debug");
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem b/security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem
new file mode 100644
index 0000000000..680b068f34
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+TCCAeGgAwIBAgIUN/Y56TvJcL2liqk2Feh/QfKrlLwwDQYJKoZIhvcNAQEL
+BQAwJTEjMCEGA1UEAwwaaW50ZXJtZWRpYXRlLXByZWxvYWRpbmctY2EwIhgPMjAx
+MDAxMDEwMDAwMDBaGA8yMDUwMDEwMTAwMDAwMFowJTEjMCEGA1UEAwwaaW50ZXJt
+ZWRpYXRlLXByZWxvYWRpbmctY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYD
+VR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBSPwr2BfSHT3saxwx6YGEautZx
+w/sdM9AJAubFLqDd3MYHtzCZcQXaeDGbAzvo8m/PKA4Yt+UYbKyDnRR8sLA4f/iu
+z1zHeenlzBWpRVHu/++ZSk/ESwn0zLprIsOcXjaYkbfrqcEGNWvLJzpT4T36Gr9t
+DvxHnpsaMsJviZS3WHzTSoioWkcRyF78bYa51ZJWYJHFKZQppqhJ+jcoJhiomRlc
+WwhI8NAU3dOOFJuEg/z+vQpcEQi0rRW9J6X/15BUZRQlF5Hs2wilGa8ViNX2+B5I
+kjbmNrdT5hcnGEfR7JpHFuihFdxQc4CFY87u1chI8yaHLhhriUP6Jq0+J5ur
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem.certspec
new file mode 100644
index 0000000000..4ccabc25b3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:intermediate-preloading-ca
+subject:intermediate-preloading-ca
+validity:20100101-20500101
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key.keyspec b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem
new file mode 100644
index 0000000000..1838ab40e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIUTw97bCabpmjf11gbMSrUIZkrl30wDQYJKoZIhvcNAQEL
+BQAwLzEtMCsGA1UEAwwkaW50ZXJtZWRpYXRlLXByZWxvYWRpbmctaW50ZXJtZWRp
+YXRlMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMBkxFzAVBgNV
+BAMMDmVlLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozIwMDATBgNVHSUEDDAKBggrBgEFBQcD
+ATAZBgNVHREEEjAQgg5lZS5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEA
+J7Ff5mE6qJuS+kszRFBNnD2OBj0ss6mrayQCqqJqeM01JHRFJ295iDvxHMbtzfYH
+EMSytow8dZ8vWoxPSDSr3TV8kJOYYKXjW5ClYi6rWwHt6aC9zPTQ87AspBBKhf3q
+2jX+fPoLIKzauktAnzYApvyTTe8fNE/LfLHpDpR2ipQBKTz1J2Pw9RHcp7WxC9rW
+6PTH40kMLD2F3fRYdrsJ37nIe853MvUsH3Fa6oMfGxpvIpN3gJtj3w0OPj8rWq9X
+gQbN/56afzE4zG0HhX+RP71LXpn1US0twQfP0GM0q6xh0P7pfWT8Z7lHk1j4j7u+
+3RU1do8+1Ak6TLrMnfkZBw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem.certspec
new file mode 100644
index 0000000000..e9decb76dc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/default-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:intermediate-preloading-intermediate
+subject:ee.example.com
+extension:extKeyUsage:serverAuth
+extension:subjectAlternativeName:ee.example.com
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem b/security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem
new file mode 100644
index 0000000000..dcb44b9953
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAc+gAwIBAgIUV3PLWwVDtiZkBpRLWYBIrcA3uowwDQYJKoZIhvcNAQEL
+BQAwMDEuMCwGA1UEAwwlaW50ZXJtZWRpYXRlLXByZWxvYWRpbmctaW50ZXJtZWRp
+YXRlMjAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAOMQwwCgYD
+VQQDDANlZTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W
+1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtq
+ZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx
+0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthV
+t2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo
+4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx
+1QOs2hgKNe2NAgMBAAGjFzAVMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3
+DQEBCwUAA4IBAQBSxrC9YRPkmqYz2MsbyytjlG/f0g2uVv78Q3yR0tVsGW67VH0l
+X3rUdqWXCrYi8NtmZjbwx/EERtXc4dLXmJ2DEaHWPygQ3zAiKBAEWh9COaz7b11/
+wHu3uZVOXsWKqt212FpjKob6n4ceQu2RlnYYX+VVSKrt4ct3ph5X31D/heerNvLp
+Et6UETIAY6PhC982XC3wn/r0ykIPQygXKdMJ3Umvq+OzOObn7RTX10EPdOmyU9su
+bmpR4oYBPB71GWGoeNpp17swOe9BVvQvIcnEwiWuVeWhaZvFAmrFe1484PnTgcjg
+kLtYKtzg0Z8t1jWExmdb00A8CP92iHa2HGl+
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem.certspec
new file mode 100644
index 0000000000..089ac63831
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/ee2.pem.certspec
@@ -0,0 +1,3 @@
+issuer:intermediate-preloading-intermediate2
+subject:ee2
+extension:extKeyUsage:serverAuth
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem b/security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem
new file mode 100644
index 0000000000..ad98f78efd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAeugAwIBAgIUHyDRZ9EprKjXteNSxinckLtLUqMwDQYJKoZIhvcNAQEL
+BQAwJTEjMCEGA1UEAwwaaW50ZXJtZWRpYXRlLXByZWxvYWRpbmctY2EwIhgPMjAx
+OTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowLzEtMCsGA1UEAwwkaW50ZXJt
+ZWRpYXRlLXByZWxvYWRpbmctaW50ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRME
+BTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEARadVY9uVwXt3
+8p81yXQxJ87Vyc02HL9qc1l1+ttJ0g0CL10pEJjT8rbXwghaGQ8Jw/GGhoYVy0VH
++5+uqnpvLaBgFX7noX5+eKABzVW3NfcD9705Ul3N8dFo11mGsvzL3+B5ucOyjY+9
+CPLIzYCBY5OnCIrsU+zkS9TcoqblvPQGkzVdHcNQLy01Fs6XJ4VQhoP6KZ2rsCJn
+QcHlg5t5HXywCf5ihzXPqWB7Ib7GL7nLIv7ViyHK6QkEPpeHtSxcs2OiwkJCeKTs
+v2HFnnLiKfF0zGhOp8nLaMXmFTCaNBLs7YaSbWqOYct9zNTBlDtO+GcYqDx74B6c
++W7hLcJ9aA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem.certspec
new file mode 100644
index 0000000000..5863b3131f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/int.pem.certspec
@@ -0,0 +1,4 @@
+issuer:intermediate-preloading-ca
+subject:intermediate-preloading-intermediate
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem b/security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem
new file mode 100644
index 0000000000..c11853b08a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBTCCAe2gAwIBAgIUb7hTHv3uBY6lDd0XbWQLu/+jjIQwDQYJKoZIhvcNAQEL
+BQAwJjEkMCIGA1UEAwwbaW50ZXJtZWRpYXRlLXByZWxvYWRpbmctY2EyMCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMDAxLjAsBgNVBAMMJWludGVy
+bWVkaWF0ZS1wcmVsb2FkaW5nLWludGVybWVkaWF0ZTIwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk
+e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg
+KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI
+YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi
+lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL
+HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1Ud
+EwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQBJYzhJ9SuX
+sqKTHwITcbGmv30f1CFOE23Pwf1KSk/Rqw2yx7CCeobGpLQUlIU72lGqZ0W4BVGy
+TJDwGOGTKtFYVtYddo5DXVuUREUcUeRzAwSZwEhm2ANyEXypRffmQNR+h4VH3skX
+2pWfNH8TAes+z6Vz4HCkEOqa3uBUe9GMjbg8Gtrqsx2Vt3hJrg9dB2/0I5POMM95
+9nRtl6ZHx81DMccy6YT/lhsBDIA106u75THS45yuFNRtzPaL06/7iiNFjW+upsf7
+3Bg6x7VKBt2ZGFebHFiMnGyVPsMyXQw/i1w6yiXD04F9PHdYjMtY90hH69aGhQNk
+Lgcw88dDaKaL
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem.certspec b/security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem.certspec
new file mode 100644
index 0000000000..27e9a008df
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/int2.pem.certspec
@@ -0,0 +1,4 @@
+issuer:intermediate-preloading-ca2
+subject:intermediate-preloading-intermediate2
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads/moz.build b/security/manager/ssl/tests/unit/test_intermediate_preloads/moz.build
new file mode 100644
index 0000000000..855b02df7f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ca.pem',
+# 'default-ee.pem',
+# 'ee2.pem',
+# 'int.pem',
+# 'int2.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'default-ee.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_keysize.js b/security/manager/ssl/tests/unit/test_keysize.js
new file mode 100644
index 0000000000..45066a3888
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize.js
@@ -0,0 +1,204 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Checks that RSA certs with key sizes below 1024 bits are rejected.
+// Checks that ECC certs using curves other than the NIST P-256, P-384 or P-521
+// curves are rejected.
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+/**
+ * Tests a cert chain.
+ *
+ * @param {String} rootKeyType
+ * The key type of the root certificate, or the name of an elliptic
+ * curve, as output by the 'openssl ecparam -list_curves' command.
+ * @param {Number} rootKeySize
+ * @param {String} intKeyType
+ * @param {Number} intKeySize
+ * @param {String} eeKeyType
+ * @param {Number} eeKeySize
+ * @param {PRErrorCode} eeExpectedError
+ * @return {Promise} a promise that will resolve when the verification has
+ * completed
+ */
+function checkChain(
+ rootKeyType,
+ rootKeySize,
+ intKeyType,
+ intKeySize,
+ eeKeyType,
+ eeKeySize,
+ eeExpectedError
+) {
+ let rootName = "root_" + rootKeyType + "_" + rootKeySize;
+ let intName = "int_" + intKeyType + "_" + intKeySize;
+ let eeName = "ee_" + eeKeyType + "_" + eeKeySize;
+
+ let intFullName = intName + "-" + rootName;
+ let eeFullName = eeName + "-" + intName + "-" + rootName;
+
+ addCertFromFile(certdb, `test_keysize/${rootName}.pem`, "CTu,CTu,CTu");
+ addCertFromFile(certdb, `test_keysize/${intFullName}.pem`, ",,");
+ let eeCert = constructCertFromFile(`test_keysize/${eeFullName}.pem`);
+
+ info("cert o=" + eeCert.organization);
+ info("cert issuer o=" + eeCert.issuerOrganization);
+ return checkCertErrorGeneric(
+ certdb,
+ eeCert,
+ eeExpectedError,
+ certificateUsageSSLServer
+ );
+}
+
+/**
+ * Tests various RSA chains.
+ *
+ * @param {Number} inadequateKeySize
+ * @param {Number} adequateKeySize
+ */
+async function checkRSAChains(inadequateKeySize, adequateKeySize) {
+ // Chain with certs that have adequate sizes for DV
+ await checkChain(
+ "rsa",
+ adequateKeySize,
+ "rsa",
+ adequateKeySize,
+ "rsa",
+ adequateKeySize,
+ PRErrorCodeSuccess
+ );
+
+ // Chain with a root cert that has an inadequate size for DV
+ await checkChain(
+ "rsa",
+ inadequateKeySize,
+ "rsa",
+ adequateKeySize,
+ "rsa",
+ adequateKeySize,
+ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE
+ );
+
+ // Chain with an intermediate cert that has an inadequate size for DV
+ await checkChain(
+ "rsa",
+ adequateKeySize,
+ "rsa",
+ inadequateKeySize,
+ "rsa",
+ adequateKeySize,
+ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE
+ );
+
+ // Chain with an end entity cert that has an inadequate size for DV
+ await checkChain(
+ "rsa",
+ adequateKeySize,
+ "rsa",
+ adequateKeySize,
+ "rsa",
+ inadequateKeySize,
+ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE
+ );
+}
+
+async function checkECCChains() {
+ await checkChain(
+ "secp256r1",
+ 256,
+ "secp384r1",
+ 384,
+ "secp521r1",
+ 521,
+ PRErrorCodeSuccess
+ );
+ await checkChain(
+ "secp256r1",
+ 256,
+ "secp224r1",
+ 224,
+ "secp256r1",
+ 256,
+ SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE
+ );
+ await checkChain(
+ "secp256r1",
+ 256,
+ "secp256r1",
+ 256,
+ "secp224r1",
+ 224,
+ SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE
+ );
+ await checkChain(
+ "secp224r1",
+ 224,
+ "secp256r1",
+ 256,
+ "secp256r1",
+ 256,
+ SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE
+ );
+ await checkChain(
+ "secp256r1",
+ 256,
+ "secp256r1",
+ 256,
+ "secp256k1",
+ 256,
+ SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE
+ );
+ await checkChain(
+ "secp256k1",
+ 256,
+ "secp256r1",
+ 256,
+ "secp256r1",
+ 256,
+ SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE
+ );
+}
+
+async function checkCombinationChains() {
+ await checkChain(
+ "rsa",
+ 2048,
+ "secp256r1",
+ 256,
+ "secp384r1",
+ 384,
+ PRErrorCodeSuccess
+ );
+ await checkChain(
+ "rsa",
+ 2048,
+ "secp256r1",
+ 256,
+ "secp224r1",
+ 224,
+ SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE
+ );
+ await checkChain(
+ "secp256r1",
+ 256,
+ "rsa",
+ 1016,
+ "secp256r1",
+ 256,
+ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE
+ );
+}
+
+add_task(async function() {
+ await checkRSAChains(1016, 1024);
+ await checkECCChains();
+ await checkCombinationChains();
+});
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem
new file mode 100644
index 0000000000..a505fe3b6c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB4DCCAUmgAwIBAgIUP87ifngF30j5hBghK5jGe5gcLtswDQYJKoZIhvcNAQEL
+BQAwJTEjMCEGA1UEAwwaaW50X3JzYV8xMDI0LXJvb3RfcnNhXzEwMjQwIhgPMjAx
+OTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowMTEvMC0GA1UEAwwmZWVfcnNh
+XzEwMTYtaW50X3JzYV8xMDI0LXJvb3RfcnNhXzEwMjQwgZ4wDQYJKoZIhvcNAQEB
+BQADgYwAMIGIAoGAANKbsS+4T93NKbOlGctmxDuNj4vlRbp5OEzmY+0D33WZFgDr
+kgeQ0lMM7OVE25mnHwWJaj7SBxZVNKqZBX5HxH47yBrab6HhLjcmi1BGpVJo+drX
+zLSF2BouGdUNTwtoVKyvbXvmnZoIMTbhWvqPU8HIyE/GB3J53Q5V1zaaW90CAwEA
+ATANBgkqhkiG9w0BAQsFAAOBgQCiWMxWN4+CamDzXDW90neja5RHg/pDlBI9pI+Q
+NRyUycpJwN5Flv+/C+OCdmxH/dd15SeGiXCrjJQcSXFBeMD/1JK427SR1acE6z+W
+qY+rYzJ+b68Dybx/4cj2VeFWpdMhZtttgvNrGfwGMbHs9gWG7n6wPKjg6h0OuThf
+2TfVIw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem.certspec
new file mode 100644
index 0000000000..7b86ef7861
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1024-root_rsa_1024
+subject:ee_rsa_1016-int_rsa_1024-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1016
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem
new file mode 100644
index 0000000000..dadab2f898
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB4DCCAUqgAwIBAgIUFclsTfsYyWNzZajFqzlMZX7fOHswDQYJKoZIhvcNAQEL
+BQAwJTEjMCEGA1UEAwwaaW50X3JzYV8xMDE2LXJvb3RfcnNhXzEwMjQwIhgPMjAx
+OTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowMTEvMC0GA1UEAwwmZWVfcnNh
+XzEwMjQtaW50X3JzYV8xMDE2LXJvb3RfcnNhXzEwMjQwgZ8wDQYJKoZIhvcNAQEB
+BQADgY0AMIGJAoGBANOpdEAQHrqMXflQPm+TXrUv/rPr6dDcXKzib5c8qUy8DZwx
+1mwMATvOnILQ1IAyjfBftrzXmQpTEt2uYVKtbuYcjBvdhmPGi9NiJKmIKueOifVW
+39vm9R2mESy/wnyKSTNrQa/bdTIbUrJKc0TRNI5kY1GlUcdXHM2guP419hp1AgMB
+AAEwDQYJKoZIhvcNAQELBQADgYAAy04O696YrNhCjvlDn3FEPZ1Ce4dmSvJQzw3U
+A6nAq3PLq16WwopAus+b3fEXke5k/u+pYfM4+5X7XRDNZW9i+5nUQVmGgiEzG3TU
++WYwGa5V0z6xMXl39i0tIaY4irYwzEpA+DvbJOz/gExQ9dpP/LW5Y8rE0kdMvBbZ
+Gz6gjw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem.certspec
new file mode 100644
index 0000000000..326d665dcc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1016-root_rsa_1024
+subject:ee_rsa_1024-int_rsa_1016-root_rsa_1024
+issuerKey:rsa1016
+subjectKey:rsa1024
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem
new file mode 100644
index 0000000000..f540304e90
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICRjCCAa+gAwIBAgIUU7Z8PfJj0EkU6p/oJRcYHJt+uckwDQYJKoZIhvcNAQEL
+BQAwJTEjMCEGA1UEAwwaaW50X3JzYV8xMDI0LXJvb3RfcnNhXzEwMTYwIhgPMjAx
+OTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowEjEQMA4GA1UEAwwHcnNhMTAy
+NDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogG
+NhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqn
+RYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHu
+p3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQ
+Lzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p
+47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo1
+7Y0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQAr4tqhlbfFvYm2bAmba+gdsk6+EwQ7
+I4qed6vUQfzFcNMSOFBfFLo5q6CG+UsZ06/Hl3YXDHOhVhZC5QANZRO9tEV59eGS
+Hbo3lfSQF8zXJ99vc0q3vNJejGgU/74FjTXX54Pe7KhdUSSjUPEapvpkc2/Y8SFF
+CcL9Lsz8hh7dQw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem.certspec
new file mode 100644
index 0000000000..c44a089ed6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1024-root_rsa_1016
+subject:ee_rsa_1024-int_rsa_1024-root_rsa_1016
+issuerKey:rsa1024
+subject:rsa1024
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem
new file mode 100644
index 0000000000..d9c8f4ea0a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB4TCCAUqgAwIBAgIUcbFravMReDSDzA6Uxk8BzwUwF2MwDQYJKoZIhvcNAQEL
+BQAwJTEjMCEGA1UEAwwaaW50X3JzYV8xMDI0LXJvb3RfcnNhXzEwMjQwIhgPMjAx
+OTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowMTEvMC0GA1UEAwwmZWVfcnNh
+XzEwMjQtaW50X3JzYV8xMDI0LXJvb3RfcnNhXzEwMjQwgZ8wDQYJKoZIhvcNAQEB
+BQADgY0AMIGJAoGBANOpdEAQHrqMXflQPm+TXrUv/rPr6dDcXKzib5c8qUy8DZwx
+1mwMATvOnILQ1IAyjfBftrzXmQpTEt2uYVKtbuYcjBvdhmPGi9NiJKmIKueOifVW
+39vm9R2mESy/wnyKSTNrQa/bdTIbUrJKc0TRNI5kY1GlUcdXHM2guP419hp1AgMB
+AAEwDQYJKoZIhvcNAQELBQADgYEABBfO6S+wGG0HKRb07V+bQpV8eiARnq5ZzoDV
+EpfClXnyorjhuU4sYuzvTUMUcKF5lSmiCF9yl4zSoV8fiDJY2qScZ2C+xMSXnf/7
+plub8Pu0oaSb0KSWSSvon4DDbjSpNfQ/QZZkjvtj2wwK9XVK/7aiZ2e7NuGwOLrd
+tTs3wec=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem.certspec
new file mode 100644
index 0000000000..a6ee408ec9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1024-root_rsa_1024
+subject:ee_rsa_1024-int_rsa_1024-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1024
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem
new file mode 100644
index 0000000000..20b43091bd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBXDCCAQOgAwIBAgIUOZ6qI6KmpVBIQuNSmsJSrHJ2W1gwCgYIKoZIzj0EAwIw
+KjEoMCYGA1UEAwwfaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9yc2FfMjA0ODAiGA8y
+MDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjA7MTkwNwYDVQQDDDBlZV9z
+ZWNwMjI0cjFfMjI0LWludF9zZWNwMjU2cjFfMjU2LXJvb3RfcnNhXzIwNDgwTTAQ
+BgcqhkjOPQIBBgUrgQQAIQM5AARmjXLMpv1qGzVXtTZhBNhECOy2N/COjIa7/4LM
+6I8AZtevY8Mpi6N3NIoSArA7N/1rH/QVqjEeMAoGCCqGSM49BAMCA0cAMEQCIFx1
+UZ8TEVDNXYreIKO8BjCR/7JzdV8xZOz9y0KACnDmAiBvUw1d8jQrTL7jxcshvTvj
+a/7XJT41KYCZC4kHc1sn8A==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem.certspec
new file mode 100644
index 0000000000..87d2f67339
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_rsa_2048
+subject:ee_secp224r1_224-int_secp256r1_256-root_rsa_2048
+issuerKey:secp256r1
+subjectKey:secp224r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem
new file mode 100644
index 0000000000..6e00e043be
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBZzCCAQ2gAwIBAgIUQd9nJAnSoGdsru1U5XzdnZSmQngwCgYIKoZIzj0EAwIw
+LzEtMCsGA1UEAwwkaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjU2cjFfMjU2
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEAxPjA8BgNVBAMM
+NWVlX3NlY3AyMjRyMV8yMjQtaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjU2
+cjFfMjU2ME0wEAYHKoZIzj0CAQYFK4EEACEDOQAEZo1yzKb9ahs1V7U2YQTYRAjs
+tjfwjoyGu/+CzOiPAGbXr2PDKYujdzSKEgKwOzf9ax/0FaoxHjAKBggqhkjOPQQD
+AgNIADBFAiBcdVGfExFQzV2K3iCjvAYwkf+yc3VfMWTs/ctCgApw5gIhAIgKiG6Y
+Wn7JOkWTie+T+AVcLVZU1CT171W8r/AtOYkq
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..1aadce0765
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp256r1_256
+subject:ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp224r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem
new file mode 100644
index 0000000000..0de0ebca77
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbzCCARagAwIBAgIUF4GiHggPJErLtUU4O82JTDsn9/UwCgYIKoZIzj0EAwIw
+LzEtMCsGA1UEAwwkaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjU2cjFfMjU2
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEAxPjA8BgNVBAMM
+NWVlX3NlY3AyNTZrMV8yNTYtaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjU2
+cjFfMjU2MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAENe58conY/veoav5dpm2Lwuu2
+qFQ/0v6tCJ9FznrND6ZDgqlQDEHa13D/1LURv0tJLrEjiADDLE92xzo/MpTnxTAK
+BggqhkjOPQQDAgNHADBEAiBcdVGfExFQzV2K3iCjvAYwkf+yc3VfMWTs/ctCgApw
+5gIgUp27SDHsVOPJ3xOj87plJRqBLZ/mkOWbhZjp8SUTsVs=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..ba999e8f14
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp256r1_256
+subject:ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp256k1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem
new file mode 100644
index 0000000000..083641b7c4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBqDCCARKgAwIBAgIUJd4SuF585qlYm74gqnZsBTfIYhQwDQYJKoZIhvcNAQEL
+BQAwKjEoMCYGA1UEAwwfaW50X3JzYV8xMDE2LXJvb3Rfc2VjcDI1NnIxXzI1NjAi
+GA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjA7MTkwNwYDVQQDDDBl
+ZV9zZWNwMjU2cjFfMjU2LWludF9yc2FfMTAxNi1yb290X3NlY3AyNTZyMV8yNTYw
+WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARPv7u7YeD4+bGmClmshwTi7AULQj48
+9y6SPyxPeUtFXCpp0jNFbDbEEZ0HBuAO7cjRk5DXmRt7LQejBOqgSqbAMA0GCSqG
+SIb3DQEBCwUAA4GAAH+y3CZOoy2Z3zegsj4EmKuwAYBRm7WjV7J7gM2Bak6JaycQ
+NhA2hAd3z3vLEVNlgw0b/WM6lVBNH6fFXikKEzNBytofZtxFqRH66J0qc/CAbmw5
+fG8rkHL4vrDQ1vr3Lyh/qbGXZJxPd2lbFk029o8BkBZADra8QcPuD2EHmDk=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..1e2e0a3759
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1016-root_secp256r1_256
+subject:ee_secp256r1_256-int_rsa_1016-root_secp256r1_256
+issuerKey:rsa1016
+subjectKey:secp256r1
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem
new file mode 100644
index 0000000000..1bf347fc08
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBazCCARmgAwIBAgIUF+3Ha/HblkQ9RVAnd5VUa5BPQ2YwCgYIKoZIzj0EAwIw
+LzEtMCsGA1UEAwwkaW50X3NlY3AyMjRyMV8yMjQtcm9vdF9zZWNwMjU2cjFfMjU2
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEAxPjA8BgNVBAMM
+NWVlX3NlY3AyNTZyMV8yNTYtaW50X3NlY3AyMjRyMV8yMjQtcm9vdF9zZWNwMjU2
+cjFfMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET7+7u2Hg+PmxpgpZrIcE
+4uwFC0I+PPcukj8sT3lLRVwqadIzRWw2xBGdBwbgDu3I0ZOQ15kbey0HowTqoEqm
+wDAKBggqhkjOPQQDAgNAADA9Ah0Amjxv8EbbcPJV9S/WmFIc1y28BSBjT5W2S7JS
+VAIcL1LLWdbRbY2jphtXTfe1/t7vRvZlRgQPxxlSrg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..bd7bc770c7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp224r1_224-root_secp256r1_256
+subject:ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256
+issuerKey:secp224r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem
new file mode 100644
index 0000000000..5f4a80948f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBcjCCARmgAwIBAgIUUfZawfrAes7uJnC3lbyou+3HboEwCgYIKoZIzj0EAwIw
+LzEtMCsGA1UEAwwkaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjI0cjFfMjI0
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEAxPjA8BgNVBAMM
+NWVlX3NlY3AyNTZyMV8yNTYtaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjI0
+cjFfMjI0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET7+7u2Hg+PmxpgpZrIcE
+4uwFC0I+PPcukj8sT3lLRVwqadIzRWw2xBGdBwbgDu3I0ZOQ15kbey0HowTqoEqm
+wDAKBggqhkjOPQQDAgNHADBEAiBcdVGfExFQzV2K3iCjvAYwkf+yc3VfMWTs/ctC
+gApw5gIgbc9p70u5ogMPqCSTG4IpDnjR+Cguf8a+8Erbgfur7Dg=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem.certspec
new file mode 100644
index 0000000000..fe7b7f7482
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp224r1_224
+subject:ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224
+issuerKey:secp256r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem
new file mode 100644
index 0000000000..b13b1fb264
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBcjCCARmgAwIBAgIUdduB4Waw8NoEQbwRm8oYqzYqyF8wCgYIKoZIzj0EAwIw
+LzEtMCsGA1UEAwwkaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjU2azFfMjU2
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEAxPjA8BgNVBAMM
+NWVlX3NlY3AyNTZyMV8yNTYtaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9zZWNwMjU2
+azFfMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET7+7u2Hg+PmxpgpZrIcE
+4uwFC0I+PPcukj8sT3lLRVwqadIzRWw2xBGdBwbgDu3I0ZOQ15kbey0HowTqoEqm
+wDAKBggqhkjOPQQDAgNHADBEAiBcdVGfExFQzV2K3iCjvAYwkf+yc3VfMWTs/ctC
+gApw5gIgKk16br1bktVUX0hUgSKha7mCXqcLm2OqbEfhnnvTGQI=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem.certspec
new file mode 100644
index 0000000000..aefffd9810
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp256k1_256
+subject:ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256
+issuerKey:secp256r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem
new file mode 100644
index 0000000000..a3aa1bccba
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBhTCCASygAwIBAgIUP57NpzW0TiQ7Dv+Eu/Jsec7uG1EwCgYIKoZIzj0EAwIw
+KjEoMCYGA1UEAwwfaW50X3NlY3AyNTZyMV8yNTYtcm9vdF9yc2FfMjA0ODAiGA8y
+MDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjA7MTkwNwYDVQQDDDBlZV9z
+ZWNwMzg0cjFfMzg0LWludF9zZWNwMjU2cjFfMjU2LXJvb3RfcnNhXzIwNDgwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtg
+jiUt5LcTLajOmOgxU05qnAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalx
+A74oiM/wAvBa9xof3cyDdKpuqc4wCgYIKoZIzj0EAwIDRwAwRAIgXHVRnxMRUM1d
+it4go7wGMJH/snN1XzFk7P3LQoAKcOYCIFJ475L6BSzMQmSQ4of5j0VvzGWLlc4p
+P7WljL/PnuoG
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem.certspec
new file mode 100644
index 0000000000..615818d08b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_rsa_2048
+subject:ee_secp384r1_384-int_secp256r1_256-root_rsa_2048
+issuerKey:secp256r1
+subjectKey:secp384r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem
new file mode 100644
index 0000000000..fa2b1196d8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB1zCCAVygAwIBAgIUZSV+XPsVuShLaoK1fI9SUQtuqpUwCgYIKoZIzj0EAwIw
+LzEtMCsGA1UEAwwkaW50X3NlY3AzODRyMV8zODQtcm9vdF9zZWNwMjU2cjFfMjU2
+MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEAxPjA8BgNVBAMM
+NWVlX3NlY3A1MjFyMV81MjEtaW50X3NlY3AzODRyMV8zODQtcm9vdF9zZWNwMjU2
+cjFfMjU2MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBTNycrMR5QQlrycxmdS7C
+f1l3NPpmxit5L4jFGdbTfw0W6hxIOhgnoBC5Eo46CAcMoz719Xg1t8G6JR9sw1Id
+xCsBBlNFGYG0RdND7tN4KjXWz/D/SE9aiD0gnxuQQrcmcDVosvMm4YuDO92KoHND
+krzRlQHhDWmKefU+EeCiK90qrZAwCgYIKoZIzj0EAwIDaQAwZgIxAO0GJz6haDpU
+tNgaQ3SESJY85j6+gRcD7Nc9cvCiVAZZ1OxFRuhW515lVbeTqfcA8wIxAKIisY3l
++oJ2fwRVdjaU3+DIoB72CX208Piq2hSKjF8eVtEHV2liiAfQoH9ktR+0mw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..b2ae9d0c8f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp384r1_384-root_secp256r1_256
+subject:ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256
+issuerKey:secp384r1
+subjectKey:secp521r1
+signature:ecdsaWithSHA256
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem
new file mode 100644
index 0000000000..c63c086abb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB5jCCAU+gAwIBAgIUN856fz51l3lVM7u/s/R3tvFzxkgwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNcm9vdF9yc2FfMTAyNDAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAlMSMwIQYDVQQDDBppbnRfcnNhXzEwMTYtcm9vdF9y
+c2FfMTAyNDCBnjANBgkqhkiG9w0BAQEFAAOBjAAwgYgCgYAA0puxL7hP3c0ps6UZ
+y2bEO42Pi+VFunk4TOZj7QPfdZkWAOuSB5DSUwzs5UTbmacfBYlqPtIHFlU0qpkF
+fkfEfjvIGtpvoeEuNyaLUEalUmj52tfMtIXYGi4Z1Q1PC2hUrK9te+admggxNuFa
++o9TwcjIT8YHcnndDlXXNppb3QIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1Ud
+DwQEAwIBBjANBgkqhkiG9w0BAQsFAAOBgQAwJyIekNQXnajDVspHOAAsNBkPw0MW
+Q8r7OPH9Q9ZzXkz1oXHdwnEXyW4uIOhBYKRvtaqEEQI3SXvV72d5gKIXbP2cf/mQ
+RLRS0GBsy1ulg2w4dSvkwkwFgeYZ+BxM5+esVECzFKZsXrPUcYraymqurqdZcAxy
+0edmg/yt37w8zA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem.certspec
new file mode 100644
index 0000000000..c6e77116b7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1024
+subject:int_rsa_1016-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1016
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem
new file mode 100644
index 0000000000..a47ac14370
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVagAwIBAgIUe0w3IjAYBfw32mIJMH/pETsJGAcwCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2cjFfMjU2MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMCoxKDAmBgNVBAMMH2ludF9yc2FfMTAxNi1yb290
+X3NlY3AyNTZyMV8yNTYwgZ4wDQYJKoZIhvcNAQEBBQADgYwAMIGIAoGAANKbsS+4
+T93NKbOlGctmxDuNj4vlRbp5OEzmY+0D33WZFgDrkgeQ0lMM7OVE25mnHwWJaj7S
+BxZVNKqZBX5HxH47yBrab6HhLjcmi1BGpVJo+drXzLSF2BouGdUNTwtoVKyvbXvm
+nZoIMTbhWvqPU8HIyE/GB3J53Q5V1zaaW90CAwEAAaMdMBswDAYDVR0TBAUwAwEB
+/zALBgNVHQ8EBAMCAQYwCgYIKoZIzj0EAwIDSAAwRQIgXHVRnxMRUM1dit4go7wG
+MJH/snN1XzFk7P3LQoAKcOYCIQCPyLXUG26XZLO3nMf58EJ2Ej+o+gXDAllaLBW5
+2nS+jA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..27728ca374
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256r1_256
+subject:int_rsa_1016-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:rsa1016
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem
new file mode 100644
index 0000000000..812e259332
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB5jCCAVCgAwIBAgIUeZhovVNDUuRN9h3SwWVSaVKNv14wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNcm9vdF9yc2FfMTAxNjAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAlMSMwIQYDVQQDDBppbnRfcnNhXzEwMjQtcm9vdF9y
+c2FfMTAxNjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA06l0QBAeuoxd+VA+
+b5NetS/+s+vp0NxcrOJvlzypTLwNnDHWbAwBO86cgtDUgDKN8F+2vNeZClMS3a5h
+Uq1u5hyMG92GY8aL02IkqYgq546J9Vbf2+b1HaYRLL/CfIpJM2tBr9t1MhtSskpz
+RNE0jmRjUaVRx1cczaC4/jX2GnUCAwEAAaMdMBswDAYDVR0TBAUwAwEB/zALBgNV
+HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADgYAAximjztC//bRBDGD/zQzHBU9DC2CR
+cfdN0A7LgtHgyue5DFtdj1RRioj3ZLIfEgMLi+gESTmZPH1YkMExF2xCJRBdTu3O
+dweani8aTdLWSF3ikt00jCSGH1kiPjWM9pRXhLEz62VW4yvxFt4eVB1xmM5VPOwZ
+B9EHfdS8AeaNVw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem.certspec
new file mode 100644
index 0000000000..fafb393bf9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1016
+subject:int_rsa_1024-root_rsa_1016
+issuerKey:rsa1016
+subjectKey:rsa1024
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem
new file mode 100644
index 0000000000..45d9ac8ee2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB5zCCAVCgAwIBAgIUYcS07xR3ydcgKjUYhNFcmIw+FKQwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNcm9vdF9yc2FfMTAyNDAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAlMSMwIQYDVQQDDBppbnRfcnNhXzEwMjQtcm9vdF9y
+c2FfMTAyNDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA06l0QBAeuoxd+VA+
+b5NetS/+s+vp0NxcrOJvlzypTLwNnDHWbAwBO86cgtDUgDKN8F+2vNeZClMS3a5h
+Uq1u5hyMG92GY8aL02IkqYgq546J9Vbf2+b1HaYRLL/CfIpJM2tBr9t1MhtSskpz
+RNE0jmRjUaVRx1cczaC4/jX2GnUCAwEAAaMdMBswDAYDVR0TBAUwAwEB/zALBgNV
+HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADgYEAzgSst5WpRJFjBpDYQ+/oQE99sYmc
+JZ6lA2Lf5kFDMXb/bF7jf0/FbEheFNRHs+I5kc95lPbwyzpA2bQ3TK9jzFpoDTjK
+kPYnXKn0xa+H7mUS4V6VQh28GdBcWffLZC8vBkB+DKR6N3M2s7erDZjEA8PodKUd
+dR5G85B8pwswVJM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem.certspec
new file mode 100644
index 0000000000..66891f9793
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1024
+subject:int_rsa_1024-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1024
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem
new file mode 100644
index 0000000000..04d2564718
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBYjCCAQmgAwIBAgIUZf9BUQycH51qhAjba7oMtj+YWtowCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2cjFfMjU2MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMC8xLTArBgNVBAMMJGludF9zZWNwMjI0cjFfMjI0
+LXJvb3Rfc2VjcDI1NnIxXzI1NjBNMBAGByqGSM49AgEGBSuBBAAhAzkABGaNcsym
+/WobNVe1NmEE2EQI7LY38I6Mhrv/gszojwBm169jwymLo3c0ihICsDs3/Wsf9BWq
+MR6jHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoGCCqGSM49BAMCA0cA
+MEQCIFx1UZ8TEVDNXYreIKO8BjCR/7JzdV8xZOz9y0KACnDmAiAgvRpeJMeD41PQ
+ydWsdlfwmrgnjM+Vn+bBX0ty9NcEEA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..89d77d3b89
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_secp256r1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256r1_256
+subject:int_secp224r1_224-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp224r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem
new file mode 100644
index 0000000000..e5e8f573fd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICJjCCAQ6gAwIBAgIUG/u+QDiBCAoaoWJYvqL0uwY1/iIwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNcm9vdF9yc2FfMjA0ODAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAqMSgwJgYDVQQDDB9pbnRfc2VjcDI1NnIxXzI1Ni1y
+b290X3JzYV8yMDQ4MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET7+7u2Hg+Pmx
+pgpZrIcE4uwFC0I+PPcukj8sT3lLRVwqadIzRWw2xBGdBwbgDu3I0ZOQ15kbey0H
+owTqoEqmwKMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcN
+AQELBQADggEBADJyKAFPb7ilK80rDDSGNqf7ls/rpElvmhpVJvivJ3Yv1DRm7EEM
+38mwE14TcmayyOS+0avA4gDqH7T9du5NhNWMfluNediBjzl2CgFgXXjSsE7sr1py
+RAi3nYSnUiih6k9Q4a69RteD6FigfSfO0w0nrmLi+njkSVqJ+0pSvnmvf2RKBK9H
+i0VR2oFjoEM/cyxibv1ETqaT6V0CWZSAu6IMT7gpmvvkF5Ti7+KeXXkbuZuQTqIo
+ow/zOhW7aMK24jGDKBLwunbh4EeLyWWtIy88ZNbdGmJkf7+s+/km6DziS9TkcQxn
+tCUR7ze5cYRZ49TNfAcQC0ogdokgo1Cag+U=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem.certspec
new file mode 100644
index 0000000000..44a65ef5a7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_rsa_2048.pem.certspec
@@ -0,0 +1,5 @@
+issuer:root_rsa_2048
+subject:int_secp256r1_256-root_rsa_2048
+subjectKey:secp256r1
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem
new file mode 100644
index 0000000000..d4d73c25da
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBaDCCARWgAwIBAgIULWja0CElNsf9uHoMLXihbb0y8dMwCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjI0cjFfMjI0MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMC8xLTArBgNVBAMMJGludF9zZWNwMjU2cjFfMjU2
+LXJvb3Rfc2VjcDIyNHIxXzIyNDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE+/
+u7th4Pj5saYKWayHBOLsBQtCPjz3LpI/LE95S0VcKmnSM0VsNsQRnQcG4A7tyNGT
+kNeZG3stB6ME6qBKpsCjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoG
+CCqGSM49BAMCA0EAMD4CHQCaPG/wRttw8lX1L9aYUhzXLbwFIGNPlbZLslJUAh0A
+4ZWHxPcxexTE/kShvISJeBka5jGQ1baqxDM6ng==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem.certspec
new file mode 100644
index 0000000000..66ebc1b93e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp224r1_224.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp224r1_224
+subject:int_secp256r1_256-root_secp224r1_224
+issuerKey:secp224r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem
new file mode 100644
index 0000000000..8a69c3e52a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbzCCARWgAwIBAgIUeQnFuTzLPx3FrBWD3ouZIpqXfFUwCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2azFfMjU2MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMC8xLTArBgNVBAMMJGludF9zZWNwMjU2cjFfMjU2
+LXJvb3Rfc2VjcDI1NmsxXzI1NjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE+/
+u7th4Pj5saYKWayHBOLsBQtCPjz3LpI/LE95S0VcKmnSM0VsNsQRnQcG4A7tyNGT
+kNeZG3stB6ME6qBKpsCjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoG
+CCqGSM49BAMCA0gAMEUCIFuwodUwyOUnIR4KN5ZCSrU7y4iz4/1EWRdHm5kWKi8d
+AiEA8M23PrqXz5+5jx1UeQEuaN+Q4ln3gT+XclPmR9P82tA=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem.certspec
new file mode 100644
index 0000000000..c7e190ab0b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256k1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256k1_256
+subject:int_secp256r1_256-root_secp256k1_256
+issuerKey:secp256k1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem
new file mode 100644
index 0000000000..a060a6fe1c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbjCCARWgAwIBAgIUS3I7amSSwFamYMLcx8D/W1zjoCowCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2cjFfMjU2MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMC8xLTArBgNVBAMMJGludF9zZWNwMjU2cjFfMjU2
+LXJvb3Rfc2VjcDI1NnIxXzI1NjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE+/
+u7th4Pj5saYKWayHBOLsBQtCPjz3LpI/LE95S0VcKmnSM0VsNsQRnQcG4A7tyNGT
+kNeZG3stB6ME6qBKpsCjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoG
+CCqGSM49BAMCA0cAMEQCIFx1UZ8TEVDNXYreIKO8BjCR/7JzdV8xZOz9y0KACnDm
+AiBnPZ/DApW1c90gQo+deVHt8TrmT4zNesvqmTzEpU4Tkg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..6854d21876
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp256r1_256-root_secp256r1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256r1_256
+subject:int_secp256r1_256-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem
new file mode 100644
index 0000000000..8e43a8e0d6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBizCCATKgAwIBAgIUIwvMnx4+Cfvo4LHUHSyx8+OIpLIwCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2cjFfMjU2MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMC8xLTArBgNVBAMMJGludF9zZWNwMzg0cjFfMzg0
+LXJvb3Rfc2VjcDI1NnIxXzI1NjB2MBAGByqGSM49AgEGBSuBBAAiA2IABKFockM2
+K1x7GInzeRVGFaHHP7SN7oY+AikV22COJS3ktxMtqM6Y6DFTTmqcDAsJyNY5regy
+BuW6gTRzoR+jMOBdqMluQ4P+J4c9qXEDviiIz/AC8Fr3Gh/dzIN0qm6pzqMdMBsw
+DAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwCgYIKoZIzj0EAwIDRwAwRAIgXHVR
+nxMRUM1dit4go7wGMJH/snN1XzFk7P3LQoAKcOYCIEefciT9jaPnIvEiHC2ktArf
+XGf7h0FGlxLkN9YhKmZ1
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..de8e851981
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_secp256r1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256r1_256
+subject:int_secp384r1_384-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp384r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/moz.build b/security/manager/ssl/tests/unit/test_keysize/moz.build
new file mode 100644
index 0000000000..80b3152dac
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/moz.build
@@ -0,0 +1,41 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem',
+# 'ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem',
+# 'ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem',
+# 'ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem',
+# 'ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem',
+# 'ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem',
+# 'ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem',
+# 'ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem',
+# 'ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem',
+# 'ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem',
+# 'ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem',
+# 'ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem',
+# 'ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem',
+# 'int_rsa_1016-root_rsa_1024.pem',
+# 'int_rsa_1016-root_secp256r1_256.pem',
+# 'int_rsa_1024-root_rsa_1016.pem',
+# 'int_rsa_1024-root_rsa_1024.pem',
+# 'int_secp224r1_224-root_secp256r1_256.pem',
+# 'int_secp256r1_256-root_rsa_2048.pem',
+# 'int_secp256r1_256-root_secp224r1_224.pem',
+# 'int_secp256r1_256-root_secp256k1_256.pem',
+# 'int_secp256r1_256-root_secp256r1_256.pem',
+# 'int_secp384r1_384-root_secp256r1_256.pem',
+# 'root_rsa_1016.pem',
+# 'root_rsa_1024.pem',
+# 'root_rsa_2048.pem',
+# 'root_secp224r1_224.pem',
+# 'root_secp256k1_256.pem',
+# 'root_secp256r1_256.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem
new file mode 100644
index 0000000000..42064fb0b4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2DCCAUKgAwIBAgIUbY90vJ4/+tgc8hsO+VMaaSWW1kgwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNcm9vdF9yc2FfMTAxNjAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAYMRYwFAYDVQQDDA1yb290X3JzYV8xMDE2MIGeMA0G
+CSqGSIb3DQEBAQUAA4GMADCBiAKBgADSm7EvuE/dzSmzpRnLZsQ7jY+L5UW6eThM
+5mPtA991mRYA65IHkNJTDOzlRNuZpx8FiWo+0gcWVTSqmQV+R8R+O8ga2m+h4S43
+JotQRqVSaPna18y0hdgaLhnVDU8LaFSsr2175p2aCDE24Vr6j1PByMhPxgdyed0O
+Vdc2mlvdAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqG
+SIb3DQEBCwUAA4GAAIV+rBvvS1MrBSnjxfYBmX/Kp4bWEXtrStskD9xfyKkmWVxO
+9NxHmA7L3NTqxkYgzfANE1OdFSd+mKK/Bx/xDCUgfRIQZCPZId4GLGl+gRq25R6X
+V3IV53b8UHiGJP0ASJbXUqTQF/7fZvuvBLBDgg9nXObe3ZwHUgWfc3I974I=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem.certspec
new file mode 100644
index 0000000000..b0b5ba8e5e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1016
+subject:root_rsa_1016
+issuerKey:rsa1016
+subjectKey:rsa1016
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem
new file mode 100644
index 0000000000..7262a5cfb0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIB2jCCAUOgAwIBAgIUEWZ1ZXY8sKYaumyn9kCsWK1AEZMwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNcm9vdF9yc2FfMTAyNDAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAYMRYwFAYDVQQDDA1yb290X3JzYV8xMDI0MIGfMA0G
+CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTqXRAEB66jF35UD5vk161L/6z6+nQ3Fys
+4m+XPKlMvA2cMdZsDAE7zpyC0NSAMo3wX7a815kKUxLdrmFSrW7mHIwb3YZjxovT
+YiSpiCrnjon1Vt/b5vUdphEsv8J8ikkza0Gv23UyG1KySnNE0TSOZGNRpVHHVxzN
+oLj+NfYadQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq
+hkiG9w0BAQsFAAOBgQBL16xkIAjU8a2lPy+Ax4N6OjeTrT+pBbj90/ynUiRuEYkI
+GR0Qpv2vcKoFBGIywsJm61z6VRojuaoZGww4D1xlKaHp4R2DmPQYPG/EsUcPdyNK
+MszBmNHK+gRG0HoR4XJUIFMIfnF2R9m9vpVK1XPikLA10UlETdnp9tGuYIXcQw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem.certspec
new file mode 100644
index 0000000000..09cd420f70
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1024
+subject:root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1024
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem b/security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem
new file mode 100644
index 0000000000..82c625161f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAcegAwIBAgIUFbP+AdxwM3PsfxQino2TeiJLnW4wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNcm9vdF9yc2FfMjA0ODAiGA8yMDE5MTEyODAwMDAwMFoY
+DzIwMjIwMjA1MDAwMDAwWjAYMRYwFAYDVQQDDA1yb290X3JzYV8yMDQ4MIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq
+5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SSc
+An7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39
+ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk
+zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3u
+JtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQAB
+ox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOC
+AQEAt1vYMFhLBuETUi2evjncIdzhu8cAqvqnN9WdB/SWL/PsS3cwGb6zu03fxkvZ
+/z07qSs/ojSUR1I70S23NDcmX5Q2vToo8H3Zks2MS+AI0BPVnxo+RllO+zOqMYOQ
+LPGJ+o9M6TGbL/jzYRhCWdF64jOnzgRFN1LU56fWYJLXMwIBHMQ/qMokiDkfc4pG
+O3TOeJHAlfm49pJ1VhTZv3otCel+EkTa7mv0TTaJ+su0/zxgNFtUYWGcCuBJ95hi
+t6Zh6GpOq0ML6NtZK7vMB7BvCIwihU3Fq2ApAiIq/I4tk9ROTvwKCNW6THQ+3+YH
+eSUgFEYNH1T1dcCjsdthf9sKJw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem.certspec
new file mode 100644
index 0000000000..cebc2f8e6a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.pem.certspec
@@ -0,0 +1,4 @@
+issuer:root_rsa_2048
+subject:root_rsa_2048
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem b/security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem
new file mode 100644
index 0000000000..a3945cf4c9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem
@@ -0,0 +1,9 @@
+-----BEGIN CERTIFICATE-----
+MIIBSTCB96ADAgECAhRLUpJGgls27Ph+GqVq1TzwkXgSPTAKBggqhkjOPQQDAjAd
+MRswGQYDVQQDDBJyb290X3NlY3AyMjRyMV8yMjQwIhgPMjAxOTExMjgwMDAwMDBa
+GA8yMDIyMDIwNTAwMDAwMFowHTEbMBkGA1UEAwwScm9vdF9zZWNwMjI0cjFfMjI0
+ME0wEAYHKoZIzj0CAQYFK4EEACEDOQAEZo1yzKb9ahs1V7U2YQTYRAjstjfwjoyG
+u/+CzOiPAGbXr2PDKYujdzSKEgKwOzf9ax/0FaoxHqMdMBswDAYDVR0TBAUwAwEB
+/zALBgNVHQ8EBAMCAQYwCgYIKoZIzj0EAwIDQQAwPgIdAJo8b/BG23DyVfUv1phS
+HNctvAUgY0+VtkuyUlQCHQDZkK9gapfJf6mFLQZOVyj5QrFII+O1AhmojjBO
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem.certspec
new file mode 100644
index 0000000000..31370f6f73
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp224r1_224
+subject:root_secp224r1_224
+issuerKey:secp224r1
+subjectKey:secp224r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem b/security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem
new file mode 100644
index 0000000000..0067d0297c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBWjCCAQCgAwIBAgIUExiTXNGw/1/DWr3T+8iOfYQqhoYwCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2azFfMjU2MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMB0xGzAZBgNVBAMMEnJvb3Rfc2VjcDI1NmsxXzI1
+NjBWMBAGByqGSM49AgEGBSuBBAAKA0IABDXufHKJ2P73qGr+XaZti8LrtqhUP9L+
+rQifRc56zQ+mQ4KpUAxB2tdw/9S1Eb9LSS6xI4gAwyxPdsc6PzKU58WjHTAbMAwG
+A1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoGCCqGSM49BAMCA0gAMEUCIFuwodUw
+yOUnIR4KN5ZCSrU7y4iz4/1EWRdHm5kWKi8dAiEA5z1yM2Xj8Xz+mTzaSV3Ic3Xi
+wEJTXssXlwfECKILxX8=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem.certspec
new file mode 100644
index 0000000000..c78aa61bba
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256k1_256
+subject:root_secp256k1_256
+issuerKey:secp256k1
+subjectKey:secp256k1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem b/security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem
new file mode 100644
index 0000000000..c8e8fd20a4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBXDCCAQOgAwIBAgIUH8NJbu+U7yxLh2580Q2Bh0WUOX0wCgYIKoZIzj0EAwIw
+HTEbMBkGA1UEAwwScm9vdF9zZWNwMjU2cjFfMjU2MCIYDzIwMTkxMTI4MDAwMDAw
+WhgPMjAyMjAyMDUwMDAwMDBaMB0xGzAZBgNVBAMMEnJvb3Rfc2VjcDI1NnIxXzI1
+NjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE+/u7th4Pj5saYKWayHBOLsBQtC
+Pjz3LpI/LE95S0VcKmnSM0VsNsQRnQcG4A7tyNGTkNeZG3stB6ME6qBKpsCjHTAb
+MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMAoGCCqGSM49BAMCA0cAMEQCIFx1
+UZ8TEVDNXYreIKO8BjCR/7JzdV8xZOz9y0KACnDmAiA1QYiu/g1qUtMLwoIqOMYb
+Qi7ckS6Dd1TkOR0kW1dGAw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem.certspec b/security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem.certspec
new file mode 100644
index 0000000000..4447fc4b47
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/root_secp256r1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256r1_256
+subject:root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev.js b/security/manager/ssl/tests/unit/test_keysize_ev.js
new file mode 100644
index 0000000000..60ebf99f82
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev.js
@@ -0,0 +1,171 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Checks that RSA certs with key sizes below 2048 bits when verifying for EV
+// are rejected.
+
+do_get_profile(); // Must be called before getting nsIX509CertDB
+const certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const SERVER_PORT = 8888;
+
+function getOCSPResponder(expectedCertNames) {
+ let expectedPaths = expectedCertNames.slice();
+ return startOCSPResponder(
+ SERVER_PORT,
+ "www.example.com",
+ "test_keysize_ev/",
+ expectedCertNames,
+ expectedPaths
+ );
+}
+
+function loadCert(certName, trustString) {
+ let certFilename = "test_keysize_ev/" + certName + ".pem";
+ addCertFromFile(certDB, certFilename, trustString);
+ return constructCertFromFile(certFilename);
+}
+
+/**
+ * Asynchronously runs a single EV key size test.
+ *
+ * @param {Array} expectedNamesForOCSP
+ * An array of nicknames of the certs to be responded to.
+ * @param {String} rootCertFileName
+ * The file name of the root cert. Can begin with ".." to reference
+ * certs in folders other than "test_keysize_ev/".
+ * @param {Array} intCertFileNames
+ * An array of file names of any intermediate certificates.
+ * @param {String} endEntityCertFileName
+ * The file name of the end entity cert.
+ * @param {Boolean} expectedResult
+ * Whether the chain is expected to validate as EV.
+ */
+async function keySizeTestForEV(
+ expectedNamesForOCSP,
+ rootCertFileName,
+ intCertFileNames,
+ endEntityCertFileName,
+ expectedResult
+) {
+ clearOCSPCache();
+ let ocspResponder = getOCSPResponder(expectedNamesForOCSP);
+
+ loadCert(rootCertFileName, "CTu,CTu,CTu");
+ for (let intCertFileName of intCertFileNames) {
+ loadCert(intCertFileName, ",,");
+ }
+ await checkEVStatus(
+ certDB,
+ constructCertFromFile(`test_keysize_ev/${endEntityCertFileName}.pem`),
+ certificateUsageSSLServer,
+ expectedResult
+ );
+
+ await stopOCSPResponder(ocspResponder);
+}
+
+/**
+ * For debug builds which have the test EV roots compiled in, checks RSA chains
+ * which contain certs with key sizes adequate for EV are validated as such,
+ * while chains that contain any cert with an inadequate key size fail EV and
+ * validate as DV.
+ * For opt builds which don't have the test EV roots compiled in, checks that
+ * none of the chains validate as EV.
+ *
+ * Note: This function assumes that the key size requirements for EV are greater
+ * than the requirements for DV.
+ *
+ * @param {Number} inadequateKeySize
+ * The inadequate key size of the generated certs.
+ * @param {Number} adequateKeySize
+ * The adequate key size of the generated certs.
+ */
+async function checkRSAChains(inadequateKeySize, adequateKeySize) {
+ // Reuse the existing test RSA EV root
+ let rootOKCertFileName = "../test_ev_certs/evroot";
+ let rootOKName = "evroot";
+ let rootNotOKName = "ev_root_rsa_" + inadequateKeySize;
+ let intOKName = "ev_int_rsa_" + adequateKeySize;
+ let intNotOKName = "ev_int_rsa_" + inadequateKeySize;
+ let eeOKName = "ev_ee_rsa_" + adequateKeySize;
+ let eeNotOKName = "ev_ee_rsa_" + inadequateKeySize;
+
+ // Chain with certs that have adequate sizes for EV and DV
+ // In opt builds, this chain is only validated for DV. Hence, an OCSP fetch
+ // will for example not be done for the "ev_int_rsa_2048-evroot" intermediate
+ // in such a build.
+ let intFullName = intOKName + "-" + rootOKName;
+ let eeFullName = eeOKName + "-" + intOKName + "-" + rootOKName;
+ let expectedNamesForOCSP = gEVExpected
+ ? [intFullName, eeFullName]
+ : [eeFullName];
+ await keySizeTestForEV(
+ expectedNamesForOCSP,
+ rootOKCertFileName,
+ [intFullName],
+ eeFullName,
+ gEVExpected
+ );
+
+ // Chain with a root cert that has an inadequate size for EV, but
+ // adequate size for DV
+ intFullName = intOKName + "-" + rootNotOKName;
+ eeFullName = eeOKName + "-" + intOKName + "-" + rootNotOKName;
+ expectedNamesForOCSP = [eeFullName];
+ await keySizeTestForEV(
+ expectedNamesForOCSP,
+ rootNotOKName,
+ [intFullName],
+ eeFullName,
+ false
+ );
+
+ // Chain with an intermediate cert that has an inadequate size for EV, but
+ // adequate size for DV
+ intFullName = intNotOKName + "-" + rootOKName;
+ eeFullName = eeOKName + "-" + intNotOKName + "-" + rootOKName;
+ expectedNamesForOCSP = [eeFullName];
+ await keySizeTestForEV(
+ expectedNamesForOCSP,
+ rootOKCertFileName,
+ [intFullName],
+ eeFullName,
+ false
+ );
+
+ // Chain with an end entity cert that has an inadequate size for EV, but
+ // adequate size for DV
+ intFullName = intOKName + "-" + rootOKName;
+ eeFullName = eeNotOKName + "-" + intOKName + "-" + rootOKName;
+ expectedNamesForOCSP = gEVExpected ? [intFullName, eeFullName] : [eeFullName];
+ await keySizeTestForEV(
+ expectedNamesForOCSP,
+ rootOKCertFileName,
+ [intFullName],
+ eeFullName,
+ false
+ );
+}
+
+add_task(async function() {
+ Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+
+ let smallKeyEVRoot = constructCertFromFile(
+ "test_keysize_ev/ev_root_rsa_2040.pem"
+ );
+ equal(
+ smallKeyEVRoot.sha256Fingerprint,
+ "40:AB:5D:A5:89:15:A9:4B:82:87:B8:A6:9A:84:B1:DB:" +
+ "7A:9D:DB:B8:4E:E1:23:E3:C6:64:E7:50:DC:35:8C:68",
+ "test sanity check: the small-key EV root must have the same " +
+ "fingerprint as the corresponding entry in ExtendedValidation.cpp"
+ );
+
+ await checkRSAChains(2040, 2048);
+});
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem
new file mode 100644
index 0000000000..3df0d3dada
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAk+gAwIBAgIUY8VXvB5H7AGqWbz0hoVm0b7dAnIwDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWZXZfaW50X3JzYV8yMDQ4LWV2cm9vdDAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAwMS4wLAYDVQQDDCVldl9lZV9yc2Ff
+MjA0MC1ldl9pbnRfcnNhXzIwNDgtZXZyb290MIIBITANBgkqhkiG9w0BAQEFAAOC
+AQ4AMIIBCQKCAQAAusBlL9+8AFWIL/uurO7Ij6LQg8KX3V1AZk3T2Q9S+aoCvYpQ
++6FuD9mRh470dfmzUNn44+sqvXF84yewl4hTHxPfjj5OO51ha7ikHlMG7tJHIWMW
+EFEYASdqTrZvBzMbXLyLyucBao+bPU8qxFU8Ykz1JjvLNI6IQN5mEocJYKeSGRsT
+j7IX92XOx7/46U8Ws5QZv3UExZp+T3m9bRc+nHvz2dKk5zzBgLBZCnPVhPt/ybVP
+pURgflP8aFx6Vf1EqB1BQravUepvps6lKWWi6MXYTzygJNb7ubAFuWUc5dny7PQO
+1ASYGp/8AmNuMRsJXGMyoMh9w5JxtVUUgXdLAgMBAAGjgYQwgYEwXgYIKwYBBQUH
+AQEEUjBQME4GCCsGAQUFBzABhkJodHRwOi8vd3d3LmV4YW1wbGUuY29tOjg4ODgv
+ZXZfZWVfcnNhXzIwNDAtZXZfaW50X3JzYV8yMDQ4LWV2cm9vdC8wHwYDVR0gBBgw
+FjAUBhIrBgEEAetJhRqFGoUaAYN0CQEwDQYJKoZIhvcNAQELBQADggEBAJlhONR0
+b4Ov6D8ZLINHf06gcsvLXOHvK3UlbB2COdd6Oe1c5bePo6D+zU44vUH4rwb1z25k
+OQt6d4LW1ISuw1Wf7HZN1PFwBpvQZ72JcvLwouSqCEuMz68tKTzZy9E9fbRdzMLo
+8N5OKxPiTY9R3gziR0AXnadZ0P0yP2mT2Oro/o7+punU4kJsOxzcfu0nMaRTht22
+6XcF6goHUD74zGxwgSeCKdImN4JvA2GYVAniDyOvJCMCjyryxeETZlgsPmXZGAVH
+hiGVhFrH1E6FVxagebdK6eB7UJ9Cau5tLefAEHpCr5Xut4QNj9ck0A1J/mj3dwl/
+wV5OZmqoCAEk2h4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem.certspec
new file mode 100644
index 0000000000..e64c651bc7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ev_int_rsa_2048-evroot
+subject:ev_ee_rsa_2040-ev_int_rsa_2048-evroot
+subjectKey:rsa2040
+extension:authorityInformationAccess:http://www.example.com:8888/ev_ee_rsa_2040-ev_int_rsa_2048-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem
new file mode 100644
index 0000000000..fdf7002fab
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAlCgAwIBAgIUf03mHDtKgdbREowlJgpuQsbxVXQwDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWZXZfaW50X3JzYV8yMDQwLWV2cm9vdDAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAwMS4wLAYDVQQDDCVldl9lZV9yc2Ff
+MjA0OC1ldl9pbnRfcnNhXzIwNDAtZXZyb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo
+4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD
+SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX
+kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx
+owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/
+Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GEMIGBMF4GCCsGAQUF
+BwEBBFIwUDBOBggrBgEFBQcwAYZCaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4
+L2V2X2VlX3JzYV8yMDQ4LWV2X2ludF9yc2FfMjA0MC1ldnJvb3QvMB8GA1UdIAQY
+MBYwFAYSKwYBBAHrSYUahRqFGgGDdAkBMA0GCSqGSIb3DQEBCwUAA4IBAABud5gZ
+fQFcRyWt2bw3rxs+c0/FsV7XSHnGxYX9DXUiGFL2glNmu4DVQEHYk3CDbAPafTBD
+7143VCXQu97AuLl7OAApEbsteRtSX2uMjatA2xDFACUPWqTAVNjhs3/oumldK4GS
+Fq7hrPrFim/OQ/pwRvIjE12fUhz4G5uClhCVArtDwCo+0SCWsPveBVVCPmgKM6TI
+3t0b20Cya2H7XCB8HC61wftGw0dIjxw5MmA7VAQmoGAxD1DOh0UAXMgMaet93riI
+96g8UETpPo89cdPSZ4tMM8JSvnyrc9bns8hvX1KxQyctjXKPgtfszd9pEYv/Aked
+S82jJp6aPYt7DFA=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem.certspec
new file mode 100644
index 0000000000..0b7bfd4269
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ev_int_rsa_2040-evroot
+subject:ev_ee_rsa_2048-ev_int_rsa_2040-evroot
+issuerKey:rsa2040
+extension:authorityInformationAccess:http://www.example.com:8888/ev_ee_rsa_2048-ev_int_rsa_2040-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem
new file mode 100644
index 0000000000..c8c52a1d00
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDhjCCAm6gAwIBAgIUG40lfqXpVIsXGwWLss7U0Guh2lcwDQYJKoZIhvcNAQEL
+BQAwKzEpMCcGA1UEAwwgZXZfaW50X3JzYV8yMDQ4LWV2X3Jvb3RfcnNhXzIwNDAw
+IhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAwMDAwMFowOjE4MDYGA1UEAwwv
+ZXZfZWVfcnNhXzIwNDgtZXZfaW50X3JzYV8yMDQ4LWV2X3Jvb3RfcnNhXzIwNDAw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ
+PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH
+9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw
+4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86
+exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0
+ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N
+AgMBAAGjgY4wgYswaAYIKwYBBQUHAQEEXDBaMFgGCCsGAQUFBzABhkxodHRwOi8v
+d3d3LmV4YW1wbGUuY29tOjg4ODgvZXZfZWVfcnNhXzIwNDgtZXZfaW50X3JzYV8y
+MDQ4LWV2X3Jvb3RfcnNhXzIwNDAvMB8GA1UdIAQYMBYwFAYSKwYBBAHrSYUahRqF
+GgGDdAkBMA0GCSqGSIb3DQEBCwUAA4IBAQAQWGc/1fHcFeIavOXfSSFDn+b9+saU
+xIuK4SujRstki8AUxoFyLDa5Hm5aLIU4MTpOxDLBGjFVyv9hGi4aEKnZuzdUDrSe
+6ezdNNfTuwgwU1VeivTDzm2lkj9C/F7MnOgste6MMcEj+Cun34ORh7F2adE8KRxU
+RgGQYhlurmUEe14ewxyEZ9PZ210v7xSqa+XPg5GNjZHjPoNUYEliASnflJ1eSRqh
+Xzm0HabdSJEoUBClQLp2m/BstlkzQgEe7m+S83XPVuRPe79Ey4BnzVYqkpFdeAGO
+ffs15R7qbIbOG2DeU5mQFiEFnRTv+80MVHGz0gspMBiyYI1h4wItnYx4
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec
new file mode 100644
index 0000000000..a36acf887d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ev_int_rsa_2048-ev_root_rsa_2040
+subject:ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040
+extension:authorityInformationAccess:http://www.example.com:8888/ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem
new file mode 100644
index 0000000000..389e359e93
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIUSxuZebHYtwEQBNR2ItC9xEgw7CkwDQYJKoZIhvcNAQEL
+BQAwITEfMB0GA1UEAwwWZXZfaW50X3JzYV8yMDQ4LWV2cm9vdDAiGA8yMDE5MTEy
+ODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAwMS4wLAYDVQQDDCVldl9lZV9yc2Ff
+MjA0OC1ldl9pbnRfcnNhXzIwNDgtZXZyb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo
+4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD
+SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX
+kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx
+owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/
+Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GEMIGBMF4GCCsGAQUF
+BwEBBFIwUDBOBggrBgEFBQcwAYZCaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4
+L2V2X2VlX3JzYV8yMDQ4LWV2X2ludF9yc2FfMjA0OC1ldnJvb3QvMB8GA1UdIAQY
+MBYwFAYSKwYBBAHrSYUahRqFGgGDdAkBMA0GCSqGSIb3DQEBCwUAA4IBAQAMgEzp
+6Q8vRl95TLzhiLXC/9otBQpV50iq8Waqf8B2JfOPIP0M/Emz9VKQxavw6iti3YPz
+utsdGyIje2W0GhlJVtMKHfvfKYXCwtVEe/XNF/Ojz7znk8KEuUPBzjKTjBuICw4F
+PnmKyzoYsD1HwkgrkoII9hr+WBqY5vWLNJA1IXyA2cc3u9oAM1+QqPWEwX+bKjai
+4QbngYKTL2ybQbM7TcCkvY/dSOoyyF4K4Kz0eAFXrH1cLHlPj2wo5kgexXRWAgO0
+klZrS2b3C3cJVEWi/bYCg7E3I1noNsgnZ1zQgXqjf7frAUwZURoSSPVO3azM88UJ
+FCh9RLnIOgbs7zos
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem.certspec
new file mode 100644
index 0000000000..0b34be6db8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ev_int_rsa_2048-evroot
+subject:ev_ee_rsa_2048-ev_int_rsa_2048-evroot
+extension:authorityInformationAccess:http://www.example.com:8888/ev_ee_rsa_2048-ev_int_rsa_2048-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem
new file mode 100644
index 0000000000..ad92bf75eb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIUM/wVnL73PZefwiXVUHFwY4BBJGwwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCExHzAdBgNVBAMMFmV2X2ludF9yc2FfMjA0MC1ldnJvb3QwggEh
+MA0GCSqGSIb3DQEBAQUAA4IBDgAwggEJAoIBAAC6wGUv37wAVYgv+66s7siPotCD
+wpfdXUBmTdPZD1L5qgK9ilD7oW4P2ZGHjvR1+bNQ2fjj6yq9cXzjJ7CXiFMfE9+O
+Pk47nWFruKQeUwbu0kchYxYQURgBJ2pOtm8HMxtcvIvK5wFqj5s9TyrEVTxiTPUm
+O8s0johA3mYShwlgp5IZGxOPshf3Zc7Hv/jpTxazlBm/dQTFmn5Peb1tFz6ce/PZ
+0qTnPMGAsFkKc9WE+3/JtU+lRGB+U/xoXHpV/USoHUFCtq9R6m+mzqUpZaLoxdhP
+PKAk1vu5sAW5ZRzl2fLs9A7UBJgan/wCY24xGwlcYzKgyH3DknG1VRSBd0sCAwEA
+AaOBkDCBjTAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjBPBggrBgEFBQcBAQRD
+MEEwPwYIKwYBBQUHMAGGM2h0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4OC9ldl9p
+bnRfcnNhXzIwNDAtZXZyb290LzAfBgNVHSAEGDAWMBQGEisGAQQB60mFGoUahRoB
+g3QJATANBgkqhkiG9w0BAQsFAAOCAQEAWnghIlyjOs/GO6obJdu5wmjDghKBP/pQ
+5L6yF9jTdfVaTudO/liGBeP1Lnor+0QbTNWlOE2MhPobve6WPbpcGbDoCk+QorFH
+sgnLbMMlSUXPgvGt4yNJefsmy02G472T9kSn6KkoLM3Zzanv0crqOUYXsQVODs17
+NEvuWuxcvsA+tuuFxwVMUuBK4AWW64zOmio/xTNqxijV8/Cd7w/20ORDhExtzxsu
+l3V04WcZp8GNk6PVbmSCyZ+Oqv8aoF3sI3JCHK0cKHdM5+sQzJyTbKZ1J7kfLE+4
+aB/LZyf3EHOS5/Qv5wCSBxIBHSmQL19l48IU6FBZ+65RtIooczs3bg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem.certspec
new file mode 100644
index 0000000000..80be711742
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040-evroot.pem.certspec
@@ -0,0 +1,8 @@
+issuer:evroot
+subject:ev_int_rsa_2040-evroot
+issuerKey:ev
+subjectKey:rsa2040
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/ev_int_rsa_2040-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key
new file mode 100644
index 0000000000..63b267865f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEtwIBADANBgkqhkiG9w0BAQEFAASCBKEwggSdAgEAAoIBAAC6wGUv37wAVYgv
++66s7siPotCDwpfdXUBmTdPZD1L5qgK9ilD7oW4P2ZGHjvR1+bNQ2fjj6yq9cXzj
+J7CXiFMfE9+OPk47nWFruKQeUwbu0kchYxYQURgBJ2pOtm8HMxtcvIvK5wFqj5s9
+TyrEVTxiTPUmO8s0johA3mYShwlgp5IZGxOPshf3Zc7Hv/jpTxazlBm/dQTFmn5P
+eb1tFz6ce/PZ0qTnPMGAsFkKc9WE+3/JtU+lRGB+U/xoXHpV/USoHUFCtq9R6m+m
+zqUpZaLoxdhPPKAk1vu5sAW5ZRzl2fLs9A7UBJgan/wCY24xGwlcYzKgyH3DknG1
+VRSBd0sCAwEAAQKB/2A9smffl1VcvthrjfNVA0ryjx638+eCnSObzCc6fHppoQvo
+8h8bbEsCxrrjcxwxWLW7/0YF9Xq3t7Kgy6LsAFottbHqbgrO6lvHRdzS0OnWuA1+
+sOorwIEnvONfpQxCzEEYcbpZHiO6ajhISjPv8TR/kH7ppakqIxG7C0NVEAIPeOO7
+AAmdtNEYKSgJZQX8uoTzyhI4/R66XuofORu7zFQksWgGP8F+HKbhkSzLpE+dApIw
+ih/tuAYSUps59Z0KP4GAtbogETIZf5OlgV3tk43459k8mxV2ZYjzObtZEAr9pJSn
+5FLX3UyaGc4uw6M6GLIPC02t4XK+4Z8m8Ny+QQKBgA7Dhpy5LUBsrd96MZqylEi8
+UFoFkTcHhzNh/FuYakmftl7rgVp+N2h9GfEoCHKJ2buIGOe8ylAsSQCtmuzhF5vh
+L/PkZ9YG/IIOqPB6yev/4iNuOBaEEgKIIj5C2+aN/ZcqhaZEflFpXyNNp5EcZ8mr
+lTHzPfO5lDLU7ojJpO+7AoGADKY5NFSehf6sjg9WBDA/0YSf6Ir0t/fhITKDu8ei
+wqUJ+Sc8Qoxo3j25PmFF8bQAvW1KJiYU6QQ602LU66SmuZU5nIk0o5mRIZnoQdjo
+2/8EifaeZjeWcwspgFMLMctwaVohYl6irczAnZMFFvqHIhGpHiLdif2et9qFdLci
+NbECgYANfTp14X9l+KZYpIXECVwQpPZpeeK3O8qc+O8hJT4frKxtR5H1g5LOhlb4
+jxJAzJDCllPjEAxtejjtRLFjsznl87bjiRISbGmzzv8uUZJCbZZJtv/KGrt10rou
+1tmiaqODxZc9ViFv8u25DM+Id0Kg8YOskslM8YdldkXHdy2a1wKBgAP1UBlMEX8k
+vqKFsgkFgDL0KYX/Vazr6IsW35o3UntOYdyRpo28mmRRNFKM5fJIvaKJPJbLe+ee
+5zmWx8Ild/bC95BAbzRyrbOyEbfpRJTzLFxvzAl4g5/kckwxsGMYokiVZ7T8oDN6
+yxuEEieqpfbHSACiMGkp8CzgOLrZQ99BAoGACAp9v6jCWEgUxxZkxW62LOTK8Wr+
+iNRJkVnWdHdKOj7N3xJWwC/JFSXFJ2kkItCrqU5cQe4S3HG7Zvhnn6F+CW8oCAhR
+ugRusxiFwUFOiYWt5ZnZB68XRT0cyuosDQZEP4Nnpr4VSxJeOQ7g2Q90bwiAHdP1
+Nn9Z+6LlpnwF83U=
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key.keyspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key.keyspec
new file mode 100644
index 0000000000..f488e73a94
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2040.key.keyspec
@@ -0,0 +1 @@
+rsa2040
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem
new file mode 100644
index 0000000000..416ad67fa1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlugAwIBAgIUFdhUsJK/XuSsxS2GLAc6Q/dnYpowDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQZXZfcm9vdF9yc2FfMjA0MDAiGA8yMDE5MTEyODAwMDAw
+MFoYDzIwMjIwMjA1MDAwMDAwWjArMSkwJwYDVQQDDCBldl9pbnRfcnNhXzIwNDgt
+ZXZfcm9vdF9yc2FfMjA0MDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG
+8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0V
+gg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g3
+04hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l
+0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz
+/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaOBmjCBlzAMBgNVHRMEBTADAQH/MAsGA1Ud
+DwQEAwIBBjBZBggrBgEFBQcBAQRNMEswSQYIKwYBBQUHMAGGPWh0dHA6Ly93d3cu
+ZXhhbXBsZS5jb206ODg4OC9ldl9pbnRfcnNhXzIwNDgtZXZfcm9vdF9yc2FfMjA0
+MC8wHwYDVR0gBBgwFjAUBhIrBgEEAetJhRqFGoUaAYN0CQEwDQYJKoZIhvcNAQEL
+BQADggEAAJNwhipzaCUDddzc+heX+MgcJ9DcupEFuuSmEgdne3PgxhfhXrIqIT4X
+4DpxmtUS4oB3FhhXLxlndiBVbC2GRzPN5Qhss3ULbyfVa3Bf45LyMpDNfjs2MozJ
++xLPjhQDJcMwq10RsDUhwQz4BCcCb5AoXHaMXZJe0J2ZhYBZMMZt/22an7mR9HRc
+AOvnLXtzUEZSrIsIXC0LvnAcEMy6g3WPfeJCcJZ5cWw0DVabtGBTlPy7/dOFYtmI
+2BHxsDj1pTKjitmgbVfA67u24Ct5glHzkVsQEPD4vB2J5Ptu0UidQ7fRyuZwpZuW
+Q72nXFdSEs0UVGp8fSsQ8ORv2m21nQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec
new file mode 100644
index 0000000000..5bc5674b2c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-ev_root_rsa_2040.pem.certspec
@@ -0,0 +1,7 @@
+issuer:ev_root_rsa_2040
+subject:ev_int_rsa_2048-ev_root_rsa_2040
+issuerKey:evRSA2040
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/ev_int_rsa_2048-ev_root_rsa_2040/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem
new file mode 100644
index 0000000000..1ab32ed4e2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVTCCAj2gAwIBAgIUO+CV3E1g9n7p1BWTj+qvQOZRjLUwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAy
+MDUwMDAwMDBaMCExHzAdBgNVBAMMFmV2X2ludF9yc2FfMjA0OC1ldnJvb3QwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT
+2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzV
+JJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8N
+jf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCA
+BiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVh
+He4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMB
+AAGjgZAwgY0wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwTwYIKwYBBQUHAQEE
+QzBBMD8GCCsGAQUFBzABhjNodHRwOi8vd3d3LmV4YW1wbGUuY29tOjg4ODgvZXZf
+aW50X3JzYV8yMDQ4LWV2cm9vdC8wHwYDVR0gBBgwFjAUBhIrBgEEAetJhRqFGoUa
+AYN0CQEwDQYJKoZIhvcNAQELBQADggEBAJ7HDMpaOr+hmteHqb414a3xwUpEO+X9
+Jh5zHvsAHG8DXXKu36Q25w+7joXsusD5GrDbZdXVEcNF3tpx+SqahzzDK0jzOIuO
+26ZRVXxkJmhyI6WdMbIrm6fC0WjGeJl0ByHo3MYJrnOgNOrGxJy3Ecpk5ttlge41
+m8TzQIi7hXYmOLqmqJ0yQoyukfX7uSr/PZsKI3tjZ8Na/q/7HesgGVH4vdbzhlmu
+lZP1MPPZmn9hWHkoLxjG4KI8hGAgaUlkwk2WKn5SC9IUuz52bm3Xz0rkK+C3RSQo
+MJagAa0x+heCFeWJpuhiIOWKRCwol72o0Nm+mFKi4/qAxNWBI1kTD1A=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem.certspec
new file mode 100644
index 0000000000..a0cb6250dc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048-evroot.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:ev_int_rsa_2048-evroot
+issuerKey:ev
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/ev_int_rsa_2048-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key.keyspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_int_rsa_2048.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key
new file mode 100644
index 0000000000..bcd996ab23
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEuQIBADANBgkqhkiG9w0BAQEFAASCBKMwggSfAgEAAoIBAADKcCDcIV9XkU00
+P65KAVERaXr5l6Xs6RhmSZ/CPxuIoRjL0wsQ2Rx7mg1O6JcvyuVsr1fyX8EnWipN
+vLmCQowy71h78jh0EDMKD/sWuAKb14OWnvZ19t44wY9nGTy2wHL4sj0LM3QRJiel
+e5AFV3HZ5iYD9TeI1/Y6+nJPXRCAlt8x+J8msetffENXmA4Aj81V2CfdJiOVyi9S
+ageJfMQMWTs4cW68DKpZZxnG8prJtzp6lEdIo6o+CenrTUYeoAJ+VAkmYUcoudJD
+l1z5oFQb79JedrUflREQsOdkT8fjhEF5G20iJzhMuABOIzQjcrHPXMPnPjG3u++h
+YOaGLrsCAwEAAQKCAQAAstt0vOkjYqv3KVWmOK6HILowM7t/lxyvORiNdULqocGr
+tdIFseIRH0eRwIkRouFB6M/XBUcC0jEAtWQsBuGjGxGK/R+aLzlsztQlxQHZFDXK
+hlZ2bO0rk7u4Zp/Om6zXJ9Hayz2vq8MpPjU4nu+OoLWOGusaIOamH5/NRT91Z/4x
+0SO2FqJv703x1sn3SQER0Cju/R2XIEWxokInPdemfr8RHbJ0GlqTx7IonMSiNvWp
+mm7HqCBv2uHB0EvbsZgNSimMWhfa5BhkdKX3g12IK87ySu9O1vFJ+U2WyffXjmR/
+x3ipAX/yCNO0oXaLGCFiECzasDL6u6s41SAKMkZJAoGADzhE0NTU1qIazXam/DcL
+hVDh1+xaYjQXLnkPACmuZR9tXFkzCrGYArnXogfeeh+3eON3T9vcQRdQYz2NGz/g
+dQBv/P0dEOdjx6kifS1fDC2t4cnmWcNQoVnTa7mG8SY21PmUKyiLwP4h2oeZR3Fz
+FEJJyi44nmxcJap4yMrX1N8CgYANTQvt0ZYvB6Hq1rI6TtZ66vEnDwUqbSm6B0lF
+xjYaXE+PB7+FngZ67T9ObjI+8qqKas00CwvcfP5P0ynjyX+HDH93NXksaqnQ9+dU
+KijtbwGw5VorjZwkplxtoxTJVIT1x8OVSoG7AWsH7RfumwYDlpW8oFmnn43CQj0y
+jVJlpQKBgAnymi/wW+ipbWFLoxsIk1QgqGxrxCuZpmkuoNpXY/AeWWlZt93Oc++c
+Lk9uW0BxCIdQDUS6DDzTEyy6J0dfOcLfdVLi0SOiSXpPlwZAKHaaSKNiRlf3K/U5
+89DeI0/szTvooKqQxr9umwvtQwcKJNBh/z7RdRo+8v9/a5C529X7AoGAAaZZ4XDK
+wSCgO+HPj53xyqNTsDWTvXR25YU72HTChziGAcbDQc6dHShKXu8aOmadMrgWpers
+2LeET+BwZLm8oMKzGNVAJ3s/fxUQ04a7NuA7BHceXSKeiIk+E7dTv7lFGLtjjiQE
+vW5qmTwWaNk/wLgv8IqvNDR9P+g5cQjIfKUCgYAEAlfA1KIcC5hDKXxlZS22YwT7
+Jjdz1yi2q/oG03rAymLGKAI+CeN9wKkB5M4SJBgOJYKjqktqGnuY4r1wB3rsFKyK
+tmp1XHHg/BAkcfm7wbRqlaoLZF8sOOdkUCiWGeo/XormEDe//PgknyKqTnbioBkJ
+8/6ykM6T7fV7EOvnlg==
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key.keyspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key.keyspec
new file mode 100644
index 0000000000..a85e16858b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.key.keyspec
@@ -0,0 +1 @@
+evRSA2040
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem
new file mode 100644
index 0000000000..fe3abd78a8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4zCCAcygAwIBAgIUJ7nCMgtzNcSPG7jAh3CWzlTGHQgwDQYJKoZIhvcNAQEL
+BQAwGzEZMBcGA1UEAwwQZXZfcm9vdF9yc2FfMjA0MDAiGA8yMDE1MDEwMTAwMDAw
+MFoYDzIwMzUwMTAxMDAwMDAwWjAbMRkwFwYDVQQDDBBldl9yb290X3JzYV8yMDQw
+MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQAAynAg3CFfV5FNND+uSgFR
+EWl6+Zel7OkYZkmfwj8biKEYy9MLENkce5oNTuiXL8rlbK9X8l/BJ1oqTby5gkKM
+Mu9Ye/I4dBAzCg/7FrgCm9eDlp72dfbeOMGPZxk8tsBy+LI9CzN0ESYnpXuQBVdx
+2eYmA/U3iNf2OvpyT10QgJbfMfifJrHrX3xDV5gOAI/NVdgn3SYjlcovUmoHiXzE
+DFk7OHFuvAyqWWcZxvKaybc6epRHSKOqPgnp601GHqACflQJJmFHKLnSQ5dc+aBU
+G+/SXna1H5URELDnZE/H44RBeRttIic4TLgATiM0I3Kxz1zD5z4xt7vvoWDmhi67
+AgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB
+CwUAA4IBAAA/4/YVyeRLPr05Uw5j0JOCx5WNUv2HxemfvTZgF4QEg4vDt8ba3VDR
+Xj3Z8hiGYG+s2Wz4k+82wCNRTglm3iutCJ/LbwOAZIa8dFyQUa03EssS0BBvVNhx
+uu6+kYMqGteIX5Q94daqZe+0KM9xKbydNCQJKSMD8IV1YHKvotF91MFQHDdnVAZX
+anpqDnw0j4YGknFHA1i++0GZC0aWxhRn6Epfza+bYCVogC5BviY6xYIg2kZE8kII
+msQ6iUrKQ2OV7HmZ03BdpsGADorycyJ/wRGR3xDDg8RYUur80jU/D0eBq8BX1md8
+Rc+IyDmcFcs7hYRUaJAoxuLPvQ+/vy4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem.certspec
new file mode 100644
index 0000000000..fd1ade8dea
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/ev_root_rsa_2040.pem.certspec
@@ -0,0 +1,7 @@
+issuer:ev_root_rsa_2040
+subject:ev_root_rsa_2040
+issuerKey:evRSA2040
+subjectKey:evRSA2040
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/evroot.key b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.key
new file mode 100644
index 0000000000..1d88a930d5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQC1SYlcnQAQjRGh
++Z+HqePRpdtd+uzxiNpXv2QTaI8s5HIs/xCQOMF0Ask6Kkc9vShq7T/c02PPWikU
+dwG92BjXYVv5NWvV08gzaqqMCXE2igbDzURhuT5RQk4XRLsuqtRqqzjOGWghlh+H
+cUoWY2k/CXYc301roSXqzse+Jw04j3ifbN94rjFE7SjEXnkpOGOnoipImAo2pA5y
+1XnJuSXf+MeTNi/9aJenwXVMXpfJZ8Pq3RquiqLMzjSKAWm4Diii1wwalgxvM18t
+oJubZD9av7pJ6Kqpgelg4n2HSAvdVd2UF/oYUJ+7VUzPgaQ5fouoEoo0vfJ4ZcGJ
+5XNPsikFAgMBAAECggEBAJg9VPlNb0x26yPW+T14UjUwz3Ow0WJUxueBdo1F9VaB
+0dAvsr0qrGq8HDiYYJNcUqDY9BSCAQOUd4MUHYZL/zCANjilwBUlcK6dGPPYyhY+
++0dbDd3zLn4W7HVl5rteAlxBxcZuV6A87eVUIh+DBFNHosTEUcPc5Ha3h84MBXJE
+vp4E7xMRjbuz1eCmzIcCnq/Upp7ZsUdZsV452KmITlb1TS+asBPw0V8xipq2svc9
+HsPJ/idK6JQxoQZAvniZsAEcXlCToYNHCGid4QBjTaveYPvWqu+joz3zSh829gwE
+MDa3SNHJ7pjEAxoK/sYO/aCpkL5ST1YU6sT9s0pS+VECgYEA6twssz5f8co3a72V
+vWoXd9LPT6xHVF6S0RpiCbnV5N7UeDRYHBabPIhHQqCeoYdQXBylVBTY0ltJdjLV
+7CqqBSM0MPrUmJJ3en1o4Dj1YaO4lp5gsKJj3vv9pIqbD/OdlbyIsVJnyK3pe1EH
+lI5B5DMknYf32xCdXXRYTYa8wdcCgYEAxZrldqIWRwJI2USlW56b+TKZ2jQexW5V
+jrqCGrzhv1e3nPQR0pBMd0+duh8VGF9gewV0oIIF1uwotmo21jQjLqry/qN1Yauv
+nWRLaNs4yZZMuMluwKxh66ZNBbRGVC9COXb1rN5OzJVTbS31eJVPk/DP2cWPt4ui
+p23VrChNyIMCgYEAwdLvOQYzHFKspkgR+f5CW+somDIvs9tRAyzo1+n8MiQL6SAZ
+zySA/NXjKYNxJxGLKlmhv+BsiD46REfz8DHNmuvQuNNo/Hl0DSzOjq2zJN9/CR6v
+4VZDYdVJILAbBHEjDl5H2T+O0zljxRe8T8ePbYsfnrqFvM7bcDMCZQjbYoUCgYEA
+hSG421aU376ASjFfnvybZSdcVJCs8qNFbWXm5hC/n2R/xnUB1PV3LyMqxwzN75/C
+pt+kFcfEG2r8evnQfDygP37ZPAnwuZ8sMEQ0Mi8QcXCbvBuqTJFXX6apWeB9SZaV
+bZXiK1eTi25HyNUf/t/Jv4iM4NGj5CtlqJvtS5HT5fUCgYEA3El7BrkgyL4LAHe3
+mOl37vdEqQ7Cxdfmy7IkSPrHLagaMxgODYoC6DFGDH/H/TphL3uZMLYbeZ+OkI5j
+LpugQJtqpwsDo7p4dCYmO1vVhD34R27bXRT2qGE+uvW5zVykL1+9KALgjk5J5XCf
+UVFRDKpassHG6z7+kpXRbowlyRY=
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/evroot.key.keyspec b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.key.keyspec
new file mode 100644
index 0000000000..1a3d76a550
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.key.keyspec
@@ -0,0 +1 @@
+ev
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem
new file mode 100644
index 0000000000..13c3031905
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUIZSHsVgzcvhPgdfrgdMGlpSfMegwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTUwMTAxMDAwMDAwWhgPMjAzNTAx
+MDEwMDAwMDBaMBExDzANBgNVBAMMBmV2cm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALVJiVydABCNEaH5n4ep49Gl21367PGI2le/ZBNojyzkciz/
+EJA4wXQCyToqRz29KGrtP9zTY89aKRR3Ab3YGNdhW/k1a9XTyDNqqowJcTaKBsPN
+RGG5PlFCThdEuy6q1GqrOM4ZaCGWH4dxShZjaT8JdhzfTWuhJerOx74nDTiPeJ9s
+33iuMUTtKMReeSk4Y6eiKkiYCjakDnLVecm5Jd/4x5M2L/1ol6fBdUxel8lnw+rd
+Gq6KoszONIoBabgOKKLXDBqWDG8zXy2gm5tkP1q/uknoqqmB6WDifYdIC91V3ZQX
++hhQn7tVTM+BpDl+i6gSijS98nhlwYnlc0+yKQUCAwEAAaMdMBswDAYDVR0TBAUw
+AwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBABTOHA9XbfLv/C7+
+5KycYXToOIBRSjQ0j2nsiqFda4Jx+aKsvdpdrrbLHvhrpfsA3ZgB2+eKHunVc4fo
+UHNqZllAs2nx+AEinq4GX8iya5BpiyTIxXWu8v06siGgz1GxlJw1cJ/ZnFEQ9IBf
+cCAr5fCoZ4RC+2OVhiSTnYPCKM+zCyw3YpISjNOg1VVkp46Htp+831Eh12YfwvdY
+Fgh1fc5ohYC5GCLRuXKc9PGTsr3gp7Y0liYbK7v0RBjd+GivNQ3dS3W+lB3Ow0LH
+z/fc3qvrhsd58jHpb1QZQzd9bQjuIIM6Gij7TNdNNarEVZfSJjPYLfXosNdYh5fH
+HmbOwao=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem.certspec b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem.certspec
new file mode 100644
index 0000000000..3121f3486e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/evroot.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:evroot
+subjectKey:ev
+issuerKey:ev
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_keysize_ev/moz.build b/security/manager/ssl/tests/unit/test_keysize_ev/moz.build
new file mode 100644
index 0000000000..96ea30e1f9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize_ev/moz.build
@@ -0,0 +1,31 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ev_ee_rsa_2040-ev_int_rsa_2048-evroot.pem',
+# 'ev_ee_rsa_2048-ev_int_rsa_2040-evroot.pem',
+# 'ev_ee_rsa_2048-ev_int_rsa_2048-ev_root_rsa_2040.pem',
+# 'ev_ee_rsa_2048-ev_int_rsa_2048-evroot.pem',
+# 'ev_int_rsa_2040-evroot.pem',
+# 'ev_int_rsa_2048-ev_root_rsa_2040.pem',
+# 'ev_int_rsa_2048-evroot.pem',
+# 'ev_root_rsa_2040.pem',
+# 'evroot.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'ev_int_rsa_2040.key',
+# 'ev_int_rsa_2048.key',
+# 'ev_root_rsa_2040.key',
+# 'evroot.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_local_cert.js b/security/manager/ssl/tests/unit/test_local_cert.js
new file mode 100644
index 0000000000..2b0e804380
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_local_cert.js
@@ -0,0 +1,87 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const certService = Cc["@mozilla.org/security/local-cert-service;1"].getService(
+ Ci.nsILocalCertService
+);
+
+const gNickname = "local-cert-test";
+
+function run_test() {
+ // Need profile dir to store the key / cert
+ do_get_profile();
+ // Ensure PSM is initialized
+ Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+ run_next_test();
+}
+
+function getOrCreateCert(nickname) {
+ return new Promise((resolve, reject) => {
+ certService.getOrCreateCert(nickname, {
+ handleCert(c, rv) {
+ if (rv) {
+ reject(rv);
+ return;
+ }
+ resolve(c);
+ },
+ });
+ });
+}
+
+function removeCert(nickname) {
+ return new Promise((resolve, reject) => {
+ certService.removeCert(nickname, {
+ handleResult(rv) {
+ if (rv) {
+ reject(rv);
+ return;
+ }
+ resolve();
+ },
+ });
+ });
+}
+
+add_task(async function() {
+ // No master password, so no prompt required here
+ ok(!certService.loginPromptRequired);
+
+ let certA = await getOrCreateCert(gNickname);
+ // The local cert service implementation takes the given nickname and uses it
+ // as the common name for the certificate it creates. nsIX509Cert.displayName
+ // uses the common name if it is present, so these should match. Should either
+ // implementation change to do something else, this won't necessarily work.
+ equal(certA.displayName, gNickname);
+
+ // Getting again should give the same cert
+ let certB = await getOrCreateCert(gNickname);
+ equal(certB.displayName, gNickname);
+
+ // Should be matching instances
+ ok(certA.equals(certB));
+
+ // Check an expected attribute
+ equal(certA.certType, Ci.nsIX509Cert.USER_CERT);
+
+ // New nickname should give a different cert
+ let diffNameCert = await getOrCreateCert("cool-stuff");
+ ok(!diffNameCert.equals(certA));
+
+ // Remove the cert, and get a new one again
+ await removeCert(gNickname);
+ let newCert = await getOrCreateCert(gNickname);
+ ok(!newCert.equals(certA));
+
+ // Drop all cert references and GC
+ let serial = newCert.serialNumber;
+ certA = certB = diffNameCert = newCert = null;
+ Cu.forceGC();
+ Cu.forceCC();
+
+ // Should still get the same cert back
+ let certAfterGC = await getOrCreateCert(gNickname);
+ equal(certAfterGC.serialNumber, serial);
+});
diff --git a/security/manager/ssl/tests/unit/test_logoutAndTeardown.js b/security/manager/ssl/tests/unit/test_logoutAndTeardown.js
new file mode 100644
index 0000000000..1546bd2cce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_logoutAndTeardown.js
@@ -0,0 +1,207 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+"use strict";
+
+// This test ensures that in-progress https connections are cancelled when the
+// user logs out of a PKCS#11 token.
+
+// Get a profile directory and ensure PSM initializes NSS.
+do_get_profile();
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+function getCert() {
+ return new Promise((resolve, reject) => {
+ let certService = Cc[
+ "@mozilla.org/security/local-cert-service;1"
+ ].getService(Ci.nsILocalCertService);
+ certService.getOrCreateCert("beConservative-test", {
+ handleCert: (c, rv) => {
+ if (rv) {
+ reject(rv);
+ return;
+ }
+ resolve(c);
+ },
+ });
+ });
+}
+
+class InputStreamCallback {
+ constructor(output) {
+ this.output = output;
+ this.stopped = false;
+ }
+
+ onInputStreamReady(stream) {
+ info("input stream ready");
+ if (this.stopped) {
+ info("input stream callback stopped - bailing");
+ return;
+ }
+ let available = 0;
+ try {
+ available = stream.available();
+ } catch (e) {
+ // onInputStreamReady may fire when the stream has been closed.
+ equal(
+ e.result,
+ Cr.NS_BASE_STREAM_CLOSED,
+ "error should be NS_BASE_STREAM_CLOSED"
+ );
+ }
+ if (available > 0) {
+ let request = NetUtil.readInputStreamToString(stream, available, {
+ charset: "utf8",
+ });
+ ok(
+ request.startsWith("GET / HTTP/1.1\r\n"),
+ "Should get a simple GET / HTTP/1.1 request"
+ );
+ let response =
+ "HTTP/1.1 200 OK\r\n" +
+ "Content-Length: 2\r\n" +
+ "Content-Type: text/plain\r\n" +
+ "\r\nOK";
+ let written = this.output.write(response, response.length);
+ equal(
+ written,
+ response.length,
+ "should have been able to write entire response"
+ );
+ }
+ this.output.close();
+ info("done with input stream ready");
+ }
+
+ stop() {
+ this.stopped = true;
+ this.output.close();
+ }
+}
+
+class TLSServerSecurityObserver {
+ constructor(input, output) {
+ this.input = input;
+ this.output = output;
+ this.callbacks = [];
+ this.stopped = false;
+ }
+
+ onHandshakeDone(socket, status) {
+ info("TLS handshake done");
+ info(`TLS version used: ${status.tlsVersionUsed}`);
+
+ if (this.stopped) {
+ info("handshake done callback stopped - bailing");
+ return;
+ }
+
+ let callback = new InputStreamCallback(this.output);
+ this.callbacks.push(callback);
+ this.input.asyncWait(callback, 0, 0, Services.tm.currentThread);
+
+ // We've set up everything needed for a successful request/response,
+ // but calling logoutAndTeardown should cause the request to be cancelled.
+ Cc["@mozilla.org/security/sdr;1"]
+ .getService(Ci.nsISecretDecoderRing)
+ .logoutAndTeardown();
+ }
+
+ stop() {
+ this.stopped = true;
+ this.input.close();
+ this.output.close();
+ this.callbacks.forEach(callback => {
+ callback.stop();
+ });
+ }
+}
+
+class ServerSocketListener {
+ constructor() {
+ this.securityObservers = [];
+ }
+
+ onSocketAccepted(socket, transport) {
+ info("accepted TLS client connection");
+ let connectionInfo = transport.securityInfo.QueryInterface(
+ Ci.nsITLSServerConnectionInfo
+ );
+ let input = transport.openInputStream(0, 0, 0);
+ let output = transport.openOutputStream(0, 0, 0);
+ let securityObserver = new TLSServerSecurityObserver(input, output);
+ this.securityObservers.push(securityObserver);
+ connectionInfo.setSecurityObserver(securityObserver);
+ }
+
+ // For some reason we get input stream callback events after we've stopped
+ // listening, so this ensures we just drop those events.
+ onStopListening() {
+ info("onStopListening");
+ this.securityObservers.forEach(observer => {
+ observer.stop();
+ });
+ }
+}
+
+function getStartedServer(cert) {
+ let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
+ Ci.nsITLSServerSocket
+ );
+ tlsServer.init(-1, true, -1);
+ tlsServer.serverCert = cert;
+ tlsServer.setSessionTickets(false);
+ tlsServer.asyncListen(new ServerSocketListener());
+ return tlsServer;
+}
+
+const hostname = "example.com";
+
+function storeCertOverride(port, cert) {
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ let overrideBits =
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_MISMATCH;
+ certOverrideService.rememberValidityOverride(
+ hostname,
+ port,
+ cert,
+ overrideBits,
+ true
+ );
+}
+
+function startClient(port) {
+ let req = new XMLHttpRequest();
+ req.open("GET", `https://${hostname}:${port}`);
+ return new Promise((resolve, reject) => {
+ req.onload = () => {
+ ok(false, "should not have gotten load event");
+ resolve();
+ };
+ req.onerror = () => {
+ ok(true, "should have gotten an error");
+ resolve();
+ };
+
+ req.send();
+ });
+}
+
+add_task(async function() {
+ Services.prefs.setCharPref("network.dns.localDomains", hostname);
+ let cert = await getCert();
+
+ let server = getStartedServer(cert);
+ storeCertOverride(server.port, cert);
+ await startClient(server.port);
+ server.close();
+});
+
+registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("network.dns.localDomains");
+});
diff --git a/security/manager/ssl/tests/unit/test_missing_intermediate.js b/security/manager/ssl/tests/unit/test_missing_intermediate.js
new file mode 100644
index 0000000000..2a22666369
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_missing_intermediate.js
@@ -0,0 +1,95 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests that if a server does not send a complete certificate chain, we can
+// make use of cached intermediates to build a trust path.
+
+const { TestUtils } = ChromeUtils.import(
+ "resource://testing-common/TestUtils.jsm"
+);
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+registerCleanupFunction(() => {
+ let certDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ certDir.append("bad_certs");
+ Assert.ok(certDir.exists(), "bad_certs should exist");
+ let args = ["-D", "-n", "manually-added-missing-intermediate"];
+ run_certutil_on_directory(certDir.path, args, false);
+});
+
+function run_test() {
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+ // If we don't know about the intermediate, we'll get an unknown issuer error.
+ add_connection_test(
+ "ee-from-missing-intermediate.example.com",
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+
+ // Make BadCertAndPinningServer aware of the intermediate.
+ add_test(() => {
+ let args = [
+ "-A",
+ "-n",
+ "manually-added-missing-intermediate",
+ "-i",
+ "test_missing_intermediate/missing-intermediate.pem",
+ "-a",
+ "-t",
+ ",,",
+ ];
+ let certDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ certDir.append("bad_certs");
+ Assert.ok(certDir.exists(), "bad_certs should exist");
+ run_certutil_on_directory(certDir.path, args);
+ run_next_test();
+ });
+
+ // We have to start observing the topic before there's a chance it gets
+ // emitted.
+ add_test(() => {
+ TestUtils.topicObserved("psm:intermediate-certs-cached").then(
+ subjectAndData => {
+ Assert.equal(subjectAndData.length, 2, "expecting [subject, data]");
+ Assert.equal(subjectAndData[1], "1", `expecting "1" cert imported`);
+ run_next_test();
+ }
+ );
+ run_next_test();
+ });
+ // Connect and cache the intermediate.
+ add_connection_test(
+ "ee-from-missing-intermediate.example.com",
+ PRErrorCodeSuccess
+ );
+
+ // Add a dummy test so that the only way we advance from here is by observing
+ // "psm:intermediate-certs-cached".
+ add_test(() => {});
+
+ // Delete the intermediate on the server again.
+ add_test(() => {
+ clearSessionCache();
+ let certDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ certDir.append("bad_certs");
+ Assert.ok(certDir.exists(), "bad_certs should exist");
+ let args = ["-D", "-n", "manually-added-missing-intermediate"];
+ run_certutil_on_directory(certDir.path, args);
+ run_next_test();
+ });
+
+ // Since we cached the intermediate in gecko, this should succeed.
+ add_connection_test(
+ "ee-from-missing-intermediate.example.com",
+ PRErrorCodeSuccess
+ );
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem b/security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem
new file mode 100644
index 0000000000..985077c6b3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4DCCAcigAwIBAgIUVwdwwrCWfp82pGODTXMV35GajGQwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAfMR0wGwYDVQQDDBRNaXNzaW5nIEludGVybWVkaWF0ZTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+AaMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQAD
+ggEBAFDTK/9saOeJ3Sb0vjJ0ogp/E7WrpjHaMsx7janNrpaIGqn+vaICF30Muazb
+/LywCjvXsJCFI1aPh+s4eavMPZVEdDIsoQIwdBLCU8u/s1wVlA+BHOy/M0u+7rGw
++F2AeKd4vUfZJ4jrxSorxqCuwyE606UXImSSngpJq4dRJul4cDcHOg6uSRl/hPU+
+sjGdo2DIJKiGquBwmMVgEwIF6qSP7iI+qguy2k6XykGnVSN/HASqB+0UfYvvHhkI
+3sPcsU1LM3tW5hnM/81SE0JUHyvNgXEHHXO1FST2U6H6LyrTsem3Ga33yhBoayD/
+L1P2Qgq8tryeqcV4cqolR7MfYx4=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem.certspec b/security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem.certspec
new file mode 100644
index 0000000000..c21e757449
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_missing_intermediate/missing-intermediate.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Missing Intermediate
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_missing_intermediate/moz.build b/security/manager/ssl/tests/unit/test_missing_intermediate/moz.build
new file mode 100644
index 0000000000..d9282b6044
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_missing_intermediate/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# BadCertAndPinningServer takes as an argument a path to a directory and loads
+# every key and certificate in it. We want to test what happens when a
+# server doesn't include an intermediate that is necessary to build a
+# complete trust path. The easiest way to do this right now is to put
+# the intermediate in a different directory, so that BadCertAndPinningServer
+# doesn't know about it and can't send it in the TLS handshake.
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'missing-intermediate.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_name_constraints.js b/security/manager/ssl/tests/unit/test_name_constraints.js
new file mode 100644
index 0000000000..e04eb93b0c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints.js
@@ -0,0 +1,71 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// This test tests two specific items:
+// 1. Are name constraints properly enforced across the entire constructed
+// certificate chain? This makes use of a certificate hierarchy like so:
+// - (trusted) root CA with permitted subtree dNSName example.com
+// - intermediate CA with permitted subtree dNSName example.org
+// a. end-entity with dNSNames example.com and example.org
+// (the first entry is allowed by the root but not by the intermediate,
+// and the second entry is allowed by the intermediate but not by the
+// root)
+// b. end-entity with dNSName example.com (not allowed by the intermediate)
+// c. end-entity with dNSName examle.org (not allowed by the root)
+// d. end-entity with dNSName example.test (not allowed by either)
+// All of these cases should fail to verify with the error that the
+// end-entity is not in the name space permitted by the hierarchy.
+//
+// 2. Are externally-imposed name constraints properly enforced? This makes use
+// of a certificate hierarchy rooted by a certificate with the same DN as an
+// existing hierarchy that has externally-imposed name constraints (DCISS).
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function certFromFile(name) {
+ return constructCertFromFile(`test_name_constraints/${name}.pem`);
+}
+
+function loadCertWithTrust(certName, trustString) {
+ addCertFromFile(certdb, `test_name_constraints/${certName}.pem`, trustString);
+}
+
+function checkCertNotInNameSpace(cert) {
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ SEC_ERROR_CERT_NOT_IN_NAME_SPACE,
+ certificateUsageSSLServer
+ );
+}
+
+function checkCertInNameSpace(cert) {
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+}
+
+add_task(async function() {
+ // Test that name constraints from the entire certificate chain are enforced.
+ loadCertWithTrust("ca-example-com-permitted", "CTu,,");
+ loadCertWithTrust("int-example-org-permitted", ",,");
+ await checkCertNotInNameSpace(certFromFile("ee-example-com-and-org"));
+ await checkCertNotInNameSpace(certFromFile("ee-example-com"));
+ await checkCertNotInNameSpace(certFromFile("ee-example-org"));
+ await checkCertNotInNameSpace(certFromFile("ee-example-test"));
+
+ // Test that externally-imposed name constraints are enforced (DCISS tests).
+ loadCertWithTrust("dciss", "CTu,,");
+ await checkCertInNameSpace(certFromFile("NameConstraints.dcissallowed"));
+ await checkCertNotInNameSpace(certFromFile("NameConstraints.dcissblocked"));
+});
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem
new file mode 100644
index 0000000000..819737c643
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVzCCAj+gAwIBAgIUH/IH7a1iPGpyhpstium2HZvHBr0wDQYJKoZIhvcNAQEL
+BQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBh
+cmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMF
+SUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEExCzAJBgNVBAYTAlVTMQsw
+CQYDVQQIDAJDQTEMMAoGA1UECgwDRm9vMRcwFQYDVQQDDA5mb28uZXhhbXBsZS5m
+cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogG
+NhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqn
+RYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHu
+p3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQ
+Lzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p
+47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo1
+7Y0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEALZy0l4IRtgX2v24zAUssBpdMpfUt
+2Evctbg02kRX3y1cWvoXMq4qrnoMhoaNBpdMf8m1kqbsirPaT5NHsr7V/NgJGPwA
+cHBEb1oV+Smy8KfhqF12VD2gWYHxs9Ooc67+Bf90cZO9yPpOjiNqJeMtat/xVA+e
+dG1kE6V5tMufIi4wARy4tjaMWoD2aw4oYFn1VREkOeyT79JzCTJMBR1bArVhas7D
+mn+EON5e16oYmFPfSRT3oWmtEHa3Er0M5v0icSaEA7YFkPdoXLRNxstggh7IPcD4
+3dmH0xosUax8+E4gp0lVk+ok0nEvRNcw+KTopFSO83tPn/4yLQMGjGB8Rg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem.certspec
new file mode 100644
index 0000000000..1a02a0cc1a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissallowed.pem.certspec
@@ -0,0 +1,2 @@
+issuer:printableString/C=FR/ST=France/L=Paris/O=PM/SGDN/OU=DCSSI/CN=IGC/A/emailAddress=igca@sgdn.pm.gouv.fr
+subject:/C=US/ST=CA/O=Foo/CN=foo.example.fr
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem
new file mode 100644
index 0000000000..ac0349ac7b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWDCCAkCgAwIBAgIUdb90UMieOa6hVljB8cPlSVhDhNUwDQYJKoZIhvcNAQEL
+BQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBh
+cmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMF
+SUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMEIxCzAJBgNVBAYTAlVTMQsw
+CQYDVQQIDAJDQTEMMAoGA1UECgwDRm9vMRgwFgYDVQQDDA9mb28uZXhhbXBsZS5j
+b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
+BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
+p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
+7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
+kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
+aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
+Ne2NAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAKIQo2A4GXqb8uYaOekw5f/SJBg4
+iPiOlhNNGEmczSCty8UbFDrd3L44EpH1Wi5YvuxnqWwHzLxU4Ze2aJIwIZ04godV
+nHCd57qFsuakPdxesdGeFdeM/Ni2h6YhZkyoZzmUKtKbMcoCQOq/WPzXehU9/D2n
+okuePkoXw92YVSFq/IBuKcPpQKIpKq+Oho9PbDXho3ZDYW5PVReif2I9Pp9mIIfN
+rGkM/oSA+3brZGe8fXUIg6zXlTiXX70FdIuWeXcd5TjY141+27jshjXvIy+5ZZep
+uPCx9jYjmOUklbf8hKeKZzFZfXo3Fg1QlXcMOmZbE4rgF7wKysux6jgnEo8=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem.certspec
new file mode 100644
index 0000000000..eabee87e83
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/NameConstraints.dcissblocked.pem.certspec
@@ -0,0 +1,2 @@
+issuer:printableString/C=FR/ST=France/L=Paris/O=PM/SGDN/OU=DCSSI/CN=IGC/A/emailAddress=igca@sgdn.pm.gouv.fr
+subject:/C=US/ST=CA/O=Foo/CN=foo.example.com
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem b/security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem
new file mode 100644
index 0000000000..efb0e9828c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfmgAwIBAgIUW/imFdGk/JA1NKPzOw1+UoGjVIUwDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYY2EtZXhhbXBsZS1jb20tcGVybWl0dGVkMCIYDzIwMTkx
+MTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCMxITAfBgNVBAMMGGNhLWV4YW1w
+bGUtY29tLXBlcm1pdHRlZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG
+8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0V
+gg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g3
+04hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l
+0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz
+/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaM5MDcwDAYDVR0TBAUwAwEB/zALBgNVHQ8E
+BAMCAQYwGgYDVR0eBBMwEaAPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUA
+A4IBAQBCxftaoGEXtPvmno3v44S7ahV7PYR6kNE1VxZDifghRNneBc+um3r/S6Rz
+Qfej9e+u6/pSNyW1laZE3EXCKX9za5LE//qbm4l7UaomqO95BD3oVNV5pVNuC5Hc
+JyLLbFTT4QKi1pHoD/Wjv17f4rBl13MFqXB4Nt9KqvZOn1RGcU+dPgivuuNYXGEq
+0REsQRqloVOdZ9WhF0lEUNDvWhxxZi1arLlv5UCrQZ+srvroZMrjyQJ9Wu3RH6Hm
+HdpBkIkoghiykIILF23zWeb8tgqlakkTtuVU1dnzFhP8NZDMYG7Ms2vpTkbBjnwb
+cYKuzvcsfwnFbmAN9v0/Hzo4zZrh
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem.certspec
new file mode 100644
index 0000000000..1cc3c1d81b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ca-example-com-permitted.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca-example-com-permitted
+subject:ca-example-com-permitted
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:nameConstraints:permitted:example.com
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/dciss.pem b/security/manager/ssl/tests/unit/test_name_constraints/dciss.pem
new file mode 100644
index 0000000000..ae37ac46a1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/dciss.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIUcxz6c/GMYB7/nrD4zXg4j6UrJckwDQYJKoZIhvcNAQEL
+BQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBh
+cmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMF
+SUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMCIYDzIw
+MTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMIGFMQswCQYDVQQGEwJGUjEP
+MA0GA1UECBMGRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczEQMA4GA1UEChMHUE0vU0dE
+TjEOMAwGA1UECxMFRENTU0kxDjAMBgNVBAMTBUlHQy9BMSMwIQYJKoZIhvcNAQkB
+FhRpZ2NhQHNnZG4ucG0uZ291di5mcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswDAYDVR0TBAUwAwEB/zAL
+BgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAKUhmbYZJnpAEOUHAxYTaGZD
+0juaNppvdhTeI2T3bfIVgL3qFqPeom3pLNU+Rnl1hTplxl0C9wxIEl849nc9cWxa
+EK/JCnGHrBCZ4qJn9xUN96AhGICE654c7CgBNLKs8KZ6tAunAGtxCR6c5R7L5G7m
+fA7DWX9lEI0R9L0NJzWyhELsafoMlZGFTiIERiYGhotemxf+QEPWAWn3cfFgN0ma
+aK23cFbzw6SXHbC2y9OCtz8nWgn417Los+H2emQrBqISdvPh54EU2uCb7vxH2/ly
+KZW8QFo5M3/UCuy/X47y7gUTHQQ+BhX9Dz40sp7DP0pDSwt0fksVxXlZoldMppM=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/dciss.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/dciss.pem.certspec
new file mode 100644
index 0000000000..5d53706bc5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/dciss.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=FR/ST=France/L=Paris/O=PM/SGDN/OU=DCSSI/CN=IGC/A/emailAddress=igca@sgdn.pm.gouv.fr
+subject:printableString/C=FR/ST=France/L=Paris/O=PM/SGDN/OU=DCSSI/CN=IGC/A/emailAddress=igca@sgdn.pm.gouv.fr
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem
new file mode 100644
index 0000000000..10064a2c0b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/jCCAeagAwIBAgIUcVd3d0Lp6rsQX88nbU7VYHOOVKswDQYJKoZIhvcNAQEL
+BQAwJDEiMCAGA1UEAwwZaW50LWV4YW1wbGUtb3JnLXBlcm1pdHRlZDAiGA8yMDE5
+MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAhMR8wHQYDVQQDDBZlZS1leGFt
+cGxlLWNvbS1hbmQtb3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+uohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGoby
+a+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWC
+D/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfT
+iEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXT
+Ce+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+
+SSP6clHEMdUDrNoYCjXtjQIDAQABoycwJTAjBgNVHREEHDAaggtleGFtcGxlLmNv
+bYILZXhhbXBsZS5vcmcwDQYJKoZIhvcNAQELBQADggEBAKZjORSM/94cF9uzMJCX
+MZ3JmZKiMRXCW9Y1mCKeimvV5LqFyRVMlE7DAHXuPwzaJVS1y/q99dUcUfMd5q1b
+jEMdLc4qM+7JSpI1qxoeFQPym+3tCH15u6ZXkPzrAdGElVraCEYsCWjEmpMaZS6Q
+Zfbe7mcBqIDiFKQma6tioiwfdg2KLKb/bFML/guxGVIYFla8UaiOFtQ0mLg6qMrp
+1RdjqJhGqqD71IS+ez65W4gaizKgAyBmKqOqRoSjvF7lqrQNAOk7m+pY4Dzlxyg9
+iGYyNVWen1R436FLJEgdFQOefGY5vhtg5dVX+D/MH+vRX8apumQ3Q1sVMgoLb5v3
+yWw=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem.certspec
new file mode 100644
index 0000000000..904ca65955
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com-and-org.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-example-org-permitted
+subject:ee-example-com-and-org
+extension:subjectAlternativeName:example.com,example.org
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem
new file mode 100644
index 0000000000..3b04129003
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6TCCAdGgAwIBAgIULdge2q8uLXhglJkSVlCLPnl/KsswDQYJKoZIhvcNAQEL
+BQAwJDEiMCAGA1UEAwwZaW50LWV4YW1wbGUtb3JnLXBlcm1pdHRlZDAiGA8yMDE5
+MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAZMRcwFQYDVQQDDA5lZS1leGFt
+cGxlLWNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaMaMBgwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20wDQYJKoZI
+hvcNAQELBQADggEBAKazM8Y0fbv4mYND26inNXWTooZz6G4zFxa9TDVEYYl0W5mS
+kC0nE1L38cP7hFpuKwChl87UNHgkpxplSozadgSUg1yqyZ+F1KzpAs1647WHqAgB
+cIPzhR1Y7d1v3/jkCxioYvwudeXwO8eqH3EPOz5h70U8LRY+Dney+qT9OLAvyAl/
+dpF3ZjT3AQRTT90ZL15I+rLR10kI4a2MwqUALaEmZtZAp1ecDoh/71VJF4CEPb3l
+JE7rR/Ff5VHC6fNMJcoc9Ze6laEOH3+BK+l8wpf4ABGiCdkfUsHbO3W/g0h4QKhS
+Me7tz24aSISqinNGxOGcFVBB//V1sPGkH3lFVTc=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem.certspec
new file mode 100644
index 0000000000..46630c4a1a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-com.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-example-org-permitted
+subject:ee-example-com
+extension:subjectAlternativeName:example.com
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem
new file mode 100644
index 0000000000..12545e3748
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6TCCAdGgAwIBAgIUd1DYJK/QJRwY0oGdFXeMsRKcwhAwDQYJKoZIhvcNAQEL
+BQAwJDEiMCAGA1UEAwwZaW50LWV4YW1wbGUtb3JnLXBlcm1pdHRlZDAiGA8yMDE5
+MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAZMRcwFQYDVQQDDA5lZS1leGFt
+cGxlLW9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAAaMaMBgwFgYDVR0RBA8wDYILZXhhbXBsZS5vcmcwDQYJKoZI
+hvcNAQELBQADggEBAJyjoYfBfON+x24YHczjV3ngQfTH0SZ4WzebR/lqsIrKKx08
+siDPKpdrkNGDyQ0A9pxaLMgDjl9e+7Z8aWLklzGYY1TN8/KWaQdqAN9VAL/9QfIz
+qo5jxzEt29nNvO9NTc9sBQ/fPiTydI96JJgsWPg/ikw3y0c+1IPVYT0jSCxHc7qN
+MLXtmDxl7ZoMyPKU9ZIACHSU3+BZee/TTqZT97TIEYkS39bJUyP6UwttTfh4Zm8l
+3Eju6bMPrQGu/M1hy+n6bliY+qxnJ0TDaJcHfd6ox9SFGajjAvU2KMVfiG7NzzBP
+qdmp5Hw6BIskbjzGD9TfOiQiDd/CqwfuoXStH1M=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem.certspec
new file mode 100644
index 0000000000..6a24090e51
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-org.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-example-org-permitted
+subject:ee-example-org
+extension:subjectAlternativeName:example.org
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem
new file mode 100644
index 0000000000..a15e04cf60
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6zCCAdOgAwIBAgIUMV6sDJo62fVf2QrGVmoUYUmkl5AwDQYJKoZIhvcNAQEL
+BQAwJDEiMCAGA1UEAwwZaW50LWV4YW1wbGUtb3JnLXBlcm1pdHRlZDAiGA8yMDE5
+MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9lZS1leGFt
+cGxlLXRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W
+1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtq
+ZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx
+0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthV
+t2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo
+4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx
+1QOs2hgKNe2NAgMBAAGjGzAZMBcGA1UdEQQQMA6CDGV4YW1wbGUudGVzdDANBgkq
+hkiG9w0BAQsFAAOCAQEAZyhNrPkiThyQJBib0iYbXOP7NOEdenQzeyCRZJ5QcPZM
+jO5iXpRRzyEqBDyOtnIG6UihP98qOmF7T8RTZmZ0Ex8Kzw5iHniXYDJPr5pnaTG+
+INyKLMMVr884xHEKKarX2sg52b+u01gAUBD6B9yQZ3MAdqjNyqcbYmOVzreFsYcL
+ddhhC9/B0iE801OPf2/vbAIcLNKVXSkPJ0Xry3KeGqwc3v5a4SNmEGLYBZKdaIAb
+q4a6ViUPFxS/MduXzXxzU8FaXDMZj8eGezExuDUWrZvWLjmfl5RaV+/JJbTRcMKR
+xIVqTKtCbuXvNsetc0/IIQ4lz7eMniQrGQd6QM3HRg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem.certspec
new file mode 100644
index 0000000000..0926ce477a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/ee-example-test.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int-example-org-permitted
+subject:ee-example-test
+extension:subjectAlternativeName:example.test
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem b/security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem
new file mode 100644
index 0000000000..4c491d708c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDEjCCAfqgAwIBAgIUTwrMhhSd1s9Xs6fjDP+FeWX6YigwDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYY2EtZXhhbXBsZS1jb20tcGVybWl0dGVkMCIYDzIwMTkx
+MTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCQxIjAgBgNVBAMMGWludC1leGFt
+cGxlLW9yZy1wZXJtaXR0ZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24a
+hvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7t
+FYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+o
+N9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0d
+JdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4
+s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjOTA3MAwGA1UdEwQFMAMBAf8wCwYDVR0P
+BAQDAgEGMBoGA1UdHgQTMBGgDzANggtleGFtcGxlLm9yZzANBgkqhkiG9w0BAQsF
+AAOCAQEAoHM57NeL7TRmMXZ5eR6hep2vYELQ6x6OHIZQy6v/B9mDY5kdNkO3tcbW
+jEbdL3wInvxxaUWgDfdHlW6isUDMWn9pqAejytzwQveCHHve+yPmaR5Kgnm/LR7U
+ygE8f1CDpm/u/+q8UXzG7B8g3qwo1HRWx07G43ZXbMp/0BiBdA2Hjhj9drohosAY
+g2aDqaRIUeq97PCarNztkzr//zt9eCXg8qQBjVSghB03ikFRBmkCBVBNLqq6iqN+
+wb6Crc5RKKEec8n1HHEBnl16sfF/W9Z3GYFXf/Oesnz9JyaLuns1MiZx+TEf9afn
+sg4XFW8K1vQ+t/1CR+OB1sEhmgkd2A==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem.certspec b/security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem.certspec
new file mode 100644
index 0000000000..87e2cf8a56
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/int-example-org-permitted.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca-example-com-permitted
+subject:int-example-org-permitted
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:nameConstraints:permitted:example.org
diff --git a/security/manager/ssl/tests/unit/test_name_constraints/moz.build b/security/manager/ssl/tests/unit/test_name_constraints/moz.build
new file mode 100644
index 0000000000..0bc6b1bb38
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_name_constraints/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'NameConstraints.dcissallowed.pem',
+# 'NameConstraints.dcissblocked.pem',
+# 'ca-example-com-permitted.pem',
+# 'int-example-org-permitted.pem',
+# 'ee-example-com-and-org.pem',
+# 'ee-example-com.pem',
+# 'ee-example-org.pem',
+# 'ee-example-test.pem',
+# 'dciss.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_nonascii_path.js b/security/manager/ssl/tests/unit/test_nonascii_path.js
new file mode 100644
index 0000000000..cac942b212
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_nonascii_path.js
@@ -0,0 +1,62 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests to make sure that the certificate DB works with non-ASCII paths.
+
+// Append a single quote and non-ASCII characters to the profile path.
+let env = Cc["@mozilla.org/process/environment;1"].getService(
+ Ci.nsIEnvironment
+);
+let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
+let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+file.initWithPath(profd);
+file.append("'÷1");
+env.set("XPCSHELL_TEST_PROFILE_DIR", file.path);
+
+file = do_get_profile(); // must be called before getting nsIX509CertDB
+Assert.ok(
+ /[^\x20-\x7f]/.test(file.path),
+ "the profile path should contain a non-ASCII character"
+);
+if (mozinfo.os == "win") {
+ file.QueryInterface(Ci.nsILocalFileWin);
+ Assert.ok(
+ /[^\x20-\x7f]/.test(file.canonicalPath),
+ "the profile short path should contain a non-ASCII character"
+ );
+}
+
+// Restore the original value.
+env.set("XPCSHELL_TEST_PROFILE_DIR", profd);
+
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function load_cert(cert_name, trust_string) {
+ let cert_filename = cert_name + ".pem";
+ return addCertFromFile(
+ certdb,
+ "test_cert_trust/" + cert_filename,
+ trust_string
+ );
+}
+
+function run_test() {
+ let certList = ["ca", "int", "ee"];
+ let loadedCerts = [];
+ for (let certName of certList) {
+ loadedCerts.push(load_cert(certName, ",,"));
+ }
+
+ let ca_cert = loadedCerts[0];
+ notEqual(ca_cert, null, "CA cert should have successfully loaded");
+ let int_cert = loadedCerts[1];
+ notEqual(int_cert, null, "Intermediate cert should have successfully loaded");
+ let ee_cert = loadedCerts[2];
+ notEqual(ee_cert, null, "EE cert should have successfully loaded");
+}
diff --git a/security/manager/ssl/tests/unit/test_nsCertType.js b/security/manager/ssl/tests/unit/test_nsCertType.js
new file mode 100644
index 0000000000..8341575473
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_nsCertType.js
@@ -0,0 +1,32 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+//
+// While the Netscape certificate type extension is not a standard and has been
+// discouraged from use for quite some time, it is still encountered. Thus, we
+// handle it slightly differently from other unknown extensions.
+// If it is not marked critical, we ignore it.
+// If it is marked critical:
+// If the basic constraints and extended key usage extensions are also
+// present, we ignore it, because they are standardized and should convey the
+// same information.
+// Otherwise, we reject it with an error indicating an unknown critical
+// extension.
+
+"use strict";
+
+function run_test() {
+ do_get_profile();
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+ add_connection_test("nsCertTypeNotCritical.example.com", PRErrorCodeSuccess);
+ add_connection_test(
+ "nsCertTypeCriticalWithExtKeyUsage.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "nsCertTypeCritical.example.com",
+ SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
+ );
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_nsIX509CertValidity.js b/security/manager/ssl/tests/unit/test_nsIX509CertValidity.js
new file mode 100644
index 0000000000..5febcbf169
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_nsIX509CertValidity.js
@@ -0,0 +1,70 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// This file tests the nsIX509CertValidity implementation.
+
+function fuzzyEqual(attributeName, dateString, expectedTime) {
+ info(`${attributeName}: ${dateString}`);
+ let absTimeDiff = Math.abs(expectedTime - Date.parse(dateString));
+ const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
+ lessOrEqual(
+ absTimeDiff,
+ ONE_DAY_IN_MS,
+ `Time difference for ${attributeName} should be <= one day`
+ );
+}
+
+function run_test() {
+ // Date.parse("2013-01-01T00:00:00Z")
+ const NOT_BEFORE_IN_MS = 1356998400000;
+ // Date.parse("2014-01-01T00:00:00Z")
+ const NOT_AFTER_IN_MS = 1388534400000;
+ let cert = constructCertFromFile("bad_certs/expired-ee.pem");
+
+ equal(
+ cert.validity.notBefore,
+ NOT_BEFORE_IN_MS * 1000,
+ "Actual and expected notBefore should be equal"
+ );
+ equal(
+ cert.validity.notAfter,
+ NOT_AFTER_IN_MS * 1000,
+ "Actual and expected notAfter should be equal"
+ );
+
+ // The following tests rely on the implementation of nsIX509CertValidity
+ // providing long formatted dates to work. If this is not true, a returned
+ // short formatted date such as "12/31/12" may be interpreted as something
+ // other than the expected "December 31st, 2012".
+ //
+ // On Android, the implementation of nsIDateTimeFormat currently does not
+ // return a long formatted date even if it is asked to. This, combined with
+ // the reason above is why the following tests are disabled on Android.
+ if (AppConstants.platform != "android") {
+ fuzzyEqual(
+ "notBeforeLocalTime",
+ cert.validity.notBeforeLocalTime,
+ NOT_BEFORE_IN_MS
+ );
+ fuzzyEqual(
+ "notBeforeLocalDay",
+ cert.validity.notBeforeLocalDay,
+ NOT_BEFORE_IN_MS
+ );
+ fuzzyEqual("notBeforeGMT", cert.validity.notBeforeGMT, NOT_BEFORE_IN_MS);
+
+ fuzzyEqual(
+ "notAfterLocalTime",
+ cert.validity.notAfterLocalTime,
+ NOT_AFTER_IN_MS
+ );
+ fuzzyEqual(
+ "notAfterLocalDay",
+ cert.validity.notAfterLocalDay,
+ NOT_AFTER_IN_MS
+ );
+ fuzzyEqual("notAfterGMT", cert.validity.notAfterGMT, NOT_AFTER_IN_MS);
+ }
+}
diff --git a/security/manager/ssl/tests/unit/test_nsIX509Cert_utf8.js b/security/manager/ssl/tests/unit/test_nsIX509Cert_utf8.js
new file mode 100644
index 0000000000..6305b878b4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_nsIX509Cert_utf8.js
@@ -0,0 +1,96 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Checks that various nsIX509Cert attributes correctly handle UTF-8.
+
+do_get_profile(); // Must be called before getting nsIX509CertDB
+const certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function run_test() {
+ let cert = certDB.constructX509FromBase64(
+ "MIIF3DCCBMSgAwIBAgIEAJiZbzANBgkqhkiG9w0BAQUFADCCAQ0xYTBfBgNVBAMM" +
+ "WEkuQ0EgLSBRdWFsaWZpZWQgcm9vdCBjZXJ0aWZpY2F0ZSAoa3ZhbGlmaWtvdmFu" +
+ "w70gY2VydGlmaWvDoXQgcG9za3l0b3ZhdGVsZSkgLSBQU0VVRE9OWU0xCzAJBgNV" +
+ "BAYTAkNaMS8wLQYDVQQHDCZQb2R2aW5uw70gbWzDvW4gMjE3OC82LCAxOTAgMDAg" +
+ "UHJhaGEgOTEsMCoGA1UECgwjUHJ2bsOtIGNlcnRpZmlrYcSNbsOtIGF1dG9yaXRh" +
+ "IGEucy4xPDA6BgNVBAsMM0FrcmVkaXRvdmFuw70gcG9za3l0b3ZhdGVsIGNlcnRp" +
+ "ZmlrYcSNbsOtY2ggc2x1xb5lYjAeFw0wMjEyMTIxMzMzNDZaFw0wMzEyMTIxMzMz" +
+ "NDZaMIIBFDELMAkGA1UEBhMCQ1oxHzAdBgNVBAMeFgBMAHUAZAEbAGsAIABSAGEB" +
+ "YQBlAGsxGTAXBgNVBAgeEABWAHkAcwBvAQ0AaQBuAGExLzAtBgNVBAceJgBQAGEA" +
+ "YwBvAHYALAAgAE4A4QBkAHIAYQF+AG4A7QAgADcANgA5MSUwIwYJKoZIhvcNAQkB" +
+ "FhZsdWRlay5yYXNla0BjZW50cnVtLmN6MRMwEQYDVQQqHgoATAB1AGQBGwBrMQ0w" +
+ "CwYDVQQrHgQATABSMR8wHQYDVQQpHhYATAB1AGQBGwBrACAAUgBhAWEAZQBrMRMw" +
+ "EQYDVQQEHgoAUgBhAWEAZQBrMRcwFQYDVQQFEw5JQ0EgLSAxMDAwMzc2OTCBnzAN" +
+ "BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxc7dGd0cNlHZ7tUUl5k30bfYlY3lnOD0" +
+ "49JGbTXSt4jNFMRLj6s/777W3kcIdcIwdKxjQULBKgryDvZJ1DAWp2TwzhPDVYj3" +
+ "sU4Niqb7mOUcp/4ckteUxGF6FmXtJR9+XHTuLZ+omF9HOUefheBKnXvZuqrLM16y" +
+ "nbJn4sPwwdcCAwEAAaOCAbswggG3MCUGA1UdEQQeMBygGgYKKwMGAQQB3BkCAaAM" +
+ "DAoxNzYyODk2ODgzMGkGA1UdHwRiMGAwHqAcoBqGGGh0dHA6Ly9xLmljYS5jei9x" +
+ "aWNhLmNybDAeoBygGoYYaHR0cDovL2IuaWNhLmN6L3FpY2EuY3JsMB6gHKAahhho" +
+ "dHRwOi8vci5pY2EuY3ovcWljYS5jcmwwHwYDVR0jBBgwFoAUK1oKfvvlDYUsZTBy" +
+ "vGN701mca/UwHQYDVR0OBBYEFPAs70DB+LS0PnA6niPUfJ5wdQH5MIG4BgNVHSAE" +
+ "gbAwga0wgaoGCysGAQQBs2EBAQQEMIGaMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3" +
+ "LmljYS5jei9xY3AvY3BxcGljYTAyLnBkZjBnBggrBgEFBQcCAjBbGllUZW50byBj" +
+ "ZXJ0aWZpa2F0IGplIHZ5ZGFuIGpha28gS3ZhbGlmaWtvdmFueSBjZXJ0aWZpa2F0" +
+ "IHYgc291bGFkdSBzZSB6YWtvbmVtIDIyNy8yMDAwIFNiLjAYBggrBgEFBQcBAwQM" +
+ "MAowCAYGBACORgEBMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQUFAAOCAQEA" +
+ "v2V+nnYYMIgabmmgHx49CtlZIHdGS3TuWKXw130xFhbXDnNhEbx3alaskNsvjQQR" +
+ "Lqs1ZwKy58yynse+eJYHqenmHDACpAfVpCF9PXC/mDarVsoQw7NTcUpsAFhSd/zT" +
+ "v9jIf3twECyxx/RVzONVcob7nPePESHiKoG4FbtcuUh0wSHvCmTwRIQqPDCIuHcF" +
+ "StSt3Jr9iXcbXEhe4mSccOZ8N+r7Rv3ncKcevlRl7uFfDKDTyd43SZeRS/7J8KRf" +
+ "hD/h2nawrCFwc5gJW10aLJGFL/mcS7ViAIT9HCVk23j4TuBjsVmnZ0VKxB5edux+" +
+ "LIEqtU428UVHZWU/I5ngLw=="
+ );
+
+ equal(
+ cert.emailAddress,
+ "ludek.rasek@centrum.cz",
+ "Actual and expected emailAddress should match"
+ );
+ equal(
+ cert.subjectName,
+ 'serialNumber=ICA - 10003769,SN=Rašek,name=Luděk Rašek,initials=LR,givenName=Luděk,E=ludek.rasek@centrum.cz,L="Pacov, Nádražní 769",ST=Vysočina,CN=Luděk Rašek,C=CZ',
+ "Actual and expected subjectName should match"
+ );
+ equal(
+ cert.commonName,
+ "Luděk Rašek",
+ "Actual and expected commonName should match"
+ );
+ equal(cert.organization, "", "Actual and expected organization should match");
+ equal(
+ cert.organizationalUnit,
+ "",
+ "Actual and expected organizationalUnit should match"
+ );
+ equal(
+ cert.displayName,
+ "Luděk Rašek",
+ "Actual and expected displayName should match"
+ );
+ equal(
+ cert.issuerName,
+ 'OU=Akreditovaný poskytovatel certifikačních služeb,O=První certifikační autorita a.s.,L="Podvinný mlýn 2178/6, 190 00 Praha 9",C=CZ,CN=I.CA - Qualified root certificate (kvalifikovaný certifikát poskytovatele) - PSEUDONYM',
+ "Actual and expected issuerName should match"
+ );
+ equal(
+ cert.issuerCommonName,
+ "I.CA - Qualified root certificate (kvalifikovaný certifikát poskytovatele) - PSEUDONYM",
+ "Actual and expected issuerCommonName should match"
+ );
+ equal(
+ cert.issuerOrganization,
+ "První certifikační autorita a.s.",
+ "Actual and expected issuerOrganization should match"
+ );
+ equal(
+ cert.issuerOrganizationUnit,
+ "Akreditovaný poskytovatel certifikačních služeb",
+ "Actual and expected issuerOrganizationUnit should match"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_nss_shutdown.js b/security/manager/ssl/tests/unit/test_nss_shutdown.js
new file mode 100644
index 0000000000..36cf283477
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_nss_shutdown.js
@@ -0,0 +1,48 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// This test attempts to ensure that PSM doesn't deadlock or crash when shutting
+// down NSS while a background thread is attempting to use NSS.
+// Uses test_signed_apps/app_mf-1_sf-1_p7-1.zip from test_signed_apps.js.
+
+function startAsyncNSSOperation(certdb, appFile) {
+ return new Promise((resolve, reject) => {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ appFile,
+ function(rv, aZipReader, aSignerCert) {
+ // rv will either indicate success (if NSS hasn't been shut down yet) or
+ // it will be some error code that varies depending on when NSS got shut
+ // down. As such, there's nothing really to check here. Just resolve the
+ // promise to continue execution.
+ resolve();
+ }
+ );
+ });
+}
+
+add_task(async function() {
+ do_get_profile();
+ let psm = Cc["@mozilla.org/psm;1"]
+ .getService(Ci.nsISupports)
+ .QueryInterface(Ci.nsIObserver);
+ let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let appFile = do_get_file("test_signed_apps/app_mf-1_sf-1_p7-1.zip");
+
+ let promises = [];
+ for (let i = 0; i < 25; i++) {
+ promises.push(startAsyncNSSOperation(certdb, appFile));
+ }
+ // Trick PSM into thinking it should shut down NSS. If this test doesn't
+ // hang or crash, we're good.
+ psm.observe(null, "profile-before-change", null);
+ for (let i = 0; i < 25; i++) {
+ promises.push(startAsyncNSSOperation(certdb, appFile));
+ }
+ await Promise.all(promises);
+});
diff --git a/security/manager/ssl/tests/unit/test_ocsp_caching.js b/security/manager/ssl/tests/unit/test_ocsp_caching.js
new file mode 100644
index 0000000000..68b3778bb9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_caching.js
@@ -0,0 +1,406 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Checks various aspects of the OCSP cache, mainly to to ensure we do not fetch
+// responses more than necessary.
+
+var gFetchCount = 0;
+var gGoodOCSPResponse = null;
+var gResponsePattern = [];
+var gMessage = "";
+
+function respondWithGoodOCSP(request, response) {
+ info("returning 200 OK");
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "application/ocsp-response");
+ response.write(gGoodOCSPResponse);
+}
+
+function respondWithSHA1OCSP(request, response) {
+ info("returning 200 OK with sha-1 delegated response");
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "application/ocsp-response");
+
+ let args = [["good-delegated", "default-ee", "delegatedSHA1Signer", 0]];
+ let responses = generateOCSPResponses(args, "ocsp_certs");
+ response.write(responses[0]);
+}
+
+function respondWithError(request, response) {
+ info("returning 500 Internal Server Error");
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ let body = "Refusing to return a response";
+ response.bodyOutputStream.write(body, body.length);
+}
+
+function generateGoodOCSPResponse(thisUpdateSkew) {
+ let args = [["good", "default-ee", "unused", thisUpdateSkew]];
+ let responses = generateOCSPResponses(args, "ocsp_certs");
+ return responses[0];
+}
+
+function add_ocsp_test(
+ aHost,
+ aExpectedResult,
+ aResponses,
+ aMessage,
+ aOriginAttributes
+) {
+ add_connection_test(
+ aHost,
+ aExpectedResult,
+ function() {
+ clearSessionCache();
+ gFetchCount = 0;
+ gResponsePattern = aResponses;
+ gMessage = aMessage;
+ },
+ function() {
+ // check the number of requests matches the size of aResponses
+ equal(
+ gFetchCount,
+ aResponses.length,
+ "should have made " +
+ aResponses.length +
+ " OCSP request" +
+ (aResponses.length == 1 ? "" : "s")
+ );
+ },
+ null,
+ aOriginAttributes
+ );
+}
+
+function run_test() {
+ do_get_profile();
+ Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 4);
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ let ocspResponder = new HttpServer();
+ ocspResponder.registerPrefixHandler("/", function(request, response) {
+ info("gFetchCount: " + gFetchCount);
+ let responseFunction = gResponsePattern[gFetchCount];
+ Assert.notEqual(undefined, responseFunction);
+
+ ++gFetchCount;
+ responseFunction(request, response);
+ });
+ ocspResponder.start(8888);
+
+ add_tests();
+
+ add_test(function() {
+ ocspResponder.stop(run_next_test);
+ });
+ run_next_test();
+}
+
+function add_tests() {
+ // Test that verifying a certificate with a "short lifetime" doesn't result
+ // in OCSP fetching. Due to longevity requirements in our testing
+ // infrastructure, the certificate we encounter is valid for a very long
+ // time, so we have to define a "short lifetime" as something very long.
+ add_test(function() {
+ Services.prefs.setIntPref(
+ "security.pki.cert_short_lifetime_in_days",
+ 12000
+ );
+ run_next_test();
+ });
+
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [],
+ "expected zero OCSP requests for a short-lived certificate"
+ );
+
+ add_test(function() {
+ Services.prefs.setIntPref("security.pki.cert_short_lifetime_in_days", 100);
+ run_next_test();
+ });
+
+ // If a "short lifetime" is something more reasonable, ensure that we do OCSP
+ // fetching for this long-lived certificate.
+
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [respondWithError],
+ "expected one OCSP request for a long-lived certificate"
+ );
+ add_test(function() {
+ Services.prefs.clearUserPref("security.pki.cert_short_lifetime_in_days");
+ run_next_test();
+ });
+ // ---------------------------------------------------------------------------
+
+ // Reset state
+ add_test(function() {
+ clearOCSPCache();
+ run_next_test();
+ });
+
+ // This test assumes that OCSPStaplingServer uses the same cert for
+ // ocsp-stapling-unknown.example.com and ocsp-stapling-none.example.com.
+
+ // Get an Unknown response for the *.example.com cert and put it in the
+ // OCSP cache.
+ add_ocsp_test(
+ "ocsp-stapling-unknown.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ [],
+ "Stapled Unknown response -> a fetch should not have been attempted"
+ );
+
+ // A failure to retrieve an OCSP response must result in the cached Unknown
+ // response being recognized and honored.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ [
+ respondWithError,
+ respondWithError,
+ respondWithError,
+ respondWithError,
+ respondWithError,
+ respondWithError,
+ ],
+ "No stapled response -> a fetch should have been attempted"
+ );
+
+ // A valid Good response from the OCSP responder must override the cached
+ // Unknown response.
+ //
+ // Note that We need to make sure that the Unknown response and the Good
+ // response have different thisUpdate timestamps; otherwise, the Good
+ // response will be seen as "not newer" and it won't replace the existing
+ // entry.
+ add_test(function() {
+ gGoodOCSPResponse = generateGoodOCSPResponse(1200);
+ run_next_test();
+ });
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [respondWithGoodOCSP],
+ "Cached Unknown response, no stapled response -> a fetch" +
+ " should have been attempted"
+ );
+
+ // The Good response retrieved from the previous fetch must have replaced
+ // the Unknown response in the cache, resulting in the catched Good response
+ // being returned and no fetch.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [],
+ "Cached Good response -> a fetch should not have been attempted"
+ );
+
+ // ---------------------------------------------------------------------------
+
+ // Reset state
+ add_test(function() {
+ clearOCSPCache();
+ run_next_test();
+ });
+
+ // A failure to retrieve an OCSP response will result in an error entry being
+ // added to the cache.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [respondWithError],
+ "No stapled response -> a fetch should have been attempted"
+ );
+
+ // The error entry will prevent a fetch from happening for a while.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [],
+ "Noted OCSP server failure -> a fetch should not have been attempted"
+ );
+
+ // The error entry must not prevent a stapled OCSP response from being
+ // honored.
+ add_ocsp_test(
+ "ocsp-stapling-revoked.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ [],
+ "Stapled Revoked response -> a fetch should not have been attempted"
+ );
+
+ // ---------------------------------------------------------------------------
+
+ // Ensure OCSP responses from signers with SHA1 certificates are OK. This
+ // is included in the OCSP caching tests since there were OCSP cache-related
+ // regressions when sha-1 telemetry probes were added.
+ add_test(function() {
+ clearOCSPCache();
+ // set security.OCSP.require so that checking the OCSP signature fails
+ Services.prefs.setBoolPref("security.OCSP.require", true);
+ run_next_test();
+ });
+
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [respondWithSHA1OCSP],
+ "signing cert is good (though sha1) - should succeed"
+ );
+
+ add_test(function() {
+ Services.prefs.setBoolPref("security.OCSP.require", false);
+ run_next_test();
+ });
+
+ // ---------------------------------------------------------------------------
+
+ // Reset state
+ add_test(function() {
+ clearOCSPCache();
+ run_next_test();
+ });
+
+ // This test makes sure that OCSP cache are isolated by firstPartyDomain.
+
+ let gObservedCnt = 0;
+ let protocolProxyService = Cc[
+ "@mozilla.org/network/protocol-proxy-service;1"
+ ].getService(Ci.nsIProtocolProxyService);
+
+ // Observe all channels and make sure the firstPartyDomain in their loadInfo's
+ // origin attributes are aFirstPartyDomain.
+ function startObservingChannels(aFirstPartyDomain) {
+ // We use a dummy proxy filter to catch all channels, even those that do not
+ // generate an "http-on-modify-request" notification.
+ let proxyFilter = {
+ applyFilter(aChannel, aProxy, aCallback) {
+ // We have the channel; provide it to the callback.
+ if (aChannel.originalURI.spec == "http://localhost:8888/") {
+ gObservedCnt++;
+ equal(
+ aChannel.loadInfo.originAttributes.firstPartyDomain,
+ aFirstPartyDomain,
+ "firstPartyDomain should match"
+ );
+ }
+ // Pass on aProxy unmodified.
+ aCallback.onProxyFilterResult(aProxy);
+ },
+ };
+ protocolProxyService.registerChannelFilter(proxyFilter, 0);
+ // Return the stop() function:
+ return () => protocolProxyService.unregisterChannelFilter(proxyFilter);
+ }
+
+ let stopObservingChannels;
+ add_test(function() {
+ stopObservingChannels = startObservingChannels("foo.com");
+ run_next_test();
+ });
+
+ // A good OCSP response will be cached.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [respondWithGoodOCSP],
+ "No stapled response (firstPartyDomain = foo.com) -> a fetch " +
+ "should have been attempted",
+ { firstPartyDomain: "foo.com" }
+ );
+
+ // The cache will prevent a fetch from happening.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [],
+ "Noted OCSP server failure (firstPartyDomain = foo.com) -> a " +
+ "fetch should not have been attempted",
+ { firstPartyDomain: "foo.com" }
+ );
+
+ add_test(function() {
+ stopObservingChannels();
+ equal(gObservedCnt, 1, "should have observed only 1 OCSP requests");
+ gObservedCnt = 0;
+ run_next_test();
+ });
+
+ add_test(function() {
+ stopObservingChannels = startObservingChannels("bar.com");
+ run_next_test();
+ });
+
+ // But using a different firstPartyDomain should result in a fetch.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [respondWithGoodOCSP],
+ "No stapled response (firstPartyDomain = bar.com) -> a fetch " +
+ "should have been attempted",
+ { firstPartyDomain: "bar.com" }
+ );
+
+ add_test(function() {
+ stopObservingChannels();
+ equal(gObservedCnt, 1, "should have observed only 1 OCSP requests");
+ gObservedCnt = 0;
+ run_next_test();
+ });
+
+ // ---------------------------------------------------------------------------
+
+ // Reset state
+ add_test(function() {
+ clearOCSPCache();
+ run_next_test();
+ });
+
+ // Test that the OCSP cache is not isolated by userContextId.
+
+ // A good OCSP response will be cached.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [respondWithGoodOCSP],
+ "No stapled response (userContextId = 1) -> a fetch " +
+ "should have been attempted",
+ { userContextId: 1 }
+ );
+
+ // The cache will prevent a fetch from happening.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [],
+ "Noted OCSP server failure (userContextId = 1) -> a " +
+ "fetch should not have been attempted",
+ { userContextId: 1 }
+ );
+
+ // Fetching is prevented even if in a different userContextId.
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ [],
+ "Noted OCSP server failure (userContextId = 2) -> a " +
+ "fetch should not have been attempted",
+ { userContextId: 2 }
+ );
+
+ // ---------------------------------------------------------------------------
+
+ // Reset state
+ add_test(function() {
+ clearOCSPCache();
+ run_next_test();
+ });
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_enabled_pref.js b/security/manager/ssl/tests/unit/test_ocsp_enabled_pref.js
new file mode 100644
index 0000000000..cf64ed17fb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_enabled_pref.js
@@ -0,0 +1,150 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Checks that the security.OCSP.enabled pref correctly controls OCSP fetching
+// behavior.
+
+do_get_profile(); // Must be called before getting nsIX509CertDB
+const gCertDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const SERVER_PORT = 8888;
+
+function certFromFile(filename) {
+ return constructCertFromFile(`test_ev_certs/${filename}.pem`);
+}
+
+function loadCert(certName, trustString) {
+ addCertFromFile(gCertDB, `test_ev_certs/${certName}.pem`, trustString);
+}
+
+function getFailingOCSPResponder() {
+ return getFailingHttpServer(SERVER_PORT, ["www.example.com"]);
+}
+
+function getOCSPResponder(expectedCertNames) {
+ return startOCSPResponder(
+ SERVER_PORT,
+ "www.example.com",
+ "test_ev_certs",
+ expectedCertNames,
+ []
+ );
+}
+
+// Tests that in ocspOff mode, OCSP fetches are never done.
+async function testOff() {
+ Services.prefs.setIntPref("security.OCSP.enabled", 0);
+ info("Setting security.OCSP.enabled to 0");
+
+ // EV chains should verify successfully but never get EV status.
+ clearOCSPCache();
+ let ocspResponder = getFailingOCSPResponder();
+ await checkEVStatus(
+ gCertDB,
+ certFromFile("test-oid-path-ee"),
+ certificateUsageSSLServer,
+ false
+ );
+ await stopOCSPResponder(ocspResponder);
+
+ // A DV chain should verify successfully.
+ clearOCSPCache();
+ ocspResponder = getFailingOCSPResponder();
+ await checkCertErrorGeneric(
+ gCertDB,
+ certFromFile("non-ev-root-path-ee"),
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ await stopOCSPResponder(ocspResponder);
+}
+
+// Tests that in ocspOn mode, OCSP fetches are done for both EV and DV certs.
+async function testOn() {
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ info("Setting security.OCSP.enabled to 1");
+
+ // If a successful OCSP response is fetched, then an EV chain should verify
+ // successfully and get EV status as well.
+ clearOCSPCache();
+ let ocspResponder = getOCSPResponder(
+ gEVExpected
+ ? ["test-oid-path-int", "test-oid-path-ee"]
+ : ["test-oid-path-ee"]
+ );
+ await checkEVStatus(
+ gCertDB,
+ certFromFile("test-oid-path-ee"),
+ certificateUsageSSLServer,
+ gEVExpected
+ );
+ await stopOCSPResponder(ocspResponder);
+
+ // If a successful OCSP response is fetched, then a DV chain should verify
+ // successfully.
+ clearOCSPCache();
+ ocspResponder = getOCSPResponder(["non-ev-root-path-ee"]);
+ await checkCertErrorGeneric(
+ gCertDB,
+ certFromFile("non-ev-root-path-ee"),
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ await stopOCSPResponder(ocspResponder);
+}
+
+// Tests that in ocspEVOnly mode, OCSP fetches are done for EV certs only.
+async function testEVOnly() {
+ Services.prefs.setIntPref("security.OCSP.enabled", 2);
+ info("Setting security.OCSP.enabled to 2");
+
+ // If a successful OCSP response is fetched, then an EV chain should verify
+ // successfully and get EV status as well.
+ clearOCSPCache();
+ let ocspResponder = gEVExpected
+ ? getOCSPResponder(["test-oid-path-int", "test-oid-path-ee"])
+ : getFailingOCSPResponder();
+ await checkEVStatus(
+ gCertDB,
+ certFromFile("test-oid-path-ee"),
+ certificateUsageSSLServer,
+ gEVExpected
+ );
+ await stopOCSPResponder(ocspResponder);
+
+ // A DV chain should verify successfully even without doing OCSP fetches.
+ clearOCSPCache();
+ ocspResponder = getFailingOCSPResponder();
+ await checkCertErrorGeneric(
+ gCertDB,
+ certFromFile("non-ev-root-path-ee"),
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer
+ );
+ await stopOCSPResponder(ocspResponder);
+}
+
+add_task(async function() {
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("network.dns.localDomains");
+ Services.prefs.clearUserPref("security.OCSP.enabled");
+ Services.prefs.clearUserPref("security.OCSP.require");
+ });
+ Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
+ // Enable hard fail to ensure chains that should only succeed because they get
+ // a good OCSP response do not succeed due to soft fail leniency.
+ Services.prefs.setBoolPref("security.OCSP.require", true);
+
+ loadCert("evroot", "CTu,,");
+ loadCert("test-oid-path-int", ",,");
+ loadCert("non-evroot-ca", "CTu,,");
+ loadCert("non-ev-root-path-int", ",,");
+
+ await testOff();
+ await testOn();
+ await testEVOnly();
+});
diff --git a/security/manager/ssl/tests/unit/test_ocsp_must_staple.js b/security/manager/ssl/tests/unit/test_ocsp_must_staple.js
new file mode 100644
index 0000000000..46f6127c50
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_must_staple.js
@@ -0,0 +1,160 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Tests OCSP Must Staple handling by connecting to various domains (as faked by
+// a server running locally) that correspond to combinations of whether the
+// extension is present in intermediate and end-entity certificates.
+
+var gExpectOCSPRequest;
+
+function add_ocsp_test(
+ aHost,
+ aExpectedResult,
+ aStaplingEnabled,
+ aExpectOCSPRequest = false,
+ aWithSecurityInfo = undefined
+) {
+ add_connection_test(
+ aHost,
+ aExpectedResult,
+ function() {
+ gExpectOCSPRequest = aExpectOCSPRequest;
+ clearOCSPCache();
+ clearSessionCache();
+ Services.prefs.setBoolPref(
+ "security.ssl.enable_ocsp_stapling",
+ aStaplingEnabled
+ );
+ },
+ aWithSecurityInfo
+ );
+}
+
+function add_tests() {
+ // Next, a case where it's present in the intermediate, not the ee
+ add_ocsp_test(
+ "ocsp-stapling-plain-ee-with-must-staple-int.example.com",
+ MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING,
+ true
+ );
+
+ // We disable OCSP stapling in the next two tests so we can perform checks
+ // on TLS Features in the chain without needing to support the TLS
+ // extension values used.
+ // Test an issuer with multiple TLS features in matched in the EE
+ add_ocsp_test(
+ "multi-tls-feature-good.example.com",
+ PRErrorCodeSuccess,
+ false
+ );
+
+ // Finally, an issuer with multiple TLS features not matched by the EE.
+ add_ocsp_test(
+ "multi-tls-feature-bad.example.com",
+ MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING,
+ false
+ );
+
+ // Now a bunch of operations with only a must-staple ee
+ add_ocsp_test(
+ "ocsp-stapling-must-staple.example.com",
+ PRErrorCodeSuccess,
+ true
+ );
+
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-revoked.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ true
+ );
+
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-missing.example.com",
+ MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING,
+ true,
+ true
+ );
+
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-empty.example.com",
+ SEC_ERROR_OCSP_MALFORMED_RESPONSE,
+ true
+ );
+
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-missing.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+
+ // If the stapled response is expired, we will try to fetch a new one.
+ // If that fails, we should report the original error.
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-expired.example.com",
+ SEC_ERROR_OCSP_OLD_RESPONSE,
+ true,
+ true
+ );
+ // Similarly with a "try server later" response.
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-try-later.example.com",
+ SEC_ERROR_OCSP_TRY_SERVER_LATER,
+ true,
+ true
+ );
+ // And again with an invalid OCSP response signing certificate.
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-invalid-signer.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+
+ // check that disabling must-staple works
+ add_test(function() {
+ clearSessionCache();
+ Services.prefs.setBoolPref("security.ssl.enable_ocsp_must_staple", false);
+ run_next_test();
+ });
+
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-missing.example.com",
+ PRErrorCodeSuccess,
+ true,
+ true
+ );
+}
+
+function run_test() {
+ do_get_profile();
+ Services.prefs.setBoolPref("security.ssl.enable_ocsp_must_staple", true);
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ // This test may sometimes fail on android due to an OCSP request timing out.
+ // That aspect of OCSP requests is not what we're testing here, so we can just
+ // bump the timeout and hopefully avoid these failures.
+ Services.prefs.setIntPref("security.OCSP.timeoutMilliseconds.soft", 5000);
+
+ let fakeOCSPResponder = new HttpServer();
+ fakeOCSPResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ ok(
+ gExpectOCSPRequest,
+ "Should be getting an OCSP request only when expected"
+ );
+ });
+ fakeOCSPResponder.start(8888);
+
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ add_tests();
+
+ add_test(function() {
+ fakeOCSPResponder.stop(run_next_test);
+ });
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
new file mode 100644
index 0000000000..1c9ba6dcce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
@@ -0,0 +1,68 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// Test that if an OCSP request is made to a domain that (erroneously)
+// has HSTS status, the request is not upgraded from HTTP to HTTPS.
+
+function run_test() {
+ do_get_profile();
+ // OCSP required means this test will only pass if the request succeeds.
+ Services.prefs.setBoolPref("security.OCSP.require", true);
+
+ // We don't actually make use of stapling in this test. This is just how we
+ // get a TLS connection.
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ let args = [["good", "default-ee", "unused", 0]];
+ let ocspResponses = generateOCSPResponses(args, "ocsp_certs");
+ let goodOCSPResponse = ocspResponses[0];
+
+ let ocspResponder = new HttpServer();
+ ocspResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "application/ocsp-response");
+ response.write(goodOCSPResponse);
+ });
+ ocspResponder.start(8888);
+
+ // ocsp-stapling-none.example.com does not staple an OCSP response in the
+ // handshake, so the revocation checking code will attempt to fetch one.
+ // Since the domain of the certificate's OCSP AIA URI is an HSTS host
+ // (as added in the setup of this test, below), a buggy implementation would
+ // upgrade the OCSP request to HTTPS. We specifically prevent this. This
+ // test demonstrates that our implementation is correct in this regard.
+ add_connection_test("ocsp-stapling-none.example.com", PRErrorCodeSuccess);
+ add_test(function() {
+ run_next_test();
+ });
+
+ add_test(function() {
+ ocspResponder.stop(run_next_test);
+ });
+
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ let uri = Services.io.newURI("http://localhost");
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+ SSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=10000",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(
+ SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
+ "Domain for the OCSP AIA URI should be considered a HSTS host, otherwise" +
+ " we wouldn't be testing what we think we're testing"
+ );
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_private_caching.js b/security/manager/ssl/tests/unit/test_ocsp_private_caching.js
new file mode 100644
index 0000000000..a3ee41af3f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_private_caching.js
@@ -0,0 +1,138 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// In which we connect to a host and encounter OCSP responses with the
+// Cache-Control header set, which Necko will normally cache. We need to ensure
+// that these responses aren't cached to disk when the original https request
+// was in a private context.
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const SERVER_PORT = 8888;
+
+function start_ocsp_responder(
+ expectedCertNames,
+ expectedPaths,
+ expectedMethods
+) {
+ return startOCSPResponder(
+ SERVER_PORT,
+ "www.example.com",
+ "test_ocsp_fetch_method",
+ expectedCertNames,
+ expectedPaths,
+ expectedMethods
+ );
+}
+
+function add_flush_cache() {
+ add_test(() => {
+ // This appears to either fire multiple times or fire once for every
+ // observer that has ever been passed to flush. To prevent multiple calls to
+ // run_next_test, keep track of if this observer has already called it.
+ let observed = false;
+ let observer = {
+ observe: () => {
+ if (!observed) {
+ observed = true;
+ run_next_test();
+ }
+ },
+ };
+ Services.cache2.QueryInterface(Ci.nsICacheTesting).flush(observer);
+ });
+}
+
+function add_ocsp_necko_cache_test(loadContext, shouldFindEntry) {
+ // Pre-testcase cleanup/setup.
+ add_test(() => {
+ Services.cache2.clear();
+ run_next_test();
+ });
+ add_flush_cache();
+
+ let responder;
+ add_test(() => {
+ clearOCSPCache();
+ clearSessionCache();
+ responder = startOCSPResponder(
+ SERVER_PORT,
+ "localhost",
+ "ocsp_certs",
+ ["default-ee"],
+ [],
+ [],
+ [],
+ [["Cache-Control", "max-age: 1000"]]
+ );
+ run_next_test();
+ });
+
+ // Prepare a connection that will cause an OCSP request.
+ add_connection_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ null,
+ null,
+ null,
+ loadContext.originAttributes
+ );
+
+ add_flush_cache();
+
+ // Traverse the cache and ensure the response made it into the cache with the
+ // appropriate properties (private or not private).
+ add_test(() => {
+ let foundEntry = false;
+ let visitor = {
+ onCacheStorageInfo() {},
+ onCacheEntryInfo(
+ aURI,
+ aIdEnhance,
+ aDataSize,
+ aFetchCount,
+ aLastModifiedTime,
+ aExpirationTime,
+ aPinned,
+ aInfo
+ ) {
+ Assert.equal(
+ aURI.spec,
+ "http://localhost:8888/",
+ "expected OCSP request URI should match"
+ );
+ foundEntry = true;
+ },
+ onCacheEntryVisitCompleted() {
+ Assert.equal(
+ foundEntry,
+ shouldFindEntry,
+ "should only find a cached entry if we're expecting one"
+ );
+ run_next_test();
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsICacheStorageVisitor"]),
+ };
+ Services.cache2.asyncVisitAllStorages(visitor, true);
+ });
+
+ // Clean up (stop the responder).
+ add_test(() => {
+ responder.stop(run_next_test);
+ });
+}
+
+function run_test() {
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+ add_ocsp_necko_cache_test(Services.loadContextInfo.private, false);
+ add_ocsp_necko_cache_test(Services.loadContextInfo.default, true);
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_required.js b/security/manager/ssl/tests/unit/test_ocsp_required.js
new file mode 100644
index 0000000000..6e1c52a6eb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_required.js
@@ -0,0 +1,74 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// In which we connect to a domain (as faked by a server running locally) and
+// start up an OCSP responder (also basically faked) that gives a response with
+// a bad signature (and later, an empty response). With security.OCSP.require
+// set to true, these connections should fail (but they also shouldn't cause
+// assertion failures).
+
+var gOCSPRequestCount = 0;
+var gOCSPResponse;
+
+function run_test() {
+ do_get_profile();
+ Services.prefs.setBoolPref("security.OCSP.require", true);
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+
+ // We don't actually make use of stapling in this test. This is just how we
+ // get a TLS connection.
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ let args = [["bad-signature", "default-ee", "unused", 0]];
+ let ocspResponses = generateOCSPResponses(args, "ocsp_certs");
+ // Start by replying with a response with a bad signature.
+ gOCSPResponse = ocspResponses[0];
+
+ let ocspResponder = new HttpServer();
+ ocspResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "application/ocsp-response");
+ response.write(gOCSPResponse);
+ gOCSPRequestCount++;
+ });
+ ocspResponder.start(8888);
+
+ add_tests();
+
+ add_test(function() {
+ ocspResponder.stop(run_next_test);
+ });
+
+ run_next_test();
+}
+
+function add_tests() {
+ add_connection_test(
+ "ocsp-stapling-none.example.com",
+ SEC_ERROR_OCSP_BAD_SIGNATURE
+ );
+ add_connection_test(
+ "ocsp-stapling-none.example.com",
+ SEC_ERROR_OCSP_BAD_SIGNATURE
+ );
+ add_test(function() {
+ equal(
+ gOCSPRequestCount,
+ 1,
+ "OCSP request count should be 1 due to OCSP response caching"
+ );
+ gOCSPRequestCount = 0;
+ // Now set the OCSP responder to reply with 200 OK but empty content.
+ gOCSPResponse = "";
+ clearOCSPCache();
+ run_next_test();
+ });
+
+ add_connection_test(
+ "ocsp-stapling-none.example.com",
+ SEC_ERROR_OCSP_MALFORMED_RESPONSE
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling.js b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
new file mode 100644
index 0000000000..a41610392b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
@@ -0,0 +1,393 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// In which we connect to a number of domains (as faked by a server running
+// locally) with and without OCSP stapling enabled to determine that good
+// things happen and bad things don't.
+
+var gExpectOCSPRequest;
+
+function add_ocsp_test(
+ aHost,
+ aExpectedResult,
+ aStaplingEnabled,
+ aExpectOCSPRequest = false
+) {
+ add_connection_test(aHost, aExpectedResult, function() {
+ gExpectOCSPRequest = aExpectOCSPRequest;
+ clearOCSPCache();
+ clearSessionCache();
+ Services.prefs.setBoolPref(
+ "security.ssl.enable_ocsp_stapling",
+ aStaplingEnabled
+ );
+ });
+}
+
+function add_tests() {
+ // In the absence of OCSP stapling, these should actually all work.
+ add_ocsp_test(
+ "ocsp-stapling-good.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-revoked.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-good-other-ca.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-malformed.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-srverr.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-trylater.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-needssig.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-unauthorized.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-unknown.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-good-other.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired-fresh-ca.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-skip-responseBytes.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-critical-extension.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-noncritical-extension.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-empty-extensions.example.com",
+ PRErrorCodeSuccess,
+ false,
+ true
+ );
+
+ // Now test OCSP stapling
+ // The following error codes are defined in security/nss/lib/util/SECerrs.h
+
+ add_ocsp_test("ocsp-stapling-good.example.com", PRErrorCodeSuccess, true);
+
+ add_ocsp_test(
+ "ocsp-stapling-revoked.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ true
+ );
+
+ // This stapled response is from a CA that is untrusted and did not issue
+ // the server's certificate.
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let otherTestCA = constructCertFromFile("ocsp_certs/other-test-ca.pem");
+ add_test(function() {
+ certDB.setCertTrust(
+ otherTestCA,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.UNTRUSTED
+ );
+ run_next_test();
+ });
+ add_ocsp_test(
+ "ocsp-stapling-good-other-ca.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+
+ // The stapled response is from a CA that is trusted but did not issue the
+ // server's certificate.
+ add_test(function() {
+ certDB.setCertTrust(
+ otherTestCA,
+ Ci.nsIX509Cert.CA_CERT,
+ Ci.nsIX509CertDB.TRUSTED_SSL
+ );
+ run_next_test();
+ });
+ // TODO(bug 979055): When using ByName instead of ByKey, the error here is
+ // SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE. We should be testing both cases.
+ add_ocsp_test(
+ "ocsp-stapling-good-other-ca.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+
+ // TODO: Test the case where the signing cert can't be found at all, which
+ // will result in SEC_ERROR_BAD_DATABASE in the NSS classic case.
+
+ add_ocsp_test(
+ "ocsp-stapling-malformed.example.com",
+ SEC_ERROR_OCSP_MALFORMED_REQUEST,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-srverr.example.com",
+ SEC_ERROR_OCSP_SERVER_ERROR,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-trylater.example.com",
+ SEC_ERROR_OCSP_TRY_SERVER_LATER,
+ true,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-needssig.example.com",
+ SEC_ERROR_OCSP_REQUEST_NEEDS_SIG,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-unauthorized.example.com",
+ SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-unknown.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-good-other.example.com",
+ MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING,
+ true
+ );
+ // If the server doesn't staple an OCSP response, we continue as normal
+ // (this means that even though stapling is enabled, we expect an OCSP
+ // request).
+ add_connection_test(
+ "ocsp-stapling-none.example.com",
+ PRErrorCodeSuccess,
+ function() {
+ gExpectOCSPRequest = true;
+ clearOCSPCache();
+ clearSessionCache();
+ Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
+ }
+ );
+ add_ocsp_test(
+ "ocsp-stapling-empty.example.com",
+ SEC_ERROR_OCSP_MALFORMED_RESPONSE,
+ true
+ );
+
+ add_ocsp_test(
+ "ocsp-stapling-skip-responseBytes.example.com",
+ SEC_ERROR_OCSP_MALFORMED_RESPONSE,
+ true
+ );
+
+ add_ocsp_test(
+ "ocsp-stapling-critical-extension.example.com",
+ SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-noncritical-extension.example.com",
+ PRErrorCodeSuccess,
+ true
+ );
+ // TODO(bug 997994): Disallow empty Extensions in responses
+ add_ocsp_test(
+ "ocsp-stapling-empty-extensions.example.com",
+ PRErrorCodeSuccess,
+ true
+ );
+
+ add_ocsp_test(
+ "ocsp-stapling-delegated-included.example.com",
+ PRErrorCodeSuccess,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-delegated-included-last.example.com",
+ PRErrorCodeSuccess,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-delegated-missing.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-delegated-missing-multiple.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-delegated-no-extKeyUsage.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-delegated-from-intermediate.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-delegated-keyUsage-crlSigning.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+ add_ocsp_test(
+ "ocsp-stapling-delegated-wrong-extKeyUsage.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+
+ // ocsp-stapling-expired.example.com and
+ // ocsp-stapling-expired-fresh-ca.example.com are handled in
+ // test_ocsp_stapling_expired.js
+
+ // Check that OCSP responder certificates with key sizes below 1024 bits are
+ // rejected, even when the main certificate chain keys are at least 1024 bits.
+ add_ocsp_test(
+ "keysize-ocsp-delegated.example.com",
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
+ true,
+ true
+ );
+
+ add_ocsp_test(
+ "revoked-ca-cert-used-as-end-entity.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ true
+ );
+}
+
+function check_ocsp_stapling_telemetry() {
+ let histogram = Services.telemetry
+ .getHistogramById("SSL_OCSP_STAPLING")
+ .snapshot();
+ equal(
+ histogram.values[0],
+ 0,
+ "Should have 0 connections for unused histogram bucket 0"
+ );
+ equal(
+ histogram.values[1],
+ 5,
+ "Actual and expected connections with a good response should match"
+ );
+ equal(
+ histogram.values[2],
+ 18,
+ "Actual and expected connections with no stapled response should match"
+ );
+ equal(
+ histogram.values[3] || 0,
+ 0,
+ "Actual and expected connections with an expired response should match"
+ );
+ equal(
+ histogram.values[4],
+ 21,
+ "Actual and expected connections with bad responses should match"
+ );
+ run_next_test();
+}
+
+function run_test() {
+ do_get_profile();
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ // This test may sometimes fail on android due to an OCSP request timing out.
+ // That aspect of OCSP requests is not what we're testing here, so we can just
+ // bump the timeout and hopefully avoid these failures.
+ Services.prefs.setIntPref("security.OCSP.timeoutMilliseconds.soft", 5000);
+
+ let fakeOCSPResponder = new HttpServer();
+ fakeOCSPResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ ok(
+ gExpectOCSPRequest,
+ "Should be getting an OCSP request only when expected"
+ );
+ });
+ fakeOCSPResponder.start(8888);
+
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ add_tests();
+
+ add_test(function() {
+ fakeOCSPResponder.stop(check_ocsp_stapling_telemetry);
+ });
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
new file mode 100644
index 0000000000..4e65baeb01
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -0,0 +1,319 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// In which we connect to a number of domains (as faked by a server running
+// locally) with OCSP stapling enabled to determine that good things happen
+// and bad things don't, specifically with respect to various expired OCSP
+// responses (stapled and otherwise).
+// According to RFC 6066, if a stapled OCSP response can't be satisfactorilly
+// verified, the client should terminate the connection. Unfortunately, due to
+// some bugs where servers will staple any old garbage without verifying it, we
+// can't be this strict in practice. Originally this caveat only applied to
+// expired responses, but recent high-profile failures have caused us to expand
+// this to "try later" responses and responses where the signing certificate
+// doesn't verify successfully.
+
+var gCurrentOCSPResponse = null;
+var gOCSPRequestCount = 0;
+
+function add_ocsp_test(
+ aHost,
+ aExpectedResult,
+ aOCSPResponseToServe,
+ aExpectedRequestCount
+) {
+ add_connection_test(
+ aHost,
+ aExpectedResult,
+ function() {
+ clearOCSPCache();
+ clearSessionCache();
+ gCurrentOCSPResponse = aOCSPResponseToServe;
+ gOCSPRequestCount = 0;
+ },
+ function() {
+ equal(
+ gOCSPRequestCount,
+ aExpectedRequestCount,
+ "Should have made " +
+ aExpectedRequestCount +
+ " fallback OCSP request" +
+ (aExpectedRequestCount == 1 ? "" : "s")
+ );
+ }
+ );
+}
+
+do_get_profile();
+Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
+Services.prefs.setIntPref("security.OCSP.enabled", 1);
+// Sometimes this test will fail on android due to an OCSP request timing out.
+// That aspect of OCSP requests is not what we're testing here, so we can just
+// bump the timeout and hopefully avoid these failures.
+Services.prefs.setIntPref("security.OCSP.timeoutMilliseconds.soft", 5000);
+Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 4);
+var args = [
+ ["good", "default-ee", "unused", 0],
+ ["expiredresponse", "default-ee", "unused", 0],
+ ["oldvalidperiod", "default-ee", "unused", 0],
+ ["revoked", "default-ee", "unused", 0],
+ ["unknown", "default-ee", "unused", 0],
+ ["good", "must-staple-ee", "unused", 0],
+];
+var ocspResponses = generateOCSPResponses(args, "ocsp_certs");
+// Fresh response, certificate is good.
+var ocspResponseGood = ocspResponses[0];
+// Expired response, certificate is good.
+var expiredOCSPResponseGood = ocspResponses[1];
+// Fresh signature, old validity period, certificate is good.
+var oldValidityPeriodOCSPResponseGood = ocspResponses[2];
+// Fresh signature, certificate is revoked.
+var ocspResponseRevoked = ocspResponses[3];
+// Fresh signature, certificate is unknown.
+var ocspResponseUnknown = ocspResponses[4];
+var ocspResponseGoodMustStaple = ocspResponses[5];
+
+// sometimes we expect a result without re-fetch
+var willNotRetry = 1;
+// but sometimes, since a bad response is in the cache, OCSP fetch will be
+// attempted for each validation - in practice, for these test certs, this
+// means 6 requests because various hash algorithm and key size combinations
+// are tried.
+var willRetry = 6;
+
+function run_test() {
+ let ocspResponder = new HttpServer();
+ ocspResponder.registerPrefixHandler("/", function(request, response) {
+ if (gCurrentOCSPResponse) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "application/ocsp-response");
+ response.write(gCurrentOCSPResponse);
+ } else {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ response.write("Internal Server Error");
+ }
+ gOCSPRequestCount++;
+ });
+ ocspResponder.start(8888);
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ // In these tests, the OCSP stapling server gives us a stapled
+ // response based on the host name ("ocsp-stapling-expired" or
+ // "ocsp-stapling-expired-fresh-ca"). We then ensure that we're
+ // properly falling back to fetching revocation information.
+ // For ocsp-stapling-expired.example.com, the OCSP stapling server
+ // staples an expired OCSP response. The certificate has not expired.
+ // For ocsp-stapling-expired-fresh-ca.example.com, the OCSP stapling
+ // server staples an OCSP response with a recent signature but with an
+ // out-of-date validity period. The certificate has not expired.
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGood,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired-fresh-ca.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGood,
+ willNotRetry
+ );
+ // if we can't fetch a more recent response when
+ // given an expired stapled response, we terminate the connection.
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ SEC_ERROR_OCSP_OLD_RESPONSE,
+ expiredOCSPResponseGood,
+ willRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired-fresh-ca.example.com",
+ SEC_ERROR_OCSP_OLD_RESPONSE,
+ expiredOCSPResponseGood,
+ willRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ SEC_ERROR_OCSP_OLD_RESPONSE,
+ oldValidityPeriodOCSPResponseGood,
+ willRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired-fresh-ca.example.com",
+ SEC_ERROR_OCSP_OLD_RESPONSE,
+ oldValidityPeriodOCSPResponseGood,
+ willRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ SEC_ERROR_OCSP_OLD_RESPONSE,
+ null,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ SEC_ERROR_OCSP_OLD_RESPONSE,
+ null,
+ willNotRetry
+ );
+ // Of course, if the newer response indicates Revoked or Unknown,
+ // that status must be returned.
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ ocspResponseRevoked,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired-fresh-ca.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ ocspResponseRevoked,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ ocspResponseUnknown,
+ willRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-expired-fresh-ca.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ ocspResponseUnknown,
+ willRetry
+ );
+
+ // If the response is expired but indicates Revoked or Unknown and a
+ // newer status can't be fetched, the Revoked or Unknown status will
+ // be returned.
+ add_ocsp_test(
+ "ocsp-stapling-revoked-old.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ null,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-unknown-old.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ null,
+ willNotRetry
+ );
+ // If the response is expired but indicates Revoked or Unknown and
+ // a newer status can be fetched and successfully verified, this
+ // should result in a successful certificate verification.
+ add_ocsp_test(
+ "ocsp-stapling-revoked-old.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGood,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-unknown-old.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGood,
+ willNotRetry
+ );
+ // If a newer status can be fetched but it fails to verify, the
+ // Revoked or Unknown status of the expired stapled response
+ // should be returned.
+ add_ocsp_test(
+ "ocsp-stapling-revoked-old.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ expiredOCSPResponseGood,
+ willRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-unknown-old.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ expiredOCSPResponseGood,
+ willRetry
+ );
+
+ // These tests are verifying that an valid but very old response
+ // is rejected as a valid stapled response, requiring a fetch
+ // from the ocsp responder.
+ add_ocsp_test(
+ "ocsp-stapling-ancient-valid.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGood,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-ancient-valid.example.com",
+ SEC_ERROR_REVOKED_CERTIFICATE,
+ ocspResponseRevoked,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-ancient-valid.example.com",
+ SEC_ERROR_OCSP_UNKNOWN_CERT,
+ ocspResponseUnknown,
+ willRetry
+ );
+
+ // Test how OCSP-must-staple (i.e. TLS feature) interacts with stapled OCSP
+ // responses that don't successfully verify.
+ // A strict reading of the relevant RFCs might say that these connections
+ // should all fail because a satisfactory stapled OCSP response is not
+ // present, but for compatibility reasons we fall back to active OCSP fetching
+ // in these situations. If the fetch succeeds, then connection succeeds.
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-expired.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGoodMustStaple,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-try-later.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGoodMustStaple,
+ willNotRetry
+ );
+ add_ocsp_test(
+ "ocsp-stapling-must-staple-invalid-signer.example.com",
+ PRErrorCodeSuccess,
+ ocspResponseGoodMustStaple,
+ willNotRetry
+ );
+
+ add_test(function() {
+ ocspResponder.stop(run_next_test);
+ });
+ add_test(check_ocsp_stapling_telemetry);
+ run_next_test();
+}
+
+function check_ocsp_stapling_telemetry() {
+ let histogram = Services.telemetry
+ .getHistogramById("SSL_OCSP_STAPLING")
+ .snapshot();
+ equal(
+ histogram.values[0] || 0,
+ 0,
+ "Should have 0 connections for unused histogram bucket 0"
+ );
+ equal(
+ histogram.values[1] || 0,
+ 0,
+ "Actual and expected connections with a good response should match"
+ );
+ equal(
+ histogram.values[2] || 0,
+ 0,
+ "Actual and expected connections with no stapled response should match"
+ );
+ equal(
+ histogram.values[3],
+ 22,
+ "Actual and expected connections with an expired response should match"
+ );
+ equal(
+ histogram.values[4],
+ 2,
+ "Actual and expected connections with bad responses should match"
+ );
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js b/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js
new file mode 100644
index 0000000000..4fc8013ea9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_with_intermediate.js
@@ -0,0 +1,48 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// In which we connect to a server that staples an OCSP response for a
+// certificate signed by an intermediate that has an OCSP AIA to ensure
+// that an OCSP request is not made for the intermediate.
+
+var gOCSPRequestCount = 0;
+
+function add_ocsp_test(aHost, aExpectedResult) {
+ add_connection_test(aHost, aExpectedResult, function() {
+ clearOCSPCache();
+ clearSessionCache();
+ });
+}
+
+function run_test() {
+ do_get_profile();
+ Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
+
+ let ocspResponder = new HttpServer();
+ ocspResponder.registerPrefixHandler("/", function(request, response) {
+ gOCSPRequestCount++;
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ let body = "Refusing to return a response";
+ response.bodyOutputStream.write(body, body.length);
+ });
+ ocspResponder.start(8888);
+
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ add_ocsp_test(
+ "ocsp-stapling-with-intermediate.example.com",
+ PRErrorCodeSuccess
+ );
+
+ add_test(function() {
+ ocspResponder.stop(run_next_test);
+ });
+ add_test(function() {
+ equal(gOCSPRequestCount, 0, "No OCSP requests should have been made");
+ run_next_test();
+ });
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_timeout.js b/security/manager/ssl/tests/unit/test_ocsp_timeout.js
new file mode 100644
index 0000000000..e06f064d04
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_timeout.js
@@ -0,0 +1,100 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// This test connects to ocsp-stapling-none.example.com to test that OCSP
+// requests are cancelled if they're taking too long.
+// ocsp-stapling-none.example.com doesn't staple an OCSP response, so
+// connecting to it will cause a request to the OCSP responder. As with all of
+// these tests, the OCSP AIA (i.e. the url of the responder) in the certificate
+// is http://localhost:8888. Since this test opens a TCP socket listening on
+// port 8888 that just accepts connections and then ignores them (with
+// connect/read/write timeouts of 30 seconds), the OCSP requests should cancel
+// themselves. When OCSP hard-fail is enabled, connections will be terminated.
+// Otherwise, they will succeed.
+
+var gSocketListener = {
+ onSocketAccepted(serverSocket, socketTransport) {
+ socketTransport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 30);
+ socketTransport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 30);
+ },
+
+ onStopListening(serverSocket, status) {},
+};
+
+function run_test() {
+ do_get_profile();
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+
+ add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
+
+ let socket = Cc["@mozilla.org/network/server-socket;1"].createInstance(
+ Ci.nsIServerSocket
+ );
+ socket.init(8888, true, -1);
+ socket.asyncListen(gSocketListener);
+
+ add_one_test(false, "security.OCSP.timeoutMilliseconds.soft", 1000);
+ add_one_test(false, "security.OCSP.timeoutMilliseconds.soft", 2000);
+ add_one_test(false, "security.OCSP.timeoutMilliseconds.soft", 4000);
+
+ add_one_test(true, "security.OCSP.timeoutMilliseconds.hard", 3000);
+ add_one_test(true, "security.OCSP.timeoutMilliseconds.hard", 10000);
+ add_one_test(true, "security.OCSP.timeoutMilliseconds.hard", 15000);
+
+ add_test(function() {
+ socket.close();
+ run_next_test();
+ });
+ run_next_test();
+}
+
+function add_one_test(useHardFail, timeoutPrefName, timeoutMilliseconds) {
+ let startTime;
+ add_test(function() {
+ Services.prefs.setBoolPref("security.OCSP.require", useHardFail);
+ Services.prefs.setIntPref(timeoutPrefName, timeoutMilliseconds);
+ startTime = new Date();
+ run_next_test();
+ });
+
+ add_connection_test(
+ "ocsp-stapling-none.example.com",
+ useHardFail ? SEC_ERROR_OCSP_SERVER_ERROR : PRErrorCodeSuccess,
+ clearSessionCache
+ );
+
+ add_test(function() {
+ let endTime = new Date();
+ let timeDifference = endTime - startTime;
+ info(`useHardFail = ${useHardFail}`);
+ info(`startTime = ${startTime.getTime()} (${startTime})`);
+ info(`endTime = ${endTime.getTime()} (${endTime})`);
+ info(`timeDifference = ${timeDifference}ms`);
+ // Date() is not guaranteed to be monotonic, so add extra fuzz time to
+ // prevent intermittent failures (this only appeared to be a problem on
+ // Windows XP). See Bug 1121117.
+ const FUZZ_MS = 300;
+ ok(
+ timeDifference + FUZZ_MS > timeoutMilliseconds,
+ `OCSP timeout should be ~${timeoutMilliseconds}s for ` +
+ `${useHardFail ? "hard" : "soft"}-fail`
+ );
+ // Make sure we didn't wait too long.
+ // (Unfortunately, we probably can't have a tight upper bound on
+ // how long is too long for this test, because we might be running
+ // on slow hardware.)
+ ok(
+ timeDifference < 60000,
+ "Automatic OCSP timeout shouldn't be more than 60s"
+ );
+
+ // Reset state
+ clearOCSPCache();
+ Services.prefs.clearUserPref("security.OCSP.require");
+ Services.prefs.clearUserPref(timeoutPrefName);
+ run_next_test();
+ });
+}
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url.js b/security/manager/ssl/tests/unit/test_ocsp_url.js
new file mode 100644
index 0000000000..b26d74b3b3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url.js
@@ -0,0 +1,122 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// In which we try to validate several ocsp responses, checking in particular
+// if the ocsp url is valid and the path expressed is correctly passed to
+// the caller.
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const SERVER_PORT = 8888;
+
+function failingOCSPResponder() {
+ return getFailingHttpServer(SERVER_PORT, ["www.example.com"]);
+}
+
+function start_ocsp_responder(expectedCertNames, expectedPaths) {
+ return startOCSPResponder(
+ SERVER_PORT,
+ "www.example.com",
+ "test_ocsp_url",
+ expectedCertNames,
+ expectedPaths
+ );
+}
+
+function check_cert_err(cert_name, expected_error) {
+ let cert = constructCertFromFile("test_ocsp_url/" + cert_name + ".pem");
+ return checkCertErrorGeneric(
+ certdb,
+ cert,
+ expected_error,
+ certificateUsageSSLServer
+ );
+}
+
+add_task(async function() {
+ addCertFromFile(certdb, "test_ocsp_url/ca.pem", "CTu,CTu,CTu");
+ addCertFromFile(certdb, "test_ocsp_url/int.pem", ",,");
+
+ // Enabled so that we can force ocsp failure responses.
+ Services.prefs.setBoolPref("security.OCSP.require", true);
+
+ Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+
+ // Note: We don't test the case of a well-formed HTTP URL with an empty port
+ // because the OCSP code would then send a request to port 80, which we
+ // can't use in tests.
+
+ clearOCSPCache();
+ let ocspResponder = failingOCSPResponder();
+ await check_cert_err("bad-scheme", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err("empty-scheme-url", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err("ftp-url", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err("https-url", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = start_ocsp_responder(["hTTp-url"], ["hTTp-url"]);
+ await check_cert_err("hTTp-url", PRErrorCodeSuccess);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err("negative-port", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err("no-host-url", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = start_ocsp_responder(["no-path-url"], [""]);
+ await check_cert_err("no-path-url", PRErrorCodeSuccess);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err(
+ "no-scheme-host-port",
+ SEC_ERROR_CERT_BAD_ACCESS_LOCATION
+ );
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err("no-scheme-url", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ clearOCSPCache();
+ ocspResponder = failingOCSPResponder();
+ await check_cert_err("unknown-scheme", SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+ await stopOCSPResponder(ocspResponder);
+
+ // Note: We currently don't have anything that ensures user:pass sections
+ // weren't sent. The following test simply checks that such sections
+ // don't cause failures.
+ clearOCSPCache();
+ ocspResponder = start_ocsp_responder(["user-pass"], [""]);
+ await check_cert_err("user-pass", PRErrorCodeSuccess);
+ await stopOCSPResponder(ocspResponder);
+});
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem b/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem
new file mode 100644
index 0000000000..15a15922f2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5jCCAc6gAwIBAgIUd7u/VsqA+jFMod09OYNM6n5mIjQwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBUxEzARBgNVBAMMCmJhZC1zY2hlbWUwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjMTAvMC0GCCsGAQUF
+BwEBBCEwHzAdBggrBgEFBQcwAYYRL3d3dy5leGFtcGxlLmNvbS8wDQYJKoZIhvcN
+AQELBQADggEBAKqR40rThjJ83dCqvxt2tqzfVWNsG8/j8Ylkh2PVe4duegymNbtx
+lrGs7e+bR4s3HXwGdtCNqInFuFlZBK1j1LWZTd0o8o/p8VxXk3ZRyWFfaBM96mfM
+EkbWSlmgraoS3OJXemV1mU8HpOnsN/P7jkTeAocf+UqXOuyg08f4qKlAwkkX4DIu
+A6NHuFmPqDjj2uclq/e/xtL1Xah22kIkql00CmMqBdbtKYnYHd4k5wCWRkEiUzlc
+Y8qBnTcamQiNYGgQUOHh/5J0mCrNDmh17A1rnJ4mjkAy4F+bDp2KFwzPtLJKhQeJ
+jLclopcJHcmgn3hFbHMcgDwQxVl6UIZXOes=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem.certspec
new file mode 100644
index 0000000000..12cc072792
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:bad-scheme
+extension:authorityInformationAccess:/www.example.com/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/ca.pem b/security/manager/ssl/tests/unit/test_ocsp_url/ca.pem
new file mode 100644
index 0000000000..45de962523
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUVSmns5EZzUHKobrhxdpy/ZtJMucwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYD
+VR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQATPLfx55bklmCWgjlOjZjD8EpY
+fenI0HAkE0044+KmVlFSpsETog4OK0aZKYKwDysClM/ZipzBpmcZ4ZczM0TbtC7/
+d8nfUmT5R/sXBuMmeNIioAn6J5JaSctKQbsVta9GwIyJUBuYIvccL1c5wUuxV8xf
+CaTrFgKWkDWkhOEY+TN/95l+Cm0Gwc3l+jom5UrPRyw9Cb5AhRiRc+x3HhgJ2hGe
+kKo4K/AfH/jE7U0u4VM+VZTQf5K92aih3f54vPW+yFfMkgFgzHtwIrj1WStknR7u
+yRjBCHfjuoOgQLm1svKtpgnMbRhucCkW+mifY+ZbE51vYMiRlK3sHj9hz3ol
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/ca.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/ca.pem.certspec
new file mode 100644
index 0000000000..d809dbd635
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/ca.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem b/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem
new file mode 100644
index 0000000000..8fe9d26633
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8zCCAdugAwIBAgIUC7eHAf4GqnJKXvCpc9aTUTnkKgEwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBsxGTAXBgNVBAMMEGVtcHR5LXNjaGVtZS11cmwwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
+V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
+0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
+fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
+W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
+TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjODA2MDQG
+CCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYOi8vd3d3LmV4YW1wbGUuY29tOjg4
+ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCNWUubk4gbD3KqPPJW/vNw8LUHoQHjglk3
+6+RdbY/UCNfd6y3aGSrDLV8a/tVnV55UM9ADi4pd35Otu5i6fGunQcoL19QqFzDV
+4dYC3WWGw+AqcF4pgqHBBBVtgDx+kZAS9NwwKCgaagPx7QOWJCDRgNxuol7q1UCI
+vthWRt2PPIh4TCA5GDrFfrEYmVH6KgLKtHHr7TBrW0j3Cy6EIAsMMGXbj7z4DIxr
+Lh0lP9sW87QJlU1cJF8VO1la9yDWvEQsETw5X5SYBdsG4y+AJ6dJI4872/fgAG7G
+DP6nKhXuFvNbbpDTDKAwW2Xu4rVmPmUzv8G2+xis/u8jPhxv9zOS
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem.certspec
new file mode 100644
index 0000000000..e8959653f3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:empty-scheme-url
+extension:authorityInformationAccess:://www.example.com:8888/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem b/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem
new file mode 100644
index 0000000000..d582a2216a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIUPf/7zF8sBjFB4sd3ys4pW8gWQZAwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBIxEDAOBgNVBAMMB2Z0cC11cmwwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg
+2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ
+5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQ
+PdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGj
+DJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8W
+iy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjOzA5MDcGCCsGAQUFBwEB
+BCswKTAnBggrBgEFBQcwAYYbZnRwOi8vd3d3LmV4YW1wbGUuY29tOjg4ODgvMA0G
+CSqGSIb3DQEBCwUAA4IBAQBoJzEDwSyDzVxFuPRiRs3kcxZBa9FKXJK472UKrwvc
+w3xx8+TJPwMZorN/UMe+rxOBMORh1U3bx/nLqfyRB6VxGD2NO/JAUuaXsrEt5cD9
+RekCsFg/nZ4kjnFS8W0ImXGs5MaAEHKyVLaQFaCySpz3lSBd6laJOLpj1qkFIMuv
+Z/AsflArUst/hZr+JbsPORu1TBOAWSUofKcfMEGs7WFZS5TwNOxFe0va1ejht7Bh
+v1eohpaa5c9SA61Z5uD2vvT3+MiDDs7IAqb1m5hHWPVsgqxSbhzEdqwtR68NejLA
+kCeintbVE3FgH/ZC5oT/DYo/9DqAD+CGlbIfORvK7rhJ
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem.certspec
new file mode 100644
index 0000000000..9f50a7d792
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:ftp-url
+extension:authorityInformationAccess:ftp://www.example.com:8888/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem b/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem
new file mode 100644
index 0000000000..87efc65d28
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9zCCAd+gAwIBAgIUFyY16h/GkLcgYETfxjsZRxt38JIwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBMxETAPBgNVBAMMCGhUVHAtdXJsMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo
+4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD
+SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX
+kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx
+owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/
+Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo0QwQjBABggrBgEFBQcB
+AQQ0MDIwMAYIKwYBBQUHMAGGJGhUVHA6Ly93d3cuZXhhbXBsZS5jb206ODg4OC9o
+VFRwLXVybDANBgkqhkiG9w0BAQsFAAOCAQEAg1XOX1zbHNpzMW9ivatvCMi9rMgQ
+ZPFkuu6i4c5diLgo1VbzpM569nsfZnXkWGBv5hhGlFlJPRPc+q9joJMMsebnAlR+
+Q27kAqd/Q9kLQfA5x1Vail+7fin/cI5gR2XbmsXztbX7QYnE1lLSR1BWkJ99LNj4
+lfG5Ofu8CG0dBPfbO4+WtwNnije+J2O3iRc54EEv9kTBaieM34ejxPwg3WY/cUsw
+h6UuRjOJf5Q/uhhRwzPZ4sphsdZcdzPhamIBSF2aCnUiFJlYLDV5eBLbr3GVAuFE
+bJN5zNNCn2lzyo5dxbzuQO6VaGsZcI4/Y2gMn5Q9Gxym59qniVGLCmzTOg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem.certspec
new file mode 100644
index 0000000000..10b1504b29
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:hTTp-url
+extension:authorityInformationAccess:hTTp://www.example.com:8888/hTTp-url
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem b/security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem
new file mode 100644
index 0000000000..16ce146d3c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+jCCAeKgAwIBAgIUL0PWDEnxL9Qrouxs08+j0g981AEwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBQxEjAQBgNVBAMMCWh0dHBzLXVybDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNGMEQwQgYIKwYBBQUH
+AQEENjA0MDIGCCsGAQUFBzABhiZodHRwczovL3d3dy5leGFtcGxlLmNvbTo4ODg4
+L2h0dHBzLXVybDANBgkqhkiG9w0BAQsFAAOCAQEAXzQbnyFm4vlXOrCe9Pt42bt+
+6gg5UZieDPBYIiTBTfUY2e1EII4pxyRJJXjubbfajd+6bxRt38nd0/Ts56WljwiN
+ZefieOoMVsRVrX6APHiMvkkZwmtqwOpAgxICpg/pRqEn2BAFIPB3Lrm5rfGHZhw2
+be+FJp4SbBHlnQUnG4oCl6yzR9hAcRssZL+6ewsWNet02aNJgDAVg00wLxNhCFAt
+ZdtPq+E6l80/LYLYoVY6O+osC5eMR4Z48FoN8NQ8Yu/pe/t8Km/59NdyQxJFneXr
+HXpcpdYsVOr4gzbNFO2552esXwx96tprehFtVc2ZxqnorfMayJz2uO/NVcrPxA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem.certspec
new file mode 100644
index 0000000000..891005bf5c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/https-url.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:https-url
+extension:authorityInformationAccess:https://www.example.com:8888/https-url
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/int.key b/security/manager/ssl/tests/unit/test_ocsp_url/int.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/int.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/int.key.keyspec b/security/manager/ssl/tests/unit/test_ocsp_url/int.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/int.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/int.pem b/security/manager/ssl/tests/unit/test_ocsp_url/int.pem
new file mode 100644
index 0000000000..44e6a36efa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/int.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyjCCAbKgAwIBAgIUNbjVwdBjmiBHgijAyjyGIHuhJqQwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxOTExMjgwMDAwMDBaGA8yMDIyMDIwNTAw
+MDAwMFowDjEMMAoGA1UEAwwDaW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGc
+BptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzC
+a2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8Xg
+uEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK
+9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGP
+mRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsG
+A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAO35vcl2OA9fAarEgTCrS42CL
+5BeNfQLO7QCIdMZBfcIrQ6v5yw7i07pYGptagz/76OwYOsKO1lqb7sr2nTCNovVp
+Co4dCAGQI460C6x3BOG7qyCFgZvf17xXl5filhMZw93LkWaeEIp8JmOTb511i1Bv
+1akdFJ+xW40AuHE7EswLXzN0eu+tVAMJcsuxibK3jTkEdIjFCVwMD/7LC1rcoKcg
+V18MHbh0zcDrXzo/jk8JfjqkOH5eylBtX2DH/9BjFcKzZDvZQFYlhsmADRHOmqvi
+YoEXJj2A6fRzONsNKtQQbmVGvW/rHP7/YIiaAvn0kNb/FDtSqmTcLazSYpRewQ==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/int.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/int.pem.certspec
new file mode 100644
index 0000000000..a7f6d81419
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/int.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/moz.build b/security/manager/ssl/tests/unit/test_ocsp_url/moz.build
new file mode 100644
index 0000000000..7863d3dff4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/moz.build
@@ -0,0 +1,33 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'bad-scheme.pem',
+# 'ca.pem',
+# 'empty-scheme-url.pem',
+# 'ftp-url.pem',
+# 'hTTp-url.pem',
+# 'https-url.pem',
+# 'int.pem',
+# 'negative-port.pem',
+# 'no-host-url.pem',
+# 'no-path-url.pem',
+# 'no-scheme-host-port.pem',
+# 'no-scheme-url.pem',
+# 'unknown-scheme.pem',
+# 'user-pass.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'int.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem b/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem
new file mode 100644
index 0000000000..6827a1b6a7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8jCCAdqgAwIBAgIUe2K7ATAb5CY6+Kltt7J1qxX5Yn0wDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBgxFjAUBgNVBAMMDW5lZ2F0aXZlLXBvcnQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk
+e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg
+KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI
+YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi
+lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL
+HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjOjA4MDYGCCsG
+AQUFBwEBBCowKDAmBggrBgEFBQcwAYYaaHR0cDovL3d3dy5leGFtcGxlLmNvbTot
+MS8wDQYJKoZIhvcNAQELBQADggEBAJbJQ0Qp9K4dFRMu5te+e4MPi+zb9ZHa1nmE
+wQLYKbmJ0/HSP7RjAnk2LqPi3Kej5aAqSWfd9x4pahEro9CEjVSrjD4y7iBTXbM8
+9jTyN01BJED2uHjVwOuaT6dVvx5I+QD7rAByxweQ0aoa14Rsf/odAszWmiburmar
+3LuVBwfB3gyqBk8z6VdfqgUMjs+vi/DesvSZ5IvHwWGFok9uab8v5Ar8L0KoM5gT
+1fAERsxk6W31SsDsSyd5si4dQo6tLJU6jVm2C+rEbt/iOU8CLHs/IozD0jXf1j9S
+XeqFoTBvmJaUqpDdgjzXX/ZACz/s5YffRuDfrRqo7H7cBNaK1r0=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem.certspec
new file mode 100644
index 0000000000..fce6d43848
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:negative-port
+extension:authorityInformationAccess:http://www.example.com:-1/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem b/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem
new file mode 100644
index 0000000000..23b1b101e9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4zCCAcugAwIBAgIUY6ACuApKijUD7p8u8yUDVv95Qr0wDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBYxFDASBgNVBAMMC25vLWhvc3QtdXJsMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoy0wKzApBggrBgEF
+BQcBAQQdMBswGQYIKwYBBQUHMAGGDWh0dHA6Ly86ODg4OC8wDQYJKoZIhvcNAQEL
+BQADggEBAKft+lbYDwaC4MkOViz/t9Zke8sOCkydLJ+gRhx7iaFjQ00VZOURHWrf
+wbOKHaCe3WYofBMlmKARmCB5U8ohvnDqVuCnyt9hXKg3dhWLRGYZOxLEeToTddxc
+OSyl736QFCT1GUtyzRE9fG7RZtSmTMrrhxDUkBf5wuwHboXEiN8pXEQY/L1wxze8
+lbGEBvUnQdTtMm2O9kz0Dg186BBw3Fi4yVcUU9COnvDRMfLt8Yt/MGbckfsdpRSn
+qC80pJ3kFvJkzBF2MwkJVlZMBPFNexBovxAuVk48uyJLDyNp4J397h09JgvCl7Fl
+FMhwIBglV3EvWERD9uq/7Hl1+zLD6GE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem.certspec
new file mode 100644
index 0000000000..4ac76e7eb3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:no-host-url
+extension:authorityInformationAccess:http://:8888/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem b/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem
new file mode 100644
index 0000000000..76bd6d16dc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIUE41GlTjHpx516QFRZ73bImTTVmwwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBYxFDASBgNVBAMMC25vLXBhdGgtdXJsMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozswOTA3BggrBgEF
+BQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly93d3cuZXhhbXBsZS5jb206ODg4
+ODANBgkqhkiG9w0BAQsFAAOCAQEARnXgGgNQHB4JClN93VLOVYzJoPkCtO70HjXc
+nIuhAAwEISvIk9RWeDlE33x5T+g8SFaEOp/pqSZaa9FUr0y7PyxEwfF72JPQMff0
+L6dYtKiKS1+Uh5FNHC38ev4SJdOyGvD8k7og4+VVlitcDtQgVqiKxBoBCzl37wuK
+s1aFx9a7FZcum3sdyFAQCwqpr9qWf9NBbiDdbZDTNyCg7zxk8olsr/DjQLKrAo40
+n2DPfYw+kAfXZgAi1XJjREe4BAaMJw9zCl7Ugsxl53YbUSl1CeicOH5yUiQ3vlCp
+eW2QVjq/HRi6zUrSiOhgIzgVys4LivV3FgKIV/m4hgAGPHYLlw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem.certspec
new file mode 100644
index 0000000000..497bb28796
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:no-path-url
+extension:authorityInformationAccess:http://www.example.com:8888
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem
new file mode 100644
index 0000000000..035792b4af
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3zCCAcegAwIBAgIUD/zYHQI03aXMAELGGvMX7vnOnyQwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMB4xHDAaBgNVBAMME25vLXNjaGVtZS1ob3N0LXBvcnQwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjITAf
+MB0GCCsGAQUFBwEBBBEwDzANBggrBgEFBQcwAYYBLzANBgkqhkiG9w0BAQsFAAOC
+AQEAddd/BUyk8OMHan7wrkL8m3euWJ7+UjxDP1yQL9Gehb34sHiSwr1pE3hfbmHU
+Xc92MX9Fnm/M/uZRoMQqUqU0aqPr91rwwnf47+sd86yNvXxCgjAIN31W4wISDuqU
+4j6dOYPyz0KGlD9qRJorJ4oxhnQfleTcvz3COrPrRypK4QuRJu3v7wR1qFvZP8Iq
+loJ4cY8WwMMpaYecuZHrhfBpG8yO3wihc6IkXqWRWIzBia00v8DNSm+neEF5FTk5
+oCXNJAdh+qWwfzad5gUYGAlPaPdNxbO6G0SOfn7RRGSQj1Nb2Cl2RWzGAcGSGdfL
+2h4H1w0XLwjStLtUM/8eQUvQrA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem.certspec
new file mode 100644
index 0000000000..42a555e411
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:no-scheme-host-port
+extension:authorityInformationAccess:/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem
new file mode 100644
index 0000000000..ca639a4acb
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7TCCAdWgAwIBAgIUBziqKKOqXlKjkoU2XMzG67w9qZQwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBgxFjAUBgNVBAMMDW5vLXNjaGVtZS11cmwwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk
+e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg
+KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI
+YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi
+lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL
+HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjNTAzMDEGCCsG
+AQUFBwEBBCUwIzAhBggrBgEFBQcwAYYVd3d3LmV4YW1wbGUuY29tOjg4ODgvMA0G
+CSqGSIb3DQEBCwUAA4IBAQB2TQq4V9zThvWdOYtaIGY/p3o6OHgtkU2n9RFVxImD
+h+CObFyHbyDpbMMKghZOfdbI9Tl3QMk+d04TnMJRyiw/XhRJqoDY1fKv1N7AT3TM
+Jmx3mR1wD1l9Zo/c4XOywljeypw++Z1DmtwP5TER0u4oLTklBzw9oTSR8snTyfSn
+YA3gQL1jFVWwVKou1Gm4pZTavUdCpjJpF3Bp7QUKRFncTvYViVSibzVAIG+/PiY9
+K/EodGfokxgcKG2GUW6kUe2XadhJ8m2KSWtJrBKXnQ1pY8ZPbWHD/jfQsExRMGJL
+wJx7HqA+8WNBPwqlqIbQejaieRIKOi++QIRuW4QPKA+6
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem.certspec
new file mode 100644
index 0000000000..a82196a6d1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:no-scheme-url
+extension:authorityInformationAccess:www.example.com:8888/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem b/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem
new file mode 100644
index 0000000000..4717e6907a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC7zCCAdegAwIBAgIUST8AviJzhAkMzTRIKaSHBtla3aEwDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBkxFzAVBgNVBAMMDnVua25vd24tc2NoZW1lMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozYwNDAyBggr
+BgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFnR0cDovL3d3dy5leGFtcGxlLmNvbS8w
+DQYJKoZIhvcNAQELBQADggEBALJakuxDp5Gpygxj3MY9tK33Dr/JrxN9NH0GmgcR
+ruhT6DwStz7irOadxVUqNxxSIMf95QBHPEAWihXxn5qyrmH5Pz/M2H6H9xpWiK1r
+fy5UTynUpcqZKVHHfER7tdw371XNdyPeGAfUKQwzO6LS4aBFhDssWxba0ZAt/iQU
+Gqf0X9d73mfEvJZeiMJSqXReITRjTb+0f++0s95IfEhKs3YL9nbP/LHw9XxfmLU/
+2ZUvuAfOTq5U1XnoGIoqg2C8LtQxPXjvezAW/XQNcjQBwyYBxa64HFp5deWv7imf
+QjF9yYYJOBz+BdyXCM+DZL+YJ/Ic6SpuEquVHewvegOpDDE=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem.certspec
new file mode 100644
index 0000000000..0089455398
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:unknown-scheme
+extension:authorityInformationAccess:ttp://www.example.com/
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem b/security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem
new file mode 100644
index 0000000000..018f6d0614
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+jCCAeKgAwIBAgIUc+MjmGMCD8xzGcHP2/uSyWK/9D4wDQYJKoZIhvcNAQEL
+BQAwDjEMMAoGA1UEAwwDaW50MCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUw
+MDAwMDBaMBQxEjAQBgNVBAMMCXVzZXItcGFzczCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNGMEQwQgYIKwYBBQUH
+AQEENjA0MDIGCCsGAQUFBzABhiZodHRwOi8vdXNlcjpwYXNzQHd3dy5leGFtcGxl
+LmNvbTo4ODg4LzANBgkqhkiG9w0BAQsFAAOCAQEAOZmeoErLJLw7l2J8terpOiFf
+68YxI5/QZcRMZhVCMG71XjcZHj6mHLAgKi3Iw2iBeaUnfb5sfg5J7nOXUzebDoHD
+j9W6h0tUxMyYDSdi8aXdvJ4WywPvYlJpWc33O+iDm3cLy224wF14eVD3rZcwroes
+r/wf/86aszBY+xpkwHf/lD6KLNhmaAo0HKq8U4Ys6kJzLpvXMLcwnTs93lCE/VE3
+PYxzndXfRikAQ3j+e8KntIb99NeXMErjnq7vhrcjY7YPHTX60y8QC3fmX60qTAtV
+YWYJusDKXiSDqo1wSHMgpgJ1EEptPsR/DdcCFLfg0PnAvYD8T7LI8jvJt8H8jw==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem.certspec
new file mode 100644
index 0000000000..337e67e5f9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ocsp_url/user-pass.pem.certspec
@@ -0,0 +1,3 @@
+issuer:int
+subject:user-pass
+extension:authorityInformationAccess:http://user:pass@www.example.com:8888/
diff --git a/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem
new file mode 100644
index 0000000000..e963b71b90
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIBHzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFUZXN0
+IEludGVybWVkaWF0ZTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAw
+WjAwMS4wLAYDVQQDDCVBbm90aGVyIEVFIFJldm9rZWQgYnkgcmV2b2NhdGlvbnMu
+dHh0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62
+iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHql
+WqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosq
+Qe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+
+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8i
+b2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoY
+CjXtjQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCZh0XjjDVy1Ev/wSgS850t9KtD
+YbFnqn3dCQjeECmXAH3BXAgb+hZwQEPvNcg1C7tzAowdZ+8h3+SbRLlEL50SkjXQ
+euMrIOSBlJcOQTKYQrpTkbT68MhvFlLsM/BCjQ6lGRIKVGTGv4Q2k8NDF3+9N//Q
+0QaTZ/3gZrmXSOGrbFUjiKrONMpQZhgVW8TMceJHnmOsxO5sQ4VPRVS3oYAbyoWU
+JwWZYN/moFvQ/bE1ZceWhdyspzF3NVy0SP+eLQ/LznkZeQ+5ZVvNftwE8D3w+rYQ
+t8ppnbaSPWQFgyL07OdFmhIpBWODrpjNQOq+ZmPkRBLKHiagw+c73gNbx5ui
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem.certspec
new file mode 100644
index 0000000000..d3ba461104
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt-serial-2.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test Intermediate
+subject:Another EE Revoked by revocations.txt
+serialNumber:31
diff --git a/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem
new file mode 100644
index 0000000000..7146aebe48
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIBTjANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFUZXN0
+IEludGVybWVkaWF0ZTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIwMjA1MDAwMDAw
+WjAwMS4wLAYDVQQDDCVBbm90aGVyIEVFIFJldm9rZWQgYnkgcmV2b2NhdGlvbnMu
+dHh0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62
+iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHql
+WqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosq
+Qe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+
+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8i
+b2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoY
+CjXtjQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAGkBeSA8w5p5awb4t6gWXuXwpO
++N3LQcIAfdWcHuOXaYfWM6tuGRD7fumskMh1fp/7IcqxI0dvNd4iRuWm2IMypQN1
+rlYhkk6Z6N4Z2q8x+vlUl9cdFbXy8q3Iyo7dZflSkk/4vyWpIL6VBZEYs8wc+zJC
+iEFFxjakhs8lNtFgJfcKDJGpb47q2burXP7D9rPkfYhNA8JnjSc2gXFd/wqP25ns
+kmtUyjt/7kCKEtEsn2jLiIWOeASYMOzn10PVIrpX2hMphNw9DDljbIXbEbMat2p4
+GmN7xOLJgCFXZ1c1tGIxxbSsCjeHVjLY1j7D0ovOx5pn/+Fuga6FdWBdeTxC
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem.certspec
new file mode 100644
index 0000000000..10f8f07cce
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/another-ee-revoked-by-revocations-txt.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test Intermediate
+subject:Another EE Revoked by revocations.txt
+serialNumber:78
diff --git a/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem
new file mode 100644
index 0000000000..41d69f6b9a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtzCCAZ+gAwIBAgIBKjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0
+IENBMCIYDzIwMTkxMTI4MDAwMDAwWhgPMjAyMjAyMDUwMDAwMDBaMCgxJjAkBgNV
+BAMMHUVFIFJldm9rZWQgYnkgcmV2b2NhdGlvbnMudHh0MIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFds
+JHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4
+ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25
+iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu3
+4pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42
+yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMA0GCSqGSIb3
+DQEBCwUAA4IBAQCnVdUu30/kzE7JGkVtpRb/obsNkFySebWpr3gNfkHQJcwAXB+6
+jX6zluZW2e+2jdalTmJ7r1jfQnC76P/trz5GyWQGiM1ht50l1yZonTPtC1Qzkzfh
+DPyqUr9PgeZZMdVPD8vaMqxYCqELRrCTxrUmC+vEjGKkBlvtY48ZBcEb/wrSm+4+
+j7vdHDpAxmg6dxZrWlvOBz7TOMwGWlF8loJD3bJYepV1w5N1LB5YR4rc6Peis3LA
+EFHhbUFZlkbY7shChIh1S0PEiTSB5yFopC4iMkALBAJxfdPNKFUyzbVIZolpxA0m
+Bk9uBCR3zv4lpm1JkUR/AOL8zXI7wrRvhh9F
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem.certspec
new file mode 100644
index 0000000000..a2a67d909c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-revocations-txt.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:EE Revoked by revocations.txt
+serialNumber:42
diff --git a/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem
new file mode 100644
index 0000000000..3f6b4651ef
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzTCCAbWgAwIBAgIUZylsQT6JPUjiShyHn9bjtXVZ1BEwDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjArMSkwJwYDVQQDDCBFRSBSZXZva2VkIEJ5IFN1YmplY3QgYW5k
+IFB1YktleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbW
+Qf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pk
+cQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHT
+AjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3
+ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jh
+s3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHV
+A6zaGAo17Y0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAeMJzZMlfsHzM/gTj02WQ
+scQ9rdvDSaPI52IWt0Dwu+Iv+nLCvn6Z2eL6E7DVp613u5y47GwtohTCvNoe9/Nt
+SWKN5WlwaOLbRA5wmt5sbrB6w1z5qt+YDJuVvaw2OmLc7klBpF+ZXHufS7XIPg3e
+xWyqnnJ7ewlYF7fMe8q6WhHlzZ5BYuhNXntpf3dvAA9xMxGYr5CQcXSDF9CqdaAF
+n2P6ZgI7K7CpWzOqLinc+kTyJ8WmcW6WiHIN2ce2XFL49oRFXasjpJtGHmIr5ns4
+yGkHw9WKKzmMG5kgLy5Ar3PSdqAD51l4IGLPIzquhnlo/oTe/Lwi5fZIiHWOmr/z
+1Q==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem.certspec
new file mode 100644
index 0000000000..cadbcf9038
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/ee-revoked-by-subject-and-pubkey.pem.certspec
@@ -0,0 +1,2 @@
+issuer:Test CA
+subject:EE Revoked By Subject and PubKey
diff --git a/security/manager/ssl/tests/unit/test_onecrl/moz.build b/security/manager/ssl/tests/unit/test_onecrl/moz.build
new file mode 100644
index 0000000000..f01134991b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'another-ee-revoked-by-revocations-txt-serial-2.pem',
+# 'another-ee-revoked-by-revocations-txt.pem',
+# 'ee-revoked-by-revocations-txt.pem',
+# 'ee-revoked-by-subject-and-pubkey.pem',
+# 'same-issuer-ee.pem',
+# 'test-int-ee.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem b/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem
new file mode 100644
index 0000000000..d48771c638
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAgmgAwIBAgIUB8T8WSJXA+AFg+rEJQ6d49gVhG4wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAiMSAwHgYDVQQDDBdBbm90aGVyIFRlc3QgRW5kLWVudGl0eTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9
+PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3
+HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3Dg
+Dw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7
+EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SK
+lWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0C
+AwEAAaNbMFkwIwYDVR0RBBwwGoIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tMDIG
+CCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL2xvY2FsaG9zdDo4ODg4
+LzANBgkqhkiG9w0BAQsFAAOCAQEAXEPHIKtQkyoGnJ0TjHvybmECV9OKdmQg34JL
+EZ276YtRZBfcPIlt6v0dTzYy8kS68OTjQ2wAa+jVyJRg24x7Jln0R2XVgyGX7FKJ
+646dlYC5sqXPx1N7bh2Zh9aU6erBBbQZbBC3KGYT6pFxHcddmSFIlkxLREj10lr1
+CTVE7Xq5pel4jE9YS4Su/n0zVmMCLdEh2mOml+L0N2oxFchczJVcogi9iGVSElPx
+PaQBsAtRW7Kl/yCEsENNXqYm/B+QaJd6c+MxYTd+DuKx3TZiT3Pk4xDGExdW+yUv
+BvVaHBtavJYAw+IDzz6MxuUIiJ/wpUXP/CnsK8eD3ADyabxkFA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem.certspec
new file mode 100644
index 0000000000..8b20f03f59
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/same-issuer-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Another Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/test_onecrl/sample_revocations.txt b/security/manager/ssl/tests/unit/test_onecrl/sample_revocations.txt
new file mode 100644
index 0000000000..8983eb65c7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/sample_revocations.txt
@@ -0,0 +1,41 @@
+# a sample revocations.txt for tests
+# Lines starting with '#' are ignored - as are empty lines like this:
+
+# otherwise:
+# non-empty lines are treated as base-64 encoded DER DN data (e.g. issuer or
+# subject)
+# ...unless the line starts with a ' ' (space) character, in which case it's
+# assumed to be base-64 encoded DER serial data, or
+# the line starts with a '\t' (tab) character, in which case it's assumed to
+# be a base-64 encoded SHA256 hash of a public key
+
+# First a serial with no issuer to ensure this doesn't cause parsing to fail
+# (there should be an issuer first, but we need to test this won't fail)
+ dGVzdA==
+# next, let's ensure data that isn't valid base64 doesn't cause breakage.
+ this serial isn't valid base64 (but then there's no issuer anyway)
+Neither is this issuer, though the serial is fine
+ dGVzdA==
+dGVzdA==
+ in this case, issuer is fine but not the serial
+# Next two entries; we can add valid base-64 encoded data for some basic tests:
+# issuer is the base-64 encoded subject DN for the shared Test CA
+# serial is the base-64 encoded integer 42
+MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=
+ Kg==
+# issuer is the base-64 encoded subject DN for the shared Test Intermediate
+# the first serial is the base-64 encoded integer 78
+# the second serial is the base-64 encoded integer 31
+MBwxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRl
+ Tg==
+ Hw==
+ c2VyaWFsMi4=
+# subject is base-64 encoded subject DN "CN=EE Revoked By Subject and PubKey"
+# pubKeyHash is the base-64 encoded sha256 hash of the shared RSA SPKI
+MCsxKTAnBgNVBAMMIEVFIFJldm9rZWQgQnkgU3ViamVjdCBhbmQgUHViS2V5
+ VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8
+# and some more data to ensure that mixed items don't cause parsing failure
+a DN
+ a serial
+ a hash
+ another serial
diff --git a/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem b/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem
new file mode 100644
index 0000000000..b7e0246486
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6jCCAdKgAwIBAgIUE/Ee/Yl0qsBfU62Pa29Gu17/HNMwDQYJKoZIhvcNAQEL
+BQAwHDEaMBgGA1UEAwwRVGVzdCBJbnRlcm1lZGlhdGUwIhgPMjAxOTExMjgwMDAw
+MDBaGA8yMDIyMDIwNTAwMDAwMFowJDEiMCAGA1UEAwwZRUUgaXNzdWVkIGJ5IGlu
+dGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahE
+jhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1
+a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1p
+GrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW
+2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcO
+p2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJR
+xDHVA6zaGAo17Y0CAwEAAaMYMBYwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqG
+SIb3DQEBCwUAA4IBAQAnJwAGpGTVIkzj1a9MyPYLMgy+36xRzYe6WmIgsFPV/51u
+AZZqcxlI+haWQQf13erUoM2HnvQXw+h4cwWTqZ59NqrWeZWFtHiVFCQVip+/1+dX
+IUmOJjgAOsKUtgg/plvp6mtaT/4ko8mA8dmhdOhASYpEocT4veDpovAQD00B2YCx
+2qxRypDTSd4oM1RFf7KsKglFOPmi5lOjfvSGprIMbj6JgoplYk+vAZC53mK5UgF+
+qCnXGJr3dEpGy7uJHX69hBtZWom84a62YqTeUMRsMDcz15XS5L6Gdrc4YW0NHRci
+lId9qfDpC7jhL1mWAPEBHOtKxsrCPjkToPND9WVz
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem.certspec b/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem.certspec
new file mode 100644
index 0000000000..24792d540a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_onecrl/test-int-ee.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test Intermediate
+subject:EE issued by intermediate
+extension:subjectAlternativeName:localhost
diff --git a/security/manager/ssl/tests/unit/test_osclientcerts_module.js b/security/manager/ssl/tests/unit/test_osclientcerts_module.js
new file mode 100644
index 0000000000..8ac31b648a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_osclientcerts_module.js
@@ -0,0 +1,63 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that the platform can load the osclientcerts module.
+
+// Ensure that the appropriate initialization has happened.
+Services.prefs.setBoolPref("security.osclientcerts.autoload", false);
+do_get_profile();
+
+const { TestUtils } = ChromeUtils.import(
+ "resource://testing-common/TestUtils.jsm"
+);
+
+async function check_osclientcerts_module_loaded() {
+ // Loading happens asynchronously, so we have to wait for the notification.
+ await TestUtils.topicObserved("psm:load-os-client-certs-module-task-ran");
+ let testModule = checkPKCS11ModuleExists(
+ "OS Client Cert Module",
+ "osclientcerts"
+ );
+
+ // Check that listing the slots for the osclientcerts module works.
+ let testModuleSlotNames = Array.from(
+ testModule.listSlots(),
+ slot => slot.name
+ );
+ testModuleSlotNames.sort();
+ const expectedSlotNames = [
+ "OS Client Cert Slot (Legacy)",
+ "OS Client Cert Slot (Modern)",
+ ];
+ deepEqual(
+ testModuleSlotNames,
+ expectedSlotNames,
+ "Actual and expected slot names should be equal"
+ );
+}
+
+add_task(async function run_test() {
+ // Check that if we haven't loaded the osclientcerts module, we don't find it
+ // in the module list.
+ checkPKCS11ModuleNotPresent("OS Client Cert Module", "osclientcerts");
+
+ // Check that enabling the pref that loads the osclientcerts module makes it
+ // appear in the module list.
+ Services.prefs.setBoolPref("security.osclientcerts.autoload", true);
+ await check_osclientcerts_module_loaded();
+
+ // Check that disabling the pref that loads the osclientcerts module (thus
+ // unloading the module) makes it disappear from the module list.
+ Services.prefs.setBoolPref("security.osclientcerts.autoload", false);
+ checkPKCS11ModuleNotPresent("OS Client Cert Module", "osclientcerts");
+
+ // Check that loading the module again succeeds.
+ Services.prefs.setBoolPref("security.osclientcerts.autoload", true);
+ await check_osclientcerts_module_loaded();
+
+ // And once more check that unloading succeeds.
+ Services.prefs.setBoolPref("security.osclientcerts.autoload", false);
+ checkPKCS11ModuleNotPresent("OS Client Cert Module", "osclientcerts");
+});
diff --git a/security/manager/ssl/tests/unit/test_oskeystore.js b/security/manager/ssl/tests/unit/test_oskeystore.js
new file mode 100644
index 0000000000..5333cb27ea
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_oskeystore.js
@@ -0,0 +1,413 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods and attributes for interfacing with nsIOSKeyStore.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+
+const LABELS = ["mylabel1", "mylabel2", "mylabel3"];
+
+async function delete_all_secrets() {
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ for (let label of LABELS) {
+ if (await keystore.asyncSecretAvailable(label)) {
+ await keystore.asyncDeleteSecret(label);
+ ok(
+ !(await keystore.asyncSecretAvailable(label)),
+ label + " should be deleted now."
+ );
+ }
+ }
+}
+
+// Test that Firefox handles locking and unlocking of the OSKeyStore properly.
+// Does so by mocking out the actual dialog and "filling in" the
+// password. Also tests that providing an incorrect password will fail (well,
+// technically the user will just get prompted again, but if they then cancel
+// the dialog the overall operation will fail).
+
+var gMockPrompter = {
+ passwordToTry: null,
+ numPrompts: 0,
+
+ // This intentionally does not use arrow function syntax to avoid an issue
+ // where in the context of the arrow function, |this != gMockPrompter| due to
+ // how objects get wrapped when going across xpcom boundaries.
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ this.numPrompts++;
+ equal(
+ text,
+ "Please enter your Primary Password.",
+ "password prompt text should be as expected"
+ );
+ equal(checkMsg, null, "checkMsg should be null");
+ ok(this.passwordToTry, "passwordToTry should be non-null");
+ if (this.passwordToTry == "DontTryThisPassword") {
+ // Cancel the prompt in this case.
+ return false;
+ }
+ password.value = this.passwordToTry;
+ return true;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+var gWindowWatcher = {
+ getNewPrompter: () => gMockPrompter,
+ QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"]),
+};
+
+async function encrypt_decrypt_test() {
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "The secret should not be available yet."
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+ let recoveryPhrase2 = await keystore.asyncGenerateSecret(LABELS[1]);
+ ok(recoveryPhrase2, "A recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x01, 0x00, 0x01]);
+ let ciphertext = "";
+ try {
+ ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+ } catch (e) {
+ ok(false, "Error encrypting " + e);
+ }
+
+ // Decrypting should give us the plaintext bytes again.
+ try {
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text."
+ );
+ } catch (e) {
+ ok(false, "Error decrypting ciphertext " + e);
+ }
+
+ // Decrypting with a wrong key should throw an error.
+ try {
+ await keystore.asyncDecryptBytes(LABELS[1], ciphertext);
+ ok(false, "Decrypting with the wrong key should fail.");
+ } catch (e) {
+ ok(true, "Decrypting with the wrong key should fail " + e);
+ }
+}
+
+add_task(async function() {
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ let windowWatcherCID;
+ if (keystore.isNSSKeyStore) {
+ windowWatcherCID = MockRegistrar.register(
+ "@mozilla.org/embedcomp/window-watcher;1",
+ gWindowWatcher
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+ }
+
+ await delete_all_secrets();
+ await encrypt_decrypt_test();
+ await delete_all_secrets();
+
+ if (
+ AppConstants.platform == "macosx" ||
+ AppConstants.platform == "win" ||
+ AppConstants.platform == "linux"
+ ) {
+ ok(
+ !keystore.isNSSKeyStore,
+ "OS X, Windows, and Linux should use the non-NSS implementation"
+ );
+ }
+
+ if (keystore.isNSSKeyStore) {
+ // If we use the NSS key store implementation test that everything works
+ // when a master password is set.
+ // Set an initial password.
+ let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
+ Ci.nsIPK11TokenDB
+ );
+ let token = tokenDB.getInternalKeyToken();
+ token.initPassword("hunter2");
+
+ // Lock the key store. This should be equivalent to token.logoutSimple()
+ await keystore.asyncLock();
+
+ // Set the correct password so that the test operations should succeed.
+ gMockPrompter.passwordToTry = "hunter2";
+ await encrypt_decrypt_test();
+ ok(
+ gMockPrompter.numPrompts == 1,
+ "There should've been one password prompt."
+ );
+ await delete_all_secrets();
+ }
+
+ // Check lock/unlock behaviour.
+ // Unfortunately we can only test this automatically for the NSS key store.
+ // Uncomment the outer keystore.isNSSKeyStore to test other key stores manually.
+ if (keystore.isNSSKeyStore) {
+ await delete_all_secrets();
+ await encrypt_decrypt_test();
+ await keystore.asyncLock();
+ info("Keystore should be locked. Cancel the login request.");
+ try {
+ if (keystore.isNSSKeyStore) {
+ gMockPrompter.passwordToTry = "DontTryThisPassword";
+ }
+ await keystore.asyncUnlock();
+ ok(false, "Unlock should've rejected.");
+ } catch (e) {
+ ok(
+ e.result == Cr.NS_ERROR_FAILURE || e.result == Cr.NS_ERROR_ABORT,
+ "Rejected login prompt."
+ );
+ }
+ // clean up
+ if (keystore.isNSSKeyStore) {
+ gMockPrompter.passwordToTry = "hunter2";
+ }
+ await delete_all_secrets();
+ }
+});
+
+// Test that if we kick off a background operation and then call a synchronous function on the
+// keystore, we don't deadlock.
+add_task(async function() {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+ try {
+ let text = new Uint8Array(8192);
+ let promise = keystore.asyncEncryptBytes(LABELS[0], text);
+ /* eslint-disable no-unused-expressions */
+ keystore.isNSSKeyStore; // we don't care what this is - we just need to access it
+ /* eslint-enable no-unused-expressions */
+ let ciphertext = await promise;
+ ok(ciphertext, "We should have a ciphertext now.");
+ } catch (e) {
+ ok(false, "Error encrypting " + e);
+ }
+
+ await delete_all_secrets();
+});
+
+// Test that using a recovery phrase works.
+add_task(async function() {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x01, 0x00, 0x01]);
+ let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+
+ await keystore.asyncDeleteSecret(LABELS[0]);
+ // Decrypting should fail after deleting the secret.
+ await keystore
+ .asyncDecryptBytes(LABELS[0], ciphertext)
+ .then(() =>
+ ok(false, "decrypting didn't throw as expected after deleting the secret")
+ )
+ .catch(() =>
+ ok(true, "decrypting threw as expected after deleting the secret")
+ );
+
+ await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text."
+ );
+
+ await delete_all_secrets();
+});
+
+// Test that trying to use a non-base64 recovery phrase fails.
+add_task(async function() {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+ await keystore
+ .asyncRecoverSecret(LABELS[0], "@##$^&*()#$^&*(@#%&*_")
+ .then(() =>
+ ok(false, "base64-decoding non-base64 should have failed but didn't")
+ )
+ .catch(() => ok(true, "base64-decoding non-base64 failed as expected"));
+
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(
+ recoveryPhrase && recoveryPhrase.length > 0,
+ "we should be able to re-use that label to generate a new secret"
+ );
+ await delete_all_secrets();
+});
+
+// Test that re-using a label overwrites any previously-stored secret.
+add_task(async function() {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x66, 0x6f, 0x6f, 0x66]);
+ let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+
+ let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
+
+ // The new secret replaced the old one so we shouldn't be able to decrypt the ciphertext now.
+ await keystore
+ .asyncDecryptBytes(LABELS[0], ciphertext)
+ .then(() =>
+ ok(false, "decrypting without the original key should have failed")
+ )
+ .catch(() =>
+ ok(true, "decrypting without the original key failed as expected")
+ );
+
+ await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text (once we have the original key again)."
+ );
+
+ await delete_all_secrets();
+});
+
+// Test that re-using a label (this time using a recovery phrase) overwrites any previously-stored
+// secret.
+add_task(async function() {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+ let newRecoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(newRecoveryPhrase, "A new recovery phrase should've been created.");
+
+ let text = new Uint8Array([0x66, 0x6f, 0x6f, 0x66]);
+ let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text);
+ ok(ciphertext, "We should have a ciphertext now.");
+
+ await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
+
+ // We recovered the old secret, so decrypting ciphertext that had been encrypted with the newer
+ // key should fail.
+ await keystore
+ .asyncDecryptBytes(LABELS[0], ciphertext)
+ .then(() => ok(false, "decrypting without the new key should have failed"))
+ .catch(() => ok(true, "decrypting without the new key failed as expected"));
+
+ await keystore.asyncRecoverSecret(LABELS[0], newRecoveryPhrase);
+ let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+ ok(
+ plaintext.toString() == text.toString(),
+ "Decrypted plaintext should be the same as text (once we have the new key again)."
+ );
+
+ await delete_all_secrets();
+});
+
+// Test that trying to use recovery phrases that are the wrong size fails.
+add_task(async function() {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"].getService(
+ Ci.nsIOSKeyStore
+ );
+
+ await keystore
+ .asyncRecoverSecret(LABELS[0], "")
+ .then(() => ok(false, "'recovering' with an empty key should have failed"))
+ .catch(() => ok(true, "'recovering' with an empty key failed as expected"));
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+
+ await keystore
+ .asyncRecoverSecret(LABELS[0], "AAAAAA")
+ .then(() =>
+ ok(false, "recovering with a key that is too short should have failed")
+ )
+ .catch(() =>
+ ok(true, "recovering with a key that is too short failed as expected")
+ );
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+
+ await keystore
+ .asyncRecoverSecret(
+ LABELS[0],
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ )
+ .then(() =>
+ ok(false, "recovering with a key that is too long should have failed")
+ )
+ .catch(() =>
+ ok(true, "recovering with a key that is too long failed as expected")
+ );
+ ok(
+ !(await keystore.asyncSecretAvailable(LABELS[0])),
+ "we didn't recover a secret, so the secret shouldn't be available"
+ );
+
+ let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+ ok(
+ recoveryPhrase && recoveryPhrase.length > 0,
+ "we should be able to use that label to generate a new secret"
+ );
+ ok(
+ await keystore.asyncSecretAvailable(LABELS[0]),
+ "the generated secret should now be available"
+ );
+
+ await delete_all_secrets();
+});
diff --git a/security/manager/ssl/tests/unit/test_osreauthenticator.js b/security/manager/ssl/tests/unit/test_osreauthenticator.js
new file mode 100644
index 0000000000..01784a5fef
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_osreauthenticator.js
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests nsIOSReauthenticator.asyncReauthenticateUser().
+// As this gets implemented on various platforms, running this test
+// will result in a prompt from the OS. Consequently, we won't be able
+// to run this in automation, but it will help in testing locally.
+add_task(async function test_asyncReauthenticateUser() {
+ const reauthenticator = Cc[
+ "@mozilla.org/security/osreauthenticator;1"
+ ].getService(Ci.nsIOSReauthenticator);
+ ok(reauthenticator, "nsIOSReauthenticator should be available");
+ const EXPECTED = false; // Change this variable to suit your needs while testing.
+ ok(
+ (
+ await reauthenticator.asyncReauthenticateUser(
+ "this is the prompt string",
+ "this is the caption string",
+ null
+ )
+ )[0] == EXPECTED,
+ "nsIOSReauthenticator.asyncReauthenticateUser should return a boolean array with the first item being the authentication result of: " +
+ EXPECTED
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_password_prompt.js b/security/manager/ssl/tests/unit/test_password_prompt.js
new file mode 100644
index 0000000000..cf4c6db7bf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_password_prompt.js
@@ -0,0 +1,87 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that PSM can successfully ask for a password from the user and relay it
+// back to NSS. Does so by mocking out the actual dialog and "filling in" the
+// password. Also tests that providing an incorrect password will fail (well,
+// technically the user will just get prompted again, but if they then cancel
+// the dialog the overall operation will fail).
+
+var gMockPrompter = {
+ passwordToTry: null,
+ numPrompts: 0,
+
+ // This intentionally does not use arrow function syntax to avoid an issue
+ // where in the context of the arrow function, |this != gMockPrompter| due to
+ // how objects get wrapped when going across xpcom boundaries.
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ this.numPrompts++;
+ if (this.numPrompts > 1) {
+ // don't keep retrying a bad password
+ return false;
+ }
+ equal(
+ text,
+ "Please enter your Primary Password.",
+ "password prompt text should be as expected"
+ );
+ equal(checkMsg, null, "checkMsg should be null");
+ ok(this.passwordToTry, "passwordToTry should be non-null");
+ password.value = this.passwordToTry;
+ return true;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+var gWindowWatcher = {
+ getNewPrompter: () => gMockPrompter,
+ QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"]),
+};
+
+function run_test() {
+ do_get_profile();
+
+ let windowWatcherCID = MockRegistrar.register(
+ "@mozilla.org/embedcomp/window-watcher;1",
+ gWindowWatcher
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+
+ // Set an initial password.
+ let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
+ Ci.nsIPK11TokenDB
+ );
+ let token = tokenDB.getInternalKeyToken();
+ token.initPassword("hunter2");
+ token.logoutSimple();
+
+ // Try with the correct password.
+ gMockPrompter.passwordToTry = "hunter2";
+ // Using nsISecretDecoderRing will cause the password prompt to come up if the
+ // token has a password and is logged out.
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+ sdr.encryptString("poke");
+ equal(gMockPrompter.numPrompts, 1, "should have prompted for password once");
+
+ // Reset state.
+ gMockPrompter.numPrompts = 0;
+ token.logoutSimple();
+
+ // Try with an incorrect password.
+ gMockPrompter.passwordToTry = "*******";
+ throws(
+ () => sdr.encryptString("poke2"),
+ /NS_ERROR_FAILURE/,
+ "logging in with the wrong password should fail"
+ );
+ equal(gMockPrompter.numPrompts, 2, "should have prompted for password twice");
+}
diff --git a/security/manager/ssl/tests/unit/test_pinning.js b/security/manager/ssl/tests/unit/test_pinning.js
new file mode 100644
index 0000000000..89539ac2bf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -0,0 +1,319 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+//
+// For all cases, the acceptable pinset includes only certificates pinned to
+// Test End Entity Cert (signed by issuer testCA). Other certificates
+// are issued by otherCA, which is never in the pinset but is a user-specified
+// trust anchor. This test covers multiple cases:
+//
+// Pinned domain include-subdomains.pinning.example.com includes subdomains
+// - PASS: include-subdomains.pinning.example.com serves a correct cert
+// - PASS: good.include-subdomains.pinning.example.com serves a correct cert
+// - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert
+// not in the pinset
+// - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not
+// in the pinset, but issued by a user-specified trust domain
+//
+// Pinned domain exclude-subdomains.pinning.example.com excludes subdomains
+// - PASS: exclude-subdomains.pinning.example.com serves a correct cert
+// - FAIL: exclude-subdomains.pinning.example.com serves an incorrect cert
+// (TODO: test using verifyCertNow)
+// - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function add_clear_override(host) {
+ add_test(function() {
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride(host, 8443);
+ run_next_test();
+ });
+}
+
+function test_strict() {
+ // In strict mode, we always evaluate pinning data, regardless of whether the
+ // issuer is a built-in trust anchor. We only enforce pins that are not in
+ // test mode.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
+ run_next_test();
+ });
+
+ // Normally this is overridable. But, since we have pinning information for
+ // this host, we don't allow overrides.
+ add_prevented_cert_override_test(
+ "unknownissuer.include-subdomains.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.include-subdomains.pinning.example.com");
+
+ // Issued by otherCA, which is not in the pinset for pinning.example.com.
+ add_connection_test(
+ "bad.include-subdomains.pinning.example.com",
+ MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE
+ );
+
+ // Check that using a FQDN doesn't bypass pinning.
+ add_connection_test(
+ "bad.include-subdomains.pinning.example.com.",
+ MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE
+ );
+ // For some reason this is also navigable (see bug 1118522).
+ add_connection_test(
+ "bad.include-subdomains.pinning.example.com..",
+ MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE
+ );
+
+ // These domains serve certs that match the pinset.
+ add_connection_test(
+ "include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "good.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+
+ // This domain serves a cert that doesn't match the pinset, but subdomains
+ // are excluded.
+ add_connection_test(
+ "sub.exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+
+ // This domain's pinset is exactly the same as
+ // include-subdomains.pinning.example.com, serves the same cert as
+ // bad.include-subdomains.pinning.example.com, but it should pass because
+ // it's in test_mode.
+ add_connection_test("test-mode.pinning.example.com", PRErrorCodeSuccess);
+ // Similarly, this pin is in test-mode, so it should be overridable.
+ add_cert_override_test(
+ "unknownissuer.test-mode.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.test-mode.pinning.example.com");
+}
+
+function test_mitm() {
+ // In MITM mode, we allow pinning to pass if the chain resolves to any
+ // user-specified trust anchor, even if it is not in the pinset.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1);
+ run_next_test();
+ });
+
+ add_connection_test(
+ "include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "good.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+
+ // Normally this is overridable. But, since we have pinning information for
+ // this host, we don't allow overrides (since building a trusted chain fails,
+ // we have no reason to believe this was issued by a user-added trust
+ // anchor, so we can't allow overrides for it).
+ add_prevented_cert_override_test(
+ "unknownissuer.include-subdomains.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.include-subdomains.pinning.example.com");
+
+ // In this case, even though otherCA is not in the pinset, it is a
+ // user-specified trust anchor and the pinning check succeeds.
+ add_connection_test(
+ "bad.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+
+ add_connection_test(
+ "exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "sub.exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test("test-mode.pinning.example.com", PRErrorCodeSuccess);
+ add_cert_override_test(
+ "unknownissuer.test-mode.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.test-mode.pinning.example.com");
+}
+
+function test_disabled() {
+ // Disable pinning.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0);
+ run_next_test();
+ });
+
+ add_connection_test(
+ "include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "good.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "bad.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "sub.exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test("test-mode.pinning.example.com", PRErrorCodeSuccess);
+
+ add_cert_override_test(
+ "unknownissuer.include-subdomains.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.include-subdomains.pinning.example.com");
+ add_cert_override_test(
+ "unknownissuer.test-mode.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.test-mode.pinning.example.com");
+}
+
+function test_enforce_test_mode() {
+ // In enforce test mode, we always enforce all pins, even test pins.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3);
+ run_next_test();
+ });
+
+ // Normally this is overridable. But, since we have pinning information for
+ // this host, we don't allow overrides.
+ add_prevented_cert_override_test(
+ "unknownissuer.include-subdomains.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.include-subdomains.pinning.example.com");
+
+ // Issued by otherCA, which is not in the pinset for pinning.example.com.
+ add_connection_test(
+ "bad.include-subdomains.pinning.example.com",
+ MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE
+ );
+
+ // These domains serve certs that match the pinset.
+ add_connection_test(
+ "include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "good.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+ add_connection_test(
+ "exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+
+ // This domain serves a cert that doesn't match the pinset, but subdomains
+ // are excluded.
+ add_connection_test(
+ "sub.exclude-subdomains.pinning.example.com",
+ PRErrorCodeSuccess
+ );
+
+ // This domain's pinset is exactly the same as
+ // include-subdomains.pinning.example.com, serves the same cert as
+ // bad.include-subdomains.pinning.example.com, is in test-mode, but we are
+ // enforcing test mode pins.
+ add_connection_test(
+ "test-mode.pinning.example.com",
+ MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE
+ );
+ // Normally this is overridable. But, since we have pinning information for
+ // this host (and since we're enforcing test mode), we don't allow overrides.
+ add_prevented_cert_override_test(
+ "unknownissuer.test-mode.pinning.example.com",
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+ SEC_ERROR_UNKNOWN_ISSUER
+ );
+ add_clear_override("unknownissuer.test-mode.pinning.example.com");
+}
+
+function check_pinning_telemetry() {
+ let prod_histogram = Services.telemetry
+ .getHistogramById("CERT_PINNING_RESULTS")
+ .snapshot();
+ let test_histogram = Services.telemetry
+ .getHistogramById("CERT_PINNING_TEST_RESULTS")
+ .snapshot();
+ // Because all of our test domains are pinned to user-specified trust
+ // anchors, effectively only strict mode and enforce test-mode get evaluated
+ equal(
+ prod_histogram.values[0],
+ 4,
+ "Actual and expected prod (non-Mozilla) failure count should match"
+ );
+ equal(
+ prod_histogram.values[1],
+ 6,
+ "Actual and expected prod (non-Mozilla) success count should match"
+ );
+ equal(
+ test_histogram.values[0],
+ 2,
+ "Actual and expected test (non-Mozilla) failure count should match"
+ );
+ equal(
+ test_histogram.values[1] || 0,
+ 0,
+ "Actual and expected test (non-Mozilla) success count should match"
+ );
+
+ run_next_test();
+}
+
+function run_test() {
+ // Ensure that static pinning works when HPKP is disabled.
+ Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", false);
+
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+ // Add a user-specified trust anchor.
+ addCertFromFile(certdb, "bad_certs/other-test-ca.pem", "CTu,u,u");
+
+ test_strict();
+ test_mitm();
+ test_disabled();
+ test_enforce_test_mode();
+
+ add_test(function() {
+ check_pinning_telemetry();
+ });
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_pkcs11_module.js b/security/manager/ssl/tests/unit/test_pkcs11_module.js
new file mode 100644
index 0000000000..abad2dbb54
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_module.js
@@ -0,0 +1,58 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods and attributes for interfacing with a PKCS #11 module and
+// the module database.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+
+const gModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+);
+
+function run_test() {
+ // Check that if we have never added the test module, that we don't find it
+ // in the module list.
+ checkPKCS11ModuleNotPresent("PKCS11 Test Module", "pkcs11testmodule");
+
+ // Check that adding the test module makes it appear in the module list.
+ let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ libraryFile.append("pkcs11testmodule");
+ libraryFile.append(ctypes.libraryName("pkcs11testmodule"));
+ loadPKCS11Module(libraryFile, "PKCS11 Test Module", true);
+ let testModule = checkPKCS11ModuleExists(
+ "PKCS11 Test Module",
+ "pkcs11testmodule"
+ );
+
+ // Check that listing the slots for the test module works.
+ let testModuleSlotNames = Array.from(
+ testModule.listSlots(),
+ slot => slot.name
+ );
+ testModuleSlotNames.sort();
+ const expectedSlotNames = [
+ "Empty PKCS11 Slot",
+ "Test PKCS11 Slot",
+ "Test PKCS11 Slot 二",
+ ];
+ deepEqual(
+ testModuleSlotNames,
+ expectedSlotNames,
+ "Actual and expected slot names should be equal"
+ );
+
+ // Check that deleting the test module makes it disappear from the module list.
+ let pkcs11ModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ pkcs11ModuleDB.deleteModule("PKCS11 Test Module");
+ checkPKCS11ModuleNotPresent("PKCS11 Test Module", "pkcs11testmodule");
+
+ // Check miscellaneous module DB methods and attributes.
+ ok(!gModuleDB.canToggleFIPS, "It should NOT be possible to toggle FIPS");
+ ok(!gModuleDB.isFIPSEnabled, "FIPS should not be enabled");
+}
diff --git a/security/manager/ssl/tests/unit/test_pkcs11_moduleDB.js b/security/manager/ssl/tests/unit/test_pkcs11_moduleDB.js
new file mode 100644
index 0000000000..52795ccd3c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_moduleDB.js
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that adding modules with invalid names are prevented.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+
+const gModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+);
+
+function run_test() {
+ let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ libraryFile.append("pkcs11testmodule");
+ libraryFile.append(ctypes.libraryName("pkcs11testmodule"));
+ ok(libraryFile.exists(), "The pkcs11testmodule file should exist");
+
+ let moduleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ throws(
+ () => moduleDB.addModule("Root Certs", libraryFile.path, 0, 0),
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "Adding a module named 'Root Certs' should fail."
+ );
+ throws(
+ () => moduleDB.addModule("", libraryFile.path, 0, 0),
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "Adding a module with an empty name should fail."
+ );
+
+ let bundle = Services.strings.createBundle(
+ "chrome://pipnss/locale/pipnss.properties"
+ );
+ let rootsModuleName = bundle.GetStringFromName("RootCertModuleName");
+ let foundRootsModule = false;
+ for (let module of moduleDB.listModules()) {
+ if (module.name == rootsModuleName) {
+ foundRootsModule = true;
+ break;
+ }
+ }
+ ok(
+ foundRootsModule,
+ "Should be able to find builtin roots module by localized name."
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js b/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js
new file mode 100644
index 0000000000..724b98d664
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_safe_mode.js
@@ -0,0 +1,61 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+"use strict";
+
+// In safe mode, PKCS#11 modules should not be loaded. This test tests this by
+// simulating starting in safe mode and then attempting to load a module.
+
+function run_test() {
+ do_get_profile();
+
+ // Simulate starting in safe mode.
+ let xulRuntime = {
+ inSafeMode: true,
+ logConsoleErrors: true,
+ OS: "XPCShell",
+ XPCOMABI: "noarch-spidermonkey",
+ invalidateCachesOnRestart: function invalidateCachesOnRestart() {
+ // Do nothing
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIXULRuntime"]),
+ };
+
+ let xulRuntimeFactory = {
+ createInstance(outer, iid) {
+ if (outer != null) {
+ throw Components.Exception("", Cr.NS_ERROR_NO_AGGREGATION);
+ }
+ return xulRuntime.QueryInterface(iid);
+ },
+ };
+
+ let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ const XULRUNTIME_CONTRACTID = "@mozilla.org/xre/runtime;1";
+ const XULRUNTIME_CID = Components.ID(
+ "{f0f0b230-5525-4127-98dc-7bca39059e70}"
+ );
+ registrar.registerFactory(
+ XULRUNTIME_CID,
+ "XULRuntime",
+ XULRUNTIME_CONTRACTID,
+ xulRuntimeFactory
+ );
+
+ // When starting in safe mode, the test module should fail to load.
+ let pkcs11ModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ let libraryName = ctypes.libraryName("pkcs11testmodule");
+ let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ libraryFile.append("pkcs11testmodule");
+ libraryFile.append(libraryName);
+ ok(libraryFile.exists(), "The pkcs11testmodule file should exist");
+ throws(
+ () =>
+ pkcs11ModuleDB.addModule("PKCS11 Test Module", libraryFile.path, 0, 0),
+ /NS_ERROR_FAILURE/,
+ "addModule should throw when in safe mode"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_pkcs11_slot.js b/security/manager/ssl/tests/unit/test_pkcs11_slot.js
new file mode 100644
index 0000000000..993a09d951
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_slot.js
@@ -0,0 +1,135 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods and attributes for interfacing with a PKCS #11 slot.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+
+function find_slot_by_name(module, name) {
+ for (let slot of module.listSlots()) {
+ if (slot.name == name) {
+ return slot;
+ }
+ }
+ return null;
+}
+
+function find_module_by_name(moduleDB, name) {
+ for (let slot of moduleDB.listModules()) {
+ if (slot.name == name) {
+ return slot;
+ }
+ }
+ return null;
+}
+
+function run_test() {
+ let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+ libraryFile.append("pkcs11testmodule");
+ libraryFile.append(ctypes.libraryName("pkcs11testmodule"));
+ loadPKCS11Module(libraryFile, "PKCS11 Test Module", false);
+
+ let moduleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
+ Ci.nsIPKCS11ModuleDB
+ );
+ let testModule = find_module_by_name(moduleDB, "PKCS11 Test Module");
+ notEqual(testModule, null, "should be able to find test module");
+ let testSlot = find_slot_by_name(testModule, "Test PKCS11 Slot 二");
+ notEqual(testSlot, null, "should be able to find 'Test PKCS11 Slot 二'");
+
+ equal(
+ testSlot.name,
+ "Test PKCS11 Slot 二",
+ "Actual and expected name should match"
+ );
+ equal(
+ testSlot.desc,
+ "Test PKCS11 Slot 二",
+ "Actual and expected description should match"
+ );
+ equal(
+ testSlot.manID,
+ "Test PKCS11 Manufacturer ID",
+ "Actual and expected manufacturer ID should match"
+ );
+ equal(
+ testSlot.HWVersion,
+ "0.0",
+ "Actual and expected hardware version should match"
+ );
+ equal(
+ testSlot.FWVersion,
+ "0.0",
+ "Actual and expected firmware version should match"
+ );
+ equal(
+ testSlot.status,
+ Ci.nsIPKCS11Slot.SLOT_READY,
+ "Actual and expected status should match"
+ );
+ equal(
+ testSlot.tokenName,
+ "Test PKCS11 Tokeñ 2 Label",
+ "Actual and expected token name should match"
+ );
+
+ let testToken = testSlot.getToken();
+ notEqual(testToken, null, "getToken() should succeed");
+ equal(
+ testToken.tokenName,
+ "Test PKCS11 Tokeñ 2 Label",
+ "Spot check: the actual and expected test token names should be equal"
+ );
+ ok(!testToken.isInternalKeyToken, "This token is not the internal key token");
+
+ testSlot = find_slot_by_name(testModule, "Empty PKCS11 Slot");
+ notEqual(testSlot, null, "should be able to find 'Empty PKCS11 Slot'");
+ equal(testSlot.tokenName, null, "Empty slot is empty");
+ equal(
+ testSlot.status,
+ Ci.nsIPKCS11Slot.SLOT_NOT_PRESENT,
+ "Actual and expected status should match"
+ );
+
+ let bundle = Services.strings.createBundle(
+ "chrome://pipnss/locale/pipnss.properties"
+ );
+ let internalModule = find_module_by_name(
+ moduleDB,
+ "NSS Internal PKCS #11 Module"
+ );
+ notEqual(internalModule, null, "should be able to find internal module");
+ let cryptoSlot = find_slot_by_name(
+ internalModule,
+ bundle.GetStringFromName("TokenDescription")
+ );
+ notEqual(cryptoSlot, "should be able to find internal crypto slot");
+ equal(
+ cryptoSlot.desc,
+ bundle.GetStringFromName("SlotDescription"),
+ "crypto slot should have expected 'desc'"
+ );
+ equal(
+ cryptoSlot.manID,
+ bundle.GetStringFromName("ManufacturerID"),
+ "crypto slot should have expected 'manID'"
+ );
+ let keySlot = find_slot_by_name(
+ internalModule,
+ bundle.GetStringFromName("PrivateTokenDescription")
+ );
+ notEqual(keySlot, "should be able to find internal key slot");
+ equal(
+ keySlot.desc,
+ bundle.GetStringFromName("PrivateSlotDescription"),
+ "key slot should have expected 'desc'"
+ );
+ equal(
+ keySlot.manID,
+ bundle.GetStringFromName("ManufacturerID"),
+ "key slot should have expected 'manID'"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_pkcs11_token.js b/security/manager/ssl/tests/unit/test_pkcs11_token.js
new file mode 100644
index 0000000000..06096eda96
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_token.js
@@ -0,0 +1,149 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods and attributes for interfacing with a PKCS #11 token, using
+// the internal key token.
+// We don't use either of the test tokens in the test PKCS #11 module because:
+// 1. Test token 1 cyclically inserts and removes itself in a tight loop.
+// Using token 1 would complicate the test and introduce intermittent
+// failures.
+// 2. Neither test token implements login or password related functionality.
+// We want to test such functionality.
+// 3. Using the internal token lets us actually test the internal token works
+// as expected.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+
+function checkBasicAttributes(token) {
+ let bundle = Services.strings.createBundle(
+ "chrome://pipnss/locale/pipnss.properties"
+ );
+
+ let expectedTokenName = bundle.GetStringFromName("PrivateTokenDescription");
+ equal(
+ token.tokenName,
+ expectedTokenName,
+ "Actual and expected name should match"
+ );
+ equal(
+ token.tokenManID,
+ bundle.GetStringFromName("ManufacturerID"),
+ "Actual and expected manufacturer ID should match"
+ );
+ equal(
+ token.tokenHWVersion,
+ "0.0",
+ "Actual and expected hardware version should match"
+ );
+ equal(
+ token.tokenFWVersion,
+ "0.0",
+ "Actual and expected firmware version should match"
+ );
+ equal(
+ token.tokenSerialNumber,
+ "0000000000000000",
+ "Actual and expected serial number should match"
+ );
+}
+
+/**
+ * Checks the various password related features of the given token.
+ * The token should already have been init with a password and be logged into.
+ * The password of the token will be reset after calling this function.
+ *
+ * @param {nsIPK11Token} token
+ * The token to test.
+ * @param {String} initialPW
+ * The password that the token should have been init with.
+ */
+function checkPasswordFeaturesAndResetPassword(token, initialPW) {
+ ok(
+ !token.needsUserInit,
+ "Token should not need user init after setting a password"
+ );
+ ok(
+ token.hasPassword,
+ "Token should have a password after setting a password"
+ );
+
+ ok(
+ token.checkPassword(initialPW),
+ "checkPassword() should succeed if the correct initial password is given"
+ );
+ token.changePassword(initialPW, "newPW ÿ 一二三");
+ ok(
+ token.checkPassword("newPW ÿ 一二三"),
+ "checkPassword() should succeed if the correct new password is given"
+ );
+
+ ok(
+ !token.checkPassword("wrongPW"),
+ "checkPassword() should fail if an incorrect password is given"
+ );
+ ok(
+ !token.isLoggedIn(),
+ "Token should be logged out after an incorrect password was given"
+ );
+ ok(
+ !token.needsUserInit,
+ "Token should still be init with a password even if an incorrect " +
+ "password was given"
+ );
+
+ token.reset();
+ ok(token.needsUserInit, "Token should need password init after reset");
+ ok(!token.hasPassword, "Token should not have a password after reset");
+ ok(!token.isLoggedIn(), "Token should be logged out of after reset");
+}
+
+function run_test() {
+ let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
+ Ci.nsIPK11TokenDB
+ );
+ let token = tokenDB.getInternalKeyToken();
+ notEqual(token, null, "The internal token should be present");
+ ok(
+ token.isInternalKeyToken,
+ "The internal token should be represented as such"
+ );
+
+ checkBasicAttributes(token);
+
+ ok(!token.isLoggedIn(), "Token should not be logged into yet");
+ // Test that attempting to log out even when the token was not logged into
+ // does not result in an error.
+ token.logoutSimple();
+ ok(!token.isLoggedIn(), "Token should still not be logged into");
+ ok(
+ !token.hasPassword,
+ "Token should not have a password before it has been set"
+ );
+
+ let initialPW = "foo 1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/? 一二三";
+ token.initPassword(initialPW);
+ token.login(/* force */ false);
+ ok(token.isLoggedIn(), "Token should now be logged into");
+
+ checkPasswordFeaturesAndResetPassword(token, initialPW);
+
+ // We reset the password previously, so we need to initialize again.
+ token.initPassword("arbitrary");
+ ok(
+ token.isLoggedIn(),
+ "Token should be logged into after initializing password again"
+ );
+ token.logoutSimple();
+ ok(
+ !token.isLoggedIn(),
+ "Token should be logged out after calling logoutSimple()"
+ );
+
+ ok(
+ token.needsLogin(),
+ "The internal token should always need authentication"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_pkcs11_tokenDB.js b/security/manager/ssl/tests/unit/test_pkcs11_tokenDB.js
new file mode 100644
index 0000000000..127c533439
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_tokenDB.js
@@ -0,0 +1,20 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods for interfacing with the PKCS #11 token database.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+
+function run_test() {
+ let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
+ Ci.nsIPK11TokenDB
+ );
+
+ notEqual(
+ tokenDB.getInternalKeyToken(),
+ null,
+ "The internal token should be non-null"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_sanctions/apple-ist-ca-8-g1-intermediate.pem b/security/manager/ssl/tests/unit/test_sanctions/apple-ist-ca-8-g1-intermediate.pem
new file mode 100644
index 0000000000..29428ad015
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/apple-ist-ca-8-g1-intermediate.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAtugAwIBAgIQE1Iuv8HdXOEe8nZAdR/n3zAKBggqhkjOPQQDAzCBmDEL
+MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
+KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
+MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
+eSAtIEcyMB4XDTE2MDYwOTAwMDAwMFoXDTMxMDYwODIzNTk1OVowYjEcMBoGA1UE
+AwwTQXBwbGUgSVNUIENBIDggLSBHMTEgMB4GA1UECwwXQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMFkwEwYH
+KoZIzj0CAQYIKoZIzj0DAQcDQgAELVSOaLAQE+/0LdvYCbJD6J1lmW40uNSXyY7J
+1qgiNzLIcWDusPHyxWT2ukdf/OYHeDIt9sqAIMn9cPhykyGIRaOCATowggE2MBIG
+A1UdEwEB/wQIMAYBAf8CAQAwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2cuc3lt
+Y2IuY29tL0dlb1RydXN0UENBLUcyLmNybDAOBgNVHQ8BAf8EBAMCAQYwLgYIKwYB
+BQUHAQEEIjAgMB4GCCsGAQUFBzABhhJodHRwOi8vZy5zeW1jZC5jb20wSQYDVR0g
+BEIwQDA+BgZngQwBAgIwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2VvdHJ1
+c3QuY29tL3Jlc291cmNlcy9jcHMwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
+BwMCMB0GA1UdDgQWBBTDxKRYBWPXgwa6lo3cso8y9ru3QTAfBgNVHSMEGDAWgBQV
+XzVXUVX7JbKtA2n8AaP6vhFV1TAKBggqhkjOPQQDAwNnADBkAjBH2jMNybjCk3Ts
+OidXxJX9YDPMd5S3KDCv8vyTdJGhtoly7fQJRNv5rnVz+6YGfsMCMEp6wyheL7NK
+mqavsduix2R+j1B3wRjelzJYgXzgM3nwhQKKlJWxpF7IGHuva1taxg==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/default-ee.key b/security/manager/ssl/tests/unit/test_sanctions/default-ee.key
new file mode 100644
index 0000000000..8af23e068d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/default-ee.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY----- \ No newline at end of file
diff --git a/security/manager/ssl/tests/unit/test_sanctions/default-ee.key.keyspec b/security/manager/ssl/tests/unit/test_sanctions/default-ee.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/default-ee.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_sanctions/default-ee.pem b/security/manager/ssl/tests/unit/test_sanctions/default-ee.pem
new file mode 100644
index 0000000000..c85c051004
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/default-ee.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDiTCCAnGgAwIBAgIUUwG2e1zCLPYPQc2aSZ4UsI/GiK0wDQYJKoZIhvcNAQEL
+BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAiGA8yMDE5MTEyODAwMDAwMFoYDzIwMjIw
+MjA1MDAwMDAwWjAaMRgwFgYDVQQDDA9UZXN0IEVuZC1lbnRpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq0
+7PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D
+/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuw
+JJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyX
+rZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWd
+q5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjgcow
+gccwgZAGA1UdEQSBiDCBhYIJbG9jYWxob3N0gg0qLmV4YW1wbGUuY29tghUqLnBp
+bm5pbmcuZXhhbXBsZS5jb22CKCouaW5jbHVkZS1zdWJkb21haW5zLnBpbm5pbmcu
+ZXhhbXBsZS5jb22CKCouZXhjbHVkZS1zdWJkb21haW5zLnBpbm5pbmcuZXhhbXBs
+ZS5jb20wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzABhhZodHRwOi8vbG9jYWxo
+b3N0Ojg4ODgvMA0GCSqGSIb3DQEBCwUAA4IBAQCbcbjTQmzRu++LJ/R1KjA99THZ
+aRGG7u0knPs40bz+rIOAR7SllYTvZ1g5HanNG3GZ5+DExVmVtixcrqJFTV0BJsi0
+rv8XR4F3Cdict+rJ+hCSBqu6BGNWdptsaSPiSm+eL//tgjGY1zm9ln1B/OvTYA/n
+f+OV07v44pwRBUe8C9Awb2J3KMHATPciKTk0Pwmh0jXi4FN9ehG1rXZMY2daHoKq
+hzbBc8EaGzPPAyFumHd6wNqWX+/chEtT00SlcJw/lbQZnK8XvUSOhRuUeRdCM5wX
+3w+Gy4P/FrI5tePoR9606GR6plC8QZxT3+Z6lTyCHz3I05+PNXwfmZH3ABSg
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/default-ee.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/default-ee.pem.certspec
new file mode 100644
index 0000000000..554339ff52
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/default-ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Test CA
+subject:Test End-entity
+extension:subjectAlternativeName:localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/test_sanctions/gspe72-4-ssl-ls-apple-com.pem b/security/manager/ssl/tests/unit/test_sanctions/gspe72-4-ssl-ls-apple-com.pem
new file mode 100644
index 0000000000..ce7c05fd0c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/gspe72-4-ssl-ls-apple-com.pem
@@ -0,0 +1,38 @@
+-----BEGIN CERTIFICATE-----
+MIIGuzCCBmCgAwIBAgIQJwfBKDdrh96xNA/Wr1LyATAKBggqhkjOPQQDAjBiMRww
+GgYDVQQDDBNBcHBsZSBJU1QgQ0EgOCAtIEcxMSAwHgYDVQQLDBdDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMw
+HhcNMTkwMzExMjA1MDE0WhcNMjEwNDA5MjA1MDE0WjCBgjEiMCAGA1UEAwwZZ3Nw
+ZTcyLTQtc3NsLmxzLmFwcGxlLmNvbTElMCMGA1UECwwcbWFuYWdlbWVudDppZG1z
+Lmdyb3VwLjY2NTAzNTETMBEGA1UECgwKQXBwbGUgSW5jLjETMBEGA1UECAwKQ2Fs
+aWZvcm5pYTELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATr
+NqVhtJlzuIqo/qrkTFUFe8IRKh9sOr6HXrbvE1vUIbUcWcfhWs8dczP9RBCw6TNI
+TGHAhuG2RZ/f9IrUjSldo4IE1TCCBNEwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAW
+gBTDxKRYBWPXgwa6lo3cso8y9ru3QTB+BggrBgEFBQcBAQRyMHAwNAYIKwYBBQUH
+MAKGKGh0dHA6Ly9jZXJ0cy5hcHBsZS5jb20vYXBwbGVpc3RjYThnMS5kZXIwOAYI
+KwYBBQUHMAGGLGh0dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtYXBwbGVpc3Rj
+YThnMTA1MCQGA1UdEQQdMBuCGWdzcGU3Mi00LXNzbC5scy5hcHBsZS5jb20wgf4G
+A1UdIASB9jCB8zCB8AYKKoZIhvdjZAULBDCB4TCBpAYIKwYBBQUHAgIwgZcMgZRS
+ZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz
+IGFjY2VwdGFuY2Ugb2YgYW55IGFwcGxpY2FibGUgdGVybXMgYW5kIGNvbmRpdGlv
+bnMgb2YgdXNlIGFuZC9vciBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVu
+dHMuMDgGCCsGAQUFBwICMCwMKmh0dHA6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmlj
+YXRlYXV0aG9yaXR5LzAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwNwYD
+VR0fBDAwLjAsoCqgKIYmaHR0cDovL2NybC5hcHBsZS5jb20vYXBwbGVpc3RjYThn
+MS5jcmwwHQYDVR0OBBYEFNNlLsYlY4okSG2S2Gl2UMdPlkbzMA4GA1UdDwEB/wQE
+AwIDiDCCAnAGCisGAQQB1nkCBAIEggJgBIICXAJaAHYAu9nfvB+KcbWTlCOXqpJ7
+RzhXlQqrUugakJZkNo4e0YUAAAFpbo5SYgAABAMARzBFAiATXJMIgS9+f9P92SWY
+0oYi/vRuE4hTvLANoLv0FLE7WwIhAJBMLXnqV3k8R7yWdISnvRZF0/QBmMK632Cp
+N/kcpd3SAHYApLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BAAAAFpbo5S
+WQAABAMARzBFAiBCsSdFBiDjDEMsKdI8FyXD0aS6fCZ9bAuNYIGNPfAGhQIhAJ/R
+mCOrVp/GBOg0CLYrjJcBbIAGditNOg8Kd8WTP85fAHYA7ku9t3XOYLrhQmkfq+Ge
+ZqMPfl+wctiDAMR7iXqo/csAAAFpbo5SXgAABAMARzBFAiEAvzTu/MJBrBuyhPCj
+6R6qtyTLyTrin/HmOttlH6iJ4IsCICuqq2EK9+k8w0c6BuBtfK1BDw/lUUGqtSDZ
+svcDKVkIAHcAVYHUwhaQNgFK6gubVzxT8MDkOHhwJQgXL6OqHQcT0wwAAAFpbo5U
+5wAABAMASDBGAiEA92TvuiKRNcHjkIZlCo2TBW/ZFWvOoZwgoC6mB1CHmTcCIQDm
+cHuw1Ap1MUC6W+s76Im/js9KBumICSxByBkjxI1w6AB3AG9Tdqwx8DEZ2JkApFEV
+/3cVHBHZAsEAKQaNsgiaN9kTAAABaW6OVTEAAAQDAEgwRgIhAPaUtnaDmwkQvO3i
+nzgdo7AOVh1DEco+axckyrcxJgAKAiEA++0eb4HftQ9rUh9oZNYvqnyVSwNZfZwW
+5DYuKcTTRhswCgYIKoZIzj0EAwIDSQAwRgIhAJVASCnbb+d9zglT5b7wirXUxvEu
+nkHBAsB7VcVNqBEfAiEAiAlD40/WXTh31J41SVyb4CaQDTn0V+MstUnS1h+M1Cg=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/moz.build b/security/manager/ssl/tests/unit/test_sanctions/moz.build
new file mode 100644
index 0000000000..7318333a99
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'default-ee.pem',
+# 'symantec-ee-from-allowlist-after-cutoff.pem',
+# 'symantec-ee-from-allowlist-before-cutoff.pem',
+# 'symantec-ee-not-allowlisted-after-cutoff.pem',
+# 'symantec-ee-not-allowlisted-before-cutoff.pem',
+# 'symantec-intermediate-other.pem',
+# 'symantec-intermediate-other-crossigned.pem',
+# 'symantec-intermediate-allowlisted.pem',
+# 'symantec-test-ca.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem
new file mode 100644
index 0000000000..95316b235e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAiagAwIBAgIUN+Dcp+HeMvZhIJ3BD5af4bwrj4IwDQYJKoZIhvcNAQEL
+BQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMT
+HEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwIhgPMjAxNjA2MDEwMDAwMDBa
+GA8yMDUwMDEwMTAwMDAwMFowKTEnMCUGA1UEAwweZWUtZnJvbS1hbGxvd2xpc3Qt
+YWZ0ZXItY3V0b2ZmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohR
+qESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+Kv
+WnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+
+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPv
+JxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5
+Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6
+clHEMdUDrNoYCjXtjQIDAQABozowODA2BgNVHREELzAtgitzeW1hbnRlYy1hbGxv
+d2xpc3QtYWZ0ZXItY3V0b2ZmLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IB
+AQB/2Pgyyje+T4uKkibYZ537NZB6OUvef4nrGtswjxKbUnHzrLK1kJd3mtyNpydK
+fzW8WV1CR+nltFI5oaoOAf26FuQfaoCADmlmFlirgnm2fEH2xCokDCQHIgQxNwXr
+Ok6JTHWeuaOsu+verDPjkuUATnONY+FRTBxfPh5B3OA+aBO62bAeNCAjIya1U60S
+emAnleYtwhXs0Q9TLsR7O7aSYP3FgnqnWPuOkPF3wrUiE8Nrd3givz5OJW42IU63
+ijiojHtPpjAiudbzD8y1zuDcxTxiI4jEjTDS1kIqnvHd3f4bSxpgcQUPk44nu+wO
+j5+as/TRu5dC+xHmWCVT10yd
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem.certspec
new file mode 100644
index 0000000000..c8a4249dfc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-after-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=US/O=Google Inc/CN=Google Internet Authority G2
+subject:ee-from-allowlist-after-cutoff
+validity:20160601-20500101
+extension:subjectAlternativeName:symantec-allowlist-after-cutoff.example.com
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem
new file mode 100644
index 0000000000..0c8a52b8d6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDQDCCAiigAwIBAgIUVWP+EbM7hYQLOXoABvWpRwoPWJ0wDQYJKoZIhvcNAQEL
+BQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMT
+HEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwIhgPMjAxNDA2MDEwMDAwMDBa
+GA8yMDUwMDEwMTAwMDAwMFowKjEoMCYGA1UEAwwfZWUtZnJvbS1hbGxvd2xpc3Qt
+YmVmb3JlLWN1dG9mZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqI
+UahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvi
+r1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/x
+fq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD
+7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnv
+uRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj
++nJRxDHVA6zaGAo17Y0CAwEAAaM7MDkwNwYDVR0RBDAwLoIsc3ltYW50ZWMtYWxs
+b3dsaXN0LWJlZm9yZS1jdXRvZmYuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQAD
+ggEBAFhn9xNv/Yp1Ua9OX7oXgbVgzxCIAm6/zDK7V4V37P5em/lI85OpbuuTO4ns
+9VB2/nijkELhSkiyCjmCaxQhlUBzA/2wJeNIfaWu9Mq5MR6jWShhNXND66lTCDIQ
+wGVQkBl8/3OqSD4IFI3pKAiPhCGsUnRIhGSARcrFMQpKssnN5XN1ump1YA7/u6Kv
+apNbKccgKvLekO8/kUpFrpHt+uQaNS2IeVrmOnNh4GtyD3DG1ZDcZ1VgLAnzbVUg
+xiUbs58mY0o1qumuzqJ0/ie5UPOjWnoXSNq6dd4jqsXcFeYXe106G6arV5BLN1Pa
+gNxxfOfBcebE1OwxZpfQwNy3JxI=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem.certspec
new file mode 100644
index 0000000000..51cecd1f8e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-from-allowlist-before-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=US/O=Google Inc/CN=Google Internet Authority G2
+subject:ee-from-allowlist-before-cutoff
+validity:20140601-20500101
+extension:subjectAlternativeName:symantec-allowlist-before-cutoff.example.com
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem
new file mode 100644
index 0000000000..65eab919b5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSzCCAjOgAwIBAgIUNWWgyC4I0/jmqdamoCRL0qhP3QQwDQYJKoZIhvcNAQEL
+BQAwTzELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD0Fub3RoZXIgQ0EgSW5jLjEmMCQG
+A1UEAxMdU29tZSBPdGhlciBDQSBUaGFuIFRoZSBPdGhlcnMwIhgPMjAxNjA2MDEw
+MDAwMDBaGA8yMDUwMDEwMTAwMDAwMFowKjEoMCYGA1UEAwwfZWUtbm90LWFsbG93
+bGlzdGVkLWFmdGVyLWN1dG9mZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAab
+bhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmts
+Du0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhI
+H6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8
+rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kX
+Mbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaNAMD4wPAYDVR0RBDUwM4Ixc3ltYW50
+ZWMtbm90LWFsbG93bGlzdGVkLWFmdGVyLWN1dG9mZi5leGFtcGxlLmNvbTANBgkq
+hkiG9w0BAQsFAAOCAQEARlQNYz+w2ekzdX8FKwlWoMX6Df5o4ZhXreRK9V4SuYJg
+SvAucL35TuTKkBz9C5VPjAL/Qts4n5DKWaWQfsvoCJOGtTMEKd1MEL9RbMFOewI0
+tN9sV9aMNsmhNPL4PB3A7lKJb8gi/tyoN3BXjsaZBxmyi5A6Vt3lyybgMOSTdzR6
+u2XAPm+zNDrOzc2tavZEyhEKzptaJuCQrefcnAM9JTHtJhvJp30WWighJyclqmRX
+bLDYaHRRuRI0jkgrrcUKolau8YKsSoIBoxP2aels8drZDH4UAC86GW2iF/tK+J65
++JJ2Mk9erdcLoYGLxHZqQySpRaFtciSF+Uym1nmNxA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem.certspec
new file mode 100644
index 0000000000..85edcf742d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-after-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=US/O=Another CA Inc./CN=Some Other CA Than The Others
+subject:ee-not-allowlisted-after-cutoff
+validity:20160601-20500101
+extension:subjectAlternativeName:symantec-not-allowlisted-after-cutoff.example.com
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem
new file mode 100644
index 0000000000..23d6fec107
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDTTCCAjWgAwIBAgIUaxGuzjOrA/2qScMaz5tKQTUrvAYwDQYJKoZIhvcNAQEL
+BQAwTzELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD0Fub3RoZXIgQ0EgSW5jLjEmMCQG
+A1UEAxMdU29tZSBPdGhlciBDQSBUaGFuIFRoZSBPdGhlcnMwIhgPMjAxNDA2MDEw
+MDAwMDBaGA8yMDUwMDEwMTAwMDAwMFowKzEpMCcGA1UEAwwgZWUtbm90LWFsbG93
+bGlzdGVkLWJlZm9yZS1jdXRvZmYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjQTA/MD0GA1UdEQQ2MDSCMnN5bWFu
+dGVjLW5vdC1hbGxvd2xpc3RlZC1iZWZvcmUtY3V0b2ZmLmV4YW1wbGUuY29tMA0G
+CSqGSIb3DQEBCwUAA4IBAQBCkIIEXnX/3rv0JuF0FZEpROnQw8zoa4vZjwG1NmvH
++PwnTL7FvBg9QBK0n8ZKfCMqJU98jR5+x6V9Eo2amXEYRAsxCU6kY/Xz43OxGLQz
+5cbr9eDswWmt7h/LXl0tsprpgfBNaQ9512UbPMkDG4MSwLjkuTgdnM4dBsLURrAU
+5lcukutuNYJ7x92/Ah7hffouB6QHyP80onxqqQTQs2j/0MvJxlUWrnKBzk+B475W
+/1iMOCE3r2q6+TVp1sun9mcn7UvRHQRPvjSdjujjJ++5fqH5s4cF2Sv4lv+dEGJn
+xrr/a9GA3bUE6Iq9PbAeX3l7RaP+q/znSDBtaLYZ1Xau
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem.certspec
new file mode 100644
index 0000000000..b736169a04
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-ee-not-allowlisted-before-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=US/O=Another CA Inc./CN=Some Other CA Than The Others
+subject:ee-not-allowlisted-before-cutoff
+validity:20140601-20500101
+extension:subjectAlternativeName:symantec-not-allowlisted-before-cutoff.example.com
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem
new file mode 100644
index 0000000000..e912276ff2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIUWaPXi1S7qZSZ1omEbmdrNrvj/0MwDQYJKoZIhvcNAQEL
+BQAwgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYD
+VQQLEzAoYykgMjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxNjA0BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkgLSBHMjAiGA8yMDEwMDEwMTAwMDAwMFoYDzIwNTAwMTAxMDAwMDAw
+WjBJMQswCQYDVQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMc
+R29vZ2xlIEludGVybmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wccl
+qODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sg
+w0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCx
+V5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1
+MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQs
+vxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswCwYDVR0PBAQD
+AgEGMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALSeQ+T+3AVPidHI
+Mk5D6qOJm57AQ1J5VWoD+S/vqO9jaBjXGtPfx1yBCgLfZvF5N9Wy/foNLhx49BXf
+gwUZPXr5skG9TXPdiZinPW1Txfq/JfJPIeHKdL1eHCm9L/7HVAv1TNlfWFIukGOh
+ZIuzBXID5MH9ubOpY1RcgAxa4wQknPAQ2ZPAcdTzEvKOOv6O78EHTv5TKU6/vk+B
+COS3S86eInm0JcomC+L0H7JT0mCGAzFB7EgWEgnPH8hvi40H2Bml35a192VOdq3o
+XZSoA0ZfARlpcetAu+PjPyoo6UwQ52p4ifXVJoOuZSsRsxmADJpjq1JwTQy3Axah
+ELBHK0c=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem.certspec
new file mode 100644
index 0000000000..745bd9436a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-allowlisted.pem.certspec
@@ -0,0 +1,5 @@
+issuer:printableString/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2
+subject:printableString/C=US/O=Google Inc/CN=Google Internet Authority G2
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem
new file mode 100644
index 0000000000..819d8a30da
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDFzCCAf+gAwIBAgIUBy1RVPb6jMqyBSQwtlK+jZftnKIwDQYJKoZIhvcNAQEL
+BQAwGTEXMBUGA1UEAwwOVW5rbm93biBJc3N1ZXIwIhgPMjAxMDAxMDEwMDAwMDBa
+GA8yMDUwMDEwMTAwMDAwMFowTzELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD0Fub3Ro
+ZXIgQ0EgSW5jLjEmMCQGA1UEAxMdU29tZSBPdGhlciBDQSBUaGFuIFRoZSBPdGhl
+cnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braI
+BjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVa
+p0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB
+7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4C
+kC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJv
+aeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgK
+Ne2NAgMBAAGjHTAbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQAs7dfRClCtgzMfYVRIciVqdjNR+jeFLmYFCDDqx5h6zve4VfxK
+AEQPWNsIVdPlu+djILHHd9+RvLSHh5HqeXKppBevnux2SxwfXJQ3T+ysqGxH4tEQ
+BCgXryt8v5q/DL9H2+T352NJCh7ZMkftEta3Hchtr4TSaT7udtib1uQ9JeLx97LJ
+A6aI8SpfI/as1Ku1LAAV9rfhkJgMyeC0ppMfTVGj/gjgq8fL52/9Su9Id8l+SeYD
+yLCXjPX0rhAjTeJyiOpAK9OPQgk7i3DRvdO/F+JCkTNE9V6PLX0J+30g+3YZND+a
+R81zibhRfa6Ki5cqRflHYhAY4GCFk7mhHLsL
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem.certspec
new file mode 100644
index 0000000000..fdcb287cd1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other-crossigned.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Unknown Issuer
+subject:printableString/C=US/O=Another CA Inc./CN=Some Other CA Than The Others
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem
new file mode 100644
index 0000000000..fae3cd2055
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlzCCAn+gAwIBAgIUcwyTGJUUq4KcHJ1Vb27ydVW1+8UwDQYJKoZIhvcNAQEL
+BQAwgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYD
+VQQLEzAoYykgMjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxNjA0BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkgLSBHMjAiGA8yMDEwMDEwMTAwMDAwMFoYDzIwNTAwMTAxMDAwMDAw
+WjBPMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPQW5vdGhlciBDQSBJbmMuMSYwJAYD
+VQQDEx1Tb21lIE90aGVyIENBIFRoYW4gVGhlIE90aGVyczCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhX
+bCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQ
+OCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9
+uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFb
+t+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhO
+NsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMdMBswCwYD
+VR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHO5Uxu5
+l0orJ5uxTWaaeRrrua66fnDWZoI4BLJ988CpNtZQ7eyXglg2CnSPdQ8GwDKY7JPl
+WGXnXNjbkxW7Br705yFFQOglVyLErXqaqzaTMGNqqS2vxZe/msiKUbhQN66glCSV
+2apocQceCIP9dvP7EZvnWcZFn/6/qjgfZ3E7VNTRC8hGcd9NI1rfreHWevBierDe
+l9cNIta1Qjvj8qzHKDfsrxrk1UlDJemtLvBvE3aGU7YrtVcOZA3l8mnrtfbBgoTJ
+FH3H5Z1rw7uY7TcHK8YSIr4BGh8AAYmx69w87tL6FpOTuJvfW24Fj3aIbm5C4JDP
+Or8OZj97VdCJ9ds=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem.certspec
new file mode 100644
index 0000000000..980edf8ac7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-intermediate-other.pem.certspec
@@ -0,0 +1,5 @@
+issuer:printableString/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2
+subject:printableString/C=US/O=Another CA Inc./CN=Some Other CA Than The Others
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem b/security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem
new file mode 100644
index 0000000000..c9aba820ad
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID4TCCAsmgAwIBAgIUdbQmR0wwLxnOuBWx8JRJkIlK0skwDQYJKoZIhvcNAQEL
+BQAwgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYD
+VQQLEzAoYykgMjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
+IG9ubHkxNjA0BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkgLSBHMjAiGA8yMDEwMDEwMTAwMDAwMFoYDzIwNTAwMTAxMDAwMDAw
+WjCBmDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNV
+BAsTMChjKSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ug
+b25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohR
+qESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+Kv
+WnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+
+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPv
+JxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5
+Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6
+clHEMdUDrNoYCjXtjQIDAQABox0wGzALBgNVHQ8EBAMCAQYwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEAMheHA8TX+WozmLeUr8J3FF+x2l2vGSWiW8W4
+wkC1ETDEc81FxEVip0EDpp6aFurIwOOejL8a8B3hEXd7rqCCsaO8yUYQxaQMPuB5
+4OtBuTcCtIpHeCfwCcUQPes12dCyf3v5stQl5tAF9S/ddEn84Rbntb2ZIRotQnof
+MjpelWuZRmMRvTtQ9BIUID5GuZcMvCmRap011az1nGOJvSsOh2ag+Yz9tTOD3gDM
+z0DIoUUGSQuu0Csw1nJiAIdtlBzwgy1g2YoGTEc3vuFp9KHywPDsdOytlLuIh03k
+C5Vi3DQOO5elqy/I8eeM86PfO9lySyVF7wjiB//IFlQoyl8TMA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem.certspec b/security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem.certspec
new file mode 100644
index 0000000000..1367ecf53e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions/symantec-test-ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:printableString/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2
+subject:printableString/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_sanctions_symantec_apple_google.js b/security/manager/ssl/tests/unit/test_sanctions_symantec_apple_google.js
new file mode 100644
index 0000000000..b94de49c41
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sanctions_symantec_apple_google.js
@@ -0,0 +1,107 @@
+/* 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/. */
+"use strict";
+
+// Tests handling of certificates issued by Symantec. If such certificates were
+// issued by an Apple or Google intermediate, they are allowlisted. Otherwise,
+// If they have a notBefore before 1 June 2016, they should be distrusted, while
+// those from that date or later emit a warning to the console.
+
+function shouldBeImminentlyDistrusted(aTransportSecurityInfo) {
+ let isDistrust =
+ aTransportSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_DISTRUST_IMMINENT;
+ Assert.ok(isDistrust, "This host should be imminently distrusted");
+}
+
+do_get_profile();
+
+const certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+add_tls_server_setup(
+ "SanctionsTestServer",
+ "test_sanctions",
+ /* Don't try to load non-existent test-ca.pem */ false
+);
+
+addCertFromFile(certDB, "test_sanctions/symantec-test-ca.pem", "CTu,u,u");
+
+// Add the necessary intermediates. This is important because the test server,
+// though it will attempt to send along an intermediate, isn't able to reliably
+// pick between the intermediate-other-crossigned and intermediate-other.
+add_test(function() {
+ addCertFromFile(
+ certDB,
+ "test_sanctions/symantec-intermediate-allowlisted.pem",
+ ",,"
+ );
+ addCertFromFile(
+ certDB,
+ "test_sanctions/symantec-intermediate-other.pem",
+ ",,"
+ );
+ run_next_test();
+});
+
+add_connection_test(
+ "symantec-not-allowlisted-before-cutoff.example.com",
+ MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
+ null,
+ null
+);
+
+add_connection_test(
+ "symantec-not-allowlisted-after-cutoff.example.com",
+ MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
+ null,
+ null
+);
+
+// Add a cross-signed intermediate into the database, and ensure we still get
+// the expected error.
+add_test(function() {
+ addCertFromFile(
+ certDB,
+ "test_sanctions/symantec-intermediate-other-crossigned.pem",
+ ",,"
+ );
+ run_next_test();
+});
+
+add_connection_test(
+ "symantec-not-allowlisted-before-cutoff.example.com",
+ MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
+ null,
+ null
+);
+
+// Load the Apple EE cert and its intermediate, then verify
+// it at a reasonable time and make sure the allowlists work
+add_task(async function() {
+ addCertFromFile(
+ certDB,
+ "test_sanctions/apple-ist-ca-8-g1-intermediate.pem",
+ ",,"
+ );
+ let allowlistedCert = constructCertFromFile(
+ "test_sanctions/gspe72-4-ssl-ls-apple-com.pem"
+ );
+
+ // Since we don't want to actually try to fetch OCSP for this certificate,
+ // (as an external fetch is bad in the tests), disable OCSP first.
+ Services.prefs.setIntPref("security.OCSP.enabled", 0);
+
+ // (new Date("2020-01-01")).getTime() / 1000
+ const VALIDATION_TIME = 1577836800;
+
+ await checkCertErrorGenericAtTime(
+ certDB,
+ allowlistedCert,
+ PRErrorCodeSuccess,
+ certificateUsageSSLServer,
+ VALIDATION_TIME
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_sdr.js b/security/manager/ssl/tests/unit/test_sdr.js
new file mode 100644
index 0000000000..e9e477efc5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sdr.js
@@ -0,0 +1,272 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests various aspects of the nsISecretDecoderRing implementation.
+
+do_get_profile();
+
+let gSetPasswordShownCount = 0;
+
+// Mock implementation of nsITokenPasswordDialogs.
+const gTokenPasswordDialogs = {
+ setPassword(ctx, tokenName) {
+ gSetPasswordShownCount++;
+ info(`setPassword() called; shown ${gSetPasswordShownCount} times`);
+ info(`tokenName: ${tokenName}`);
+ return false; // Returning false means "the user didn't cancel".
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsITokenPasswordDialogs"]),
+};
+
+let gMockPrompter = {
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ // Returning false simulates the user canceling the password prompt.
+ return false;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+let gWindowWatcher = {
+ getNewPrompter: () => gMockPrompter,
+ QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"]),
+};
+
+add_task(function setup() {
+ let windowWatcherCID = MockRegistrar.register(
+ "@mozilla.org/embedcomp/window-watcher;1",
+ gWindowWatcher
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+});
+
+add_task(function testEncryptString() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test valid inputs for encryptString() and decryptString().
+ let inputs = [
+ "",
+ " ", // First printable latin1 character (code point 32).
+ "foo",
+ "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
+ "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
+ "aaa 一二三", // Includes Unicode with code points outside [0, 255].
+ ];
+ for (let input of inputs) {
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+
+ let convertedInput = converter.ConvertFromUnicode(input);
+ convertedInput += converter.Finish();
+
+ let encrypted = sdr.encryptString(convertedInput);
+
+ notEqual(
+ convertedInput,
+ encrypted,
+ "Encrypted input should not just be the input itself"
+ );
+
+ try {
+ atob(encrypted);
+ } catch (e) {
+ ok(false, `encryptString() should have returned Base64: ${e}`);
+ }
+
+ equal(
+ convertedInput,
+ sdr.decryptString(encrypted),
+ "decryptString(encryptString(input)) should return input"
+ );
+ }
+
+ // Test invalid inputs for decryptString().
+ throws(
+ () => sdr.decryptString("*"),
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "decryptString() should throw if given non-Base64 input"
+ );
+
+ // Test calling changePassword() pops up the appropriate dialog.
+ // Note: On Android, nsITokenPasswordDialogs is apparently not implemented,
+ // which also seems to prevent us from mocking out the interface.
+ if (AppConstants.platform != "android") {
+ let tokenPasswordDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsTokenPasswordDialogs;1",
+ gTokenPasswordDialogs
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(tokenPasswordDialogsCID);
+ });
+
+ equal(
+ gSetPasswordShownCount,
+ 0,
+ "changePassword() dialog should have been shown zero times"
+ );
+ sdr.changePassword();
+ equal(
+ gSetPasswordShownCount,
+ 1,
+ "changePassword() dialog should have been shown exactly once"
+ );
+ }
+});
+
+add_task(async function testAsyncEncryptStrings() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test valid inputs for encryptString() and decryptString().
+ let inputs = [
+ "",
+ " ", // First printable latin1 character (code point 32).
+ "foo",
+ "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
+ "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
+ "aaa 一二三", // Includes Unicode with code points outside [0, 255].
+ ];
+
+ let encrypteds = await sdr.asyncEncryptStrings(inputs);
+ for (let i = 0; i < inputs.length; i++) {
+ let encrypted = encrypteds[i];
+ let input = inputs[i];
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+
+ let convertedInput = converter.ConvertFromUnicode(input);
+ convertedInput += converter.Finish();
+ notEqual(
+ convertedInput,
+ encrypted,
+ "Encrypted input should not just be the input itself"
+ );
+
+ try {
+ atob(encrypted);
+ } catch (e) {
+ ok(false, `encryptString() should have returned Base64: ${e}`);
+ }
+
+ equal(
+ convertedInput,
+ sdr.decryptString(encrypted),
+ "decryptString(encryptString(input)) should return input"
+ );
+ }
+});
+
+add_task(async function testAsyncDecryptStrings() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test valid inputs for encryptString() and decryptString().
+ let testCases = [
+ "",
+ " ", // First printable latin1 character (code point 32).
+ "foo",
+ "1234567890`~!@#$%^&*()-_=+{[}]|\\:;'\",<.>/?",
+ "¡äöüÿ", // Misc + last printable latin1 character (code point 255).
+ "aaa 一二三", // Includes Unicode with code points outside [0, 255].
+ ];
+
+ let convertedTestCases = testCases.map(tc => {
+ let converter = Cc[
+ "@mozilla.org/intl/scriptableunicodeconverter"
+ ].createInstance(Ci.nsIScriptableUnicodeConverter);
+ converter.charset = "UTF-8";
+
+ let convertedInput = converter.ConvertFromUnicode(tc);
+ convertedInput += converter.Finish();
+ return convertedInput;
+ });
+
+ let encryptedStrings = convertedTestCases.map(tc => sdr.encryptString(tc));
+ let decrypteds = await sdr.asyncDecryptStrings(encryptedStrings);
+ for (let i = 0; i < encryptedStrings.length; i++) {
+ let decrypted = decrypteds[i];
+
+ equal(
+ decrypted,
+ testCases[i],
+ "decrypted string should match expected value"
+ );
+ equal(
+ sdr.decryptString(encryptedStrings[i]),
+ convertedTestCases[i],
+ "decryptString(encryptString(input)) should return the initial decrypted string value"
+ );
+ }
+});
+
+add_task(async function testAsyncDecryptInvalidStrings() {
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ // Test invalid inputs for sdr.asyncDecryptStrings
+ let testCases = [
+ "~bmV0cGxheQ==", // invalid base64 encoding
+ "bmV0cGxheQ==", // valid base64 characters but not encrypted
+ "https://www.example.com", // website address from erroneous migration
+ ];
+
+ let decrypteds = await sdr.asyncDecryptStrings(testCases);
+ equal(
+ decrypteds.length,
+ testCases.length,
+ "each testcase should still return a response"
+ );
+ for (let i = 0; i < decrypteds.length; i++) {
+ let decrypted = decrypteds[i];
+
+ equal(
+ decrypted,
+ "",
+ "decrypted string should be empty when trying to decrypt an invalid input with asyncDecryptStrings"
+ );
+
+ Assert.throws(
+ () => sdr.decryptString(testCases[i]),
+ /NS_ERROR_ILLEGAL_VALUE|NS_ERROR_FAILURE/,
+ `Check testcase would have thrown: ${testCases[i]}`
+ );
+ }
+});
+
+add_task(async function testAsyncDecryptLoggedOut() {
+ // Set a master password.
+ let token = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .getService(Ci.nsIPK11TokenDB)
+ .getInternalKeyToken();
+ token.initPassword("password");
+ token.logoutSimple();
+
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ await Assert.rejects(
+ sdr.asyncDecryptStrings(["irrelevant"]),
+ /NS_ERROR_NOT_AVAILABLE/,
+ "Check error is thrown instead of returning empty strings"
+ );
+
+ token.reset();
+ token.initPassword("");
+});
diff --git a/security/manager/ssl/tests/unit/test_sdr_preexisting.js b/security/manager/ssl/tests/unit/test_sdr_preexisting.js
new file mode 100644
index 0000000000..69b5c194df
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting.js
@@ -0,0 +1,79 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests that the SDR implementation is able to decrypt strings encrypted using
+// a preexisting NSS key database. Creating the database is straight-forward:
+// simply run Firefox (or xpcshell) and encrypt something using
+// nsISecretDecoderRing (e.g. by saving a password or directly using the
+// interface). The resulting key4.db file (in the profile directory) now
+// contains the private key used to encrypt the data.
+
+function run_test() {
+ const keyDBName = "key4.db";
+ let profile = do_get_profile();
+ let keyDBFile = do_get_file(`test_sdr_preexisting/${keyDBName}`);
+ keyDBFile.copyTo(profile, keyDBName);
+
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ let testcases = [
+ // a full padding block
+ {
+ ciphertext:
+ "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d",
+ plaintext: "password",
+ },
+ // 7 bytes of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCAzLDVmYG2/BAh3IoIsMmT8dQ==",
+ plaintext: "a",
+ },
+ // 6 bytes of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECPN8zlZzn8FdBAiu2acpT8UHsg==",
+ plaintext: "bb",
+ },
+ // 1 byte of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECD5px1eMKkJQBAgUPp35GlrDvQ==",
+ plaintext: "!seven!",
+ },
+ // 2 bytes of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECMh0hLtKDyUdBAixw9UZsMt+vA==",
+ plaintext: "sixsix",
+ },
+ // long plaintext requiring more than two blocks
+ {
+ ciphertext:
+ "MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDATFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
+ plaintext: "thisismuchlongerandsotakesupmultipleblocks",
+ },
+ // this differs from the previous ciphertext by one bit and demonstrates
+ // that this implementation does not enforce message integrity
+ {
+ ciphertext:
+ "MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDAbFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
+ plaintext: "nnLbuwLRkhlongerandsotakesupmultipleblocks",
+ },
+ ];
+
+ for (let testcase of testcases) {
+ let decrypted = sdr.decryptString(testcase.ciphertext);
+ equal(
+ decrypted,
+ testcase.plaintext,
+ "decrypted ciphertext should match expected plaintext"
+ );
+ }
+}
diff --git a/security/manager/ssl/tests/unit/test_sdr_preexisting/key4.db b/security/manager/ssl/tests/unit/test_sdr_preexisting/key4.db
new file mode 100644
index 0000000000..8f320dfdbd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting/key4.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js b/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
new file mode 100644
index 0000000000..8502d88a40
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
@@ -0,0 +1,138 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests that the SDR implementation is able to decrypt strings encrypted using
+// a preexisting NSS key database that has a password.
+// To create such a database, run Firefox (or xpcshell), set a primary
+// password, and then encrypt something using nsISecretDecoderRing.
+
+var gMockPrompter = {
+ passwordToTry: "password",
+ numPrompts: 0,
+
+ // This intentionally does not use arrow function syntax to avoid an issue
+ // where in the context of the arrow function, |this != gMockPrompter| due to
+ // how objects get wrapped when going across xpcom boundaries.
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ this.numPrompts++;
+ if (this.numPrompts > 1) {
+ // don't keep retrying a bad password
+ return false;
+ }
+ equal(
+ text,
+ "Please enter your Primary Password.",
+ "password prompt text should be as expected"
+ );
+ equal(checkMsg, null, "checkMsg should be null");
+ ok(this.passwordToTry, "passwordToTry should be non-null");
+ password.value = this.passwordToTry;
+ return true;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPrompt"]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+var gWindowWatcher = {
+ getNewPrompter: () => gMockPrompter,
+ QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"]),
+};
+
+function run_test() {
+ let windowWatcherCID = MockRegistrar.register(
+ "@mozilla.org/embedcomp/window-watcher;1",
+ gWindowWatcher
+ );
+ registerCleanupFunction(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+
+ // Append a single quote and non-ASCII characters to the profile path.
+ let env = Cc["@mozilla.org/process/environment;1"].getService(
+ Ci.nsIEnvironment
+ );
+ let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(profd);
+ file.append("'÷1");
+ env.set("XPCSHELL_TEST_PROFILE_DIR", file.path);
+
+ let profile = do_get_profile(); // must be called before getting nsIX509CertDB
+ Assert.ok(
+ /[^\x20-\x7f]/.test(profile.path),
+ "the profile path should contain a non-ASCII character"
+ );
+
+ let key4DBFile = do_get_file("test_sdr_preexisting_with_password/key4.db");
+ key4DBFile.copyTo(profile, "key4.db");
+
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+
+ let testcases = [
+ // a full padding block
+ {
+ ciphertext:
+ "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d",
+ plaintext: "password",
+ },
+ // 7 bytes of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCAzLDVmYG2/BAh3IoIsMmT8dQ==",
+ plaintext: "a",
+ },
+ // 6 bytes of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECPN8zlZzn8FdBAiu2acpT8UHsg==",
+ plaintext: "bb",
+ },
+ // 1 byte of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECD5px1eMKkJQBAgUPp35GlrDvQ==",
+ plaintext: "!seven!",
+ },
+ // 2 bytes of padding
+ {
+ ciphertext:
+ "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECMh0hLtKDyUdBAixw9UZsMt+vA==",
+ plaintext: "sixsix",
+ },
+ // long plaintext requiring more than two blocks
+ {
+ ciphertext:
+ "MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDATFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
+ plaintext: "thisismuchlongerandsotakesupmultipleblocks",
+ },
+ // this differs from the previous ciphertext by one bit and demonstrates
+ // that this implementation does not enforce message integrity
+ {
+ ciphertext:
+ "MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDAbFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
+ plaintext: "nnLbuwLRkhlongerandsotakesupmultipleblocks",
+ },
+ ];
+
+ for (let testcase of testcases) {
+ let decrypted = sdr.decryptString(testcase.ciphertext);
+ equal(
+ decrypted,
+ testcase.plaintext,
+ "decrypted ciphertext should match expected plaintext"
+ );
+ }
+ equal(
+ gMockPrompter.numPrompts,
+ 1,
+ "Should have been prompted for a password once"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password/key4.db b/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password/key4.db
new file mode 100644
index 0000000000..959718da34
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password/key4.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_self_signed_certs.js b/security/manager/ssl/tests/unit/test_self_signed_certs.js
new file mode 100644
index 0000000000..662cd59714
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_self_signed_certs.js
@@ -0,0 +1,69 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+// This test uses a specially-crafted NSS cert DB containing 12 self-signed certificates that all
+// have the same subject and issuer distinguished name. Since they all have different keys and none
+// of them are trust anchors, there are a large number of potential trust paths that could be
+// explored. If our trust domain were naive enough to allow mozilla::pkix to explore them all, it
+// would take a long time to perform (mozilla::pkix does have the concept of a path-building budget,
+// but even on a fast computer, it takes an unacceptable amount of time to exhaust). To prevent the
+// full exploration of this space, NSSCertDBTrustDomain skips searching through self-signed
+// certificates that aren't trust anchors, since those would never otherwise be essential to
+// complete a path (note that this is only true as long as the extensions we support are restrictive
+// rather than additive).
+// When we try to verify one of these certificates in this test, we should finish relatively
+// quickly, even on slow hardware.
+// Should these certificates ever need regenerating, they were produced with the following commands:
+// certutil -N -d . --empty-password
+// for num in 00 01 02 03 04 05 06 07 08 09 10 11; do
+// echo -ne "5\n6\n9\ny\ny\n\ny\n" | certutil -d . -S -s "CN=self-signed cert" -t ,, \
+// -q secp256r1 -x -k ec -z <(date +%s) -1 -2 -n cert$num; sleep 2;
+// done
+
+add_task(async function run_test_no_overlong_path_building() {
+ let profile = do_get_profile();
+ const CERT_DB_NAME = "cert9.db";
+ let srcCertDBFile = do_get_file(`test_self_signed_certs/${CERT_DB_NAME}`);
+ srcCertDBFile.copyTo(profile, CERT_DB_NAME);
+
+ let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+ );
+ let certToVerify = null;
+ for (let cert of certDB.getCerts()) {
+ if (cert.subjectName == "CN=self-signed cert") {
+ certToVerify = cert;
+ break;
+ }
+ }
+ notEqual(
+ certToVerify,
+ null,
+ "should have found one of the preloaded self-signed certs"
+ );
+ let timeBefore = Date.now();
+ // As mentioned above, mozilla::pkix limits how much it will search for a trusted path, even if a
+ // trust domain keeps providing potential issuers. So, if we only tried to verify a certificate
+ // once, this test could potentially pass on a fast computer even if we weren't properly skipping
+ // unnecessary paths. If we were to try and lower our time limit (the comparison with
+ // secondsElapsed, below), this test would intermittently fail on slow hardware. By trying to
+ // verify the certificate 10 times, we hopefully end up with a meaningful test (it should still
+ // fail on fast hardware if we don't properly skip unproductive paths) that won't intermittently
+ // time out on slow hardware.
+ for (let i = 0; i < 10; i++) {
+ let date = new Date("2019-05-15T00:00:00.000Z");
+ await checkCertErrorGenericAtTime(
+ certDB,
+ certToVerify,
+ SEC_ERROR_UNKNOWN_ISSUER,
+ certificateUsageSSLCA,
+ date.getTime() / 1000
+ );
+ }
+ let timeAfter = Date.now();
+ let secondsElapsed = (timeAfter - timeBefore) / 1000;
+ ok(secondsElapsed < 120, "verifications shouldn't take too long");
+});
diff --git a/security/manager/ssl/tests/unit/test_self_signed_certs/cert9.db b/security/manager/ssl/tests/unit/test_self_signed_certs/cert9.db
new file mode 100644
index 0000000000..5450fe82e5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_self_signed_certs/cert9.db
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_session_resumption.js b/security/manager/ssl/tests/unit/test_session_resumption.js
new file mode 100644
index 0000000000..eb8e1796b5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_session_resumption.js
@@ -0,0 +1,298 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that PSM makes the correct determination of the security status of
+// loads involving session resumption (i.e. when a TLS handshake bypasses the
+// AuthCertificate callback).
+
+do_get_profile();
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.OCSP.enabled");
+ Services.prefs.clearUserPref("network.ssl_tokens_cache_enabled");
+});
+
+Services.prefs.setIntPref("security.OCSP.enabled", 1);
+
+addCertFromFile(certdb, "bad_certs/evroot.pem", "CTu,,");
+addCertFromFile(certdb, "bad_certs/ev-test-intermediate.pem", ",,");
+
+// For expired.example.com, the platform will make a connection that will fail.
+// Using information gathered at that point, an override will be added and
+// another connection will be made. This connection will succeed. At that point,
+// as long as the session cache isn't cleared, subsequent new connections should
+// use session resumption, thereby bypassing the AuthCertificate hook. We need
+// to ensure that the correct security state is propagated to the new connection
+// information object.
+function add_resume_non_ev_with_override_test() {
+ // This adds the override and makes one successful connection.
+ add_cert_override_test(
+ "expired.example.com",
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_EXPIRED_CERTIFICATE
+ );
+
+ // This connects again, using session resumption. Note that we don't clear
+ // the TLS session cache between these operations (that would defeat the
+ // purpose).
+ add_connection_test(
+ "expired.example.com",
+ PRErrorCodeSuccess,
+ null,
+ transportSecurityInfo => {
+ ok(
+ transportSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
+ "expired.example.com should have STATE_CERT_USER_OVERRIDDEN flag"
+ );
+ equal(
+ transportSecurityInfo.succeededCertChain.length,
+ 0,
+ "ev-test.example.com should not have succeededCertChain set"
+ );
+ ok(
+ !transportSecurityInfo.isDomainMismatch,
+ "expired.example.com should not have isDomainMismatch set"
+ );
+ ok(
+ transportSecurityInfo.isNotValidAtThisTime,
+ "expired.example.com should have isNotValidAtThisTime set"
+ );
+ ok(
+ !transportSecurityInfo.isUntrusted,
+ "expired.example.com should not have isUntrusted set"
+ );
+ ok(
+ !transportSecurityInfo.isExtendedValidation,
+ "expired.example.com should not have isExtendedValidation set"
+ );
+
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ certOverrideService.clearValidityOverride("expired.example.com", 8443);
+ }
+ );
+}
+
+// Helper function that adds a test that connects to ev-test.example.com and
+// verifies that it validates as EV (or not, if we're running a non-debug
+// build). This assumes that an appropriate OCSP responder is running or that
+// good responses are cached.
+function add_one_ev_test() {
+ add_connection_test(
+ "ev-test.example.com",
+ PRErrorCodeSuccess,
+ null,
+ transportSecurityInfo => {
+ ok(
+ !(
+ transportSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
+ ),
+ "ev-test.example.com should not have STATE_CERT_USER_OVERRIDDEN flag"
+ );
+ ok(
+ transportSecurityInfo.succeededCertChain,
+ "ev-test.example.com should have succeededCertChain set"
+ );
+ ok(
+ !transportSecurityInfo.isDomainMismatch,
+ "ev-test.example.com should not have isDomainMismatch set"
+ );
+ ok(
+ !transportSecurityInfo.isNotValidAtThisTime,
+ "ev-test.example.com should not have isNotValidAtThisTime set"
+ );
+ ok(
+ !transportSecurityInfo.isUntrusted,
+ "ev-test.example.com should not have isUntrusted set"
+ );
+ ok(
+ !gEVExpected || transportSecurityInfo.isExtendedValidation,
+ "ev-test.example.com should have isExtendedValidation set " +
+ "(or this is a non-debug build)"
+ );
+ }
+ );
+}
+
+// This test is similar, except with extended validation. We should connect
+// successfully, and the certificate should be EV in debug builds. Without
+// clearing the session cache, we should connect successfully again, this time
+// with session resumption. The certificate should again be EV in debug builds.
+function add_resume_ev_test() {
+ const SERVER_PORT = 8888;
+ let expectedRequestPaths = gEVExpected
+ ? ["ev-test-intermediate", "ev-test"]
+ : ["ev-test"];
+ let responseTypes = gEVExpected ? ["good", "good"] : ["good"];
+ // Since we cache OCSP responses, we only ever actually serve one set.
+ let ocspResponder;
+ // If we don't wrap this in an `add_test`, the OCSP responder will be running
+ // while we are actually running unrelated testcases, which can disrupt them.
+ add_test(() => {
+ ocspResponder = startOCSPResponder(
+ SERVER_PORT,
+ "localhost",
+ "bad_certs",
+ expectedRequestPaths,
+ expectedRequestPaths.slice(),
+ null,
+ responseTypes
+ );
+ run_next_test();
+ });
+ // We should be able to connect and verify the certificate as EV (in debug
+ // builds).
+ add_one_ev_test();
+ // We should be able to connect again (using session resumption). In debug
+ // builds, the certificate should be noted as EV. Again, it's important that
+ // nothing clears the TLS cache in between these two operations.
+ add_one_ev_test();
+
+ add_test(() => {
+ ocspResponder.stop(run_next_test);
+ });
+}
+
+const GOOD_DOMAIN = "good.include-subdomains.pinning.example.com";
+
+// Helper function that adds a test that connects to a domain that should
+// succeed (but isn't EV) and verifies that its succeededCertChain gets set
+// appropriately.
+function add_one_non_ev_test() {
+ add_connection_test(
+ GOOD_DOMAIN,
+ PRErrorCodeSuccess,
+ null,
+ transportSecurityInfo => {
+ ok(
+ !(
+ transportSecurityInfo.securityState &
+ Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
+ ),
+ `${GOOD_DOMAIN} should not have STATE_CERT_USER_OVERRIDDEN flag`
+ );
+ ok(
+ transportSecurityInfo.succeededCertChain,
+ `${GOOD_DOMAIN} should have succeededCertChain set`
+ );
+ ok(
+ !transportSecurityInfo.isDomainMismatch,
+ `${GOOD_DOMAIN} should not have isDomainMismatch set`
+ );
+ ok(
+ !transportSecurityInfo.isNotValidAtThisTime,
+ `${GOOD_DOMAIN} should not have isNotValidAtThisTime set`
+ );
+ ok(
+ !transportSecurityInfo.isUntrusted,
+ `${GOOD_DOMAIN} should not have isUntrusted set`
+ );
+ ok(
+ !transportSecurityInfo.isExtendedValidation,
+ `${GOOD_DOMAIN} should not have isExtendedValidation set`
+ );
+ }
+ );
+}
+
+// This test is similar, except with non-extended validation. We should connect
+// successfully, and the certificate should not be EV. Without clearing the
+// session cache, we should connect successfully again, this time with session
+// resumption. In this case, though, we want to ensure the succeededCertChain is
+// set.
+function add_resume_non_ev_test() {
+ add_one_non_ev_test();
+ add_one_non_ev_test();
+}
+
+const statsPtr = getSSLStatistics();
+const toInt32 = ctypes.Int64.lo;
+
+// Connect to the same domain with two origin attributes and check if any ssl
+// session is resumed.
+function add_origin_attributes_test(
+ originAttributes1,
+ originAttributes2,
+ resumeExpected
+) {
+ add_connection_test(
+ GOOD_DOMAIN,
+ PRErrorCodeSuccess,
+ clearSessionCache,
+ null,
+ null,
+ originAttributes1
+ );
+
+ let hitsBeforeConnect;
+ let missesBeforeConnect;
+ let expectedHits = resumeExpected ? 1 : 0;
+ let expectedMisses = 1 - expectedHits;
+
+ add_connection_test(
+ GOOD_DOMAIN,
+ PRErrorCodeSuccess,
+ function() {
+ // Add the hits and misses before connection.
+ let stats = statsPtr.contents;
+ hitsBeforeConnect = toInt32(stats.sch_sid_cache_hits);
+ missesBeforeConnect = toInt32(stats.sch_sid_cache_misses);
+ },
+ function() {
+ let stats = statsPtr.contents;
+ equal(
+ toInt32(stats.sch_sid_cache_hits),
+ hitsBeforeConnect + expectedHits,
+ "Unexpected cache hits"
+ );
+ equal(
+ toInt32(stats.sch_sid_cache_misses),
+ missesBeforeConnect + expectedMisses,
+ "Unexpected cache misses"
+ );
+ },
+ null,
+ originAttributes2
+ );
+}
+
+function add_resumption_tests() {
+ add_resume_ev_test();
+ add_resume_non_ev_test();
+ add_resume_non_ev_with_override_test();
+ add_origin_attributes_test({}, {}, true);
+ add_origin_attributes_test({ userContextId: 1 }, { userContextId: 2 }, false);
+ add_origin_attributes_test({ userContextId: 3 }, { userContextId: 3 }, true);
+ add_origin_attributes_test(
+ { firstPartyDomain: "foo.com" },
+ { firstPartyDomain: "bar.com" },
+ false
+ );
+ add_origin_attributes_test(
+ { firstPartyDomain: "baz.com" },
+ { firstPartyDomain: "baz.com" },
+ true
+ );
+}
+
+function run_test() {
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+ add_resumption_tests();
+ // Enable external session cache and reset the status.
+ add_test(function() {
+ Services.prefs.setBoolPref("network.ssl_tokens_cache_enabled", true);
+ certdb.clearOCSPCache();
+ run_next_test();
+ });
+ // Do tests again.
+ add_resumption_tests();
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_signed_apps.js b/security/manager/ssl/tests/unit/test_signed_apps.js
new file mode 100644
index 0000000000..13f9a995fa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -0,0 +1,1013 @@
+"use strict";
+
+// Tests the API nsIX509CertDB.openSignedAppFileAsync, which backs add-on
+// signature verification. Testcases include various ways of tampering with
+// add-ons as well as different hash algorithms used in the various
+// signature/metadata files.
+
+// from prio.h
+const PR_RDWR = 0x04;
+const PR_CREATE_FILE = 0x08;
+const PR_TRUNCATE = 0x20;
+const PR_USEC_PER_MSEC = 1000;
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+// Creates a new app package based in the inFilePath package, with a set of
+// modifications (including possibly deletions) applied to the existing entries,
+// and/or a set of new entries to be included.
+function tamper(inFilePath, outFilePath, modifications, newEntries) {
+ let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
+ writer.open(outFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+ try {
+ let reader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(
+ Ci.nsIZipReader
+ );
+ reader.open(inFilePath);
+ try {
+ for (let entryName of reader.findEntries("")) {
+ let inEntry = reader.getEntry(entryName);
+ let entryInput = reader.getInputStream(entryName);
+ try {
+ let f = modifications[entryName];
+ let outEntry, outEntryInput;
+ if (f) {
+ [outEntry, outEntryInput] = f(inEntry, entryInput);
+ delete modifications[entryName];
+ } else {
+ [outEntry, outEntryInput] = [inEntry, entryInput];
+ }
+ // if f does not want the input entry to be copied to the output entry
+ // at all (i.e. it wants it to be deleted), it will return null.
+ if (outEntryInput) {
+ try {
+ writer.addEntryStream(
+ entryName,
+ outEntry.lastModifiedTime,
+ outEntry.compression,
+ outEntryInput,
+ false
+ );
+ } finally {
+ if (entryInput != outEntryInput) {
+ outEntryInput.close();
+ }
+ }
+ }
+ } finally {
+ entryInput.close();
+ }
+ }
+ } finally {
+ reader.close();
+ }
+
+ // Any leftover modification means that we were expecting to modify an entry
+ // in the input file that wasn't there.
+ for (let name in modifications) {
+ if (modifications.hasOwnProperty(name)) {
+ throw new Error("input file was missing expected entries: " + name);
+ }
+ }
+
+ // Now, append any new entries to the end
+ newEntries.forEach(function(newEntry) {
+ let sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
+ Ci.nsIStringInputStream
+ );
+ try {
+ sis.setData(newEntry.content, newEntry.content.length);
+ writer.addEntryStream(
+ newEntry.name,
+ new Date() * PR_USEC_PER_MSEC,
+ Ci.nsIZipWriter.COMPRESSION_BEST,
+ sis,
+ false
+ );
+ } finally {
+ sis.close();
+ }
+ });
+ } finally {
+ writer.close();
+ }
+}
+
+function removeEntry(entry, entryInput) {
+ return [null, null];
+}
+
+function truncateEntry(entry, entryInput) {
+ if (entryInput.available() == 0) {
+ throw new Error(
+ "Truncating already-zero length entry will result in " +
+ "identical entry."
+ );
+ }
+
+ let content = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
+ Ci.nsIStringInputStream
+ );
+ content.data = "";
+
+ return [entry, content];
+}
+
+function check_open_result(name, expectedRv) {
+ return function openSignedAppFileCallback(rv, aZipReader, aSignerCert) {
+ info("openSignedAppFileCallback called for " + name);
+ equal(rv, expectedRv, "Actual and expected return value should match");
+ equal(
+ aZipReader != null,
+ Components.isSuccessCode(expectedRv),
+ "ZIP reader should be null only if the return value denotes failure"
+ );
+ equal(
+ aSignerCert != null,
+ Components.isSuccessCode(expectedRv),
+ "Signer cert should be null only if the return value denotes failure"
+ );
+ run_next_test();
+ };
+}
+
+function original_app_path(test_name) {
+ return do_get_file("test_signed_apps/" + test_name + ".zip", false);
+}
+
+function tampered_app_path(test_name) {
+ return FileUtils.getFile("TmpD", ["test_signed_app-" + test_name + ".zip"]);
+}
+
+var hashTestcases = [
+ // SHA-256 in PKCS#7 + SHA-256 present elsewhere => OK
+ { name: "app_mf-1-256_sf-1-256_p7-1-256", expectedResult: Cr.NS_OK },
+ { name: "app_mf-1-256_sf-1-256_p7-256", expectedResult: Cr.NS_OK },
+ { name: "app_mf-1-256_sf-256_p7-1-256", expectedResult: Cr.NS_OK },
+ { name: "app_mf-1-256_sf-256_p7-256", expectedResult: Cr.NS_OK },
+ { name: "app_mf-256_sf-1-256_p7-1-256", expectedResult: Cr.NS_OK },
+ { name: "app_mf-256_sf-1-256_p7-256", expectedResult: Cr.NS_OK },
+ { name: "app_mf-256_sf-256_p7-1-256", expectedResult: Cr.NS_OK },
+ { name: "app_mf-256_sf-256_p7-256", expectedResult: Cr.NS_OK },
+
+ // SHA-1 in PKCS#7 + SHA-1 present elsewhere => OK
+ { name: "app_mf-1-256_sf-1-256_p7-1", expectedResult: Cr.NS_OK },
+ { name: "app_mf-1-256_sf-1_p7-1", expectedResult: Cr.NS_OK },
+ { name: "app_mf-1_sf-1-256_p7-1", expectedResult: Cr.NS_OK },
+ { name: "app_mf-1_sf-1_p7-1", expectedResult: Cr.NS_OK },
+
+ // SHA-256 in PKCS#7 + SHA-256 not present elsewhere => INVALID
+ {
+ name: "app_mf-1-256_sf-1_p7-1-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1-256_sf-1_p7-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1_sf-1-256_p7-1-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1_sf-1-256_p7-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1_sf-1_p7-1-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1_sf-1_p7-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1_sf-256_p7-1-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1_sf-256_p7-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-256_sf-1_p7-1-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-256_sf-1_p7-256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+
+ // SHA-1 in PKCS#7 + SHA-1 not present elsewhere => INVALID
+ {
+ name: "app_mf-1-256_sf-256_p7-1",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-1_sf-256_p7-1",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-256_sf-1-256_p7-1",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-256_sf-1_p7-1",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+ {
+ name: "app_mf-256_sf-256_p7-1",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ },
+];
+
+// Policy values for the preference "security.signed_app_signatures.policy"
+const PKCS7WithSHA1OrSHA256 = 0b0;
+const PKCS7WithSHA256 = 0b1;
+const COSEAndPKCS7WithSHA1OrSHA256 = 0b10;
+const COSEAndPKCS7WithSHA256 = 0b11;
+const COSERequiredAndPKCS7WithSHA1OrSHA256 = 0b100;
+const COSERequiredAndPKCS7WithSHA256 = 0b101;
+const COSEOnly = 0b110;
+const COSEOnlyAgain = 0b111;
+
+function add_signature_test(policy, test) {
+ // First queue up a test to set the desired policy:
+ add_test(function() {
+ Services.prefs.setIntPref("security.signed_app_signatures.policy", policy);
+ run_next_test();
+ });
+ // Then queue up the test itself:
+ add_test(test);
+}
+
+for (let testcase of hashTestcases) {
+ add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path(testcase.name),
+ check_open_result(testcase.name, testcase.expectedResult)
+ );
+ });
+}
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("empty_signerInfos"),
+ check_open_result(
+ "the signerInfos in the PKCS#7 signature is empty",
+ Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED
+ )
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("unsigned_app"),
+ check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("unknown_issuer_app"),
+ check_open_result(
+ "unknown_issuer",
+ getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)
+ )
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_signed_with_pkcs7"),
+ check_open_result("cose_signed_with_pkcs7", Cr.NS_OK)
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-256_sf-256_p7-256"),
+ check_open_result("no COSE but correct PK#7", Cr.NS_OK)
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-1_sf-256_p7-256"),
+ check_open_result(
+ "no COSE and wrong PK#7 hash",
+ Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID
+ )
+ );
+});
+
+add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-256_sf-256_p7-256"),
+ check_open_result(
+ "COSE signature missing (SHA1 or 256)",
+ Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE
+ )
+ );
+});
+
+add_signature_test(COSERequiredAndPKCS7WithSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-256_sf-256_p7-256"),
+ check_open_result(
+ "COSE signature missing (SHA256)",
+ Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE
+ )
+ );
+});
+
+add_signature_test(COSERequiredAndPKCS7WithSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("only_cose_signed"),
+ check_open_result(
+ "COSE signature only (PK#7 allowed, not present)",
+ Cr.NS_OK
+ )
+ );
+});
+
+add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("only_cose_signed"),
+ check_open_result(
+ "COSE signature only (PK#7 allowed, not present)",
+ Cr.NS_OK
+ )
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_multiple_signed_with_pkcs7"),
+ check_open_result("cose_multiple_signed_with_pkcs7", Cr.NS_OK)
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_int_signed_with_pkcs7"),
+ check_open_result("COSE signed with an intermediate", Cr.NS_OK)
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("only_cose_signed"),
+ check_open_result(
+ "PK7 signature missing",
+ Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_multiple_signed_with_pkcs7"),
+ check_open_result(
+ "Expected only COSE signature",
+ Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("only_cose_multiple_signed"),
+ check_open_result("only Multiple COSE signatures", Cr.NS_OK)
+ );
+});
+
+add_signature_test(COSEOnly, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("only_cose_signed"),
+ check_open_result("only_cose_signed", Cr.NS_OK)
+ );
+});
+
+add_signature_test(COSEOnlyAgain, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("only_cose_signed"),
+ check_open_result("only_cose_signed (again)", Cr.NS_OK)
+ );
+});
+
+add_signature_test(COSEOnly, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_signed_with_pkcs7"),
+ check_open_result(
+ "COSE only expected but also PK#7 signed",
+ Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY
+ )
+ );
+});
+
+// Sanity check to ensure a no-op tampering gives a valid result
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("identity_tampering");
+ tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, []);
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ check_open_result("identity_tampering", Cr.NS_OK)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("missing_rsa");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "META-INF/A.RSA": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("missing_sf");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "META-INF/A.SF": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("missing_manifest_mf");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "META-INF/MANIFEST.MF": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "missing_manifest_mf",
+ Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID
+ )
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("missing_entry");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "manifest.json": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("truncated_entry");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "manifest.json": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("truncated_entry", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("truncated_manifestFile");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "META-INF/MANIFEST.MF": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "truncated_manifestFile",
+ Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID
+ )
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("truncated_signatureFile");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "META-INF/A.SF": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "truncated_signatureFile",
+ getXPCOMStatusFromNSS(SEC_ERROR_PKCS7_BAD_SIGNATURE)
+ )
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("truncated_pkcs7File");
+ tamper(
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ tampered,
+ { "META-INF/A.RSA": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("truncated_pkcs7File", Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("unsigned_entry");
+ tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [
+ { name: "unsigned.txt", content: "unsigned content!" },
+ ]);
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("unsigned_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY)
+ );
+});
+
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ let tampered = tampered_app_path("unsigned_metainf_entry");
+ tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [
+ { name: "META-INF/unsigned.txt", content: "unsigned content!" },
+ ]);
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "unsigned_metainf_entry",
+ Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY
+ )
+ );
+});
+
+add_signature_test(PKCS7WithSHA256, function testSHA1Disabled() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-1_sf-1_p7-1"),
+ check_open_result(
+ "SHA-1 should not be accepted if disabled by policy",
+ Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE
+ )
+ );
+});
+
+add_signature_test(PKCS7WithSHA256, function testSHA256WorksWithSHA1Disabled() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-256_sf-256_p7-256"),
+ check_open_result(
+ "SHA-256 should work if SHA-1 is disabled by policy",
+ Cr.NS_OK
+ )
+ );
+});
+
+add_signature_test(
+ PKCS7WithSHA256,
+ function testMultipleSignaturesWorkWithSHA1Disabled() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("app_mf-1-256_sf-1-256_p7-1-256"),
+ check_open_result(
+ "Multiple signatures should work if SHA-1 is " +
+ "disabled by policy (if SHA-256 signature verifies)",
+ Cr.NS_OK
+ )
+ );
+ }
+);
+
+var cosePolicies = [
+ COSEAndPKCS7WithSHA1OrSHA256,
+ COSERequiredAndPKCS7WithSHA1OrSHA256,
+];
+
+// PS256 is not yet supported.
+var coseTestcasesStage = [
+ {
+ name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-ES384",
+ expectedResult: Cr.NS_OK,
+ root: Ci.nsIX509CertDB.AddonsStageRoot,
+ },
+ {
+ name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-PS256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ root: Ci.nsIX509CertDB.AddonsStageRoot,
+ },
+ {
+ name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256",
+ expectedResult: Cr.NS_OK,
+ root: Ci.nsIX509CertDB.AddonsStageRoot,
+ },
+ {
+ name: "autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-PS256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ root: Ci.nsIX509CertDB.AddonsStageRoot,
+ },
+];
+
+var coseTestcasesProd = [
+ {
+ name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384",
+ expectedResult: Cr.NS_OK,
+ root: Ci.nsIX509CertDB.AddonsPublicRoot,
+ },
+ {
+ name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ root: Ci.nsIX509CertDB.AddonsPublicRoot,
+ },
+ {
+ name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256",
+ expectedResult: Cr.NS_OK,
+ root: Ci.nsIX509CertDB.AddonsPublicRoot,
+ },
+ {
+ name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256",
+ expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
+ root: Ci.nsIX509CertDB.AddonsPublicRoot,
+ },
+];
+
+for (let policy of cosePolicies) {
+ for (let testcase of coseTestcasesStage) {
+ add_signature_test(policy, function() {
+ certdb.openSignedAppFileAsync(
+ testcase.root,
+ original_app_path(testcase.name),
+ check_open_result(testcase.name, testcase.expectedResult)
+ );
+ });
+ }
+}
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigTampered() {
+ let tampered = tampered_app_path("cose_sig_tampered");
+ tamper(
+ original_app_path("cose_signed_with_pkcs7"),
+ tampered,
+ { "META-INF/cose.sig": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "cose_sig_tampered",
+ Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY
+ )
+ );
+});
+
+// PKCS7 is processed before COSE, so if a COSE signature file is removed or
+// tampered with, this appears as a PKCS7 signature verification failure.
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigRemoved() {
+ let tampered = tampered_app_path("cose_sig_removed");
+ tamper(
+ original_app_path("cose_signed_with_pkcs7"),
+ tampered,
+ { "META-INF/cose.sig": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("cose_sig_removed", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING)
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestTampered() {
+ let tampered = tampered_app_path("cose_manifest_tampered");
+ tamper(
+ original_app_path("cose_signed_with_pkcs7"),
+ tampered,
+ { "META-INF/cose.manifest": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "cose_manifest_tampered",
+ Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY
+ )
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestRemoved() {
+ let tampered = tampered_app_path("cose_manifest_removed");
+ tamper(
+ original_app_path("cose_signed_with_pkcs7"),
+ tampered,
+ { "META-INF/cose.manifest": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "cose_manifest_removed",
+ Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING
+ )
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileAdded() {
+ let tampered = tampered_app_path("cose_file_added");
+ tamper(original_app_path("cose_signed_with_pkcs7"), tampered, {}, [
+ { name: "unsigned.txt", content: "unsigned content!" },
+ ]);
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("cose_file_added", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY)
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileRemoved() {
+ let tampered = tampered_app_path("cose_file_removed");
+ tamper(
+ original_app_path("cose_signed_with_pkcs7"),
+ tampered,
+ { "manifest.json": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result("cose_file_removed", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING)
+ );
+});
+
+add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileTampered() {
+ let tampered = tampered_app_path("cose_file_tampered");
+ tamper(
+ original_app_path("cose_signed_with_pkcs7"),
+ tampered,
+ { "manifest.json": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "cose_file_tampered",
+ Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSESigTampered() {
+ let tampered = tampered_app_path("only_cose_sig_tampered");
+ tamper(
+ original_app_path("only_cose_signed"),
+ tampered,
+ { "META-INF/cose.sig": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "only_cose_sig_tampered",
+ Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSESigRemoved() {
+ let tampered = tampered_app_path("only_cose_sig_removed");
+ tamper(
+ original_app_path("only_cose_signed"),
+ tampered,
+ { "META-INF/cose.sig": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "only_cose_sig_removed",
+ Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEManifestTampered() {
+ let tampered = tampered_app_path("only_cose_manifest_tampered");
+ tamper(
+ original_app_path("only_cose_signed"),
+ tampered,
+ { "META-INF/cose.manifest": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "only_cose_manifest_tampered",
+ Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEManifestRemoved() {
+ let tampered = tampered_app_path("only_cose_manifest_removed");
+ tamper(
+ original_app_path("only_cose_signed"),
+ tampered,
+ { "META-INF/cose.manifest": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "only_cose_manifest_removed",
+ Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEFileAdded() {
+ let tampered = tampered_app_path("only_cose_file_added");
+ tamper(original_app_path("only_cose_signed"), tampered, {}, [
+ { name: "unsigned.txt", content: "unsigned content!" },
+ ]);
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "only_cose_file_added",
+ Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEFileRemoved() {
+ let tampered = tampered_app_path("only_cose_file_removed");
+ tamper(
+ original_app_path("only_cose_signed"),
+ tampered,
+ { "manifest.json": removeEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "only_cose_file_removed",
+ Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function testOnlyCOSEFileTampered() {
+ let tampered = tampered_app_path("only_cose_file_tampered");
+ tamper(
+ original_app_path("only_cose_signed"),
+ tampered,
+ { "manifest.json": truncateEntry },
+ []
+ );
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ tampered,
+ check_open_result(
+ "only_cose_file_tampered",
+ Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY
+ )
+ );
+});
+
+// This was signed with only COSE first, and then the contents were tampered
+// with (making the signature invalid). Then, the file was signed with
+// PKCS7/SHA1. We need to ensure that if we're configured to process COSE, this
+// verification fails.
+add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_tampered_good_pkcs7"),
+ check_open_result(
+ "tampered COSE with good PKCS7 signature should fail " +
+ "when COSE and PKCS7 is processed",
+ Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY
+ )
+ );
+});
+
+add_signature_test(COSEOnly, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_tampered_good_pkcs7"),
+ check_open_result(
+ "tampered COSE with good PKCS7 signature should fail " +
+ "when only COSE is processed",
+ Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY
+ )
+ );
+});
+
+// If we're not processing COSE, this should verify successfully.
+add_signature_test(PKCS7WithSHA1OrSHA256, function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("cose_tampered_good_pkcs7"),
+ check_open_result(
+ "tampered COSE with good PKCS7 signature should succeed" +
+ "when COSE is not processed",
+ Cr.NS_OK
+ )
+ );
+});
+
+add_test(function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("bug_1411458"),
+ check_open_result("bug 1411458", Cr.NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO)
+ );
+});
+
+// This has a big manifest file (~2MB). It should verify correctly.
+add_test(function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("big_manifest"),
+ check_open_result("add-on with big manifest file", Cr.NS_OK)
+ );
+});
+
+// This has a huge manifest file (~10MB). Manifest files this large are not
+// supported (8MB is the limit). It should not verify correctly.
+add_test(function() {
+ certdb.openSignedAppFileAsync(
+ Ci.nsIX509CertDB.AppXPCShellRoot,
+ original_app_path("huge_manifest"),
+ check_open_result(
+ "add-on with huge manifest file",
+ Cr.NS_ERROR_SIGNED_JAR_ENTRY_INVALID
+ )
+ );
+});
+
+// TODO: tampered MF, tampered SF
+// TODO: too-large MF, too-large RSA, too-large SF
+// TODO: MF and SF that end immediately after the last main header
+// (no CR nor LF)
+// TODO: broken headers to exercise the parser
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app/README b/security/manager/ssl/tests/unit/test_signed_apps/app/README
new file mode 100644
index 0000000000..4f4db4f73e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app/README
@@ -0,0 +1 @@
+This is the readme for the test extension.
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app/data/image.png b/security/manager/ssl/tests/unit/test_signed_apps/app/data/image.png
new file mode 100644
index 0000000000..f4a62faddf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app/data/image.png
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app/manifest.json b/security/manager/ssl/tests/unit/test_signed_apps/app/manifest.json
new file mode 100644
index 0000000000..eacaedfa7a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app/manifest.json
@@ -0,0 +1,5 @@
+{
+ "manifest_version": 2,
+ "name": "Test Extension",
+ "version": "0.0.1"
+}
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.manifest b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.manifest
new file mode 100644
index 0000000000..be5069f57b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.manifest
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+
+Name: README
+SHA256-Digest: bY0l9xqGJYCpqYeJ0K6q4DWUQqu0mNBFM4H4emhjiJg=
+
+Name: manifest.json
+SHA256-Digest: BTnCpT154N26RZm8bhdD43WXd0tj5bg6ofM19NLI0OE=
+
+Name: data/image.png
+SHA256-Digest: EPjkNZwya9X+pruLlxG+FACLwGC48XU4S9oZOA0lVVQ=
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.sig b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.sig
new file mode 100644
index 0000000000..ee9f3e2ce9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/META-INF/cose.sig
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/README b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/README
new file mode 100644
index 0000000000..46217087d8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/README
@@ -0,0 +1,2 @@
+This is the readme for the test extension.
+This app was created by unzipping only_cose_signed.zip and adding this line (thus invalidating the COSE signature).
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/data/image.png b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/data/image.png
new file mode 100644
index 0000000000..f4a62faddf
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/data/image.png
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/manifest.json b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/manifest.json
new file mode 100644
index 0000000000..eacaedfa7a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_cose_tampered/manifest.json
@@ -0,0 +1,5 @@
+{
+ "manifest_version": 2,
+ "name": "Test Extension",
+ "version": "0.0.1"
+}
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1-256.zip
new file mode 100644
index 0000000000..47c40c6bd5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1.zip
new file mode 100644
index 0000000000..1420cd5579
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-256.zip
new file mode 100644
index 0000000000..669b4d8a8b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1-256_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1-256.zip
new file mode 100644
index 0000000000..f39b297300
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1.zip
new file mode 100644
index 0000000000..710dc42788
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-256.zip
new file mode 100644
index 0000000000..001b80177f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-1_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1-256.zip
new file mode 100644
index 0000000000..b529044fcc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1.zip
new file mode 100644
index 0000000000..cdb365ae9c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-256.zip
new file mode 100644
index 0000000000..0e9eb538ec
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1-256_sf-256_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1-256.zip
new file mode 100644
index 0000000000..c94f001690
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1.zip
new file mode 100644
index 0000000000..30c1a2f4c5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-256.zip
new file mode 100644
index 0000000000..20a6236356
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1-256_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1-256.zip
new file mode 100644
index 0000000000..6570b1afb2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1.zip
new file mode 100644
index 0000000000..413bcba3ef
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-256.zip
new file mode 100644
index 0000000000..31a481c954
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-1_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1-256.zip
new file mode 100644
index 0000000000..850917b843
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1.zip
new file mode 100644
index 0000000000..9b17b3b736
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-256.zip
new file mode 100644
index 0000000000..9c2c8a6b8a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-1_sf-256_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1-256.zip
new file mode 100644
index 0000000000..5795fe4f3d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1.zip
new file mode 100644
index 0000000000..3436f161ab
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-256.zip
new file mode 100644
index 0000000000..d9f3fa9fca
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1-256_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1-256.zip
new file mode 100644
index 0000000000..8000501fd6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1.zip
new file mode 100644
index 0000000000..7c6f9bcbfa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-256.zip
new file mode 100644
index 0000000000..8a4509c112
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-1_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1-256.zip
new file mode 100644
index 0000000000..adc6ecf26c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1.zip
new file mode 100644
index 0000000000..abeaa61a81
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-1.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-256.zip b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-256.zip
new file mode 100644
index 0000000000..3b90736b24
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app_mf-256_sf-256_p7-256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384.zip
new file mode 100644
index 0000000000..40b4fc7857
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256.zip
new file mode 100644
index 0000000000..d364e590e5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256.zip
new file mode 100644
index 0000000000..a4353bba19
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256.zip
new file mode 100644
index 0000000000..51ae592ee9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-ES384.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-ES384.zip
new file mode 100644
index 0000000000..b74e087620
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-ES384.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-PS256.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-PS256.zip
new file mode 100644
index 0000000000..772c42e494
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256-PS256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256.zip
new file mode 100644
index 0000000000..b1d1999551
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-ES256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-PS256.zip b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-PS256.zip
new file mode 100644
index 0000000000..0ce563680d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/autograph-714ba248-stage-tomato-clock-PKCS7-SHA1-PS256.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/big_manifest.zip b/security/manager/ssl/tests/unit/test_signed_apps/big_manifest.zip
new file mode 100644
index 0000000000..7a0ec4698c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/big_manifest.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/bug_1411458.zip b/security/manager/ssl/tests/unit/test_signed_apps/bug_1411458.zip
new file mode 100644
index 0000000000..0b296945ab
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/bug_1411458.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/cose_int_signed_with_pkcs7.zip b/security/manager/ssl/tests/unit/test_signed_apps/cose_int_signed_with_pkcs7.zip
new file mode 100644
index 0000000000..d1fa6a7356
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/cose_int_signed_with_pkcs7.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/cose_multiple_signed_with_pkcs7.zip b/security/manager/ssl/tests/unit/test_signed_apps/cose_multiple_signed_with_pkcs7.zip
new file mode 100644
index 0000000000..afd449fcb5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/cose_multiple_signed_with_pkcs7.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/cose_signed_with_pkcs7.zip b/security/manager/ssl/tests/unit/test_signed_apps/cose_signed_with_pkcs7.zip
new file mode 100644
index 0000000000..a1c3175e4f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/cose_signed_with_pkcs7.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/cose_tampered_good_pkcs7.zip b/security/manager/ssl/tests/unit/test_signed_apps/cose_tampered_good_pkcs7.zip
new file mode 100644
index 0000000000..bedc6a9c1a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/cose_tampered_good_pkcs7.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/empty_signerInfos.zip b/security/manager/ssl/tests/unit/test_signed_apps/empty_signerInfos.zip
new file mode 100644
index 0000000000..7a7432596b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/empty_signerInfos.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/huge_manifest.zip b/security/manager/ssl/tests/unit/test_signed_apps/huge_manifest.zip
new file mode 100644
index 0000000000..af2573abf7
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/huge_manifest.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/moz.build b/security/manager/ssl/tests/unit/test_signed_apps/moz.build
new file mode 100644
index 0000000000..5d50d06e11
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/moz.build
@@ -0,0 +1,72 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+
+@template
+def SignedAppFile(name, flags, app_directory="app/"):
+ if not CONFIG["COMPILE_ENVIRONMENT"]:
+ return
+
+ GENERATED_FILES += [name]
+ props = GENERATED_FILES[name]
+ props.script = "/security/manager/ssl/tests/unit/sign_app.py"
+ props.inputs = [app_directory]
+ props.flags = flags
+ # Turn RELATIVEDIR into list entry: like
+ # 'security/manager/ssl/tests/unit/test_signed_apps' ->
+ # TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_signed_apps.
+ files = TEST_HARNESS_FILES.xpcshell
+ for part in RELATIVEDIR.split("/"):
+ files = files[part]
+ files += ["!%s" % name]
+
+
+# Except for unusual testcases (unknown issuer, unsigned app, empty
+# signerInfos), the naming scheme is as follows:
+# app_mf{-1,-256}_sf{-1,-256}_p7{-1,-256}.zip, where:
+# "mf" refers to the manifest file, "sf" refers to the signature file,
+# and "p7" refers to the pkcs#7 file. The "{-1,-256}" indicates which
+# hash algorithms are present in the corresponding file (both may be
+# present).
+# For example, "app_mf-1_sf-1-256_p7-256.zip" means that the manifest
+# file has sha-1 hashes, the signature file has sha-1 hashes and sha-256
+# hashes, and the pkcs#7 file only has sha-256.
+#
+# Temporarily disabled. See bug 1256495.
+# signed_app_files = (
+# ['unknown_issuer_app.zip', '-i', 'unknown issuer', '-p', 'sha256'],
+# ['unsigned_app.zip'],
+# ['empty_signerInfos.zip', '-e'],
+# )
+#
+# for signed_app_file_params in signed_app_files:
+# SignedAppFile(signed_app_file_params[0], signed_app_file_params[1:])
+#
+# for mf_algs in [['1'], ['256'], ['1', '256']]:
+# for sf_algs in [['1'], ['256'], ['1', '256']]:
+# for p7_algs in [['1'], ['256'], ['1', '256']]:
+# filename = "app_mf-%s_sf-%s_p7-%s.zip" % ('-'.join(mf_algs), '-'.join(sf_algs), '-'.join(p7_algs))
+# args = []
+# for mf_alg in mf_algs:
+# args.append('-m')
+# args.append('sha%s' % mf_alg)
+# for sf_alg in sf_algs:
+# args.append('-s')
+# args.append('sha%s' % sf_alg)
+# for p7_alg in p7_algs:
+# args.append('-p')
+# args.append('sha%s' % p7_alg)
+# SignedAppFile(filename, args)
+#
+# COSE test-cases
+# SignedAppFile('cose_signed_with_pkcs7.zip', ['-c', 'ES256', '-p', 'sha256'])
+# SignedAppFile('cose_int_signed_with_pkcs7.zip', ['-c', 'ES256', '-r', 'xpcshell signed apps test root', '-p', 'sha256'])
+# SignedAppFile('cose_multiple_signed_with_pkcs7.zip', ['-c', 'ES256', '-c', 'ES384', '-p', 'sha256'])
+# SignedAppFile('only_cose_signed.zip', ['-c', 'ES256'])
+# SignedAppFile('only_cose_multiple_signed.zip', ['-c', 'ES384', '-c', 'ES256'])
+# SignedAppFile('cose_tampered_good_pkcs7.zip', ['-m', 'sha1', '-s', 'sha1', '-p', 'sha1'], 'app_cose_tampered/')
+# SignedAppFile('big_manifest.zip', ['-p', 'sha256', '--pad-headers', '2'])
+# SignedAppFile('huge_manifest.zip', ['-p', 'sha256', '--pad-headers', '10'])
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/only_cose_multiple_signed.zip b/security/manager/ssl/tests/unit/test_signed_apps/only_cose_multiple_signed.zip
new file mode 100644
index 0000000000..3536354f37
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/only_cose_multiple_signed.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/only_cose_signed.zip b/security/manager/ssl/tests/unit/test_signed_apps/only_cose_signed.zip
new file mode 100644
index 0000000000..4ff76efcb8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/only_cose_signed.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app.zip b/security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app.zip
new file mode 100644
index 0000000000..4fa533a77a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/unsigned_app.zip b/security/manager/ssl/tests/unit/test_signed_apps/unsigned_app.zip
new file mode 100644
index 0000000000..f28e5df226
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/unsigned_app.zip
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.der b/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.der
new file mode 100644
index 0000000000..3c1869b13b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.der
Binary files differ
diff --git a/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.pem.certspec b/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.pem.certspec
new file mode 100644
index 0000000000..500c4185cd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.pem.certspec
@@ -0,0 +1,6 @@
+issuer:xpcshell signed apps test root
+subject:xpcshell signed apps test root
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:keyEncipherment,keyCertSign
+extension:extKeyUsage:codeSigning
diff --git a/security/manager/ssl/tests/unit/test_ssl_status.js b/security/manager/ssl/tests/unit/test_ssl_status.js
new file mode 100644
index 0000000000..16484ebb59
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ssl_status.js
@@ -0,0 +1,76 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+"use strict";
+
+do_get_profile();
+
+function run_test() {
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+ let fakeOCSPResponder = new HttpServer();
+ fakeOCSPResponder.registerPrefixHandler("/", function(request, response) {
+ response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+ });
+ fakeOCSPResponder.start(8888);
+
+ // Test successful connection (failedCertChain should be null,
+ // succeededCertChain should be set as expected)
+ add_connection_test(
+ "good.include-subdomains.pinning.example.com",
+ PRErrorCodeSuccess,
+ null,
+ function withSecurityInfo(aSecInfo) {
+ equal(
+ aSecInfo.failedCertChain.length,
+ 0,
+ "failedCertChain for a successful connection should be empty"
+ );
+ ok(
+ areCertArraysEqual(
+ aSecInfo.succeededCertChain,
+ build_cert_chain(["default-ee", "test-ca"])
+ ),
+ "succeededCertChain for a successful connection should be as expected"
+ );
+ }
+ );
+
+ // Test failed connection (failedCertChain should be set as expected,
+ // succeededCertChain should be null)
+ add_connection_test(
+ "expired.example.com",
+ SEC_ERROR_EXPIRED_CERTIFICATE,
+ null,
+ function withSecurityInfo(aSecInfo) {
+ equal(
+ aSecInfo.succeededCertChain.length,
+ 0,
+ "succeededCertChain for a failed connection should be null"
+ );
+ ok(
+ areCertArraysEqual(
+ aSecInfo.failedCertChain,
+ build_cert_chain(["expired-ee", "test-ca"])
+ ),
+ "failedCertChain for a failed connection should be as expected"
+ );
+ }
+ );
+
+ // Ensure the correct failed cert chain is set on cert override
+ let overrideStatus = {
+ failedCertChain: build_cert_chain(["expired-ee", "test-ca"]),
+ };
+ add_cert_override_test(
+ "expired.example.com",
+ Ci.nsICertOverrideService.ERROR_TIME,
+ SEC_ERROR_EXPIRED_CERTIFICATE,
+ undefined,
+ overrideStatus
+ );
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_enumerate.js b/security/manager/ssl/tests/unit/test_sss_enumerate.js
new file mode 100644
index 0000000000..e039660f33
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_enumerate.js
@@ -0,0 +1,101 @@
+/* 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/. */
+"use strict";
+
+do_get_profile(); // must be done before instantiating nsIX509CertDB
+
+const SECS_IN_A_WEEK = 7 * 24 * 60 * 60 * 1000;
+const TESTCASES = [
+ {
+ hostname: "a.pinning.example.com",
+ includeSubdomains: true,
+ expireTime: Date.now() + 12 * SECS_IN_A_WEEK * 1000,
+ },
+ {
+ hostname: "b.pinning.example.com",
+ includeSubdomains: false,
+ expireTime: Date.now() + 13 * SECS_IN_A_WEEK * 1000,
+ },
+].sort((a, b) => a.expireTime - b.expireTime);
+
+let sss = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);
+
+function getEntries(type) {
+ return Array.from(sss.enumerate(type));
+}
+
+function checkSiteSecurityStateAttrs(entries) {
+ entries.sort((a, b) => a.expireTime - b.expireTime);
+ equal(
+ entries.length,
+ TESTCASES.length,
+ "Should get correct number of entries"
+ );
+ for (let i = 0; i < TESTCASES.length; i++) {
+ equal(entries[i].hostname, TESTCASES[i].hostname, "Hostnames should match");
+ equal(
+ entries[i].securityPropertyState,
+ Ci.nsISiteSecurityState.SECURITY_PROPERTY_SET,
+ "Entries should have security property set"
+ );
+ equal(
+ entries[i].includeSubdomains,
+ TESTCASES[i].includeSubdomains,
+ "IncludeSubdomains should match"
+ );
+ // There's a delay from our "now" and the "now" that the implementation uses.
+ less(
+ Math.abs(entries[i].expireTime - TESTCASES[i].expireTime),
+ 60000,
+ "ExpireTime should be within 60-second error"
+ );
+ }
+}
+
+function add_tests() {
+ sss.clearAll();
+
+ for (const testcase of TESTCASES) {
+ add_connection_test(
+ testcase.hostname,
+ PRErrorCodeSuccess,
+ undefined,
+ function insertEntry(secInfo) {
+ const uri = Services.io.newURI(`https://${testcase.hostname}`);
+
+ // MaxAge is in seconds.
+ let maxAge = Math.round((testcase.expireTime - Date.now()) / 1000);
+ let header = `max-age=${maxAge}`;
+ if (testcase.includeSubdomains) {
+ header += "; includeSubdomains";
+ }
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ header,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ }
+ );
+ }
+
+ add_task(() => {
+ let hstsEntries = getEntries(Ci.nsISiteSecurityService.HEADER_HSTS);
+
+ checkSiteSecurityStateAttrs(hstsEntries);
+
+ sss.clearAll();
+ hstsEntries = getEntries(Ci.nsISiteSecurityService.HEADER_HSTS);
+
+ equal(hstsEntries.length, 0, "Should clear all HSTS entries");
+ });
+}
+
+function run_test() {
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+ add_tests();
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_eviction.js b/security/manager/ssl/tests/unit/test_sss_eviction.js
new file mode 100644
index 0000000000..dbb0880064
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_eviction.js
@@ -0,0 +1,100 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to check that a frequently visited site
+// will not be evicted over an infrequently visited site.
+
+var gSSService = null;
+var gProfileDir = null;
+
+function do_state_written(aSubject, aTopic, aData) {
+ if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
+ return;
+ }
+
+ equal(aData, SSS_STATE_FILE_NAME);
+
+ let stateFile = gProfileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ ok(stateFile.exists());
+ let stateFileContents = readFile(stateFile);
+ // the last part is removed because it's the empty string after the final \n
+ let lines = stateFileContents.split("\n").slice(0, -1);
+ // We can receive multiple data-storage-written events. In particular, we
+ // may receive one where DataStorage wrote out data before we were done
+ // processing all of our headers. In this case, the data may not be
+ // as we expect. We only care about the final one being correct, however,
+ // so we return and wait for the next event if things aren't as we expect.
+ // There should be 1024 entries.
+ if (lines.length != 1024) {
+ return;
+ }
+
+ let foundLegitSite = false;
+ for (let line of lines) {
+ if (line.startsWith("frequentlyused.example.com:HSTS")) {
+ foundLegitSite = true;
+ break;
+ }
+ }
+
+ ok(foundLegitSite);
+ do_test_finished();
+}
+
+function do_state_read(aSubject, aTopic, aData) {
+ if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
+ return;
+ }
+
+ equal(aData, SSS_STATE_FILE_NAME);
+
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://frequentlyused.example.com"),
+ 0
+ )
+ );
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+ for (let i = 0; i < 2000; i++) {
+ let uri = Services.io.newURI("http://bad" + i + ".example.com");
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1000",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ }
+ do_test_pending();
+ Services.obs.addObserver(do_state_written, "data-storage-written");
+ do_test_finished();
+}
+
+function run_test() {
+ Services.prefs.setIntPref("test.datastorage.write_timer_ms", 100);
+ gProfileDir = do_get_profile();
+ let stateFile = gProfileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ // Assuming we're working with a clean slate, the file shouldn't exist
+ // until we create it.
+ ok(!stateFile.exists());
+ let outputStream = FileUtils.openFileOutputStream(stateFile);
+ let now = new Date().getTime();
+ let line =
+ "frequentlyused.example.com:HSTS\t4\t0\t" + (now + 100000) + ",1,0\n";
+ outputStream.write(line, line.length);
+ outputStream.close();
+ Services.obs.addObserver(do_state_read, "data-storage-ready");
+ do_test_pending();
+ gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ notEqual(gSSService, null);
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_originAttributes.js b/security/manager/ssl/tests/unit/test_sss_originAttributes.js
new file mode 100644
index 0000000000..181a57ee28
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_originAttributes.js
@@ -0,0 +1,179 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * 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/. */
+
+"use strict";
+
+// Ensures nsISiteSecurityService APIs respects origin attributes.
+
+const GOOD_MAX_AGE_SECONDS = 69403;
+const GOOD_MAX_AGE = `max-age=${GOOD_MAX_AGE_SECONDS};`;
+
+do_get_profile(); // must be done before instantiating nsIX509CertDB
+
+let sss = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);
+let host = "a.pinning.example.com";
+let uri = Services.io.newURI("https://" + host);
+
+// Check if originAttributes1 and originAttributes2 are isolated with respect
+// to HSTS storage.
+function doTest(secInfo, originAttributes1, originAttributes2, shouldShare) {
+ sss.clearAll();
+ let header = GOOD_MAX_AGE;
+ // Set HSTS for originAttributes1.
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ header,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
+ originAttributes1
+ );
+ ok(
+ sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes1
+ ),
+ "URI should be secure given original origin attributes"
+ );
+ equal(
+ sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes2
+ ),
+ shouldShare,
+ "URI should be secure given different origin attributes if and " +
+ "only if shouldShare is true"
+ );
+
+ if (!shouldShare) {
+ // Remove originAttributes2 from the storage.
+ sss.resetState(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes2
+ );
+ ok(
+ sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes1
+ ),
+ "URI should still be secure given original origin attributes"
+ );
+ }
+
+ // Remove originAttributes1 from the storage.
+ sss.resetState(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes1
+ );
+ ok(
+ !sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes1
+ ),
+ "URI should not be secure after removeState"
+ );
+
+ sss.clearAll();
+}
+
+function testInvalidOriginAttributes(secInfo, originAttributes) {
+ let header = GOOD_MAX_AGE;
+
+ let callbacks = [
+ () =>
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ header,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
+ originAttributes
+ ),
+ () =>
+ sss.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes
+ ),
+ () =>
+ sss.resetState(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ 0,
+ originAttributes
+ ),
+ ];
+
+ for (let callback of callbacks) {
+ throws(
+ callback,
+ /NS_ERROR_ILLEGAL_VALUE/,
+ "Should get an error with invalid origin attributes"
+ );
+ }
+}
+
+function add_tests() {
+ sss.clearAll();
+
+ let secInfo = null;
+ add_connection_test(
+ "a.pinning.example.com",
+ PRErrorCodeSuccess,
+ undefined,
+ aSecInfo => {
+ secInfo = aSecInfo;
+ }
+ );
+
+ add_task(function() {
+ let originAttributesList = [];
+ for (let userContextId of [0, 1, 2]) {
+ for (let firstPartyDomain of ["", "foo.com", "bar.com"]) {
+ originAttributesList.push({ userContextId, firstPartyDomain });
+ }
+ }
+ for (let attrs1 of originAttributesList) {
+ for (let attrs2 of originAttributesList) {
+ // SSS storage is not isolated by userContext
+ doTest(
+ secInfo,
+ attrs1,
+ attrs2,
+ attrs1.firstPartyDomain == attrs2.firstPartyDomain
+ );
+ }
+ }
+
+ testInvalidOriginAttributes(secInfo, undefined);
+ testInvalidOriginAttributes(secInfo, null);
+ testInvalidOriginAttributes(secInfo, 1);
+ testInvalidOriginAttributes(secInfo, "foo");
+ });
+}
+
+function run_test() {
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+ add_tests();
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_readstate.js b/security/manager/ssl/tests/unit/test_sss_readstate.js
new file mode 100644
index 0000000000..1f3951a3f0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_readstate.js
@@ -0,0 +1,161 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to create a site security service state file
+// and see that the site security service reads it properly.
+
+var gSSService = null;
+
+function checkStateRead(aSubject, aTopic, aData) {
+ if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
+ return;
+ }
+
+ equal(aData, SSS_STATE_FILE_NAME);
+
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://expired.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://notexpired.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://incsubdomain.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.incsubdomain.example.com"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains2.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.includesubdomains2.preloaded.test"),
+ 0
+ )
+ );
+
+ // Clearing the data should make everything go back to default.
+ gSSService.clearAll();
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://expired.example.com"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://notexpired.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://incsubdomain.example.com"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.incsubdomain.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains2.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sub.includesubdomains2.preloaded.test"),
+ 0
+ )
+ );
+ do_test_finished();
+}
+
+function run_test() {
+ let profileDir = do_get_profile();
+ let stateFile = profileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ // Assuming we're working with a clean slate, the file shouldn't exist
+ // until we create it.
+ ok(!stateFile.exists());
+ let outputStream = FileUtils.openFileOutputStream(stateFile);
+ let now = Date.now();
+ let lines = [
+ `expired.example.com:HSTS\t0\t0\t${now - 100000},1,0`,
+ `notexpired.example.com:HSTS\t0\t0\t${now + 100000},1,0`,
+ // This overrides an entry on the preload list.
+ `includesubdomains.preloaded.test:HSTS\t0\t0\t${now + 100000},1,0`,
+ `incsubdomain.example.com:HSTS\t0\t0\t${now + 100000},1,1`,
+ // This overrides an entry on the preload list.
+ "includesubdomains2.preloaded.test:HSTS\t0\t0\t0,2,0",
+ ];
+ writeLinesAndClose(lines, outputStream);
+ Services.obs.addObserver(checkStateRead, "data-storage-ready");
+ do_test_pending();
+ gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ notEqual(gSSService, null);
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_readstate_child.js b/security/manager/ssl/tests/unit/test_sss_readstate_child.js
new file mode 100644
index 0000000000..e45fedd18d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_readstate_child.js
@@ -0,0 +1,40 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to create a site security service state file
+// and see that the site security service reads it properly. We also verify
+// that state changes are reflected in the child process.
+
+function start_test_in_child() {
+ run_test_in_child("sss_readstate_child_worker.js");
+ do_test_finished();
+}
+
+function run_test() {
+ let profileDir = do_get_profile();
+ let stateFile = profileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ // Assuming we're working with a clean slate, the file shouldn't exist
+ // until we create it.
+ ok(!stateFile.exists());
+ let outputStream = FileUtils.openFileOutputStream(stateFile);
+ let now = Date.now();
+ let lines = [
+ `expired.example.com:HSTS\t0\t0\t${now - 100000},1,0`,
+ `notexpired.example.com:HSTS\t0\t0\t${now + 100000},1,0`,
+ // This overrides an entry on the preload list.
+ `includesubdomains.preloaded.test:HSTS\t0\t0\t${now + 100000},1,0`,
+ `incsubdomain.example.com:HSTS\t0\t0\t${now + 100000},1,1`,
+ // This overrides an entry on the preload list.
+ "includesubdomains2.preloaded.test:HSTS\t0\t0\t0,2,0",
+ ];
+ writeLinesAndClose(lines, outputStream);
+ Services.obs.addObserver(start_test_in_child, "data-storage-ready");
+ do_test_pending();
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ notEqual(SSService, null);
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_readstate_empty.js b/security/manager/ssl/tests/unit/test_sss_readstate_empty.js
new file mode 100644
index 0000000000..b1def6bdd8
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_readstate_empty.js
@@ -0,0 +1,56 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to create an empty site security service state
+// file and see that the site security service doesn't fail when reading it.
+
+var gSSService = null;
+
+function checkStateRead(aSubject, aTopic, aData) {
+ // nonexistent.example.com should never be an HSTS host
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://nonexistent.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ // notexpired.example.com is an HSTS host in a different test - we
+ // want to make sure that test hasn't interfered with this one.
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://notexpired.example.com"),
+ 0
+ )
+ );
+ do_test_finished();
+}
+
+function run_test() {
+ let profileDir = do_get_profile();
+ let stateFile = profileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ // Assuming we're working with a clean slate, the file shouldn't exist
+ // until we create it.
+ ok(!stateFile.exists());
+ stateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0x1a4); // 0x1a4 == 0o644
+ ok(stateFile.exists());
+ // Initialize nsISiteSecurityService after do_get_profile() so it
+ // can read the state file.
+ Services.obs.addObserver(checkStateRead, "data-storage-ready");
+ do_test_pending();
+ gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ notEqual(gSSService, null);
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_readstate_garbage.js b/security/manager/ssl/tests/unit/test_sss_readstate_garbage.js
new file mode 100644
index 0000000000..94f1ca9e6b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_readstate_garbage.js
@@ -0,0 +1,110 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to create a mostly bogus site security service
+// state file and see that the site security service handles it properly.
+
+var gSSService = null;
+
+function checkStateRead(aSubject, aTopic, aData) {
+ if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
+ return;
+ }
+
+ equal(aData, SSS_STATE_FILE_NAME);
+
+ const HSTS_HOSTS = [
+ "https://example1.example.com",
+ "https://example2.example.com",
+ ];
+ for (let host of HSTS_HOSTS) {
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI(host),
+ 0
+ ),
+ `${host} should be HSTS enabled`
+ );
+ }
+
+ const NOT_HSTS_HOSTS = [
+ "https://example.com",
+ "https://example3.example.com",
+ "https://extra.comma.example.com",
+ "https://empty.statestring.example.com",
+ "https://rubbish.statestring.example.com",
+ "https://spaces.statestring.example.com",
+ "https://invalid.expirytime.example.com",
+ "https://text.securitypropertystate.example.com",
+ "https://invalid.securitypropertystate.example.com",
+ "https://text.includesubdomains.example.com",
+ "https://invalid.includesubdomains.example.com",
+ ];
+ for (let host of NOT_HSTS_HOSTS) {
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI(host),
+ 0
+ ),
+ `${host} should not be HSTS enabled`
+ );
+ }
+
+ do_test_finished();
+}
+
+const PINNING_ROOT_KEY_HASH = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
+const BASE64_BUT_NOT_SHA256 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+const STARTS_WITH_NUMBER = "1ABC23defG/hiJKlmNoP+QRStuVwxYZ9a+bcD/+/EFg=";
+const STARTS_WITH_SYMBOL = "+ABC23defG/hiJKlmNoP+QRStuVwxYZ9a+bcD/+/EFg=";
+const MULTIPLE_KEYS =
+ PINNING_ROOT_KEY_HASH + STARTS_WITH_NUMBER + STARTS_WITH_SYMBOL;
+
+function run_test() {
+ Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
+ let profileDir = do_get_profile();
+ let stateFile = profileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ // Assuming we're working with a clean slate, the file shouldn't exist
+ // until we create it.
+ ok(!stateFile.exists());
+ let outputStream = FileUtils.openFileOutputStream(stateFile);
+ let expiryTime = Date.now() + 100000;
+ let lines = [
+ // General state file entry tests.
+ `example1.example.com:HSTS\t0\t0\t${expiryTime},1,0`,
+ "I'm a lumberjack and I'm okay; I work all night and I sleep all day!",
+ "This is a totally bogus entry\t",
+ "0\t0\t0\t0\t",
+ "\t\t\t\t\t\t\t",
+ "example.com:HSTS\t\t\t\t\t\t\t",
+ "example3.example.com:HSTS\t0\t\t\t\t\t\t",
+ `example2.example.com:HSTS\t0\t0\t${expiryTime},1,0`,
+ // HSTS state string parsing tests
+ `extra.comma.example.com:HSTS\t0\t0\t${expiryTime},,1,0`,
+ "empty.statestring.example.com:HSTS\t0\t0\t",
+ "rubbish.statestring.example.com:HSTS\t0\t0\tfoobar",
+ `spaces.statestring.example.com:HSTS\t0\t0\t${expiryTime}, 1,0 `,
+ `invalid.expirytime.example.com:HSTS\t0\t0\t${expiryTime}foo123,1,0`,
+ `text.securitypropertystate.example.com:HSTS\t0\t0\t${expiryTime},1foo,0`,
+ `invalid.securitypropertystate.example.com:HSTS\t0\t0\t${expiryTime},999,0`,
+ `text.includesubdomains.example.com:HSTS\t0\t0\t${expiryTime},1,1foo`,
+ `invalid.includesubdomains.example.com:HSTS\t0\t0\t${expiryTime},1,0foo`,
+ ];
+ writeLinesAndClose(lines, outputStream);
+ Services.obs.addObserver(checkStateRead, "data-storage-ready");
+ do_test_pending();
+ gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ notEqual(gSSService, null);
+
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
+ });
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_readstate_huge.js b/security/manager/ssl/tests/unit/test_sss_readstate_huge.js
new file mode 100644
index 0000000000..d0c7a7541d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_readstate_huge.js
@@ -0,0 +1,98 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to create a site security service state file
+// that is too large and see that the site security service reads it properly
+// (this means discarding all entries after the 1024th).
+
+var gSSService = null;
+
+function checkStateRead(aSubject, aTopic, aData) {
+ if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
+ return;
+ }
+
+ equal(aData, SSS_STATE_FILE_NAME);
+
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example0.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example423.example.com"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example1023.example.com"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example1024.example.com"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example1025.example.com"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example9000.example.com"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://example99999.example.com"),
+ 0
+ )
+ );
+ do_test_finished();
+}
+
+function run_test() {
+ let profileDir = do_get_profile();
+ let stateFile = profileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ // Assuming we're working with a clean slate, the file shouldn't exist
+ // until we create it.
+ ok(!stateFile.exists());
+ let outputStream = FileUtils.openFileOutputStream(stateFile);
+ let expiryTime = Date.now() + 100000;
+ let lines = [];
+ for (let i = 0; i < 10000; i++) {
+ // The 0s will all get squashed down into one 0 when they are read.
+ // This is just to make the file size large (>2MB).
+ lines.push(
+ `example${i}.example.com:HSTS\t` +
+ "0000000000000000000000000000000000000000000000000\t" +
+ "00000000000000000000000000000000000000\t" +
+ `${expiryTime},1,0000000000000000000000000000000000000000000000000000000000000000000000000`
+ );
+ }
+ writeLinesAndClose(lines, outputStream);
+ Services.obs.addObserver(checkStateRead, "data-storage-ready");
+ do_test_pending();
+ gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ notEqual(gSSService, null);
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_resetState.js b/security/manager/ssl/tests/unit/test_sss_resetState.js
new file mode 100644
index 0000000000..1850442b64
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_resetState.js
@@ -0,0 +1,113 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests that resetting HSTS state in the way the "forget about this site"
+// functionality does works as expected for preloaded and non-preloaded sites.
+
+do_get_profile();
+
+var gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+);
+
+function test_removeState(secInfo, type, flags) {
+ info(`running test_removeState(type=${type}, flags=${flags})`);
+ // Simulate visiting a non-preloaded site by processing an HSTS header check
+ // that the HSTS bit gets set, simulate "forget about this site" (call
+ // removeState), and then check that the HSTS bit isn't set.
+ let notPreloadedURI = Services.io.newURI("https://not-preloaded.example.com");
+ ok(!gSSService.isSecureURI(type, notPreloadedURI, flags));
+ gSSService.processHeader(
+ type,
+ notPreloadedURI,
+ "max-age=1000;",
+ secInfo,
+ flags,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(gSSService.isSecureURI(type, notPreloadedURI, flags));
+ gSSService.resetState(type, notPreloadedURI, flags);
+ ok(!gSSService.isSecureURI(type, notPreloadedURI, flags));
+
+ // Simulate visiting a non-preloaded site that unsets HSTS by processing
+ // an HSTS header with "max-age=0", check that the HSTS bit isn't
+ // set, simulate "forget about this site" (call removeState), and then check
+ // that the HSTS bit isn't set.
+ gSSService.processHeader(
+ type,
+ notPreloadedURI,
+ "max-age=0;",
+ secInfo,
+ flags,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(!gSSService.isSecureURI(type, notPreloadedURI, flags));
+ gSSService.resetState(type, notPreloadedURI, flags);
+ ok(!gSSService.isSecureURI(type, notPreloadedURI, flags));
+
+ // Simulate visiting a preloaded site by processing an HSTS header, check
+ // that the HSTS bit is still set, simulate "forget about this site"
+ // (call removeState), and then check that the HSTS bit is still set.
+ let preloadedHost = "includesubdomains.preloaded.test";
+ let preloadedURI = Services.io.newURI(`https://${preloadedHost}`);
+ ok(gSSService.isSecureURI(type, preloadedURI, flags));
+ gSSService.processHeader(
+ type,
+ preloadedURI,
+ "max-age=1000;",
+ secInfo,
+ flags,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(gSSService.isSecureURI(type, preloadedURI, flags));
+ gSSService.resetState(type, preloadedURI, flags);
+ ok(gSSService.isSecureURI(type, preloadedURI, flags));
+
+ // Simulate visiting a preloaded site that unsets HSTS by processing an
+ // HSTS header with "max-age=0", check that the HSTS bit is what we
+ // expect (see below), simulate "forget about this site" (call removeState),
+ // and then check that the HSTS bit is set.
+ gSSService.processHeader(
+ type,
+ preloadedURI,
+ "max-age=0;",
+ secInfo,
+ flags,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(!gSSService.isSecureURI(type, preloadedURI, flags));
+ gSSService.resetState(type, preloadedURI, flags);
+ ok(gSSService.isSecureURI(type, preloadedURI, flags));
+}
+
+function add_tests() {
+ let secInfo = null;
+ add_connection_test(
+ "not-preloaded.example.com",
+ PRErrorCodeSuccess,
+ undefined,
+ aSecInfo => {
+ secInfo = aSecInfo;
+ }
+ );
+
+ add_task(() => {
+ test_removeState(secInfo, Ci.nsISiteSecurityService.HEADER_HSTS, 0);
+ test_removeState(
+ secInfo,
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Ci.nsISocketProvider.NO_PERMANENT_STORAGE
+ );
+ });
+}
+
+function run_test() {
+ add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
+
+ add_tests();
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_sss_sanitizeOnShutdown.js b/security/manager/ssl/tests/unit/test_sss_sanitizeOnShutdown.js
new file mode 100644
index 0000000000..6e1142b4b4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_sanitizeOnShutdown.js
@@ -0,0 +1,72 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to ensure that Firefox sanitizes site security
+// service data on shutdown if configured to do so.
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ TestUtils: "resource://testing-common/TestUtils.jsm",
+ Sanitizer: "resource:///modules/Sanitizer.jsm",
+});
+
+Sanitizer.onStartup();
+
+// This helps us away from test timed out. If service worker manager(swm) hasn't
+// been initilaized before profile-change-teardown, this test would fail due to
+// the shutdown blocker added by swm. Normally, swm should be initialized before
+// that and the similar crash signatures are fixed. So, assume this cannot
+// happen in the real world and initilaize swm here as a workaround.
+const swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService(
+ Ci.nsIServiceWorkerManager
+);
+
+function getStateFileContents() {
+ let stateFile = do_get_profile();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ ok(stateFile.exists());
+ return readFile(stateFile);
+}
+
+add_task(async function run_test() {
+ Services.prefs.setIntPref("test.datastorage.write_timer_ms", 100);
+ do_get_profile();
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+ let header = "max-age=50000";
+ SSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("http://example.com"),
+ header,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ await TestUtils.topicObserved(
+ "data-storage-written",
+ (_, data) => data == SSS_STATE_FILE_NAME
+ );
+ let stateFileContents = getStateFileContents();
+ ok(
+ stateFileContents.includes("example.com"),
+ "should have written out state file"
+ );
+
+ // Configure Firefox to clear this data on shutdown.
+ Services.prefs.setBoolPref(
+ Sanitizer.PREF_SHUTDOWN_BRANCH + "siteSettings",
+ true
+ );
+ Services.prefs.setBoolPref(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, true);
+
+ // Simulate shutdown.
+ Services.obs.notifyObservers(null, "profile-change-teardown");
+ Services.obs.notifyObservers(null, "profile-before-change");
+
+ equal(getStateFileContents(), "", "state file should be empty");
+});
diff --git a/security/manager/ssl/tests/unit/test_sss_savestate.js b/security/manager/ssl/tests/unit/test_sss_savestate.js
new file mode 100644
index 0000000000..300afe4983
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sss_savestate.js
@@ -0,0 +1,122 @@
+/* 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/. */
+"use strict";
+
+// The purpose of this test is to see that the site security service properly
+// writes its state file.
+
+const EXPECTED_ENTRIES = 5;
+const EXPECTED_HSTS_COLUMNS = 4;
+var gProfileDir = null;
+
+const NON_ISSUED_KEY_HASH = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+
+// For reference, the format of the state file is a list of:
+// <domain name> <expiration time in milliseconds>,<sts status>,<includeSubdomains>
+// separated by newlines ('\n')
+
+function checkStateWritten(aSubject, aTopic, aData) {
+ if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
+ return;
+ }
+
+ equal(aData, SSS_STATE_FILE_NAME);
+
+ let stateFile = gProfileDir.clone();
+ stateFile.append(SSS_STATE_FILE_NAME);
+ ok(stateFile.exists());
+ let stateFileContents = readFile(stateFile);
+ // the last line is removed because it's just a trailing newline
+ let lines = stateFileContents.split("\n").slice(0, -1);
+ equal(lines.length, EXPECTED_ENTRIES);
+ let sites = {}; // a map of domain name -> [the entry in the state file]
+ for (let line of lines) {
+ let parts = line.split("\t");
+ let host = parts[0];
+ let entry = parts[3].split(",");
+ let expectedColumns = EXPECTED_HSTS_COLUMNS;
+ equal(entry.length, expectedColumns);
+ sites[host] = entry;
+ }
+
+ // We can receive multiple data-storage-written events. In particular, we
+ // may receive one where DataStorage wrote out data before we were done
+ // processing all of our headers. In this case, the data may not be
+ // as we expect. We only care about the final one being correct, however,
+ // so we return and wait for the next event if things aren't as we expect.
+ // sites[url][1] corresponds to SecurityPropertySet (if 1) and
+ // SecurityPropertyUnset (if 0)
+ // sites[url][2] corresponds to includeSubdomains
+ if (sites["includesubdomains.preloaded.test:HSTS"][1] != 1) {
+ return;
+ }
+ if (sites["includesubdomains.preloaded.test:HSTS"][2] != 0) {
+ return;
+ }
+ if (sites["a.example.com:HSTS"][1] != 1) {
+ return;
+ }
+ if (sites["a.example.com:HSTS"][2] != 1) {
+ return;
+ }
+ if (sites["b.example.com:HSTS"][1] != 1) {
+ return;
+ }
+ if (sites["b.example.com:HSTS"][2] != 0) {
+ return;
+ }
+ if (sites["c.c.example.com:HSTS"][1] != 1) {
+ return;
+ }
+ if (sites["c.c.example.com:HSTS"][2] != 1) {
+ return;
+ }
+ if (sites["d.example.com:HSTS"][1] != 1) {
+ return;
+ }
+ if (sites["d.example.com:HSTS"][2] != 0) {
+ return;
+ }
+
+ do_test_finished();
+}
+
+function run_test() {
+ Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
+ Services.prefs.setIntPref("test.datastorage.write_timer_ms", 100);
+ gProfileDir = do_get_profile();
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+
+ let uris = [
+ Services.io.newURI("http://includesubdomains.preloaded.test"),
+ Services.io.newURI("http://a.example.com"),
+ Services.io.newURI("http://b.example.com"),
+ Services.io.newURI("http://c.c.example.com"),
+ Services.io.newURI("http://d.example.com"),
+ ];
+
+ for (let i = 0; i < 1000; i++) {
+ let uriIndex = i % uris.length;
+ // vary max-age
+ let maxAge = "max-age=" + i * 1000;
+ // alternate setting includeSubdomains
+ let includeSubdomains = i % 2 == 0 ? "; includeSubdomains" : "";
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+ SSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uris[uriIndex],
+ maxAge + includeSubdomains,
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ }
+
+ do_test_pending();
+ Services.obs.addObserver(checkStateWritten, "data-storage-written");
+}
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign.js b/security/manager/ssl/tests/unit/test_startcom_wosign.js
new file mode 100644
index 0000000000..1fda3a9a96
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign.js
@@ -0,0 +1,67 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests handling of certificates issued by StartCom and WoSign. If such
+// certificates have a notBefore before 21 October 2016, they are handled
+// normally. Otherwise, they are treated as revoked.
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+function loadCertWithTrust(certName, trustString) {
+ addCertFromFile(
+ certdb,
+ "test_startcom_wosign/" + certName + ".pem",
+ trustString
+ );
+}
+
+function certFromFile(certName) {
+ return constructCertFromFile("test_startcom_wosign/" + certName + ".pem");
+}
+
+function checkEndEntity(cert, expectedResult) {
+ // (new Date("2016-11-01")).getTime() / 1000
+ const VALIDATION_TIME = 1477958400;
+ return checkCertErrorGenericAtTime(
+ certdb,
+ cert,
+ expectedResult,
+ certificateUsageSSLServer,
+ VALIDATION_TIME
+ );
+}
+
+add_task(async function() {
+ loadCertWithTrust("ca", "CTu,,");
+ // This is not a real StartCom CA - it merely has the same distinguished name
+ // as one (namely "/C=IL/O=StartCom Ltd./CN=StartCom Certification Authority
+ // G2", encoded with PrintableStrings). By checking for specific DNs, we can
+ // enforce the date-based policy in a way that is testable.
+ loadCertWithTrust("StartComCA", ",,");
+ await checkEndEntity(
+ certFromFile("StartCom-before-cutoff"),
+ PRErrorCodeSuccess
+ );
+ await checkEndEntity(
+ certFromFile("StartCom-after-cutoff"),
+ SEC_ERROR_REVOKED_CERTIFICATE
+ );
+
+ // Similarly, this is not a real WoSign CA. It has the same distinguished name
+ // as "/C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign",
+ // encoded with PrintableStrings).
+ loadCertWithTrust("WoSignCA", ",,");
+ await checkEndEntity(
+ certFromFile("WoSign-before-cutoff"),
+ PRErrorCodeSuccess
+ );
+ await checkEndEntity(
+ certFromFile("WoSign-after-cutoff"),
+ SEC_ERROR_REVOKED_CERTIFICATE
+ );
+});
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem
new file mode 100644
index 0000000000..031b2e8674
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDHzCCAgegAwIBAgIUMzsA8O2TjNkD5ARjfvom8NIOGV0wDQYJKoZIhvcNAQEL
+BQAwUzELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xLDAqBgNV
+BAMTI1N0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEcyMCIYDzIwMTYx
+MDIyMDAwMDAwWhgPMjAxNzEwMjIwMDAwMDBaMCAxHjAcBgNVBAMMFVN0YXJ0Q29t
+LWFmdGVyLWN1dG9mZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqI
+UahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvi
+r1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/x
+fq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD
+7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnv
+uRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj
++nJRxDHVA6zaGAo17Y0CAwEAAaMaMBgwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20w
+DQYJKoZIhvcNAQELBQADggEBAF45Nn67efx82OhjZ865DeQdHjTL4IhIo3dcZwf2
+1fLgV1+ZXDFUewnE0Sw7pR57uUKGmaISjoF2lXvNm0U/5Nq6dUbhN9KtnRifaM3x
+NavEvpTZAwERnnphDJFlgSJAFSPWLGZDULl7JaZyLyQe0AoQXAFTyghkXrk/QA5m
+1LfDYqLiwL1G4NHLGu7QRvLUZ/pxkLS3PaKfZVILCnKiOvI7bmPq+2U7H6ZgTPPP
+24Sy/E9AKV5I6IEojGuM6qP+QYgLANOaGygWrIVJ+QpS36V8uRRbetzTcJUHDesw
+iMJaepPkWnFNhz2CSS3HyoG9wu/RqfRwiLWqjsOs0dEZTRw=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem.certspec
new file mode 100644
index 0000000000..9f0fe22fcd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-after-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2
+subject:StartCom-after-cutoff
+validity:20161022-20171022
+extension:subjectAlternativeName:example.com
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem
new file mode 100644
index 0000000000..61ef3cbf72
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIUF7D74A6qEV/s6DRPxUqAk7N+bX8wDQYJKoZIhvcNAQEL
+BQAwUzELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xLDAqBgNV
+BAMTI1N0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEcyMCIYDzIwMTUx
+MDIyMDAwMDAwWhgPMjAxNzEwMjIwMDAwMDBaMCExHzAdBgNVBAMMFlN0YXJ0Q29t
+LWJlZm9yZS1jdXRvZmYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAGjGjAYMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29t
+MA0GCSqGSIb3DQEBCwUAA4IBAQB2r07MUxWXUj7gAlQJUKNhNJ9Fqlt13751C4Lo
+KL9TeUeROqDviPtpwoigG0NV+IMWdXJorRmbkcFmgBOFOZmhyspi2BJ4rCCWC1FI
+WFe9SlFsuka7a7sAov9B3ClLJE+JX48H84kZ1yMq1jQmv0tAko9di3d7oMhHpLMp
+tBzOQUnuq/kBeS5VlHxyZoRxj7U0MSIORhIOkih/pRzmeLDnn7xBj9FZ6ipoukRL
+n3l3wTmj9/aar7DhhgA8QvD6ZtNHXP8ZnheVqW07OZVjWcrzg7nID3+j4LWOZNq/
+hdm1nZG5DltMk7JqIGuA4PmdStXQNftEVbeWMzdQ+8cb/wmC
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem.certspec
new file mode 100644
index 0000000000..b7fbd4954e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/StartCom-before-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2
+subject:StartCom-before-cutoff
+validity:20151022-20171022
+extension:subjectAlternativeName:example.com
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem b/security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem
new file mode 100644
index 0000000000..98ce37dabe
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDDzCCAfegAwIBAgIUY5ffXJsiXdmKh2ybX2nxSg57mRwwDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDUwMDEwMTAw
+MDAwMFowUzELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xLDAq
+BgNVBAMTI1N0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEcyMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq
+5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SSc
+An7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39
+ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYk
+zBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3u
+JtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQAB
+ox0wGzALBgNVHQ8EBAMCAQYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
+AQEAe6mpnTBxkfYlTrzuvtKQ73A5KCEBfmtRGZaMRPh8rSxdnd8Zf2zTl3eUHYX1
+y+pqY1U9YJOkR1TKfbtvQll7bnD5RJ+FJD6eA9QEwSpII7v66teRS7wBPxQrvsrq
+SWVZuHyKMpBG3148/nmrPvaIB5kAO5fdedIET88PL3K8LM1XzoVZc272V577pmeD
+4N116ghEIz7rhrR6yoGVN0s2TXT7H5AlOscZuVRkirickVUbjSWZma+mYJWdd8hi
+Cjufl43OY4EY31w9qO8BKxx8ZugmoSmp93VyMvqWZAV1AoxgxMpOsC9e7ZB83yrU
+luXN7wik7bf+xNz78Be6XhBOBA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem.certspec b/security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem.certspec
new file mode 100644
index 0000000000..d1e3c4c4f4
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/StartComCA.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:printableString/C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem
new file mode 100644
index 0000000000..dc976c7c61
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDHzCCAgegAwIBAgIUC57N1fgWjCRoz95Wt5tIBI4U+AMwDQYJKoZIhvcNAQEL
+BQAwVTELMAkGA1UEBhMCQ04xGjAYBgNVBAoTEVdvU2lnbiBDQSBMaW1pdGVkMSow
+KAYDVQQDEyFDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBvZiBXb1NpZ24wIhgPMjAx
+NjEwMjIwMDAwMDBaGA8yMDE3MTAyMjAwMDAwMFowHjEcMBoGA1UEAwwTV29TaWdu
+LWFmdGVyLWN1dG9mZjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqI
+UahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvi
+r1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/x
+fq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD
+7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnv
+uRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj
++nJRxDHVA6zaGAo17Y0CAwEAAaMaMBgwFgYDVR0RBA8wDYILZXhhbXBsZS5jb20w
+DQYJKoZIhvcNAQELBQADggEBAK+d8oCtnO+HwtV1nEqKAd1/3ATIGmbDJn5kQWKY
+m5cVi4NO6UivqhQO7Z8if+sO5DuX/VoodC+LESuv8NQdn8pDH1Ou7WUtm9xyG5Ly
+j+D+WgpjmxKfBfD2L0Pd4b8ZrCg8Az3wmz6Jz7MwEU8FqmScQkxJN4JH7S2QlmLa
+asrPwrFKy9uOD3jSW6d5H3gv+nV7thIiMASor3up5KV//UdVqWTISOpBZdJKE8wp
+QtlPfvIgP5DbrfhtOzDYLuPnO3WzT7oC4Pau0eMXlT5EKC41p+fXGqtU402H0xMB
+5ftgAboQ42FDDkp+y13a7wfm1KqcaahWR9CQKrO1ag7Mspk=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem.certspec
new file mode 100644
index 0000000000..2afb5d6ea6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-after-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign
+subject:WoSign-after-cutoff
+validity:20161022-20171022
+extension:subjectAlternativeName:example.com
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem
new file mode 100644
index 0000000000..8dc84c549c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIUI4ScwRAmd+wFemr14yqwfKljyEswDQYJKoZIhvcNAQEL
+BQAwVTELMAkGA1UEBhMCQ04xGjAYBgNVBAoTEVdvU2lnbiBDQSBMaW1pdGVkMSow
+KAYDVQQDEyFDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBvZiBXb1NpZ24wIhgPMjAx
+NTEwMjIwMDAwMDBaGA8yMDE3MTAyMjAwMDAwMFowHzEdMBsGA1UEAwwUV29TaWdu
+LWJlZm9yZS1jdXRvZmYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAGjGjAYMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29t
+MA0GCSqGSIb3DQEBCwUAA4IBAQAtq4bkF5Dh+Gouc8D+v4c5Siol9ucFZziegFTe
+CwCel//tShZwFSObespSLFVjrPPrEhdv+9Wl7/faHaFEIYjX/xBJ+ZsdKal56E/+
+hnuXzCWhojpnMcMzWRVPBzKo0KD3g673pdC6m86r78eXDwG+8zYak+IQ4CJAaUOd
+hT23GDajLjBbUNdT1pBjIKxYa2iNJOyQ8SVil18r2c1/AtoejS0xZda2MO+FDnf5
+01413PgAU1Lf1C6tEQNnncLtMQhT+LKlDIMo9PolJcvMto57f/awQhyiYMLvIAE+
+O8NIYXn0cyn+dbpaL59hmx1AJJwtyn3RsskVKQjle7ky3gt7
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem.certspec b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem.certspec
new file mode 100644
index 0000000000..224522bf01
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSign-before-cutoff.pem.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign
+subject:WoSign-before-cutoff
+validity:20151022-20171022
+extension:subjectAlternativeName:example.com
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem
new file mode 100644
index 0000000000..490e22cfdd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfmgAwIBAgIUYx3n8sfSMjvHAMPX4R896bPDFB4wDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDUwMDEwMTAw
+MDAwMFowVTELMAkGA1UEBhMCQ04xGjAYBgNVBAoTEVdvU2lnbiBDQSBMaW1pdGVk
+MSowKAYDVQQDEyFDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBvZiBXb1NpZ24wggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT
+2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzV
+JJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8N
+jf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCA
+BiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVh
+He4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMB
+AAGjHTAbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4IBAQABpMKSJzIqD+0DHDAzcBl99nTDTYalGAbYvKvnxKOiD0WVw8K/Lp+Ofoh2
+ZicDP35liyEPiyZMfR5IIyfPwPkgvRTIrZtX87SFxHcrsvZHVj+ilijOSWBx9Tgy
+z1PhkAdTg49ljzsKa77+nEKDkRXSWIbmt3MUymvCSMq1HXUFOwpPSqB98ssvjMhs
+acKcMnpSe5m39Z9OIAczhsR64otg+flV4XH2ocdE0ywBzMnw4HVY49TXEkojs1bY
+aXgkTEaFrFKj45UeGw2yBbftZB18bhPemOtkxiMR7ChOtilMKHGI/qH5rvQJXuwV
+HkC3s3YcMbHtx0w3aYSC8caspSxt
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem.certspec b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem.certspec
new file mode 100644
index 0000000000..8c293bf9b5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/WoSignCA.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:printableString/C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem b/security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem
new file mode 100644
index 0000000000..560684144f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICyTCCAbGgAwIBAgIUGUKJPXqFyHlaMLy7vfWOkCn+6RswDQYJKoZIhvcNAQEL
+BQAwDTELMAkGA1UEAwwCY2EwIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDUwMDEwMTAw
+MDAwMFowDTELMAkGA1UEAwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwG
+m24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJr
+bA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4
+SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3
+/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+Z
+FzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAsGA1UdDwQEAwIBBjAMBgNV
+HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCV2ftsqVDw6pxfW7ToIowvicAL
+H1Gwj5AL1aVpPOBFHXJGkKbYkAVl62t2R8OTJzrqjNo9D4sgRKKtqJXwmqlAEyuk
+dsA18pA/0/jIEU12/Oq68ra6HB+efTfxQrm4/uU2Yr2UNcCcAo8nKtzxsPsPAiMm
+hqKCRkCtYZjGRk18S2y85XihanfTkAqBGlI4GC7q4Otnq7j7y9FwtiQ7iWP+IYQv
+OlS+FwCoctrNLGOQE9Jzc4GRD2zlwsbWfHDQUvB1p3tjKwtT35EtWf6F5NpKLWTm
+m3SmHIG6pvIePX0zhlac1tR/uvCSpyFAiQ7nG4XDpNyPle1WEFreiS8+ITeM
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem.certspec b/security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem.certspec
new file mode 100644
index 0000000000..efd24b1532
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/ca.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:ca
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
diff --git a/security/manager/ssl/tests/unit/test_startcom_wosign/moz.build b/security/manager/ssl/tests/unit/test_startcom_wosign/moz.build
new file mode 100644
index 0000000000..657bcf6bed
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_startcom_wosign/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'StartCom-after-cutoff.pem',
+# 'StartCom-before-cutoff.pem',
+# 'StartComCA.pem',
+# 'WoSign-after-cutoff.pem',
+# 'WoSign-before-cutoff.pem',
+# 'WoSignCA.pem',
+# 'ca.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
diff --git a/security/manager/ssl/tests/unit/test_sts_fqdn.js b/security/manager/ssl/tests/unit/test_sts_fqdn.js
new file mode 100644
index 0000000000..d8ecbf1530
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sts_fqdn.js
@@ -0,0 +1,50 @@
+/* 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/.
+ */
+"use strict";
+
+function run_test() {
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ let uri = Services.io.newURI("https://example.com");
+ let uri1 = Services.io.newURI("https://example.com.");
+ let uri2 = Services.io.newURI("https://example.com..");
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri1, 0));
+ // These cases are only relevant as long as bug 1118522 hasn't been fixed.
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri2, 0));
+
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+ SSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1000;includeSubdomains",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri1, 0));
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri2, 0));
+
+ SSService.resetState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0);
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri1, 0));
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri2, 0));
+
+ // Somehow creating this malformed URI succeeds - we need to handle it
+ // gracefully.
+ uri = Services.io.newURI("https://../foo");
+ equal(uri.host, "..");
+ throws(
+ () => {
+ SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0);
+ },
+ /NS_ERROR_UNEXPECTED/,
+ "Malformed URI should be rejected"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js b/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js
new file mode 100644
index 0000000000..1950d7b1bd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sts_ipv4_ipv6.js
@@ -0,0 +1,59 @@
+"use strict";
+
+function check_ip(s, v, ip) {
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+
+ let str = "https://";
+ if (v == 6) {
+ str += "[";
+ }
+ str += ip;
+ if (v == 6) {
+ str += "]";
+ }
+ str += "/";
+
+ let uri = Services.io.newURI(str);
+ ok(!s.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ let parsedMaxAge = {};
+ let parsedIncludeSubdomains = {};
+ s.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1000;includeSubdomains",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
+ {},
+ parsedMaxAge,
+ parsedIncludeSubdomains
+ );
+ ok(
+ !s.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
+ "URI should not be secure if it contains an IP address"
+ );
+
+ /* Test that processHeader will ignore headers for an uri, if the uri
+ * contains an IP address not a hostname.
+ * If processHeader indeed ignore the header, then the output parameters will
+ * remain empty, and we shouldn't see the values passed as the header.
+ */
+ notEqual(parsedMaxAge.value, 1000);
+ notEqual(parsedIncludeSubdomains.value, true);
+ notEqual(parsedMaxAge.value, undefined);
+ notEqual(parsedIncludeSubdomains.value, undefined);
+}
+
+function run_test() {
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+
+ check_ip(SSService, 4, "127.0.0.1");
+ check_ip(SSService, 4, "10.0.0.1");
+ check_ip(SSService, 6, "2001:db8::1");
+ check_ip(SSService, 6, "1080::8:800:200C:417A");
+}
diff --git a/security/manager/ssl/tests/unit/test_sts_parser.js b/security/manager/ssl/tests/unit/test_sts_parser.js
new file mode 100644
index 0000000000..8475d2e558
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sts_parser.js
@@ -0,0 +1,146 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 sts=2
+ * 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/. */
+
+"use strict";
+
+// STS parser tests
+
+let sss = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);
+let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+].createInstance(Ci.nsITransportSecurityInfo);
+
+function testSuccess(header, expectedMaxAge, expectedIncludeSubdomains) {
+ let dummyUri = Services.io.newURI("https://foo.com/bar.html");
+ let maxAge = {};
+ let includeSubdomains = {};
+
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ dummyUri,
+ header,
+ secInfo,
+ 0,
+ sss.SOURCE_ORGANIC_REQUEST,
+ {},
+ maxAge,
+ includeSubdomains
+ );
+
+ equal(maxAge.value, expectedMaxAge, "Did not correctly parse maxAge");
+ equal(
+ includeSubdomains.value,
+ expectedIncludeSubdomains,
+ "Did not correctly parse presence/absence of includeSubdomains"
+ );
+}
+
+function testFailure(header) {
+ let dummyUri = Services.io.newURI("https://foo.com/bar.html");
+ let maxAge = {};
+ let includeSubdomains = {};
+
+ throws(
+ () => {
+ sss.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ dummyUri,
+ header,
+ secInfo,
+ 0,
+ sss.SOURCE_ORGANIC_REQUEST,
+ {},
+ maxAge,
+ includeSubdomains
+ );
+ },
+ /NS_ERROR_FAILURE/,
+ "Parsed invalid header: " + header
+ );
+}
+
+function run_test() {
+ // SHOULD SUCCEED:
+ testSuccess("max-age=100", 100, false);
+ testSuccess("max-age =100", 100, false);
+ testSuccess(" max-age=100", 100, false);
+ testSuccess("max-age = 100 ", 100, false);
+ testSuccess('max-age = "100" ', 100, false);
+ testSuccess('max-age="100"', 100, false);
+ testSuccess(' max-age ="100" ', 100, false);
+ testSuccess('\tmax-age\t=\t"100"\t', 100, false);
+ testSuccess("max-age = 100 ", 100, false);
+
+ testSuccess("maX-aGe=100", 100, false);
+ testSuccess("MAX-age =100", 100, false);
+ testSuccess("max-AGE=100", 100, false);
+ testSuccess("Max-Age = 100 ", 100, false);
+ testSuccess("MAX-AGE = 100 ", 100, false);
+
+ testSuccess("max-age=100;includeSubdomains", 100, true);
+ testSuccess("max-age=100\t; includeSubdomains", 100, true);
+ testSuccess(" max-age=100; includeSubdomains", 100, true);
+ testSuccess("max-age = 100 ; includeSubdomains", 100, true);
+ testSuccess(
+ "max-age = 100 ; includeSubdomains",
+ 100,
+ true
+ );
+
+ testSuccess("maX-aGe=100; includeSUBDOMAINS", 100, true);
+ testSuccess("MAX-age =100; includeSubDomains", 100, true);
+ testSuccess("max-AGE=100; iNcLuDeSuBdoMaInS", 100, true);
+ testSuccess("Max-Age = 100; includesubdomains ", 100, true);
+ testSuccess("INCLUDESUBDOMAINS;MaX-AgE = 100 ", 100, true);
+ // Turns out, the actual directive is entirely optional (hence the
+ // trailing semicolon)
+ testSuccess("max-age=100;includeSubdomains;", 100, true);
+
+ // these are weird tests, but are testing that some extended syntax is
+ // still allowed (but it is ignored)
+ testSuccess("max-age=100 ; includesubdomainsSomeStuff", 100, false);
+ testSuccess(
+ "\r\n\t\t \tcompletelyUnrelated = foobar; max-age= 34520103" +
+ "\t \t; alsoUnrelated;asIsThis;\tincludeSubdomains\t\t \t",
+ 34520103,
+ true
+ );
+ testSuccess('max-age=100; unrelated="quoted \\"thingy\\""', 100, false);
+
+ // SHOULD FAIL:
+ // invalid max-ages
+ testFailure("max-age");
+ testFailure("max-age ");
+ testFailure("max-age=p");
+ testFailure("max-age=*1p2");
+ testFailure("max-age=.20032");
+ testFailure("max-age=!20032");
+ testFailure("max-age==20032");
+
+ // invalid headers
+ testFailure("foobar");
+ testFailure("maxage=100");
+ testFailure("maxa-ge=100");
+ testFailure("max-ag=100");
+ testFailure("includesubdomains");
+ testFailure(";");
+ testFailure('max-age="100');
+ // The max-age directive here doesn't conform to the spec, so it MUST
+ // be ignored. Consequently, the REQUIRED max-age directive is not
+ // present in this header, and so it is invalid.
+ testFailure("max-age=100, max-age=200; includeSubdomains");
+ testFailure("max-age=100 includesubdomains");
+ testFailure("max-age=100 bar foo");
+ testFailure("max-age=100randomstuffhere");
+ // All directives MUST appear only once in an STS header field.
+ testFailure("max-age=100; max-age=200");
+ testFailure("includeSubdomains; max-age=200; includeSubdomains");
+ testFailure("max-age=200; includeSubdomains; includeSubdomains");
+ // The includeSubdomains directive is valueless.
+ testFailure("max-age=100; includeSubdomains=unexpected");
+ // LWS must have at least one space or horizontal tab
+ testFailure("\r\nmax-age=200");
+}
diff --git a/security/manager/ssl/tests/unit/test_sts_preload_dynamic.js b/security/manager/ssl/tests/unit/test_sts_preload_dynamic.js
new file mode 100644
index 0000000000..f66f13ddd9
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sts_preload_dynamic.js
@@ -0,0 +1,86 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+"use strict";
+
+// Tests the dynamic preloading of HPKP hosts:
+// * checks that preloads can be set
+// * checks that includeSubdomains is honored
+// * checks that clearing preloads works correctly
+// * checks that clearing a host's HSTS state via a header correctly
+// overrides dynamic preload entries
+
+function run_test() {
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ let secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+ ].createInstance(Ci.nsITransportSecurityInfo);
+ let unlikelyHost = "highlyunlikely.example.com";
+ let uri = Services.io.newURI("https://" + unlikelyHost);
+ let subDomainUri = Services.io.newURI("https://subdomain." + unlikelyHost);
+
+ // first check that a host probably not on the preload list is not identified
+ // as an sts host
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ // now add a preload entry for this host
+ SSService.setHSTSPreload(unlikelyHost, false, Date.now() + 60000);
+
+ // check that it's now an STS host
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ // check that it's honoring the fact we set includeSubdomains to false
+ ok(
+ !SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ 0
+ )
+ );
+
+ // clear the non-preloaded entries
+ SSService.clearAll();
+
+ // check that it's still an STS host
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ // clear the preloads
+ SSService.clearPreloads();
+
+ // Check that it's no longer an STS host now that the preloads have been
+ // cleared
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ // Now let's do the same, this time with includeSubdomains on
+ SSService.setHSTSPreload(unlikelyHost, true, Date.now() + 60000);
+
+ // check that it's now an STS host
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ // check that it's now including subdomains
+ ok(
+ SSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ 0
+ )
+ );
+
+ // Now let's simulate overriding the entry by setting an entry from a header
+ // with max-age set to 0
+ SSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=0",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+
+ // this should no longer be an HSTS host
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+}
diff --git a/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js b/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js
new file mode 100644
index 0000000000..142825829e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js
@@ -0,0 +1,455 @@
+"use strict";
+
+var gSSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+);
+
+function Observer() {}
+Observer.prototype = {
+ observe(subject, topic, data) {
+ if (topic == "last-pb-context-exited") {
+ run_next_test();
+ }
+ },
+};
+
+var gObserver = new Observer();
+var secInfo = Cc[
+ "@mozilla.org/security/transportsecurityinfo;1"
+].createInstance(Ci.nsITransportSecurityInfo);
+
+function cleanup() {
+ Services.obs.removeObserver(gObserver, "last-pb-context-exited");
+ gSSService.clearAll();
+}
+
+function run_test() {
+ do_get_profile();
+
+ registerCleanupFunction(cleanup);
+ Services.obs.addObserver(gObserver, "last-pb-context-exited");
+
+ add_test(test_part1);
+ add_test(test_private_browsing1);
+ add_test(test_private_browsing2);
+
+ run_next_test();
+}
+
+function test_part1() {
+ // check that a host not in the list is not identified as an sts host
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://nonexistent.example.com"),
+ 0
+ )
+ );
+
+ // check that an ancestor domain is not identified as an sts host
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://com"),
+ 0
+ )
+ );
+
+ // check that the pref to toggle using the preload list works
+ Services.prefs.setBoolPref(
+ "network.stricttransportsecurity.preloadlist",
+ false
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ Services.prefs.setBoolPref(
+ "network.stricttransportsecurity.preloadlist",
+ true
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+
+ // check that a subdomain is an sts host (includeSubdomains is set)
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://subdomain.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+
+ // check that another subdomain is an sts host (includeSubdomains is set)
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://a.b.c.def.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+
+ // check that a subdomain is not an sts host (includeSubdomains is not set)
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI(
+ "https://subdomain.noincludesubdomains.preloaded.test"
+ ),
+ 0
+ )
+ );
+
+ // check that a host with a dot on the end won't break anything
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://notsts.nonexistent.example.com."),
+ 0
+ )
+ );
+
+ // check that processing a header with max-age: 0 will remove a preloaded
+ // site from the list
+ let uri = Services.io.newURI("https://includesubdomains.preloaded.test");
+ let subDomainUri = Services.io.newURI(
+ "https://subdomain.includesubdomains.preloaded.test"
+ );
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=0",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ 0
+ )
+ );
+ // check that processing another header (with max-age non-zero) will
+ // re-enable a site's sts status
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1000",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+ // but this time include subdomains was not set, so test for that
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ 0
+ )
+ );
+ gSSService.clearAll();
+
+ // check that processing a header with max-age: 0 from a subdomain of a site
+ // will not remove that (ancestor) site from the list
+ uri = Services.io.newURI(
+ "https://subdomain.noincludesubdomains.preloaded.test"
+ );
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=0",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://noincludesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ uri = Services.io.newURI(
+ "https://subdomain.includesubdomains.preloaded.test"
+ );
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=0",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ // we received a header with "max-age=0", so we have "no information"
+ // regarding the sts state of subdomain.includesubdomains.preloaded.test specifically,
+ // but it is actually still an STS host, because of the preloaded
+ // includesubdomains.preloaded.test including subdomains.
+ // Here's a drawing:
+ // |-- includesubdomains.preloaded.test (in preload list, includes subdomains) IS sts host
+ // |-- subdomain.includesubdomains.preloaded.test IS sts host
+ // | `-- another.subdomain.includesubdomains.preloaded.test IS sts host
+ // `-- sibling.includesubdomains.preloaded.test IS sts host
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://subdomain.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sibling.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI(
+ "https://another.subdomain.includesubdomains.preloaded.test"
+ ),
+ 0
+ )
+ );
+
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1000",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ // Here's what we have now:
+ // |-- includesubdomains.preloaded.test (in preload list, includes subdomains) IS sts host
+ // |-- subdomain.includesubdomains.preloaded.test (include subdomains is false) IS sts host
+ // | `-- another.subdomain.includesubdomains.preloaded.test IS NOT sts host
+ // `-- sibling.includesubdomains.preloaded.test IS sts host
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://subdomain.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://sibling.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI(
+ "https://another.subdomain.includesubdomains.preloaded.test"
+ ),
+ 0
+ )
+ );
+
+ // Test that an expired non-private browsing entry results in correctly
+ // identifying a host that is on the preload list as no longer sts.
+ // (This happens when we're in regular browsing mode, we get a header from
+ // a site on the preload list, and that header later expires. We need to
+ // then treat that host as no longer an sts host.)
+ // (sanity check first - this should be in the preload list)
+ uri = Services.io.newURI("https://includesubdomains2.preloaded.test");
+ ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1",
+ secInfo,
+ 0,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ do_timeout(1250, function() {
+ ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+ run_next_test();
+ });
+}
+
+const IS_PRIVATE = Ci.nsISocketProvider.NO_PERMANENT_STORAGE;
+
+function test_private_browsing1() {
+ gSSService.clearAll();
+ let uri = Services.io.newURI("https://includesubdomains.preloaded.test");
+ let subDomainUri = Services.io.newURI(
+ "https://a.b.c.subdomain.includesubdomains.preloaded.test"
+ );
+ // sanity - includesubdomains.preloaded.test is preloaded, includeSubdomains set
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ IS_PRIVATE
+ )
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ IS_PRIVATE
+ )
+ );
+
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=0",
+ secInfo,
+ IS_PRIVATE,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ IS_PRIVATE
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ IS_PRIVATE
+ )
+ );
+
+ // check adding it back in
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1000",
+ secInfo,
+ IS_PRIVATE,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ IS_PRIVATE
+ )
+ );
+ // but no includeSubdomains this time
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ IS_PRIVATE
+ )
+ );
+
+ // do the hokey-pokey...
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=0",
+ secInfo,
+ IS_PRIVATE,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ IS_PRIVATE
+ )
+ );
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ subDomainUri,
+ IS_PRIVATE
+ )
+ );
+
+ // Test that an expired private browsing entry results in correctly
+ // identifying a host that is on the preload list as no longer sts.
+ // (This happens when we're in private browsing mode, we get a header from
+ // a site on the preload list, and that header later expires. We need to
+ // then treat that host as no longer an sts host.)
+ // (sanity check first - this should be in the preload list)
+ uri = Services.io.newURI("https://includesubdomains2.preloaded.test");
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ IS_PRIVATE
+ )
+ );
+ gSSService.processHeader(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ "max-age=1",
+ secInfo,
+ IS_PRIVATE,
+ Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
+ );
+ do_timeout(1250, function() {
+ ok(
+ !gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ uri,
+ IS_PRIVATE
+ )
+ );
+ // Simulate leaving private browsing mode
+ Services.obs.notifyObservers(null, "last-pb-context-exited");
+ });
+}
+
+function test_private_browsing2() {
+ // if this test gets this far, it means there's a private browsing service
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+ // the includesubdomains.preloaded.test entry has includeSubdomains set
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://subdomain.includesubdomains.preloaded.test"),
+ 0
+ )
+ );
+
+ // Now that we're out of private browsing mode, we need to make sure
+ // we've "forgotten" that we "forgot" this site's sts status.
+ ok(
+ gSSService.isSecureURI(
+ Ci.nsISiteSecurityService.HEADER_HSTS,
+ Services.io.newURI("https://includesubdomains2.preloaded.test"),
+ 0
+ )
+ );
+
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/test_sts_preloadlist_selfdestruct.js b/security/manager/ssl/tests/unit/test_sts_preloadlist_selfdestruct.js
new file mode 100644
index 0000000000..897fcbd3be
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_sts_preloadlist_selfdestruct.js
@@ -0,0 +1,22 @@
+"use strict";
+
+function run_test() {
+ let SSService = Cc["@mozilla.org/ssservice;1"].getService(
+ Ci.nsISiteSecurityService
+ );
+ let uri = Services.io.newURI("https://includesubdomains.preloaded.test");
+
+ // check that a host on the preload list is identified as an sts host
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ // now simulate that it's 19 weeks later than it actually is
+ let offsetSeconds = 19 * 7 * 24 * 60 * 60;
+ Services.prefs.setIntPref("test.currentTimeOffsetSeconds", offsetSeconds);
+
+ // check that the preloaded host is no longer considered sts
+ ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+
+ // just make sure we can get everything back to normal
+ Services.prefs.clearUserPref("test.currentTimeOffsetSeconds");
+ ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
+}
diff --git a/security/manager/ssl/tests/unit/test_validity.js b/security/manager/ssl/tests/unit/test_validity.js
new file mode 100644
index 0000000000..1809faf24c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity.js
@@ -0,0 +1,108 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests that chains containing an end-entity cert with an overly long validity
+// period are rejected.
+
+do_get_profile(); // Must be called before getting nsIX509CertDB
+const certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+const SERVER_PORT = 8888;
+
+function getOCSPResponder(expectedCertNames) {
+ let expectedPaths = expectedCertNames.slice();
+ return startOCSPResponder(
+ SERVER_PORT,
+ "www.example.com",
+ "test_validity",
+ expectedCertNames,
+ expectedPaths
+ );
+}
+
+function certFromFile(filename) {
+ return constructCertFromFile(`test_validity/${filename}`);
+}
+
+function loadCert(certFilename, trustString) {
+ addCertFromFile(certDB, `test_validity/${certFilename}`, trustString);
+}
+
+/**
+ * Asynchronously runs a single EV test.
+ *
+ * @param {Array} expectedNamesForOCSP
+ * An array of nicknames of the certs to be responded to.
+ * @param {String} rootCertFileName
+ * The file name of the root cert. Can begin with ".." to reference
+ * certs in folders other than "test_validity/".
+ * @param {Array} intCertFileNames
+ * An array of file names of any intermediate certificates.
+ * @param {String} endEntityCertFileName
+ * The file name of the end entity cert.
+ * @param {Boolean} expectedResult
+ * Whether the chain is expected to validate as EV.
+ */
+async function doEVTest(
+ expectedNamesForOCSP,
+ rootCertFileName,
+ intCertFileNames,
+ endEntityCertFileName,
+ expectedResult
+) {
+ clearOCSPCache();
+ let ocspResponder = getOCSPResponder(expectedNamesForOCSP);
+
+ loadCert(`${rootCertFileName}.pem`, "CTu,CTu,CTu");
+ for (let intCertFileName of intCertFileNames) {
+ loadCert(`${intCertFileName}.pem`, ",,");
+ }
+ await checkEVStatus(
+ certDB,
+ certFromFile(`${endEntityCertFileName}.pem`),
+ certificateUsageSSLServer,
+ expectedResult
+ );
+
+ await stopOCSPResponder(ocspResponder);
+}
+
+async function checkEVChains() {
+ // Chain with an end entity cert with a validity period that is acceptable
+ // for EV.
+ const intFullName = "ev_int_60_months-evroot";
+ let eeFullName = `ev_ee_27_months-${intFullName}`;
+ let expectedNamesForOCSP = gEVExpected
+ ? [intFullName, eeFullName]
+ : [eeFullName];
+ await doEVTest(
+ expectedNamesForOCSP,
+ "../test_ev_certs/evroot",
+ [intFullName],
+ eeFullName,
+ gEVExpected
+ );
+
+ // Chain with an end entity cert with a validity period that is too long
+ // for EV.
+ eeFullName = `ev_ee_28_months-${intFullName}`;
+ expectedNamesForOCSP = gEVExpected ? [intFullName, eeFullName] : [eeFullName];
+ await doEVTest(
+ expectedNamesForOCSP,
+ "../test_ev_certs/evroot",
+ [intFullName],
+ eeFullName,
+ false
+ );
+}
+
+add_task(async function() {
+ Services.prefs.setCharPref("network.dns.localDomains", "www.example.com");
+ Services.prefs.setIntPref("security.OCSP.enabled", 1);
+
+ await checkEVChains();
+});
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem b/security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem
new file mode 100644
index 0000000000..ea41b00545
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIUYYDQjys/AnzMoLUwrnp0+jqAmQMwDQYJKoZIhvcNAQEL
+BQAwIjEgMB4GA1UEAwwXZXZfaW50XzYwX21vbnRocy1ldnJvb3QwIhgPMjAxOTEx
+MTYxMjAwMDBaGA8yMDIyMDIxNjEyMDAwMFowMjEwMC4GA1UEAwwnZXZfZWVfMjdf
+bW9udGhzLWV2X2ludF82MF9tb250aHMtZXZyb290MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GGMIGDMGAGCCsG
+AQUFBwEBBFQwUjBQBggrBgEFBQcwAYZEaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4
+ODg4L2V2X2VlXzI3X21vbnRocy1ldl9pbnRfNjBfbW9udGhzLWV2cm9vdC8wHwYD
+VR0gBBgwFjAUBhIrBgEEAetJhRqFGoUaAYN0CQEwDQYJKoZIhvcNAQELBQADggEB
+AIP1/h4cT0t0hvLupI1lMwefjvlUtiXfTLW8u6BU8kEaENJybcWbIsCnPM2Bi2bz
+AwxQ6ByBMyicnxzgiHZofC9ATxXUrIKHLOk5I8aW6FNvEcD4KdJoju3u/EmbaDjP
+ORijvoa9gm9FQjl9SKYohfnjmOfNKcHQDQafnm4GHJV4nh7cIPfzl9kLrFYD9Hsu
+Ct2Yo8qEaWltZ84JqZX2GYD0eNLDt6WSxhIDB1WcRhMsa/XNBm5TwzfGt1yx/Mqb
+SHOKviSFUv5HTjJ/l5NDtzvUV+KVEcTA99Ve12Ok74JO7ChRG1U3nHONRKcfJWu9
+7va//Iq102BI6uilMkbGjlQ=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem.certspec
new file mode 100644
index 0000000000..d2c7fa1275
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_ee_27_months-ev_int_60_months-evroot.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ev_int_60_months-evroot
+subject:ev_ee_27_months-ev_int_60_months-evroot
+validity:823
+extension:authorityInformationAccess:http://www.example.com:8888/ev_ee_27_months-ev_int_60_months-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem b/security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem
new file mode 100644
index 0000000000..672175fe84
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIUD55RmXcxc2Ubvzh7CUeXD5mJm20wDQYJKoZIhvcNAQEL
+BQAwIjEgMB4GA1UEAwwXZXZfaW50XzYwX21vbnRocy1ldnJvb3QwIhgPMjAxOTEx
+MDEwMDAwMDBaGA8yMDIyMDMwNDAwMDAwMFowMjEwMC4GA1UEAwwnZXZfZWVfMjhf
+bW9udGhzLWV2X2ludF82MF9tb250aHMtZXZyb290MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvB
+xyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmT
+qyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5
+kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYS
+wHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwk
+BCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABo4GGMIGDMGAGCCsG
+AQUFBwEBBFQwUjBQBggrBgEFBQcwAYZEaHR0cDovL3d3dy5leGFtcGxlLmNvbTo4
+ODg4L2V2X2VlXzI4X21vbnRocy1ldl9pbnRfNjBfbW9udGhzLWV2cm9vdC8wHwYD
+VR0gBBgwFjAUBhIrBgEEAetJhRqFGoUaAYN0CQEwDQYJKoZIhvcNAQELBQADggEB
+AF1J9oNzg5u2a/E4nHHY1WgEPrMD3A4KFgprQxzzyebOqypMWK3+jXNdloF8AS8u
+5mIQoLNNw/bCFb0zgpK+0qKwxGHkreN+eJyfoi+zJr2Ygf4BmRlb02t79v4mYcFN
+rfCFSyvgwOI5aUr5n/BYFkUn38IAllJDn5igGGeROHSyOYZa9lOQZRAsh4vnas99
+V1RWo9E+hOmDH5be67HqJw4DHJ0sld8SWDxtjyXd6RbdJd4VIXScp2sk1W6F49Av
+otRZSrZFaZeS95a9SmN+xhCfpL3VNF3YdHCbfYeam/A/AUlD0TFBSoqJo6l9nT1c
+Of56pBg7jLup+dDdx7whu/w=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem.certspec
new file mode 100644
index 0000000000..2dcfb2e29c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_ee_28_months-ev_int_60_months-evroot.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ev_int_60_months-evroot
+subject:ev_ee_28_months-ev_int_60_months-evroot
+validity:854
+extension:authorityInformationAccess:http://www.example.com:8888/ev_ee_28_months-ev_int_60_months-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key
new file mode 100644
index 0000000000..09e044f5e0
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6iFGoRI4W1kH9
+braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEI
+eqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6
+iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Za
+qn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7
+LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs
+2hgKNe2NAgMBAAECggEBAJ7LzjhhpFTsseD+j4XdQ8kvWCXOLpl4hNDhqUnaosWs
+VZskBFDlrJ/gw+McDu+mUlpl8MIhlABO4atGPd6e6CKHzJPnRqkZKcXmrD2IdT9s
+JbpZeec+XY+yOREaPNq4pLDN9fnKsF8SM6ODNcZLVWBSXn47kq18dQTPHcfLAFeI
+r8vh6Pld90AqFRUw1YCDRoZOs3CqeZVqWHhiy1M3kTB/cNkcltItABppAJuSPGgz
+iMnzbLm16+ZDAgQceNkIIGuHAJy4yrrK09vbJ5L7kRss9NtmA1hb6a4Mo7jmQXqg
+SwbkcOoaO1gcoDpngckxW2KzDmAR8iRyWUbuxXxtlEECgYEA3W4dT//r9o2InE0R
+TNqqnKpjpZN0KGyKXCmnF7umA3VkTVyqZ0xLi8cyY1hkYiDkVQ12CKwn1Vttt0+N
+gSfvj6CQmLaRR94GVXNEfhg9Iv59iFrOtRPZWB3V4HwakPXOCHneExNx7O/JznLp
+xD3BJ9I4GQ3oEXc8pdGTAfSMdCsCgYEA16dz2evDgKdn0v7Ak0rU6LVmckB3Gs3r
+ta15b0eP7E1FmF77yVMpaCicjYkQL63yHzTi3UlA66jAnW0fFtzClyl3TEMnXpJR
+3b5JCeH9O/Hkvt9Go5uLODMo70rjuVuS8gcK8myefFybWH/t3gXo59hspXiG+xZY
+EKd7mEW8MScCgYEAlkcrQaYQwK3hryJmwWAONnE1W6QtS1oOtOnX6zWBQAul3RMs
+2xpekyjHu8C7sBVeoZKXLt+X0SdR2Pz2rlcqMLHqMJqHEt1OMyQdse5FX8CT9byb
+WS11bmYhR08ywHryL7J100B5KzK6JZC7smGu+5WiWO6lN2VTFb6cJNGRmS0CgYAo
+tFCnp1qFZBOyvab3pj49lk+57PUOOCPvbMjo+ibuQT+LnRIFVA8Su+egx2got7pl
+rYPMpND+KiIBFOGzXQPVqFv+Jwa9UPzmz83VcbRspiG47UfWBbvnZbCqSgZlrCU2
+TaIBVAMuEgS4VZ0+NPtbF3yaVv+TUQpaSmKHwVHeLQKBgCgGe5NVgB0u9S36ltit
+tYlnPPjuipxv9yruq+nva+WKT0q/BfeIlH3IUf2qNFQhR6caJGv7BU7naqNGq80m
+ks/J5ExR5vBpxzXgc7oBn2pyFJYckbJoccrqv48GRBigJpDjmo1f8wZ7fNt/ULH1
+NBinA5ZsT8d0v3QCr2xDJH9D
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key.keyspec b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key.keyspec
new file mode 100644
index 0000000000..4ad96d5159
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.key.keyspec
@@ -0,0 +1 @@
+default
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem
new file mode 100644
index 0000000000..6fc6bdeec5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDVzCCAj+gAwIBAgIUGWNNFPvyjk63DRepZV/gB/+L1VUwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTgwNzAzMTIwMDAwWhgPMjAyMzA3
+MDIxMjAwMDBaMCIxIDAeBgNVBAMMF2V2X2ludF82MF9tb250aHMtZXZyb290MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08
+E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc
+1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAP
+DY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQ
+gAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqV
+YR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQID
+AQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMFAGCCsGAQUFBwEB
+BEQwQjBABggrBgEFBQcwAYY0aHR0cDovL3d3dy5leGFtcGxlLmNvbTo4ODg4L2V2
+X2ludF82MF9tb250aHMtZXZyb290LzAfBgNVHSAEGDAWMBQGEisGAQQB60mFGoUa
+hRoBg3QJATANBgkqhkiG9w0BAQsFAAOCAQEADae0P+9+rtDogzf5zQega4VLX+Nj
+tUru5/tU1IKifEGO1UibkrACklVZPh5I8A0scUNQVuvsSTmRSR3np25bhFtibs0T
+7cx4x9Uwuk/KO2257NUJwj1tZFMdTCxkcIDOujU5XIWo3q1+b2MMgzwgcESjSV8K
+1dUi0mDbhut9SjfimmUgfB+53ZVJDk40RTQIZYFvQy1XKnFpd0R/FXxz3YTZkICm
+E0fAuSsif/wjopUgMnjgXHxA7bZYAuWQxgcZ28MRuqI9JESDZ3eH2oPUkYcpR8DK
+FGd5wcJGvQtR2u/e3EmzcC9jZzBzZE7rvXuyl+AAwXhC25eq1Tqg/7XxjA==
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem.certspec b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem.certspec
new file mode 100644
index 0000000000..e169514ffa
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/ev_int_60_months-evroot.pem.certspec
@@ -0,0 +1,8 @@
+issuer:evroot
+subject:ev_int_60_months-evroot
+issuerKey:ev
+validity:1825
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
+extension:authorityInformationAccess:http://www.example.com:8888/ev_int_60_months-evroot/
+extension:certificatePolicies:1.3.6.1.4.1.13769.666.666.666.1.500.9.1
diff --git a/security/manager/ssl/tests/unit/test_validity/evroot.key b/security/manager/ssl/tests/unit/test_validity/evroot.key
new file mode 100644
index 0000000000..1d88a930d5
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/evroot.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQC1SYlcnQAQjRGh
++Z+HqePRpdtd+uzxiNpXv2QTaI8s5HIs/xCQOMF0Ask6Kkc9vShq7T/c02PPWikU
+dwG92BjXYVv5NWvV08gzaqqMCXE2igbDzURhuT5RQk4XRLsuqtRqqzjOGWghlh+H
+cUoWY2k/CXYc301roSXqzse+Jw04j3ifbN94rjFE7SjEXnkpOGOnoipImAo2pA5y
+1XnJuSXf+MeTNi/9aJenwXVMXpfJZ8Pq3RquiqLMzjSKAWm4Diii1wwalgxvM18t
+oJubZD9av7pJ6Kqpgelg4n2HSAvdVd2UF/oYUJ+7VUzPgaQ5fouoEoo0vfJ4ZcGJ
+5XNPsikFAgMBAAECggEBAJg9VPlNb0x26yPW+T14UjUwz3Ow0WJUxueBdo1F9VaB
+0dAvsr0qrGq8HDiYYJNcUqDY9BSCAQOUd4MUHYZL/zCANjilwBUlcK6dGPPYyhY+
++0dbDd3zLn4W7HVl5rteAlxBxcZuV6A87eVUIh+DBFNHosTEUcPc5Ha3h84MBXJE
+vp4E7xMRjbuz1eCmzIcCnq/Upp7ZsUdZsV452KmITlb1TS+asBPw0V8xipq2svc9
+HsPJ/idK6JQxoQZAvniZsAEcXlCToYNHCGid4QBjTaveYPvWqu+joz3zSh829gwE
+MDa3SNHJ7pjEAxoK/sYO/aCpkL5ST1YU6sT9s0pS+VECgYEA6twssz5f8co3a72V
+vWoXd9LPT6xHVF6S0RpiCbnV5N7UeDRYHBabPIhHQqCeoYdQXBylVBTY0ltJdjLV
+7CqqBSM0MPrUmJJ3en1o4Dj1YaO4lp5gsKJj3vv9pIqbD/OdlbyIsVJnyK3pe1EH
+lI5B5DMknYf32xCdXXRYTYa8wdcCgYEAxZrldqIWRwJI2USlW56b+TKZ2jQexW5V
+jrqCGrzhv1e3nPQR0pBMd0+duh8VGF9gewV0oIIF1uwotmo21jQjLqry/qN1Yauv
+nWRLaNs4yZZMuMluwKxh66ZNBbRGVC9COXb1rN5OzJVTbS31eJVPk/DP2cWPt4ui
+p23VrChNyIMCgYEAwdLvOQYzHFKspkgR+f5CW+somDIvs9tRAyzo1+n8MiQL6SAZ
+zySA/NXjKYNxJxGLKlmhv+BsiD46REfz8DHNmuvQuNNo/Hl0DSzOjq2zJN9/CR6v
+4VZDYdVJILAbBHEjDl5H2T+O0zljxRe8T8ePbYsfnrqFvM7bcDMCZQjbYoUCgYEA
+hSG421aU376ASjFfnvybZSdcVJCs8qNFbWXm5hC/n2R/xnUB1PV3LyMqxwzN75/C
+pt+kFcfEG2r8evnQfDygP37ZPAnwuZ8sMEQ0Mi8QcXCbvBuqTJFXX6apWeB9SZaV
+bZXiK1eTi25HyNUf/t/Jv4iM4NGj5CtlqJvtS5HT5fUCgYEA3El7BrkgyL4LAHe3
+mOl37vdEqQ7Cxdfmy7IkSPrHLagaMxgODYoC6DFGDH/H/TphL3uZMLYbeZ+OkI5j
+LpugQJtqpwsDo7p4dCYmO1vVhD34R27bXRT2qGE+uvW5zVykL1+9KALgjk5J5XCf
+UVFRDKpassHG6z7+kpXRbowlyRY=
+-----END PRIVATE KEY-----
diff --git a/security/manager/ssl/tests/unit/test_validity/evroot.key.keyspec b/security/manager/ssl/tests/unit/test_validity/evroot.key.keyspec
new file mode 100644
index 0000000000..1a3d76a550
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/evroot.key.keyspec
@@ -0,0 +1 @@
+ev
diff --git a/security/manager/ssl/tests/unit/test_validity/evroot.pem b/security/manager/ssl/tests/unit/test_validity/evroot.pem
new file mode 100644
index 0000000000..13c3031905
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/evroot.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIUIZSHsVgzcvhPgdfrgdMGlpSfMegwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGZXZyb290MCIYDzIwMTUwMTAxMDAwMDAwWhgPMjAzNTAx
+MDEwMDAwMDBaMBExDzANBgNVBAMMBmV2cm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALVJiVydABCNEaH5n4ep49Gl21367PGI2le/ZBNojyzkciz/
+EJA4wXQCyToqRz29KGrtP9zTY89aKRR3Ab3YGNdhW/k1a9XTyDNqqowJcTaKBsPN
+RGG5PlFCThdEuy6q1GqrOM4ZaCGWH4dxShZjaT8JdhzfTWuhJerOx74nDTiPeJ9s
+33iuMUTtKMReeSk4Y6eiKkiYCjakDnLVecm5Jd/4x5M2L/1ol6fBdUxel8lnw+rd
+Gq6KoszONIoBabgOKKLXDBqWDG8zXy2gm5tkP1q/uknoqqmB6WDifYdIC91V3ZQX
++hhQn7tVTM+BpDl+i6gSijS98nhlwYnlc0+yKQUCAwEAAaMdMBswDAYDVR0TBAUw
+AwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBABTOHA9XbfLv/C7+
+5KycYXToOIBRSjQ0j2nsiqFda4Jx+aKsvdpdrrbLHvhrpfsA3ZgB2+eKHunVc4fo
+UHNqZllAs2nx+AEinq4GX8iya5BpiyTIxXWu8v06siGgz1GxlJw1cJ/ZnFEQ9IBf
+cCAr5fCoZ4RC+2OVhiSTnYPCKM+zCyw3YpISjNOg1VVkp46Htp+831Eh12YfwvdY
+Fgh1fc5ohYC5GCLRuXKc9PGTsr3gp7Y0liYbK7v0RBjd+GivNQ3dS3W+lB3Ow0LH
+z/fc3qvrhsd58jHpb1QZQzd9bQjuIIM6Gij7TNdNNarEVZfSJjPYLfXosNdYh5fH
+HmbOwao=
+-----END CERTIFICATE-----
diff --git a/security/manager/ssl/tests/unit/test_validity/evroot.pem.certspec b/security/manager/ssl/tests/unit/test_validity/evroot.pem.certspec
new file mode 100644
index 0000000000..3121f3486e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/evroot.pem.certspec
@@ -0,0 +1,7 @@
+issuer:evroot
+subject:evroot
+subjectKey:ev
+issuerKey:ev
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:keyCertSign,cRLSign
diff --git a/security/manager/ssl/tests/unit/test_validity/moz.build b/security/manager/ssl/tests/unit/test_validity/moz.build
new file mode 100644
index 0000000000..9121c4e49b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_validity/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+# test_certificates = (
+# 'ev_ee_27_months-ev_int_60_months-evroot.pem',
+# 'ev_ee_28_months-ev_int_60_months-evroot.pem',
+# 'ev_int_60_months-evroot.pem',
+# 'evroot.pem',
+# )
+#
+# for test_certificate in test_certificates:
+# GeneratedTestCertificate(test_certificate)
+#
+# test_keys = (
+# 'ev_int_60_months-evroot.key',
+# 'evroot.key',
+# )
+#
+# for test_key in test_keys:
+# GeneratedTestKey(test_key)
diff --git a/security/manager/ssl/tests/unit/test_x509.js b/security/manager/ssl/tests/unit/test_x509.js
new file mode 100644
index 0000000000..73eb4ed97a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_x509.js
@@ -0,0 +1,142 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests X509.jsm functionality.
+
+function stringToArray(s) {
+ let b = [];
+ for (let i = 0; i < s.length; i++) {
+ b.push(s.charCodeAt(i));
+ }
+ return b;
+}
+
+function readPEMToBytes(filename) {
+ return stringToArray(atob(pemToBase64(readFile(do_get_file(filename)))));
+}
+
+function run_test() {
+ let certificate = new X509.Certificate();
+ // We use this certificate because it has a set validity period, which means that when
+ // the test certificates get regenerated each year, the values in this test won't change.
+ certificate.parse(readPEMToBytes("bad_certs/expired-ee.pem"));
+
+ equal(
+ certificate.tbsCertificate.version,
+ 3,
+ "expired-ee.pem should be x509v3"
+ );
+
+ // serialNumber
+ deepEqual(
+ certificate.tbsCertificate.serialNumber,
+ [
+ 0x63,
+ 0xd1,
+ 0x11,
+ 0x00,
+ 0x82,
+ 0xa3,
+ 0xd2,
+ 0x3b,
+ 0x3f,
+ 0x61,
+ 0xb8,
+ 0x49,
+ 0xa0,
+ 0xca,
+ 0xdc,
+ 0x2e,
+ 0x78,
+ 0xfe,
+ 0xfa,
+ 0xea,
+ ],
+ "expired-ee.pem should have expected serialNumber"
+ );
+
+ deepEqual(
+ certificate.tbsCertificate.signature.algorithm._values,
+ [1, 2, 840, 113549, 1, 1, 11], // sha256WithRSAEncryption
+ "expired-ee.pem should have sha256WithRSAEncryption signature"
+ );
+ deepEqual(
+ certificate.tbsCertificate.signature.parameters._contents,
+ [],
+ "expired-ee.pem should have NULL parameters for signature"
+ );
+
+ equal(
+ certificate.tbsCertificate.issuer.rdns.length,
+ 1,
+ "expired-ee.pem should have one RDN in issuer"
+ );
+ equal(
+ certificate.tbsCertificate.issuer.rdns[0].avas.length,
+ 1,
+ "expired-ee.pem should have one AVA in RDN in issuer"
+ );
+ deepEqual(
+ certificate.tbsCertificate.issuer.rdns[0].avas[0].value.value,
+ stringToArray("Test CA"),
+ "expired-ee.pem should have issuer 'Test CA'"
+ );
+
+ equal(
+ certificate.tbsCertificate.validity.notBefore.time.getTime(),
+ Date.parse("2013-01-01T00:00:00.000Z"),
+ "expired-ee.pem should have the correct value for notBefore"
+ );
+ equal(
+ certificate.tbsCertificate.validity.notAfter.time.getTime(),
+ Date.parse("2014-01-01T00:00:00.000Z"),
+ "expired-ee.pem should have the correct value for notAfter"
+ );
+
+ equal(
+ certificate.tbsCertificate.subject.rdns.length,
+ 1,
+ "expired-ee.pem should have one RDN in subject"
+ );
+ equal(
+ certificate.tbsCertificate.subject.rdns[0].avas.length,
+ 1,
+ "expired-ee.pem should have one AVA in RDN in subject"
+ );
+ deepEqual(
+ certificate.tbsCertificate.subject.rdns[0].avas[0].value.value,
+ stringToArray("Expired Test End-entity"),
+ "expired-ee.pem should have subject 'Expired Test End-entity'"
+ );
+
+ deepEqual(
+ certificate.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm._values,
+ [1, 2, 840, 113549, 1, 1, 1], // rsaEncryption
+ "expired-ee.pem should have a spki algorithm of rsaEncryption"
+ );
+
+ equal(
+ certificate.tbsCertificate.extensions.length,
+ 2,
+ "expired-ee.pem should have two extensions"
+ );
+
+ deepEqual(
+ certificate.signatureAlgorithm.algorithm._values,
+ [1, 2, 840, 113549, 1, 1, 11], // sha256WithRSAEncryption
+ "expired-ee.pem should have sha256WithRSAEncryption signatureAlgorithm"
+ );
+ deepEqual(
+ certificate.signatureAlgorithm.parameters._contents,
+ [],
+ "expired-ee.pem should have NULL parameters for signatureAlgorithm"
+ );
+
+ equal(
+ certificate.signatureValue.length,
+ 2048 / 8,
+ "length of signature on expired-ee.pem should be 2048 bits"
+ );
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertAndPinningServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertAndPinningServer.cpp
new file mode 100644
index 0000000000..b13d2e9e61
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertAndPinningServer.cpp
@@ -0,0 +1,140 @@
+/* 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/. */
+
+// This is a standalone server that uses various bad certificates.
+// The client is expected to connect, initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to), and verify the certificate.
+// If all is good, the client then sends one encrypted byte and receives that
+// same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdio.h>
+
+#include "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct BadCertAndPinningHost {
+ const char* mHostName;
+ const char* mCertName;
+};
+
+// Hostname, cert nickname pairs.
+const BadCertAndPinningHost sBadCertAndPinningHosts[] = {
+ {"expired.example.com", "expired-ee"},
+ {"notyetvalid.example.com", "notYetValid"},
+ {"before-epoch.example.com", "beforeEpoch"},
+ {"selfsigned.example.com", "selfsigned"},
+ {"unknownissuer.example.com", "unknownissuer"},
+ {"mismatch.example.com", "mismatch"},
+ {"mismatch-CN.example.com", "mismatchCN"},
+ {"mitm.example.com", "mitm"},
+ {"expiredissuer.example.com", "expiredissuer"},
+ {"notyetvalidissuer.example.com", "notYetValidIssuer"},
+ {"before-epoch-issuer.example.com", "beforeEpochIssuer"},
+ {"md5signature.example.com", "md5signature"},
+ {"untrusted.example.com", "default-ee"},
+ {"untrustedissuer.example.com", "untrustedissuer"},
+ {"mismatch-expired.example.com", "mismatch-expired"},
+ {"mismatch-notYetValid.example.com", "mismatch-notYetValid"},
+ {"mismatch-untrusted.example.com", "mismatch-untrusted"},
+ {"untrusted-expired.example.com", "untrusted-expired"},
+ {"md5signature-expired.example.com", "md5signature-expired"},
+ {"mismatch-untrusted-expired.example.com", "mismatch-untrusted-expired"},
+ {"inadequatekeyusage.example.com", "inadequatekeyusage-ee"},
+ {"selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU"},
+ {"self-signed-end-entity-with-cA-true.example.com",
+ "self-signed-EE-with-cA-true"},
+ {"ca-used-as-end-entity.example.com", "ca-used-as-end-entity"},
+ {"ca-used-as-end-entity-name-mismatch.example.com",
+ "ca-used-as-end-entity"},
+ // All of include-subdomains.pinning.example.com is pinned to End Entity
+ // Test Cert with nick default-ee. Any other nick will only
+ // pass pinning when security.cert_pinning.enforcement.level != strict and
+ // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h.
+ {"include-subdomains.pinning.example.com", "default-ee"},
+ {"good.include-subdomains.pinning.example.com", "default-ee"},
+ {"bad.include-subdomains.pinning.example.com", "other-issuer-ee"},
+ {"bad.include-subdomains.pinning.example.com.", "other-issuer-ee"},
+ {"bad.include-subdomains.pinning.example.com..", "other-issuer-ee"},
+ {"exclude-subdomains.pinning.example.com", "default-ee"},
+ {"sub.exclude-subdomains.pinning.example.com", "other-issuer-ee"},
+ {"test-mode.pinning.example.com", "other-issuer-ee"},
+ {"unknownissuer.include-subdomains.pinning.example.com", "unknownissuer"},
+ {"unknownissuer.test-mode.pinning.example.com", "unknownissuer"},
+ {"nsCertTypeNotCritical.example.com", "nsCertTypeNotCritical"},
+ {"nsCertTypeCriticalWithExtKeyUsage.example.com",
+ "nsCertTypeCriticalWithExtKeyUsage"},
+ {"nsCertTypeCritical.example.com", "nsCertTypeCritical"},
+ {"end-entity-issued-by-v1-cert.example.com", "eeIssuedByV1Cert"},
+ {"end-entity-issued-by-non-CA.example.com", "eeIssuedByNonCA"},
+ {"inadequate-key-size-ee.example.com", "inadequateKeySizeEE"},
+ {"badSubjectAltNames.example.com", "badSubjectAltNames"},
+ {"ipAddressAsDNSNameInSAN.example.com", "ipAddressAsDNSNameInSAN"},
+ {"noValidNames.example.com", "noValidNames"},
+ {"bug413909.xn--hxajbheg2az3al.xn--jxalpdlp", "idn-certificate"},
+ {"emptyissuername.example.com", "emptyIssuerName"},
+ {"ev-test.example.com", "ev-test"},
+ {"ee-from-missing-intermediate.example.com",
+ "ee-from-missing-intermediate"},
+ {"imminently-distrusted.example.com", "ee-imminently-distrusted"},
+ {"localhost", "unknownissuer"},
+ {"a.pinning.example.com", "default-ee"},
+ {"b.pinning.example.com", "default-ee"},
+ {"not-preloaded.example.com", "default-ee"},
+ {"ee.example.com", "default-ee"},
+ {nullptr, nullptr}};
+
+int32_t DoSNISocketConfigBySubjectCN(PRFileDesc* aFd,
+ const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize) {
+ for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
+ UniquePORTString name(
+ static_cast<char*>(PORT_ZAlloc(aSrvNameArr[i].len + 1)));
+ if (name) {
+ PORT_Memcpy(name.get(), aSrvNameArr[i].data, aSrvNameArr[i].len);
+ if (ConfigSecureServerWithNamedCert(aFd, name.get(), nullptr, nullptr,
+ nullptr) == SECSuccess) {
+ return 0;
+ }
+ }
+ }
+
+ return SSL_SNI_SEND_ALERT;
+}
+
+int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize, void* aArg) {
+ const BadCertAndPinningHost* host =
+ GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sBadCertAndPinningHosts);
+ if (!host) {
+ // No static cert <-> hostname mapping found. This happens when we use a
+ // collection of certificates in a given directory and build a cert DB at
+ // runtime, rather than using an NSS cert DB populated at build time.
+ // (This will be the default in the future.)
+ // For all given server names, check if the runtime-built cert DB contains
+ // a certificate with a matching subject CN.
+ return DoSNISocketConfigBySubjectCN(aFd, aSrvNameArr, aSrvNameArrSize);
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+ }
+
+ UniqueCERTCertificate cert;
+ SSLKEAType certKEA;
+ if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName, &cert,
+ &certKEA, nullptr)) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ return StartServer(argc, argv, DoSNISocketConfig, nullptr);
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/DelegatedCredentialsServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/DelegatedCredentialsServer.cpp
new file mode 100644
index 0000000000..5f12756425
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/DelegatedCredentialsServer.cpp
@@ -0,0 +1,142 @@
+/* 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/. */
+
+// This is a standalone server used to test Delegated Credentials
+// (see: https://tools.ietf.org/html/draft-ietf-tls-subcerts-03).
+//
+// The client is expected to connect, initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to), and verify the certificate.
+// If all is good, the client then sends one encrypted byte and receives that
+// same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <iostream>
+
+#include "TLSServer.h"
+
+#include "sslexp.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct DelegatedCertHost {
+ const char* mHostName;
+ const char* mCertName;
+ const char* mDCKeyNick;
+ bool mEnableDelegatedCredentials;
+};
+
+const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds) */;
+
+// {host, eeCert, dcCert, enableDC}
+const DelegatedCertHost sDelegatedCertHosts[] = {
+ {"delegated-enabled.example.com", "delegated-ee", "delegated.key", true},
+ {"standard-enabled.example.com", "default-ee", "delegated.key", true},
+ {"delegated-disabled.example.com", "delegated-ee",
+ /* anything non-null */ "delegated.key", false},
+ {nullptr, nullptr, nullptr, false}};
+
+int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize, void* aArg) {
+ const DelegatedCertHost* host =
+ GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sDelegatedCertHosts);
+ if (!host) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ std::cerr << "Identified host " << host->mHostName << std::endl;
+ }
+
+ UniqueCERTCertificate delegatorCert(
+ PK11_FindCertFromNickname(host->mCertName, nullptr));
+ if (!delegatorCert) {
+ PrintPRError("PK11_FindCertFromNickname failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (!slot) {
+ PrintPRError("PK11_GetInternalKeySlot failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ SSLExtraServerCertData extra_data = {ssl_auth_null,
+ /* Filled in by callee */ nullptr,
+ nullptr,
+ nullptr,
+ /* DC */ nullptr,
+ /* DC PrivKey */ nullptr};
+
+ UniqueSECKEYPrivateKey delegatorPriv(
+ PK11_FindKeyByDERCert(slot.get(), delegatorCert.get(), nullptr));
+ if (!delegatorPriv) {
+ PrintPRError("PK11_FindKeyByDERCert failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // Find the DC keypair by the file (nick) name.
+ ScopedAutoSECItem dc;
+ UniqueSECKEYPrivateKey dcPriv;
+ if (host->mEnableDelegatedCredentials) {
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ std::cerr << "Enabling a delegated credential for host "
+ << host->mHostName << std::endl;
+ }
+
+ if (PK11_NeedLogin(slot.get())) {
+ SECStatus rv = PK11_Authenticate(slot.get(), PR_TRUE, nullptr);
+ if (rv != SECSuccess) {
+ PrintPRError("PK11_Authenticate failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+ }
+ UniqueSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot(
+ slot.get(), const_cast<char*>(host->mDCKeyNick), nullptr));
+ if (!list) {
+ PrintPRError("PK11_ListPrivKeysInSlot failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+ SECKEYPrivateKeyListNode* node = PRIVKEY_LIST_HEAD(list);
+
+ dcPriv.reset(SECKEY_CopyPrivateKey(node->key));
+ if (!dcPriv) {
+ PrintPRError("PK11_ListPrivKeysInSlot could not find dcPriv");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ UniqueSECKEYPublicKey dcPub(SECKEY_ConvertToPublicKey(dcPriv.get()));
+ if (!dcPub) {
+ PrintPRError("SECKEY_ConvertToPublicKey failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // Create and set the DC.
+ if (SSL_DelegateCredential(delegatorCert.get(), delegatorPriv.get(),
+ dcPub.get(), ssl_sig_ecdsa_secp384r1_sha384,
+ kDCValidFor, PR_Now(), &dc) != SECSuccess) {
+ PrintPRError("SSL_DelegateCredential failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+ extra_data.delegCred = &dc;
+ extra_data.delegCredPrivKey = dcPriv.get();
+
+ // The list should only have a single key.
+ PORT_Assert(PRIVKEY_LIST_END(PRIVKEY_LIST_NEXT(node), list));
+ }
+
+ if (ConfigSecureServerWithNamedCert(aFd, host->mCertName, nullptr, nullptr,
+ &extra_data) != SECSuccess) {
+ PrintPRError("ConfigSecureServerWithNamedCert failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ return StartServer(argc, argv, DoSNISocketConfig, nullptr);
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/EncryptedClientHelloServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/EncryptedClientHelloServer.cpp
new file mode 100644
index 0000000000..f768b888f6
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/EncryptedClientHelloServer.cpp
@@ -0,0 +1,143 @@
+/* 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/. */
+
+// This is a standalone server that offers TLS 1.3 Encrypted
+// Client Hello support.
+
+#include <stdio.h>
+
+#include "nspr.h"
+#include "ScopedNSSTypes.h"
+#include "ssl.h"
+#include "sslexp.h"
+#include "TLSServer.h"
+#include <pk11pub.h>
+#include <vector>
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct EchHost {
+ const char* mHostName;
+ const char* mCertName;
+};
+
+const std::vector<uint32_t> kSuiteChaCha = {
+ (static_cast<uint32_t>(HpkeKdfHkdfSha256) << 16) |
+ HpkeAeadChaCha20Poly1305};
+
+// Hostname, cert nickname pairs.
+const EchHost sEchHosts[] = {{"ech-public.example.com", "default-ee"},
+ {"ech-private.example.com", "private-ee"},
+ {"selfsigned.example.com", "selfsigned"},
+ {nullptr, nullptr}};
+
+int32_t DoSNISocketConfigBySubjectCN(PRFileDesc* aFd,
+ const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize) {
+ for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
+ UniquePORTString name(
+ static_cast<char*>(PORT_ZAlloc(aSrvNameArr[i].len + 1)));
+ if (name) {
+ PORT_Memcpy(name.get(), aSrvNameArr[i].data, aSrvNameArr[i].len);
+ if (ConfigSecureServerWithNamedCert(aFd, name.get(), nullptr, nullptr,
+ nullptr) == SECSuccess) {
+ return 0;
+ }
+ }
+ }
+
+ return SSL_SNI_SEND_ALERT;
+}
+
+int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize, void* aArg) {
+ const EchHost* host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sEchHosts);
+ if (!host) {
+ PrintPRError("No cert found for hostname");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+ }
+
+ UniqueCERTCertificate cert;
+ SSLKEAType certKEA;
+ if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName, &cert,
+ &certKEA, nullptr)) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ return 0;
+}
+
+SECStatus ConfigureServer(PRFileDesc* aFd) {
+ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (!slot) {
+ PrintPRError("PK11_GetInternalKeySlot failed");
+ return SECFailure;
+ }
+
+ UniqueSECKEYPublicKey pubKey;
+ UniqueSECKEYPrivateKey privKey;
+ SECKEYPublicKey* tmpPubKey = nullptr;
+ SECKEYPrivateKey* tmpPrivKey = nullptr;
+
+ static const std::vector<uint8_t> pkcs8{
+ 0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
+ 0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
+ 0x8c, 0x49, 0x0e, 0x5b, 0x0c, 0x7d, 0xbe, 0x0c, 0x6d, 0x21, 0x92, 0x48,
+ 0x4d, 0x2b, 0x7a, 0x04, 0x23, 0xb3, 0xb4, 0x54, 0x4f, 0x24, 0x81, 0x09,
+ 0x5a, 0x99, 0xdb, 0xf2, 0x38, 0xfb, 0x35, 0x0f, 0xa1, 0x23, 0x03, 0x21,
+ 0x00, 0x8a, 0x07, 0x56, 0x39, 0x49, 0xfa, 0xc6, 0x23, 0x29, 0x36, 0xed,
+ 0x6f, 0x36, 0xc4, 0xfa, 0x73, 0x59, 0x30, 0xec, 0xde, 0xae, 0xf6, 0x73,
+ 0x4e, 0x31, 0x4a, 0xea, 0xc3, 0x5a, 0x56, 0xfd, 0x0a};
+
+ SECItem pkcs8Item = {siBuffer, const_cast<uint8_t*>(pkcs8.data()),
+ static_cast<unsigned int>(pkcs8.size())};
+ SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL,
+ &tmpPrivKey, nullptr);
+
+ if (rv != SECSuccess) {
+ PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
+ return SECFailure;
+ }
+ privKey.reset(tmpPrivKey);
+ tmpPubKey = SECKEY_ConvertToPublicKey(privKey.get());
+ pubKey.reset(tmpPubKey);
+
+ if (!privKey || !pubKey) {
+ PrintPRError("ECH/HPKE Public or Private key is null!");
+ return SECFailure;
+ }
+
+ std::vector<uint8_t> echConfig(1000, 0);
+ unsigned int len = 0;
+ rv = SSL_EncodeEchConfig("ech-public.example.com", kSuiteChaCha.data(), 1,
+ HpkeDhKemX25519Sha256, pubKey.get(), 50,
+ echConfig.data(), &len, echConfig.size());
+ if (rv != SECSuccess) {
+ PrintPRError("SSL_EncodeEchConfig failed");
+ return rv;
+ }
+
+ rv = SSL_SetServerEchConfigs(aFd, pubKey.get(), privKey.get(),
+ echConfig.data(), len);
+ if (rv != SECSuccess) {
+ PrintPRError("SSL_SetServerEchConfigs failed");
+ return rv;
+ }
+
+ return SECSuccess;
+}
+
+int main(int argc, char* argv[]) {
+ int rv = StartServer(argc, argv, DoSNISocketConfig, nullptr, ConfigureServer);
+ if (rv < 0) {
+ return rv;
+ }
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
new file mode 100644
index 0000000000..113e668f89
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 tw=80 et: */
+/* 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/. */
+
+/* This simple program takes a database directory, and one or more tuples like
+ * <typeOfResponse> <CertNick> <ExtraCertNick> <outPutFilename>
+ * to generate (one or more) ocsp responses.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+#include "mozilla/ArrayUtils.h"
+
+#include "cert.h"
+#include "nspr.h"
+#include "nss.h"
+#include "plarenas.h"
+#include "prerror.h"
+#include "ssl.h"
+#include "secerr.h"
+
+#include "OCSPCommon.h"
+#include "ScopedNSSTypes.h"
+#include "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct OCSPResponseName {
+ const char* mTypeString;
+ const OCSPResponseType mORT;
+};
+
+const static OCSPResponseName kOCSPResponseNameList[] = {
+ {"good", ORTGood}, // the certificate is good
+ {"good-delegated", ORTDelegatedIncluded}, // the certificate is good, using
+ // a delegated signer
+ {"revoked", ORTRevoked}, // the certificate has been revoked
+ {"unknown", ORTUnknown}, // the responder doesn't know if the
+ // cert is good
+ {"goodotherca", ORTGoodOtherCA}, // the wrong CA has signed the
+ // response
+ {"expiredresponse", ORTExpired}, // the signature on the response has
+ // expired
+ {"oldvalidperiod", ORTExpiredFreshCA}, // fresh signature, but old validity
+ // period
+ {"empty", ORTEmpty}, // an empty stapled response
+
+ {"malformed", ORTMalformed}, // the response from the responder
+ // was malformed
+ {"serverr", ORTSrverr}, // the response indicates there was a
+ // server error
+ {"trylater", ORTTryLater}, // the responder replied with
+ // "try again later"
+ {"resp-unsigned", ORTNeedsSig}, // the response needs a signature
+ {"unauthorized", ORTUnauthorized}, // the responder does not know about
+ // the cert
+ {"bad-signature", ORTBadSignature}, // the response has a bad signature
+ {"longvalidityalmostold",
+ ORTLongValidityAlmostExpired}, // the response is
+ // still valid, but the generation
+ // is almost a year old
+ {"ancientstillvalid", ORTAncientAlmostExpired}, // The response is still
+ // valid but the generation
+ // is almost two years old
+};
+
+bool StringToOCSPResponseType(const char* respText,
+ /*out*/ OCSPResponseType* OCSPType) {
+ if (!OCSPType) {
+ return false;
+ }
+ for (auto ocspResponseName : kOCSPResponseNameList) {
+ if (strcmp(respText, ocspResponseName.mTypeString) == 0) {
+ *OCSPType = ocspResponseName.mORT;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WriteResponse(const char* filename, const SECItem* item) {
+ if (!filename || !item || !item->data) {
+ PR_fprintf(PR_STDERR, "invalid parameters to WriteResponse");
+ return false;
+ }
+
+ UniquePRFileDesc outFile(
+ PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644));
+ if (!outFile) {
+ PrintPRError("cannot open file for writing");
+ return false;
+ }
+ int32_t rv = PR_Write(outFile.get(), item->data, item->len);
+ if (rv < 0 || (uint32_t)rv != item->len) {
+ PrintPRError("File write failure");
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc < 7 || (argc - 7) % 5 != 0) {
+ PR_fprintf(
+ PR_STDERR,
+ "usage: %s <NSS DB directory> <responsetype> "
+ "<cert_nick> <extranick> <this_update_skew> <outfilename> [<resptype> "
+ "<cert_nick> <extranick> <this_update_skew> <outfilename>]* \n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ SECStatus rv = InitializeNSS(argv[1]);
+ if (rv != SECSuccess) {
+ PR_fprintf(PR_STDERR, "Failed to initialize NSS\n");
+ exit(EXIT_FAILURE);
+ }
+ UniquePLArenaPool arena(PORT_NewArena(256 * argc));
+ if (!arena) {
+ PrintPRError("PORT_NewArena failed");
+ exit(EXIT_FAILURE);
+ }
+
+ for (int i = 2; i + 3 < argc; i += 5) {
+ const char* ocspTypeText = argv[i];
+ const char* certNick = argv[i + 1];
+ const char* extraCertname = argv[i + 2];
+ const char* skewChars = argv[i + 3];
+ const char* filename = argv[i + 4];
+
+ OCSPResponseType ORT;
+ if (!StringToOCSPResponseType(ocspTypeText, &ORT)) {
+ PR_fprintf(PR_STDERR, "Cannot generate OCSP response of type %s\n",
+ ocspTypeText);
+ exit(EXIT_FAILURE);
+ }
+
+ UniqueCERTCertificate cert(PK11_FindCertFromNickname(certNick, nullptr));
+ if (!cert) {
+ PrintPRError("PK11_FindCertFromNickname failed");
+ PR_fprintf(PR_STDERR, "Failed to find certificate with nick '%s'\n",
+ certNick);
+ exit(EXIT_FAILURE);
+ }
+
+ time_t skew = static_cast<time_t>(atoll(skewChars));
+
+ SECItemArray* response =
+ GetOCSPResponseForType(ORT, cert, arena, extraCertname, skew);
+ if (!response) {
+ PR_fprintf(PR_STDERR,
+ "Failed to generate OCSP response of type %s "
+ "for %s\n",
+ ocspTypeText, certNick);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!WriteResponse(filename, &response->items[0])) {
+ PR_fprintf(PR_STDERR, "Failed to write file %s\n", filename);
+ exit(EXIT_FAILURE);
+ }
+ }
+ return 0;
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
new file mode 100644
index 0000000000..b35484572f
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
@@ -0,0 +1,153 @@
+/* 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/. */
+
+// This is a standalone server that delivers various stapled OCSP responses.
+// The client is expected to connect, initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to), and verify the OCSP response.
+// If all is good, the client then sends one encrypted byte and receives that
+// same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdio.h>
+
+#include "OCSPCommon.h"
+#include "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+const OCSPHost sOCSPHosts[] = {
+ {"ocsp-stapling-good.example.com", ORTGood, nullptr, nullptr},
+ {"ocsp-stapling-revoked.example.com", ORTRevoked, nullptr, nullptr},
+ {"ocsp-stapling-revoked-old.example.com", ORTRevokedOld, nullptr, nullptr},
+ {"ocsp-stapling-unknown.example.com", ORTUnknown, nullptr, nullptr},
+ {"ocsp-stapling-unknown-old.example.com", ORTUnknownOld, nullptr, nullptr},
+ {"ocsp-stapling-good-other.example.com", ORTGoodOtherCert,
+ "ocspOtherEndEntity", nullptr},
+ {"ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "other-test-ca",
+ nullptr},
+ {"ocsp-stapling-expired.example.com", ORTExpired, nullptr, nullptr},
+ {"ocsp-stapling-expired-fresh-ca.example.com", ORTExpiredFreshCA, nullptr,
+ nullptr},
+ {"ocsp-stapling-none.example.com", ORTNone, nullptr, nullptr},
+ {"ocsp-stapling-empty.example.com", ORTEmpty, nullptr, nullptr},
+ {"ocsp-stapling-malformed.example.com", ORTMalformed, nullptr, nullptr},
+ {"ocsp-stapling-srverr.example.com", ORTSrverr, nullptr, nullptr},
+ {"ocsp-stapling-trylater.example.com", ORTTryLater, nullptr, nullptr},
+ {"ocsp-stapling-needssig.example.com", ORTNeedsSig, nullptr, nullptr},
+ {"ocsp-stapling-unauthorized.example.com", ORTUnauthorized, nullptr,
+ nullptr},
+ {"ocsp-stapling-with-intermediate.example.com", ORTGood, nullptr,
+ "ocspEEWithIntermediate"},
+ {"ocsp-stapling-bad-signature.example.com", ORTBadSignature, nullptr,
+ nullptr},
+ {"ocsp-stapling-skip-responseBytes.example.com", ORTSkipResponseBytes,
+ nullptr, nullptr},
+ {"ocsp-stapling-critical-extension.example.com", ORTCriticalExtension,
+ nullptr, nullptr},
+ {"ocsp-stapling-noncritical-extension.example.com", ORTNoncriticalExtension,
+ nullptr, nullptr},
+ {"ocsp-stapling-empty-extensions.example.com", ORTEmptyExtensions, nullptr,
+ nullptr},
+ {"ocsp-stapling-delegated-included.example.com", ORTDelegatedIncluded,
+ "delegatedSigner", nullptr},
+ {"ocsp-stapling-delegated-included-last.example.com",
+ ORTDelegatedIncludedLast, "delegatedSigner", nullptr},
+ {"ocsp-stapling-delegated-missing.example.com", ORTDelegatedMissing,
+ "delegatedSigner", nullptr},
+ {"ocsp-stapling-delegated-missing-multiple.example.com",
+ ORTDelegatedMissingMultiple, "delegatedSigner", nullptr},
+ {"ocsp-stapling-delegated-no-extKeyUsage.example.com", ORTDelegatedIncluded,
+ "invalidDelegatedSignerNoExtKeyUsage", nullptr},
+ {"ocsp-stapling-delegated-from-intermediate.example.com",
+ ORTDelegatedIncluded, "invalidDelegatedSignerFromIntermediate", nullptr},
+ {"ocsp-stapling-delegated-keyUsage-crlSigning.example.com",
+ ORTDelegatedIncluded, "invalidDelegatedSignerKeyUsageCrlSigning", nullptr},
+ {"ocsp-stapling-delegated-wrong-extKeyUsage.example.com",
+ ORTDelegatedIncluded, "invalidDelegatedSignerWrongExtKeyUsage", nullptr},
+ {"ocsp-stapling-ancient-valid.example.com", ORTAncientAlmostExpired,
+ nullptr, nullptr},
+ {"keysize-ocsp-delegated.example.com", ORTDelegatedIncluded,
+ "rsa-1016-keysizeDelegatedSigner", nullptr},
+ {"revoked-ca-cert-used-as-end-entity.example.com", ORTRevoked,
+ "ca-used-as-end-entity", nullptr},
+ {"ocsp-stapling-must-staple.example.com", ORTGood, nullptr,
+ "must-staple-ee"},
+ {"ocsp-stapling-must-staple-revoked.example.com", ORTRevoked, nullptr,
+ "must-staple-ee"},
+ {"ocsp-stapling-must-staple-missing.example.com", ORTNone, nullptr,
+ "must-staple-ee"},
+ {"ocsp-stapling-must-staple-empty.example.com", ORTEmpty, nullptr,
+ "must-staple-ee"},
+ {"ocsp-stapling-must-staple-ee-with-must-staple-int.example.com", ORTGood,
+ nullptr, "must-staple-ee-with-must-staple-int"},
+ {"ocsp-stapling-plain-ee-with-must-staple-int.example.com", ORTGood,
+ nullptr, "must-staple-missing-ee"},
+ {"ocsp-stapling-must-staple-expired.example.com", ORTExpired, nullptr,
+ "must-staple-ee"},
+ {"ocsp-stapling-must-staple-try-later.example.com", ORTTryLater, nullptr,
+ "must-staple-ee"},
+ {"ocsp-stapling-must-staple-invalid-signer.example.com", ORTGoodOtherCA,
+ "other-test-ca", "must-staple-ee"},
+ {"multi-tls-feature-good.example.com", ORTNone, nullptr,
+ "multi-tls-feature-good-ee"},
+ {"multi-tls-feature-bad.example.com", ORTNone, nullptr,
+ "multi-tls-feature-bad-ee"},
+ {nullptr, ORTNull, nullptr, nullptr}};
+
+int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize, void* aArg) {
+ const OCSPHost* host =
+ GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sOCSPHosts);
+ if (!host) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+ }
+
+ const char* certNickname =
+ host->mServerCertName ? host->mServerCertName : DEFAULT_CERT_NICKNAME;
+
+ UniqueCERTCertificate cert;
+ SSLKEAType certKEA;
+ if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, certNickname, &cert,
+ &certKEA, nullptr)) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // If the OCSP response type is "none", don't staple a response.
+ if (host->mORT == ORTNone) {
+ return 0;
+ }
+
+ UniquePLArenaPool arena(PORT_NewArena(1024));
+ if (!arena) {
+ PrintPRError("PORT_NewArena failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // response is contained by the arena - freeing the arena will free it
+ SECItemArray* response = GetOCSPResponseForType(host->mORT, cert, arena,
+ host->mAdditionalCertName, 0);
+ if (!response) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // SSL_SetStapledOCSPResponses makes a deep copy of response
+ SECStatus st = SSL_SetStapledOCSPResponses(aFd, response, certKEA);
+ if (st != SECSuccess) {
+ PrintPRError("SSL_SetStapledOCSPResponses failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ return StartServer(argc, argv, DoSNISocketConfig, nullptr);
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/SanctionsTestServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/SanctionsTestServer.cpp
new file mode 100644
index 0000000000..9371617305
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/SanctionsTestServer.cpp
@@ -0,0 +1,87 @@
+/* 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/. */
+
+// This is a standalone server that uses various bad certificates.
+// The client is expected to connect, initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to), and verify the certificate.
+// If all is good, the client then sends one encrypted byte and receives that
+// same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdio.h>
+
+#include "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct SanctionsCertHost {
+ const char* mHostName;
+ const char* mCertName;
+};
+
+// Hostname, cert nickname pairs.
+const SanctionsCertHost sSanctionsCertHosts[] = {
+ {"symantec-allowlist-after-cutoff.example.com",
+ "symantec-ee-from-allowlist-after-cutoff"},
+ {"symantec-allowlist-before-cutoff.example.com",
+ "symantec-ee-from-allowlist-before-cutoff"},
+ {"symantec-not-allowlisted-after-cutoff.example.com",
+ "symantec-ee-not-allowlisted-after-cutoff"},
+ {"symantec-not-allowlisted-before-cutoff.example.com",
+ "symantec-ee-not-allowlisted-before-cutoff"},
+ {"symantec-unaffected.example.com", "symantec-ee-unaffected"},
+ {nullptr, nullptr}};
+
+int32_t DoSNISocketConfigBySubjectCN(PRFileDesc* aFd,
+ const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize) {
+ for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
+ UniquePORTString name(
+ static_cast<char*>(PORT_ZAlloc(aSrvNameArr[i].len + 1)));
+ if (name) {
+ PORT_Memcpy(name.get(), aSrvNameArr[i].data, aSrvNameArr[i].len);
+ if (ConfigSecureServerWithNamedCert(aFd, name.get(), nullptr, nullptr,
+ nullptr) == SECSuccess) {
+ return 0;
+ }
+ }
+ }
+
+ return SSL_SNI_SEND_ALERT;
+}
+
+int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize, void* aArg) {
+ const SanctionsCertHost* host =
+ GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sSanctionsCertHosts);
+ if (!host) {
+ // No static cert <-> hostname mapping found. This happens when we use a
+ // collection of certificates in a given directory and build a cert DB at
+ // runtime, rather than using an NSS cert DB populated at build time.
+ // (This will be the default in the future.)
+ // For all given server names, check if the runtime-built cert DB contains
+ // a certificate with a matching subject CN.
+ return DoSNISocketConfigBySubjectCN(aFd, aSrvNameArr, aSrvNameArrSize);
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+ }
+
+ UniqueCERTCertificate cert;
+ SSLKEAType certKEA;
+ if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName, &cert,
+ &certKEA, nullptr)) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ return StartServer(argc, argv, DoSNISocketConfig, nullptr);
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build b/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
new file mode 100644
index 0000000000..5c6dbadc71
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
@@ -0,0 +1,30 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+GeckoSimplePrograms(
+ [
+ "BadCertAndPinningServer",
+ "DelegatedCredentialsServer",
+ "EncryptedClientHelloServer",
+ "GenerateOCSPResponse",
+ "OCSPStaplingServer",
+ "SanctionsTestServer",
+ ],
+ linkage=None,
+)
+
+LOCAL_INCLUDES += [
+ "../lib",
+]
+
+USE_LIBS += [
+ "mozpkix",
+ "nspr",
+ "nss",
+ "tlsserver",
+]
+
+CXXFLAGS += CONFIG["TK_CFLAGS"]
diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.der b/security/manager/ssl/tests/unit/tlsserver/default-ee.der
new file mode 100644
index 0000000000..3a9b8fa9bc
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/default-ee.der
@@ -0,0 +1,3 @@
+This is now an unused file. It exists to ease the coordination between gecko
+development trees and the automation infrastructure that runs periodic updates.
+See bug 1203312 and bug 1205406.
diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp
new file mode 100644
index 0000000000..be9a9af9b1
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp
@@ -0,0 +1,204 @@
+/* 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/. */
+
+#include "OCSPCommon.h"
+
+#include <stdio.h>
+
+#include "mozpkix/test/pkixtestutil.h"
+#include "mozpkix/test/pkixtestnss.h"
+#include "TLSServer.h"
+#include "secder.h"
+#include "secerr.h"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+using namespace mozilla::test;
+
+static TestKeyPair* CreateTestKeyPairFromCert(
+ const UniqueCERTCertificate& cert) {
+ ScopedSECKEYPrivateKey privateKey(PK11_FindKeyByAnyCert(cert.get(), nullptr));
+ if (!privateKey) {
+ return nullptr;
+ }
+ ScopedSECKEYPublicKey publicKey(CERT_ExtractPublicKey(cert.get()));
+ if (!publicKey) {
+ return nullptr;
+ }
+ return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey);
+}
+
+SECItemArray* GetOCSPResponseForType(OCSPResponseType aORT,
+ const UniqueCERTCertificate& aCert,
+ const UniquePLArenaPool& aArena,
+ const char* aAdditionalCertName,
+ time_t aThisUpdateSkew) {
+ MOZ_ASSERT(aArena);
+ MOZ_ASSERT(aCert);
+ // Note: |aAdditionalCertName| may or may not need to be non-null depending
+ // on the |aORT| value given.
+
+ if (aORT == ORTNone) {
+ if (gDebugLevel >= DEBUG_WARNINGS) {
+ fprintf(stderr,
+ "GetOCSPResponseForType called with type ORTNone, "
+ "which makes no sense.\n");
+ }
+ return nullptr;
+ }
+
+ if (aORT == ORTEmpty) {
+ SECItemArray* arr = SECITEM_AllocArray(aArena.get(), nullptr, 1);
+ arr->items[0].data = nullptr;
+ arr->items[0].len = 0;
+ return arr;
+ }
+
+ time_t now = time(nullptr) + aThisUpdateSkew;
+ time_t oldNow = now - (8 * Time::ONE_DAY_IN_SECONDS);
+
+ mozilla::UniqueCERTCertificate cert(CERT_DupCertificate(aCert.get()));
+
+ if (aORT == ORTGoodOtherCert) {
+ cert.reset(PK11_FindCertFromNickname(aAdditionalCertName, nullptr));
+ if (!cert) {
+ PrintPRError("PK11_FindCertFromNickname failed");
+ return nullptr;
+ }
+ }
+ // XXX CERT_FindCertIssuer uses the old, deprecated path-building logic
+ mozilla::UniqueCERTCertificate issuerCert(
+ CERT_FindCertIssuer(aCert.get(), PR_Now(), certUsageSSLCA));
+ if (!issuerCert) {
+ PrintPRError("CERT_FindCertIssuer failed");
+ return nullptr;
+ }
+ Input issuer;
+ if (issuer.Init(cert->derIssuer.data, cert->derIssuer.len) != Success) {
+ return nullptr;
+ }
+ Input issuerPublicKey;
+ if (issuerPublicKey.Init(issuerCert->derPublicKey.data,
+ issuerCert->derPublicKey.len) != Success) {
+ return nullptr;
+ }
+ Input serialNumber;
+ if (serialNumber.Init(cert->serialNumber.data, cert->serialNumber.len) !=
+ Success) {
+ return nullptr;
+ }
+ CertID certID(issuer, issuerPublicKey, serialNumber);
+ OCSPResponseContext context(certID, now);
+
+ mozilla::UniqueCERTCertificate signerCert;
+ if (aORT == ORTGoodOtherCA || aORT == ORTDelegatedIncluded ||
+ aORT == ORTDelegatedIncludedLast || aORT == ORTDelegatedMissing ||
+ aORT == ORTDelegatedMissingMultiple) {
+ signerCert.reset(PK11_FindCertFromNickname(aAdditionalCertName, nullptr));
+ if (!signerCert) {
+ PrintPRError("PK11_FindCertFromNickname failed");
+ return nullptr;
+ }
+ }
+
+ ByteString certs[5];
+
+ if (aORT == ORTDelegatedIncluded) {
+ certs[0].assign(signerCert->derCert.data, signerCert->derCert.len);
+ context.certs = certs;
+ }
+ if (aORT == ORTDelegatedIncludedLast || aORT == ORTDelegatedMissingMultiple) {
+ certs[0].assign(issuerCert->derCert.data, issuerCert->derCert.len);
+ certs[1].assign(cert->derCert.data, cert->derCert.len);
+ certs[2].assign(issuerCert->derCert.data, issuerCert->derCert.len);
+ if (aORT != ORTDelegatedMissingMultiple) {
+ certs[3].assign(signerCert->derCert.data, signerCert->derCert.len);
+ }
+ context.certs = certs;
+ }
+
+ switch (aORT) {
+ case ORTMalformed:
+ context.responseStatus = 1;
+ break;
+ case ORTSrverr:
+ context.responseStatus = 2;
+ break;
+ case ORTTryLater:
+ context.responseStatus = 3;
+ break;
+ case ORTNeedsSig:
+ context.responseStatus = 5;
+ break;
+ case ORTUnauthorized:
+ context.responseStatus = 6;
+ break;
+ default:
+ // context.responseStatus is 0 in all other cases, and it has
+ // already been initialized in the constructor.
+ break;
+ }
+ if (aORT == ORTSkipResponseBytes) {
+ context.skipResponseBytes = true;
+ }
+ if (aORT == ORTExpired || aORT == ORTExpiredFreshCA ||
+ aORT == ORTRevokedOld || aORT == ORTUnknownOld) {
+ context.thisUpdate = oldNow;
+ context.nextUpdate = oldNow + Time::ONE_DAY_IN_SECONDS;
+ }
+ if (aORT == ORTLongValidityAlmostExpired) {
+ context.thisUpdate = now - (320 * Time::ONE_DAY_IN_SECONDS);
+ }
+ if (aORT == ORTAncientAlmostExpired) {
+ context.thisUpdate = now - (640 * Time::ONE_DAY_IN_SECONDS);
+ }
+ if (aORT == ORTRevoked || aORT == ORTRevokedOld) {
+ context.certStatus = 1;
+ }
+ if (aORT == ORTUnknown || aORT == ORTUnknownOld) {
+ context.certStatus = 2;
+ }
+ if (aORT == ORTBadSignature) {
+ context.badSignature = true;
+ }
+ OCSPResponseExtension extension;
+ if (aORT == ORTCriticalExtension || aORT == ORTNoncriticalExtension) {
+ // python DottedOIDToCode.py --tlv
+ // some-Mozilla-OID 1.3.6.1.4.1.13769.666.666.666.1.500.9.2
+ static const uint8_t tlv_some_Mozilla_OID[] = {
+ 0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85,
+ 0x1a, 0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x02};
+
+ extension.id.assign(tlv_some_Mozilla_OID, sizeof(tlv_some_Mozilla_OID));
+ extension.critical = (aORT == ORTCriticalExtension);
+ extension.value.push_back(0x05); // tag: NULL
+ extension.value.push_back(0x00); // length: 0
+ extension.next = nullptr;
+ context.responseExtensions = &extension;
+ }
+ if (aORT == ORTEmptyExtensions) {
+ context.includeEmptyExtensions = true;
+ }
+
+ if (!signerCert) {
+ signerCert.reset(CERT_DupCertificate(issuerCert.get()));
+ }
+ context.signerKeyPair.reset(CreateTestKeyPairFromCert(signerCert));
+ if (!context.signerKeyPair) {
+ PrintPRError("PK11_FindKeyByAnyCert failed");
+ return nullptr;
+ }
+
+ ByteString response(CreateEncodedOCSPResponse(context));
+ if (ENCODING_FAILED(response)) {
+ PrintPRError("CreateEncodedOCSPResponse failed");
+ return nullptr;
+ }
+
+ SECItem item = {siBuffer, const_cast<uint8_t*>(response.data()),
+ static_cast<unsigned int>(response.length())};
+ SECItemArray arr = {&item, 1};
+ return SECITEM_DupArray(aArena.get(), &arr);
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h
new file mode 100644
index 0000000000..c72eae6a8e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.h
@@ -0,0 +1,66 @@
+/* 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/. */
+
+// Implements generating OCSP responses of various types. Used by the
+// programs in tlsserver/cmd.
+
+#ifndef OCSPCommon_h
+#define OCSPCommon_h
+
+#include "ScopedNSSTypes.h"
+#include "certt.h"
+#include "seccomon.h"
+
+enum OCSPResponseType {
+ ORTNull = 0,
+ ORTGood, // the certificate is good
+ ORTRevoked, // the certificate has been revoked
+ ORTRevokedOld, // same, but the response is old
+ ORTUnknown, // the responder doesn't know if the cert is good
+ ORTUnknownOld, // same, but the response is old
+ ORTGoodOtherCert, // the response references a different certificate
+ ORTGoodOtherCA, // the wrong CA has signed the response
+ ORTExpired, // the signature on the response has expired
+ ORTExpiredFreshCA, // fresh signature, but old validity period
+ ORTNone, // no stapled response
+ ORTEmpty, // an empty stapled response
+ ORTMalformed, // the response from the responder was malformed
+ ORTSrverr, // the response indicates there was a server error
+ ORTTryLater, // the responder replied with "try again later"
+ ORTNeedsSig, // the response needs a signature
+ ORTUnauthorized, // the responder is not authorized for this certificate
+ ORTBadSignature, // the response has a signature that does not verify
+ ORTSkipResponseBytes, // the response does not include responseBytes
+ ORTCriticalExtension, // the response includes a critical extension
+ ORTNoncriticalExtension, // the response includes an extension that is not
+ // critical
+ ORTEmptyExtensions, // the response includes a SEQUENCE OF Extension that is
+ // empty
+ ORTDelegatedIncluded, // the response is signed by an included delegated
+ // responder
+ ORTDelegatedIncludedLast, // same, but multiple other certificates are
+ // included
+ ORTDelegatedMissing, // the response is signed by a not included delegated
+ // responder
+ ORTDelegatedMissingMultiple, // same, but multiple other certificates are
+ // included
+ ORTLongValidityAlmostExpired, // a good response, but that was generated a
+ // almost a year ago
+ ORTAncientAlmostExpired, // a good response, with a validity of almost two
+ // years almost expiring
+};
+
+struct OCSPHost {
+ const char* mHostName;
+ OCSPResponseType mORT;
+ const char* mAdditionalCertName; // useful for ORTGoodOtherCert, etc.
+ const char* mServerCertName;
+};
+
+SECItemArray* GetOCSPResponseForType(
+ OCSPResponseType aORT, const mozilla::UniqueCERTCertificate& aCert,
+ const mozilla::UniquePLArenaPool& aArena, const char* aAdditionalCertName,
+ time_t aThisUpdateSkew);
+
+#endif // OCSPCommon_h
diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp
new file mode 100644
index 0000000000..463f865411
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.cpp
@@ -0,0 +1,669 @@
+/* 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/. */
+
+#include "TLSServer.h"
+
+#include <stdio.h>
+#include <string>
+#include <thread>
+#include <vector>
+#include <fstream>
+#include <iostream>
+#ifdef XP_WIN
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#include <utility>
+
+#include "base64.h"
+#include "mozilla/Sprintf.h"
+#include "nspr.h"
+#include "nss.h"
+#include "plarenas.h"
+#include "prenv.h"
+#include "prerror.h"
+#include "prnetdb.h"
+#include "prtime.h"
+#include "ssl.h"
+#include "sslproto.h"
+
+namespace mozilla {
+namespace test {
+
+static const uint16_t LISTEN_PORT = 8443;
+
+DebugLevel gDebugLevel = DEBUG_ERRORS;
+uint16_t gCallbackPort = 0;
+
+const std::string kPEMBegin = "-----BEGIN ";
+const std::string kPEMEnd = "-----END ";
+const char DEFAULT_CERT_NICKNAME[] = "default-ee";
+
+struct Connection {
+ PRFileDesc* mSocket;
+ char mByte;
+
+ explicit Connection(PRFileDesc* aSocket);
+ ~Connection();
+};
+
+Connection::Connection(PRFileDesc* aSocket) : mSocket(aSocket), mByte(0) {}
+
+Connection::~Connection() {
+ if (mSocket) {
+ PR_Close(mSocket);
+ }
+}
+
+void PrintPRError(const char* aPrefix) {
+ const char* err = PR_ErrorToName(PR_GetError());
+ if (err) {
+ if (gDebugLevel >= DEBUG_ERRORS) {
+ fprintf(stderr, "%s: %s\n", aPrefix, err);
+ }
+ } else {
+ if (gDebugLevel >= DEBUG_ERRORS) {
+ fprintf(stderr, "%s\n", aPrefix);
+ }
+ }
+}
+
+// This decodes a PEM file into `item`. The line endings need to be
+// UNIX-style, or there will be cross-platform issues.
+static bool DecodePEMFile(const std::string& filename, SECItem* item) {
+ std::ifstream in(filename);
+ if (in.bad()) {
+ return false;
+ }
+
+ char buf[1024];
+ in.getline(buf, sizeof(buf));
+ if (in.bad()) {
+ return false;
+ }
+
+ if (strncmp(buf, kPEMBegin.c_str(), kPEMBegin.size()) != 0) {
+ return false;
+ }
+
+ std::string value;
+ for (;;) {
+ in.getline(buf, sizeof(buf));
+ if (in.bad()) {
+ return false;
+ }
+
+ if (strncmp(buf, kPEMEnd.c_str(), kPEMEnd.size()) == 0) {
+ break;
+ }
+
+ value += buf;
+ }
+
+ unsigned int binLength;
+ UniquePORTString bin(BitwiseCast<char*, unsigned char*>(
+ ATOB_AsciiToData(value.c_str(), &binLength)));
+ if (!bin || binLength == 0) {
+ PrintPRError("ATOB_AsciiToData failed");
+ return false;
+ }
+
+ if (SECITEM_AllocItem(nullptr, item, binLength) == nullptr) {
+ return false;
+ }
+
+ PORT_Memcpy(item->data, bin.get(), binLength);
+ return true;
+}
+
+static SECStatus AddKeyFromFile(const std::string& path,
+ const std::string& filename) {
+ ScopedAutoSECItem item;
+
+ std::string file = path + "/" + filename;
+ if (!DecodePEMFile(file, &item)) {
+ return SECFailure;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (!slot) {
+ PrintPRError("PK11_GetInternalKeySlot failed");
+ return SECFailure;
+ }
+
+ if (PK11_NeedUserInit(slot.get())) {
+ if (PK11_InitPin(slot.get(), nullptr, nullptr) != SECSuccess) {
+ PrintPRError("PK11_InitPin failed");
+ return SECFailure;
+ }
+ }
+
+ SECKEYPrivateKey* privateKey = nullptr;
+ SECItem nick = {siBuffer,
+ BitwiseCast<unsigned char*, const char*>(filename.data()),
+ static_cast<unsigned int>(filename.size())};
+ if (PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot.get(), &item, &nick, nullptr, true, false, KU_ALL, &privateKey,
+ nullptr) != SECSuccess) {
+ PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
+ return SECFailure;
+ }
+
+ SECKEY_DestroyPrivateKey(privateKey);
+ return SECSuccess;
+}
+
+static SECStatus AddCertificateFromFile(const std::string& path,
+ const std::string& filename) {
+ ScopedAutoSECItem item;
+
+ std::string file = path + "/" + filename;
+ if (!DecodePEMFile(file, &item)) {
+ return SECFailure;
+ }
+
+ UniqueCERTCertificate cert(CERT_NewTempCertificate(
+ CERT_GetDefaultCertDB(), &item, nullptr, false, true));
+ if (!cert) {
+ PrintPRError("CERT_NewTempCertificate failed");
+ return SECFailure;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (!slot) {
+ PrintPRError("PK11_GetInternalKeySlot failed");
+ return SECFailure;
+ }
+ // The nickname is the filename without '.pem'.
+ std::string nickname = filename.substr(0, filename.length() - 4);
+ SECStatus rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
+ nickname.c_str(), false);
+ if (rv != SECSuccess) {
+ PrintPRError("PK11_ImportCert failed");
+ return rv;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus LoadCertificatesAndKeys(const char* basePath) {
+ // The NSS cert DB path could have been specified as "sql:path". Trim off
+ // the leading "sql:" if so.
+ if (strncmp(basePath, "sql:", 4) == 0) {
+ basePath = basePath + 4;
+ }
+
+ UniquePRDir fdDir(PR_OpenDir(basePath));
+ if (!fdDir) {
+ PrintPRError("PR_OpenDir failed");
+ return SECFailure;
+ }
+ // On the B2G ICS emulator, operations taken in AddCertificateFromFile
+ // appear to interact poorly with readdir (more specifically, something is
+ // causing readdir to never return null - it indefinitely loops through every
+ // file in the directory, which causes timeouts). Rather than waste more time
+ // chasing this down, loading certificates and keys happens in two phases:
+ // filename collection and then loading. (This is probably a good
+ // idea anyway because readdir isn't reentrant. Something could change later
+ // such that it gets called as a result of calling AddCertificateFromFile or
+ // AddKeyFromFile.)
+ std::vector<std::string> certificates;
+ std::vector<std::string> keys;
+ for (PRDirEntry* dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH); dirEntry;
+ dirEntry = PR_ReadDir(fdDir.get(), PR_SKIP_BOTH)) {
+ size_t nameLength = strlen(dirEntry->name);
+ if (nameLength > 4) {
+ if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) {
+ certificates.push_back(dirEntry->name);
+ } else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) {
+ keys.push_back(dirEntry->name);
+ }
+ }
+ }
+ SECStatus rv;
+ for (std::string& certificate : certificates) {
+ rv = AddCertificateFromFile(basePath, certificate.c_str());
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ for (std::string& key : keys) {
+ rv = AddKeyFromFile(basePath, key.c_str());
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ }
+ return SECSuccess;
+}
+
+SECStatus InitializeNSS(const char* nssCertDBDir) {
+ // Try initializing an existing DB.
+ if (NSS_Init(nssCertDBDir) == SECSuccess) {
+ return SECSuccess;
+ }
+
+ // Create a new DB if there is none...
+ SECStatus rv = NSS_Initialize(nssCertDBDir, nullptr, nullptr, nullptr, 0);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ // ...and load all certificates into it.
+ return LoadCertificatesAndKeys(nssCertDBDir);
+}
+
+nsresult SendAll(PRFileDesc* aSocket, const char* aData, size_t aDataLen) {
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "sending '%s'\n", aData);
+ }
+
+ while (aDataLen > 0) {
+ int32_t bytesSent =
+ PR_Send(aSocket, aData, aDataLen, 0, PR_INTERVAL_NO_TIMEOUT);
+ if (bytesSent == -1) {
+ PrintPRError("PR_Send failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ aDataLen -= bytesSent;
+ aData += bytesSent;
+ }
+
+ return NS_OK;
+}
+
+nsresult ReplyToRequest(Connection* aConn) {
+ // For debugging purposes, SendAll can print out what it's sending.
+ // So, any strings we give to it to send need to be null-terminated.
+ char buf[2] = {aConn->mByte, 0};
+ return SendAll(aConn->mSocket, buf, 1);
+}
+
+nsresult SetupTLS(Connection* aConn, PRFileDesc* aModelSocket) {
+ PRFileDesc* sslSocket = SSL_ImportFD(aModelSocket, aConn->mSocket);
+ if (!sslSocket) {
+ PrintPRError("SSL_ImportFD failed");
+ return NS_ERROR_FAILURE;
+ }
+ aConn->mSocket = sslSocket;
+
+ SSL_OptionSet(sslSocket, SSL_SECURITY, true);
+ SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, false);
+ SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, true);
+
+ SSL_ResetHandshake(sslSocket, /* asServer */ 1);
+
+ return NS_OK;
+}
+
+nsresult ReadRequest(Connection* aConn) {
+ int32_t bytesRead =
+ PR_Recv(aConn->mSocket, &aConn->mByte, 1, 0, PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ PrintPRError("PR_Recv failed");
+ return NS_ERROR_FAILURE;
+ } else if (bytesRead == 0) {
+ PR_SetError(PR_IO_ERROR, 0);
+ PrintPRError("PR_Recv EOF in ReadRequest");
+ return NS_ERROR_FAILURE;
+ } else {
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "read '0x%hhx'\n", aConn->mByte);
+ }
+ }
+ return NS_OK;
+}
+
+void HandleConnection(PRFileDesc* aSocket,
+ const UniquePRFileDesc& aModelSocket) {
+ Connection conn(aSocket);
+ nsresult rv = SetupTLS(&conn, aModelSocket.get());
+ if (NS_FAILED(rv)) {
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ PrintPRError("PR_Recv failed");
+ exit(1);
+ }
+
+ // TODO: On tests that are expected to fail (e.g. due to a revoked
+ // certificate), the client will close the connection wtihout sending us the
+ // request byte. In those cases, we should keep going. But, in the cases
+ // where the connection is supposed to suceed, we should verify that we
+ // successfully receive the request and send the response.
+ rv = ReadRequest(&conn);
+ if (NS_SUCCEEDED(rv)) {
+ rv = ReplyToRequest(&conn);
+ }
+}
+
+// returns 0 on success, non-zero on error
+int DoCallback() {
+ UniquePRFileDesc socket(PR_NewTCPSocket());
+ if (!socket) {
+ PrintPRError("PR_NewTCPSocket failed");
+ return 1;
+ }
+
+ PRNetAddr addr;
+ PR_InitializeNetAddr(PR_IpAddrLoopback, gCallbackPort, &addr);
+ if (PR_Connect(socket.get(), &addr, PR_INTERVAL_NO_TIMEOUT) != PR_SUCCESS) {
+ PrintPRError("PR_Connect failed");
+ return 1;
+ }
+
+ const char* request = "GET / HTTP/1.0\r\n\r\n";
+ SendAll(socket.get(), request, strlen(request));
+ char buf[4096];
+ memset(buf, 0, sizeof(buf));
+ int32_t bytesRead =
+ PR_Recv(socket.get(), buf, sizeof(buf) - 1, 0, PR_INTERVAL_NO_TIMEOUT);
+ if (bytesRead < 0) {
+ PrintPRError("PR_Recv failed 1");
+ return 1;
+ }
+ if (bytesRead == 0) {
+ fprintf(stderr, "PR_Recv eof 1\n");
+ return 1;
+ }
+ fprintf(stderr, "%s\n", buf);
+ return 0;
+}
+
+SECStatus ConfigSecureServerWithNamedCert(
+ PRFileDesc* fd, const char* certName,
+ /*optional*/ UniqueCERTCertificate* certOut,
+ /*optional*/ SSLKEAType* keaOut,
+ /*optional*/ SSLExtraServerCertData* extraData) {
+ UniqueCERTCertificate cert(PK11_FindCertFromNickname(certName, nullptr));
+ if (!cert) {
+ PrintPRError("PK11_FindCertFromNickname failed");
+ return SECFailure;
+ }
+ // If an intermediate certificate issued the server certificate (rather than
+ // directly by a trust anchor), we want to send it along in the handshake so
+ // we don't encounter unknown issuer errors when that's not what we're
+ // testing.
+ UniqueCERTCertificateList certList;
+ UniqueCERTCertificate issuerCert(
+ CERT_FindCertByName(CERT_GetDefaultCertDB(), &cert->derIssuer));
+ // If we can't find the issuer cert, continue without it.
+ if (issuerCert) {
+ // Sadly, CERTCertificateList does not have a CERT_NewCertificateList
+ // utility function, so we must create it ourselves. This consists
+ // of creating an arena, allocating space for the CERTCertificateList,
+ // and then transferring ownership of the arena to that list.
+ UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!arena) {
+ PrintPRError("PORT_NewArena failed");
+ return SECFailure;
+ }
+ certList.reset(static_cast<CERTCertificateList*>(
+ PORT_ArenaAlloc(arena.get(), sizeof(CERTCertificateList))));
+ if (!certList) {
+ PrintPRError("PORT_ArenaAlloc failed");
+ return SECFailure;
+ }
+ certList->arena = arena.release();
+ // We also have to manually copy the certificates we care about to the
+ // list, because there aren't any utility functions for that either.
+ certList->certs = static_cast<SECItem*>(
+ PORT_ArenaAlloc(certList->arena, 2 * sizeof(SECItem)));
+ if (SECITEM_CopyItem(certList->arena, certList->certs, &cert->derCert) !=
+ SECSuccess) {
+ PrintPRError("SECITEM_CopyItem failed");
+ return SECFailure;
+ }
+ if (SECITEM_CopyItem(certList->arena, certList->certs + 1,
+ &issuerCert->derCert) != SECSuccess) {
+ PrintPRError("SECITEM_CopyItem failed");
+ return SECFailure;
+ }
+ certList->len = 2;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (!slot) {
+ PrintPRError("PK11_GetInternalKeySlot failed");
+ return SECFailure;
+ }
+ UniqueSECKEYPrivateKey key(
+ PK11_FindKeyByDERCert(slot.get(), cert.get(), nullptr));
+ if (!key) {
+ PrintPRError("PK11_FindKeyByDERCert failed");
+ return SECFailure;
+ }
+
+ if (extraData) {
+ SSLExtraServerCertData dataCopy = {ssl_auth_null, nullptr, nullptr,
+ nullptr, nullptr, nullptr};
+ memcpy(&dataCopy, extraData, sizeof(dataCopy));
+ dataCopy.certChain = certList.get();
+
+ if (SSL_ConfigServerCert(fd, cert.get(), key.get(), &dataCopy,
+ sizeof(dataCopy)) != SECSuccess) {
+ PrintPRError("SSL_ConfigServerCert failed");
+ return SECFailure;
+ }
+
+ } else {
+ // This is the deprecated setup mechanism, to be cleaned up in Bug 1569222
+ SSLKEAType certKEA = NSS_FindCertKEAType(cert.get());
+ if (SSL_ConfigSecureServerWithCertChain(fd, cert.get(), certList.get(),
+ key.get(), certKEA) != SECSuccess) {
+ PrintPRError("SSL_ConfigSecureServer failed");
+ return SECFailure;
+ }
+
+ if (keaOut) {
+ *keaOut = certKEA;
+ }
+ }
+
+ if (certOut) {
+ *certOut = std::move(cert);
+ }
+
+ SSL_OptionSet(fd, SSL_NO_CACHE, false);
+ SSL_OptionSet(fd, SSL_ENABLE_SESSION_TICKETS, true);
+
+ return SECSuccess;
+}
+
+#ifdef XP_WIN
+using PidType = DWORD;
+constexpr bool IsValidPid(long long pid) {
+ // Excluding `(DWORD)-1` because it is not a valid process ID.
+ // See https://devblogs.microsoft.com/oldnewthing/20040223-00/?p=40503
+ return pid > 0 && pid < std::numeric_limits<PidType>::max();
+}
+#else
+using PidType = pid_t;
+constexpr bool IsValidPid(long long pid) {
+ return pid > 0 && pid <= std::numeric_limits<PidType>::max();
+}
+#endif
+
+PidType ConvertPid(const char* pidStr) {
+ long long pid = strtoll(pidStr, nullptr, 10);
+ if (!IsValidPid(pid)) {
+ return 0;
+ }
+ return static_cast<PidType>(pid);
+}
+
+int StartServer(int argc, char* argv[], SSLSNISocketConfig sniSocketConfig,
+ void* sniSocketConfigArg, ServerConfigFunc configFunc) {
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s <NSS DB directory> <ppid>\n", argv[0]);
+ return 1;
+ }
+ const char* nssCertDBDir = argv[1];
+ PidType ppid = ConvertPid(argv[2]);
+
+ const char* debugLevel = PR_GetEnv("MOZ_TLS_SERVER_DEBUG_LEVEL");
+ if (debugLevel) {
+ int level = atoi(debugLevel);
+ switch (level) {
+ case DEBUG_ERRORS:
+ gDebugLevel = DEBUG_ERRORS;
+ break;
+ case DEBUG_WARNINGS:
+ gDebugLevel = DEBUG_WARNINGS;
+ break;
+ case DEBUG_VERBOSE:
+ gDebugLevel = DEBUG_VERBOSE;
+ break;
+ default:
+ PrintPRError("invalid MOZ_TLS_SERVER_DEBUG_LEVEL");
+ return 1;
+ }
+ }
+
+ const char* callbackPort = PR_GetEnv("MOZ_TLS_SERVER_CALLBACK_PORT");
+ if (callbackPort) {
+ gCallbackPort = atoi(callbackPort);
+ }
+
+ if (InitializeNSS(nssCertDBDir) != SECSuccess) {
+ PR_fprintf(PR_STDERR, "InitializeNSS failed");
+ return 1;
+ }
+
+ if (NSS_SetDomesticPolicy() != SECSuccess) {
+ PrintPRError("NSS_SetDomesticPolicy failed");
+ return 1;
+ }
+
+ if (SSL_ConfigServerSessionIDCache(0, 0, 0, nullptr) != SECSuccess) {
+ PrintPRError("SSL_ConfigServerSessionIDCache failed");
+ return 1;
+ }
+
+ UniquePRFileDesc serverSocket(PR_NewTCPSocket());
+ if (!serverSocket) {
+ PrintPRError("PR_NewTCPSocket failed");
+ return 1;
+ }
+
+ PRSocketOptionData socketOption;
+ socketOption.option = PR_SockOpt_Reuseaddr;
+ socketOption.value.reuse_addr = true;
+ PR_SetSocketOption(serverSocket.get(), &socketOption);
+
+ PRNetAddr serverAddr;
+ PR_InitializeNetAddr(PR_IpAddrLoopback, LISTEN_PORT, &serverAddr);
+ if (PR_Bind(serverSocket.get(), &serverAddr) != PR_SUCCESS) {
+ PrintPRError("PR_Bind failed");
+ return 1;
+ }
+
+ if (PR_Listen(serverSocket.get(), 1) != PR_SUCCESS) {
+ PrintPRError("PR_Listen failed");
+ return 1;
+ }
+
+ UniquePRFileDesc rawModelSocket(PR_NewTCPSocket());
+ if (!rawModelSocket) {
+ PrintPRError("PR_NewTCPSocket failed for rawModelSocket");
+ return 1;
+ }
+
+ UniquePRFileDesc modelSocket(SSL_ImportFD(nullptr, rawModelSocket.release()));
+ if (!modelSocket) {
+ PrintPRError("SSL_ImportFD of rawModelSocket failed");
+ return 1;
+ }
+
+ SSLVersionRange range = {0, 0};
+ if (SSL_VersionRangeGet(modelSocket.get(), &range) != SECSuccess) {
+ PrintPRError("SSL_VersionRangeGet failed");
+ return 1;
+ }
+
+ if (range.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ range.max = SSL_LIBRARY_VERSION_TLS_1_3;
+ if (SSL_VersionRangeSet(modelSocket.get(), &range) != SECSuccess) {
+ PrintPRError("SSL_VersionRangeSet failed");
+ return 1;
+ }
+ }
+
+ if (SSL_SNISocketConfigHook(modelSocket.get(), sniSocketConfig,
+ sniSocketConfigArg) != SECSuccess) {
+ PrintPRError("SSL_SNISocketConfigHook failed");
+ return 1;
+ }
+
+ // We have to configure the server with a certificate, but it's not one
+ // we're actually going to end up using. In the SNI callback, we pick
+ // the right certificate for the connection.
+ //
+ // Provide an empty |extra_data| to force config via SSL_ConfigServerCert.
+ // This is a temporary mechanism to work around inconsistent setting of
+ // |authType| in the deprecated API (preventing the default cert from
+ // being removed in favor of the SNI-selected cert). This may be removed
+ // after Bug 1569222 removes the deprecated mechanism.
+ SSLExtraServerCertData extra_data = {ssl_auth_null, nullptr, nullptr,
+ nullptr, nullptr, nullptr};
+ if (ConfigSecureServerWithNamedCert(modelSocket.get(), DEFAULT_CERT_NICKNAME,
+ nullptr, nullptr,
+ &extra_data) != SECSuccess) {
+ return 1;
+ }
+
+ // Call back to implementation-defined configuration func, if provided.
+ if (configFunc) {
+ if (((configFunc)(modelSocket.get())) != SECSuccess) {
+ PrintPRError("configFunc failed");
+ return 1;
+ }
+ }
+
+ if (gCallbackPort != 0) {
+ if (DoCallback()) {
+ return 1;
+ }
+ }
+
+ std::thread([ppid] {
+ if (!ppid) {
+ if (gDebugLevel >= DEBUG_ERRORS) {
+ fprintf(stderr, "invalid ppid\n");
+ }
+ return;
+ }
+#ifdef XP_WIN
+ HANDLE parent = OpenProcess(SYNCHRONIZE, false, ppid);
+ if (!parent) {
+ if (gDebugLevel >= DEBUG_ERRORS) {
+ fprintf(stderr, "OpenProcess failed\n");
+ }
+ return;
+ }
+ WaitForSingleObject(parent, INFINITE);
+ CloseHandle(parent);
+#else
+ while (getppid() == ppid) {
+ sleep(1);
+ }
+#endif
+ if (gDebugLevel >= DEBUG_ERRORS) {
+ fprintf(stderr, "Parent process crashed\n");
+ }
+ exit(1);
+ }).detach();
+
+ while (true) {
+ PRNetAddr clientAddr;
+ PRFileDesc* clientSocket =
+ PR_Accept(serverSocket.get(), &clientAddr, PR_INTERVAL_NO_TIMEOUT);
+ HandleConnection(clientSocket, modelSocket);
+ }
+
+ return 0;
+}
+
+} // namespace test
+} // namespace mozilla
diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h
new file mode 100644
index 0000000000..3927b3e541
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/TLSServer.h
@@ -0,0 +1,93 @@
+/* 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/. */
+
+#ifndef TLSServer_h
+#define TLSServer_h
+
+// This is a standalone server for testing SSL features of Gecko.
+// The client is expected to connect and initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to). If all is good, the client then
+// sends one encrypted byte and receives that same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdint.h>
+
+#include "ScopedNSSTypes.h"
+#include "mozilla/Casting.h"
+#include "prio.h"
+#include "secerr.h"
+#include "ssl.h"
+
+namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRDir, PRDir, PR_CloseDir);
+
+} // namespace mozilla
+
+namespace mozilla {
+namespace test {
+
+typedef SECStatus (*ServerConfigFunc)(PRFileDesc* fd);
+
+enum DebugLevel { DEBUG_ERRORS = 1, DEBUG_WARNINGS = 2, DEBUG_VERBOSE = 3 };
+
+extern DebugLevel gDebugLevel;
+
+void PrintPRError(const char* aPrefix);
+
+// The default certificate is trusted for localhost and *.example.com
+extern const char DEFAULT_CERT_NICKNAME[];
+
+// ConfigSecureServerWithNamedCert sets up the hostname name provided. If the
+// extraData parameter is presented, extraData->certChain will be automatically
+// filled in using database information.
+// Pass DEFAULT_CERT_NICKNAME as certName unless you need a specific
+// certificate.
+SECStatus ConfigSecureServerWithNamedCert(
+ PRFileDesc* fd, const char* certName,
+ /*optional*/ UniqueCERTCertificate* cert,
+ /*optional*/ SSLKEAType* kea,
+ /*optional*/ SSLExtraServerCertData* extraData);
+
+SECStatus InitializeNSS(const char* nssCertDBDir);
+
+// StartServer initializes NSS, sockets, the SNI callback, and a default
+// certificate. configFunc (optional) is a pointer to an implementation-
+// defined configuration function, which is called on the model socket
+// prior to handling any connections.
+int StartServer(int argc, char* argv[], SSLSNISocketConfig sniSocketConfig,
+ void* sniSocketConfigArg,
+ ServerConfigFunc configFunc = nullptr);
+
+template <typename Host>
+inline const Host* GetHostForSNI(const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize, const Host* hosts) {
+ for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
+ for (const Host* host = hosts; host->mHostName; ++host) {
+ SECItem hostName;
+ hostName.data = BitwiseCast<unsigned char*, const char*>(host->mHostName);
+ hostName.len = strlen(host->mHostName);
+ if (SECITEM_ItemsAreEqual(&hostName, &aSrvNameArr[i])) {
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+ }
+ return host;
+ }
+ }
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "could not find host info from SNI\n");
+ }
+
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return nullptr;
+}
+
+} // namespace test
+} // namespace mozilla
+
+#endif // TLSServer_h
diff --git a/security/manager/ssl/tests/unit/tlsserver/lib/moz.build b/security/manager/ssl/tests/unit/tlsserver/lib/moz.build
new file mode 100644
index 0000000000..1fd92d724d
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+ "OCSPCommon.cpp",
+ "TLSServer.cpp",
+]
+
+USE_LIBS += [
+ "mozpkix-testlib",
+]
+
+Library("tlsserver")
diff --git a/security/manager/ssl/tests/unit/tlsserver/moz.build b/security/manager/ssl/tests/unit/tlsserver/moz.build
new file mode 100644
index 0000000000..1488352914
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# lib must be first, because cmd depends on its output
+DIRS += ["lib", "cmd"]
diff --git a/security/manager/ssl/tests/unit/xpcshell-smartcards.ini b/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
new file mode 100644
index 0000000000..df3cfa1c87
--- /dev/null
+++ b/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
@@ -0,0 +1,15 @@
+[DEFAULT]
+head = head_psm.js
+tail =
+tags = psm
+skip-if = toolkit == 'android'
+support-files =
+
+[test_osclientcerts_module.js]
+skip-if = (os != 'win' && os != 'mac') || processor == 'aarch64'
+[test_pkcs11_module.js]
+[test_pkcs11_moduleDB.js]
+[test_pkcs11_safe_mode.js]
+[test_pkcs11_slot.js]
+[test_pkcs11_token.js]
+[test_pkcs11_tokenDB.js]
diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini
new file mode 100644
index 0000000000..e80615104a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -0,0 +1,237 @@
+[DEFAULT]
+head = head_psm.js
+tags = psm
+firefox-appdir = browser
+support-files =
+ bad_certs/**
+ ocsp_certs/**
+ test_baseline_requirements/**
+ test_broken_fips/**
+ test_cert_eku/**
+ test_cert_embedded_null/**
+ test_cert_isBuiltInRoot_reload/**
+ test_cert_keyUsage/**
+ test_cert_overrides_read_only/**
+ test_cert_sha1/**
+ test_cert_signatures/**
+ test_cert_storage_direct/**
+ test_cert_storage_preexisting/**
+ test_cert_storage_preexisting_crlite/**
+ test_cert_trust/**
+ test_cert_utf8/**
+ test_cert_version/**
+ test_certDB_import/**
+ test_content_signing/**
+ test_crlite_filters/**
+ test_ct/**
+ test_delegated_credentials/**
+ test_encrypted_client_hello/**
+ test_ev_certs/**
+ test_intermediate_basic_usage_constraints/**
+ test_intermediate_preloads/**
+ test_keysize/**
+ test_keysize_ev/**
+ test_missing_intermediate/**
+ test_name_constraints/**
+ test_ocsp_url/**
+ test_onecrl/**
+ test_sanctions/**
+ test_sdr_preexisting/**
+ test_sdr_preexisting_with_password/**
+ test_self_signed_certs/**
+ test_signed_apps/**
+ test_startcom_wosign/**
+ test_validity/**
+ tlsserver/**
+
+[test_add_preexisting_cert.js]
+[test_baseline_requirements_subject_common_name.js]
+[test_blocklist_onecrl.js]
+# Skip signature tests for Thunderbird (Bug 1341983).
+skip-if = appname == "thunderbird"
+tags = remote-settings blocklist psm
+[test_blocklist_pinning.js]
+tags = remote-settings blocklist psm
+[test_broken_fips.js]
+# FIPS has never been a thing on Android, so the workaround doesn't
+# exist on that platform.
+# FIPS still works on Linux, so this test doesn't make any sense there.
+# FIPS still works on Windows, but running the test to ensure that it does not
+# break with a non-ASCII profile path.
+skip-if = toolkit == 'android' || os == 'linux'
+[test_cert_storage.js]
+tags = addons psm blocklist
+[test_cert_storage_broken_db.js]
+[test_cert_storage_direct.js]
+[test_cert_storage_preexisting.js]
+[test_cert_storage_preexisting_crlite.js]
+# This test cannot succeed on 32-bit platforms. See bugs 1546361 and 1548956.
+skip-if = bits != 64
+[test_cert_storage_prefs.js]
+[test_cert_chains.js]
+run-sequentially = hardcoded ports
+[test_cert_dbKey.js]
+[test_cert_eku.js]
+[test_cert_embedded_null.js]
+[test_cert_expiration_canary.js]
+run-if = nightly_build
+[test_cert_keyUsage.js]
+[test_cert_isBuiltInRoot.js]
+[test_cert_isBuiltInRoot_reload.js]
+[test_cert_overrides.js]
+run-sequentially = hardcoded ports
+[test_cert_overrides_read_only.js]
+run-sequentially = hardcoded ports
+[test_cert_override_bits_mismatches.js]
+run-sequentially = hardcoded ports
+[test_cert_sha1.js]
+[test_cert_signatures.js]
+[test_cert_trust.js]
+[test_cert_version.js]
+[test_cert_utf8.js]
+[test_certDB_export_pkcs12.js]
+[test_certDB_export_pkcs12_with_master_password.js]
+[test_certDB_import.js]
+# nsCertificateDialogs not available in geckoview, bug 1554276
+skip-if = toolkit == 'android' && processor == 'x86_64'
+[test_certDB_import_pkcs12.js]
+[test_certDB_import_with_master_password.js]
+# nsCertificateDialogs not available in geckoview, bug 1554276
+skip-if = toolkit == 'android' && processor == 'x86_64'
+[test_constructX509FromBase64.js]
+[test_content_signing.js]
+[test_crlite_filters.js]
+tags = remote-settings psm
+[test_ct.js]
+# Requires hard-coded debug-only data
+skip-if = !debug
+run-sequentially = hardcoded ports
+[test_db_format_pref_new.js]
+# Android always has and always will use the new format, so
+# this test doesn't apply.
+skip-if = toolkit == 'android'
+[test_delegated_credentials.js]
+run-sequentially = hardcoded ports
+[test_encrypted_client_hello.js]
+run-sequentially = hardcoded ports
+[test_encrypted_client_hello_client_only.js]
+run-sequentially = hardcoded ports
+[test_der.js]
+[test_enterprise_roots.js]
+# This feature is implemented for Windows and OS X. However, we don't currently
+# have a way to test it on OS X.
+skip-if = os != 'win'
+[test_ev_certs.js]
+tags = blocklist psm
+run-sequentially = hardcoded ports
+[test_forget_about_site_security_headers.js]
+run-sequentially = hardcoded ports
+[test_hash_algorithms.js]
+[test_hash_algorithms_wrap.js]
+# bug 1124289 - run_test_in_child violates the sandbox on android
+skip-if = toolkit == 'android'
+[test_hmac.js]
+[test_intermediate_basic_usage_constraints.js]
+[test_intermediate_preloads.js]
+run-sequentially = hardcoded ports
+tags = blocklist psm remote-settings
+# Bug 1520297 - do something to handle tighter resource constraints on Android
+skip-if = toolkit == 'android'
+[test_imminent_distrust.js]
+run-sequentially = hardcoded ports
+[test_allow_all_cert_errors.js]
+run-sequentially = hardcoded ports
+[test_keysize.js]
+[test_keysize_ev.js]
+run-sequentially = hardcoded ports
+[test_local_cert.js]
+[test_logoutAndTeardown.js]
+skip-if = socketprocess_networking
+run-sequentially = hardcoded ports
+[test_missing_intermediate.js]
+run-sequentially = hardcoded ports
+[test_name_constraints.js]
+[test_nonascii_path.js]
+[test_nsCertType.js]
+run-sequentially = hardcoded ports
+[test_nsIX509Cert_utf8.js]
+[test_nsIX509CertValidity.js]
+[test_nss_shutdown.js]
+[test_ocsp_caching.js]
+run-sequentially = hardcoded ports
+[test_ocsp_enabled_pref.js]
+run-sequentially = hardcoded ports
+[test_ocsp_must_staple.js]
+run-sequentially = hardcoded ports
+[test_ocsp_private_caching.js]
+run-sequentially = hardcoded ports
+[test_ocsp_no_hsts_upgrade.js]
+run-sequentially = hardcoded ports
+[test_ocsp_required.js]
+run-sequentially = hardcoded ports
+[test_ocsp_stapling.js]
+run-sequentially = hardcoded ports
+[test_ocsp_stapling_expired.js]
+run-sequentially = hardcoded ports
+[test_ocsp_stapling_with_intermediate.js]
+run-sequentially = hardcoded ports
+[test_ocsp_timeout.js]
+run-sequentially = hardcoded ports
+[test_ocsp_url.js]
+run-sequentially = hardcoded ports
+[test_oskeystore.js]
+[test_osreauthenticator.js]
+# Reauthentication has been implemented on Windows and MacOS, so running this
+# test results in the OS popping up a dialog, which means we can't run it in
+# automation.
+skip-if = os == 'win' || os == 'mac'
+[test_password_prompt.js]
+[test_pinning.js]
+run-sequentially = hardcoded ports
+[test_sdr.js]
+[test_sdr_preexisting.js]
+# Not relevant to Android. See the comment in the test.
+skip-if = toolkit == 'android'
+[test_sdr_preexisting_with_password.js]
+# Not relevant to Android. See the comment in the test.
+skip-if = toolkit == 'android'
+[test_self_signed_certs.js]
+[test_session_resumption.js]
+skip-if = ccov && webrender && os == "win" # Bug 1585916
+run-sequentially = hardcoded ports
+[test_signed_apps.js]
+[test_ssl_status.js]
+run-sequentially = hardcoded ports
+[test_sss_enumerate.js]
+run-sequentially = hardcoded ports
+[test_sss_eviction.js]
+[test_sss_originAttributes.js]
+run-sequentially = hardcoded ports
+[test_sss_readstate.js]
+[test_sss_readstate_child.js]
+support-files = sss_readstate_child_worker.js
+# bug 1124289 - run_test_in_child violates the sandbox on android
+skip-if = toolkit == 'android'
+[test_sss_readstate_empty.js]
+[test_sss_readstate_garbage.js]
+[test_sss_readstate_huge.js]
+[test_sss_resetState.js]
+run-sequentially = hardcoded ports
+[test_sss_savestate.js]
+[test_sss_sanitizeOnShutdown.js]
+firefox-appdir = browser
+# Sanitization works differently on Android - this doesn't apply.
+# browser/modules/Sanitizer.jsm used by the test isn't available in Thunderbird.
+skip-if = toolkit == 'android' || appname == 'thunderbird'
+[test_startcom_wosign.js]
+[test_sts_fqdn.js]
+[test_sts_ipv4_ipv6.js]
+[test_sts_parser.js]
+[test_sts_preload_dynamic.js]
+[test_sts_preloadlist_perwindowpb.js]
+[test_sts_preloadlist_selfdestruct.js]
+[test_sanctions_symantec_apple_google.js]
+run-sequentially = hardcoded ports
+[test_validity.js]
+run-sequentially = hardcoded ports
+[test_x509.js]