--- a/security/apps/gen_cert_header.py
+++ b/security/apps/gen_cert_header.py
@@ -1,38 +1,42 @@
# 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/.
import binascii
+
def _file_byte_generator(filename):
- with open(filename, "rb") as f:
- contents = f.read()
+ with open(filename, "rb") as f:
+ contents = f.read()
- # Treat empty files the same as a file containing a lone 0;
- # a single-element array will fail cert verifcation just as an
- # empty array would.
- if not contents:
- return ['\0']
+ # Treat empty files the same as a file containing a lone 0;
+ # a single-element array will fail cert verifcation just as an
+ # empty array would.
+ if not contents:
+ return ['\0']
- return contents
+ return contents
+
def _create_header(array_name, cert_bytes):
- hexified = ["0x" + binascii.hexlify(byte) for byte in cert_bytes]
- substs = { 'array_name': array_name, 'bytes': ', '.join(hexified) }
- return "const uint8_t %(array_name)s[] = {\n%(bytes)s\n};\n" % substs
+ hexified = ["0x" + binascii.hexlify(byte) for byte in cert_bytes]
+ substs = {'array_name': array_name, 'bytes': ', '.join(hexified)}
+ return "const uint8_t %(array_name)s[] = {\n%(bytes)s\n};\n" % substs
+
# Create functions named the same as the data arrays that we're going to
# write to the headers, so we don't have to duplicate the names like so:
#
# def arrayName(header, cert_filename):
# header.write(_create_header("arrayName", cert_filename))
array_names = [
- 'xpcshellRoot',
- 'addonsPublicRoot',
- 'addonsStageRoot',
- 'privilegedPackageRoot',
+ 'xpcshellRoot',
+ 'addonsPublicRoot',
+ 'addonsStageRoot',
+ 'privilegedPackageRoot',
]
for n in array_names:
- # Make sure the lambda captures the right string.
- globals()[n] = lambda header, cert_filename, name=n: header.write(_create_header(name, _file_byte_generator(cert_filename)))
+ # Make sure the lambda captures the right string.
+ globals()[n] = lambda header, cert_filename, name=n: header.write(
+ _create_header(name, _file_byte_generator(cert_filename)))
--- a/security/generate_certdata.py
+++ b/security/generate_certdata.py
@@ -2,14 +2,15 @@
#
# This exists to paper over differences between gyp's `action` definitions
# and moz.build `GENERATED_FILES` semantics.
import buildconfig
import os
import subprocess
+
def main(output, *inputs):
- env=dict(os.environ)
+ env = dict(os.environ)
env['PERL'] = str(buildconfig.substs['PERL'])
output.write(subprocess.check_output([buildconfig.substs['PYTHON'],
- inputs[0], inputs[2]], env=env))
+ inputs[0], inputs[2]], env=env))
return None
--- a/security/manager/ssl/tests/unit/pycert.py
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -100,16 +100,17 @@ 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))
@@ -217,28 +218,30 @@ class InvalidSerialNumber(Error):
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
@@ -286,16 +289,17 @@ def stringToDN(string, tag=None):
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
@@ -325,39 +329,42 @@ def stringToAlgorithmIdentifiers(string)
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 ''.join(chr(b) for b in 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.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'
--- a/security/manager/ssl/tests/unit/pycms.py
+++ b/security/manager/ssl/tests/unit/pycms.py
@@ -31,16 +31,17 @@ from pyasn1.codec.der import encoder
from pyasn1.type import tag, univ
from pyasn1_modules import rfc2315, rfc2459
import StringIO
import base64
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."""
@@ -128,17 +129,17 @@ class CMS(object):
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))
+ 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
@@ -160,29 +161,29 @@ class CMS(object):
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]
+ 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)
+ pykey.HASH_SHA1, self.sha1)
if len(self.sha256) > 0:
signerInfos[len(signerInfos)] = self.buildSignerInfo(certificate,
- pykey.HASH_SHA256, self.sha256)
+ 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)
--- a/security/manager/ssl/tests/unit/pyct.py
+++ b/security/manager/ssl/tests/unit/pyct.py
@@ -13,18 +13,20 @@ sign. Currently only supports precert_en
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):
--- a/security/manager/ssl/tests/unit/pykey.py
+++ b/security/manager/ssl/tests/unit/pykey.py
@@ -43,25 +43,28 @@ 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'
+
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" % binascii.hexlify(string)
+
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))
@@ -79,16 +82,17 @@ class UnknownHashAlgorithmError(UnknownB
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)
@@ -619,56 +623,59 @@ secp521r1 = univ.ObjectIdentifier('1.3.1
# E: y^2 = x^3 + ax + b (a in this case is 0)
# The fourth and fifth numbers constitute the base point G.
secp256k1Params = (long('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16),
long('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16),
7,
long('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 16),
long('483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', 16))
+
def longToEvenLengthHexString(val):
h = format(val, 'x')
if not len(h) % 2 == 0:
h = '0' + h
return h
+
def notRandom(n):
return n * '\x04'
+
class ECCKey(object):
secp256k1Encoded = str('08fd87b04fba98090100004035ee7c7289d8fef7a8'
- '6afe5da66d8bc2ebb6a8543fd2fead089f45ce7acd0fa64382a9500c41dad'
- '770ffd4b511bf4b492eb1238800c32c4f76c73a3f3294e7c5002067cebc20'
- '8a5fa3df16ec2bb34acc59a42ab4abb0538575ca99b92b6a2149a04f')
+ '6afe5da66d8bc2ebb6a8543fd2fead089f45ce7acd0fa64382a9500c41dad'
+ '770ffd4b511bf4b492eb1238800c32c4f76c73a3f3294e7c5002067cebc20'
+ '8a5fa3df16ec2bb34acc59a42ab4abb0538575ca99b92b6a2149a04f')
secp224r1Encoded = str('0ee5587c4d18526f00e00038668d72cca6fd6a1b35'
- '57b5366104d84408ecb637f08e8c86bbff82cc00e88f0066d7af63c3298ba'
- '377348a1202b03b37fd6b1ff415aa311e001c04389459926c3296c242b83e'
- '10a6cd2011c8fe2dae1b772ea5b21067')
+ '57b5366104d84408ecb637f08e8c86bbff82cc00e88f0066d7af63c3298ba'
+ '377348a1202b03b37fd6b1ff415aa311e001c04389459926c3296c242b83e'
+ '10a6cd2011c8fe2dae1b772ea5b21067')
secp256r1Encoded = str('cb872ac99cd31827010000404fbfbbbb61e0f8f9b1'
- 'a60a59ac8704e2ec050b423e3cf72e923f2c4f794b455c2a69d233456c36c'
- '4119d0706e00eedc8d19390d7991b7b2d07a304eaa04aa6c000202191403d'
- '5710bf15a265818cd42ed6fedf09add92d78b18e7a1e9feb95524702')
+ 'a60a59ac8704e2ec050b423e3cf72e923f2c4f794b455c2a69d233456c36c'
+ '4119d0706e00eedc8d19390d7991b7b2d07a304eaa04aa6c000202191403d'
+ '5710bf15a265818cd42ed6fedf09add92d78b18e7a1e9feb95524702')
secp384r1Encoded = str('d3103f5ac81741e801800060a1687243362b5c7b18'
- '89f379154615a1c73fb48dee863e022915db608e252de4b7132da8ce98e83'
- '1534e6a9c0c0b09c8d639ade83206e5ba813473a11fa330e05da8c96e4383'
- 'fe27873da97103be2888cff002f05af71a1fddcc8374aa6ea9ce0030035c7'
- 'a1b10d9fafe837b64ad92f22f5ced0789186538669b5c6d872cec3d926122'
- 'b393772b57602ff31365efe1393246')
+ '89f379154615a1c73fb48dee863e022915db608e252de4b7132da8ce98e83'
+ '1534e6a9c0c0b09c8d639ade83206e5ba813473a11fa330e05da8c96e4383'
+ 'fe27873da97103be2888cff002f05af71a1fddcc8374aa6ea9ce0030035c7'
+ 'a1b10d9fafe837b64ad92f22f5ced0789186538669b5c6d872cec3d926122'
+ 'b393772b57602ff31365efe1393246')
secp521r1Encoded = str('77f4b0ac81948ddc02090084014cdc9cacc4794109'
- '6bc9cc66752ec27f597734fa66c62b792f88c519d6d37f0d16ea1c483a182'
- '7a010b9128e3a08070ca33ef5f57835b7c1ba251f6cc3521dc42b01065345'
- '1981b445d343eed3782a35d6cff0ff484f5a883d209f1b9042b726703568b'
- '2f326e18b833bdd8aa0734392bcd19501e10d698a79f53e11e0a22bdd2aad'
- '900042014f3284fa698dd9fe1118dd331851cdfaac5a3829278eb8994839d'
- 'e9471c940b858c69d2d05e8c01788a7d0b6e235aa5e783fc1bee807dcc386'
- '5f920e12cf8f2d29')
+ '6bc9cc66752ec27f597734fa66c62b792f88c519d6d37f0d16ea1c483a182'
+ '7a010b9128e3a08070ca33ef5f57835b7c1ba251f6cc3521dc42b01065345'
+ '1981b445d343eed3782a35d6cff0ff484f5a883d209f1b9042b726703568b'
+ '2f326e18b833bdd8aa0734392bcd19501e10d698a79f53e11e0a22bdd2aad'
+ '900042014f3284fa698dd9fe1118dd331851cdfaac5a3829278eb8994839d'
+ 'e9471c940b858c69d2d05e8c01788a7d0b6e235aa5e783fc1bee807dcc386'
+ '5f920e12cf8f2d29')
def __init__(self, specification=None):
if specification == 'secp256k1':
self.key = Key.Key.decode(binascii.unhexlify(self.secp256k1Encoded))
self.keyOID = secp256k1
elif specification == 'secp224r1':
self.key = Key.Key.decode(binascii.unhexlify(self.secp224r1Encoded))
self.keyOID = secp224r1
@@ -743,16 +750,18 @@ def keyFromSpecification(specification):
if specification.startswith('secp'):
return ECCKey(specification)
else:
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())
# When run as a standalone program, this will read a specification from
# stdin and output the certificate as PEM to stdout.
if __name__ == '__main__':
--- a/security/manager/ssl/tests/unit/sign_app.py
+++ b/security/manager/ssl/tests/unit/sign_app.py
@@ -25,16 +25,17 @@ 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
elif algorithm == ES384:
return pykey.HASH_SHA384
elif algorithm == ES512:
@@ -42,16 +43,18 @@ def coseAlgorithmToPykeyHash(algorithm):
else:
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
@@ -73,16 +76,18 @@ def coseSignature(payload, algorithm, si
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
"""
@@ -91,41 +96,44 @@ def coseSig(payload, intermediates, sign
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())
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
@@ -136,37 +144,40 @@ def getCert(subject, keyName, issuerName
'extension:keyUsage:cRLSign,keyCertSign'
if issuerKey:
certSpecification += '\nissuerKey:%s' % issuerKey
certSpecificationStream = StringIO.StringIO()
print >>certSpecificationStream, certSpecification
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
+ 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')
+ 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):
"""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, and whether the pkcs#7 signer info should be kept empty,
@@ -193,18 +204,18 @@ def signZip(appDirectory, outputFile, is
intermediates = []
coseIssuerName = issuerName
if rootName:
coseIssuerName = 'xpcshell signed app test issuer'
intermediate = getCert(coseIssuerName, 'default', rootName, False)
intermediate = intermediate.toDER()
intermediates.append(intermediate)
signatures = map(lambda coseAlgorithm:
- coseAlgorithmToSignatureParams(coseAlgorithm, coseIssuerName),
- coseAlgorithms)
+ coseAlgorithmToSignatureParams(coseAlgorithm, coseIssuerName),
+ 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'
@@ -225,16 +236,17 @@ def signZip(appDirectory, outputFile, is
print >>cmsSpecificationStream, cmsSpecification
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."""
@@ -259,16 +271,17 @@ class UnknownCOSEAlgorithmError(Error):
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')
--- a/security/manager/ssl/tests/unit/test_cert_version/generate.py
+++ b/security/manager/ssl/tests/unit/test_cert_version/generate.py
@@ -38,16 +38,17 @@ versions = {
}
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:
--- a/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py
+++ b/security/manager/tools/crtshToIdentifyingStruct/crtshToIdentifyingStruct.py
@@ -25,35 +25,39 @@ from pyasn1_modules import rfc5280
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.x509.oid import NameOID
assert sys.version_info >= (3, 2), "Requires Python 3.2 or later"
+
def hex_string_for_struct(bytes):
return ["0x{:02X}".format(x) for x in bytes]
+
def hex_string_human_readable(bytes):
return ["{:02X}".format(x) for x in bytes]
+
def nameOIDtoString(oid):
if oid == NameOID.COUNTRY_NAME:
return "C"
if oid == NameOID.COMMON_NAME:
return "CN"
if oid == NameOID.LOCALITY_NAME:
return "L"
if oid == NameOID.ORGANIZATION_NAME:
return "O"
if oid == NameOID.ORGANIZATIONAL_UNIT_NAME:
return "OU"
raise Exception("Unknown OID: {}".format(oid))
+
def print_block(pemData, identifierType="DN", crtshId=None):
substrate = pem.readPemFromFile(io.StringIO(pemData.decode("utf-8")))
cert, rest = decoder.decode(substrate, asn1Spec=rfc5280.Certificate())
octets = None
if identifierType == "DN":
der_subject = encoder.encode(cert['tbsCertificate']['subject'])
octets = hex_string_for_struct(der_subject)
--- a/security/manager/tools/getCTKnownLogs.py
+++ b/security/manager/tools/getCTKnownLogs.py
@@ -183,65 +183,67 @@ def generate_cpp_header_file(json_data,
log_info_initializers = get_log_info_structs(json_data)
operator_info_initializers = get_log_operator_structs(json_data)
out_file.write(Template(OUTPUT_TEMPLATE).substitute(
prog=os.path.basename(sys.argv[0]),
include_guard=include_guard,
logs="\n".join(log_info_initializers),
operators="\n".join(operator_info_initializers)))
+
def patch_in_test_logs(json_data):
""" Insert Mozilla-specific test log data. """
max_id = 0
for operator in json_data["operators"]:
if operator["id"] > max_id:
max_id = operator["id"]
mozilla_test_operator_1 = {"name": "Mozilla Test Org 1", "id": max_id + 1,
- "test_only": True}
+ "test_only": True}
mozilla_test_operator_2 = {"name": "Mozilla Test Org 2", "id": max_id + 2,
- "test_only": True}
+ "test_only": True}
json_data["operators"].append(mozilla_test_operator_1)
json_data["operators"].append(mozilla_test_operator_2)
# The easiest way to get this is
# `openssl x509 -noout -pubkey -in <path/to/default-ee.pem>`
mozilla_rsa_log_1 = {"description": "Mozilla Test RSA Log 1",
- "key": """
+ "key": """
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2
ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdF
h/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6n
cOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAv
OnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2nj
tIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXt
jQIDAQAB
""",
- "operated_by": [max_id + 1]}
+ "operated_by": [max_id + 1]}
# Similarly,
# `openssl x509 -noout -pubkey -in <path/to/other-test-ca.pem>`
mozilla_rsa_log_2 = {"description": "Mozilla Test RSA Log 2",
- "key": """
+ "key": """
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXXGUmYJn3cIKmeR8bh2
w39c5TiwbErNIrHL1G+mWtoq3UHIwkmKxKOzwfYUh/QbaYlBvYClHDwSAkTFhKTE
SDMF5ROMAQbPCL6ahidguuai6PNvI8XZgxO53683g0XazlHU1tzSpss8xwbrzTBw
7JjM5AqlkdcpWn9xxb5maR0rLf7ISURZC8Wj6kn9k7HXU0BfF3N2mZWGZiVHl+1C
aQiICBFCIGmYikP+5Izmh4HdIramnNKDdRMfkysSjOKG+n0lHAYq0n7wFvGHzdVO
gys1uJMPdLqQqovHYWckKrH9bWIUDRjEwLjGj8N0hFcyStfehuZVLx0eGR1xIWjT
uwIDAQAB
""",
- "operated_by": [max_id + 2]}
+ "operated_by": [max_id + 2]}
# `openssl x509 -noout -pubkey -in <path/to/root_secp256r1_256.pem`
mozilla_ec_log = {"description": "Mozilla Test EC Log",
- "key": """
+ "key": """
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET7+7u2Hg+PmxpgpZrIcE4uwFC0I+
PPcukj8sT3lLRVwqadIzRWw2xBGdBwbgDu3I0ZOQ15kbey0HowTqoEqmwA==
""",
- "operated_by": [max_id + 1]}
+ "operated_by": [max_id + 1]}
json_data["logs"].append(mozilla_rsa_log_1)
json_data["logs"].append(mozilla_rsa_log_2)
json_data["logs"].append(mozilla_ec_log)
+
def run(args):
"""
Load the input JSON file and generate the C++ header according to the
command line arguments.
"""
if args.file:
print("Reading file: ", args.file)
with open(args.file, "rb") as json_file:
--- a/security/pkix/tools/DottedOIDToCode.py
+++ b/security/pkix/tools/DottedOIDToCode.py
@@ -19,16 +19,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
import argparse
import itertools
import sys
+
def base128(value):
"""
Given an integral value, returns an array of the base-128 representation
of that value, with all but the last byte having the high bit set as
required by the DER rules for the nodes of an OID after the first two
bytes.
>>> base128(1)
@@ -45,16 +46,17 @@ def base128(value):
value /= 0x80
while value != 0:
result = [0x80 | (value % 0x80)] + result
value /= 0x80
return result
+
def dottedOIDToEncodedArray(dottedOID):
"""
Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and
returns an array that contains the DER encoding of its value, without
the tag and length (e.g. [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04]).
"""
nodes = [int(x) for x in dottedOID.strip().split(".")]
if len(nodes) < 2:
@@ -63,16 +65,17 @@ def dottedOIDToEncodedArray(dottedOID):
raise ValueError("The first node of an OID must be 0, 1, or 2.")
if not (0 <= nodes[1] <= 39):
# XXX: Does this restriction apply when the first part is 2?
raise ValueError("The second node of an OID must be 0-39.")
firstByte = (40 * nodes[0]) + nodes[1]
restBase128 = [base128(x) for x in nodes[2:]]
return [firstByte] + list(itertools.chain.from_iterable(restBase128))
+
def dottedOIDToCArray(dottedOID, mode):
"""
Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and
returns a string that contains the hex encoding of the OID in C++ literal
notation, e.g. '0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04'.
"""
bytes = dottedOIDToEncodedArray(dottedOID)
@@ -81,27 +84,29 @@ def dottedOIDToCArray(dottedOID, mode):
if mode == "alg":
# Wrap the DER-encoded OID in a SEQUENCE to create an
# AlgorithmIdentifier with no parameters.
bytes = [0x30, len(bytes)] + bytes
return ", ".join(["0x%.2x" % b for b in bytes])
+
def specNameToCName(specName):
"""
Given an string containing an ASN.1 name, returns a string that is a valid
C++ identifier that is as similar to that name as possible. Since most
ASN.1 identifiers used in PKIX specifications are legal C++ names except
for containing hyphens, this function just converts the hyphens to
underscores. This may need to be improved in the future if we encounter
names with other funny characters.
"""
return specName.replace("-", "_")
+
def toCode(programName, specName, dottedOID, mode):
"""
Given an ASN.1 name and a string containing the dotted representation of an
OID, returns a string that contains a C++ declaration for a named constant
that contains that OID value. If mode is "value" then only the value of
the OID (without the tag or length) will be included in the output. If mode
is "tlv" then the value will be prefixed with the tag and length. If mode
is "alg" then the value will be a complete der-encoded AlgorithmIdentifier
@@ -175,21 +180,22 @@ def toCode(programName, specName, dotted
varName = varName
return (" // python %s %s %s\n" +
" static const uint8_t %s[] = {\n" +
" %s\n" +
" };\n") % (programNameWithOptions, specName, dottedOID, varName,
dottedOIDToCArray(dottedOID, mode))
+
if __name__ == "__main__":
parser = argparse.ArgumentParser(
- description="Generate code snippets to handle OIDs in C++",
- epilog="example: python %s ecdsa-with-SHA1 1.2.840.10045.4.1"
- % sys.argv[0])
+ description="Generate code snippets to handle OIDs in C++",
+ epilog="example: python %s ecdsa-with-SHA1 1.2.840.10045.4.1"
+ % sys.argv[0])
group = parser.add_mutually_exclusive_group()
group.add_argument("--tlv", action='store_true',
help="Wrap the encoded OID value with the tag and length")
group.add_argument("--alg", action='store_true',
help="Wrap the encoded OID value in an encoded SignatureAlgorithm")
group.add_argument("--prefixdefine", action='store_true',
help="generate a OID prefix #define")
parser.add_argument("name",