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

Side by Side Diff: xarfile.py

Issue 29349885: Issue 4340 - Drop dependency on external xar tool (Closed)
Patch Set: Created Aug. 16, 2016, 7:44 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 | « packagerSafari.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
(Empty)
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
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5 import base64
6 import re
7 import struct
8 import time
9 from xml.etree import ElementTree
10 import zlib
11
12 from Crypto.Hash import SHA
13 from Crypto.PublicKey import RSA
14 from Crypto.Signature import PKCS1_v1_5
15
16 XAR_HEADER_MAGIC = 0x78617221
17 XAR_HEADER_SIZE = 28
18 XAR_VERSION = 1
19 XAR_CKSUM_SHA1 = 1
20
21 def read_key(keyfile):
22 with open(keyfile, 'r') as file:
23 data = file.read()
24 data = re.sub(r'(-+END PRIVATE KEY-+).*', r'\1', data, flags=re.S)
25 return RSA.importKey(data)
26
27 def read_certificates(keyfile):
28 certs = []
29 with open(keyfile, 'r') as file:
30 data = file.read()
31 for match in re.finditer(r'-+BEGIN CERTIFICATE-+(.*?)-+END CERTIFICATE-+ ', data, re.S):
32 certs.append(base64.b64decode(match.group(1)))
33 return certs
34
35 def get_checksum(data):
36 return SHA.new(data).digest()
37
38 def get_hexchecksum(data):
39 return SHA.new(data).hexdigest()
40
41 def get_signature(key, data):
42 return PKCS1_v1_5.new(key).sign(SHA.new(data))
43
44 def compress_files(filedata, root, offset):
45 files = []
46 filedata = sorted(filedata)
47 directory_stack = [{'path': '', 'element': root}]
48 file_id = 1
49 for path, data in filedata:
50 # Remove directories that are done
51 while True:
52 directory = directory_stack[-1]
53 directory_path = directory['path']
54 if path.startswith(directory_path):
55 break
56 directory_stack.pop()
57
58 # Add new directories
59 relpath = path[len(directory_path):]
60 while '/' in relpath:
61 directory_name, relpath = relpath.split('/', 1)
62 directory_path += directory_name + '/'
63 element = ElementTree.SubElement(directory['element'], 'file')
64 directory = {
65 'path': directory_path,
66 'element': element,
67 }
68 element.set('id', str(file_id))
69 file_id += 1
70 ElementTree.SubElement(element, 'name').text = directory_name
71 ElementTree.SubElement(element, 'type').text = 'directory'
72 ElementTree.SubElement(element, 'mode').text = '0755'
73 directory_stack.append(directory)
74
75 # Add the actual file
76 element = ElementTree.SubElement(directory['element'], 'file')
77 element.set('id', str(file_id))
78 file_id += 1
79 ElementTree.SubElement(element, 'name').text = relpath
80 ElementTree.SubElement(element, 'type').text = 'file'
81 ElementTree.SubElement(element, 'mode').text = '0644'
82
83 datatag = ElementTree.SubElement(element, 'data')
84 ElementTree.SubElement(datatag, 'extracted-checksum', {'style': 'sha1'}) .text = get_hexchecksum(data)
85 ElementTree.SubElement(datatag, 'size').text = str(len(data))
86
87 compressed = zlib.compress(data, 9)
88 ElementTree.SubElement(datatag, 'encoding', {'style': 'application/x-gzi p'})
89 ElementTree.SubElement(datatag, 'archived-checksum', {'style': 'sha1'}). text = get_hexchecksum(compressed)
90 ElementTree.SubElement(datatag, 'offset').text = str(offset)
91 ElementTree.SubElement(datatag, 'length').text = str(len(compressed))
92 offset += len(compressed)
93
94 files.append(compressed)
95 return files
96
97 def create(archivepath, contents, keyfile):
98 key = read_key(keyfile)
99 certs = read_certificates(keyfile)
100
101 root = ElementTree.Element('xar')
102 toc = ElementTree.SubElement(root, 'toc')
103
104 creation_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
105 ElementTree.SubElement(toc, 'creation-time').text = creation_time
106
107 # Timestamp epoch starts at 2001-01-01T00:00:00.000Z
108 sign_time = str(time.time() - 978307200)
109 ElementTree.SubElement(toc, 'signature-creation-time').text = sign_time
110
111 offset = 0
112
113 checksum_size = len(get_checksum(''))
114 checksum = ElementTree.SubElement(toc, 'checksum', {'style': 'sha1'})
115 ElementTree.SubElement(checksum, 'offset').text = str(offset)
116 ElementTree.SubElement(checksum, 'size').text = str(checksum_size)
117 offset += checksum_size
118
119 signature_size = len(get_signature(key, ''))
120 signature = ElementTree.SubElement(toc, 'signature', {'style': 'RSA'})
121 ElementTree.SubElement(signature, 'offset').text = str(offset)
122 ElementTree.SubElement(signature, 'size').text = str(signature_size)
123 offset += signature_size
124
125 keyinfo = ElementTree.SubElement(signature, 'KeyInfo')
126 keyinfo.set('xmlns', 'http://www.w3.org/2000/09/xmldsig#')
127 x509data = ElementTree.SubElement(keyinfo, 'X509Data')
128 for cert in certs:
129 ElementTree.SubElement(x509data, 'X509Certificate').text = base64.b64enc ode(cert)
130
131 files = compress_files(contents.iteritems(), toc, offset)
132
133 toc_uncompressed = ElementTree.tostring(root).encode('utf-8')
134 toc_compressed = zlib.compress(toc_uncompressed, 9)
135
136 with open(archivepath, 'wb') as file:
137 # The file starts with a minimalistic header
138 header = struct.pack('>IHHQQI', XAR_HEADER_MAGIC, XAR_HEADER_SIZE,
139 XAR_VERSION, len(toc_compressed), len(toc_uncompressed),
140 XAR_CKSUM_SHA1)
141 file.write(header)
142
143 # It's followed up with a compressed XML table of contents
144 file.write(toc_compressed)
145
146 # Now the actual data, all the offsets are in the table of contents
147 file.write(get_checksum(toc_compressed))
148 file.write(get_signature(key, toc_compressed))
149 for compressed in files:
150 file.write(compressed)
OLDNEW
« no previous file with comments | « packagerSafari.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld