blob: 849ca1db44852be794b9f19c0b81619cff64a0be [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',
Tao Bao35dc2552018-02-01 13:18:00 -0800317 'ro.build.version.sdk' : '27',
318 'ro.build.version.security_patch' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800319 'ro.build.date.utc' : '1500000000',
320 },
321 }
322
323 TEST_SOURCE_INFO_DICT = {
324 'build.prop' : {
325 'ro.product.device' : 'product-device',
326 'ro.build.fingerprint' : 'build-fingerprint-source',
327 'ro.build.version.incremental' : 'build-version-incremental-source',
Tao Bao35dc2552018-02-01 13:18:00 -0800328 'ro.build.version.sdk' : '25',
329 'ro.build.version.security_patch' : '2016-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800330 'ro.build.date.utc' : '1400000000',
331 },
332 }
333
334 def setUp(self):
335 # Reset the global options as in ota_from_target_files.py.
336 common.OPTIONS.incremental_source = None
337 common.OPTIONS.downgrade = False
338 common.OPTIONS.timestamp = False
339 common.OPTIONS.wipe_user_data = False
340
341 def test_GetPackageMetadata_abOta_full(self):
342 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
343 target_info_dict['ab_update'] = 'true'
344 target_info = BuildInfo(target_info_dict, None)
345 metadata = GetPackageMetadata(target_info)
346 self.assertDictEqual(
347 {
348 'ota-type' : 'AB',
349 'ota-required-cache' : '0',
350 'post-build' : 'build-fingerprint-target',
351 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800352 'post-sdk-level' : '27',
353 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800354 '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',
Tao Bao35dc2552018-02-01 13:18:00 -0800372 'post-sdk-level' : '27',
373 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800374 'post-timestamp' : '1500000000',
375 'pre-device' : 'product-device',
376 'pre-build' : 'build-fingerprint-source',
377 'pre-build-incremental' : 'build-version-incremental-source',
378 },
379 metadata)
380
381 def test_GetPackageMetadata_nonAbOta_full(self):
382 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
383 metadata = GetPackageMetadata(target_info)
384 self.assertDictEqual(
385 {
386 'ota-type' : 'BLOCK',
387 'post-build' : 'build-fingerprint-target',
388 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800389 'post-sdk-level' : '27',
390 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800391 'post-timestamp' : '1500000000',
392 'pre-device' : 'product-device',
393 },
394 metadata)
395
396 def test_GetPackageMetadata_nonAbOta_incremental(self):
397 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
398 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
399 common.OPTIONS.incremental_source = ''
400 metadata = GetPackageMetadata(target_info, source_info)
401 self.assertDictEqual(
402 {
403 'ota-type' : 'BLOCK',
404 'post-build' : 'build-fingerprint-target',
405 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800406 'post-sdk-level' : '27',
407 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800408 'post-timestamp' : '1500000000',
409 'pre-device' : 'product-device',
410 'pre-build' : 'build-fingerprint-source',
411 'pre-build-incremental' : 'build-version-incremental-source',
412 },
413 metadata)
414
415 def test_GetPackageMetadata_wipe(self):
416 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
417 common.OPTIONS.wipe_user_data = True
418 metadata = GetPackageMetadata(target_info)
419 self.assertDictEqual(
420 {
421 'ota-type' : 'BLOCK',
422 'ota-wipe' : 'yes',
423 'post-build' : 'build-fingerprint-target',
424 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800425 'post-sdk-level' : '27',
426 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800427 'post-timestamp' : '1500000000',
428 'pre-device' : 'product-device',
429 },
430 metadata)
431
432 @staticmethod
433 def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
434 (target_info['build.prop']['ro.build.date.utc'],
435 source_info['build.prop']['ro.build.date.utc']) = (
436 source_info['build.prop']['ro.build.date.utc'],
437 target_info['build.prop']['ro.build.date.utc'])
438
439 def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
440 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
441 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
442 self._test_GetPackageMetadata_swapBuildTimestamps(
443 target_info_dict, source_info_dict)
444
445 target_info = BuildInfo(target_info_dict, None)
446 source_info = BuildInfo(source_info_dict, None)
447 common.OPTIONS.incremental_source = ''
448 self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
449 source_info)
450
451 def test_GetPackageMetadata_downgrade(self):
452 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
453 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
454 self._test_GetPackageMetadata_swapBuildTimestamps(
455 target_info_dict, source_info_dict)
456
457 target_info = BuildInfo(target_info_dict, None)
458 source_info = BuildInfo(source_info_dict, None)
459 common.OPTIONS.incremental_source = ''
460 common.OPTIONS.downgrade = True
461 common.OPTIONS.wipe_user_data = True
462 metadata = GetPackageMetadata(target_info, source_info)
463 self.assertDictEqual(
464 {
465 'ota-downgrade' : 'yes',
466 'ota-type' : 'BLOCK',
467 'ota-wipe' : 'yes',
468 'post-build' : 'build-fingerprint-target',
469 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800470 'post-sdk-level' : '27',
471 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800472 'pre-device' : 'product-device',
473 'pre-build' : 'build-fingerprint-source',
474 'pre-build-incremental' : 'build-version-incremental-source',
475 },
476 metadata)
477
478 def test_GetPackageMetadata_overrideTimestamp(self):
479 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
480 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
481 self._test_GetPackageMetadata_swapBuildTimestamps(
482 target_info_dict, source_info_dict)
483
484 target_info = BuildInfo(target_info_dict, None)
485 source_info = BuildInfo(source_info_dict, None)
486 common.OPTIONS.incremental_source = ''
487 common.OPTIONS.timestamp = True
488 metadata = GetPackageMetadata(target_info, source_info)
489 self.assertDictEqual(
490 {
491 'ota-type' : 'BLOCK',
492 'post-build' : 'build-fingerprint-target',
493 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800494 'post-sdk-level' : '27',
495 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800496 'post-timestamp' : '1500000001',
497 'pre-device' : 'product-device',
498 'pre-build' : 'build-fingerprint-source',
499 'pre-build-incremental' : 'build-version-incremental-source',
500 },
501 metadata)
Tao Baofabe0832018-01-17 15:52:28 -0800502
503
504class PayloadSignerTest(unittest.TestCase):
505
506 SIGFILE = 'sigfile.bin'
507 SIGNED_SIGFILE = 'signed-sigfile.bin'
508
509 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -0800510 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baofabe0832018-01-17 15:52:28 -0800511 self.assertTrue(os.path.exists(self.testdata_dir))
512
513 common.OPTIONS.payload_signer = None
514 common.OPTIONS.payload_signer_args = []
515 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
516 common.OPTIONS.key_passwords = {
517 common.OPTIONS.package_key : None,
518 }
519
520 def tearDown(self):
521 common.Cleanup()
522
523 def _assertFilesEqual(self, file1, file2):
524 with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:
525 self.assertEqual(fp1.read(), fp2.read())
526
527 def test_init(self):
528 payload_signer = PayloadSigner()
529 self.assertEqual('openssl', payload_signer.signer)
530
531 def test_init_withPassword(self):
532 common.OPTIONS.package_key = os.path.join(
533 self.testdata_dir, 'testkey_with_passwd')
534 common.OPTIONS.key_passwords = {
535 common.OPTIONS.package_key : 'foo',
536 }
537 payload_signer = PayloadSigner()
538 self.assertEqual('openssl', payload_signer.signer)
539
540 def test_init_withExternalSigner(self):
541 common.OPTIONS.payload_signer = 'abc'
542 common.OPTIONS.payload_signer_args = ['arg1', 'arg2']
543 payload_signer = PayloadSigner()
544 self.assertEqual('abc', payload_signer.signer)
545 self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)
546
547 def test_Sign(self):
548 payload_signer = PayloadSigner()
549 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
550 signed_file = payload_signer.Sign(input_file)
551
552 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
553 self._assertFilesEqual(verify_file, signed_file)
554
555 def test_Sign_withExternalSigner_openssl(self):
556 """Uses openssl as the external payload signer."""
557 common.OPTIONS.payload_signer = 'openssl'
558 common.OPTIONS.payload_signer_args = [
559 'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',
560 os.path.join(self.testdata_dir, 'testkey.pk8'),
561 '-pkeyopt', 'digest:sha256']
562 payload_signer = PayloadSigner()
563 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
564 signed_file = payload_signer.Sign(input_file)
565
566 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
567 self._assertFilesEqual(verify_file, signed_file)
568
569 def test_Sign_withExternalSigner_script(self):
570 """Uses testdata/payload_signer.sh as the external payload signer."""
571 common.OPTIONS.payload_signer = os.path.join(
572 self.testdata_dir, 'payload_signer.sh')
573 common.OPTIONS.payload_signer_args = [
574 os.path.join(self.testdata_dir, 'testkey.pk8')]
575 payload_signer = PayloadSigner()
576 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
577 signed_file = payload_signer.Sign(input_file)
578
579 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
580 self._assertFilesEqual(verify_file, signed_file)
Tao Baoc7b403a2018-01-30 18:19:04 -0800581
582
583class PayloadTest(unittest.TestCase):
584
585 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -0800586 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baoc7b403a2018-01-30 18:19:04 -0800587 self.assertTrue(os.path.exists(self.testdata_dir))
588
589 common.OPTIONS.wipe_user_data = False
590 common.OPTIONS.payload_signer = None
591 common.OPTIONS.payload_signer_args = None
592 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
593 common.OPTIONS.key_passwords = {
594 common.OPTIONS.package_key : None,
595 }
596
597 def tearDown(self):
598 common.Cleanup()
599
600 @staticmethod
601 def _construct_target_files():
602 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
603 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
604 # META/update_engine_config.txt
605 target_files_zip.writestr(
606 'META/update_engine_config.txt',
607 "PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
608
609 # META/ab_partitions.txt
610 ab_partitions = ['boot', 'system', 'vendor']
611 target_files_zip.writestr(
612 'META/ab_partitions.txt',
613 '\n'.join(ab_partitions))
614
615 # Create dummy images for each of them.
616 for partition in ab_partitions:
617 target_files_zip.writestr('IMAGES/' + partition + '.img',
618 os.urandom(len(partition)))
619
620 return target_files
621
622 def _create_payload_full(self):
623 target_file = self._construct_target_files()
624 payload = Payload()
625 payload.Generate(target_file)
626 return payload
627
628 def _create_payload_incremental(self):
629 target_file = self._construct_target_files()
630 source_file = self._construct_target_files()
631 payload = Payload()
632 payload.Generate(target_file, source_file)
633 return payload
634
635 def test_Generate_full(self):
636 payload = self._create_payload_full()
637 self.assertTrue(os.path.exists(payload.payload_file))
638
639 def test_Generate_incremental(self):
640 payload = self._create_payload_incremental()
641 self.assertTrue(os.path.exists(payload.payload_file))
642
643 def test_Generate_additionalArgs(self):
644 target_file = self._construct_target_files()
645 source_file = self._construct_target_files()
646 payload = Payload()
647 # This should work the same as calling payload.Generate(target_file,
648 # source_file).
649 payload.Generate(
650 target_file, additional_args=["--source_image", source_file])
651 self.assertTrue(os.path.exists(payload.payload_file))
652
653 def test_Generate_invalidInput(self):
654 target_file = self._construct_target_files()
655 common.ZipDelete(target_file, 'IMAGES/vendor.img')
656 payload = Payload()
657 self.assertRaises(AssertionError, payload.Generate, target_file)
658
659 def test_Sign_full(self):
660 payload = self._create_payload_full()
661 payload.Sign(PayloadSigner())
662
663 output_file = common.MakeTempFile(suffix='.zip')
664 with zipfile.ZipFile(output_file, 'w') as output_zip:
665 payload.WriteToZip(output_zip)
666
667 import check_ota_package_signature
668 check_ota_package_signature.VerifyAbOtaPayload(
669 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
670 output_file)
671
672 def test_Sign_incremental(self):
673 payload = self._create_payload_incremental()
674 payload.Sign(PayloadSigner())
675
676 output_file = common.MakeTempFile(suffix='.zip')
677 with zipfile.ZipFile(output_file, 'w') as output_zip:
678 payload.WriteToZip(output_zip)
679
680 import check_ota_package_signature
681 check_ota_package_signature.VerifyAbOtaPayload(
682 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
683 output_file)
684
685 def test_Sign_withDataWipe(self):
686 common.OPTIONS.wipe_user_data = True
687 payload = self._create_payload_full()
688 payload.Sign(PayloadSigner())
689
690 with open(payload.payload_properties) as properties_fp:
691 self.assertIn("POWERWASH=1", properties_fp.read())
692
693 def test_Sign_badSigner(self):
694 """Tests that signing failure can be captured."""
695 payload = self._create_payload_full()
696 payload_signer = PayloadSigner()
697 payload_signer.signer_args.append('bad-option')
698 self.assertRaises(AssertionError, payload.Sign, payload_signer)
699
700 def test_WriteToZip(self):
701 payload = self._create_payload_full()
702 payload.Sign(PayloadSigner())
703
704 output_file = common.MakeTempFile(suffix='.zip')
705 with zipfile.ZipFile(output_file, 'w') as output_zip:
706 payload.WriteToZip(output_zip)
707
708 with zipfile.ZipFile(output_file) as verify_zip:
709 # First make sure we have the essential entries.
710 namelist = verify_zip.namelist()
711 self.assertIn(Payload.PAYLOAD_BIN, namelist)
712 self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
713
714 # Then assert these entries are stored.
715 for entry_info in verify_zip.infolist():
716 if entry_info.filename not in (Payload.PAYLOAD_BIN,
717 Payload.PAYLOAD_PROPERTIES_TXT):
718 continue
719 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
720
721 def test_WriteToZip_unsignedPayload(self):
722 """Unsigned payloads should not be allowed to be written to zip."""
723 payload = self._create_payload_full()
724
725 output_file = common.MakeTempFile(suffix='.zip')
726 with zipfile.ZipFile(output_file, 'w') as output_zip:
727 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
728
729 # Also test with incremental payload.
730 payload = self._create_payload_incremental()
731
732 output_file = common.MakeTempFile(suffix='.zip')
733 with zipfile.ZipFile(output_file, 'w') as output_zip:
734 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)