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