Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: packagerSafari.py

Issue 29349869: Issue 4339 - Replace M2Crypto by PyCrypto (Closed)
Patch Set: Addressed comments Created Aug. 17, 2016, 10:02 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « packagerChrome.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: packagerSafari.py
===================================================================
--- a/packagerSafari.py
+++ b/packagerSafari.py
@@ -1,21 +1,25 @@
# 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 base64
+import ConfigParser
+import json
import os
import re
-import json
-import ConfigParser
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']
@@ -105,51 +109,70 @@ def fixAbsoluteUrls(files):
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):
- import M2Crypto
+ from Crypto.PublicKey import RSA
certs = []
- bio = M2Crypto.BIO.openfile(keyfile)
+ 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))
- try:
- key = M2Crypto.RSA.load_key_bio(bio)
- bio.reset()
- while True:
- try:
- certs.append(M2Crypto.X509.load_cert_bio(bio))
- except M2Crypto.X509.X509Error:
- break
- finally:
- bio.close()
+ for match in re.finditer(CERTIFICATE_REGEXP, data, re.S):
Sebastian Noack 2016/08/17 12:19:24 I think it might make sense to unify the logic her
Wladimir Palant 2016/08/17 14:10:03 Done.
+ 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:
- subject = cert.get_subject()
- for entry in subject.get_entries_by_nid(subject.nid['CN']):
- m = re.match(r'Safari Developer: \((.*?)\)', entry.get_data().as_text())
- if m:
- return m.group(1)
+ # See https://tools.ietf.org/html/rfc5280#section-4
+ tbscertificate = _get_sequence(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
- import M2Crypto
# 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:
@@ -170,51 +193,48 @@ def createSignedXarArchive(outFile, file
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.as_der())
+ os.write(fd, cert)
finally:
os.close(fd)
# add certificates and placeholder signature
# to the xar archive, and get data to sign
- fd, digestinfo_filename = tempfile.mkstemp()
+ fd, digest_filename = tempfile.mkstemp()
os.close(fd)
try:
subprocess.check_call(
[
'xar', '--sign', '-f', outFile,
- '--digestinfo-to-sign', digestinfo_filename,
- '--sig-size', str(len(key.private_encrypt('', M2Crypto.RSA.pkcs1_padding)))
+ '--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(digestinfo_filename, 'rb') as file:
- digestinfo = file.read()
+ with open(digest_filename, 'rb') as file:
+ digest = file.read()
finally:
- os.unlink(digestinfo_filename)
+ 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, key.private_encrypt(
- digestinfo,
- M2Crypto.RSA.pkcs1_padding
- ))
+ 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)
« no previous file with comments | « packagerChrome.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld