• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#
2# Copyright (C) 2017 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 base64
18import io
19import os.path
20import zipfile
21
22import common
23import test_utils
24from sign_target_files_apks import (
25    CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ReadApexKeysInfo,
26    ReplaceCerts, ReplaceGkiSigningKey, ReplaceVerityKeyId, RewriteAvbProps,
27    RewriteProps, WriteOtacerts)
28
29
30class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
31
32  MAC_PERMISSIONS_XML = """<?xml version="1.0" encoding="iso-8859-1"?>
33<policy>
34  <signer signature="{}"><seinfo value="platform"/></signer>
35  <signer signature="{}"><seinfo value="media"/></signer>
36</policy>"""
37
38  # Note that we test one apex with the partition tag, and another without to
39  # make sure that new OTA tools can process an older target files package that
40  # does not include the partition tag.
41
42  # pylint: disable=line-too-long
43  APEX_KEYS_TXT = """name="apex.apexd_test.apex" public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package.avbpubkey" private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem" container_certificate="build/make/target/product/security/testkey.x509.pem" container_private_key="build/make/target/product/security/testkey.pk8" partition="system"
44name="apex.apexd_test_different_app.apex" public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey" private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem" container_certificate="build/make/target/product/security/testkey.x509.pem" container_private_key="build/make/target/product/security/testkey.pk8"
45"""
46
47  def setUp(self):
48    self.testdata_dir = test_utils.get_testdata_dir()
49
50  def test_EditTags(self):
51    self.assertEqual(EditTags('dev-keys'), ('release-keys'))
52    self.assertEqual(EditTags('test-keys'), ('release-keys'))
53
54    # Multiple tags.
55    self.assertEqual(EditTags('abc,dev-keys,xyz'), ('abc,release-keys,xyz'))
56
57    # Tags are sorted.
58    self.assertEqual(EditTags('xyz,abc,dev-keys,xyz'), ('abc,release-keys,xyz'))
59
60  def test_RewriteAvbProps(self):
61    misc_info = {
62      'avb_boot_add_hash_footer_args':
63          ('--prop com.android.build.boot.os_version:R '
64           '--prop com.android.build.boot.security_patch:2019-09-05'),
65      'avb_init_boot_add_hash_footer_args':
66          ('--prop com.android.build.boot.os_version:R '
67           '--prop com.android.build.boot.security_patch:2019-09-05'),
68      'avb_system_add_hashtree_footer_args':
69          ('--prop com.android.build.system.os_version:R '
70           '--prop com.android.build.system.security_patch:2019-09-05 '
71           '--prop com.android.build.system.fingerprint:'
72           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/test-keys'),
73      'avb_vendor_add_hashtree_footer_args':
74          ('--prop com.android.build.vendor.os_version:R '
75           '--prop com.android.build.vendor.security_patch:2019-09-05 '
76           '--prop com.android.build.vendor.fingerprint:'
77           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/dev-keys'),
78    }
79    expected_dict = {
80      'avb_boot_add_hash_footer_args':
81          ('--prop com.android.build.boot.os_version:R '
82           '--prop com.android.build.boot.security_patch:2019-09-05'),
83      'avb_init_boot_add_hash_footer_args':
84          ('--prop com.android.build.boot.os_version:R '
85           '--prop com.android.build.boot.security_patch:2019-09-05'),
86      'avb_system_add_hashtree_footer_args':
87          ('--prop com.android.build.system.os_version:R '
88           '--prop com.android.build.system.security_patch:2019-09-05 '
89           '--prop com.android.build.system.fingerprint:'
90           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/release-keys'),
91      'avb_vendor_add_hashtree_footer_args':
92          ('--prop com.android.build.vendor.os_version:R '
93           '--prop com.android.build.vendor.security_patch:2019-09-05 '
94           '--prop com.android.build.vendor.fingerprint:'
95           'Android/aosp_taimen/taimen:R/QT/foo:userdebug/release-keys'),
96    }
97    RewriteAvbProps(misc_info)
98    self.assertDictEqual(expected_dict, misc_info)
99
100  def test_RewriteProps(self):
101    props = (
102        ('', ''),
103        ('ro.build.fingerprint=foo/bar/dev-keys',
104         'ro.build.fingerprint=foo/bar/release-keys'),
105        ('ro.build.thumbprint=foo/bar/dev-keys',
106         'ro.build.thumbprint=foo/bar/release-keys'),
107        ('ro.vendor.build.fingerprint=foo/bar/dev-keys',
108         'ro.vendor.build.fingerprint=foo/bar/release-keys'),
109        ('ro.vendor.build.thumbprint=foo/bar/dev-keys',
110         'ro.vendor.build.thumbprint=foo/bar/release-keys'),
111        ('ro.odm.build.fingerprint=foo/bar/test-keys',
112         'ro.odm.build.fingerprint=foo/bar/release-keys'),
113        ('ro.odm.build.thumbprint=foo/bar/test-keys',
114         'ro.odm.build.thumbprint=foo/bar/release-keys'),
115        ('ro.product.build.fingerprint=foo/bar/dev-keys',
116         'ro.product.build.fingerprint=foo/bar/release-keys'),
117        ('ro.product.build.thumbprint=foo/bar/dev-keys',
118         'ro.product.build.thumbprint=foo/bar/release-keys'),
119        ('ro.system_ext.build.fingerprint=foo/bar/test-keys',
120         'ro.system_ext.build.fingerprint=foo/bar/release-keys'),
121        ('ro.system_ext.build.thumbprint=foo/bar/test-keys',
122         'ro.system_ext.build.thumbprint=foo/bar/release-keys'),
123        ('# comment line 1', '# comment line 1'),
124        ('ro.bootimage.build.fingerprint=foo/bar/dev-keys',
125         'ro.bootimage.build.fingerprint=foo/bar/release-keys'),
126        ('ro.build.description='
127         'sailfish-user 8.0.0 OPR6.170623.012 4283428 dev-keys',
128         'ro.build.description='
129         'sailfish-user 8.0.0 OPR6.170623.012 4283428 release-keys'),
130        ('ro.build.tags=dev-keys', 'ro.build.tags=release-keys'),
131        ('ro.build.tags=test-keys', 'ro.build.tags=release-keys'),
132        ('ro.system.build.tags=dev-keys',
133         'ro.system.build.tags=release-keys'),
134        ('ro.vendor.build.tags=dev-keys',
135         'ro.vendor.build.tags=release-keys'),
136        ('ro.odm.build.tags=dev-keys',
137         'ro.odm.build.tags=release-keys'),
138        ('ro.product.build.tags=dev-keys',
139         'ro.product.build.tags=release-keys'),
140        ('ro.system_ext.build.tags=dev-keys',
141         'ro.system_ext.build.tags=release-keys'),
142        ('# comment line 2', '# comment line 2'),
143        ('ro.build.display.id=OPR6.170623.012 dev-keys',
144         'ro.build.display.id=OPR6.170623.012'),
145        ('# comment line 3', '# comment line 3'),
146    )
147
148    # Assert the case for each individual line.
149    for prop, expected in props:
150      self.assertEqual(expected + '\n', RewriteProps(prop))
151
152    # Concatenate all the input lines.
153    self.assertEqual(
154        '\n'.join([prop[1] for prop in props]) + '\n',
155        RewriteProps('\n'.join([prop[0] for prop in props])))
156
157  def test_ReplaceVerityKeyId(self):
158    BOOT_CMDLINE1 = (
159        "console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 "
160        "androidboot.hardware=marlin user_debug=31 ehci-hcd.park=3 "
161        "lpm_levels.sleep_disabled=1 cma=32M@0-0xffffffff loop.max_part=7 "
162        "buildvariant=userdebug "
163        "veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f\n")
164
165    BOOT_CMDLINE2 = (
166        "console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 "
167        "androidboot.hardware=marlin user_debug=31 ehci-hcd.park=3 "
168        "lpm_levels.sleep_disabled=1 cma=32M@0-0xffffffff loop.max_part=7 "
169        "buildvariant=userdebug "
170        "veritykeyid=id:d24f2590e9abab5cff5f59da4c4f0366e3f43e94\n")
171
172    input_file = common.MakeTempFile(suffix='.zip')
173    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
174      input_zip.writestr('BOOT/cmdline', BOOT_CMDLINE1)
175
176    # Test with the first certificate.
177    cert_file = os.path.join(self.testdata_dir, 'verity.x509.pem')
178
179    output_file = common.MakeTempFile(suffix='.zip')
180    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip, \
181         zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
182      ReplaceVerityKeyId(input_zip, output_zip, cert_file)
183
184    with zipfile.ZipFile(output_file) as output_zip:
185      self.assertEqual(BOOT_CMDLINE1, output_zip.read('BOOT/cmdline').decode())
186
187    # Test with the second certificate.
188    cert_file = os.path.join(self.testdata_dir, 'testkey.x509.pem')
189
190    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip, \
191         zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
192      ReplaceVerityKeyId(input_zip, output_zip, cert_file)
193
194    with zipfile.ZipFile(output_file) as output_zip:
195      self.assertEqual(BOOT_CMDLINE2, output_zip.read('BOOT/cmdline').decode())
196
197  def test_ReplaceVerityKeyId_no_veritykeyid(self):
198    BOOT_CMDLINE = (
199        "console=ttyHSL0,115200,n8 androidboot.hardware=bullhead boot_cpus=0-5 "
200        "lpm_levels.sleep_disabled=1 msm_poweroff.download_mode=0 "
201        "loop.max_part=7\n")
202
203    input_file = common.MakeTempFile(suffix='.zip')
204    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
205      input_zip.writestr('BOOT/cmdline', BOOT_CMDLINE)
206
207    output_file = common.MakeTempFile(suffix='.zip')
208    with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip, \
209         zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
210      ReplaceVerityKeyId(input_zip, output_zip, None)
211
212    with zipfile.ZipFile(output_file) as output_zip:
213      self.assertEqual(BOOT_CMDLINE, output_zip.read('BOOT/cmdline').decode())
214
215  def test_ReplaceCerts(self):
216    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')
217    with open(cert1_path) as cert1_fp:
218      cert1 = cert1_fp.read()
219    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')
220    with open(cert2_path) as cert2_fp:
221      cert2 = cert2_fp.read()
222    cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem')
223    with open(cert3_path) as cert3_fp:
224      cert3 = cert3_fp.read()
225
226    # Replace cert1 with cert3.
227    input_xml = self.MAC_PERMISSIONS_XML.format(
228        base64.b16encode(common.ParseCertificate(cert1)).lower(),
229        base64.b16encode(common.ParseCertificate(cert2)).lower())
230
231    output_xml = self.MAC_PERMISSIONS_XML.format(
232        base64.b16encode(common.ParseCertificate(cert3)).lower(),
233        base64.b16encode(common.ParseCertificate(cert2)).lower())
234
235    common.OPTIONS.key_map = {
236        cert1_path[:-9] : cert3_path[:-9],
237    }
238
239    self.assertEqual(output_xml, ReplaceCerts(input_xml))
240
241  def test_ReplaceCerts_duplicateEntries(self):
242    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')
243    with open(cert1_path) as cert1_fp:
244      cert1 = cert1_fp.read()
245    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')
246    with open(cert2_path) as cert2_fp:
247      cert2 = cert2_fp.read()
248
249    # Replace cert1 with cert2, which leads to duplicate entries.
250    input_xml = self.MAC_PERMISSIONS_XML.format(
251        base64.b16encode(common.ParseCertificate(cert1)).lower(),
252        base64.b16encode(common.ParseCertificate(cert2)).lower())
253
254    common.OPTIONS.key_map = {
255        cert1_path[:-9] : cert2_path[:-9],
256    }
257    self.assertRaises(AssertionError, ReplaceCerts, input_xml)
258
259  def test_ReplaceCerts_skipNonExistentCerts(self):
260    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')
261    with open(cert1_path) as cert1_fp:
262      cert1 = cert1_fp.read()
263    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')
264    with open(cert2_path) as cert2_fp:
265      cert2 = cert2_fp.read()
266    cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem')
267    with open(cert3_path) as cert3_fp:
268      cert3 = cert3_fp.read()
269
270    input_xml = self.MAC_PERMISSIONS_XML.format(
271        base64.b16encode(common.ParseCertificate(cert1)).lower(),
272        base64.b16encode(common.ParseCertificate(cert2)).lower())
273
274    output_xml = self.MAC_PERMISSIONS_XML.format(
275        base64.b16encode(common.ParseCertificate(cert3)).lower(),
276        base64.b16encode(common.ParseCertificate(cert2)).lower())
277
278    common.OPTIONS.key_map = {
279        cert1_path[:-9] : cert3_path[:-9],
280        'non-existent' : cert3_path[:-9],
281        cert2_path[:-9] : 'non-existent',
282    }
283    self.assertEqual(output_xml, ReplaceCerts(input_xml))
284
285  def test_WriteOtacerts(self):
286    certs = [
287        os.path.join(self.testdata_dir, 'platform.x509.pem'),
288        os.path.join(self.testdata_dir, 'media.x509.pem'),
289        os.path.join(self.testdata_dir, 'testkey.x509.pem'),
290    ]
291    entry_name = 'SYSTEM/etc/security/otacerts.zip'
292    output_file = common.MakeTempFile(suffix='.zip')
293    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
294      WriteOtacerts(output_zip, entry_name, certs)
295    with zipfile.ZipFile(output_file) as input_zip:
296      self.assertIn(entry_name, input_zip.namelist())
297      otacerts_file = io.BytesIO(input_zip.read(entry_name))
298      with zipfile.ZipFile(otacerts_file) as otacerts_zip:
299        self.assertEqual(3, len(otacerts_zip.namelist()))
300
301  def test_CheckApkAndApexKeysAvailable(self):
302    input_file = common.MakeTempFile(suffix='.zip')
303    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
304      input_zip.writestr('SYSTEM/app/App1.apk', "App1-content")
305      input_zip.writestr('SYSTEM/app/App2.apk.gz', "App2-content")
306
307    apk_key_map = {
308        'App1.apk' : 'key1',
309        'App2.apk' : 'key2',
310        'App3.apk' : 'key3',
311    }
312    with zipfile.ZipFile(input_file) as input_zip:
313      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})
314      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.gz', {})
315
316      # 'App2.apk.gz' won't be considered as an APK.
317      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})
318      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.xz', {})
319
320      del apk_key_map['App2.apk']
321      self.assertRaises(
322          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
323          '.gz', {})
324
325  def test_CheckApkAndApexKeysAvailable_invalidApexKeys(self):
326    input_file = common.MakeTempFile(suffix='.zip')
327    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
328      input_zip.writestr('SYSTEM/apex/Apex1.apex', "Apex1-content")
329      input_zip.writestr('SYSTEM/apex/Apex2.apex', "Apex2-content")
330
331    apk_key_map = {
332        'Apex1.apex' : 'key1',
333        'Apex2.apex' : 'key2',
334        'Apex3.apex' : 'key3',
335    }
336    apex_keys = {
337        'Apex1.apex' : ('payload-key1', 'container-key1', None),
338        'Apex2.apex' : ('payload-key2', 'container-key2', None),
339    }
340    with zipfile.ZipFile(input_file) as input_zip:
341      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
342
343      # Fine to have both keys as PRESIGNED.
344      apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED', None)
345      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
346
347      # Having only one of them as PRESIGNED is not allowed.
348      apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED', None)
349      self.assertRaises(
350          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
351          None, apex_keys)
352
353      apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1', None)
354      self.assertRaises(
355          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
356          None, apex_keys)
357
358  def test_GetApkFileInfo(self):
359    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
360        "PRODUCT/apps/Chats.apk", None, [])
361    self.assertTrue(is_apk)
362    self.assertFalse(is_compressed)
363    self.assertFalse(should_be_skipped)
364
365    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
366        "PRODUCT/apps/Chats.apk", None, [])
367    self.assertTrue(is_apk)
368    self.assertFalse(is_compressed)
369    self.assertFalse(should_be_skipped)
370
371    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
372        "PRODUCT/apps/Chats.dat", None, [])
373    self.assertFalse(is_apk)
374    self.assertFalse(is_compressed)
375    self.assertFalse(should_be_skipped)
376
377  def test_GetApkFileInfo_withCompressedApks(self):
378    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
379        "PRODUCT/apps/Chats.apk.gz", ".gz", [])
380    self.assertTrue(is_apk)
381    self.assertTrue(is_compressed)
382    self.assertFalse(should_be_skipped)
383
384    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
385        "PRODUCT/apps/Chats.apk.gz", ".xz", [])
386    self.assertFalse(is_apk)
387    self.assertFalse(is_compressed)
388    self.assertFalse(should_be_skipped)
389
390    self.assertRaises(
391        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "", [])
392
393    self.assertRaises(
394        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "apk", [])
395
396  def test_GetApkFileInfo_withSkippedPrefixes(self):
397    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
398        "PRODUCT/preloads/apps/Chats.apk", None, set())
399    self.assertTrue(is_apk)
400    self.assertFalse(is_compressed)
401    self.assertFalse(should_be_skipped)
402
403    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
404        "PRODUCT/preloads/apps/Chats.apk",
405        None,
406        set(["PRODUCT/preloads/"]))
407    self.assertTrue(is_apk)
408    self.assertFalse(is_compressed)
409    self.assertTrue(should_be_skipped)
410
411    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
412        "SYSTEM_OTHER/preloads/apps/Chats.apk",
413        None,
414        set(["SYSTEM/preloads/", "SYSTEM_OTHER/preloads/"]))
415    self.assertTrue(is_apk)
416    self.assertFalse(is_compressed)
417    self.assertTrue(should_be_skipped)
418
419    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
420        "SYSTEM_OTHER/preloads/apps/Chats.apk.gz",
421        ".gz",
422        set(["PRODUCT/prebuilts/", "SYSTEM_OTHER/preloads/"]))
423    self.assertTrue(is_apk)
424    self.assertTrue(is_compressed)
425    self.assertTrue(should_be_skipped)
426
427    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
428        "SYSTEM_OTHER/preloads/apps/Chats.dat",
429        None,
430        set(["SYSTEM_OTHER/preloads/"]))
431    self.assertFalse(is_apk)
432    self.assertFalse(is_compressed)
433    self.assertFalse(should_be_skipped)
434
435  def test_GetApkFileInfo_checkSkippedPrefixesInput(self):
436    # set
437    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
438        "SYSTEM_OTHER/preloads/apps/Chats.apk",
439        None,
440        set(["SYSTEM_OTHER/preloads/"]))
441    self.assertTrue(is_apk)
442    self.assertFalse(is_compressed)
443    self.assertTrue(should_be_skipped)
444
445    # tuple
446    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
447        "SYSTEM_OTHER/preloads/apps/Chats.apk",
448        None,
449        ("SYSTEM_OTHER/preloads/",))
450    self.assertTrue(is_apk)
451    self.assertFalse(is_compressed)
452    self.assertTrue(should_be_skipped)
453
454    # list
455    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
456        "SYSTEM_OTHER/preloads/apps/Chats.apk",
457        None,
458        ["SYSTEM_OTHER/preloads/"])
459    self.assertTrue(is_apk)
460    self.assertFalse(is_compressed)
461    self.assertTrue(should_be_skipped)
462
463    # str is invalid.
464    self.assertRaises(
465        AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
466        None, "SYSTEM_OTHER/preloads/")
467
468    # None is invalid.
469    self.assertRaises(
470        AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
471        None, None)
472
473  def test_ReadApexKeysInfo(self):
474    target_files = common.MakeTempFile(suffix='.zip')
475    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
476      target_files_zip.writestr('META/apexkeys.txt', self.APEX_KEYS_TXT)
477
478    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
479      keys_info = ReadApexKeysInfo(target_files_zip)
480
481    self.assertEqual({
482        'apex.apexd_test.apex': (
483            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
484            'build/make/target/product/security/testkey', None),
485        'apex.apexd_test_different_app.apex': (
486            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
487            'build/make/target/product/security/testkey', None),
488        }, keys_info)
489
490  def test_ReadApexKeysInfo_mismatchingContainerKeys(self):
491    # Mismatching payload public / private keys.
492    apex_keys = self.APEX_KEYS_TXT + (
493        'name="apex.apexd_test_different_app2.apex" '
494        'public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey" '
495        'private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem" '
496        'container_certificate="build/make/target/product/security/testkey.x509.pem" '
497        'container_private_key="build/make/target/product/security/testkey2.pk8" '
498        'partition="system"')
499    target_files = common.MakeTempFile(suffix='.zip')
500    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
501      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
502
503    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
504      self.assertRaises(ValueError, ReadApexKeysInfo, target_files_zip)
505
506  def test_ReadApexKeysInfo_missingPayloadPrivateKey(self):
507    # Invalid lines will be skipped.
508    apex_keys = self.APEX_KEYS_TXT + (
509        'name="apex.apexd_test_different_app2.apex" '
510        'public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey" '
511        'container_certificate="build/make/target/product/security/testkey.x509.pem" '
512        'container_private_key="build/make/target/product/security/testkey.pk8"')
513    target_files = common.MakeTempFile(suffix='.zip')
514    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
515      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
516
517    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
518      keys_info = ReadApexKeysInfo(target_files_zip)
519
520    self.assertEqual({
521        'apex.apexd_test.apex': (
522            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
523            'build/make/target/product/security/testkey', None),
524        'apex.apexd_test_different_app.apex': (
525            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
526            'build/make/target/product/security/testkey', None),
527        }, keys_info)
528
529  def test_ReadApexKeysInfo_missingPayloadPublicKey(self):
530    # Invalid lines will be skipped.
531    apex_keys = self.APEX_KEYS_TXT + (
532        'name="apex.apexd_test_different_app2.apex" '
533        'private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem" '
534        'container_certificate="build/make/target/product/security/testkey.x509.pem" '
535        'container_private_key="build/make/target/product/security/testkey.pk8"')
536    target_files = common.MakeTempFile(suffix='.zip')
537    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
538      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
539
540    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
541      keys_info = ReadApexKeysInfo(target_files_zip)
542
543    self.assertEqual({
544        'apex.apexd_test.apex': (
545            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
546            'build/make/target/product/security/testkey', None),
547        'apex.apexd_test_different_app.apex': (
548            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
549            'build/make/target/product/security/testkey', None),
550        }, keys_info)
551
552  def test_ReadApexKeysInfo_presignedKeys(self):
553    apex_keys = self.APEX_KEYS_TXT + (
554        'name="apex.apexd_test_different_app2.apex" '
555        'private_key="PRESIGNED" '
556        'public_key="PRESIGNED" '
557        'container_certificate="PRESIGNED" '
558        'container_private_key="PRESIGNED"')
559    target_files = common.MakeTempFile(suffix='.zip')
560    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
561      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
562
563    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
564      keys_info = ReadApexKeysInfo(target_files_zip)
565
566    self.assertEqual({
567        'apex.apexd_test.apex': (
568            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
569            'build/make/target/product/security/testkey', None),
570        'apex.apexd_test_different_app.apex': (
571            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
572            'build/make/target/product/security/testkey', None),
573        }, keys_info)
574
575  def test_ReadApexKeysInfo_presignedKeys(self):
576    apex_keys = self.APEX_KEYS_TXT + (
577        'name="apex.apexd_test_different_app2.apex" '
578        'private_key="PRESIGNED" '
579        'public_key="PRESIGNED" '
580        'container_certificate="PRESIGNED" '
581        'container_private_key="PRESIGNED"')
582    target_files = common.MakeTempFile(suffix='.zip')
583    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
584      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
585
586    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
587      keys_info = ReadApexKeysInfo(target_files_zip)
588
589    self.assertEqual({
590        'apex.apexd_test.apex': (
591            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
592            'build/make/target/product/security/testkey', None),
593        'apex.apexd_test_different_app.apex': (
594            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
595            'build/make/target/product/security/testkey', None),
596        }, keys_info)
597
598  def test_ReplaceGkiSigningKey(self):
599    common.OPTIONS.gki_signing_key = 'release_gki_key'
600    common.OPTIONS.gki_signing_algorithm = 'release_gki_algorithm'
601    common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
602
603    misc_info = {
604        'gki_signing_key_path': 'default_gki_key',
605        'gki_signing_algorithm': 'default_gki_algorithm',
606        'gki_signing_signature_args': 'default_gki_signature_args',
607    }
608    expected_dict = {
609        'gki_signing_key_path': 'release_gki_key',
610        'gki_signing_algorithm': 'release_gki_algorithm',
611        'gki_signing_signature_args': 'release_gki_signature_extra_args',
612    }
613    ReplaceGkiSigningKey(misc_info)
614    self.assertDictEqual(expected_dict, misc_info)
615
616  def test_ReplaceGkiSigningKey_MissingSigningAlgorithm(self):
617    common.OPTIONS.gki_signing_key = 'release_gki_key'
618    common.OPTIONS.gki_signing_algorithm = None
619    common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
620
621    misc_info = {
622        'gki_signing_key_path': 'default_gki_key',
623        'gki_signing_algorithm': 'default_gki_algorithm',
624        'gki_signing_signature_args': 'default_gki_signature_args',
625    }
626    self.assertRaises(ValueError, ReplaceGkiSigningKey, misc_info)
627
628  def test_ReplaceGkiSigningKey_MissingSigningKeyNop(self):
629    common.OPTIONS.gki_signing_key = None
630    common.OPTIONS.gki_signing_algorithm = 'release_gki_algorithm'
631    common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
632
633    # No change to misc_info if common.OPTIONS.gki_signing_key is missing.
634    misc_info = {
635        'gki_signing_key_path': 'default_gki_key',
636        'gki_signing_algorithm': 'default_gki_algorithm',
637        'gki_signing_signature_args': 'default_gki_signature_args',
638    }
639    expected_dict = {
640        'gki_signing_key_path': 'default_gki_key',
641        'gki_signing_algorithm': 'default_gki_algorithm',
642        'gki_signing_signature_args': 'default_gki_signature_args',
643    }
644    ReplaceGkiSigningKey(misc_info)
645    self.assertDictEqual(expected_dict, misc_info)
646