blob: 97687e79542061bdfb15824a141378775e87c4ba [file] [log] [blame]
Tao Bao481bab82017-12-21 11:23:09 -08001#
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import copy
Tao Baoc7b403a2018-01-30 18:19:04 -080018import os
Tao Baofabe0832018-01-17 15:52:28 -080019import os.path
Tao Baob6304672018-03-08 16:28:33 -080020import subprocess
Tao Bao481bab82017-12-21 11:23:09 -080021import unittest
Tao Baoc7b403a2018-01-30 18:19:04 -080022import zipfile
Tao Bao481bab82017-12-21 11:23:09 -080023
24import common
Tao Bao04e1f012018-02-04 12:13:35 -080025import test_utils
Tao Bao481bab82017-12-21 11:23:09 -080026from ota_from_target_files import (
Tao Baob6304672018-03-08 16:28:33 -080027 _LoadOemDicts, AbOtaPropertyFiles, BuildInfo, GetPackageMetadata,
Tao Bao15a146a2018-02-21 16:06:59 -080028 GetTargetFilesZipForSecondaryImages,
Tao Baoc0746f42018-02-21 13:17:22 -080029 GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles,
Tao Bao69203522018-03-08 16:09:01 -080030 Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles,
31 StreamingPropertyFiles, WriteFingerprintAssertion)
Tao Baofabe0832018-01-17 15:52:28 -080032
33
Tao Baof7140c02018-01-30 17:09:24 -080034def construct_target_files(secondary=False):
35 """Returns a target-files.zip file for generating OTA packages."""
36 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
37 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
38 # META/update_engine_config.txt
39 target_files_zip.writestr(
40 'META/update_engine_config.txt',
41 "PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
42
Tao Bao15a146a2018-02-21 16:06:59 -080043 # META/postinstall_config.txt
44 target_files_zip.writestr(
45 POSTINSTALL_CONFIG,
46 '\n'.join([
47 "RUN_POSTINSTALL_system=true",
48 "POSTINSTALL_PATH_system=system/bin/otapreopt_script",
49 "FILESYSTEM_TYPE_system=ext4",
50 "POSTINSTALL_OPTIONAL_system=true",
51 ]))
52
Tao Baof7140c02018-01-30 17:09:24 -080053 # META/ab_partitions.txt
54 ab_partitions = ['boot', 'system', 'vendor']
55 target_files_zip.writestr(
56 'META/ab_partitions.txt',
57 '\n'.join(ab_partitions))
58
59 # Create dummy images for each of them.
60 for partition in ab_partitions:
61 target_files_zip.writestr('IMAGES/' + partition + '.img',
62 os.urandom(len(partition)))
63
64 if secondary:
65 target_files_zip.writestr('IMAGES/system_other.img',
66 os.urandom(len("system_other")))
67
68 return target_files
69
70
Tao Bao481bab82017-12-21 11:23:09 -080071class MockScriptWriter(object):
72 """A class that mocks edify_generator.EdifyGenerator.
73
74 It simply pushes the incoming arguments onto script stack, which is to assert
75 the calls to EdifyGenerator functions.
76 """
77
78 def __init__(self):
79 self.script = []
80
81 def Mount(self, *args):
82 self.script.append(('Mount',) + args)
83
84 def AssertDevice(self, *args):
85 self.script.append(('AssertDevice',) + args)
86
87 def AssertOemProperty(self, *args):
88 self.script.append(('AssertOemProperty',) + args)
89
90 def AssertFingerprintOrThumbprint(self, *args):
91 self.script.append(('AssertFingerprintOrThumbprint',) + args)
92
93 def AssertSomeFingerprint(self, *args):
94 self.script.append(('AssertSomeFingerprint',) + args)
95
96 def AssertSomeThumbprint(self, *args):
97 self.script.append(('AssertSomeThumbprint',) + args)
98
99
100class BuildInfoTest(unittest.TestCase):
101
102 TEST_INFO_DICT = {
103 'build.prop' : {
104 'ro.product.device' : 'product-device',
105 'ro.product.name' : 'product-name',
106 'ro.build.fingerprint' : 'build-fingerprint',
107 'ro.build.foo' : 'build-foo',
108 },
109 'vendor.build.prop' : {
110 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
111 },
112 'property1' : 'value1',
113 'property2' : 4096,
114 }
115
116 TEST_INFO_DICT_USES_OEM_PROPS = {
117 'build.prop' : {
118 'ro.product.name' : 'product-name',
119 'ro.build.thumbprint' : 'build-thumbprint',
120 'ro.build.bar' : 'build-bar',
121 },
122 'vendor.build.prop' : {
123 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
124 },
125 'property1' : 'value1',
126 'property2' : 4096,
127 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
128 }
129
130 TEST_OEM_DICTS = [
131 {
132 'ro.product.brand' : 'brand1',
133 'ro.product.device' : 'device1',
134 },
135 {
136 'ro.product.brand' : 'brand2',
137 'ro.product.device' : 'device2',
138 },
139 {
140 'ro.product.brand' : 'brand3',
141 'ro.product.device' : 'device3',
142 },
143 ]
144
145 def test_init(self):
146 target_info = BuildInfo(self.TEST_INFO_DICT, None)
147 self.assertEqual('product-device', target_info.device)
148 self.assertEqual('build-fingerprint', target_info.fingerprint)
149 self.assertFalse(target_info.is_ab)
150 self.assertIsNone(target_info.oem_props)
151
152 def test_init_with_oem_props(self):
153 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
154 self.TEST_OEM_DICTS)
155 self.assertEqual('device1', target_info.device)
156 self.assertEqual('brand1/product-name/device1:build-thumbprint',
157 target_info.fingerprint)
158
159 # Swap the order in oem_dicts, which would lead to different BuildInfo.
160 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
161 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
162 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, oem_dicts)
163 self.assertEqual('device3', target_info.device)
164 self.assertEqual('brand3/product-name/device3:build-thumbprint',
165 target_info.fingerprint)
166
167 # Missing oem_dict should be rejected.
168 self.assertRaises(AssertionError, BuildInfo,
169 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
170
171 def test___getitem__(self):
172 target_info = BuildInfo(self.TEST_INFO_DICT, None)
173 self.assertEqual('value1', target_info['property1'])
174 self.assertEqual(4096, target_info['property2'])
175 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
176
177 def test___getitem__with_oem_props(self):
178 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
179 self.TEST_OEM_DICTS)
180 self.assertEqual('value1', target_info['property1'])
181 self.assertEqual(4096, target_info['property2'])
182 self.assertRaises(KeyError,
183 lambda: target_info['build.prop']['ro.build.foo'])
184
185 def test_get(self):
186 target_info = BuildInfo(self.TEST_INFO_DICT, None)
187 self.assertEqual('value1', target_info.get('property1'))
188 self.assertEqual(4096, target_info.get('property2'))
189 self.assertEqual(4096, target_info.get('property2', 1024))
190 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
191 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
192
193 def test_get_with_oem_props(self):
194 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
195 self.TEST_OEM_DICTS)
196 self.assertEqual('value1', target_info.get('property1'))
197 self.assertEqual(4096, target_info.get('property2'))
198 self.assertEqual(4096, target_info.get('property2', 1024))
199 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
200 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
201 self.assertRaises(KeyError,
202 lambda: target_info.get('build.prop')['ro.build.foo'])
203
204 def test_GetBuildProp(self):
205 target_info = BuildInfo(self.TEST_INFO_DICT, None)
206 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
207 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
208 'ro.build.nonexistent')
209
210 def test_GetBuildProp_with_oem_props(self):
211 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
212 self.TEST_OEM_DICTS)
213 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
214 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
215 'ro.build.nonexistent')
216
217 def test_GetVendorBuildProp(self):
218 target_info = BuildInfo(self.TEST_INFO_DICT, None)
219 self.assertEqual('vendor-build-fingerprint',
220 target_info.GetVendorBuildProp(
221 'ro.vendor.build.fingerprint'))
222 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
223 'ro.build.nonexistent')
224
225 def test_GetVendorBuildProp_with_oem_props(self):
226 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
227 self.TEST_OEM_DICTS)
228 self.assertEqual('vendor-build-fingerprint',
229 target_info.GetVendorBuildProp(
230 'ro.vendor.build.fingerprint'))
231 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
232 'ro.build.nonexistent')
233
234 def test_WriteMountOemScript(self):
235 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
236 self.TEST_OEM_DICTS)
237 script_writer = MockScriptWriter()
238 target_info.WriteMountOemScript(script_writer)
239 self.assertEqual([('Mount', '/oem', None)], script_writer.script)
240
241 def test_WriteDeviceAssertions(self):
242 target_info = BuildInfo(self.TEST_INFO_DICT, None)
243 script_writer = MockScriptWriter()
244 target_info.WriteDeviceAssertions(script_writer, False)
245 self.assertEqual([('AssertDevice', 'product-device')], script_writer.script)
246
247 def test_WriteDeviceAssertions_with_oem_props(self):
248 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
249 self.TEST_OEM_DICTS)
250 script_writer = MockScriptWriter()
251 target_info.WriteDeviceAssertions(script_writer, False)
252 self.assertEqual(
253 [
254 ('AssertOemProperty', 'ro.product.device',
255 ['device1', 'device2', 'device3'], False),
256 ('AssertOemProperty', 'ro.product.brand',
257 ['brand1', 'brand2', 'brand3'], False),
258 ],
259 script_writer.script)
260
261 def test_WriteFingerprintAssertion_without_oem_props(self):
262 target_info = BuildInfo(self.TEST_INFO_DICT, None)
263 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT)
264 source_info_dict['build.prop']['ro.build.fingerprint'] = (
265 'source-build-fingerprint')
266 source_info = BuildInfo(source_info_dict, None)
267
268 script_writer = MockScriptWriter()
269 WriteFingerprintAssertion(script_writer, target_info, source_info)
270 self.assertEqual(
271 [('AssertSomeFingerprint', 'source-build-fingerprint',
272 'build-fingerprint')],
273 script_writer.script)
274
275 def test_WriteFingerprintAssertion_with_source_oem_props(self):
276 target_info = BuildInfo(self.TEST_INFO_DICT, None)
277 source_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
278 self.TEST_OEM_DICTS)
279
280 script_writer = MockScriptWriter()
281 WriteFingerprintAssertion(script_writer, target_info, source_info)
282 self.assertEqual(
283 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
284 'build-thumbprint')],
285 script_writer.script)
286
287 def test_WriteFingerprintAssertion_with_target_oem_props(self):
288 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
289 self.TEST_OEM_DICTS)
290 source_info = BuildInfo(self.TEST_INFO_DICT, None)
291
292 script_writer = MockScriptWriter()
293 WriteFingerprintAssertion(script_writer, target_info, source_info)
294 self.assertEqual(
295 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
296 'build-thumbprint')],
297 script_writer.script)
298
299 def test_WriteFingerprintAssertion_with_both_oem_props(self):
300 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
301 self.TEST_OEM_DICTS)
302 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
303 source_info_dict['build.prop']['ro.build.thumbprint'] = (
304 'source-build-thumbprint')
305 source_info = BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
306
307 script_writer = MockScriptWriter()
308 WriteFingerprintAssertion(script_writer, target_info, source_info)
309 self.assertEqual(
310 [('AssertSomeThumbprint', 'build-thumbprint',
311 'source-build-thumbprint')],
312 script_writer.script)
313
314
315class LoadOemDictsTest(unittest.TestCase):
316
317 def tearDown(self):
318 common.Cleanup()
319
320 def test_NoneDict(self):
321 self.assertIsNone(_LoadOemDicts(None))
322
323 def test_SingleDict(self):
324 dict_file = common.MakeTempFile()
325 with open(dict_file, 'w') as dict_fp:
326 dict_fp.write('abc=1\ndef=2\nxyz=foo\na.b.c=bar\n')
327
328 oem_dicts = _LoadOemDicts([dict_file])
329 self.assertEqual(1, len(oem_dicts))
330 self.assertEqual('foo', oem_dicts[0]['xyz'])
331 self.assertEqual('bar', oem_dicts[0]['a.b.c'])
332
333 def test_MultipleDicts(self):
334 oem_source = []
335 for i in range(3):
336 dict_file = common.MakeTempFile()
337 with open(dict_file, 'w') as dict_fp:
338 dict_fp.write(
339 'ro.build.index={}\ndef=2\nxyz=foo\na.b.c=bar\n'.format(i))
340 oem_source.append(dict_file)
341
342 oem_dicts = _LoadOemDicts(oem_source)
343 self.assertEqual(3, len(oem_dicts))
344 for i, oem_dict in enumerate(oem_dicts):
345 self.assertEqual('2', oem_dict['def'])
346 self.assertEqual('foo', oem_dict['xyz'])
347 self.assertEqual('bar', oem_dict['a.b.c'])
348 self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])
Tao Baodf3a48b2018-01-10 16:30:43 -0800349
350
351class OtaFromTargetFilesTest(unittest.TestCase):
352
353 TEST_TARGET_INFO_DICT = {
354 'build.prop' : {
355 'ro.product.device' : 'product-device',
356 'ro.build.fingerprint' : 'build-fingerprint-target',
357 'ro.build.version.incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800358 'ro.build.version.sdk' : '27',
359 'ro.build.version.security_patch' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800360 'ro.build.date.utc' : '1500000000',
361 },
362 }
363
364 TEST_SOURCE_INFO_DICT = {
365 'build.prop' : {
366 'ro.product.device' : 'product-device',
367 'ro.build.fingerprint' : 'build-fingerprint-source',
368 'ro.build.version.incremental' : 'build-version-incremental-source',
Tao Bao35dc2552018-02-01 13:18:00 -0800369 'ro.build.version.sdk' : '25',
370 'ro.build.version.security_patch' : '2016-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800371 'ro.build.date.utc' : '1400000000',
372 },
373 }
374
375 def setUp(self):
376 # Reset the global options as in ota_from_target_files.py.
377 common.OPTIONS.incremental_source = None
378 common.OPTIONS.downgrade = False
379 common.OPTIONS.timestamp = False
380 common.OPTIONS.wipe_user_data = False
381
Tao Baof5110492018-03-02 09:47:43 -0800382 def tearDown(self):
383 common.Cleanup()
384
Tao Baodf3a48b2018-01-10 16:30:43 -0800385 def test_GetPackageMetadata_abOta_full(self):
386 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
387 target_info_dict['ab_update'] = 'true'
388 target_info = BuildInfo(target_info_dict, None)
389 metadata = GetPackageMetadata(target_info)
390 self.assertDictEqual(
391 {
392 'ota-type' : 'AB',
393 'ota-required-cache' : '0',
394 'post-build' : 'build-fingerprint-target',
395 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800396 'post-sdk-level' : '27',
397 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800398 'post-timestamp' : '1500000000',
399 'pre-device' : 'product-device',
400 },
401 metadata)
402
403 def test_GetPackageMetadata_abOta_incremental(self):
404 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
405 target_info_dict['ab_update'] = 'true'
406 target_info = BuildInfo(target_info_dict, None)
407 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
408 common.OPTIONS.incremental_source = ''
409 metadata = GetPackageMetadata(target_info, source_info)
410 self.assertDictEqual(
411 {
412 'ota-type' : 'AB',
413 'ota-required-cache' : '0',
414 'post-build' : 'build-fingerprint-target',
415 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800416 'post-sdk-level' : '27',
417 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800418 'post-timestamp' : '1500000000',
419 'pre-device' : 'product-device',
420 'pre-build' : 'build-fingerprint-source',
421 'pre-build-incremental' : 'build-version-incremental-source',
422 },
423 metadata)
424
425 def test_GetPackageMetadata_nonAbOta_full(self):
426 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
427 metadata = GetPackageMetadata(target_info)
428 self.assertDictEqual(
429 {
430 'ota-type' : 'BLOCK',
431 'post-build' : 'build-fingerprint-target',
432 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800433 'post-sdk-level' : '27',
434 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800435 'post-timestamp' : '1500000000',
436 'pre-device' : 'product-device',
437 },
438 metadata)
439
440 def test_GetPackageMetadata_nonAbOta_incremental(self):
441 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
442 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
443 common.OPTIONS.incremental_source = ''
444 metadata = GetPackageMetadata(target_info, source_info)
445 self.assertDictEqual(
446 {
447 'ota-type' : 'BLOCK',
448 'post-build' : 'build-fingerprint-target',
449 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800450 'post-sdk-level' : '27',
451 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800452 'post-timestamp' : '1500000000',
453 'pre-device' : 'product-device',
454 'pre-build' : 'build-fingerprint-source',
455 'pre-build-incremental' : 'build-version-incremental-source',
456 },
457 metadata)
458
459 def test_GetPackageMetadata_wipe(self):
460 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
461 common.OPTIONS.wipe_user_data = True
462 metadata = GetPackageMetadata(target_info)
463 self.assertDictEqual(
464 {
465 'ota-type' : 'BLOCK',
466 'ota-wipe' : 'yes',
467 'post-build' : 'build-fingerprint-target',
468 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800469 'post-sdk-level' : '27',
470 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800471 'post-timestamp' : '1500000000',
472 'pre-device' : 'product-device',
473 },
474 metadata)
475
476 @staticmethod
477 def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
478 (target_info['build.prop']['ro.build.date.utc'],
479 source_info['build.prop']['ro.build.date.utc']) = (
480 source_info['build.prop']['ro.build.date.utc'],
481 target_info['build.prop']['ro.build.date.utc'])
482
483 def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
484 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
485 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
486 self._test_GetPackageMetadata_swapBuildTimestamps(
487 target_info_dict, source_info_dict)
488
489 target_info = BuildInfo(target_info_dict, None)
490 source_info = BuildInfo(source_info_dict, None)
491 common.OPTIONS.incremental_source = ''
492 self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
493 source_info)
494
495 def test_GetPackageMetadata_downgrade(self):
496 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
497 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
498 self._test_GetPackageMetadata_swapBuildTimestamps(
499 target_info_dict, source_info_dict)
500
501 target_info = BuildInfo(target_info_dict, None)
502 source_info = BuildInfo(source_info_dict, None)
503 common.OPTIONS.incremental_source = ''
504 common.OPTIONS.downgrade = True
505 common.OPTIONS.wipe_user_data = True
506 metadata = GetPackageMetadata(target_info, source_info)
507 self.assertDictEqual(
508 {
509 'ota-downgrade' : 'yes',
510 'ota-type' : 'BLOCK',
511 'ota-wipe' : 'yes',
512 'post-build' : 'build-fingerprint-target',
513 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800514 'post-sdk-level' : '27',
515 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800516 'pre-device' : 'product-device',
517 'pre-build' : 'build-fingerprint-source',
518 'pre-build-incremental' : 'build-version-incremental-source',
519 },
520 metadata)
521
522 def test_GetPackageMetadata_overrideTimestamp(self):
523 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
524 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
525 self._test_GetPackageMetadata_swapBuildTimestamps(
526 target_info_dict, source_info_dict)
527
528 target_info = BuildInfo(target_info_dict, None)
529 source_info = BuildInfo(source_info_dict, None)
530 common.OPTIONS.incremental_source = ''
531 common.OPTIONS.timestamp = True
532 metadata = GetPackageMetadata(target_info, source_info)
533 self.assertDictEqual(
534 {
535 'ota-type' : 'BLOCK',
536 'post-build' : 'build-fingerprint-target',
537 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800538 'post-sdk-level' : '27',
539 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800540 'post-timestamp' : '1500000001',
541 'pre-device' : 'product-device',
542 'pre-build' : 'build-fingerprint-source',
543 'pre-build-incremental' : 'build-version-incremental-source',
544 },
545 metadata)
Tao Baofabe0832018-01-17 15:52:28 -0800546
Tao Baof7140c02018-01-30 17:09:24 -0800547 def test_GetTargetFilesZipForSecondaryImages(self):
548 input_file = construct_target_files(secondary=True)
549 target_file = GetTargetFilesZipForSecondaryImages(input_file)
550
551 with zipfile.ZipFile(target_file) as verify_zip:
552 namelist = verify_zip.namelist()
553
554 self.assertIn('META/ab_partitions.txt', namelist)
555 self.assertIn('IMAGES/boot.img', namelist)
556 self.assertIn('IMAGES/system.img', namelist)
557 self.assertIn('IMAGES/vendor.img', namelist)
Tao Bao15a146a2018-02-21 16:06:59 -0800558 self.assertIn(POSTINSTALL_CONFIG, namelist)
Tao Baof7140c02018-01-30 17:09:24 -0800559
560 self.assertNotIn('IMAGES/system_other.img', namelist)
561 self.assertNotIn('IMAGES/system.map', namelist)
562
Tao Bao15a146a2018-02-21 16:06:59 -0800563 def test_GetTargetFilesZipForSecondaryImages_skipPostinstall(self):
564 input_file = construct_target_files(secondary=True)
565 target_file = GetTargetFilesZipForSecondaryImages(
566 input_file, skip_postinstall=True)
567
568 with zipfile.ZipFile(target_file) as verify_zip:
569 namelist = verify_zip.namelist()
570
571 self.assertIn('META/ab_partitions.txt', namelist)
572 self.assertIn('IMAGES/boot.img', namelist)
573 self.assertIn('IMAGES/system.img', namelist)
574 self.assertIn('IMAGES/vendor.img', namelist)
575
576 self.assertNotIn('IMAGES/system_other.img', namelist)
577 self.assertNotIn('IMAGES/system.map', namelist)
578 self.assertNotIn(POSTINSTALL_CONFIG, namelist)
579
580 def test_GetTargetFilesZipWithoutPostinstallConfig(self):
581 input_file = construct_target_files()
582 target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
583 with zipfile.ZipFile(target_file) as verify_zip:
584 self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
585
586 def test_GetTargetFilesZipWithoutPostinstallConfig_missingEntry(self):
587 input_file = construct_target_files()
588 common.ZipDelete(input_file, POSTINSTALL_CONFIG)
589 target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
590 with zipfile.ZipFile(target_file) as verify_zip:
591 self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
592
Tao Baoae5e4c32018-03-01 19:30:00 -0800593
Tao Bao69203522018-03-08 16:09:01 -0800594class TestPropertyFiles(PropertyFiles):
595 """A class that extends PropertyFiles for testing purpose."""
596
597 def __init__(self):
598 super(TestPropertyFiles, self).__init__()
599 self.name = 'ota-test-property-files'
600 self.required = (
601 'required-entry1',
602 'required-entry2',
603 )
604 self.optional = (
605 'optional-entry1',
606 'optional-entry2',
607 )
608
609
610class PropertyFilesTest(unittest.TestCase):
Tao Baoae5e4c32018-03-01 19:30:00 -0800611
612 def tearDown(self):
613 common.Cleanup()
614
Tao Baof5110492018-03-02 09:47:43 -0800615 @staticmethod
616 def _construct_zip_package(entries):
617 zip_file = common.MakeTempFile(suffix='.zip')
618 with zipfile.ZipFile(zip_file, 'w') as zip_fp:
619 for entry in entries:
620 zip_fp.writestr(
621 entry,
622 entry.replace('.', '-').upper(),
623 zipfile.ZIP_STORED)
624 return zip_file
625
626 @staticmethod
Tao Bao69203522018-03-08 16:09:01 -0800627 def _parse_property_files_string(data):
Tao Baof5110492018-03-02 09:47:43 -0800628 result = {}
629 for token in data.split(','):
630 name, info = token.split(':', 1)
631 result[name] = info
632 return result
633
634 def _verify_entries(self, input_file, tokens, entries):
635 for entry in entries:
636 offset, size = map(int, tokens[entry].split(':'))
637 with open(input_file, 'rb') as input_fp:
638 input_fp.seek(offset)
639 if entry == 'metadata':
640 expected = b'META-INF/COM/ANDROID/METADATA'
641 else:
642 expected = entry.replace('.', '-').upper().encode()
643 self.assertEqual(expected, input_fp.read(size))
644
Tao Baoae5e4c32018-03-01 19:30:00 -0800645 def test_Compute(self):
Tao Baof5110492018-03-02 09:47:43 -0800646 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800647 'required-entry1',
648 'required-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800649 )
650 zip_file = self._construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800651 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800652 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Tao Bao69203522018-03-08 16:09:01 -0800653 property_files_string = property_files.Compute(zip_fp)
Tao Baof5110492018-03-02 09:47:43 -0800654
Tao Bao69203522018-03-08 16:09:01 -0800655 tokens = self._parse_property_files_string(property_files_string)
Tao Baof5110492018-03-02 09:47:43 -0800656 self.assertEqual(3, len(tokens))
657 self._verify_entries(zip_file, tokens, entries)
658
Tao Bao69203522018-03-08 16:09:01 -0800659 def test_Compute_withOptionalEntries(self):
Tao Baof5110492018-03-02 09:47:43 -0800660 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800661 'required-entry1',
662 'required-entry2',
663 'optional-entry1',
664 'optional-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800665 )
666 zip_file = self._construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800667 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800668 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Tao Bao69203522018-03-08 16:09:01 -0800669 property_files_string = property_files.Compute(zip_fp)
Tao Baof5110492018-03-02 09:47:43 -0800670
Tao Bao69203522018-03-08 16:09:01 -0800671 tokens = self._parse_property_files_string(property_files_string)
Tao Baof5110492018-03-02 09:47:43 -0800672 self.assertEqual(5, len(tokens))
673 self._verify_entries(zip_file, tokens, entries)
674
Tao Bao69203522018-03-08 16:09:01 -0800675 def test_Compute_missingRequiredEntry(self):
676 entries = (
677 'required-entry2',
678 )
679 zip_file = self._construct_zip_package(entries)
680 property_files = TestPropertyFiles()
681 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
682 self.assertRaises(KeyError, property_files.Compute, zip_fp)
683
Tao Baoae5e4c32018-03-01 19:30:00 -0800684 def test_Finalize(self):
Tao Baof5110492018-03-02 09:47:43 -0800685 entries = [
Tao Bao69203522018-03-08 16:09:01 -0800686 'required-entry1',
687 'required-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800688 'META-INF/com/android/metadata',
689 ]
690 zip_file = self._construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800691 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800692 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Tao Bao69203522018-03-08 16:09:01 -0800693 # pylint: disable=protected-access
Tao Baoae5e4c32018-03-01 19:30:00 -0800694 raw_metadata = property_files._GetPropertyFilesString(
695 zip_fp, reserve_space=False)
696 streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
Tao Bao69203522018-03-08 16:09:01 -0800697 tokens = self._parse_property_files_string(streaming_metadata)
Tao Baof5110492018-03-02 09:47:43 -0800698
699 self.assertEqual(3, len(tokens))
700 # 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
701 # streaming metadata.
702 entries[2] = 'metadata'
703 self._verify_entries(zip_file, tokens, entries)
704
Tao Baoae5e4c32018-03-01 19:30:00 -0800705 def test_Finalize_assertReservedLength(self):
Tao Baof5110492018-03-02 09:47:43 -0800706 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800707 'required-entry1',
708 'required-entry2',
709 'optional-entry1',
710 'optional-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800711 'META-INF/com/android/metadata',
712 )
713 zip_file = self._construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800714 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800715 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
716 # First get the raw metadata string (i.e. without padding space).
Tao Bao69203522018-03-08 16:09:01 -0800717 # pylint: disable=protected-access
Tao Baoae5e4c32018-03-01 19:30:00 -0800718 raw_metadata = property_files._GetPropertyFilesString(
719 zip_fp, reserve_space=False)
Tao Baof5110492018-03-02 09:47:43 -0800720 raw_length = len(raw_metadata)
721
722 # Now pass in the exact expected length.
Tao Baoae5e4c32018-03-01 19:30:00 -0800723 streaming_metadata = property_files.Finalize(zip_fp, raw_length)
Tao Baof5110492018-03-02 09:47:43 -0800724 self.assertEqual(raw_length, len(streaming_metadata))
725
726 # Or pass in insufficient length.
727 self.assertRaises(
728 AssertionError,
Tao Baoae5e4c32018-03-01 19:30:00 -0800729 property_files.Finalize,
Tao Baof5110492018-03-02 09:47:43 -0800730 zip_fp,
Tao Baoae5e4c32018-03-01 19:30:00 -0800731 raw_length - 1)
Tao Baof5110492018-03-02 09:47:43 -0800732
733 # Or pass in a much larger size.
Tao Baoae5e4c32018-03-01 19:30:00 -0800734 streaming_metadata = property_files.Finalize(
Tao Baof5110492018-03-02 09:47:43 -0800735 zip_fp,
Tao Baoae5e4c32018-03-01 19:30:00 -0800736 raw_length + 20)
Tao Baof5110492018-03-02 09:47:43 -0800737 self.assertEqual(raw_length + 20, len(streaming_metadata))
738 self.assertEqual(' ' * 20, streaming_metadata[raw_length:])
739
Tao Baoae5e4c32018-03-01 19:30:00 -0800740 def test_Verify(self):
741 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800742 'required-entry1',
743 'required-entry2',
744 'optional-entry1',
745 'optional-entry2',
746 'META-INF/com/android/metadata',
747 )
748 zip_file = self._construct_zip_package(entries)
749 property_files = TestPropertyFiles()
750 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
751 # First get the raw metadata string (i.e. without padding space).
752 # pylint: disable=protected-access
753 raw_metadata = property_files._GetPropertyFilesString(
754 zip_fp, reserve_space=False)
755
756 # Should pass the test if verification passes.
757 property_files.Verify(zip_fp, raw_metadata)
758
759 # Or raise on verification failure.
760 self.assertRaises(
761 AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
762
763
764class StreamingPropertyFilesTest(PropertyFilesTest):
765 """Additional sanity checks specialized for StreamingPropertyFiles."""
766
767 def test_init(self):
768 property_files = StreamingPropertyFiles()
769 self.assertEqual('ota-streaming-property-files', property_files.name)
770 self.assertEqual(
771 (
772 'payload.bin',
773 'payload_properties.txt',
774 ),
775 property_files.required)
776 self.assertEqual(
777 (
778 'care_map.txt',
779 'compatibility.zip',
780 ),
781 property_files.optional)
782
783 def test_Compute(self):
784 entries = (
Tao Baoae5e4c32018-03-01 19:30:00 -0800785 'payload.bin',
786 'payload_properties.txt',
787 'care_map.txt',
Tao Bao69203522018-03-08 16:09:01 -0800788 'compatibility.zip',
789 )
790 zip_file = self._construct_zip_package(entries)
791 property_files = StreamingPropertyFiles()
792 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
793 property_files_string = property_files.Compute(zip_fp)
794
795 tokens = self._parse_property_files_string(property_files_string)
796 self.assertEqual(5, len(tokens))
797 self._verify_entries(zip_file, tokens, entries)
798
799 def test_Finalize(self):
800 entries = [
801 'payload.bin',
802 'payload_properties.txt',
803 'care_map.txt',
804 'compatibility.zip',
805 'META-INF/com/android/metadata',
806 ]
807 zip_file = self._construct_zip_package(entries)
808 property_files = StreamingPropertyFiles()
809 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
810 # pylint: disable=protected-access
811 raw_metadata = property_files._GetPropertyFilesString(
812 zip_fp, reserve_space=False)
813 streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
814 tokens = self._parse_property_files_string(streaming_metadata)
815
816 self.assertEqual(5, len(tokens))
817 # 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
818 # streaming metadata.
819 entries[4] = 'metadata'
820 self._verify_entries(zip_file, tokens, entries)
821
822 def test_Verify(self):
823 entries = (
824 'payload.bin',
825 'payload_properties.txt',
826 'care_map.txt',
827 'compatibility.zip',
Tao Baoae5e4c32018-03-01 19:30:00 -0800828 'META-INF/com/android/metadata',
829 )
830 zip_file = self._construct_zip_package(entries)
831 property_files = StreamingPropertyFiles()
832 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
833 # First get the raw metadata string (i.e. without padding space).
Tao Bao69203522018-03-08 16:09:01 -0800834 # pylint: disable=protected-access
Tao Baoae5e4c32018-03-01 19:30:00 -0800835 raw_metadata = property_files._GetPropertyFilesString(
836 zip_fp, reserve_space=False)
837
838 # Should pass the test if verification passes.
839 property_files.Verify(zip_fp, raw_metadata)
840
841 # Or raise on verification failure.
842 self.assertRaises(
843 AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
844
Tao Baofabe0832018-01-17 15:52:28 -0800845
Tao Baob6304672018-03-08 16:28:33 -0800846class AbOtaPropertyFilesTest(PropertyFilesTest):
847 """Additional sanity checks specialized for AbOtaPropertyFiles."""
848
849 # The size for payload and metadata signature size.
850 SIGNATURE_SIZE = 256
851
852 def setUp(self):
853 self.testdata_dir = test_utils.get_testdata_dir()
854 self.assertTrue(os.path.exists(self.testdata_dir))
855
856 common.OPTIONS.wipe_user_data = False
857 common.OPTIONS.payload_signer = None
858 common.OPTIONS.payload_signer_args = None
859 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
860 common.OPTIONS.key_passwords = {
861 common.OPTIONS.package_key : None,
862 }
863
864 def test_init(self):
865 property_files = AbOtaPropertyFiles()
866 self.assertEqual('ota-property-files', property_files.name)
867 self.assertEqual(
868 (
869 'payload.bin',
870 'payload_properties.txt',
871 ),
872 property_files.required)
873 self.assertEqual(
874 (
875 'care_map.txt',
876 'compatibility.zip',
877 ),
878 property_files.optional)
879
880 def test_GetPayloadMetadataOffsetAndSize(self):
881 target_file = construct_target_files()
882 payload = Payload()
883 payload.Generate(target_file)
884
885 payload_signer = PayloadSigner()
886 payload.Sign(payload_signer)
887
888 output_file = common.MakeTempFile(suffix='.zip')
889 with zipfile.ZipFile(output_file, 'w') as output_zip:
890 payload.WriteToZip(output_zip)
891
892 # Find out the payload metadata offset and size.
893 property_files = AbOtaPropertyFiles()
894 with zipfile.ZipFile(output_file) as input_zip:
895 # pylint: disable=protected-access
896 payload_offset, metadata_total = (
897 property_files._GetPayloadMetadataOffsetAndSize(input_zip))
898
899 # Read in the metadata signature directly.
900 with open(output_file, 'rb') as verify_fp:
901 verify_fp.seek(payload_offset + metadata_total - self.SIGNATURE_SIZE)
902 metadata_signature = verify_fp.read(self.SIGNATURE_SIZE)
903
904 # Now we extract the metadata hash via brillo_update_payload script, which
905 # will serve as the oracle result.
906 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
907 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
908 cmd = ['brillo_update_payload', 'hash',
909 '--unsigned_payload', payload.payload_file,
910 '--signature_size', str(self.SIGNATURE_SIZE),
911 '--metadata_hash_file', metadata_sig_file,
912 '--payload_hash_file', payload_sig_file]
913 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
914 stdoutdata, _ = proc.communicate()
915 self.assertEqual(
916 0, proc.returncode,
917 'Failed to run brillo_update_payload: {}'.format(stdoutdata))
918
919 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
920
921 # Finally we can compare the two signatures.
922 with open(signed_metadata_sig_file, 'rb') as verify_fp:
923 self.assertEqual(verify_fp.read(), metadata_signature)
924
925 @staticmethod
926 def _construct_zip_package_withValidPayload(with_metadata=False):
927 # Cannot use _construct_zip_package() since we need a "valid" payload.bin.
928 target_file = construct_target_files()
929 payload = Payload()
930 payload.Generate(target_file)
931
932 payload_signer = PayloadSigner()
933 payload.Sign(payload_signer)
934
935 zip_file = common.MakeTempFile(suffix='.zip')
936 with zipfile.ZipFile(zip_file, 'w') as zip_fp:
937 # 'payload.bin',
938 payload.WriteToZip(zip_fp)
939
940 # Other entries.
941 entries = ['care_map.txt', 'compatibility.zip']
942
943 # Put META-INF/com/android/metadata if needed.
944 if with_metadata:
945 entries.append('META-INF/com/android/metadata')
946
947 for entry in entries:
948 zip_fp.writestr(
949 entry, entry.replace('.', '-').upper(), zipfile.ZIP_STORED)
950
951 return zip_file
952
953 def test_Compute(self):
954 zip_file = self._construct_zip_package_withValidPayload()
955 property_files = AbOtaPropertyFiles()
956 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
957 property_files_string = property_files.Compute(zip_fp)
958
959 tokens = self._parse_property_files_string(property_files_string)
960 # "6" indcludes the four entries above, one metadata entry, and one entry
961 # for payload-metadata.bin.
962 self.assertEqual(6, len(tokens))
963 self._verify_entries(
964 zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
965
966 def test_Finalize(self):
967 zip_file = self._construct_zip_package_withValidPayload(with_metadata=True)
968 property_files = AbOtaPropertyFiles()
969 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
970 # pylint: disable=protected-access
971 raw_metadata = property_files._GetPropertyFilesString(
972 zip_fp, reserve_space=False)
973 property_files_string = property_files.Finalize(zip_fp, len(raw_metadata))
974
975 tokens = self._parse_property_files_string(property_files_string)
976 # "6" indcludes the four entries above, one metadata entry, and one entry
977 # for payload-metadata.bin.
978 self.assertEqual(6, len(tokens))
979 self._verify_entries(
980 zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
981
982 def test_Verify(self):
983 zip_file = self._construct_zip_package_withValidPayload(with_metadata=True)
984 property_files = AbOtaPropertyFiles()
985 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
986 # pylint: disable=protected-access
987 raw_metadata = property_files._GetPropertyFilesString(
988 zip_fp, reserve_space=False)
989
990 property_files.Verify(zip_fp, raw_metadata)
991
992
Tao Baoc0746f42018-02-21 13:17:22 -0800993class NonAbOtaPropertyFilesTest(PropertyFilesTest):
994 """Additional sanity checks specialized for NonAbOtaPropertyFiles."""
995
996 def test_init(self):
997 property_files = NonAbOtaPropertyFiles()
998 self.assertEqual('ota-property-files', property_files.name)
999 self.assertEqual((), property_files.required)
1000 self.assertEqual((), property_files.optional)
1001
1002 def test_Compute(self):
1003 entries = ()
1004 zip_file = self._construct_zip_package(entries)
1005 property_files = NonAbOtaPropertyFiles()
1006 with zipfile.ZipFile(zip_file) as zip_fp:
1007 property_files_string = property_files.Compute(zip_fp)
1008
1009 tokens = self._parse_property_files_string(property_files_string)
1010 self.assertEqual(1, len(tokens))
1011 self._verify_entries(zip_file, tokens, entries)
1012
1013 def test_Finalize(self):
1014 entries = [
1015 'META-INF/com/android/metadata',
1016 ]
1017 zip_file = self._construct_zip_package(entries)
1018 property_files = NonAbOtaPropertyFiles()
1019 with zipfile.ZipFile(zip_file) as zip_fp:
1020 # pylint: disable=protected-access
1021 raw_metadata = property_files._GetPropertyFilesString(
1022 zip_fp, reserve_space=False)
1023 property_files_string = property_files.Finalize(zip_fp, len(raw_metadata))
1024 tokens = self._parse_property_files_string(property_files_string)
1025
1026 self.assertEqual(1, len(tokens))
1027 # 'META-INF/com/android/metadata' will be key'd as 'metadata'.
1028 entries[0] = 'metadata'
1029 self._verify_entries(zip_file, tokens, entries)
1030
1031 def test_Verify(self):
1032 entries = (
1033 'META-INF/com/android/metadata',
1034 )
1035 zip_file = self._construct_zip_package(entries)
1036 property_files = NonAbOtaPropertyFiles()
1037 with zipfile.ZipFile(zip_file) as zip_fp:
1038 # pylint: disable=protected-access
1039 raw_metadata = property_files._GetPropertyFilesString(
1040 zip_fp, reserve_space=False)
1041
1042 property_files.Verify(zip_fp, raw_metadata)
1043
1044
Tao Baofabe0832018-01-17 15:52:28 -08001045class PayloadSignerTest(unittest.TestCase):
1046
1047 SIGFILE = 'sigfile.bin'
1048 SIGNED_SIGFILE = 'signed-sigfile.bin'
1049
1050 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -08001051 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baofabe0832018-01-17 15:52:28 -08001052 self.assertTrue(os.path.exists(self.testdata_dir))
1053
1054 common.OPTIONS.payload_signer = None
1055 common.OPTIONS.payload_signer_args = []
1056 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
1057 common.OPTIONS.key_passwords = {
1058 common.OPTIONS.package_key : None,
1059 }
1060
1061 def tearDown(self):
1062 common.Cleanup()
1063
1064 def _assertFilesEqual(self, file1, file2):
1065 with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:
1066 self.assertEqual(fp1.read(), fp2.read())
1067
1068 def test_init(self):
1069 payload_signer = PayloadSigner()
1070 self.assertEqual('openssl', payload_signer.signer)
1071
1072 def test_init_withPassword(self):
1073 common.OPTIONS.package_key = os.path.join(
1074 self.testdata_dir, 'testkey_with_passwd')
1075 common.OPTIONS.key_passwords = {
1076 common.OPTIONS.package_key : 'foo',
1077 }
1078 payload_signer = PayloadSigner()
1079 self.assertEqual('openssl', payload_signer.signer)
1080
1081 def test_init_withExternalSigner(self):
1082 common.OPTIONS.payload_signer = 'abc'
1083 common.OPTIONS.payload_signer_args = ['arg1', 'arg2']
1084 payload_signer = PayloadSigner()
1085 self.assertEqual('abc', payload_signer.signer)
1086 self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)
1087
1088 def test_Sign(self):
1089 payload_signer = PayloadSigner()
1090 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
1091 signed_file = payload_signer.Sign(input_file)
1092
1093 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
1094 self._assertFilesEqual(verify_file, signed_file)
1095
1096 def test_Sign_withExternalSigner_openssl(self):
1097 """Uses openssl as the external payload signer."""
1098 common.OPTIONS.payload_signer = 'openssl'
1099 common.OPTIONS.payload_signer_args = [
1100 'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',
1101 os.path.join(self.testdata_dir, 'testkey.pk8'),
1102 '-pkeyopt', 'digest:sha256']
1103 payload_signer = PayloadSigner()
1104 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
1105 signed_file = payload_signer.Sign(input_file)
1106
1107 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
1108 self._assertFilesEqual(verify_file, signed_file)
1109
1110 def test_Sign_withExternalSigner_script(self):
1111 """Uses testdata/payload_signer.sh as the external payload signer."""
1112 common.OPTIONS.payload_signer = os.path.join(
1113 self.testdata_dir, 'payload_signer.sh')
1114 common.OPTIONS.payload_signer_args = [
1115 os.path.join(self.testdata_dir, 'testkey.pk8')]
1116 payload_signer = PayloadSigner()
1117 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
1118 signed_file = payload_signer.Sign(input_file)
1119
1120 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
1121 self._assertFilesEqual(verify_file, signed_file)
Tao Baoc7b403a2018-01-30 18:19:04 -08001122
1123
1124class PayloadTest(unittest.TestCase):
1125
1126 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -08001127 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baoc7b403a2018-01-30 18:19:04 -08001128 self.assertTrue(os.path.exists(self.testdata_dir))
1129
1130 common.OPTIONS.wipe_user_data = False
1131 common.OPTIONS.payload_signer = None
1132 common.OPTIONS.payload_signer_args = None
1133 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
1134 common.OPTIONS.key_passwords = {
1135 common.OPTIONS.package_key : None,
1136 }
1137
1138 def tearDown(self):
1139 common.Cleanup()
1140
1141 @staticmethod
Tao Baof7140c02018-01-30 17:09:24 -08001142 def _create_payload_full(secondary=False):
1143 target_file = construct_target_files(secondary)
Tao Bao667ff572018-02-10 00:02:40 -08001144 payload = Payload(secondary)
Tao Baoc7b403a2018-01-30 18:19:04 -08001145 payload.Generate(target_file)
1146 return payload
1147
Tao Baof7140c02018-01-30 17:09:24 -08001148 @staticmethod
1149 def _create_payload_incremental():
1150 target_file = construct_target_files()
1151 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -08001152 payload = Payload()
1153 payload.Generate(target_file, source_file)
1154 return payload
1155
1156 def test_Generate_full(self):
1157 payload = self._create_payload_full()
1158 self.assertTrue(os.path.exists(payload.payload_file))
1159
1160 def test_Generate_incremental(self):
1161 payload = self._create_payload_incremental()
1162 self.assertTrue(os.path.exists(payload.payload_file))
1163
1164 def test_Generate_additionalArgs(self):
Tao Baof7140c02018-01-30 17:09:24 -08001165 target_file = construct_target_files()
1166 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -08001167 payload = Payload()
1168 # This should work the same as calling payload.Generate(target_file,
1169 # source_file).
1170 payload.Generate(
1171 target_file, additional_args=["--source_image", source_file])
1172 self.assertTrue(os.path.exists(payload.payload_file))
1173
1174 def test_Generate_invalidInput(self):
Tao Baof7140c02018-01-30 17:09:24 -08001175 target_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -08001176 common.ZipDelete(target_file, 'IMAGES/vendor.img')
1177 payload = Payload()
1178 self.assertRaises(AssertionError, payload.Generate, target_file)
1179
1180 def test_Sign_full(self):
1181 payload = self._create_payload_full()
1182 payload.Sign(PayloadSigner())
1183
1184 output_file = common.MakeTempFile(suffix='.zip')
1185 with zipfile.ZipFile(output_file, 'w') as output_zip:
1186 payload.WriteToZip(output_zip)
1187
1188 import check_ota_package_signature
1189 check_ota_package_signature.VerifyAbOtaPayload(
1190 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
1191 output_file)
1192
1193 def test_Sign_incremental(self):
1194 payload = self._create_payload_incremental()
1195 payload.Sign(PayloadSigner())
1196
1197 output_file = common.MakeTempFile(suffix='.zip')
1198 with zipfile.ZipFile(output_file, 'w') as output_zip:
1199 payload.WriteToZip(output_zip)
1200
1201 import check_ota_package_signature
1202 check_ota_package_signature.VerifyAbOtaPayload(
1203 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
1204 output_file)
1205
1206 def test_Sign_withDataWipe(self):
1207 common.OPTIONS.wipe_user_data = True
1208 payload = self._create_payload_full()
1209 payload.Sign(PayloadSigner())
1210
1211 with open(payload.payload_properties) as properties_fp:
1212 self.assertIn("POWERWASH=1", properties_fp.read())
1213
Tao Bao667ff572018-02-10 00:02:40 -08001214 def test_Sign_secondary(self):
1215 payload = self._create_payload_full(secondary=True)
1216 payload.Sign(PayloadSigner())
1217
1218 with open(payload.payload_properties) as properties_fp:
1219 self.assertIn("SWITCH_SLOT_ON_REBOOT=0", properties_fp.read())
1220
Tao Baoc7b403a2018-01-30 18:19:04 -08001221 def test_Sign_badSigner(self):
1222 """Tests that signing failure can be captured."""
1223 payload = self._create_payload_full()
1224 payload_signer = PayloadSigner()
1225 payload_signer.signer_args.append('bad-option')
1226 self.assertRaises(AssertionError, payload.Sign, payload_signer)
1227
1228 def test_WriteToZip(self):
1229 payload = self._create_payload_full()
1230 payload.Sign(PayloadSigner())
1231
1232 output_file = common.MakeTempFile(suffix='.zip')
1233 with zipfile.ZipFile(output_file, 'w') as output_zip:
1234 payload.WriteToZip(output_zip)
1235
1236 with zipfile.ZipFile(output_file) as verify_zip:
1237 # First make sure we have the essential entries.
1238 namelist = verify_zip.namelist()
1239 self.assertIn(Payload.PAYLOAD_BIN, namelist)
1240 self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
1241
1242 # Then assert these entries are stored.
1243 for entry_info in verify_zip.infolist():
1244 if entry_info.filename not in (Payload.PAYLOAD_BIN,
1245 Payload.PAYLOAD_PROPERTIES_TXT):
1246 continue
1247 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
1248
1249 def test_WriteToZip_unsignedPayload(self):
1250 """Unsigned payloads should not be allowed to be written to zip."""
1251 payload = self._create_payload_full()
1252
1253 output_file = common.MakeTempFile(suffix='.zip')
1254 with zipfile.ZipFile(output_file, 'w') as output_zip:
1255 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
1256
1257 # Also test with incremental payload.
1258 payload = self._create_payload_incremental()
1259
1260 output_file = common.MakeTempFile(suffix='.zip')
1261 with zipfile.ZipFile(output_file, 'w') as output_zip:
1262 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001263
1264 def test_WriteToZip_secondary(self):
1265 payload = self._create_payload_full(secondary=True)
1266 payload.Sign(PayloadSigner())
1267
1268 output_file = common.MakeTempFile(suffix='.zip')
1269 with zipfile.ZipFile(output_file, 'w') as output_zip:
Tao Bao667ff572018-02-10 00:02:40 -08001270 payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001271
1272 with zipfile.ZipFile(output_file) as verify_zip:
1273 # First make sure we have the essential entries.
1274 namelist = verify_zip.namelist()
1275 self.assertIn(Payload.SECONDARY_PAYLOAD_BIN, namelist)
1276 self.assertIn(Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT, namelist)
1277
1278 # Then assert these entries are stored.
1279 for entry_info in verify_zip.infolist():
1280 if entry_info.filename not in (
1281 Payload.SECONDARY_PAYLOAD_BIN,
1282 Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT):
1283 continue
1284 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)