| Left: | ||
| Right: |
| LEFT | RIGHT |
|---|---|
| 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 | 5 import base64 |
| 6 import ConfigParser | 6 import ConfigParser |
| 7 import json | 7 import json |
| 8 import math | |
| 9 import os | 8 import os |
| 10 import re | 9 import re |
| 11 from urlparse import urlparse | 10 from urlparse import urlparse |
| 12 | 11 |
| 13 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl ate, Files | 12 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl ate, Files |
| 14 from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPa ckageFiles, defaultLocale, createScriptPage | 13 from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPa ckageFiles, defaultLocale, createScriptPage |
| 15 | 14 |
| 16 | 15 |
| 17 def processFile(path, data, params): | 16 def processFile(path, data, params): |
| 18 return data | 17 return data |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 107 files[filename] = re.sub( | 106 files[filename] = re.sub( |
| 108 r'(<[^<>]*?\b(?:href|src)\s*=\s*["\']?)\/+', | 107 r'(<[^<>]*?\b(?:href|src)\s*=\s*["\']?)\/+', |
| 109 r'\1' + '/'.join(['..'] * filename.count('/') + ['']), | 108 r'\1' + '/'.join(['..'] * filename.count('/') + ['']), |
| 110 content, re.S | re.I | 109 content, re.S | re.I |
| 111 ) | 110 ) |
| 112 | 111 |
| 113 | 112 |
| 114 def get_certificates_and_key(keyfile): | 113 def get_certificates_and_key(keyfile): |
| 115 from Crypto.PublicKey import RSA | 114 from Crypto.PublicKey import RSA |
| 116 | 115 |
| 117 certs = [] | |
| 118 with open(keyfile, 'r') as file: | 116 with open(keyfile, 'r') as file: |
| 119 data = file.read() | 117 data = file.read() |
| 120 keydata = re.sub(r'(-+END PRIVATE KEY-+).*', r'\1', data, flags=re.S) | 118 |
|
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) | 119 certificates = [] |
| 122 | 120 key = None |
| 123 for match in re.finditer(r'-+BEGIN CERTIFICATE-+(.*?)-+END CERTIFICATE-+', d ata, re.S): | 121 for match in re.finditer(r'-+BEGIN (.*?)-+(.*?)-+END \1-+', data, re.S): |
| 124 certs.append(base64.b64decode(match.group(1))) | 122 section = match.group(1) |
| 125 | 123 if section == 'CERTIFICATE': |
| 126 return certs, key | 124 certificates.append(base64.b64decode(match.group(2))) |
| 125 elif section == 'PRIVATE KEY': | |
| 126 key = RSA.importKey(match.group(0)) | |
| 127 if not key: | |
| 128 raise Exception('Could not find private key in file') | |
| 129 | |
| 130 return certificates, key | |
| 131 | |
| 132 | |
| 133 def _get_sequence(data): | |
| 134 from Crypto.Util import asn1 | |
| 135 sequence = asn1.DerSequence() | |
| 136 sequence.decode(data) | |
| 137 return sequence | |
| 127 | 138 |
| 128 | 139 |
| 129 def get_developer_identifier(certs): | 140 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 | |
| 136 for cert in certs: | 141 for cert in certs: |
| 137 # See https://tools.ietf.org/html/rfc5280#section-4 | 142 # See https://tools.ietf.org/html/rfc5280#section-4 |
| 138 tbsCertificate = get_sequence(cert)[0] | 143 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.
| |
| 139 subject = get_sequence(tbsCertificate)[5] | 144 subject = _get_sequence(tbscertificate)[5] |
| 140 | 145 |
| 141 # We could decode the subject but since we have to apply a regular | 146 # We could decode the subject but since we have to apply a regular |
| 142 # expression on CN entry anyway we can just skip that. | 147 # expression on CN entry anyway we can just skip that. |
| 143 m = re.search(r'Safari Developer: \((\S*?)\)', subject) | 148 m = re.search(r'Safari Developer: \((\S*?)\)', subject) |
| 144 if m: | 149 if m: |
| 145 return m.group(1) | 150 return m.group(1) |
| 146 | 151 |
| 147 raise Exception('No Safari developer certificate found in chain') | 152 raise Exception('No Safari developer certificate found in chain') |
| 148 | 153 |
| 149 | 154 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 198 os.close(fd) | 203 os.close(fd) |
| 199 | 204 |
| 200 # add certificates and placeholder signature | 205 # add certificates and placeholder signature |
| 201 # to the xar archive, and get data to sign | 206 # to the xar archive, and get data to sign |
| 202 fd, digest_filename = tempfile.mkstemp() | 207 fd, digest_filename = tempfile.mkstemp() |
| 203 os.close(fd) | 208 os.close(fd) |
| 204 try: | 209 try: |
| 205 subprocess.check_call( | 210 subprocess.check_call( |
| 206 [ | 211 [ |
| 207 'xar', '--sign', '-f', outFile, | 212 'xar', '--sign', '-f', outFile, |
| 208 '--data-to-sign', digest_filename, | 213 '--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
| |
| 209 '--sig-size', str(len(sign_digest(key, ''))) | 214 '--sig-size', str(len(sign_digest(key, ''))) |
| 210 ] + [ | 215 ] + [ |
| 211 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) |
| 212 ] | 217 ] |
| 213 ) | 218 ) |
| 214 | 219 |
| 215 with open(digest_filename, 'rb') as file: | 220 with open(digest_filename, 'rb') as file: |
| 216 digest = file.read() | 221 digest = file.read() |
| 217 finally: | 222 finally: |
| 218 os.unlink(digest_filename) | 223 os.unlink(digest_filename) |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 fixAbsoluteUrls(files) | 288 fixAbsoluteUrls(files) |
| 284 | 289 |
| 285 dirname = metadata.get('general', 'basename') + '.safariextension' | 290 dirname = metadata.get('general', 'basename') + '.safariextension' |
| 286 for filename in files.keys(): | 291 for filename in files.keys(): |
| 287 files[os.path.join(dirname, filename)] = files.pop(filename) | 292 files[os.path.join(dirname, filename)] = files.pop(filename) |
| 288 | 293 |
| 289 if not devenv and keyFile: | 294 if not devenv and keyFile: |
| 290 createSignedXarArchive(outFile, files, certs, key) | 295 createSignedXarArchive(outFile, files, certs, key) |
| 291 else: | 296 else: |
| 292 files.zip(outFile) | 297 files.zip(outFile) |
| LEFT | RIGHT |