diff --git a/security/manager/ssl/tests/unit/moz.build b/security/manager/ssl/tests/unit/moz.build index 85bea2f5115..2433f5b843f 100644 --- a/security/manager/ssl/tests/unit/moz.build +++ b/security/manager/ssl/tests/unit/moz.build @@ -12,6 +12,7 @@ TEST_DIRS += [ 'test_cert_version', 'test_intermediate_basic_usage_constraints', 'test_pinning_dynamic', + 'test_ocsp_url', ] if not CONFIG['MOZ_NO_SMART_CARDS']: diff --git a/security/manager/ssl/tests/unit/pycert.py b/security/manager/ssl/tests/unit/pycert.py index daf85f7d16c..0da1e71b551 100755 --- a/security/manager/ssl/tests/unit/pycert.py +++ b/security/manager/ssl/tests/unit/pycert.py @@ -26,6 +26,7 @@ extKeyUsage:[serverAuth,clientAuth,codeSigning,emailProtection nsSGC, # Netscape Server Gated Crypto OCSPSigning,timeStamping] subjectAlternativeName:[,...] +authorityInformationAccess: Where: [] indicates an optional field or component of a field @@ -120,6 +121,18 @@ def getASN1Tag(asn1Type): type from the pyasn1 package""" return asn1Type.baseTagSet.getBaseTag().asTuple()[2] +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.setComponentByName('uniformResourceIdentifier', string) + sequence = univ.Sequence() + sequence.setComponentByPosition(0, accessMethod) + sequence.setComponentByPosition(1, accessLocation) + return sequence + def stringToAlgorithmIdentifier(string): """Helper function that converts a description of an algorithm to a representation usable by the pyasn1 package""" @@ -247,6 +260,8 @@ class Certificate: self.addExtKeyUsage(value) elif extensionType == 'subjectAlternativeName': self.addSubjectAlternativeName(value) + elif extensionType == 'authorityInformationAccess': + self.addAuthorityInformationAccess(value) else: raise UnknownExtensionTypeError(extensionType) @@ -321,6 +336,12 @@ class Certificate: count += 1 self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName) + def addAuthorityInformationAccess(self, ocspURI): + sequence = univ.Sequence() + accessDescription = stringToAccessDescription(ocspURI) + sequence.setComponentByPosition(0, accessDescription) + self.addExtension(rfc2459.id_pe_authorityInfoAccess, sequence) + def getVersion(self): return rfc2459.Version(self.versionValue).subtype( explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)) diff --git a/security/manager/ssl/tests/unit/pykey.py b/security/manager/ssl/tests/unit/pykey.py index 862c67c5a8b..3d701791a35 100755 --- a/security/manager/ssl/tests/unit/pykey.py +++ b/security/manager/ssl/tests/unit/pykey.py @@ -5,8 +5,10 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. """ -Provides methods for signing data and representing a key as a -subject public key info for use with pyasn1. +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 currently very simple. If it is empty, one RSA key is used. If it consists of the string @@ -18,8 +20,10 @@ strength, signature algorithm, etc.). from pyasn1.codec.der import encoder from pyasn1.type import univ, namedtype from pyasn1_modules import rfc2459 +import base64 import binascii import rsa +import sys def byteStringToHexifiedBitString(string): """Takes a string of bytes and returns a hex string representing @@ -53,6 +57,30 @@ class RSAPublicKey(univ.Sequence): 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 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: # For reference, when encoded as a subject public key info, the # base64-encoded sha-256 hash of this key is @@ -186,6 +214,37 @@ class RSAKey: else: raise UnknownKeySpecificationError(specification) + def toDER(self): + privateKeyInfo = PrivateKeyInfo() + privateKeyInfo.setComponentByName('version', 0) + algorithmIdentifier = rfc2459.AlgorithmIdentifier() + algorithmIdentifier.setComponentByName('algorithm', rfc2459.rsaEncryption) + algorithmIdentifier.setComponentByName('parameters', univ.Null()) + privateKeyInfo.setComponentByName('privateKeyAlgorithm', algorithmIdentifier) + rsaPrivateKey = RSAPrivateKey() + rsaPrivateKey.setComponentByName('version', 0) + rsaPrivateKey.setComponentByName('modulus', self.RSA_N) + rsaPrivateKey.setComponentByName('publicExponent', self.RSA_E) + rsaPrivateKey.setComponentByName('privateExponent', self.RSA_D) + rsaPrivateKey.setComponentByName('prime1', self.RSA_P) + rsaPrivateKey.setComponentByName('prime2', self.RSA_Q) + rsaPrivateKey.setComponentByName('exponent1', self.RSA_exp1) + rsaPrivateKey.setComponentByName('exponent2', self.RSA_exp2) + rsaPrivateKey.setComponentByName('coefficient', self.RSA_coef) + rsaPrivateKeyEncoded = encoder.encode(rsaPrivateKey) + privateKeyInfo.setComponentByName('privateKey', univ.OctetString(rsaPrivateKeyEncoded)) + return encoder.encode(privateKeyInfo) + + def toPEM(self): + output = '-----BEGIN PRIVATE KEY-----' + der = self.toDER() + b64 = 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.""" @@ -208,3 +267,16 @@ class RSAKey: rsaPrivateKey = rsa.PrivateKey(self.RSA_N, self.RSA_E, self.RSA_D, self.RSA_P, self.RSA_Q) signature = rsa.sign(data, rsaPrivateKey, 'SHA-256') return byteStringToHexifiedBitString(signature) + + +# 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(RSAKey(configStream.read()).toPEM()) + +# 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 RSAKey(sys.stdin.read()).toPEM() diff --git a/security/manager/ssl/tests/unit/test_ocsp_url.js b/security/manager/ssl/tests/unit/test_ocsp_url.js index 385f568f853..b731a1bcf6b 100644 --- a/security/manager/ssl/tests/unit/test_ocsp_url.js +++ b/security/manager/ssl/tests/unit/test_ocsp_url.js @@ -25,14 +25,14 @@ function start_ocsp_responder(expectedCertNames, expectedPaths) { } function check_cert_err(cert_name, expected_error) { - let cert = constructCertFromFile("test_ocsp_url/" + cert_name + ".der"); + let cert = constructCertFromFile("test_ocsp_url/" + cert_name + ".pem"); return checkCertErrorGeneric(certdb, cert, expected_error, certificateUsageSSLServer); } function run_test() { - addCertFromFile(certdb, "test_ocsp_url/ca.der", 'CTu,CTu,CTu'); - addCertFromFile(certdb, "test_ocsp_url/int.der", ',,'); + 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); @@ -44,7 +44,7 @@ function run_test() { add_test(function() { clearOCSPCache(); let ocspResponder = failingOCSPResponder(); - check_cert_err("bad-scheme",SEC_ERROR_CERT_BAD_ACCESS_LOCATION); + check_cert_err("bad-scheme", SEC_ERROR_CERT_BAD_ACCESS_LOCATION); ocspResponder.stop(run_next_test); }); diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.der b/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.der deleted file mode 100644 index 0b26ef20e5e..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/bad-scheme.der and /dev/null differ 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 00000000000..ceabe006c62 --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/ca.der deleted file mode 100644 index ab4020577f9..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/ca.der and /dev/null differ 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 00000000000..d809dbd635d --- /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/cert9.db b/security/manager/ssl/tests/unit/test_ocsp_url/cert9.db deleted file mode 100644 index ddfac8e73aa..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/cert9.db and /dev/null differ diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/empty-port.der b/security/manager/ssl/tests/unit/test_ocsp_url/empty-port.der deleted file mode 100644 index 8293d79b2ab..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/empty-port.der and /dev/null differ diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/empty-port.pem.certspec b/security/manager/ssl/tests/unit/test_ocsp_url/empty-port.pem.certspec new file mode 100644 index 00000000000..4f3e296f9c9 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_ocsp_url/empty-port.pem.certspec @@ -0,0 +1,3 @@ +issuer:int +subject:empty-port +extension:authorityInformationAccess:http://www.example.com:/ diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.der b/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.der deleted file mode 100644 index 599c6ca54ee..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/empty-scheme-url.der and /dev/null differ 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 00000000000..e8959653f3c --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.der deleted file mode 100644 index efa716a4b2a..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/ftp-url.der and /dev/null differ 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 00000000000..9f50a7d792e --- /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/generate.py b/security/manager/ssl/tests/unit/test_ocsp_url/generate.py deleted file mode 100755 index a646bce5b6d..00000000000 --- a/security/manager/ssl/tests/unit/test_ocsp_url/generate.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/python - -import tempfile, os, sys - -libpath = os.path.abspath('../psm_common_py') -sys.path.append(libpath) -import CertUtils - -srcdir = os.getcwd() -db = tempfile.mkdtemp() - -def generate_ca_cert(db_dir, dest_dir, noise_file, name): - return CertUtils.generate_ca_cert(db_dir, dest_dir, noise_file, name, - 3, True) - -def generate_child_cert(db_dir, dest_dir, noise_file, name, ca_nick, is_ee, - ocsp_url): - return CertUtils.generate_child_cert(db_dir, dest_dir, noise_file, name, - ca_nick, 3, True, is_ee, ocsp_url) - -def generate_certs(): - [noise_file, pwd_file] = CertUtils.init_nss_db(srcdir) - generate_ca_cert(srcdir, srcdir, noise_file, 'ca') - generate_child_cert(srcdir, srcdir, noise_file, 'int', 'ca', False, '') - nick_baseurl = { 'no-path-url': "http://www.example.com:8888", - 'ftp-url': "ftp://www.example.com:8888/", - 'no-scheme-url': "www.example.com:8888/", - 'empty-scheme-url': "://www.example.com:8888/", - 'no-host-url': "http://:8888/", - 'hTTp-url': "hTTp://www.example.com:8888/hTTp-url", - 'https-url': "https://www.example.com:8888/https-url", - 'bad-scheme': "/www.example.com", - 'empty-port': "http://www.example.com:/", - 'unknown-scheme': "ttp://www.example.com", - 'negative-port': "http://www.example.com:-1", - 'no-scheme-host-port': "/" } - for nick, url in nick_baseurl.iteritems(): - generate_child_cert(srcdir, srcdir, noise_file, nick, 'int', True, url) - -generate_certs() diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.der b/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.der deleted file mode 100644 index 7e8a53155f6..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/hTTp-url.der and /dev/null differ 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 00000000000..10b1504b293 --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/https-url.der deleted file mode 100644 index 83bdd0cacfd..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/https-url.der and /dev/null differ 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 00000000000..891005bf5c2 --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/int.der deleted file mode 100644 index d3c4d73c882..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/int.der and /dev/null differ 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 00000000000..e69de29bb2d 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 00000000000..a7f6d81419d --- /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/key4.db b/security/manager/ssl/tests/unit/test_ocsp_url/key4.db deleted file mode 100644 index ab062850da8..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/key4.db and /dev/null differ 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 00000000000..5cf49a7f994 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_ocsp_url/moz.build @@ -0,0 +1,42 @@ +# -*- Mode: python; c-basic-offset: 4; 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_certificates = ( + 'bad-scheme.pem', + 'ca.pem', + 'empty-port.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', +) + +for test_certificate in test_certificates: + input_file = test_certificate + '.certspec' + GENERATED_FILES += [test_certificate] + props = GENERATED_FILES[test_certificate] + props.script = '../pycert.py' + props.inputs = [input_file, '!/config/buildid'] + TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_ocsp_url += ['!%s' % test_certificate] + +test_keys = ( + 'int.key', +) + +for test_key in test_keys: + input_file = test_key + '.keyspec' + GENERATED_FILES += [test_key] + props = GENERATED_FILES[test_key] + props.script = '../pykey.py' + props.inputs = [input_file] + TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_ocsp_url += ['!%s' % test_key] diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.der b/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.der deleted file mode 100644 index 2e334b682ea..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/negative-port.der and /dev/null differ 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 00000000000..4abc0720519 --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.der deleted file mode 100644 index 236dd8186cb..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/no-host-url.der and /dev/null differ 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 00000000000..4ac76e7eb33 --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.der deleted file mode 100644 index 2333b33a1f8..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/no-path-url.der and /dev/null differ 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 00000000000..497bb287967 --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.der deleted file mode 100644 index f6e0179a6e4..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-host-port.der and /dev/null differ 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 00000000000..42a555e411b --- /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.der b/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.der deleted file mode 100644 index 0bab0213518..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/no-scheme-url.der and /dev/null differ 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 00000000000..a82196a6d1e --- /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/pkcs11.txt b/security/manager/ssl/tests/unit/test_ocsp_url/pkcs11.txt deleted file mode 100644 index 47b2de98340..00000000000 --- a/security/manager/ssl/tests/unit/test_ocsp_url/pkcs11.txt +++ /dev/null @@ -1,5 +0,0 @@ -library= -name=NSS Internal PKCS #11 Module -parameters=configdir='sql:./security/manager/ssl/tests/unit/test_ocsp_url' certPrefix='' keyPrefix='' secmod='secmod.db' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription='' -NSS=Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512] askpw=any timeout=30}) - diff --git a/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.der b/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.der deleted file mode 100644 index 1cfa8b13199..00000000000 Binary files a/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.der and /dev/null differ 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 00000000000..a5e62b3e6e6 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_ocsp_url/unknown-scheme.pem.certspec @@ -0,0 +1,3 @@ +issuer:int +subject:unknown-scheme-url +extension:authorityInformationAccess:ttp://www.example.com diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp index 8bb54bcbce6..a19170611c1 100644 --- a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp @@ -13,6 +13,8 @@ #include "mozilla/ArrayUtils.h" +#include "base64.h" +#include "cert.h" #include "nspr.h" #include "nss.h" #include "plarenas.h" @@ -66,9 +68,8 @@ const static OCSPResponseName kOCSPResponseNameList[] = { // two years old }; - bool -stringToOCSPResponseType(const char* respText, +StringToOCSPResponseType(const char* respText, /*out*/ OCSPResponseType* OCSPType) { if (!OCSPType) { @@ -107,7 +108,201 @@ WriteResponse(const char* filename, const SECItem* item) return true; } +template +SECStatus +ReadFileToBuffer(const char* basePath, const char* filename, char (&buf)[N]) +{ + static_assert(N > 0, "input buffer too small for ReadFileToBuffer"); + if (PR_snprintf(buf, N - 1, "%s/%s", basePath, filename) == 0) { + PrintPRError("PR_snprintf failed"); + return SECFailure; + } + ScopedPRFileDesc fd(PR_OpenFile(buf, PR_RDONLY, 0)); + if (!fd) { + PrintPRError("PR_Open failed"); + return SECFailure; + } + int32_t fileSize = PR_Available(fd); + if (fileSize < 0) { + PrintPRError("PR_Available failed"); + return SECFailure; + } + if (static_cast(fileSize) > N - 1) { + PR_fprintf(PR_STDERR, "file too large - not reading\n"); + return SECFailure; + } + int32_t bytesRead = PR_Read(fd, buf, fileSize); + if (bytesRead != fileSize) { + PrintPRError("PR_Read failed"); + return SECFailure; + } + buf[bytesRead] = 0; + return SECSuccess; +} +namespace mozilla { + +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRDir, PRDir, PR_CloseDir); +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPORTString, unsigned char, PORT_Free); + +}; + +void +AddKeyFromFile(const char* basePath, const char* filename) +{ + const char* PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----"; + const char* PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----"; + + char buf[16384] = { 0 }; + SECStatus rv = ReadFileToBuffer(basePath, filename, buf); + if (rv != SECSuccess) { + return; + } + if (strncmp(buf, PRIVATE_KEY_HEADER, strlen(PRIVATE_KEY_HEADER)) != 0) { + PR_fprintf(PR_STDERR, "invalid key - not importing\n"); + return; + } + const char* bufPtr = buf + strlen(PRIVATE_KEY_HEADER); + size_t bufLen = strlen(buf); + char base64[16384] = { 0 }; + char* base64Ptr = base64; + while (bufPtr < buf + bufLen) { + if (strncmp(bufPtr, PRIVATE_KEY_FOOTER, strlen(PRIVATE_KEY_FOOTER)) == 0) { + break; + } + if (*bufPtr != '\r' && *bufPtr != '\n') { + *base64Ptr = *bufPtr; + base64Ptr++; + } + bufPtr++; + } + + unsigned int binLength; + ScopedPORTString bin(ATOB_AsciiToData(base64, &binLength)); + if (!bin || binLength == 0) { + PrintPRError("ATOB_AsciiToData failed"); + return; + } + ScopedSECItem secitem(SECITEM_AllocItem(nullptr, nullptr, binLength)); + if (!secitem) { + PrintPRError("SECITEM_AllocItem failed"); + return; + } + memcpy(secitem->data, bin, binLength); + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + if (!slot) { + PrintPRError("PK11_GetInternalKeySlot failed"); + return; + } + if (PK11_NeedUserInit(slot)) { + if (PK11_InitPin(slot, nullptr, nullptr) != SECSuccess) { + PrintPRError("PK11_InitPin failed"); + return; + } + } + SECKEYPrivateKey* privateKey; + if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, secitem, nullptr, nullptr, + true, false, KU_ALL, + &privateKey, nullptr) + != SECSuccess) { + PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed"); + return; + } + SECKEY_DestroyPrivateKey(privateKey); +} + +SECStatus +DecodeCertCallback(void* arg, SECItem** certs, int numcerts) +{ + if (numcerts != 1) { + PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); + return SECFailure; + } + SECItem* certDEROut = static_cast(arg); + return SECITEM_CopyItem(nullptr, certDEROut, *certs); +} + +void +AddCertificateFromFile(const char* basePath, const char* filename) +{ + char buf[16384] = { 0 }; + SECStatus rv = ReadFileToBuffer(basePath, filename, buf); + if (rv != SECSuccess) { + return; + } + SECItem certDER; + rv = CERT_DecodeCertPackage(buf, strlen(buf), DecodeCertCallback, &certDER); + if (rv != SECSuccess) { + PrintPRError("CERT_DecodeCertPackage failed"); + return; + } + ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), + &certDER, nullptr, false, + true)); + PORT_Free(certDER.data); + if (!cert) { + PrintPRError("CERT_NewTempCertificate failed"); + return; + } + const char* extension = strstr(filename, ".pem"); + if (!extension) { + PR_SetError(SEC_ERROR_INVALID_ARGS, 0); + return; + } + size_t nicknameLength = extension - filename; + memset(buf, 0, sizeof(buf)); + memcpy(buf, filename, nicknameLength); + buf[nicknameLength] = 0; + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + if (!slot) { + PrintPRError("PK11_GetInternalKeySlot failed"); + return; + } + rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, buf, false); + if (rv != SECSuccess) { + PrintPRError("PK11_ImportCert failed"); + } +} + +SECStatus +InitializeNSS(const char* nssCertDBDir) +{ + // First attempt to initialize NSS in read-only mode, in case the specified + // directory contains NSS DBs that are tracked by revision control. + // If this succeeds, we're done. + if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, NSS_INIT_READONLY) + == SECSuccess) { + return SECSuccess; + } + // Otherwise, create a new read-write DB and load all .pem and .key files. + if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, 0) != SECSuccess) { + PrintPRError("NSS_Initialize failed"); + return SECFailure; + } + const char* basePath = nssCertDBDir; + // 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; + } + ScopedPRDir fdDir(PR_OpenDir(basePath)); + if (!fdDir) { + PrintPRError("PR_OpenDir failed"); + return SECFailure; + } + for (PRDirEntry* dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH); dirEntry; + dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH)) { + size_t nameLength = strlen(dirEntry->name); + if (nameLength > 4) { + if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) { + AddCertificateFromFile(basePath, dirEntry->name); + } else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) { + AddKeyFromFile(basePath, dirEntry->name); + } + } + } + return SECSuccess; +} int main(int argc, char* argv[]) @@ -120,12 +315,9 @@ main(int argc, char* argv[]) argv[0]); exit(EXIT_FAILURE); } - const char* dbdir = argv[1]; - - SECStatus rv; - rv = NSS_Init(dbdir); + SECStatus rv = InitializeNSS(argv[1]); if (rv != SECSuccess) { - PrintPRError("Failed to initialize NSS"); + PR_fprintf(PR_STDERR, "Failed to initialize NSS\n"); exit(EXIT_FAILURE); } PLArenaPool* arena = PORT_NewArena(256 * argc); @@ -141,15 +333,15 @@ main(int argc, char* argv[]) const char* filename = argv[i + 3]; OCSPResponseType ORT; - if (!stringToOCSPResponseType(ocspTypeText, &ORT)) { + if (!StringToOCSPResponseType(ocspTypeText, &ORT)) { PR_fprintf(PR_STDERR, "Cannot generate OCSP response of type %s\n", ocspTypeText); exit(EXIT_FAILURE); } - ScopedCERTCertificate cert; - cert = PK11_FindCertFromNickname(certNick, nullptr); + ScopedCERTCertificate 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);