xref: /aosp_15_r20/build/make/tools/releasetools/test_sign_target_files_apks.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
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, ParseAvbInfo,
26    ReadApexKeysInfo, ReplaceCerts, RewriteAvbProps, RewriteProps,
27    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_ReplaceCerts(self):
158    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')
159    with open(cert1_path) as cert1_fp:
160      cert1 = cert1_fp.read()
161    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')
162    with open(cert2_path) as cert2_fp:
163      cert2 = cert2_fp.read()
164    cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem')
165    with open(cert3_path) as cert3_fp:
166      cert3 = cert3_fp.read()
167
168    # Replace cert1 with cert3.
169    input_xml = self.MAC_PERMISSIONS_XML.format(
170        base64.b16encode(common.ParseCertificate(cert1)).lower(),
171        base64.b16encode(common.ParseCertificate(cert2)).lower())
172
173    output_xml = self.MAC_PERMISSIONS_XML.format(
174        base64.b16encode(common.ParseCertificate(cert3)).lower(),
175        base64.b16encode(common.ParseCertificate(cert2)).lower())
176
177    common.OPTIONS.key_map = {
178        cert1_path[:-9] : cert3_path[:-9],
179    }
180
181    self.assertEqual(output_xml, ReplaceCerts(input_xml))
182
183  def test_ReplaceCerts_duplicateEntries(self):
184    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')
185    with open(cert1_path) as cert1_fp:
186      cert1 = cert1_fp.read()
187    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')
188    with open(cert2_path) as cert2_fp:
189      cert2 = cert2_fp.read()
190
191    # Replace cert1 with cert2, which leads to duplicate entries.
192    input_xml = self.MAC_PERMISSIONS_XML.format(
193        base64.b16encode(common.ParseCertificate(cert1)).lower(),
194        base64.b16encode(common.ParseCertificate(cert2)).lower())
195
196    common.OPTIONS.key_map = {
197        cert1_path[:-9] : cert2_path[:-9],
198    }
199    self.assertRaises(AssertionError, ReplaceCerts, input_xml)
200
201  def test_ReplaceCerts_skipNonExistentCerts(self):
202    cert1_path = os.path.join(self.testdata_dir, 'platform.x509.pem')
203    with open(cert1_path) as cert1_fp:
204      cert1 = cert1_fp.read()
205    cert2_path = os.path.join(self.testdata_dir, 'media.x509.pem')
206    with open(cert2_path) as cert2_fp:
207      cert2 = cert2_fp.read()
208    cert3_path = os.path.join(self.testdata_dir, 'testkey.x509.pem')
209    with open(cert3_path) as cert3_fp:
210      cert3 = cert3_fp.read()
211
212    input_xml = self.MAC_PERMISSIONS_XML.format(
213        base64.b16encode(common.ParseCertificate(cert1)).lower(),
214        base64.b16encode(common.ParseCertificate(cert2)).lower())
215
216    output_xml = self.MAC_PERMISSIONS_XML.format(
217        base64.b16encode(common.ParseCertificate(cert3)).lower(),
218        base64.b16encode(common.ParseCertificate(cert2)).lower())
219
220    common.OPTIONS.key_map = {
221        cert1_path[:-9] : cert3_path[:-9],
222        'non-existent' : cert3_path[:-9],
223        cert2_path[:-9] : 'non-existent',
224    }
225    self.assertEqual(output_xml, ReplaceCerts(input_xml))
226
227  def test_WriteOtacerts(self):
228    certs = [
229        os.path.join(self.testdata_dir, 'platform.x509.pem'),
230        os.path.join(self.testdata_dir, 'media.x509.pem'),
231        os.path.join(self.testdata_dir, 'testkey.x509.pem'),
232    ]
233    entry_name = 'SYSTEM/etc/security/otacerts.zip'
234    output_file = common.MakeTempFile(suffix='.zip')
235    with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
236      WriteOtacerts(output_zip, entry_name, certs)
237    with zipfile.ZipFile(output_file) as input_zip:
238      self.assertIn(entry_name, input_zip.namelist())
239      otacerts_file = io.BytesIO(input_zip.read(entry_name))
240      with zipfile.ZipFile(otacerts_file) as otacerts_zip:
241        self.assertEqual(3, len(otacerts_zip.namelist()))
242
243  def test_CheckApkAndApexKeysAvailable(self):
244    input_file = common.MakeTempFile(suffix='.zip')
245    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
246      input_zip.writestr('SYSTEM/app/App1.apk', "App1-content")
247      input_zip.writestr('SYSTEM/app/App2.apk.gz', "App2-content")
248
249    apk_key_map = {
250        'App1.apk' : 'key1',
251        'App2.apk' : 'key2',
252        'App3.apk' : 'key3',
253    }
254    with zipfile.ZipFile(input_file) as input_zip:
255      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})
256      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.gz', {})
257
258      # 'App2.apk.gz' won't be considered as an APK.
259      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, {})
260      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, '.xz', {})
261
262      del apk_key_map['App2.apk']
263      self.assertRaises(
264          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
265          '.gz', {})
266
267  def test_CheckApkAndApexKeysAvailable_invalidApexKeys(self):
268    input_file = common.MakeTempFile(suffix='.zip')
269    with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
270      input_zip.writestr('SYSTEM/apex/Apex1.apex', "Apex1-content")
271      input_zip.writestr('SYSTEM/apex/Apex2.apex', "Apex2-content")
272
273    apk_key_map = {
274        'Apex1.apex' : 'key1',
275        'Apex2.apex' : 'key2',
276        'Apex3.apex' : 'key3',
277    }
278    apex_keys = {
279        'Apex1.apex' : ('payload-key1', 'container-key1', None),
280        'Apex2.apex' : ('payload-key2', 'container-key2', None),
281    }
282    with zipfile.ZipFile(input_file) as input_zip:
283      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
284
285      # Fine to have both keys as PRESIGNED.
286      apex_keys['Apex2.apex'] = ('PRESIGNED', 'PRESIGNED', None)
287      CheckApkAndApexKeysAvailable(input_zip, apk_key_map, None, apex_keys)
288
289      # Having only one of them as PRESIGNED is not allowed.
290      apex_keys['Apex2.apex'] = ('payload-key2', 'PRESIGNED', None)
291      self.assertRaises(
292          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
293          None, apex_keys)
294
295      apex_keys['Apex2.apex'] = ('PRESIGNED', 'container-key1', None)
296      self.assertRaises(
297          AssertionError, CheckApkAndApexKeysAvailable, input_zip, apk_key_map,
298          None, apex_keys)
299
300  def test_GetApkFileInfo(self):
301    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
302        "PRODUCT/apps/Chats.apk", None, [])
303    self.assertTrue(is_apk)
304    self.assertFalse(is_compressed)
305    self.assertFalse(should_be_skipped)
306
307    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
308        "PRODUCT/apps/Chats.apk", None, [])
309    self.assertTrue(is_apk)
310    self.assertFalse(is_compressed)
311    self.assertFalse(should_be_skipped)
312
313    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
314        "PRODUCT/apps/Chats.dat", None, [])
315    self.assertFalse(is_apk)
316    self.assertFalse(is_compressed)
317    self.assertFalse(should_be_skipped)
318
319  def test_GetApkFileInfo_withCompressedApks(self):
320    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
321        "PRODUCT/apps/Chats.apk.gz", ".gz", [])
322    self.assertTrue(is_apk)
323    self.assertTrue(is_compressed)
324    self.assertFalse(should_be_skipped)
325
326    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
327        "PRODUCT/apps/Chats.apk.gz", ".xz", [])
328    self.assertFalse(is_apk)
329    self.assertFalse(is_compressed)
330    self.assertFalse(should_be_skipped)
331
332    self.assertRaises(
333        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "", [])
334
335    self.assertRaises(
336        AssertionError, GetApkFileInfo, "PRODUCT/apps/Chats.apk", "apk", [])
337
338  def test_GetApkFileInfo_withSkippedPrefixes(self):
339    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
340        "PRODUCT/preloads/apps/Chats.apk", None, set())
341    self.assertTrue(is_apk)
342    self.assertFalse(is_compressed)
343    self.assertFalse(should_be_skipped)
344
345    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
346        "PRODUCT/preloads/apps/Chats.apk",
347        None,
348        set(["PRODUCT/preloads/"]))
349    self.assertTrue(is_apk)
350    self.assertFalse(is_compressed)
351    self.assertTrue(should_be_skipped)
352
353    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
354        "SYSTEM_OTHER/preloads/apps/Chats.apk",
355        None,
356        set(["SYSTEM/preloads/", "SYSTEM_OTHER/preloads/"]))
357    self.assertTrue(is_apk)
358    self.assertFalse(is_compressed)
359    self.assertTrue(should_be_skipped)
360
361    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
362        "SYSTEM_OTHER/preloads/apps/Chats.apk.gz",
363        ".gz",
364        set(["PRODUCT/prebuilts/", "SYSTEM_OTHER/preloads/"]))
365    self.assertTrue(is_apk)
366    self.assertTrue(is_compressed)
367    self.assertTrue(should_be_skipped)
368
369    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
370        "SYSTEM_OTHER/preloads/apps/Chats.dat",
371        None,
372        set(["SYSTEM_OTHER/preloads/"]))
373    self.assertFalse(is_apk)
374    self.assertFalse(is_compressed)
375    self.assertFalse(should_be_skipped)
376
377  def test_GetApkFileInfo_checkSkippedPrefixesInput(self):
378    # set
379    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
380        "SYSTEM_OTHER/preloads/apps/Chats.apk",
381        None,
382        set(["SYSTEM_OTHER/preloads/"]))
383    self.assertTrue(is_apk)
384    self.assertFalse(is_compressed)
385    self.assertTrue(should_be_skipped)
386
387    # tuple
388    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
389        "SYSTEM_OTHER/preloads/apps/Chats.apk",
390        None,
391        ("SYSTEM_OTHER/preloads/",))
392    self.assertTrue(is_apk)
393    self.assertFalse(is_compressed)
394    self.assertTrue(should_be_skipped)
395
396    # list
397    (is_apk, is_compressed, should_be_skipped) = GetApkFileInfo(
398        "SYSTEM_OTHER/preloads/apps/Chats.apk",
399        None,
400        ["SYSTEM_OTHER/preloads/"])
401    self.assertTrue(is_apk)
402    self.assertFalse(is_compressed)
403    self.assertTrue(should_be_skipped)
404
405    # str is invalid.
406    self.assertRaises(
407        AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
408        None, "SYSTEM_OTHER/preloads/")
409
410    # None is invalid.
411    self.assertRaises(
412        AssertionError, GetApkFileInfo, "SYSTEM_OTHER/preloads/apps/Chats.apk",
413        None, None)
414
415  def test_ReadApexKeysInfo(self):
416    target_files = common.MakeTempFile(suffix='.zip')
417    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
418      target_files_zip.writestr('META/apexkeys.txt', self.APEX_KEYS_TXT)
419
420    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
421      keys_info = ReadApexKeysInfo(target_files_zip)
422
423    self.assertEqual({
424        'apex.apexd_test.apex': (
425            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
426            'build/make/target/product/security/testkey', None),
427        'apex.apexd_test_different_app.apex': (
428            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
429            'build/make/target/product/security/testkey', None),
430        }, keys_info)
431
432  def test_ReadApexKeysInfo_mismatchingContainerKeys(self):
433    # Mismatching payload public / private keys.
434    apex_keys = self.APEX_KEYS_TXT + (
435        'name="apex.apexd_test_different_app2.apex" '
436        'public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey" '
437        'private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem" '
438        'container_certificate="build/make/target/product/security/testkey.x509.pem" '
439        'container_private_key="build/make/target/product/security/testkey2.pk8" '
440        'partition="system"')
441    target_files = common.MakeTempFile(suffix='.zip')
442    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
443      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
444
445    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
446      self.assertRaises(ValueError, ReadApexKeysInfo, target_files_zip)
447
448  def test_ReadApexKeysInfo_missingPayloadPrivateKey(self):
449    # Invalid lines will be skipped.
450    apex_keys = self.APEX_KEYS_TXT + (
451        'name="apex.apexd_test_different_app2.apex" '
452        'public_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey" '
453        'container_certificate="build/make/target/product/security/testkey.x509.pem" '
454        'container_private_key="build/make/target/product/security/testkey.pk8"')
455    target_files = common.MakeTempFile(suffix='.zip')
456    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
457      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
458
459    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
460      keys_info = ReadApexKeysInfo(target_files_zip)
461
462    self.assertEqual({
463        'apex.apexd_test.apex': (
464            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
465            'build/make/target/product/security/testkey', None),
466        'apex.apexd_test_different_app.apex': (
467            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
468            'build/make/target/product/security/testkey', None),
469        }, keys_info)
470
471  def test_ReadApexKeysInfo_missingPayloadPublicKey(self):
472    # Invalid lines will be skipped.
473    apex_keys = self.APEX_KEYS_TXT + (
474        'name="apex.apexd_test_different_app2.apex" '
475        'private_key="system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem" '
476        'container_certificate="build/make/target/product/security/testkey.x509.pem" '
477        'container_private_key="build/make/target/product/security/testkey.pk8"')
478    target_files = common.MakeTempFile(suffix='.zip')
479    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
480      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
481
482    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
483      keys_info = ReadApexKeysInfo(target_files_zip)
484
485    self.assertEqual({
486        'apex.apexd_test.apex': (
487            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
488            'build/make/target/product/security/testkey', None),
489        'apex.apexd_test_different_app.apex': (
490            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
491            'build/make/target/product/security/testkey', None),
492        }, keys_info)
493
494  def test_ReadApexKeysInfo_presignedKeys(self):
495    apex_keys = self.APEX_KEYS_TXT + (
496        'name="apex.apexd_test_different_app2.apex" '
497        'private_key="PRESIGNED" '
498        'public_key="PRESIGNED" '
499        'container_certificate="PRESIGNED" '
500        'container_private_key="PRESIGNED"')
501    target_files = common.MakeTempFile(suffix='.zip')
502    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
503      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
504
505    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
506      keys_info = ReadApexKeysInfo(target_files_zip)
507
508    self.assertEqual({
509        'apex.apexd_test.apex': (
510            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
511            'build/make/target/product/security/testkey', None),
512        'apex.apexd_test_different_app.apex': (
513            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
514            'build/make/target/product/security/testkey', None),
515        }, keys_info)
516
517  def test_ReadApexKeysInfo_presignedKeys(self):
518    apex_keys = self.APEX_KEYS_TXT + (
519        'name="apex.apexd_test_different_app2.apex" '
520        'private_key="PRESIGNED" '
521        'public_key="PRESIGNED" '
522        'container_certificate="PRESIGNED" '
523        'container_private_key="PRESIGNED"')
524    target_files = common.MakeTempFile(suffix='.zip')
525    with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
526      target_files_zip.writestr('META/apexkeys.txt', apex_keys)
527
528    with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zip:
529      keys_info = ReadApexKeysInfo(target_files_zip)
530
531    self.assertEqual({
532        'apex.apexd_test.apex': (
533            'system/apex/apexd/apexd_testdata/com.android.apex.test_package.pem',
534            'build/make/target/product/security/testkey', None),
535        'apex.apexd_test_different_app.apex': (
536            'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
537            'build/make/target/product/security/testkey', None),
538        }, keys_info)
539
540  def test_ParseAvbInfo(self):
541    avb_info_string = """
542    Footer version:           1.0
543    Image size:               9999999 bytes
544    Original image size:      8888888 bytes
545    VBMeta offset:            7777777
546    VBMeta size:              1111 bytes
547    --
548    Minimum libavb version:   1.0
549    Header Block:             222 bytes
550    Authentication Block:     333 bytes
551    Auxiliary Block:          888 bytes
552    Public key (sha1):        abababababababababababababababababababab
553    Algorithm:                SHA256_RSA2048
554    Rollback Index:           0
555    Flags:                    0
556    Rollback Index Location:  0
557    Release String:           'avbtool 1.3.0'
558    Descriptors:
559        Hashtree descriptor:
560          Version of dm-verity:  1
561          Image Size:            8888888 bytes
562          Tree Offset:           8888888
563          Tree Size:             44444 bytes
564          Data Block Size:       4444 bytes
565          Hash Block Size:       4444 bytes
566          FEC num roots:         0
567          FEC offset:            0
568          FEC size:              0 bytes
569          Hash Algorithm:        sha1
570          Partition Name:        partition-name
571          Salt:                  cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd
572          Root Digest:           efefefefefefefefefefefefefefefefefef
573          Flags:                 0
574        Prop: prop.key -> 'prop.value'
575    """
576
577    self.assertEqual(
578        {
579            'Footer version': '1.0',
580            'Image size': '9999999 bytes',
581            'Original image size': '8888888 bytes',
582            'VBMeta offset': '7777777',
583            'VBMeta size': '1111 bytes',
584            'Minimum libavb version': '1.0',
585            'Header Block': '222 bytes',
586            'Authentication Block': '333 bytes',
587            'Auxiliary Block': '888 bytes',
588            'Public key (sha1)': 'abababababababababababababababababababab',
589            'Algorithm': 'SHA256_RSA2048',
590            'Rollback Index': '0',
591            'Flags': '0',
592            'Rollback Index Location': '0',
593            'Release String': "'avbtool 1.3.0'",
594            'Descriptors': [
595                {
596                    'Hashtree descriptor': {
597                        'Version of dm-verity': '1',
598                        'Image Size': '8888888 bytes',
599                        'Tree Offset': '8888888',
600                        'Tree Size': '44444 bytes',
601                        'Data Block Size': '4444 bytes',
602                        'Hash Block Size': '4444 bytes',
603                        'FEC num roots': '0',
604                        'FEC offset': '0',
605                        'FEC size': '0 bytes',
606                        'Hash Algorithm': 'sha1',
607                        'Partition Name': 'partition-name',
608                        'Salt': 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd',
609                        'Root Digest': 'efefefefefefefefefefefefefefefefefef',
610                        'Flags': '0',
611                    }
612                },
613                {
614                    'Prop': {
615                        'prop.key': 'prop.value',
616                    }
617                },
618            ],
619        },
620        ParseAvbInfo(avb_info_string),
621    )