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: Created Aug. 16, 2016, 2:59 p.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
8 import math
5 import os 9 import os
6 import re 10 import re
7 import json
8 import ConfigParser
9 from urlparse import urlparse 11 from urlparse import urlparse
10 12
11 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl ate, Files 13 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl ate, Files
12 from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPa ckageFiles, defaultLocale, createScriptPage 14 from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPa ckageFiles, defaultLocale, createScriptPage
13 15
14 16
15 def processFile(path, data, params): 17 def processFile(path, data, params):
16 return data 18 return data
17 19
18 20
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 for filename, content in files.iteritems(): 105 for filename, content in files.iteritems():
104 if os.path.splitext(filename)[1].lower() == '.html': 106 if os.path.splitext(filename)[1].lower() == '.html':
105 files[filename] = re.sub( 107 files[filename] = re.sub(
106 r'(<[^<>]*?\b(?:href|src)\s*=\s*["\']?)\/+', 108 r'(<[^<>]*?\b(?:href|src)\s*=\s*["\']?)\/+',
107 r'\1' + '/'.join(['..'] * filename.count('/') + ['']), 109 r'\1' + '/'.join(['..'] * filename.count('/') + ['']),
108 content, re.S | re.I 110 content, re.S | re.I
109 ) 111 )
110 112
111 113
112 def get_certificates_and_key(keyfile): 114 def get_certificates_and_key(keyfile):
113 import M2Crypto 115 from Crypto.PublicKey import RSA
114 116
115 certs = [] 117 certs = []
116 bio = M2Crypto.BIO.openfile(keyfile) 118 with open(keyfile, 'r') as file:
119 data = file.read()
120 keydata = re.sub(r'(-+END PRIVATE KEY-+).*', r'\1', data, flags=re.S)
Sebastian Noack 2016/08/16 16:32:05 Is this even necessary? I would expect RSA.importK
Wladimir Palant 2016/08/16 17:06:04 Yes, I would expect that as well. However, I get "
Sebastian Noack 2016/08/17 08:43:07 I just noticed that this code assumes the key to b
Wladimir Palant 2016/08/17 10:03:15 True, PyCrypto seems to be angry about certificate
121 key = RSA.importKey(keydata)
117 122
118 try: 123 for match in re.finditer(r'-+BEGIN CERTIFICATE-+(.*?)-+END CERTIFICATE-+', d ata, re.S):
119 key = M2Crypto.RSA.load_key_bio(bio) 124 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 125
129 return certs, key 126 return certs, key
130 127
131 128
132 def get_developer_identifier(certs): 129 def get_developer_identifier(certs):
130 from Crypto.Util import asn1
131 def get_sequence(data):
Sebastian Noack 2016/08/16 16:32:05 Any reason this is a nested function, and not just
Wladimir Palant 2016/08/16 17:06:04 It isn't exactly a general-purpose function - mere
Sebastian Noack 2016/08/17 08:43:07 It doesn't need to be general-purpose to go on the
Wladimir Palant 2016/08/17 10:03:15 Done.
132 sequence = asn1.DerSequence()
133 sequence.decode(data)
134 return sequence
135
133 for cert in certs: 136 for cert in certs:
134 subject = cert.get_subject() 137 # See https://tools.ietf.org/html/rfc5280#section-4
135 for entry in subject.get_entries_by_nid(subject.nid['CN']): 138 tbsCertificate = get_sequence(cert)[0]
Sebastian Noack 2016/08/16 16:32:05 Nit: camel case
Wladimir Palant 2016/08/16 17:06:04 I know. But that's how this field is named in the
Sebastian Noack 2016/08/17 08:43:07 That doesn't seem a reason to me, to violate PEP-8
Wladimir Palant 2016/08/17 10:03:15 Done.
136 m = re.match(r'Safari Developer: \((.*?)\)', entry.get_data().as_tex t()) 139 subject = get_sequence(tbsCertificate)[5]
137 if m: 140
138 return m.group(1) 141 # We could decode the subject but since we have to apply a regular
142 # expression on CN entry anyway we can just skip that.
143 m = re.search(r'Safari Developer: \((\S*?)\)', subject)
144 if m:
145 return m.group(1)
139 146
140 raise Exception('No Safari developer certificate found in chain') 147 raise Exception('No Safari developer certificate found in chain')
141 148
142 149
150 def sign_digest(key, digest):
151 from Crypto.Hash import SHA
152 from Crypto.Signature import PKCS1_v1_5
153
154 # xar already calculated the SHA1 digest so we have to fake hashing here.
155 class FakeHash(SHA.SHA1Hash):
156 def digest(self):
157 return digest
158
159 return PKCS1_v1_5.new(key).sign(FakeHash())
160
161
143 def createSignedXarArchive(outFile, files, certs, key): 162 def createSignedXarArchive(outFile, files, certs, key):
144 import subprocess 163 import subprocess
145 import tempfile 164 import tempfile
146 import shutil 165 import shutil
147 import M2Crypto
148 166
149 # write files to temporary directory and create a xar archive 167 # write files to temporary directory and create a xar archive
150 dirname = tempfile.mkdtemp() 168 dirname = tempfile.mkdtemp()
151 try: 169 try:
152 for filename, contents in files.iteritems(): 170 for filename, contents in files.iteritems():
153 path = os.path.join(dirname, filename) 171 path = os.path.join(dirname, filename)
154 172
155 try: 173 try:
156 os.makedirs(os.path.dirname(path)) 174 os.makedirs(os.path.dirname(path))
157 except OSError: 175 except OSError:
(...skipping 10 matching lines...) Expand all
168 shutil.rmtree(dirname) 186 shutil.rmtree(dirname)
169 187
170 certificate_filenames = [] 188 certificate_filenames = []
171 try: 189 try:
172 # write each certificate in DER format to a separate 190 # write each certificate in DER format to a separate
173 # temporary file, that they can be passed to xar 191 # temporary file, that they can be passed to xar
174 for cert in certs: 192 for cert in certs:
175 fd, filename = tempfile.mkstemp() 193 fd, filename = tempfile.mkstemp()
176 try: 194 try:
177 certificate_filenames.append(filename) 195 certificate_filenames.append(filename)
178 os.write(fd, cert.as_der()) 196 os.write(fd, cert)
179 finally: 197 finally:
180 os.close(fd) 198 os.close(fd)
181 199
182 # add certificates and placeholder signature 200 # add certificates and placeholder signature
183 # to the xar archive, and get data to sign 201 # to the xar archive, and get data to sign
184 fd, digestinfo_filename = tempfile.mkstemp() 202 fd, digest_filename = tempfile.mkstemp()
185 os.close(fd) 203 os.close(fd)
186 try: 204 try:
187 subprocess.check_call( 205 subprocess.check_call(
188 [ 206 [
189 'xar', '--sign', '-f', outFile, 207 'xar', '--sign', '-f', outFile,
190 '--digestinfo-to-sign', digestinfo_filename, 208 '--data-to-sign', digest_filename,
Wladimir Palant 2016/08/16 15:12:08 For reference: despite the misleading name, --data
Wladimir Palant 2016/08/16 15:37:38 Actually, I finally realized what's going on there
191 '--sig-size', str(len(key.private_encrypt('', M2Crypto.RSA.p kcs1_padding))) 209 '--sig-size', str(len(sign_digest(key, '')))
192 ] + [ 210 ] + [
193 arg for cert in certificate_filenames for arg in ('--cert-lo c', cert) 211 arg for cert in certificate_filenames for arg in ('--cert-lo c', cert)
194 ] 212 ]
195 ) 213 )
196 214
197 with open(digestinfo_filename, 'rb') as file: 215 with open(digest_filename, 'rb') as file:
198 digestinfo = file.read() 216 digest = file.read()
199 finally: 217 finally:
200 os.unlink(digestinfo_filename) 218 os.unlink(digest_filename)
201 finally: 219 finally:
202 for filename in certificate_filenames: 220 for filename in certificate_filenames:
203 os.unlink(filename) 221 os.unlink(filename)
204 222
205 # sign data and inject signature into xar archive 223 # sign data and inject signature into xar archive
206 fd, signature_filename = tempfile.mkstemp() 224 fd, signature_filename = tempfile.mkstemp()
207 try: 225 try:
208 try: 226 try:
209 os.write(fd, key.private_encrypt( 227 os.write(fd, sign_digest(key, digest))
210 digestinfo,
211 M2Crypto.RSA.pkcs1_padding
212 ))
213 finally: 228 finally:
214 os.close(fd) 229 os.close(fd)
215 230
216 subprocess.check_call(['xar', '--inject-sig', signature_filename, '-f', outFile]) 231 subprocess.check_call(['xar', '--inject-sig', signature_filename, '-f', outFile])
217 finally: 232 finally:
218 os.unlink(signature_filename) 233 os.unlink(signature_filename)
219 234
220 235
221 def createBuild(baseDir, type, outFile=None, buildNum=None, releaseBuild=False, keyFile=None, devenv=False): 236 def createBuild(baseDir, type, outFile=None, buildNum=None, releaseBuild=False, keyFile=None, devenv=False):
222 metadata = readMetadata(baseDir, type) 237 metadata = readMetadata(baseDir, type)
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 fixAbsoluteUrls(files) 283 fixAbsoluteUrls(files)
269 284
270 dirname = metadata.get('general', 'basename') + '.safariextension' 285 dirname = metadata.get('general', 'basename') + '.safariextension'
271 for filename in files.keys(): 286 for filename in files.keys():
272 files[os.path.join(dirname, filename)] = files.pop(filename) 287 files[os.path.join(dirname, filename)] = files.pop(filename)
273 288
274 if not devenv and keyFile: 289 if not devenv and keyFile:
275 createSignedXarArchive(outFile, files, certs, key) 290 createSignedXarArchive(outFile, files, certs, key)
276 else: 291 else:
277 files.zip(outFile) 292 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