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

Side by Side Diff: packagerSafari.py

Issue 29349869: Issue 4339 - Replace M2Crypto by PyCrypto (Closed)
Patch Set: Addressed comments Created Aug. 17, 2016, 10:02 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « packagerChrome.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # This Source Code Form is subject to the terms of the Mozilla Public 1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 4
5 import base64
6 import ConfigParser
7 import json
5 import os 8 import os
6 import re 9 import re
7 import json
8 import ConfigParser
9 from urlparse import urlparse 10 from urlparse import urlparse
10 11
11 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl ate, Files 12 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl ate, Files
12 from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPa ckageFiles, defaultLocale, createScriptPage 13 from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPa ckageFiles, defaultLocale, createScriptPage
13 14
15 PRIVATE_KEY_REGEXP = r'-+BEGIN PRIVATE KEY-+(.*?)-+END PRIVATE KEY-+'
16 CERTIFICATE_REGEXP = r'-+BEGIN CERTIFICATE-+(.*?)-+END CERTIFICATE-+'
17
14 18
15 def processFile(path, data, params): 19 def processFile(path, data, params):
16 return data 20 return data
17 21
18 22
19 def createManifest(params, files): 23 def createManifest(params, files):
20 template = getTemplate('Info.plist.tmpl', autoEscape=True) 24 template = getTemplate('Info.plist.tmpl', autoEscape=True)
21 metadata = params['metadata'] 25 metadata = params['metadata']
22 catalog = json.loads(files['_locales/%s/messages.json' % defaultLocale]) 26 catalog = json.loads(files['_locales/%s/messages.json' % defaultLocale])
23 27
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 for filename, content in files.iteritems(): 107 for filename, content in files.iteritems():
104 if os.path.splitext(filename)[1].lower() == '.html': 108 if os.path.splitext(filename)[1].lower() == '.html':
105 files[filename] = re.sub( 109 files[filename] = re.sub(
106 r'(<[^<>]*?\b(?:href|src)\s*=\s*["\']?)\/+', 110 r'(<[^<>]*?\b(?:href|src)\s*=\s*["\']?)\/+',
107 r'\1' + '/'.join(['..'] * filename.count('/') + ['']), 111 r'\1' + '/'.join(['..'] * filename.count('/') + ['']),
108 content, re.S | re.I 112 content, re.S | re.I
109 ) 113 )
110 114
111 115
112 def get_certificates_and_key(keyfile): 116 def get_certificates_and_key(keyfile):
113 import M2Crypto 117 from Crypto.PublicKey import RSA
114 118
115 certs = [] 119 certs = []
116 bio = M2Crypto.BIO.openfile(keyfile) 120 with open(keyfile, 'r') as file:
121 data = file.read()
122 match = re.search(PRIVATE_KEY_REGEXP, data, re.S)
123 if not match:
124 raise Exception('Cound not find private key in file')
125 key = RSA.importKey(match.group(0))
117 126
118 try: 127 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.
119 key = M2Crypto.RSA.load_key_bio(bio) 128 certs.append(base64.b64decode(match.group(1)))
120 bio.reset()
121 while True:
122 try:
123 certs.append(M2Crypto.X509.load_cert_bio(bio))
124 except M2Crypto.X509.X509Error:
125 break
126 finally:
127 bio.close()
128 129
129 return certs, key 130 return certs, key
130 131
131 132
133 def _get_sequence(data):
134 from Crypto.Util import asn1
135 sequence = asn1.DerSequence()
136 sequence.decode(data)
137 return sequence
138
139
132 def get_developer_identifier(certs): 140 def get_developer_identifier(certs):
133 for cert in certs: 141 for cert in certs:
134 subject = cert.get_subject() 142 # See https://tools.ietf.org/html/rfc5280#section-4
135 for entry in subject.get_entries_by_nid(subject.nid['CN']): 143 tbscertificate = _get_sequence(cert)[0]
136 m = re.match(r'Safari Developer: \((.*?)\)', entry.get_data().as_tex t()) 144 subject = _get_sequence(tbscertificate)[5]
137 if m: 145
138 return m.group(1) 146 # We could decode the subject but since we have to apply a regular
147 # expression on CN entry anyway we can just skip that.
148 m = re.search(r'Safari Developer: \((\S*?)\)', subject)
149 if m:
150 return m.group(1)
139 151
140 raise Exception('No Safari developer certificate found in chain') 152 raise Exception('No Safari developer certificate found in chain')
141 153
142 154
155 def sign_digest(key, digest):
156 from Crypto.Hash import SHA
157 from Crypto.Signature import PKCS1_v1_5
158
159 # xar already calculated the SHA1 digest so we have to fake hashing here.
160 class FakeHash(SHA.SHA1Hash):
161 def digest(self):
162 return digest
163
164 return PKCS1_v1_5.new(key).sign(FakeHash())
165
166
143 def createSignedXarArchive(outFile, files, certs, key): 167 def createSignedXarArchive(outFile, files, certs, key):
144 import subprocess 168 import subprocess
145 import tempfile 169 import tempfile
146 import shutil 170 import shutil
147 import M2Crypto
148 171
149 # write files to temporary directory and create a xar archive 172 # write files to temporary directory and create a xar archive
150 dirname = tempfile.mkdtemp() 173 dirname = tempfile.mkdtemp()
151 try: 174 try:
152 for filename, contents in files.iteritems(): 175 for filename, contents in files.iteritems():
153 path = os.path.join(dirname, filename) 176 path = os.path.join(dirname, filename)
154 177
155 try: 178 try:
156 os.makedirs(os.path.dirname(path)) 179 os.makedirs(os.path.dirname(path))
157 except OSError: 180 except OSError:
(...skipping 10 matching lines...) Expand all
168 shutil.rmtree(dirname) 191 shutil.rmtree(dirname)
169 192
170 certificate_filenames = [] 193 certificate_filenames = []
171 try: 194 try:
172 # write each certificate in DER format to a separate 195 # write each certificate in DER format to a separate
173 # temporary file, that they can be passed to xar 196 # temporary file, that they can be passed to xar
174 for cert in certs: 197 for cert in certs:
175 fd, filename = tempfile.mkstemp() 198 fd, filename = tempfile.mkstemp()
176 try: 199 try:
177 certificate_filenames.append(filename) 200 certificate_filenames.append(filename)
178 os.write(fd, cert.as_der()) 201 os.write(fd, cert)
179 finally: 202 finally:
180 os.close(fd) 203 os.close(fd)
181 204
182 # add certificates and placeholder signature 205 # add certificates and placeholder signature
183 # to the xar archive, and get data to sign 206 # to the xar archive, and get data to sign
184 fd, digestinfo_filename = tempfile.mkstemp() 207 fd, digest_filename = tempfile.mkstemp()
185 os.close(fd) 208 os.close(fd)
186 try: 209 try:
187 subprocess.check_call( 210 subprocess.check_call(
188 [ 211 [
189 'xar', '--sign', '-f', outFile, 212 'xar', '--sign', '-f', outFile,
190 '--digestinfo-to-sign', digestinfo_filename, 213 '--data-to-sign', digest_filename,
191 '--sig-size', str(len(key.private_encrypt('', M2Crypto.RSA.p kcs1_padding))) 214 '--sig-size', str(len(sign_digest(key, '')))
192 ] + [ 215 ] + [
193 arg for cert in certificate_filenames for arg in ('--cert-lo c', cert) 216 arg for cert in certificate_filenames for arg in ('--cert-lo c', cert)
194 ] 217 ]
195 ) 218 )
196 219
197 with open(digestinfo_filename, 'rb') as file: 220 with open(digest_filename, 'rb') as file:
198 digestinfo = file.read() 221 digest = file.read()
199 finally: 222 finally:
200 os.unlink(digestinfo_filename) 223 os.unlink(digest_filename)
201 finally: 224 finally:
202 for filename in certificate_filenames: 225 for filename in certificate_filenames:
203 os.unlink(filename) 226 os.unlink(filename)
204 227
205 # sign data and inject signature into xar archive 228 # sign data and inject signature into xar archive
206 fd, signature_filename = tempfile.mkstemp() 229 fd, signature_filename = tempfile.mkstemp()
207 try: 230 try:
208 try: 231 try:
209 os.write(fd, key.private_encrypt( 232 os.write(fd, sign_digest(key, digest))
210 digestinfo,
211 M2Crypto.RSA.pkcs1_padding
212 ))
213 finally: 233 finally:
214 os.close(fd) 234 os.close(fd)
215 235
216 subprocess.check_call(['xar', '--inject-sig', signature_filename, '-f', outFile]) 236 subprocess.check_call(['xar', '--inject-sig', signature_filename, '-f', outFile])
217 finally: 237 finally:
218 os.unlink(signature_filename) 238 os.unlink(signature_filename)
219 239
220 240
221 def createBuild(baseDir, type, outFile=None, buildNum=None, releaseBuild=False, keyFile=None, devenv=False): 241 def createBuild(baseDir, type, outFile=None, buildNum=None, releaseBuild=False, keyFile=None, devenv=False):
222 metadata = readMetadata(baseDir, type) 242 metadata = readMetadata(baseDir, type)
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 fixAbsoluteUrls(files) 288 fixAbsoluteUrls(files)
269 289
270 dirname = metadata.get('general', 'basename') + '.safariextension' 290 dirname = metadata.get('general', 'basename') + '.safariextension'
271 for filename in files.keys(): 291 for filename in files.keys():
272 files[os.path.join(dirname, filename)] = files.pop(filename) 292 files[os.path.join(dirname, filename)] = files.pop(filename)
273 293
274 if not devenv and keyFile: 294 if not devenv and keyFile:
275 createSignedXarArchive(outFile, files, certs, key) 295 createSignedXarArchive(outFile, files, certs, key)
276 else: 296 else:
277 files.zip(outFile) 297 files.zip(outFile)
OLDNEW
« no previous file with comments | « packagerChrome.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld