blob: f2c6ae7299b92d29ded95d4716cc2f24510d3d7e [file] [log] [blame]
"""Extracts the following hashes from the AVB footer of Microdroid's kernel:
- kernel hash
- initrd_normal hash
- initrd_debug hash
The hashes are written to stdout as a Rust file.
In unsupportive environments such as x86, when the kernel is just an empty file,
the output Rust file has the same hash constant fields for compatibility
reasons, but all of them are empty.
"""
#!/usr/bin/env python3
import argparse
from collections import defaultdict
import subprocess
from typing import Dict
PARTITION_NAME_BOOT = 'boot'
PARTITION_NAME_INITRD_NORMAL = 'initrd_normal'
PARTITION_NAME_INITRD_DEBUG = 'initrd_debug'
HASH_SIZE = 32
def main(args):
"""Main function."""
avbtool = args.avbtool
num_kernel_images = len(args.kernel)
print("//! This file is generated by extract_microdroid_kernel_hashes.py.")
print("//! It contains the hashes of the kernel and initrds.\n")
print("#![no_std]\n#![allow(missing_docs)]\n")
print("pub const HASH_SIZE: usize = " + str(HASH_SIZE) + ";\n")
print("pub struct OsHashes {")
print(" pub kernel: [u8; HASH_SIZE],")
print(" pub initrd_normal: [u8; HASH_SIZE],")
print(" pub initrd_debug: [u8; HASH_SIZE],")
print("}\n")
hashes = defaultdict(list)
for kernel_image_path in args.kernel:
collected_hashes = collect_hashes(avbtool, kernel_image_path)
if collected_hashes.keys() == {PARTITION_NAME_BOOT,
PARTITION_NAME_INITRD_NORMAL,
PARTITION_NAME_INITRD_DEBUG}:
for partition_name, v in collected_hashes.items():
hashes[partition_name].append(v)
else:
# Microdroid's kernel is just an empty file in unsupportive
# environments such as x86, in this case the hashes should be empty.
print("/// The kernel is empty, no hashes are available.")
hashes[PARTITION_NAME_BOOT].append("")
hashes[PARTITION_NAME_INITRD_NORMAL].append("")
hashes[PARTITION_NAME_INITRD_DEBUG].append("")
print("pub const OS_HASHES: [OsHashes; " + str(num_kernel_images) + "] = [")
for i in range(num_kernel_images):
print("OsHashes {")
print(" kernel: [" +
format_hex_string(hashes[PARTITION_NAME_BOOT][i]) + "],")
print(" initrd_normal: [" +
format_hex_string(hashes[PARTITION_NAME_INITRD_NORMAL][i]) + "],")
print(" initrd_debug: [" +
format_hex_string(hashes[PARTITION_NAME_INITRD_DEBUG][i]) + "],")
print("},")
print("];")
def collect_hashes(avbtool: str, kernel_image_path: str) -> Dict[str, str]:
"""Collects the hashes from the AVB footer of the kernel image."""
hashes = {}
with subprocess.Popen(
[avbtool, 'print_partition_digests', '--image', kernel_image_path],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc:
stdout, _ = proc.communicate()
for line in stdout.decode("utf-8").split("\n"):
line = line.replace(" ", "").split(":")
if len(line) == 2:
partition_name, hash_ = line
hashes[partition_name] = hash_
return hashes
def format_hex_string(hex_string: str) -> str:
"""Formats a hex string into a Rust array."""
if not hex_string:
return "0x00, " * HASH_SIZE
assert len(hex_string) == HASH_SIZE * 2, \
"Hex string must have length " + str(HASH_SIZE * 2) + ": " + \
hex_string
return ", ".join(["\n0x" + hex_string[i:i+2] if i % 32 == 0
else "0x" + hex_string[i:i+2]
for i in range(0, len(hex_string), 2)])
def parse_args():
"""Parses the command line arguments."""
parser = argparse.ArgumentParser(
"Extracts the hashes from the kernels' AVB footer")
parser.add_argument('--avbtool', help='Path to the avbtool binary')
parser.add_argument('--kernel', help='Path to the kernel image', nargs='+')
return parser.parse_args()
if __name__ == '__main__':
main(parse_args())