diff --git a/README.md b/README.md index eeecbcb..4a8cecf 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,9 @@ The `-v`/`--version` option must be set correctly depending on the chipset: - **`-v3` (default):** MSM8916, MSM8939, MSM8953 - MSM8909, MDM9607 (they usually accept unsigned ELF images as well) - **`-v5`:** MSM8998, SDM845 -- **`-v6`:** SM8150 +- **`-v6`:** SM8150, IPQ9574, IPQ5332 - In case of problems, try using `-v5` instead. Sometimes both seem to be supported. +- **`-v7`:** IPQ5424 The list of chipsets is not complete, most other Qualcomm chipsets likely use one of the already supported versions above or an older/newer version that is not supported yet by [qtestsign]. @@ -56,7 +57,7 @@ versions above or an older/newer version that is not supported yet by [qtestsign ## Supported firmware Qualcomm's own signing tool is proprietary and not publicly available. [qtestsign] was created to allow building open-source firmware projects without access to Qualcomm's tool, e.g.: -- `aboot`: [U-Boot] bootloader (for DragonBoard 410c) +- `aboot`: [U-Boot] bootloader (for DragonBoard 410c, QCA SoCs etc..) - `aboot`: [Qualcomm's fork of LK (Little Kernel)], used as bootloader on older platforms - `abl`: [Qualcomm's Android Bootloader for UEFI], used on newer platforms - `hyp`: [qhypstub], [tfalkstub] @@ -82,7 +83,10 @@ from Qualcomm's whitepaper _"Secure Boot and Image Authentication"_ (both [v1.0](https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf) and [v2.0](https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v2-0.pdf)). Some implementation details (e.g. the exact MBN header format) are adapted from [signlk] and [coreboot] -(`util/qualcomm/mbn_tools.py`) available under a `BSD-3-Clause` license. +(`util/qualcomm/mbn_tools.py`, `util/cbfstool/platform_fixups.c`) available under a `BSD-3-Clause` license. + +For QCA chipsets, Qualcomm maintains a similar set of tools at https://git.codelinaro.org/clo/qsdk/oss/system/tools/meta +available under a `ISC` license. [qtestsign]: https://github.com/msm8916-mainline/qtestsign [cryptography]: https://cryptography.io diff --git a/fw/hashseg.py b/fw/hashseg.py index e73f6e9..439498f 100644 --- a/fw/hashseg.py +++ b/fw/hashseg.py @@ -2,7 +2,7 @@ # Copyright (C) 2021-2023 Stephan Gerhold (GPL-2.0-only) # MBN header format adapted from: # - signlk: https://git.linaro.org/landing-teams/working/qualcomm/signlk.git -# - coreboot (util/qualcomm/mbn_tools.py) +# - coreboot (util/qualcomm/mbn_tools.py, util/cbfstool/platform_fixups.c) # Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved. (BSD-3-Clause) # See also: # - https://www.qualcomm.com/media/documents/files/secure-boot-and-image-authentication-technical-overview-v1-0.pdf @@ -43,8 +43,20 @@ # the ELF header (including all program headers). It is a placeholder so that # each hash covers the data of exactly one program header. -PHDR_FLAGS_HDR_PLACEHOLDER = 0x7000000 # placeholder for hash over ELF header -PHDR_FLAGS_HASH_SEGMENT = 0x2200000 # hash table segment +# For definitions of the ELF PHDR flags used by Qualcomm, see: +# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L108-L189 +PHDR_FLAGS_SEGMENT_TYPE_MASK = 0x07000000 +PHDR_FLAGS_SEGMENT_TYPE_SHIFT = 0x18 +PHDR_FLAGS_SEGMENT_TYPE_HASH = (0x2 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT) +PHDR_FLAGS_SEGMENT_TYPE_HDR = (0x7 << PHDR_FLAGS_SEGMENT_TYPE_SHIFT) + +PDHR_FLAGS_ACCESS_TYPE_MASK = 0x00E00000 +PHDR_FLAGS_ACCESS_TYPE_SHIFT = 0x15 +PHDR_FLAGS_ACCESS_TYPE_RO = (0x1 << PHDR_FLAGS_ACCESS_TYPE_SHIFT) + +# Flags we use for placeholder for hash over ELF header and hash segment +PHDR_FLAGS_HDR_PLACEHOLDER = PHDR_FLAGS_SEGMENT_TYPE_HDR +PHDR_FLAGS_HASH_SEGMENT = (PHDR_FLAGS_SEGMENT_TYPE_HASH | PHDR_FLAGS_ACCESS_TYPE_RO) EXTRA_PHDRS = 2 # header placeholder + hash segment @@ -55,7 +67,11 @@ # According to the v2.0 PDF the metadata is 128 bytes long, but this does not # seem to work. All official firmware seems to use 120 bytes instead. -METADATA_SIZE = 120 +MBN_V6_METADATA_SIZE = 120 + +# See OEM Metadata 2.0 definition in coreboot source code: +# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691 +MBN_V7_OEM_2_0_METADATA_SIZE = 224 def _align(i: int, alignment: int) -> int: @@ -191,17 +207,74 @@ def pack(self): + self.signature + self.cert_chain +@dataclass +# Information from MBNv7 definition in Coreboot source code: +# https://github.com/coreboot/coreboot/blob/812d0e2f626dfea7e7deb960a8dc08ff0e026bc1/util/qualcomm/mbn_tools.py#L506-L691 +class HashSegmentV7(_HashSegment): + version: int = 7 # Header version number + + common_metadata_size: int = 24 # Size of "common metadata" below + metadata_size_qcom: int = 0 # Size of metadata from Qualcomm + metadata_size: int = 0 # Size of metadata from OEM + hash_size: int = 0 # Size of hashes for all program segments + signature_size_qcom: int = 0 # Size of signature from Qualcomm + cert_chain_size_qcom: int = 0 # Size of certificate chain from Qualcomm + signature_size: int = 0 # Size of attestation signature + cert_chain_size: int = 0 # Size of certificate chain + + # Common metadata, placed directly after MBNv7 header + common_metadata_major_version: int = 0 + common_metadata_minor_version: int = 0 + software_id: int = 0 # Type of software image, mandatory + secondary_software_id: int = 0 + hash_table_algorithm: int = 3 # SHA384 + measurement_register_target: int = 0 + + metadata_qcom = b'' + metadata = b'' + signature_qcom = b'' + cert_chain_qcom = b'' + + FORMAT = Struct('<16L') + Hash = hashlib.sha384 + + def update(self, dest_addr: int): + super().update(dest_addr) + self.metadata_size_qcom = len(self.metadata_qcom) + self.metadata_size = len(self.metadata) + self.signature_size_qcom = len(self.signature_qcom) + self.cert_chain_size_qcom = len(self.cert_chain_qcom) + # self.common_metadata_size is already included as part of the header + self.total_size += self.metadata_size_qcom + self.metadata_size + self.total_size += self.signature_size_qcom + self.cert_chain_size_qcom + + def check(self): + super().check() + assert len(self.metadata_qcom) == self.metadata_size_qcom + assert len(self.metadata) == self.metadata_size + assert len(self.signature_qcom) == self.signature_size_qcom + assert len(self.cert_chain_qcom) == self.cert_chain_size_qcom + + def pack(self): + return self.pack_header() \ + + self.metadata_qcom + self.metadata \ + + b''.join(self.hashes) \ + + self.signature_qcom + self.cert_chain_qcom \ + + self.signature + self.cert_chain + HashSegment = { 3: HashSegmentV3, 5: HashSegmentV5, 6: HashSegmentV6, + 7: HashSegmentV7, } def drop(elff: elf.Elf): # Drop existing hash segments - elff.phdrs = [phdr for phdr in elff.phdrs if phdr.p_type != 0 or phdr.p_flags not in - [PHDR_FLAGS_HASH_SEGMENT, PHDR_FLAGS_HDR_PLACEHOLDER]] + elff.phdrs = [phdr for phdr in elff.phdrs if phdr.p_type != 0 + or (phdr.p_flags & PHDR_FLAGS_SEGMENT_TYPE_MASK) not in + [PHDR_FLAGS_SEGMENT_TYPE_HASH, PHDR_FLAGS_SEGMENT_TYPE_HDR]] def generate(elff: elf.Elf, version: int, sw_id: int): @@ -210,9 +283,16 @@ def generate(elff: elf.Elf, version: int, sw_id: int): hash_seg = HashSegment[version]() - if version >= 6: + if version == 6: # TODO: Figure out metadata format and fill this with useful data - hash_seg.metadata = b'\0' * METADATA_SIZE + hash_seg.metadata = b'\0' * MBN_V6_METADATA_SIZE + + # Software ID is mandatory for MBN v7 + if version == 7: + hash_seg.software_id = sw_id + # The format is documented in Coreboot util/qualcomm/mbn_tools.py + # (see class Boot_Hdr), but for simplicity we just keep this empty. + hash_seg.metadata = b'\0' * MBN_V7_OEM_2_0_METADATA_SIZE # Generate hash for all existing segments with data digest_size = hash_seg.Hash().digest_size @@ -279,3 +359,4 @@ def generate(elff: elf.Elf, version: int, sw_id: int): # And finally, assemble the hash segment hash_phdr.data = hash_seg.pack() + assert len(hash_phdr.data) == hash_phdr.p_filesz diff --git a/qtestsign.py b/qtestsign.py index 5221be4..ad81856 100755 --- a/qtestsign.py +++ b/qtestsign.py @@ -33,6 +33,9 @@ "aop": 0x21, "qup": 0x24, "xbl-config": 0x25, + "cdsp-dtb": 0x52, + "adsp-dtb": 0x53, + "av1": 0x69, } @@ -58,7 +61,7 @@ def _sign_elf(b: bytes, out: Path, version: int, sw_id: int): """) parser.add_argument('type', choices=FW_SW_ID.keys(), help="Firmware type (for SW_ID)") parser.add_argument('elf', type=argparse.FileType('rb'), help="ELF image to sign") -parser.add_argument('-v', '--version', type=int, choices=[3, 5, 6], default=3, +parser.add_argument('-v', '--version', type=int, choices=[3, 5, 6, 7], default=3, help="MBN header version. Must be set correctly depending on the target chipset. " "See README for details.") parser.add_argument('-o', '--output', type=Path, help="Output file")