blob: 103d4b6441dd98653b6e635c38c548ade7324b29 [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 Bao481bab82017-12-21 11:23:09 -080020import unittest
Tao Baoc7b403a2018-01-30 18:19:04 -080021import zipfile
Tao Bao481bab82017-12-21 11:23:09 -080022
23import common
24from ota_from_target_files import (
Tao Baoc7b403a2018-01-30 18:19:04 -080025 _LoadOemDicts, BuildInfo, GetPackageMetadata, Payload, PayloadSigner,
Tao Baofabe0832018-01-17 15:52:28 -080026 WriteFingerprintAssertion)
27
28
29def get_testdata_dir():
30 """Returns the testdata dir, in relative to the script dir."""
31 # The script dir is the one we want, which could be different from pwd.
32 current_dir = os.path.dirname(os.path.realpath(__file__))
33 return os.path.join(current_dir, 'testdata')
Tao Bao481bab82017-12-21 11:23:09 -080034
35
36class MockScriptWriter(object):
37 """A class that mocks edify_generator.EdifyGenerator.
38
39 It simply pushes the incoming arguments onto script stack, which is to assert
40 the calls to EdifyGenerator functions.
41 """
42
43 def __init__(self):
44 self.script = []
45
46 def Mount(self, *args):
47 self.script.append(('Mount',) + args)
48
49 def AssertDevice(self, *args):
50 self.script.append(('AssertDevice',) + args)
51
52 def AssertOemProperty(self, *args):
53 self.script.append(('AssertOemProperty',) + args)
54
55 def AssertFingerprintOrThumbprint(self, *args):
56 self.script.append(('AssertFingerprintOrThumbprint',) + args)
57
58 def AssertSomeFingerprint(self, *args):
59 self.script.append(('AssertSomeFingerprint',) + args)
60
61 def AssertSomeThumbprint(self, *args):
62 self.script.append(('AssertSomeThumbprint',) + args)
63
64
65class BuildInfoTest(unittest.TestCase):
66
67 TEST_INFO_DICT = {
68 'build.prop' : {
69 'ro.product.device' : 'product-device',
70 'ro.product.name' : 'product-name',
71 'ro.build.fingerprint' : 'build-fingerprint',
72 'ro.build.foo' : 'build-foo',
73 },
74 'vendor.build.prop' : {
75 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
76 },
77 'property1' : 'value1',
78 'property2' : 4096,
79 }
80
81 TEST_INFO_DICT_USES_OEM_PROPS = {
82 'build.prop' : {
83 'ro.product.name' : 'product-name',
84 'ro.build.thumbprint' : 'build-thumbprint',
85 'ro.build.bar' : 'build-bar',
86 },
87 'vendor.build.prop' : {
88 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
89 },
90 'property1' : 'value1',
91 'property2' : 4096,
92 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
93 }
94
95 TEST_OEM_DICTS = [
96 {
97 'ro.product.brand' : 'brand1',
98 'ro.product.device' : 'device1',
99 },
100 {
101 'ro.product.brand' : 'brand2',
102 'ro.product.device' : 'device2',
103 },
104 {
105 'ro.product.brand' : 'brand3',
106 'ro.product.device' : 'device3',
107 },
108 ]
109
110 def test_init(self):
111 target_info = BuildInfo(self.TEST_INFO_DICT, None)
112 self.assertEqual('product-device', target_info.device)
113 self.assertEqual('build-fingerprint', target_info.fingerprint)
114 self.assertFalse(target_info.is_ab)
115 self.assertIsNone(target_info.oem_props)
116
117 def test_init_with_oem_props(self):
118 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
119 self.TEST_OEM_DICTS)
120 self.assertEqual('device1', target_info.device)
121 self.assertEqual('brand1/product-name/device1:build-thumbprint',
122 target_info.fingerprint)
123
124 # Swap the order in oem_dicts, which would lead to different BuildInfo.
125 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
126 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
127 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, oem_dicts)
128 self.assertEqual('device3', target_info.device)
129 self.assertEqual('brand3/product-name/device3:build-thumbprint',
130 target_info.fingerprint)
131
132 # Missing oem_dict should be rejected.
133 self.assertRaises(AssertionError, BuildInfo,
134 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
135
136 def test___getitem__(self):
137 target_info = BuildInfo(self.TEST_INFO_DICT, None)
138 self.assertEqual('value1', target_info['property1'])
139 self.assertEqual(4096, target_info['property2'])
140 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
141
142 def test___getitem__with_oem_props(self):
143 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
144 self.TEST_OEM_DICTS)
145 self.assertEqual('value1', target_info['property1'])
146 self.assertEqual(4096, target_info['property2'])
147 self.assertRaises(KeyError,
148 lambda: target_info['build.prop']['ro.build.foo'])
149
150 def test_get(self):
151 target_info = BuildInfo(self.TEST_INFO_DICT, None)
152 self.assertEqual('value1', target_info.get('property1'))
153 self.assertEqual(4096, target_info.get('property2'))
154 self.assertEqual(4096, target_info.get('property2', 1024))
155 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
156 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
157
158 def test_get_with_oem_props(self):
159 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
160 self.TEST_OEM_DICTS)
161 self.assertEqual('value1', target_info.get('property1'))
162 self.assertEqual(4096, target_info.get('property2'))
163 self.assertEqual(4096, target_info.get('property2', 1024))
164 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
165 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
166 self.assertRaises(KeyError,
167 lambda: target_info.get('build.prop')['ro.build.foo'])
168
169 def test_GetBuildProp(self):
170 target_info = BuildInfo(self.TEST_INFO_DICT, None)
171 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
172 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
173 'ro.build.nonexistent')
174
175 def test_GetBuildProp_with_oem_props(self):
176 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
177 self.TEST_OEM_DICTS)
178 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
179 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
180 'ro.build.nonexistent')
181
182 def test_GetVendorBuildProp(self):
183 target_info = BuildInfo(self.TEST_INFO_DICT, None)
184 self.assertEqual('vendor-build-fingerprint',
185 target_info.GetVendorBuildProp(
186 'ro.vendor.build.fingerprint'))
187 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
188 'ro.build.nonexistent')
189
190 def test_GetVendorBuildProp_with_oem_props(self):
191 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
192 self.TEST_OEM_DICTS)
193 self.assertEqual('vendor-build-fingerprint',
194 target_info.GetVendorBuildProp(
195 'ro.vendor.build.fingerprint'))
196 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
197 'ro.build.nonexistent')
198
199 def test_WriteMountOemScript(self):
200 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
201 self.TEST_OEM_DICTS)
202 script_writer = MockScriptWriter()
203 target_info.WriteMountOemScript(script_writer)
204 self.assertEqual([('Mount', '/oem', None)], script_writer.script)
205
206 def test_WriteDeviceAssertions(self):
207 target_info = BuildInfo(self.TEST_INFO_DICT, None)
208 script_writer = MockScriptWriter()
209 target_info.WriteDeviceAssertions(script_writer, False)
210 self.assertEqual([('AssertDevice', 'product-device')], script_writer.script)
211
212 def test_WriteDeviceAssertions_with_oem_props(self):
213 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
214 self.TEST_OEM_DICTS)
215 script_writer = MockScriptWriter()
216 target_info.WriteDeviceAssertions(script_writer, False)
217 self.assertEqual(
218 [
219 ('AssertOemProperty', 'ro.product.device',
220 ['device1', 'device2', 'device3'], False),
221 ('AssertOemProperty', 'ro.product.brand',
222 ['brand1', 'brand2', 'brand3'], False),
223 ],
224 script_writer.script)
225
226 def test_WriteFingerprintAssertion_without_oem_props(self):
227 target_info = BuildInfo(self.TEST_INFO_DICT, None)
228 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT)
229 source_info_dict['build.prop']['ro.build.fingerprint'] = (
230 'source-build-fingerprint')
231 source_info = BuildInfo(source_info_dict, None)
232
233 script_writer = MockScriptWriter()
234 WriteFingerprintAssertion(script_writer, target_info, source_info)
235 self.assertEqual(
236 [('AssertSomeFingerprint', 'source-build-fingerprint',
237 'build-fingerprint')],
238 script_writer.script)
239
240 def test_WriteFingerprintAssertion_with_source_oem_props(self):
241 target_info = BuildInfo(self.TEST_INFO_DICT, None)
242 source_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
243 self.TEST_OEM_DICTS)
244
245 script_writer = MockScriptWriter()
246 WriteFingerprintAssertion(script_writer, target_info, source_info)
247 self.assertEqual(
248 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
249 'build-thumbprint')],
250 script_writer.script)
251
252 def test_WriteFingerprintAssertion_with_target_oem_props(self):
253 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
254 self.TEST_OEM_DICTS)
255 source_info = BuildInfo(self.TEST_INFO_DICT, None)
256
257 script_writer = MockScriptWriter()
258 WriteFingerprintAssertion(script_writer, target_info, source_info)
259 self.assertEqual(
260 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
261 'build-thumbprint')],
262 script_writer.script)
263
264 def test_WriteFingerprintAssertion_with_both_oem_props(self):
265 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
266 self.TEST_OEM_DICTS)
267 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
268 source_info_dict['build.prop']['ro.build.thumbprint'] = (
269 'source-build-thumbprint')
270 source_info = BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
271
272 script_writer = MockScriptWriter()
273 WriteFingerprintAssertion(script_writer, target_info, source_info)
274 self.assertEqual(
275 [('AssertSomeThumbprint', 'build-thumbprint',
276 'source-build-thumbprint')],
277 script_writer.script)
278
279
280class LoadOemDictsTest(unittest.TestCase):
281
282 def tearDown(self):
283 common.Cleanup()
284
285 def test_NoneDict(self):
286 self.assertIsNone(_LoadOemDicts(None))
287
288 def test_SingleDict(self):
289 dict_file = common.MakeTempFile()
290 with open(dict_file, 'w') as dict_fp:
291 dict_fp.write('abc=1\ndef=2\nxyz=foo\na.b.c=bar\n')
292
293 oem_dicts = _LoadOemDicts([dict_file])
294 self.assertEqual(1, len(oem_dicts))
295 self.assertEqual('foo', oem_dicts[0]['xyz'])
296 self.assertEqual('bar', oem_dicts[0]['a.b.c'])
297
298 def test_MultipleDicts(self):
299 oem_source = []
300 for i in range(3):
301 dict_file = common.MakeTempFile()
302 with open(dict_file, 'w') as dict_fp:
303 dict_fp.write(
304 'ro.build.index={}\ndef=2\nxyz=foo\na.b.c=bar\n'.format(i))
305 oem_source.append(dict_file)
306
307 oem_dicts = _LoadOemDicts(oem_source)
308 self.assertEqual(3, len(oem_dicts))
309 for i, oem_dict in enumerate(oem_dicts):
310 self.assertEqual('2', oem_dict['def'])
311 self.assertEqual('foo', oem_dict['xyz'])
312 self.assertEqual('bar', oem_dict['a.b.c'])
313 self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])
Tao Baodf3a48b2018-01-10 16:30:43 -0800314
315
316class OtaFromTargetFilesTest(unittest.TestCase):
317
318 TEST_TARGET_INFO_DICT = {
319 'build.prop' : {
320 'ro.product.device' : 'product-device',
321 'ro.build.fingerprint' : 'build-fingerprint-target',
322 'ro.build.version.incremental' : 'build-version-incremental-target',
323 'ro.build.date.utc' : '1500000000',
324 },
325 }
326
327 TEST_SOURCE_INFO_DICT = {
328 'build.prop' : {
329 'ro.product.device' : 'product-device',
330 'ro.build.fingerprint' : 'build-fingerprint-source',
331 'ro.build.version.incremental' : 'build-version-incremental-source',
332 'ro.build.date.utc' : '1400000000',
333 },
334 }
335
336 def setUp(self):
337 # Reset the global options as in ota_from_target_files.py.
338 common.OPTIONS.incremental_source = None
339 common.OPTIONS.downgrade = False
340 common.OPTIONS.timestamp = False
341 common.OPTIONS.wipe_user_data = False
342
343 def test_GetPackageMetadata_abOta_full(self):
344 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
345 target_info_dict['ab_update'] = 'true'
346 target_info = BuildInfo(target_info_dict, None)
347 metadata = GetPackageMetadata(target_info)
348 self.assertDictEqual(
349 {
350 'ota-type' : 'AB',
351 'ota-required-cache' : '0',
352 'post-build' : 'build-fingerprint-target',
353 'post-build-incremental' : 'build-version-incremental-target',
354 'post-timestamp' : '1500000000',
355 'pre-device' : 'product-device',
356 },
357 metadata)
358
359 def test_GetPackageMetadata_abOta_incremental(self):
360 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
361 target_info_dict['ab_update'] = 'true'
362 target_info = BuildInfo(target_info_dict, None)
363 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
364 common.OPTIONS.incremental_source = ''
365 metadata = GetPackageMetadata(target_info, source_info)
366 self.assertDictEqual(
367 {
368 'ota-type' : 'AB',
369 'ota-required-cache' : '0',
370 'post-build' : 'build-fingerprint-target',
371 'post-build-incremental' : 'build-version-incremental-target',
372 'post-timestamp' : '1500000000',
373 'pre-device' : 'product-device',
374 'pre-build' : 'build-fingerprint-source',
375 'pre-build-incremental' : 'build-version-incremental-source',
376 },
377 metadata)
378
379 def test_GetPackageMetadata_nonAbOta_full(self):
380 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
381 metadata = GetPackageMetadata(target_info)
382 self.assertDictEqual(
383 {
384 'ota-type' : 'BLOCK',
385 'post-build' : 'build-fingerprint-target',
386 'post-build-incremental' : 'build-version-incremental-target',
387 'post-timestamp' : '1500000000',
388 'pre-device' : 'product-device',
389 },
390 metadata)
391
392 def test_GetPackageMetadata_nonAbOta_incremental(self):
393 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
394 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
395 common.OPTIONS.incremental_source = ''
396 metadata = GetPackageMetadata(target_info, source_info)
397 self.assertDictEqual(
398 {
399 'ota-type' : 'BLOCK',
400 'post-build' : 'build-fingerprint-target',
401 'post-build-incremental' : 'build-version-incremental-target',
402 'post-timestamp' : '1500000000',
403 'pre-device' : 'product-device',
404 'pre-build' : 'build-fingerprint-source',
405 'pre-build-incremental' : 'build-version-incremental-source',
406 },
407 metadata)
408
409 def test_GetPackageMetadata_wipe(self):
410 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
411 common.OPTIONS.wipe_user_data = True
412 metadata = GetPackageMetadata(target_info)
413 self.assertDictEqual(
414 {
415 'ota-type' : 'BLOCK',
416 'ota-wipe' : 'yes',
417 'post-build' : 'build-fingerprint-target',
418 'post-build-incremental' : 'build-version-incremental-target',
419 'post-timestamp' : '1500000000',
420 'pre-device' : 'product-device',
421 },
422 metadata)
423
424 @staticmethod
425 def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
426 (target_info['build.prop']['ro.build.date.utc'],
427 source_info['build.prop']['ro.build.date.utc']) = (
428 source_info['build.prop']['ro.build.date.utc'],
429 target_info['build.prop']['ro.build.date.utc'])
430
431 def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
432 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
433 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
434 self._test_GetPackageMetadata_swapBuildTimestamps(
435 target_info_dict, source_info_dict)
436
437 target_info = BuildInfo(target_info_dict, None)
438 source_info = BuildInfo(source_info_dict, None)
439 common.OPTIONS.incremental_source = ''
440 self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
441 source_info)
442
443 def test_GetPackageMetadata_downgrade(self):
444 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
445 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
446 self._test_GetPackageMetadata_swapBuildTimestamps(
447 target_info_dict, source_info_dict)
448
449 target_info = BuildInfo(target_info_dict, None)
450 source_info = BuildInfo(source_info_dict, None)
451 common.OPTIONS.incremental_source = ''
452 common.OPTIONS.downgrade = True
453 common.OPTIONS.wipe_user_data = True
454 metadata = GetPackageMetadata(target_info, source_info)
455 self.assertDictEqual(
456 {
457 'ota-downgrade' : 'yes',
458 'ota-type' : 'BLOCK',
459 'ota-wipe' : 'yes',
460 'post-build' : 'build-fingerprint-target',
461 'post-build-incremental' : 'build-version-incremental-target',
462 'pre-device' : 'product-device',
463 'pre-build' : 'build-fingerprint-source',
464 'pre-build-incremental' : 'build-version-incremental-source',
465 },
466 metadata)
467
468 def test_GetPackageMetadata_overrideTimestamp(self):
469 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
470 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
471 self._test_GetPackageMetadata_swapBuildTimestamps(
472 target_info_dict, source_info_dict)
473
474 target_info = BuildInfo(target_info_dict, None)
475 source_info = BuildInfo(source_info_dict, None)
476 common.OPTIONS.incremental_source = ''
477 common.OPTIONS.timestamp = True
478 metadata = GetPackageMetadata(target_info, source_info)
479 self.assertDictEqual(
480 {
481 'ota-type' : 'BLOCK',
482 'post-build' : 'build-fingerprint-target',
483 'post-build-incremental' : 'build-version-incremental-target',
484 'post-timestamp' : '1500000001',
485 'pre-device' : 'product-device',
486 'pre-build' : 'build-fingerprint-source',
487 'pre-build-incremental' : 'build-version-incremental-source',
488 },
489 metadata)
Tao Baofabe0832018-01-17 15:52:28 -0800490
491
492class PayloadSignerTest(unittest.TestCase):
493
494 SIGFILE = 'sigfile.bin'
495 SIGNED_SIGFILE = 'signed-sigfile.bin'
496
497 def setUp(self):
498 self.testdata_dir = get_testdata_dir()
499 self.assertTrue(os.path.exists(self.testdata_dir))
500
501 common.OPTIONS.payload_signer = None
502 common.OPTIONS.payload_signer_args = []
503 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
504 common.OPTIONS.key_passwords = {
505 common.OPTIONS.package_key : None,
506 }
507
508 def tearDown(self):
509 common.Cleanup()
510
511 def _assertFilesEqual(self, file1, file2):
512 with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:
513 self.assertEqual(fp1.read(), fp2.read())
514
515 def test_init(self):
516 payload_signer = PayloadSigner()
517 self.assertEqual('openssl', payload_signer.signer)
518
519 def test_init_withPassword(self):
520 common.OPTIONS.package_key = os.path.join(
521 self.testdata_dir, 'testkey_with_passwd')
522 common.OPTIONS.key_passwords = {
523 common.OPTIONS.package_key : 'foo',
524 }
525 payload_signer = PayloadSigner()
526 self.assertEqual('openssl', payload_signer.signer)
527
528 def test_init_withExternalSigner(self):
529 common.OPTIONS.payload_signer = 'abc'
530 common.OPTIONS.payload_signer_args = ['arg1', 'arg2']
531 payload_signer = PayloadSigner()
532 self.assertEqual('abc', payload_signer.signer)
533 self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)
534
535 def test_Sign(self):
536 payload_signer = PayloadSigner()
537 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
538 signed_file = payload_signer.Sign(input_file)
539
540 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
541 self._assertFilesEqual(verify_file, signed_file)
542
543 def test_Sign_withExternalSigner_openssl(self):
544 """Uses openssl as the external payload signer."""
545 common.OPTIONS.payload_signer = 'openssl'
546 common.OPTIONS.payload_signer_args = [
547 'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',
548 os.path.join(self.testdata_dir, 'testkey.pk8'),
549 '-pkeyopt', 'digest:sha256']
550 payload_signer = PayloadSigner()
551 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
552 signed_file = payload_signer.Sign(input_file)
553
554 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
555 self._assertFilesEqual(verify_file, signed_file)
556
557 def test_Sign_withExternalSigner_script(self):
558 """Uses testdata/payload_signer.sh as the external payload signer."""
559 common.OPTIONS.payload_signer = os.path.join(
560 self.testdata_dir, 'payload_signer.sh')
561 common.OPTIONS.payload_signer_args = [
562 os.path.join(self.testdata_dir, 'testkey.pk8')]
563 payload_signer = PayloadSigner()
564 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
565 signed_file = payload_signer.Sign(input_file)
566
567 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
568 self._assertFilesEqual(verify_file, signed_file)
Tao Baoc7b403a2018-01-30 18:19:04 -0800569
570
571class PayloadTest(unittest.TestCase):
572
573 def setUp(self):
574 self.testdata_dir = get_testdata_dir()
575 self.assertTrue(os.path.exists(self.testdata_dir))
576
577 common.OPTIONS.wipe_user_data = False
578 common.OPTIONS.payload_signer = None
579 common.OPTIONS.payload_signer_args = None
580 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
581 common.OPTIONS.key_passwords = {
582 common.OPTIONS.package_key : None,
583 }
584
585 def tearDown(self):
586 common.Cleanup()
587
588 @staticmethod
589 def _construct_target_files():
590 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
591 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
592 # META/update_engine_config.txt
593 target_files_zip.writestr(
594 'META/update_engine_config.txt',
595 "PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
596
597 # META/ab_partitions.txt
598 ab_partitions = ['boot', 'system', 'vendor']
599 target_files_zip.writestr(
600 'META/ab_partitions.txt',
601 '\n'.join(ab_partitions))
602
603 # Create dummy images for each of them.
604 for partition in ab_partitions:
605 target_files_zip.writestr('IMAGES/' + partition + '.img',
606 os.urandom(len(partition)))
607
608 return target_files
609
610 def _create_payload_full(self):
611 target_file = self._construct_target_files()
612 payload = Payload()
613 payload.Generate(target_file)
614 return payload
615
616 def _create_payload_incremental(self):
617 target_file = self._construct_target_files()
618 source_file = self._construct_target_files()
619 payload = Payload()
620 payload.Generate(target_file, source_file)
621 return payload
622
623 def test_Generate_full(self):
624 payload = self._create_payload_full()
625 self.assertTrue(os.path.exists(payload.payload_file))
626
627 def test_Generate_incremental(self):
628 payload = self._create_payload_incremental()
629 self.assertTrue(os.path.exists(payload.payload_file))
630
631 def test_Generate_additionalArgs(self):
632 target_file = self._construct_target_files()
633 source_file = self._construct_target_files()
634 payload = Payload()
635 # This should work the same as calling payload.Generate(target_file,
636 # source_file).
637 payload.Generate(
638 target_file, additional_args=["--source_image", source_file])
639 self.assertTrue(os.path.exists(payload.payload_file))
640
641 def test_Generate_invalidInput(self):
642 target_file = self._construct_target_files()
643 common.ZipDelete(target_file, 'IMAGES/vendor.img')
644 payload = Payload()
645 self.assertRaises(AssertionError, payload.Generate, target_file)
646
647 def test_Sign_full(self):
648 payload = self._create_payload_full()
649 payload.Sign(PayloadSigner())
650
651 output_file = common.MakeTempFile(suffix='.zip')
652 with zipfile.ZipFile(output_file, 'w') as output_zip:
653 payload.WriteToZip(output_zip)
654
655 import check_ota_package_signature
656 check_ota_package_signature.VerifyAbOtaPayload(
657 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
658 output_file)
659
660 def test_Sign_incremental(self):
661 payload = self._create_payload_incremental()
662 payload.Sign(PayloadSigner())
663
664 output_file = common.MakeTempFile(suffix='.zip')
665 with zipfile.ZipFile(output_file, 'w') as output_zip:
666 payload.WriteToZip(output_zip)
667
668 import check_ota_package_signature
669 check_ota_package_signature.VerifyAbOtaPayload(
670 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
671 output_file)
672
673 def test_Sign_withDataWipe(self):
674 common.OPTIONS.wipe_user_data = True
675 payload = self._create_payload_full()
676 payload.Sign(PayloadSigner())
677
678 with open(payload.payload_properties) as properties_fp:
679 self.assertIn("POWERWASH=1", properties_fp.read())
680
681 def test_Sign_badSigner(self):
682 """Tests that signing failure can be captured."""
683 payload = self._create_payload_full()
684 payload_signer = PayloadSigner()
685 payload_signer.signer_args.append('bad-option')
686 self.assertRaises(AssertionError, payload.Sign, payload_signer)
687
688 def test_WriteToZip(self):
689 payload = self._create_payload_full()
690 payload.Sign(PayloadSigner())
691
692 output_file = common.MakeTempFile(suffix='.zip')
693 with zipfile.ZipFile(output_file, 'w') as output_zip:
694 payload.WriteToZip(output_zip)
695
696 with zipfile.ZipFile(output_file) as verify_zip:
697 # First make sure we have the essential entries.
698 namelist = verify_zip.namelist()
699 self.assertIn(Payload.PAYLOAD_BIN, namelist)
700 self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
701
702 # Then assert these entries are stored.
703 for entry_info in verify_zip.infolist():
704 if entry_info.filename not in (Payload.PAYLOAD_BIN,
705 Payload.PAYLOAD_PROPERTIES_TXT):
706 continue
707 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
708
709 def test_WriteToZip_unsignedPayload(self):
710 """Unsigned payloads should not be allowed to be written to zip."""
711 payload = self._create_payload_full()
712
713 output_file = common.MakeTempFile(suffix='.zip')
714 with zipfile.ZipFile(output_file, 'w') as output_zip:
715 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
716
717 # Also test with incremental payload.
718 payload = self._create_payload_incremental()
719
720 output_file = common.MakeTempFile(suffix='.zip')
721 with zipfile.ZipFile(output_file, 'w') as output_zip:
722 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)