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