def signZip(appDirectory, outputFile, issuerName, manifestHashes, signatureHashes, pkcs7Hashes, doSign): """Given a directory containing the files to package up, an output filename to write to, the name of the issuer of the signing certificate, 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, and whether or not to actually sign the resulting package, packages up the files in the directory and creates the output as appropriate.""" mfEntries = [] with zipfile.ZipFile(outputFile, 'w') as outZip: for (fullPath, internalPath) in walkDirectory(appDirectory): with open(fullPath) as inputFile: contents = inputFile.read() outZip.writestr(internalPath, contents) # Add the entry to the manifest we're building mfEntry = 'Name: %s\n' % internalPath for (hashFunc, name) in manifestHashes: base64hash = b64encode(hashFunc(contents).digest()) mfEntry += '%s-Digest: %s\n' % (name, base64hash) mfEntries.append(mfEntry) # Just exit early if we're not actually signing. if not doSign: return mfContents = 'Manifest-Version: 1.0\n\n' + '\n'.join(mfEntries) sfContents = 'Signature-Version: 1.0\n' for (hashFunc, name) in signatureHashes: base64hash = b64encode(hashFunc(mfContents).digest()) sfContents += '%s-Digest-Manifest: %s\n' % (name, base64hash) cmsSpecification = '' for name in pkcs7Hashes: hashFunc, _ = hashNameToFunctionAndIdentifier(name) cmsSpecification += '%s:%s\n' % (name, hashFunc(sfContents).hexdigest()) cmsSpecification += 'signer:\n' + \ 'issuer:%s\n' % issuerName + \ 'subject:xpcshell signed app test signer\n' + \ 'extension:keyUsage:digitalSignature' cmsSpecificationStream = StringIO.StringIO() 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)
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)
def signZip(appDirectory, outputFile, issuerName, manifestHashes, signatureHashes, pkcs7Hashes, doSign, coseAlgorithms): """Given a directory containing the files to package up, an output filename to write to, the name of the issuer of the signing certificate, 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, whether or not to actually sign the resulting package, and a list of COSE signature algorithms to include, packages up the files in the directory and creates the output as appropriate.""" # This ensures each manifest file starts with the magic string and # then a blank line. mfEntries = ['Manifest-Version: 1.0', ''] with zipfile.ZipFile(outputFile, 'w') as outZip: for (fullPath, internalPath) in walkDirectory(appDirectory): with open(fullPath) as inputFile: contents = inputFile.read() outZip.writestr(internalPath, contents) # Add the entry to the manifest we're building addManifestEntry(internalPath, manifestHashes, contents, mfEntries) # Just exit early if we're not actually signing. if not doSign: return if len(coseAlgorithms) > 0: coseManifest = '\n'.join(mfEntries) outZip.writestr('META-INF/cose.manifest', coseManifest) addManifestEntry('META-INF/cose.manifest', manifestHashes, coseManifest, mfEntries) signatures = map( lambda coseAlgorithm: coseAlgorithmToSignatureParams( coseAlgorithm, issuerName), coseAlgorithms) coseSignatureBytes = coseSig(coseManifest, [], signatures) outZip.writestr('META-INF/cose.sig', coseSignatureBytes) addManifestEntry('META-INF/cose.sig', manifestHashes, coseSignatureBytes, mfEntries) mfContents = '\n'.join(mfEntries) sfContents = 'Signature-Version: 1.0\n' for (hashFunc, name) in signatureHashes: base64hash = b64encode(hashFunc(mfContents).digest()) sfContents += '%s-Digest-Manifest: %s\n' % (name, base64hash) cmsSpecification = '' for name in pkcs7Hashes: hashFunc, _ = hashNameToFunctionAndIdentifier(name) cmsSpecification += '%s:%s\n' % (name, hashFunc(sfContents).hexdigest()) cmsSpecification += 'signer:\n' + \ 'issuer:%s\n' % issuerName + \ 'subject:xpcshell signed app test signer\n' + \ 'extension:keyUsage:digitalSignature' cmsSpecificationStream = StringIO.StringIO() 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)