| Index: packagerSafari.py | 
| =================================================================== | 
| --- a/packagerSafari.py | 
| +++ b/packagerSafari.py | 
| @@ -7,19 +7,16 @@ import ConfigParser | 
| import json | 
| import os | 
| import re | 
| from urlparse import urlparse | 
| from packager import readMetadata, getDefaultFileName, getBuildVersion, getTemplate, Files | 
| from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPackageFiles, defaultLocale, createScriptPage | 
| -PRIVATE_KEY_REGEXP = r'-+BEGIN PRIVATE KEY-+(.*?)-+END PRIVATE KEY-+' | 
| -CERTIFICATE_REGEXP = r'-+BEGIN CERTIFICATE-+(.*?)-+END CERTIFICATE-+' | 
| - | 
| def processFile(path, data, params): | 
| return data | 
| def createManifest(params, files): | 
| template = getTemplate('Info.plist.tmpl', autoEscape=True) | 
| metadata = params['metadata'] | 
| @@ -108,141 +105,38 @@ def fixAbsoluteUrls(files): | 
| if os.path.splitext(filename)[1].lower() == '.html': | 
| files[filename] = re.sub( | 
| r'(<[^<>]*?\b(?:href|src)\s*=\s*["\']?)\/+', | 
| r'\1' + '/'.join(['..'] * filename.count('/') + ['']), | 
| content, re.S | re.I | 
| ) | 
| -def get_certificates_and_key(keyfile): | 
| - from Crypto.PublicKey import RSA | 
| - | 
| - certs = [] | 
| - with open(keyfile, 'r') as file: | 
| - data = file.read() | 
| - match = re.search(PRIVATE_KEY_REGEXP, data, re.S) | 
| - if not match: | 
| - raise Exception('Cound not find private key in file') | 
| - key = RSA.importKey(match.group(0)) | 
| - | 
| - for match in re.finditer(CERTIFICATE_REGEXP, data, re.S): | 
| - certs.append(base64.b64decode(match.group(1))) | 
| - | 
| - return certs, key | 
| - | 
| - | 
| def _get_sequence(data): | 
| from Crypto.Util import asn1 | 
| sequence = asn1.DerSequence() | 
| sequence.decode(data) | 
| return sequence | 
| def get_developer_identifier(certs): | 
| for cert in certs: | 
| # See https://tools.ietf.org/html/rfc5280#section-4 | 
| - tbscertificate = _get_sequence(cert)[0] | 
| + tbscertificate = _get_sequence(base64.b64decode(cert))[0] | 
| subject = _get_sequence(tbscertificate)[5] | 
| # We could decode the subject but since we have to apply a regular | 
| # expression on CN entry anyway we can just skip that. | 
| m = re.search(r'Safari Developer: \((\S*?)\)', subject) | 
| if m: | 
| return m.group(1) | 
| raise Exception('No Safari developer certificate found in chain') | 
| -def sign_digest(key, digest): | 
| - from Crypto.Hash import SHA | 
| - from Crypto.Signature import PKCS1_v1_5 | 
| - | 
| - # xar already calculated the SHA1 digest so we have to fake hashing here. | 
| - class FakeHash(SHA.SHA1Hash): | 
| - def digest(self): | 
| - return digest | 
| - | 
| - return PKCS1_v1_5.new(key).sign(FakeHash()) | 
| - | 
| - | 
| -def createSignedXarArchive(outFile, files, certs, key): | 
| - import subprocess | 
| - import tempfile | 
| - import shutil | 
| - | 
| - # write files to temporary directory and create a xar archive | 
| - dirname = tempfile.mkdtemp() | 
| - try: | 
| - for filename, contents in files.iteritems(): | 
| - path = os.path.join(dirname, filename) | 
| - | 
| - try: | 
| - os.makedirs(os.path.dirname(path)) | 
| - except OSError: | 
| - pass | 
| - | 
| - with open(path, 'wb') as file: | 
| - file.write(contents) | 
| - | 
| - subprocess.check_output( | 
| - ['xar', '-czf', os.path.abspath(outFile), '--distribution'] + os.listdir(dirname), | 
| - cwd=dirname | 
| - ) | 
| - finally: | 
| - shutil.rmtree(dirname) | 
| - | 
| - certificate_filenames = [] | 
| - try: | 
| - # write each certificate in DER format to a separate | 
| - # temporary file, that they can be passed to xar | 
| - for cert in certs: | 
| - fd, filename = tempfile.mkstemp() | 
| - try: | 
| - certificate_filenames.append(filename) | 
| - os.write(fd, cert) | 
| - finally: | 
| - os.close(fd) | 
| - | 
| - # add certificates and placeholder signature | 
| - # to the xar archive, and get data to sign | 
| - fd, digest_filename = tempfile.mkstemp() | 
| - os.close(fd) | 
| - try: | 
| - subprocess.check_call( | 
| - [ | 
| - 'xar', '--sign', '-f', outFile, | 
| - '--data-to-sign', digest_filename, | 
| - '--sig-size', str(len(sign_digest(key, ''))) | 
| - ] + [ | 
| - arg for cert in certificate_filenames for arg in ('--cert-loc', cert) | 
| - ] | 
| - ) | 
| - | 
| - with open(digest_filename, 'rb') as file: | 
| - digest = file.read() | 
| - finally: | 
| - os.unlink(digest_filename) | 
| - finally: | 
| - for filename in certificate_filenames: | 
| - os.unlink(filename) | 
| - | 
| - # sign data and inject signature into xar archive | 
| - fd, signature_filename = tempfile.mkstemp() | 
| - try: | 
| - try: | 
| - os.write(fd, sign_digest(key, digest)) | 
| - finally: | 
| - os.close(fd) | 
| - | 
| - subprocess.check_call(['xar', '--inject-sig', signature_filename, '-f', outFile]) | 
| - finally: | 
| - os.unlink(signature_filename) | 
| - | 
| - | 
| def createBuild(baseDir, type, outFile=None, buildNum=None, releaseBuild=False, keyFile=None, devenv=False): | 
| metadata = readMetadata(baseDir, type) | 
| version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) | 
| if not outFile: | 
| outFile = getDefaultFileName(metadata, version, 'safariextz' if keyFile else 'zip') | 
| params = { | 
| @@ -272,26 +166,28 @@ def createBuild(baseDir, type, outFile=N | 
| if metadata.has_section('import_locales'): | 
| importGeckoLocales(params, files) | 
| if metadata.has_option('general', 'testScripts'): | 
| files['qunit/index.html'] = createScriptPage(params, 'testIndex.html.tmpl', | 
| ('general', 'testScripts')) | 
| if keyFile: | 
| - certs, key = get_certificates_and_key(keyFile) | 
| + from buildtools import xarfile | 
| + certs = xarfile.read_certificates(keyFile) | 
| params['developerIdentifier'] = get_developer_identifier(certs) | 
| files['lib/info.js'] = createInfoModule(params) | 
| files['background.html'] = createScriptPage(params, 'background.html.tmpl', | 
| ('general', 'backgroundScripts')) | 
| files['Info.plist'] = createManifest(params, files) | 
| fixAbsoluteUrls(files) | 
| dirname = metadata.get('general', 'basename') + '.safariextension' | 
| for filename in files.keys(): | 
| files[os.path.join(dirname, filename)] = files.pop(filename) | 
| if not devenv and keyFile: | 
| - createSignedXarArchive(outFile, files, certs, key) | 
| + from buildtools import xarfile | 
| + xarfile.create(outFile, files, keyFile) | 
| else: | 
| files.zip(outFile) |