1134 lines
51 KiB
Python
Executable file
1134 lines
51 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# Copyright 2022, The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Tests certify_bootimg."""
|
|
|
|
import logging
|
|
import glob
|
|
import os
|
|
import random
|
|
import shutil
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
BOOT_SIGNATURE_SIZE = 16 * 1024
|
|
|
|
TEST_KERNEL_CMDLINE = (
|
|
'printk.devkmsg=on firmware_class.path=/vendor/etc/ init=/init '
|
|
'kfence.sample_interval=500 loop.max_part=7 bootconfig'
|
|
)
|
|
|
|
|
|
def generate_test_file(pathname, size, seed=None):
|
|
"""Generates a gibberish-filled test file and returns its pathname."""
|
|
random.seed(os.path.basename(pathname) if seed is None else seed)
|
|
with open(pathname, 'wb') as file:
|
|
file.write(random.randbytes(size))
|
|
return pathname
|
|
|
|
|
|
def generate_test_boot_image(boot_img, kernel_size=4096, seed='kernel',
|
|
avb_partition_size=None):
|
|
"""Generates a test boot.img without a ramdisk."""
|
|
with tempfile.NamedTemporaryFile() as kernel_tmpfile:
|
|
generate_test_file(kernel_tmpfile.name, kernel_size, seed)
|
|
kernel_tmpfile.flush()
|
|
|
|
mkbootimg_cmds = [
|
|
'mkbootimg',
|
|
'--header_version', '4',
|
|
'--kernel', kernel_tmpfile.name,
|
|
'--cmdline', TEST_KERNEL_CMDLINE,
|
|
'--os_version', '12.0.0',
|
|
'--os_patch_level', '2022-03',
|
|
'--output', boot_img,
|
|
]
|
|
subprocess.check_call(mkbootimg_cmds)
|
|
|
|
if avb_partition_size:
|
|
avbtool_cmd = ['avbtool', 'add_hash_footer', '--image', boot_img,
|
|
'--partition_name', 'boot',
|
|
'--partition_size', str(avb_partition_size)]
|
|
subprocess.check_call(avbtool_cmd)
|
|
|
|
|
|
def generate_test_boot_image_archive(archive_file_name, archive_format,
|
|
boot_img_info, gki_info=None):
|
|
"""Generates an archive of test boot images.
|
|
|
|
It also adds a file gki-info.txt, which contains additional settings for
|
|
for `certify_bootimg --extra_args`.
|
|
|
|
Args:
|
|
archive_file_name: the name of the archive file to create, including the
|
|
path, minus any format-specific extension.
|
|
archive_format: the |format| parameter for shutil.make_archive().
|
|
e.g., 'zip', 'tar', or 'gztar', etc.
|
|
boot_img_info: a list of (boot_image_name, kernel_size,
|
|
partition_size) tuples. e.g.,
|
|
[('boot.img', 4096, 4 * 1024),
|
|
('boot-lz4.img', 8192, 8 * 1024)].
|
|
gki_info: the file content to be written into 'gki-info.txt' in the
|
|
created archive.
|
|
|
|
Returns:
|
|
The full path of the created archive. e.g., /path/to/boot-img.tar.gz.
|
|
"""
|
|
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
for name, kernel_size, partition_size in boot_img_info:
|
|
boot_img = os.path.join(temp_out_dir, name)
|
|
generate_test_boot_image(boot_img=boot_img,
|
|
kernel_size=kernel_size,
|
|
seed=name,
|
|
avb_partition_size=partition_size)
|
|
|
|
if gki_info:
|
|
gki_info_path = os.path.join(temp_out_dir, 'gki-info.txt')
|
|
with open(gki_info_path, 'w', encoding='utf-8') as f:
|
|
f.write(gki_info)
|
|
|
|
return shutil.make_archive(archive_file_name,
|
|
archive_format,
|
|
temp_out_dir)
|
|
|
|
|
|
def has_avb_footer(image):
|
|
"""Returns true if the image has a avb footer."""
|
|
|
|
avbtool_info_cmd = ['avbtool', 'info_image', '--image', image]
|
|
result = subprocess.run(avbtool_info_cmd, check=False,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL)
|
|
|
|
return result.returncode == 0
|
|
|
|
|
|
def get_vbmeta_size(vbmeta_bytes):
|
|
"""Returns the total size of a AvbVBMeta image."""
|
|
|
|
# Keep in sync with |AvbVBMetaImageHeader|.
|
|
AVB_MAGIC = b'AVB0' # pylint: disable=C0103
|
|
AVB_VBMETA_IMAGE_HEADER_SIZE = 256 # pylint: disable=C0103
|
|
FORMAT_STRING = ( # pylint: disable=C0103
|
|
'!4s2L' # magic, 2 x version.
|
|
'2Q' # 2 x block size: Authentication and Auxiliary blocks.
|
|
)
|
|
|
|
if len(vbmeta_bytes) < struct.calcsize(FORMAT_STRING):
|
|
return 0
|
|
|
|
data = vbmeta_bytes[:struct.calcsize(FORMAT_STRING)]
|
|
(magic, _, _,
|
|
authentication_block_size,
|
|
auxiliary_data_block_size) = struct.unpack(FORMAT_STRING, data)
|
|
|
|
if magic == AVB_MAGIC:
|
|
return (AVB_VBMETA_IMAGE_HEADER_SIZE +
|
|
authentication_block_size +
|
|
auxiliary_data_block_size)
|
|
return 0
|
|
|
|
|
|
def extract_boot_signatures(boot_img, output_dir):
|
|
"""Extracts the boot signatures of a boot image.
|
|
|
|
This functions extracts the boot signatures of |boot_img| as:
|
|
- |output_dir|/boot_signature1
|
|
- |output_dir|/boot_signature2
|
|
"""
|
|
|
|
boot_img_copy = os.path.join(output_dir, 'boot_image_copy')
|
|
shutil.copy2(boot_img, boot_img_copy)
|
|
avbtool_cmd = ['avbtool', 'erase_footer', '--image', boot_img_copy]
|
|
subprocess.run(avbtool_cmd, check=False, stderr=subprocess.DEVNULL)
|
|
|
|
# The boot signature is assumed to be at the end of boot image, after
|
|
# the AVB footer is erased.
|
|
with open(boot_img_copy, 'rb') as image:
|
|
image.seek(-BOOT_SIGNATURE_SIZE, os.SEEK_END)
|
|
boot_signature_bytes = image.read(BOOT_SIGNATURE_SIZE)
|
|
assert len(boot_signature_bytes) == BOOT_SIGNATURE_SIZE
|
|
os.unlink(boot_img_copy)
|
|
|
|
num_signatures = 0
|
|
while True:
|
|
next_signature_size = get_vbmeta_size(boot_signature_bytes)
|
|
if next_signature_size <= 0:
|
|
break
|
|
|
|
num_signatures += 1
|
|
next_signature = boot_signature_bytes[:next_signature_size]
|
|
output_path = os.path.join(
|
|
output_dir, 'boot_signature' + str(num_signatures))
|
|
with open(output_path, 'wb') as output:
|
|
output.write(next_signature)
|
|
|
|
# Moves to the next signature.
|
|
boot_signature_bytes = boot_signature_bytes[next_signature_size:]
|
|
|
|
|
|
def extract_boot_archive_with_signatures(boot_img_archive, output_dir):
|
|
"""Extracts boot images and signatures of a boot images archive.
|
|
|
|
Suppose there are two boot images in |boot_img_archive|: boot.img
|
|
and boot-lz4.img. This function then extracts each boot*.img and
|
|
their signatures as:
|
|
- |output_dir|/boot.img
|
|
- |output_dir|/boot-lz4.img
|
|
- |output_dir|/boot/boot_signature1
|
|
- |output_dir|/boot/boot_signature2
|
|
- |output_dir|/boot-lz4/boot_signature1
|
|
- |output_dir|/boot-lz4/boot_signature2
|
|
"""
|
|
shutil.unpack_archive(boot_img_archive, output_dir)
|
|
for boot_img in glob.glob(os.path.join(output_dir, 'boot*.img')):
|
|
img_name = os.path.splitext(os.path.basename(boot_img))[0]
|
|
signature_output_dir = os.path.join(output_dir, img_name)
|
|
os.mkdir(signature_output_dir, 0o777)
|
|
extract_boot_signatures(boot_img, signature_output_dir)
|
|
|
|
|
|
class CertifyBootimgTest(unittest.TestCase):
|
|
"""Tests the functionalities of certify_bootimg."""
|
|
|
|
def setUp(self):
|
|
# Saves the test executable directory so that relative path references
|
|
# to test dependencies don't rely on being manually run from the
|
|
# executable directory.
|
|
# With this, we can just open "./testdata/testkey_rsa2048.pem" in the
|
|
# following tests with subprocess.run(..., cwd=self._exec_dir, ...).
|
|
self._exec_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
|
|
|
|
# Set self.maxDiff to None to see full diff in assertion.
|
|
# C0103: invalid-name for maxDiff.
|
|
self.maxDiff = None # pylint: disable=C0103
|
|
|
|
# For AVB footers, we don't sign it so the Authentication block
|
|
# is zero bytes and the Algorithm is NONE. The footer will be
|
|
# replaced by device-specific settings when being incorporated into
|
|
# a device codebase. The footer here is just to pass some GKI
|
|
# pre-release test.
|
|
self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED = ( # pylint: disable=C0103
|
|
'Footer version: 1.0\n'
|
|
'Image size: 131072 bytes\n'
|
|
'Original image size: 24576 bytes\n'
|
|
'VBMeta offset: 24576\n'
|
|
'VBMeta size: 576 bytes\n'
|
|
'--\n'
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 0 bytes\n'
|
|
'Auxiliary Block: 320 bytes\n'
|
|
'Algorithm: NONE\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 24576 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n'
|
|
' Salt: a11ba11b\n'
|
|
' Digest: '
|
|
'c9b4ad78fae6f72f7eff939dee6078ed'
|
|
'8a75132e53f6c11ba1ec0f4b57f9eab0\n'
|
|
' Flags: 0\n'
|
|
" Prop: avb -> 'nice'\n"
|
|
" Prop: avb_space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED_2 = ( # pylint: disable=C0103
|
|
'Footer version: 1.0\n'
|
|
'Image size: 131072 bytes\n'
|
|
'Original image size: 24576 bytes\n'
|
|
'VBMeta offset: 24576\n'
|
|
'VBMeta size: 576 bytes\n'
|
|
'--\n'
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 0 bytes\n'
|
|
'Auxiliary Block: 320 bytes\n'
|
|
'Algorithm: NONE\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 24576 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n'
|
|
' Salt: a11ba11b\n'
|
|
' Digest: '
|
|
'ae2538e78b2a30b1112cede30d858a5f'
|
|
'6f8dc2a1b109dd4a7bb28124b77d2ab0\n'
|
|
' Flags: 0\n'
|
|
" Prop: avb -> 'nice'\n"
|
|
" Prop: avb_space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_AVB_FOOTER_WITH_GKI_INFO = ( # pylint: disable=C0103
|
|
'Footer version: 1.0\n'
|
|
'Image size: 131072 bytes\n'
|
|
'Original image size: 24576 bytes\n'
|
|
'VBMeta offset: 24576\n'
|
|
'VBMeta size: 704 bytes\n'
|
|
'--\n'
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 0 bytes\n'
|
|
'Auxiliary Block: 448 bytes\n'
|
|
'Algorithm: NONE\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 24576 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n'
|
|
' Salt: a11ba11b\n'
|
|
' Digest: '
|
|
'363d4f246a4a5e1bba8ba8b86f5eb0cf'
|
|
'9817e4e51663ba26edccf71c3861090a\n'
|
|
' Flags: 0\n'
|
|
" Prop: avb -> 'nice'\n"
|
|
" Prop: avb_space -> 'nice to meet you'\n"
|
|
" Prop: com.android.build.boot.os_version -> '13'\n"
|
|
" Prop: com.android.build.boot.security_patch -> '2022-05-05'\n"
|
|
)
|
|
|
|
self._EXPECTED_AVB_FOOTER_BOOT = ( # pylint: disable=C0103
|
|
'Footer version: 1.0\n'
|
|
'Image size: 131072 bytes\n'
|
|
'Original image size: 28672 bytes\n'
|
|
'VBMeta offset: 28672\n'
|
|
'VBMeta size: 704 bytes\n'
|
|
'--\n'
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 0 bytes\n'
|
|
'Auxiliary Block: 448 bytes\n'
|
|
'Algorithm: NONE\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 28672 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n'
|
|
' Salt: a11ba11b\n'
|
|
' Digest: '
|
|
'b93084707ba2367120e19547f17f1073'
|
|
'4c7ad8e56008ec2159d5f01b950335ad\n'
|
|
' Flags: 0\n'
|
|
" Prop: avb -> 'nice'\n"
|
|
" Prop: avb_space -> 'nice to meet you'\n"
|
|
" Prop: com.android.build.boot.os_version -> '13'\n"
|
|
" Prop: com.android.build.boot.security_patch -> '2022-05-05'\n"
|
|
)
|
|
|
|
self._EXPECTED_AVB_FOOTER_BOOT_LZ4 = ( # pylint: disable=C0103
|
|
'Footer version: 1.0\n'
|
|
'Image size: 262144 bytes\n'
|
|
'Original image size: 36864 bytes\n'
|
|
'VBMeta offset: 36864\n'
|
|
'VBMeta size: 704 bytes\n'
|
|
'--\n'
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 0 bytes\n'
|
|
'Auxiliary Block: 448 bytes\n'
|
|
'Algorithm: NONE\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 36864 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n'
|
|
' Salt: a11ba11b\n'
|
|
' Digest: '
|
|
'6b3f583f1bc5fbc284102e0185d02c6b'
|
|
'294f675c95b9337e89ea1e6b743af2ab\n'
|
|
' Flags: 0\n'
|
|
" Prop: avb -> 'nice'\n"
|
|
" Prop: avb_space -> 'nice to meet you'\n"
|
|
" Prop: com.android.build.boot.os_version -> '13'\n"
|
|
" Prop: com.android.build.boot.security_patch -> '2022-05-05'\n"
|
|
)
|
|
|
|
self._EXPECTED_AVB_FOOTER_BOOT_GZ = ( # pylint: disable=C0103
|
|
'Footer version: 1.0\n'
|
|
'Image size: 131072 bytes\n'
|
|
'Original image size: 28672 bytes\n'
|
|
'VBMeta offset: 28672\n'
|
|
'VBMeta size: 576 bytes\n'
|
|
'--\n'
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 0 bytes\n'
|
|
'Auxiliary Block: 320 bytes\n'
|
|
'Algorithm: NONE\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 28672 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n'
|
|
' Salt: a11ba11b\n'
|
|
' Digest: '
|
|
'd2098d507e039afc6b4d7ec3de129a8d'
|
|
'd0e0cf889c9181ebee65ce2fb25de3f5\n'
|
|
' Flags: 0\n'
|
|
" Prop: avb -> 'nice'\n"
|
|
" Prop: avb_space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_SIGNATURE_RSA2048 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 320 bytes\n'
|
|
'Auxiliary Block: 832 bytes\n'
|
|
'Public key (sha1): '
|
|
'cdbb77177f731920bbe0a0f94f84d9038ae0617d\n'
|
|
'Algorithm: SHA256_RSA2048\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 8192 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n' # boot
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'faf1da72a4fba97ddab0b8f7a410db86'
|
|
'8fb72392a66d1440ff8bff490c73c771\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_KERNEL_SIGNATURE_RSA2048 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 320 bytes\n'
|
|
'Auxiliary Block: 832 bytes\n'
|
|
'Public key (sha1): '
|
|
'cdbb77177f731920bbe0a0f94f84d9038ae0617d\n'
|
|
'Algorithm: SHA256_RSA2048\n'
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 4096 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: generic_kernel\n' # generic_kernel
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'762c877f3af0d50a4a4fbc1385d5c7ce'
|
|
'52a1288db74b33b72217d93db6f2909f\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_SIGNATURE_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1344 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 8192 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n' # boot
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'faf1da72a4fba97ddab0b8f7a410db86'
|
|
'8fb72392a66d1440ff8bff490c73c771\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_KERNEL_SIGNATURE_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1344 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 4096 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: generic_kernel\n' # generic_kernel
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'762c877f3af0d50a4a4fbc1385d5c7ce'
|
|
'52a1288db74b33b72217d93db6f2909f\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_SIGNATURE_WITH_GKI_INFO = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1600 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 8192 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n' # boot
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'faf1da72a4fba97ddab0b8f7a410db86'
|
|
'8fb72392a66d1440ff8bff490c73c771\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
" Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
|
|
"ged21d463f856'\n"
|
|
" Prop: BRANCH -> 'android13-5.10-2022-05'\n"
|
|
" Prop: BUILD_NUMBER -> 'ab8295296'\n"
|
|
" Prop: GKI_INFO -> 'added here'\n"
|
|
)
|
|
|
|
self._EXPECTED_KERNEL_SIGNATURE_WITH_GKI_INFO = (# pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1600 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 4096 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: generic_kernel\n' # generic_kernel
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'762c877f3af0d50a4a4fbc1385d5c7ce'
|
|
'52a1288db74b33b72217d93db6f2909f\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
" Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
|
|
"ged21d463f856'\n"
|
|
" Prop: BRANCH -> 'android13-5.10-2022-05'\n"
|
|
" Prop: BUILD_NUMBER -> 'ab8295296'\n"
|
|
" Prop: GKI_INFO -> 'added here'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1600 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 12288 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n' # boot
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'30208b4d0a6d16db47fc13c9527bfe81'
|
|
'a168d3b3940325d1ca8d3439792bfe18\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
" Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
|
|
"ged21d463f856'\n"
|
|
" Prop: BRANCH -> 'android13-5.10-2022-05'\n"
|
|
" Prop: BUILD_NUMBER -> 'ab8295296'\n"
|
|
" Prop: SPACE -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1600 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 8192 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: generic_kernel\n' # generic_kernel
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'd4c8847e7d9900a98f77e1f0b5272854'
|
|
'7bf9c1e428fea500d419275f72ec5bd6\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
" Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
|
|
"ged21d463f856'\n"
|
|
" Prop: BRANCH -> 'android13-5.10-2022-05'\n"
|
|
" Prop: BUILD_NUMBER -> 'ab8295296'\n"
|
|
" Prop: SPACE -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_LZ4_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1600 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 20480 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n' # boot
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'9d3a0670a9fd3de66e940117ef97700f'
|
|
'ed5fd1c6fb90798fd3873af45fc91cb4\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
" Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
|
|
"ged21d463f856'\n"
|
|
" Prop: BRANCH -> 'android13-5.10-2022-05'\n"
|
|
" Prop: BUILD_NUMBER -> 'ab8295296'\n"
|
|
" Prop: SPACE -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_LZ4_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1600 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 16384 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: generic_kernel\n' # generic_kernel
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'7d109e3dccca9e30e04249162d07e58c'
|
|
'62fdf269804b35857b956fba339b2679\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
" Prop: KERNEL_RELEASE -> '5.10.42-android13-0-00544-"
|
|
"ged21d463f856'\n"
|
|
" Prop: BRANCH -> 'android13-5.10-2022-05'\n"
|
|
" Prop: BUILD_NUMBER -> 'ab8295296'\n"
|
|
" Prop: SPACE -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_GZ_SIGNATURE1_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1344 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 12288 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: boot\n' # boot
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'6fcddc6167ae3c2037b424d35c3ef107'
|
|
'f586510dbb2d652d7c08b88e6ea52fc6\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
self._EXPECTED_BOOT_GZ_SIGNATURE2_RSA4096 = ( # pylint: disable=C0103
|
|
'Minimum libavb version: 1.0\n'
|
|
'Header Block: 256 bytes\n'
|
|
'Authentication Block: 576 bytes\n'
|
|
'Auxiliary Block: 1344 bytes\n'
|
|
'Public key (sha1): '
|
|
'2597c218aae470a130f61162feaae70afd97f011\n'
|
|
'Algorithm: SHA256_RSA4096\n' # RSA4096
|
|
'Rollback Index: 0\n'
|
|
'Flags: 0\n'
|
|
'Rollback Index Location: 0\n'
|
|
"Release String: 'avbtool 1.2.0'\n"
|
|
'Descriptors:\n'
|
|
' Hash descriptor:\n'
|
|
' Image Size: 8192 bytes\n'
|
|
' Hash Algorithm: sha256\n'
|
|
' Partition Name: generic_kernel\n' # generic_kernel
|
|
' Salt: d00df00d\n'
|
|
' Digest: '
|
|
'7a6a43eb4048b783346fb6d039103647'
|
|
'6c4313146da521467af282dff1838d0e\n'
|
|
' Flags: 0\n'
|
|
" Prop: gki -> 'nice'\n"
|
|
" Prop: space -> 'nice to meet you'\n"
|
|
)
|
|
|
|
def _test_boot_signatures(self, signatures_dir, expected_signatures_info):
|
|
"""Tests the info of each boot signature under the signature directory.
|
|
|
|
Args:
|
|
signatures_dir: the directory containing the boot signatures. e.g.,
|
|
- signatures_dir/boot_signature1
|
|
- signatures_dir/boot_signature2
|
|
expected_signatures_info: A dict containing the expected output
|
|
of `avbtool info_image` for each signature under
|
|
|signatures_dir|. e.g.,
|
|
{'boot_signature1': expected_stdout_signature1
|
|
'boot_signature2': expected_stdout_signature2}
|
|
"""
|
|
for signature in expected_signatures_info:
|
|
avbtool_info_cmds = [
|
|
'avbtool', 'info_image', '--image',
|
|
os.path.join(signatures_dir, signature)
|
|
]
|
|
result = subprocess.run(avbtool_info_cmds, check=True,
|
|
capture_output=True, encoding='utf-8')
|
|
self.assertEqual(result.stdout, expected_signatures_info[signature])
|
|
|
|
def test_certify_bootimg_without_avb_footer(self):
|
|
"""Tests certify_bootimg on a boot image without an AVB footer."""
|
|
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
boot_img = os.path.join(temp_out_dir, 'boot.img')
|
|
generate_test_boot_image(boot_img)
|
|
|
|
# Generates the certified boot image, with a RSA2048 key.
|
|
boot_certified_img = os.path.join(temp_out_dir,
|
|
'boot-certified.img')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img', boot_img,
|
|
'--algorithm', 'SHA256_RSA2048',
|
|
'--key', './testdata/testkey_rsa2048.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--output', boot_certified_img,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
extract_boot_signatures(boot_certified_img, temp_out_dir)
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA2048,
|
|
'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA2048})
|
|
|
|
# Generates the certified boot image again, with a RSA4096 key.
|
|
boot_certified2_img = os.path.join(temp_out_dir,
|
|
'boot-certified2.img')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img', boot_certified_img,
|
|
'--algorithm', 'SHA256_RSA4096',
|
|
'--key', './testdata/testkey_rsa4096.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--output', boot_certified2_img,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
extract_boot_signatures(boot_certified2_img, temp_out_dir)
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA4096,
|
|
'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA4096})
|
|
|
|
def test_certify_bootimg_with_avb_footer(self):
|
|
"""Tests the AVB footer location remains after certify_bootimg."""
|
|
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
boot_img = os.path.join(temp_out_dir, 'boot.img')
|
|
generate_test_boot_image(boot_img=boot_img,
|
|
avb_partition_size=128 * 1024)
|
|
self.assertTrue(has_avb_footer(boot_img))
|
|
|
|
# Generates the certified boot image, with a RSA2048 key.
|
|
boot_certified_img = os.path.join(temp_out_dir,
|
|
'boot-certified.img')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img', boot_img,
|
|
'--algorithm', 'SHA256_RSA2048',
|
|
'--key', './testdata/testkey_rsa2048.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--extra_footer_args', '--salt a11ba11b --prop avb:nice '
|
|
'--prop avb_space:"nice to meet you"',
|
|
'--output', boot_certified_img,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
# Checks an AVB footer exists and the image size remains.
|
|
self.assertTrue(has_avb_footer(boot_certified_img))
|
|
self.assertEqual(os.path.getsize(boot_img),
|
|
os.path.getsize(boot_certified_img))
|
|
# Checks the content in the AVB footer.
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot-certified.img':
|
|
self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED})
|
|
|
|
# Checks the content in the GKI certificate.
|
|
extract_boot_signatures(boot_certified_img, temp_out_dir)
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA2048,
|
|
'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA2048})
|
|
|
|
# Generates the certified boot image again, with a RSA4096 key.
|
|
boot_certified2_img = os.path.join(temp_out_dir,
|
|
'boot-certified2.img')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img', boot_certified_img,
|
|
'--algorithm', 'SHA256_RSA4096',
|
|
'--key', './testdata/testkey_rsa4096.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--extra_footer_args', '--salt a11ba11b --prop avb:nice '
|
|
'--prop avb_space:"nice to meet you"',
|
|
'--output', boot_certified2_img,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
# Checks an AVB footer exists and the image size remains.
|
|
self.assertTrue(has_avb_footer(boot_certified2_img))
|
|
self.assertEqual(os.path.getsize(boot_certified_img),
|
|
os.path.getsize(boot_certified2_img))
|
|
# Checks the content in the AVB footer.
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot-certified2.img':
|
|
self._EXPECTED_AVB_FOOTER_BOOT_CERTIFIED_2})
|
|
|
|
# Checks the content in the GKI certificate.
|
|
extract_boot_signatures(boot_certified2_img, temp_out_dir)
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot_signature1': self._EXPECTED_BOOT_SIGNATURE_RSA4096,
|
|
'boot_signature2': self._EXPECTED_KERNEL_SIGNATURE_RSA4096})
|
|
|
|
def test_certify_bootimg_with_gki_info(self):
|
|
"""Tests certify_bootimg with --gki_info."""
|
|
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
boot_img = os.path.join(temp_out_dir, 'boot.img')
|
|
generate_test_boot_image(boot_img=boot_img,
|
|
avb_partition_size=128 * 1024)
|
|
self.assertTrue(has_avb_footer(boot_img))
|
|
|
|
gki_info = ('certify_bootimg_extra_args='
|
|
'--prop KERNEL_RELEASE:5.10.42'
|
|
'-android13-0-00544-ged21d463f856 '
|
|
'--prop BRANCH:android13-5.10-2022-05 '
|
|
'--prop BUILD_NUMBER:ab8295296 '
|
|
'--prop GKI_INFO:"added here"\n'
|
|
'certify_bootimg_extra_footer_args='
|
|
'--prop com.android.build.boot.os_version:13 '
|
|
'--prop com.android.build.boot.security_patch:'
|
|
'2022-05-05\n')
|
|
gki_info_path = os.path.join(temp_out_dir, 'gki-info.txt')
|
|
with open(gki_info_path, 'w', encoding='utf-8') as f:
|
|
f.write(gki_info)
|
|
|
|
# Certifies the boot image with --gki_info.
|
|
boot_certified_img = os.path.join(temp_out_dir,
|
|
'boot-certified.img')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img', boot_img,
|
|
'--algorithm', 'SHA256_RSA4096',
|
|
'--key', './testdata/testkey_rsa4096.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--extra_footer_args', '--salt a11ba11b --prop avb:nice '
|
|
'--prop avb_space:"nice to meet you"',
|
|
'--gki_info', gki_info_path,
|
|
'--output', boot_certified_img,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
# Checks an AVB footer exists and the image size remains.
|
|
self.assertTrue(has_avb_footer(boot_certified_img))
|
|
self.assertEqual(os.path.getsize(boot_img),
|
|
os.path.getsize(boot_certified_img))
|
|
|
|
# Checks the content in the AVB footer.
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot-certified.img': self._EXPECTED_AVB_FOOTER_WITH_GKI_INFO})
|
|
|
|
# Checks the content in the GKI certificate.
|
|
extract_boot_signatures(boot_certified_img, temp_out_dir)
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot_signature1':
|
|
self._EXPECTED_BOOT_SIGNATURE_WITH_GKI_INFO,
|
|
'boot_signature2':
|
|
self._EXPECTED_KERNEL_SIGNATURE_WITH_GKI_INFO})
|
|
|
|
def test_certify_bootimg_exceed_size(self):
|
|
"""Tests the boot signature size exceeded max size of the signature."""
|
|
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
boot_img = os.path.join(temp_out_dir, 'boot.img')
|
|
generate_test_boot_image(boot_img)
|
|
|
|
# Certifies the boot.img with many --extra_args, and checks
|
|
# it will raise the ValueError() exception.
|
|
boot_certified_img = os.path.join(temp_out_dir,
|
|
'boot-certified.img')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img', boot_img,
|
|
'--algorithm', 'SHA256_RSA2048',
|
|
'--key', './testdata/testkey_rsa2048.pem',
|
|
# Makes it exceed the signature max size.
|
|
'--extra_args', '--prop foo:bar --prop gki:nice ' * 128,
|
|
'--output', boot_certified_img,
|
|
]
|
|
|
|
try:
|
|
subprocess.run(certify_bootimg_cmds, check=True,
|
|
capture_output=True, cwd=self._exec_dir,
|
|
encoding='utf-8')
|
|
self.fail('Exceeding signature size assertion is not raised')
|
|
except subprocess.CalledProcessError as err:
|
|
self.assertIn('ValueError: boot_signature size must be <= ',
|
|
err.stderr)
|
|
|
|
def test_certify_bootimg_archive(self):
|
|
"""Tests certify_bootimg for a boot images archive.."""
|
|
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
boot_img_archive_name = os.path.join(temp_out_dir, 'boot-img')
|
|
gki_info = ('certify_bootimg_extra_args='
|
|
'--prop KERNEL_RELEASE:5.10.42'
|
|
'-android13-0-00544-ged21d463f856 '
|
|
'--prop BRANCH:android13-5.10-2022-05 '
|
|
'--prop BUILD_NUMBER:ab8295296 '
|
|
'--prop SPACE:"nice to meet you"\n'
|
|
'certify_bootimg_extra_footer_args='
|
|
'--prop com.android.build.boot.os_version:13 '
|
|
'--prop com.android.build.boot.security_patch:'
|
|
'2022-05-05\n')
|
|
boot_img_archive_path = generate_test_boot_image_archive(
|
|
boot_img_archive_name,
|
|
'gztar',
|
|
# A list of (boot_img_name, kernel_size, partition_size).
|
|
[('boot.img', 8 * 1024, 128 * 1024),
|
|
('boot-lz4.img', 16 * 1024, 256 * 1024)],
|
|
gki_info)
|
|
|
|
# Certify the boot image archive, with a RSA4096 key.
|
|
boot_certified_img_archive = os.path.join(
|
|
temp_out_dir, 'boot-certified-img.tar.gz')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img_archive', boot_img_archive_path,
|
|
'--algorithm', 'SHA256_RSA4096',
|
|
'--key', './testdata/testkey_rsa4096.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--extra_footer_args', '--salt a11ba11b --prop avb:nice '
|
|
'--prop avb_space:"nice to meet you"',
|
|
'--output', boot_certified_img_archive,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
extract_boot_archive_with_signatures(boot_certified_img_archive,
|
|
temp_out_dir)
|
|
|
|
# Checks an AVB footer exists and the image size remains.
|
|
boot_img = os.path.join(temp_out_dir, 'boot.img')
|
|
self.assertTrue(has_avb_footer(boot_img))
|
|
self.assertEqual(os.path.getsize(boot_img), 128 * 1024)
|
|
|
|
boot_lz4_img = os.path.join(temp_out_dir, 'boot-lz4.img')
|
|
self.assertTrue(has_avb_footer(boot_lz4_img))
|
|
self.assertEqual(os.path.getsize(boot_lz4_img), 256 * 1024)
|
|
|
|
# Checks the content in the AVB footer.
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot.img': self._EXPECTED_AVB_FOOTER_BOOT,
|
|
'boot-lz4.img': self._EXPECTED_AVB_FOOTER_BOOT_LZ4})
|
|
|
|
# Checks the content in the GKI certificate.
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot/boot_signature1':
|
|
self._EXPECTED_BOOT_SIGNATURE1_RSA4096,
|
|
'boot/boot_signature2':
|
|
self._EXPECTED_BOOT_SIGNATURE2_RSA4096,
|
|
'boot-lz4/boot_signature1':
|
|
self._EXPECTED_BOOT_LZ4_SIGNATURE1_RSA4096,
|
|
'boot-lz4/boot_signature2':
|
|
self._EXPECTED_BOOT_LZ4_SIGNATURE2_RSA4096})
|
|
|
|
def test_certify_bootimg_archive_without_gki_info(self):
|
|
"""Tests certify_bootimg for a boot images archive."""
|
|
with tempfile.TemporaryDirectory() as temp_out_dir:
|
|
boot_img_archive_name = os.path.join(temp_out_dir, 'boot-img')
|
|
|
|
# Checks ceritfy_bootimg works for a boot images archive without a
|
|
# gki-info.txt. Using *.zip -> *.tar.
|
|
boot_img_archive_path = generate_test_boot_image_archive(
|
|
boot_img_archive_name,
|
|
'zip',
|
|
# A list of (boot_img_name, kernel_size, partition_size).
|
|
[('boot-gz.img', 8 * 1024, 128 * 1024)],
|
|
gki_info=None)
|
|
# Certify the boot image archive, with a RSA4096 key.
|
|
boot_certified_img_archive = os.path.join(
|
|
temp_out_dir, 'boot-certified-img.tar')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img_archive', boot_img_archive_path,
|
|
'--algorithm', 'SHA256_RSA4096',
|
|
'--key', './testdata/testkey_rsa4096.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--extra_footer_args', '--salt a11ba11b --prop avb:nice '
|
|
'--prop avb_space:"nice to meet you"',
|
|
'--output', boot_certified_img_archive,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
# Checks ceritfy_bootimg works for a boot images archive with a
|
|
# special gki-info.txt. Using *.tar -> *.tgz.
|
|
boot_img_archive_path = generate_test_boot_image_archive(
|
|
boot_img_archive_name,
|
|
'tar',
|
|
# A list of (boot_img_name, kernel_size, partition_size).
|
|
[('boot-gz.img', 8 * 1024, 128 * 1024)],
|
|
gki_info='a=b\n'
|
|
'c=d\n')
|
|
# Certify the boot image archive, with a RSA4096 key.
|
|
boot_certified_img_archive2 = os.path.join(
|
|
temp_out_dir, 'boot-certified-img.tgz')
|
|
certify_bootimg_cmds = [
|
|
'certify_bootimg',
|
|
'--boot_img_archive', boot_img_archive_path,
|
|
'--algorithm', 'SHA256_RSA4096',
|
|
'--key', './testdata/testkey_rsa4096.pem',
|
|
'--extra_args', '--prop gki:nice '
|
|
'--prop space:"nice to meet you"',
|
|
'--extra_footer_args', '--salt a11ba11b --prop avb:nice '
|
|
'--prop avb_space:"nice to meet you"',
|
|
'--output', boot_certified_img_archive2,
|
|
]
|
|
subprocess.run(certify_bootimg_cmds, check=True, cwd=self._exec_dir)
|
|
|
|
extract_boot_archive_with_signatures(boot_certified_img_archive2,
|
|
temp_out_dir)
|
|
|
|
# Checks an AVB footer exists and the image size remains.
|
|
boot_3_img = os.path.join(temp_out_dir, 'boot-gz.img')
|
|
self.assertTrue(has_avb_footer(boot_3_img))
|
|
self.assertEqual(os.path.getsize(boot_3_img), 128 * 1024)
|
|
|
|
# Checks the content in the AVB footer.
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot-gz.img': self._EXPECTED_AVB_FOOTER_BOOT_GZ})
|
|
|
|
# Checks the content in the GKI certificate.
|
|
self._test_boot_signatures(
|
|
temp_out_dir,
|
|
{'boot-gz/boot_signature1':
|
|
self._EXPECTED_BOOT_GZ_SIGNATURE1_RSA4096,
|
|
'boot-gz/boot_signature2':
|
|
self._EXPECTED_BOOT_GZ_SIGNATURE2_RSA4096})
|
|
|
|
|
|
# I don't know how, but we need both the logger configuration and verbosity
|
|
# level > 2 to make atest work. And yes this line needs to be at the very top
|
|
# level, not even in the "__main__" indentation block.
|
|
logging.basicConfig(stream=sys.stdout)
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main(verbosity=2)
|