1#!/usr/bin/env python3
2#
3# Copyright 2022, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Tests certify_bootimg."""
18
19import logging
20import glob
21import os
22import random
23import shutil
24import struct
25import subprocess
26import sys
27import tempfile
28import unittest
29
30BOOT_SIGNATURE_SIZE = 16 * 1024
31
32TEST_KERNEL_CMDLINE = (
33    'printk.devkmsg=on firmware_class.path=/vendor/etc/ init=/init '
34    'kfence.sample_interval=500 loop.max_part=7 bootconfig'
35)
36
37
38def generate_test_file(pathname, size, seed=None):
39    """Generates a gibberish-filled test file and returns its pathname."""
40    random.seed(os.path.basename(pathname) if seed is None else seed)
41    with open(pathname, 'wb') as file:
42        file.write(random.randbytes(size))
43    return pathname
44
45
46def generate_test_boot_image(boot_img, kernel_size=4096, seed='kernel',
47                             avb_partition_size=None):
48    """Generates a test boot.img without a ramdisk."""
49    with tempfile.NamedTemporaryFile() as kernel_tmpfile:
50        generate_test_file(kernel_tmpfile.name, kernel_size, seed)
51        kernel_tmpfile.flush()
52
53        mkbootimg_cmds = [
54            'mkbootimg',
55            '--header_version', '4',
56            '--kernel', kernel_tmpfile.name,
57            '--cmdline', TEST_KERNEL_CMDLINE,
58            '--os_version', '12.0.0',
59            '--os_patch_level', '2022-03',
60            '--output', boot_img,
61        ]
62        subprocess.check_call(mkbootimg_cmds)
63
64    if avb_partition_size:
65        avbtool_cmd = ['avbtool', 'add_hash_footer', '--image', boot_img,
66                       '--partition_name', 'boot',
67                       '--partition_size', str(avb_partition_size)]
68        subprocess.check_call(avbtool_cmd)
69
70
71def generate_test_boot_image_archive(archive_file_name, archive_format,
72                                     boot_img_info, gki_info=None):
73    """Generates an archive of test boot images.
74
75    It also adds a file gki-info.txt, which contains additional settings for
76    for `certify_bootimg --extra_args`.
77
78    Args:
79        archive_file_name: the name of the archive file to create, including the
80          path, minus any format-specific extension.
81        archive_format: the |format| parameter for shutil.make_archive().
82          e.g., 'zip', 'tar', or 'gztar', etc.
83        boot_img_info: a list of (boot_image_name, kernel_size,
84          partition_size) tuples. e.g.,
85          [('boot.img', 4096, 4 * 1024),
86           ('boot-lz4.img', 8192, 8 * 1024)].
87        gki_info: the file content to be written into 'gki-info.txt' in the
88          created archive.
89
90    Returns:
91        The full path of the created archive. e.g., /path/to/boot-img.tar.gz.
92    """
93    with tempfile.TemporaryDirectory() as temp_out_dir:
94        for name, kernel_size, partition_size in boot_img_info:
95            boot_img = os.path.join(temp_out_dir, name)
96            generate_test_boot_image(boot_img=boot_img,
97                                     kernel_size=kernel_size,
98                                     seed=name,
99                                     avb_partition_size=partition_size)
100
101        if gki_info:
102            gki_info_path = os.path.join(temp_out_dir, 'gki-info.txt')
103            with open(gki_info_path, 'w', encoding='utf-8') as f:
104                f.write(gki_info)
105
106        return shutil.make_archive(archive_file_name,
107                                   archive_format,
108                                   temp_out_dir)
109
110
111def has_avb_footer(image):
112    """Returns true if the image has a avb footer."""
113
114    avbtool_info_cmd = ['avbtool', 'info_image', '--image', image]
115    result = subprocess.run(avbtool_info_cmd, check=False,
116                            stdout=subprocess.DEVNULL,
117                            stderr=subprocess.DEVNULL)
118
119    return result.returncode == 0
120
121
122def get_vbmeta_size(vbmeta_bytes):
123    """Returns the total size of a AvbVBMeta image."""
124
125    # Keep in sync with |AvbVBMetaImageHeader|.
126    AVB_MAGIC = b'AVB0'                        # pylint: disable=C0103
127    AVB_VBMETA_IMAGE_HEADER_SIZE = 256         # pylint: disable=C0103
128    FORMAT_STRING = (                          # pylint: disable=C0103
129        '!4s2L'      # magic, 2 x version.
130        '2Q'         # 2 x block size: Authentication and Auxiliary blocks.
131    )
132
133    if len(vbmeta_bytes) < struct.calcsize(FORMAT_STRING):
134        return 0
135
136    data = vbmeta_bytes[:struct.calcsize(FORMAT_STRING)]
137    (magic, _, _,
138     authentication_block_size,
139     auxiliary_data_block_size) = struct.unpack(FORMAT_STRING, data)
140
141    if magic == AVB_MAGIC:
142        return (AVB_VBMETA_IMAGE_HEADER_SIZE +
143                authentication_block_size +
144                auxiliary_data_block_size)
145    return 0
146
147
148def extract_boot_signatures(boot_img, output_dir):
149    """Extracts the boot signatures of a boot image.
150
151    This functions extracts the boot signatures of |boot_img| as:
152      - |output_dir|/boot_signature1
153      - |output_dir|/boot_signature2
154    """
155
156    boot_img_copy = os.path.join(output_dir, 'boot_image_copy')
157    shutil.copy2(boot_img, boot_img_copy)
158    avbtool_cmd = ['avbtool', 'erase_footer', '--image', boot_img_copy]
159    subprocess.run(avbtool_cmd, check=False, stderr=subprocess.DEVNULL)
160
161    # The boot signature is assumed to be at the end of boot image, after
162    # the AVB footer is erased.
163    with open(boot_img_copy, 'rb') as image:
164        image.seek(-BOOT_SIGNATURE_SIZE, os.SEEK_END)
165        boot_signature_bytes = image.read(BOOT_SIGNATURE_SIZE)
166        assert len(boot_signature_bytes) == BOOT_SIGNATURE_SIZE
167    os.unlink(boot_img_copy)
168
169    num_signatures = 0
170    while True:
171        next_signature_size = get_vbmeta_size(boot_signature_bytes)
172        if next_signature_size <= 0:
173            break
174
175        num_signatures += 1
176        next_signature = boot_signature_bytes[:next_signature_size]
177        output_path = os.path.join(
178            output_dir, 'boot_signature' + str(num_signatures))
179        with open(output_path, 'wb') as output:
180            output.write(next_signature)
181
182        # Moves to the next signature.
183        boot_signature_bytes = boot_signature_bytes[next_signature_size:]
184
185
186def extract_boot_archive_with_signatures(boot_img_archive, output_dir):
187    """Extracts boot images and signatures of a boot images archive.
188
189    Suppose there are two boot images in |boot_img_archive|: boot.img
190    and boot-lz4.img. This function then extracts each boot*.img and
191    their signatures as:
192      - |output_dir|/boot.img
193      - |output_dir|/boot-lz4.img
194      - |output_dir|/boot/boot_signature1
195      - |output_dir|/boot/boot_signature2
196      - |output_dir|/boot-lz4/boot_signature1
197      - |output_dir|/boot-lz4/boot_signature2
198    """
199    shutil.unpack_archive(boot_img_archive, output_dir)
200    for boot_img in glob.glob(os.path.join(output_dir, 'boot*.img')):
201        img_name = os.path.splitext(os.path.basename(boot_img))[0]
202        signature_output_dir = os.path.join(output_dir, img_name)
203        os.mkdir(signature_output_dir, 0o777)
204        extract_boot_signatures(boot_img, signature_output_dir)
205
206
207class CertifyBootimgTest(unittest.TestCase):
208    """Tests the functionalities of certify_bootimg."""
209
210    def setUp(self):
211        # Saves the test executable directory so that relative path references
212        # to test dependencies don't rely on being manually run from the
213        # executable directory.
214        # With this, we can just open "./testdata/testkey_rsa2048.pem" in the
215        # following tests with subprocess.run(..., cwd=self._exec_dir, ...).
216        self._exec_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
217
218        # Set self.maxDiff to None to see full diff in assertion.
219        # C0103: invalid-name for maxDiff.
220        self.maxDiff = None  # pylint: disable=C0103
221
222        # For AVB footers, we don't sign it so the Authentication block
223        # is zero bytes and the Algorithm is NONE. The footer will be
224        # replaced by device-specific settings when being incorporated into
225        # a device codebase. The footer here is just to pass some GKI
226        # pre-release test.
227        self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED = (    # pylint: disable=C0103
228            'Footer version:           1.0\n'
229            'Image size:               131072 bytes\n'
230            'Original image size:      24576 bytes\n'
231            'VBMeta offset:            24576\n'
232            'VBMeta size:              576 bytes\n'
233            '--\n'
234            'Minimum libavb version:   1.0\n'
235            'Header Block:             256 bytes\n'
236            'Authentication Block:     0 bytes\n'
237            'Auxiliary Block:          320 bytes\n'
238            'Algorithm:                NONE\n'
239            'Rollback Index:           0\n'
240            'Flags:                    0\n'
241            'Rollback Index Location:  0\n'
242            "Release String:           'avbtool 1.3.0'\n"
243            'Descriptors:\n'
244            '    Hash descriptor:\n'
245            '      Image Size:            24576 bytes\n'
246            '      Hash Algorithm:        sha256\n'
247            '      Partition Name:        boot\n'
248            '      Salt:                  a11ba11b\n'
249            '      Digest:                '
250            '0dcbb90bc005403e79eef4209a5ac6fc'
251            '02c3d5c5f3abae9d07570ef8e4ca9b98\n'
252            '      Flags:                 0\n'
253            "    Prop: avb -> 'nice'\n"
254            "    Prop: avb_space -> 'nice to meet you'\n"
255        )
256
257        self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED_2 = (  # pylint: disable=C0103
258            'Footer version:           1.0\n'
259            'Image size:               131072 bytes\n'
260            'Original image size:      24576 bytes\n'
261            'VBMeta offset:            24576\n'
262            'VBMeta size:              576 bytes\n'
263            '--\n'
264            'Minimum libavb version:   1.0\n'
265            'Header Block:             256 bytes\n'
266            'Authentication Block:     0 bytes\n'
267            'Auxiliary Block:          320 bytes\n'
268            'Algorithm:                NONE\n'
269            'Rollback Index:           0\n'
270            'Flags:                    0\n'
271            'Rollback Index Location:  0\n'
272            "Release String:           'avbtool 1.3.0'\n"
273            'Descriptors:\n'
274            '    Hash descriptor:\n'
275            '      Image Size:            24576 bytes\n'
276            '      Hash Algorithm:        sha256\n'
277            '      Partition Name:        boot\n'
278            '      Salt:                  a11ba11b\n'
279            '      Digest:                '
280            '54b0f4babe7f8fd1c204b6bfffbbe200'
281            'b9ccb8499776fc50f110344380cc1bf9\n'
282            '      Flags:                 0\n'
283            "    Prop: avb -> 'nice'\n"
284            "    Prop: avb_space -> 'nice to meet you'\n"
285        )
286
287        self._EXPECTED_AVB_FOOTER_WITH_GKI_INFO = (     # pylint: disable=C0103
288            'Footer version:           1.0\n'
289            'Image size:               131072 bytes\n'
290            'Original image size:      24576 bytes\n'
291            'VBMeta offset:            24576\n'
292            'VBMeta size:              704 bytes\n'
293            '--\n'
294            'Minimum libavb version:   1.0\n'
295            'Header Block:             256 bytes\n'
296            'Authentication Block:     0 bytes\n'
297            'Auxiliary Block:          448 bytes\n'
298            'Algorithm:                NONE\n'
299            'Rollback Index:           0\n'
300            'Flags:                    0\n'
301            'Rollback Index Location:  0\n'
302            "Release String:           'avbtool 1.3.0'\n"
303            'Descriptors:\n'
304            '    Hash descriptor:\n'
305            '      Image Size:            24576 bytes\n'
306            '      Hash Algorithm:        sha256\n'
307            '      Partition Name:        boot\n'
308            '      Salt:                  a11ba11b\n'
309            '      Digest:                '
310            '9501fb3e889f926976e1566bb748c891'
311            '0c0a0837108764262fa1ecc413056cf8\n'
312            '      Flags:                 0\n'
313            "    Prop: avb -> 'nice'\n"
314            "    Prop: avb_space -> 'nice to meet you'\n"
315            "    Prop: com.android.build.boot.os_version -> '13'\n"
316            "    Prop: com.android.build.boot.security_patch -> '2022-05-05'\n"
317        )
318
319        self._EXPECTED_AVB_FOOTER_BOOT = (              # pylint: disable=C0103
320            'Footer version:           1.0\n'
321            'Image size:               131072 bytes\n'
322            'Original image size:      28672 bytes\n'
323            'VBMeta offset:            28672\n'
324            'VBMeta size:              704 bytes\n'
325            '--\n'
326            'Minimum libavb version:   1.0\n'
327            'Header Block:             256 bytes\n'
328            'Authentication Block:     0 bytes\n'
329            'Auxiliary Block:          448 bytes\n'
330            'Algorithm:                NONE\n'
331            'Rollback Index:           0\n'
332            'Flags:                    0\n'
333            'Rollback Index Location:  0\n'
334            "Release String:           'avbtool 1.3.0'\n"
335            'Descriptors:\n'
336            '    Hash descriptor:\n'
337            '      Image Size:            28672 bytes\n'
338            '      Hash Algorithm:        sha256\n'
339            '      Partition Name:        boot\n'
340            '      Salt:                  a11ba11b\n'
341            '      Digest:                '
342            '925ee1495c95d302c4a2c8c2edaf29c8'
343            '41b1c8ae2ae7bf5d46561d21b7494cb3\n'
344            '      Flags:                 0\n'
345            "    Prop: avb -> 'nice'\n"
346            "    Prop: avb_space -> 'nice to meet you'\n"
347            "    Prop: com.android.build.boot.os_version -> '13'\n"
348            "    Prop: com.android.build.boot.security_patch -> '2022-05-05'\n"
349        )
350
351        self._EXPECTED_AVB_FOOTER_BOOT_LZ4 = (          # pylint: disable=C0103
352            'Footer version:           1.0\n'
353            'Image size:               262144 bytes\n'
354            'Original image size:      36864 bytes\n'
355            'VBMeta offset:            36864\n'
356            'VBMeta size:              704 bytes\n'
357            '--\n'
358            'Minimum libavb version:   1.0\n'
359            'Header Block:             256 bytes\n'
360            'Authentication Block:     0 bytes\n'
361            'Auxiliary Block:          448 bytes\n'
362            'Algorithm:                NONE\n'
363            'Rollback Index:           0\n'
364            'Flags:                    0\n'
365            'Rollback Index Location:  0\n'
366            "Release String:           'avbtool 1.3.0'\n"
367            'Descriptors:\n'
368            '    Hash descriptor:\n'
369            '      Image Size:            36864 bytes\n'
370            '      Hash Algorithm:        sha256\n'
371            '      Partition Name:        boot\n'
372            '      Salt:                  a11ba11b\n'
373            '      Digest:                '
374            'a390b4febe92d0331b44edacaad54ad8'
375            '972aaa05bb4ea427697c132b7e3741a2\n'
376            '      Flags:                 0\n'
377            "    Prop: avb -> 'nice'\n"
378            "    Prop: avb_space -> 'nice to meet you'\n"
379            "    Prop: com.android.build.boot.os_version -> '13'\n"
380            "    Prop: com.android.build.boot.security_patch -> '2022-05-05'\n"
381        )
382
383        self._EXPECTED_AVB_FOOTER_BOOT_GZ = (           # pylint: disable=C0103
384            'Footer version:           1.0\n'
385            'Image size:               131072 bytes\n'
386            'Original image size:      28672 bytes\n'
387            'VBMeta offset:            28672\n'
388            'VBMeta size:              576 bytes\n'
389            '--\n'
390            'Minimum libavb version:   1.0\n'
391            'Header Block:             256 bytes\n'
392            'Authentication Block:     0 bytes\n'
393            'Auxiliary Block:          320 bytes\n'
394            'Algorithm:                NONE\n'
395            'Rollback Index:           0\n'
396            'Flags:                    0\n'
397            'Rollback Index Location:  0\n'
398            "Release String:           'avbtool 1.3.0'\n"
399            'Descriptors:\n'
400            '    Hash descriptor:\n'
401            '      Image Size:            28672 bytes\n'
402            '      Hash Algorithm:        sha256\n'
403            '      Partition Name:        boot\n'
404            '      Salt:                  a11ba11b\n'
405            '      Digest:                '
406            '3808076404808e468a0a9df34c6d9d7a'
407            '8dc8ae69fb03fa62f0f23aa5b65e3c66\n'
408            '      Flags:                 0\n'
409            "    Prop: avb -> 'nice'\n"
410            "    Prop: avb_space -> 'nice to meet you'\n"
411        )
412
413        self._EXPECTED_BOOT_SIGNATURE_RSA2048 = (       # pylint: disable=C0103
414            'Minimum libavb version:   1.0\n'
415            'Header Block:             256 bytes\n'
416            'Authentication Block:     320 bytes\n'
417            'Auxiliary Block:          832 bytes\n'
418            'Public key (sha1):        '
419            'cdbb77177f731920bbe0a0f94f84d9038ae0617d\n'
420            'Algorithm:                SHA256_RSA2048\n'
421            'Rollback Index:           0\n'
422            'Flags:                    0\n'
423            'Rollback Index Location:  0\n'
424            "Release String:           'avbtool 1.3.0'\n"
425            'Descriptors:\n'
426            '    Hash descriptor:\n'
427            '      Image Size:            8192 bytes\n'
428            '      Hash Algorithm:        sha256\n'
429            '      Partition Name:        boot\n'           # boot
430            '      Salt:                  d00df00d\n'
431            '      Digest:                '
432            'faf1da72a4fba97ddab0b8f7a410db86'
433            '8fb72392a66d1440ff8bff490c73c771\n'
434            '      Flags:                 0\n'
435            "    Prop: gki -> 'nice'\n"
436            "    Prop: space -> 'nice to meet you'\n"
437        )
438
439        self._EXPECTED_KERNEL_SIGNATURE_RSA2048 = (     # pylint: disable=C0103
440            'Minimum libavb version:   1.0\n'
441            'Header Block:             256 bytes\n'
442            'Authentication Block:     320 bytes\n'
443            'Auxiliary Block:          832 bytes\n'
444            'Public key (sha1):        '
445            'cdbb77177f731920bbe0a0f94f84d9038ae0617d\n'
446            'Algorithm:                SHA256_RSA2048\n'
447            'Rollback Index:           0\n'
448            'Flags:                    0\n'
449            'Rollback Index Location:  0\n'
450            "Release String:           'avbtool 1.3.0'\n"
451            'Descriptors:\n'
452            '    Hash descriptor:\n'
453            '      Image Size:            4096 bytes\n'
454            '      Hash Algorithm:        sha256\n'
455            '      Partition Name:        generic_kernel\n' # generic_kernel
456            '      Salt:                  d00df00d\n'
457            '      Digest:                '
458            '762c877f3af0d50a4a4fbc1385d5c7ce'
459            '52a1288db74b33b72217d93db6f2909f\n'
460            '      Flags:                 0\n'
461            "    Prop: gki -> 'nice'\n"
462            "    Prop: space -> 'nice to meet you'\n"
463        )
464
465        self._EXPECTED_BOOT_SIGNATURE_RSA4096 = (       # pylint: disable=C0103
466            'Minimum libavb version:   1.0\n'
467            'Header Block:             256 bytes\n'
468            'Authentication Block:     576 bytes\n'
469            'Auxiliary Block:          1344 bytes\n'
470            'Public key (sha1):        '
471            '2597c218aae470a130f61162feaae70afd97f011\n'
472            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
473            'Rollback Index:           0\n'
474            'Flags:                    0\n'
475            'Rollback Index Location:  0\n'
476            "Release String:           'avbtool 1.3.0'\n"
477            'Descriptors:\n'
478            '    Hash descriptor:\n'
479            '      Image Size:            8192 bytes\n'
480            '      Hash Algorithm:        sha256\n'
481            '      Partition Name:        boot\n'           # boot
482            '      Salt:                  d00df00d\n'
483            '      Digest:                '
484            'faf1da72a4fba97ddab0b8f7a410db86'
485            '8fb72392a66d1440ff8bff490c73c771\n'
486            '      Flags:                 0\n'
487            "    Prop: gki -> 'nice'\n"
488            "    Prop: space -> 'nice to meet you'\n"
489        )
490
491        self._EXPECTED_KERNEL_SIGNATURE_RSA4096 = (     # pylint: disable=C0103
492            'Minimum libavb version:   1.0\n'
493            'Header Block:             256 bytes\n'
494            'Authentication Block:     576 bytes\n'
495            'Auxiliary Block:          1344 bytes\n'
496            'Public key (sha1):        '
497            '2597c218aae470a130f61162feaae70afd97f011\n'
498            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
499            'Rollback Index:           0\n'
500            'Flags:                    0\n'
501            'Rollback Index Location:  0\n'
502            "Release String:           'avbtool 1.3.0'\n"
503            'Descriptors:\n'
504            '    Hash descriptor:\n'
505            '      Image Size:            4096 bytes\n'
506            '      Hash Algorithm:        sha256\n'
507            '      Partition Name:        generic_kernel\n' # generic_kernel
508            '      Salt:                  d00df00d\n'
509            '      Digest:                '
510            '762c877f3af0d50a4a4fbc1385d5c7ce'
511            '52a1288db74b33b72217d93db6f2909f\n'
512            '      Flags:                 0\n'
513            "    Prop: gki -> 'nice'\n"
514            "    Prop: space -> 'nice to meet you'\n"
515        )
516
517        self._EXPECTED_BOOT_SIGNATURE_WITH_GKI_INFO = (  # pylint: disable=C0103
518            'Minimum libavb version:   1.0\n'
519            'Header Block:             256 bytes\n'
520            'Authentication Block:     576 bytes\n'
521            'Auxiliary Block:          1600 bytes\n'
522            'Public key (sha1):        '
523            '2597c218aae470a130f61162feaae70afd97f011\n'
524            'Algorithm:                SHA256_RSA4096\n' # RSA4096
525            'Rollback Index:           0\n'
526            'Flags:                    0\n'
527            'Rollback Index Location:  0\n'
528            "Release String:           'avbtool 1.3.0'\n"
529            'Descriptors:\n'
530            '    Hash descriptor:\n'
531            '      Image Size:            8192 bytes\n'
532            '      Hash Algorithm:        sha256\n'
533            '      Partition Name:        boot\n'        # boot
534            '      Salt:                  d00df00d\n'
535            '      Digest:                '
536            'faf1da72a4fba97ddab0b8f7a410db86'
537            '8fb72392a66d1440ff8bff490c73c771\n'
538            '      Flags:                 0\n'
539            "    Prop: gki -> 'nice'\n"
540            "    Prop: space -> 'nice to meet you'\n"
541            "    Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
542            "ged21d463f856'\n"
543            "    Prop: BRANCH -> 'android13-5.10-2022-05'\n"
544            "    Prop: BUILD_NUMBER -> 'ab8295296'\n"
545            "    Prop: GKI_INFO -> 'added here'\n"
546        )
547
548        self._EXPECTED_KERNEL_SIGNATURE_WITH_GKI_INFO = (# pylint: disable=C0103
549            'Minimum libavb version:   1.0\n'
550            'Header Block:             256 bytes\n'
551            'Authentication Block:     576 bytes\n'
552            'Auxiliary Block:          1600 bytes\n'
553            'Public key (sha1):        '
554            '2597c218aae470a130f61162feaae70afd97f011\n'
555            'Algorithm:                SHA256_RSA4096\n' # RSA4096
556            'Rollback Index:           0\n'
557            'Flags:                    0\n'
558            'Rollback Index Location:  0\n'
559            "Release String:           'avbtool 1.3.0'\n"
560            'Descriptors:\n'
561            '    Hash descriptor:\n'
562            '      Image Size:            4096 bytes\n'
563            '      Hash Algorithm:        sha256\n'
564            '      Partition Name:        generic_kernel\n' # generic_kernel
565            '      Salt:                  d00df00d\n'
566            '      Digest:                '
567            '762c877f3af0d50a4a4fbc1385d5c7ce'
568            '52a1288db74b33b72217d93db6f2909f\n'
569            '      Flags:                 0\n'
570            "    Prop: gki -> 'nice'\n"
571            "    Prop: space -> 'nice to meet you'\n"
572            "    Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
573            "ged21d463f856'\n"
574            "    Prop: BRANCH -> 'android13-5.10-2022-05'\n"
575            "    Prop: BUILD_NUMBER -> 'ab8295296'\n"
576            "    Prop: GKI_INFO -> 'added here'\n"
577        )
578
579        self._EXPECTED_BOOT_SIGNATURE1_RSA4096 = (       # pylint: disable=C0103
580            'Minimum libavb version:   1.0\n'
581            'Header Block:             256 bytes\n'
582            'Authentication Block:     576 bytes\n'
583            'Auxiliary Block:          1600 bytes\n'
584            'Public key (sha1):        '
585            '2597c218aae470a130f61162feaae70afd97f011\n'
586            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
587            'Rollback Index:           0\n'
588            'Flags:                    0\n'
589            'Rollback Index Location:  0\n'
590            "Release String:           'avbtool 1.3.0'\n"
591            'Descriptors:\n'
592            '    Hash descriptor:\n'
593            '      Image Size:            12288 bytes\n'
594            '      Hash Algorithm:        sha256\n'
595            '      Partition Name:        boot\n'           # boot
596            '      Salt:                  d00df00d\n'
597            '      Digest:                '
598            '30208b4d0a6d16db47fc13c9527bfe81'
599            'a168d3b3940325d1ca8d3439792bfe18\n'
600            '      Flags:                 0\n'
601            "    Prop: gki -> 'nice'\n"
602            "    Prop: space -> 'nice to meet you'\n"
603            "    Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
604            "ged21d463f856'\n"
605            "    Prop: BRANCH -> 'android13-5.10-2022-05'\n"
606            "    Prop: BUILD_NUMBER -> 'ab8295296'\n"
607            "    Prop: SPACE -> 'nice to meet you'\n"
608        )
609
610        self._EXPECTED_BOOT_SIGNATURE2_RSA4096 = (       # pylint: disable=C0103
611            'Minimum libavb version:   1.0\n'
612            'Header Block:             256 bytes\n'
613            'Authentication Block:     576 bytes\n'
614            'Auxiliary Block:          1600 bytes\n'
615            'Public key (sha1):        '
616            '2597c218aae470a130f61162feaae70afd97f011\n'
617            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
618            'Rollback Index:           0\n'
619            'Flags:                    0\n'
620            'Rollback Index Location:  0\n'
621            "Release String:           'avbtool 1.3.0'\n"
622            'Descriptors:\n'
623            '    Hash descriptor:\n'
624            '      Image Size:            8192 bytes\n'
625            '      Hash Algorithm:        sha256\n'
626            '      Partition Name:        generic_kernel\n' # generic_kernel
627            '      Salt:                  d00df00d\n'
628            '      Digest:                '
629            'd4c8847e7d9900a98f77e1f0b5272854'
630            '7bf9c1e428fea500d419275f72ec5bd6\n'
631            '      Flags:                 0\n'
632            "    Prop: gki -> 'nice'\n"
633            "    Prop: space -> 'nice to meet you'\n"
634            "    Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
635            "ged21d463f856'\n"
636            "    Prop: BRANCH -> 'android13-5.10-2022-05'\n"
637            "    Prop: BUILD_NUMBER -> 'ab8295296'\n"
638            "    Prop: SPACE -> 'nice to meet you'\n"
639        )
640
641        self._EXPECTED_BOOT_LZ4_SIGNATURE1_RSA4096 = (   # pylint: disable=C0103
642            'Minimum libavb version:   1.0\n'
643            'Header Block:             256 bytes\n'
644            'Authentication Block:     576 bytes\n'
645            'Auxiliary Block:          1600 bytes\n'
646            'Public key (sha1):        '
647            '2597c218aae470a130f61162feaae70afd97f011\n'
648            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
649            'Rollback Index:           0\n'
650            'Flags:                    0\n'
651            'Rollback Index Location:  0\n'
652            "Release String:           'avbtool 1.3.0'\n"
653            'Descriptors:\n'
654            '    Hash descriptor:\n'
655            '      Image Size:            20480 bytes\n'
656            '      Hash Algorithm:        sha256\n'
657            '      Partition Name:        boot\n'           # boot
658            '      Salt:                  d00df00d\n'
659            '      Digest:                '
660            '9d3a0670a9fd3de66e940117ef97700f'
661            'ed5fd1c6fb90798fd3873af45fc91cb4\n'
662            '      Flags:                 0\n'
663            "    Prop: gki -> 'nice'\n"
664            "    Prop: space -> 'nice to meet you'\n"
665            "    Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
666            "ged21d463f856'\n"
667            "    Prop: BRANCH -> 'android13-5.10-2022-05'\n"
668            "    Prop: BUILD_NUMBER -> 'ab8295296'\n"
669            "    Prop: SPACE -> 'nice to meet you'\n"
670        )
671
672        self._EXPECTED_BOOT_LZ4_SIGNATURE2_RSA4096 = (   # pylint: disable=C0103
673            'Minimum libavb version:   1.0\n'
674            'Header Block:             256 bytes\n'
675            'Authentication Block:     576 bytes\n'
676            'Auxiliary Block:          1600 bytes\n'
677            'Public key (sha1):        '
678            '2597c218aae470a130f61162feaae70afd97f011\n'
679            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
680            'Rollback Index:           0\n'
681            'Flags:                    0\n'
682            'Rollback Index Location:  0\n'
683            "Release String:           'avbtool 1.3.0'\n"
684            'Descriptors:\n'
685            '    Hash descriptor:\n'
686            '      Image Size:            16384 bytes\n'
687            '      Hash Algorithm:        sha256\n'
688            '      Partition Name:        generic_kernel\n' # generic_kernel
689            '      Salt:                  d00df00d\n'
690            '      Digest:                '
691            '7d109e3dccca9e30e04249162d07e58c'
692            '62fdf269804b35857b956fba339b2679\n'
693            '      Flags:                 0\n'
694            "    Prop: gki -> 'nice'\n"
695            "    Prop: space -> 'nice to meet you'\n"
696            "    Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
697            "ged21d463f856'\n"
698            "    Prop: BRANCH -> 'android13-5.10-2022-05'\n"
699            "    Prop: BUILD_NUMBER -> 'ab8295296'\n"
700            "    Prop: SPACE -> 'nice to meet you'\n"
701        )
702
703        self._EXPECTED_BOOT_GZ_SIGNATURE1_RSA4096 = (    # pylint: disable=C0103
704            'Minimum libavb version:   1.0\n'
705            'Header Block:             256 bytes\n'
706            'Authentication Block:     576 bytes\n'
707            'Auxiliary Block:          1344 bytes\n'
708            'Public key (sha1):        '
709            '2597c218aae470a130f61162feaae70afd97f011\n'
710            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
711            'Rollback Index:           0\n'
712            'Flags:                    0\n'
713            'Rollback Index Location:  0\n'
714            "Release String:           'avbtool 1.3.0'\n"
715            'Descriptors:\n'
716            '    Hash descriptor:\n'
717            '      Image Size:            12288 bytes\n'
718            '      Hash Algorithm:        sha256\n'
719            '      Partition Name:        boot\n'           # boot
720            '      Salt:                  d00df00d\n'
721            '      Digest:                '
722            '6fcddc6167ae3c2037b424d35c3ef107'
723            'f586510dbb2d652d7c08b88e6ea52fc6\n'
724            '      Flags:                 0\n'
725            "    Prop: gki -> 'nice'\n"
726            "    Prop: space -> 'nice to meet you'\n"
727        )
728
729        self._EXPECTED_BOOT_GZ_SIGNATURE2_RSA4096 = (    # pylint: disable=C0103
730            'Minimum libavb version:   1.0\n'
731            'Header Block:             256 bytes\n'
732            'Authentication Block:     576 bytes\n'
733            'Auxiliary Block:          1344 bytes\n'
734            'Public key (sha1):        '
735            '2597c218aae470a130f61162feaae70afd97f011\n'
736            'Algorithm:                SHA256_RSA4096\n'    # RSA4096
737            'Rollback Index:           0\n'
738            'Flags:                    0\n'
739            'Rollback Index Location:  0\n'
740            "Release String:           'avbtool 1.3.0'\n"
741            'Descriptors:\n'
742            '    Hash descriptor:\n'
743            '      Image Size:            8192 bytes\n'
744            '      Hash Algorithm:        sha256\n'
745            '      Partition Name:        generic_kernel\n' # generic_kernel
746            '      Salt:                  d00df00d\n'
747            '      Digest:                '
748            '7a6a43eb4048b783346fb6d039103647'
749            '6c4313146da521467af282dff1838d0e\n'
750            '      Flags:                 0\n'
751            "    Prop: gki -> 'nice'\n"
752            "    Prop: space -> 'nice to meet you'\n"
753        )
754
755    def _test_boot_signatures(self, signatures_dir, expected_signatures_info):
756        """Tests the info of each boot signature under the signature directory.
757
758        Args:
759            signatures_dir: the directory containing the boot signatures. e.g.,
760                - signatures_dir/boot_signature1
761                - signatures_dir/boot_signature2
762            expected_signatures_info: A dict containing the expected output
763                of `avbtool info_image` for each signature under
764                |signatures_dir|. e.g.,
765                {'boot_signature1': expected_stdout_signature1
766                 'boot_signature2': expected_stdout_signature2}
767        """
768        for signature in expected_signatures_info:
769            avbtool_info_cmds = [
770                'avbtool', 'info_image', '--image',
771                os.path.join(signatures_dir, signature)
772            ]
773            result = subprocess.run(avbtool_info_cmds, check=True,
774                                    capture_output=True, encoding='utf-8')
775            self.assertEqual(result.stdout, expected_signatures_info[signature])
776
777    def test_certify_bootimg_without_avb_footer(self):
778        """Tests certify_bootimg on a boot image without an AVB footer."""
779        with tempfile.TemporaryDirectory() as temp_out_dir:
780            boot_img = os.path.join(temp_out_dir, 'boot.img')
781            generate_test_boot_image(boot_img)
782
783            # Generates the certified boot image, with a RSA2048 key.
784            boot_certified_img = os.path.join(temp_out_dir,
785                                              'boot-certified.img')
786            certify_bootimg_cmds = [
787                'certify_bootimg',
788                '--boot_img', boot_img,
789                '--algorithm', 'SHA256_RSA2048',
790                '--key', './testdata/testkey_rsa2048.pem',
791                '--extra_args', '--prop gki:nice '
792                '--prop space:"nice to meet you"',
793                '--output', boot_certified_img,
794            ]
795            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
796
797            extract_boot_signatures(boot_certified_img, temp_out_dir)
798            self._test_boot_signatures(
799                temp_out_dir,
800                {'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA2048,
801                 'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA2048})
802
803            # Generates the certified boot image again, with a RSA4096 key.
804            boot_certified2_img = os.path.join(temp_out_dir,
805                                              'boot-certified2.img')
806            certify_bootimg_cmds = [
807                'certify_bootimg',
808                '--boot_img', boot_certified_img,
809                '--algorithm', 'SHA256_RSA4096',
810                '--key', './testdata/testkey_rsa4096.pem',
811                '--extra_args', '--prop gki:nice '
812                '--prop space:"nice to meet you"',
813                '--output', boot_certified2_img,
814            ]
815            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
816
817            extract_boot_signatures(boot_certified2_img, temp_out_dir)
818            self._test_boot_signatures(
819                temp_out_dir,
820                {'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA4096,
821                 'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA4096})
822
823    def test_certify_bootimg_with_avb_footer(self):
824        """Tests the AVB footer location remains after certify_bootimg."""
825        with tempfile.TemporaryDirectory() as temp_out_dir:
826            boot_img = os.path.join(temp_out_dir, 'boot.img')
827            generate_test_boot_image(boot_img=boot_img,
828                                     avb_partition_size=128 * 1024)
829            self.assertTrue(has_avb_footer(boot_img))
830
831            # Generates the certified boot image, with a RSA2048 key.
832            boot_certified_img = os.path.join(temp_out_dir,
833                                              'boot-certified.img')
834            certify_bootimg_cmds = [
835                'certify_bootimg',
836                '--boot_img', boot_img,
837                '--algorithm', 'SHA256_RSA2048',
838                '--key', './testdata/testkey_rsa2048.pem',
839                '--extra_args', '--prop gki:nice '
840                '--prop space:"nice to meet you"',
841                '--extra_footer_args', '--salt a11ba11b --prop avb:nice '
842                '--prop avb_space:"nice to meet you"',
843                '--output', boot_certified_img,
844            ]
845            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
846
847            # Checks an AVB footer exists and the image size remains.
848            self.assertTrue(has_avb_footer(boot_certified_img))
849            self.assertEqual(os.path.getsize(boot_img),
850                             os.path.getsize(boot_certified_img))
851            # Checks the content in the AVB footer.
852            self._test_boot_signatures(
853                temp_out_dir,
854                {'boot-certified.img':
855                    self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED})
856
857            # Checks the content in the GKI certificate.
858            extract_boot_signatures(boot_certified_img, temp_out_dir)
859            self._test_boot_signatures(
860                temp_out_dir,
861                {'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA2048,
862                 'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA2048})
863
864            # Generates the certified boot image again, with a RSA4096 key.
865            boot_certified2_img = os.path.join(temp_out_dir,
866                                              'boot-certified2.img')
867            certify_bootimg_cmds = [
868                'certify_bootimg',
869                '--boot_img', boot_certified_img,
870                '--algorithm', 'SHA256_RSA4096',
871                '--key', './testdata/testkey_rsa4096.pem',
872                '--extra_args', '--prop gki:nice '
873                '--prop space:"nice to meet you"',
874                '--extra_footer_args', '--salt a11ba11b --prop avb:nice '
875                '--prop avb_space:"nice to meet you"',
876                '--output', boot_certified2_img,
877            ]
878            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
879
880            # Checks an AVB footer exists and the image size remains.
881            self.assertTrue(has_avb_footer(boot_certified2_img))
882            self.assertEqual(os.path.getsize(boot_certified_img),
883                             os.path.getsize(boot_certified2_img))
884            # Checks the content in the AVB footer.
885            self._test_boot_signatures(
886                temp_out_dir,
887                {'boot-certified2.img':
888                    self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED_2})
889
890            # Checks the content in the GKI certificate.
891            extract_boot_signatures(boot_certified2_img, temp_out_dir)
892            self._test_boot_signatures(
893                temp_out_dir,
894                {'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA4096,
895                 'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA4096})
896
897    def test_certify_bootimg_with_gki_info(self):
898        """Tests certify_bootimg with --gki_info."""
899        with tempfile.TemporaryDirectory() as temp_out_dir:
900            boot_img = os.path.join(temp_out_dir, 'boot.img')
901            generate_test_boot_image(boot_img=boot_img,
902                                     avb_partition_size=128 * 1024)
903            self.assertTrue(has_avb_footer(boot_img))
904
905            gki_info = ('certify_bootimg_extra_args='
906                        '--prop KERNEL_RELEASE:5.10.42'
907                        '-android13-0-00544-ged21d463f856 '
908                        '--prop BRANCH:android13-5.10-2022-05 '
909                        '--prop BUILD_NUMBER:ab8295296 '
910                        '--prop GKI_INFO:"added here"\n'
911                        'certify_bootimg_extra_footer_args='
912                        '--prop com.android.build.boot.os_version:13 '
913                        '--prop com.android.build.boot.security_patch:'
914                        '2022-05-05\n')
915            gki_info_path = os.path.join(temp_out_dir, 'gki-info.txt')
916            with open(gki_info_path, 'w', encoding='utf-8') as f:
917                f.write(gki_info)
918
919            # Certifies the boot image with --gki_info.
920            boot_certified_img = os.path.join(temp_out_dir,
921                                              'boot-certified.img')
922            certify_bootimg_cmds = [
923                'certify_bootimg',
924                '--boot_img', boot_img,
925                '--algorithm', 'SHA256_RSA4096',
926                '--key', './testdata/testkey_rsa4096.pem',
927                '--extra_args', '--prop gki:nice '
928                '--prop space:"nice to meet you"',
929                '--extra_footer_args', '--salt a11ba11b --prop avb:nice '
930                '--prop avb_space:"nice to meet you"',
931                '--gki_info', gki_info_path,
932                '--output', boot_certified_img,
933            ]
934            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
935
936            # Checks an AVB footer exists and the image size remains.
937            self.assertTrue(has_avb_footer(boot_certified_img))
938            self.assertEqual(os.path.getsize(boot_img),
939                             os.path.getsize(boot_certified_img))
940
941            # Checks the content in the AVB footer.
942            self._test_boot_signatures(
943                temp_out_dir,
944                {'boot-certified.img': self._EXPECTED_AVB_FOOTER_WITH_GKI_INFO})
945
946            # Checks the content in the GKI certificate.
947            extract_boot_signatures(boot_certified_img, temp_out_dir)
948            self._test_boot_signatures(
949                temp_out_dir,
950                {'boot_signature1':
951                    self._EXPECTED_BOOT_SIGNATURE_WITH_GKI_INFO,
952                 'boot_signature2':
953                    self._EXPECTED_KERNEL_SIGNATURE_WITH_GKI_INFO})
954
955    def test_certify_bootimg_exceed_size(self):
956        """Tests the boot signature size exceeded max size of the signature."""
957        with tempfile.TemporaryDirectory() as temp_out_dir:
958            boot_img = os.path.join(temp_out_dir, 'boot.img')
959            generate_test_boot_image(boot_img)
960
961            # Certifies the boot.img with many --extra_args, and checks
962            # it will raise the ValueError() exception.
963            boot_certified_img = os.path.join(temp_out_dir,
964                                              'boot-certified.img')
965            certify_bootimg_cmds = [
966                'certify_bootimg',
967                '--boot_img', boot_img,
968                '--algorithm', 'SHA256_RSA2048',
969                '--key', './testdata/testkey_rsa2048.pem',
970                # Makes it exceed the signature max size.
971                '--extra_args', '--prop foo:bar --prop gki:nice ' * 128,
972                '--output', boot_certified_img,
973            ]
974
975            try:
976                subprocess.run(certify_bootimg_cmds, check=True,
977                               capture_output=True, cwd=self._exec_dir,
978                               encoding='utf-8')
979                self.fail('Exceeding signature size assertion is not raised')
980            except subprocess.CalledProcessError as err:
981                self.assertIn('ValueError: boot_signature size must be <= ',
982                              err.stderr)
983
984    def test_certify_bootimg_archive(self):
985        """Tests certify_bootimg for a boot images archive.."""
986        with tempfile.TemporaryDirectory() as temp_out_dir:
987            boot_img_archive_name = os.path.join(temp_out_dir, 'boot-img')
988            gki_info = ('certify_bootimg_extra_args='
989                        '--prop KERNEL_RELEASE:5.10.42'
990                        '-android13-0-00544-ged21d463f856 '
991                        '--prop BRANCH:android13-5.10-2022-05 '
992                        '--prop BUILD_NUMBER:ab8295296 '
993                        '--prop SPACE:"nice to meet you"\n'
994                        'certify_bootimg_extra_footer_args='
995                        '--prop com.android.build.boot.os_version:13 '
996                        '--prop com.android.build.boot.security_patch:'
997                        '2022-05-05\n')
998            boot_img_archive_path = generate_test_boot_image_archive(
999                boot_img_archive_name,
1000                'gztar',
1001                # A list of (boot_img_name, kernel_size, partition_size).
1002                [('boot.img', 8 * 1024, 128 * 1024),
1003                 ('boot-lz4.img', 16 * 1024, 256 * 1024)],
1004                gki_info)
1005
1006            # Certify the boot image archive, with a RSA4096 key.
1007            boot_certified_img_archive = os.path.join(
1008                temp_out_dir, 'boot-certified-img.tar.gz')
1009            certify_bootimg_cmds = [
1010                'certify_bootimg',
1011                '--boot_img_archive', boot_img_archive_path,
1012                '--algorithm', 'SHA256_RSA4096',
1013                '--key', './testdata/testkey_rsa4096.pem',
1014                '--extra_args', '--prop gki:nice '
1015                '--prop space:"nice to meet you"',
1016                '--extra_footer_args', '--salt a11ba11b --prop avb:nice '
1017                '--prop avb_space:"nice to meet you"',
1018                '--output', boot_certified_img_archive,
1019            ]
1020            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
1021
1022            extract_boot_archive_with_signatures(boot_certified_img_archive,
1023                                                 temp_out_dir)
1024
1025            # Checks an AVB footer exists and the image size remains.
1026            boot_img = os.path.join(temp_out_dir, 'boot.img')
1027            self.assertTrue(has_avb_footer(boot_img))
1028            self.assertEqual(os.path.getsize(boot_img), 128 * 1024)
1029
1030            boot_lz4_img = os.path.join(temp_out_dir, 'boot-lz4.img')
1031            self.assertTrue(has_avb_footer(boot_lz4_img))
1032            self.assertEqual(os.path.getsize(boot_lz4_img), 256 * 1024)
1033
1034            # Checks the content in the AVB footer.
1035            self._test_boot_signatures(
1036                temp_out_dir,
1037                {'boot.img': self._EXPECTED_AVB_FOOTER_BOOT,
1038                 'boot-lz4.img': self._EXPECTED_AVB_FOOTER_BOOT_LZ4})
1039
1040            # Checks the content in the GKI certificate.
1041            self._test_boot_signatures(
1042                temp_out_dir,
1043                {'boot/boot_signature1':
1044                    self._EXPECTED_BOOT_SIGNATURE1_RSA4096,
1045                 'boot/boot_signature2':
1046                    self._EXPECTED_BOOT_SIGNATURE2_RSA4096,
1047                 'boot-lz4/boot_signature1':
1048                    self._EXPECTED_BOOT_LZ4_SIGNATURE1_RSA4096,
1049                 'boot-lz4/boot_signature2':
1050                    self._EXPECTED_BOOT_LZ4_SIGNATURE2_RSA4096})
1051
1052    def test_certify_bootimg_archive_without_gki_info(self):
1053        """Tests certify_bootimg for a boot images archive."""
1054        with tempfile.TemporaryDirectory() as temp_out_dir:
1055            boot_img_archive_name = os.path.join(temp_out_dir, 'boot-img')
1056
1057            # Checks ceritfy_bootimg works for a boot images archive without a
1058            # gki-info.txt. Using *.zip -> *.tar.
1059            boot_img_archive_path = generate_test_boot_image_archive(
1060                boot_img_archive_name,
1061                'zip',
1062                # A list of (boot_img_name, kernel_size, partition_size).
1063                [('boot-gz.img', 8 * 1024, 128 * 1024)],
1064                gki_info=None)
1065            # Certify the boot image archive, with a RSA4096 key.
1066            boot_certified_img_archive = os.path.join(
1067                temp_out_dir, 'boot-certified-img.tar')
1068            certify_bootimg_cmds = [
1069                'certify_bootimg',
1070                '--boot_img_archive', boot_img_archive_path,
1071                '--algorithm', 'SHA256_RSA4096',
1072                '--key', './testdata/testkey_rsa4096.pem',
1073                '--extra_args', '--prop gki:nice '
1074                '--prop space:"nice to meet you"',
1075                '--extra_footer_args', '--salt a11ba11b --prop avb:nice '
1076                '--prop avb_space:"nice to meet you"',
1077                '--output', boot_certified_img_archive,
1078            ]
1079            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
1080
1081            # Checks ceritfy_bootimg works for a boot images archive with a
1082            # special gki-info.txt. Using *.tar -> *.tgz.
1083            boot_img_archive_path = generate_test_boot_image_archive(
1084                boot_img_archive_name,
1085                'tar',
1086                # A list of (boot_img_name, kernel_size, partition_size).
1087                [('boot-gz.img', 8 * 1024, 128 * 1024)],
1088                gki_info='a=b\n'
1089                         'c=d\n')
1090            # Certify the boot image archive, with a RSA4096 key.
1091            boot_certified_img_archive2 = os.path.join(
1092                temp_out_dir, 'boot-certified-img.tgz')
1093            certify_bootimg_cmds = [
1094                'certify_bootimg',
1095                '--boot_img_archive', boot_img_archive_path,
1096                '--algorithm', 'SHA256_RSA4096',
1097                '--key', './testdata/testkey_rsa4096.pem',
1098                '--extra_args', '--prop gki:nice '
1099                '--prop space:"nice to meet you"',
1100                '--extra_footer_args', '--salt a11ba11b --prop avb:nice '
1101                '--prop avb_space:"nice to meet you"',
1102                '--output', boot_certified_img_archive2,
1103            ]
1104            subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
1105
1106            extract_boot_archive_with_signatures(boot_certified_img_archive2,
1107                                                 temp_out_dir)
1108
1109            # Checks an AVB footer exists and the image size remains.
1110            boot_3_img = os.path.join(temp_out_dir, 'boot-gz.img')
1111            self.assertTrue(has_avb_footer(boot_3_img))
1112            self.assertEqual(os.path.getsize(boot_3_img), 128 * 1024)
1113
1114            # Checks the content in the AVB footer.
1115            self._test_boot_signatures(
1116                temp_out_dir,
1117                {'boot-gz.img': self._EXPECTED_AVB_FOOTER_BOOT_GZ})
1118
1119            # Checks the content in the GKI certificate.
1120            self._test_boot_signatures(
1121                temp_out_dir,
1122                {'boot-gz/boot_signature1':
1123                    self._EXPECTED_BOOT_GZ_SIGNATURE1_RSA4096,
1124                 'boot-gz/boot_signature2':
1125                    self._EXPECTED_BOOT_GZ_SIGNATURE2_RSA4096})
1126
1127
1128# I don't know how, but we need both the logger configuration and verbosity
1129# level > 2 to make atest work. And yes this line needs to be at the very top
1130# level, not even in the "__main__" indentation block.
1131logging.basicConfig(stream=sys.stdout)
1132
1133if __name__ == '__main__':
1134    unittest.main(verbosity=2)
1135