[RFC] qcom-firmware-sign: build-time secure-boot signing of Qualcomm firmware#2598
[RFC] qcom-firmware-sign: build-time secure-boot signing of Qualcomm firmware#2598Igor Opaniuk (igoropaniuk) wants to merge 10 commits into
Conversation
Add a -native recipe that fetches the per-chipset Security Profile XML files from https://github.com/qualcomm/security-profiles and stages them under ${datadir}/qcom-security-profiles/ so they can be resolved by classes-recipe/qcom-firmware-sign.bbclass via the bare filename in QCOM_FIRMWARE_SIGN_SECPROFILE. The upstream repository is currently private; fetching it requires a GitHub credential that has SAML-SSO authorised the qualcomm organisation (gh auth login + browser flow). This restriction is expected to lift soon, at which point this note becomes obsolete. License is BSD-3-Clause-Clear, anchored to the upstream LICENSE.txt. Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add a -native recipe that fetches Qualcomm Sectools v2 from the
Software Center (1.48.0) and stages the per-platform `sectools`
binary under ${datadir}/sectools/, with a symlink in ${bindir} so
consumers can invoke `sectools` from PATH irrespective of build-host
architecture.
The build-host architecture override selects the matching tree from
the upstream zip (Linux x86_64 by default; Linux_aarch64 on aarch64
hosts). Other platforms shipped in the zip (macOS, Windows) are
intentionally not installed.
The Software Center download is anonymously accessible (verified by
plain wget GET against the published 1.48.0 URL); BitBake's HTTP
fetcher works without any pre-staging step. The SHA256 is pinned to
the released 1.48.0 archive.
LIC_FILES_CHKSUM is anchored to the in-archive CHANGES.txt because
the zip ships no LICENSE file; LICENSE.qcom-2 is provided via the
layer-local licenses directory through LICENSE_PATH (see
conf/layer.conf).
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add a build-time signing class that wraps sectools-native and security-profiles-native, modelled on the meta-partner approach in foundriesio/meta-partner#12. The class adds a do_qcom_firmware_sign task between do_install and do_deploy. When QCOM_FIRMWARE_SIGN_ENABLE is "0" (the default) the task is a no-op and no extra DEPENDS are pulled in -- consumers can inherit unconditionally without affecting non-signing builds. When enabled, do_qcom_firmware_sign: * walks ${B}/firmware-to-sign for *.mbn / *.elf files (or whatever the recipe overrides the default with); * looks each filename up in QCOM_FIRMWARE_SIGN_IMAGE_ID_MAP to obtain the sectools image-id label; * invokes `sectools secure-image --sign ...` with the OEM root / attestation CA cert + key from QCOM_FIRMWARE_SIGN_KEY_DIR and the per-SoC profile resolved from QCOM_FIRMWARE_SIGN_SECPROFILE; * verifies the resulting blob's root hash against the OEM hash file. Bare filenames in QCOM_FIRMWARE_SIGN_SECPROFILE resolve against the native datadir staged by security-profiles-native; absolute paths are passed through unchanged so OEM-specific profiles can override the in-tree set. Fuse-binding identifiers (QCOM_FUSE_OEM_HW_ID, QCOM_FUSE_OEM_PRODUCT_ID, QCOM_FUSE_SEC_KEY_DERIVATION_KEY) are declared here so they participate in task signature hashing; the actual fuse-blower invocation lives in downstream consumers (and the qcom-sec-tools shell pipeline). Image-id lookup uses a space-separated `filename:imageid` map (also overridable from a recipe) to keep the SoC-specific knowledge in data rather than code. Unknown filenames trigger a bbfatal so new firmware blobs are noticed at build time rather than silently shipped unsigned. Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add the development ECDSA PKI material and a kas overlay that wires
qcom-firmware-sign.bbclass at it, so a fresh checkout can exercise
the firmware-sign path end-to-end without sourcing OEM signing
material.
* ci/test-keys/ecdsa/ -- the four fixtures consumed by
qcom-firmware-sign.bbclass:
qpsa_rootca0.cer (root certificate)
qpsa_attestca0.cer (attestation CA certificate)
qpsa_attestca0.key (attestation CA key)
sha384_roots_hash.txt (root hash for sectools --verify-root)
These are DEVELOPMENT KEYS ONLY and must never appear in
production builds.
* ci/ecdsa-secure-boot-test-keys.yml -- mirrors the existing
ci/capsule-test-keys.yml pattern; points
QCOM_FIRMWARE_SIGN_KEY_DIR at the in-tree test fixtures so the
next commit's secure-boot.yml can be combined with it:
kas build ci/base.yml:ci/<machine>.yml:\
ci/secure-boot.yml:\
ci/ecdsa-secure-boot-test-keys.yml
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add ci/secure-boot.yml, the main kas overlay that turns the
firmware-sign pipeline on:
* Flips QCOM_FIRMWARE_SIGN_ENABLE to "1", so the no-op default in
qcom-firmware-sign.bbclass is replaced with a real sectools
invocation.
* Defaults QCOM_FIRMWARE_SIGN_SECPROFILE to the Kodiak Security
Profile, resolved against the directory staged by
security-profiles-native.
* Provides placeholder values for the fuse-binding identifiers
(QCOM_FUSE_OEM_HW_ID, QCOM_FUSE_OEM_PRODUCT_ID,
QCOM_FUSE_SEC_KEY_DERIVATION_KEY) and the anti-rollback floor
(QCOM_FIRMWARE_SIGN_ANTI_ROLLBACK). Production builds override
these per OEM.
* Pulls meta-oe from meta-openembedded for libzip-native, the qdl
build-host dep used by the qcomflash-vip image type.
To bind the build to actual key material the user combines this
overlay with one of:
* ci/ecdsa-secure-boot-test-keys.yml -- in-tree development fixtures
(added in the previous commit).
* An OEM-specific overlay pointing QCOM_FIRMWARE_SIGN_KEY_DIR at
production keys sourced from a secrets manager.
Typical CI / dev invocation:
kas build ci/base.yml:ci/<machine>.yml:\
ci/secure-boot.yml:\
ci/ecdsa-secure-boot-test-keys.yml
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add `inherit qcom-firmware-sign` plus a recipe-appropriate do_qcom_firmware_sign override so the build-time signing class actually runs against the SoC boot binaries every firmware-qcom-boot-*.bb recipe ships. Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add `inherit qcom-firmware-sign` plus a recipe-appropriate do_qcom_firmware_sign override so HLOS firmware shipped through the linux-firmware-qcom-* split packages gets signed alongside the boot binaries. Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Remove recipes-devtools/qdl/qdl_git.bb. The recipe has not been functionally maintained, is pinned to a SRCREV from before qdl switched its build system from Makefile to meson, and still uses `oe_runmake install`. Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add a -native recipe that builds Qualcomm's Download Tool (qdl) from the linux-msm/qdl upstream at v2.7. The recipe is mainly used for VIP (Validated Image Programming) digest-table generation. In --dry-run + --create-digests mode, qdl walks the rawprogram / patch XMLs the same way a real flash would but, instead of transferring data, computes a digest over each programmed segment and writes a DigestsToSign.bin file. classes-recipe/ image_types_qcom_vip.bbclass consumes this to build the signed qcomflash-vip image type. Build system is meson + ninja with libusb-1.0, libxml-2.0 and libzip runtime deps, all of which are packaged as -native by oe-core, so the recipe collapses to `inherit native meson pkgconfig` plus the three DEPENDS. Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
Add a new image type that bolts onto the existing qcomflash image
type to produce a signed VIP (Validated Image Programming) digest
table. Modelled on the "make sign-build" pipeline in
qcom-sec-tools/secure.sh; the three on-target steps boil down to:
1. qdl --dry-run --create-digests -- walks the rawprogram/patch
XMLs the same way a real flash would, but computes per-segment
digests instead of transferring data. Produces
DigestsToSign.bin under ${IMGDEPLOYDIR}/${IMAGE_NAME}.qcomflash-vip/.
2. sectools mbn-tool generate --mbn-version 6 -- wraps the digest
binary in a Qualcomm firmware MBN container so the on-target
verifier recognises it.
3. qcom_sign_verify_file -- signs the MBN as image-id VIP using
the same keying material as the rest of the firmware blobs.
Output: ${IMAGE_NAME}.qcomflash-vip.tar.gz, intended to be flashed
alongside the qcomflash tarball via qdl --vip-table-path.
The class inherits image_types_qcom (so qcomflash is defined) and
qcom-firmware-sign (so signing helpers / key material are wired in)
and adds itself to IMAGE_TYPES with an IMAGE_TYPEDEP on qcomflash so
the dependency graph keeps the qcomflash build as a prerequisite of
the VIP step. Activate via:
IMAGE_FSTYPES += "qcomflash-vip"
When QCOM_FIRMWARE_SIGN_ENABLE is not "1" the VIP step is a no-op
because there is nothing OEM-specific to bind the digests to.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
94cd6b7 to
a1ee798
Compare
Koen Kooi (koenkooi)
left a comment
There was a problem hiding this comment.
This completely removes qdl (non-native), please change that to one commit updating qdl_git to 2.7+ and adding inherit native. I use qdl on-device to flash other devices, it's more convenient than buying even more USB hubs 😎
Sure, I thought it's no used anymore (as still points to 2.1, which is a bit obsolete). Also 2.7 release requires libzip, which is available in meta-oe, that's why I also moved the recipe to the dynamic layers |
|
FYI: the build fails because of security-profiles |
| install -d "${D}${datadir}/qcom-security-profiles" | ||
| install -m 0644 "${S}"/*_security_profile.xml \ | ||
| "${D}${datadir}/qcom-security-profiles/" | ||
| } No newline at end of file |
There was a problem hiding this comment.
Missing EOL.
| HOMEPAGE = "https://softwarecenter.qualcomm.com/catalog/item/Qualcomm_Security_Tools" | ||
| LICENSE = "LICENSE.qcom-2" | ||
|
|
||
| LIC_FILES_CHKSUM = "file://${UNPACKDIR}/${ZIP_TOPDIR}/CHANGES.txt;md5=d2a0bb01dcd8befe660b832fbbe05900" |
There was a problem hiding this comment.
This is not really correct, started an internal thread to see if we can have a license inside the zip file.
There was a problem hiding this comment.
Yeah, that was actually the issue (no LICENSE file inside the archive )
| LICENSE = "LICENSE.qcom-2" | ||
|
|
||
| LIC_FILES_CHKSUM = "file://${UNPACKDIR}/${ZIP_TOPDIR}/CHANGES.txt;md5=d2a0bb01dcd8befe660b832fbbe05900" | ||
| ZIP_TOPDIR = "1.48" |
There was a problem hiding this comment.
PV?
There was a problem hiding this comment.
Saw now that the directory is 1.48.0 but the zip is 1.48.zip, not great.
| } | ||
|
|
||
| FILES:${PN} += "${datadir}/sectools" | ||
| INSANE_SKIP:${PN} += "already-stripped" No newline at end of file |
There was a problem hiding this comment.
Missing EOL.
| do_compile[noexec] = "1" | ||
|
|
||
| # Pick the per-platform sectools binary that matches the build host. | ||
| SECTOOLS_PLATFORM_DIR = "${@'Linux_aarch64' if d.getVar('BUILD_ARCH') == 'aarch64' else 'Linux'}" |
There was a problem hiding this comment.
We should also set COMPATIBLE_HOST.
| # e.g. firmware-qcom-boot-common.inc) and before do_package (for recipes | ||
| # that ship via package_install into /lib/firmware, e.g. | ||
| # firmware-qcom.inc / firmware-qcom-hlosfw style consumers) | ||
| addtask qcom_firmware_sign before do_deploy before do_package after do_install No newline at end of file |
There was a problem hiding this comment.
Again :-)
| XblRamdump.elf:XBL-RAM-DUMP \ | ||
| DigestsToSign.bin.mbn:VIP \ | ||
| FD02C9DA-306C-48C7-A49C-BBD827AE86EE.mbn:TZ-APP-OEM \ | ||
| " |
There was a problem hiding this comment.
Can we move this to an inc file or similar? This is just metadata for the class.
|
|
||
| local_conf_header: | ||
| ecdsa-secure-boot-test-keys: | | ||
| QCOM_FIRMWARE_SIGN_KEY_DIR = "${LAYERDIR_qcom}/ci/test-keys/ecdsa" No newline at end of file |
There was a problem hiding this comment.
Same.
| @@ -0,0 +1,9 @@ | |||
| -----BEGIN EC PARAMETERS----- | |||
There was a problem hiding this comment.
Split this commit in 2, have one dedicated just for the keys.
| # Per-SoC Security Profile XML. Bare filenames are resolved | ||
| # against the directory staged by security-profiles-native; an | ||
| # absolute path can be used to point at a custom profile. | ||
| QCOM_SECURITY_PROFILE ?= "kodiak_security_profile.xml" |
There was a problem hiding this comment.
Profile should probably come from the board conf.
There was a problem hiding this comment.
make sense, will do
| # `qdl --vip-table-path`. See classes-recipe/image_types_qcom_vip.bbclass | ||
| # and recipes-devtools/qdl/qdl-native_2.7.bb. | ||
| IMAGE_CLASSES += "image_types_qcom_vip" | ||
| IMAGE_FSTYPES += "qcomflash-vip" No newline at end of file |
There was a problem hiding this comment.
Same.
| # by the meson + pkgconfig classes. | ||
| DEPENDS = "libusb1-native libxml2-native libzip-native" | ||
|
|
||
| inherit native meson pkgconfig No newline at end of file |
There was a problem hiding this comment.
Same.
| # qdl links against libusb-1.0, libxml-2.0 and libzip; all three are | ||
| # packaged as -native in OE-core. meson/ninja and pkgconfig provided | ||
| # by the meson + pkgconfig classes. | ||
| DEPENDS = "libusb1-native libxml2-native libzip-native" |
There was a problem hiding this comment.
Why is it depending on libzip?
There was a problem hiding this comment.
Because of this feature linux-msm/qdl@fb3b622
There was a problem hiding this comment.
Maybe we can decouple this functionality and have a compile flag in meson.option (it can be enabled by default, but in our case for VIP generation it's not needed)
There was a problem hiding this comment.
Yeah, would be nice to have this as an optional dependency upstream, that way we won't have to depend on meta-oe for such a core BSP functionality.
There was a problem hiding this comment.
If it needs to be a ZIP file, can we use a standard zlib for it?
|
|
||
| ln -sf "${IMAGE_NAME}.qcomflash-vip.tar.gz" \ | ||
| "${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.qcomflash-vip.tar.gz" | ||
| } |
There was a problem hiding this comment.
I would prefer this to be done as part of the main tarball, to avoid confusion.
There was a problem hiding this comment.
will do
| HOMEPAGE = "https://softwarecenter.qualcomm.com/catalog/item/Qualcomm_Security_Tools" | ||
| LICENSE = "LICENSE.qcom-2" | ||
|
|
||
| LIC_FILES_CHKSUM = "file://${UNPACKDIR}/${ZIP_TOPDIR}/CHANGES.txt;md5=d2a0bb01dcd8befe660b832fbbe05900" |
There was a problem hiding this comment.
Drop UNPACKDIR. Also, can we work with the sectools authors to include the licence into the archive?
|
|
||
| do_unpack[postfuncs] += "sectools_chmod_unpacked" | ||
| sectools_chmod_unpacked() { | ||
| chmod -R u+w "${UNPACKDIR}" |
| # e.g. firmware-qcom-boot-common.inc) and before do_package (for recipes | ||
| # that ship via package_install into /lib/firmware, e.g. | ||
| # firmware-qcom.inc / firmware-qcom-hlosfw style consumers) | ||
| addtask qcom_firmware_sign before do_deploy before do_package after do_install No newline at end of file |
There was a problem hiding this comment.
Missing EOL
| # `qdl --vip-table-path`. See classes-recipe/image_types_qcom_vip.bbclass | ||
| # and recipes-devtools/qdl/qdl-native_2.7.bb. | ||
| IMAGE_CLASSES += "image_types_qcom_vip" | ||
| IMAGE_FSTYPES += "qcomflash-vip" No newline at end of file |
There was a problem hiding this comment.
Do we need a separate class and separate image type?
Also the class hasn't been seen up to now, so this commit breaks the build. Please reoder them.
| @@ -1,23 +0,0 @@ | |||
| SUMMARY = "Qualcomm DownLoader flashing tool" | |||
There was a problem hiding this comment.
Would it be better to upgrade it instead of adding a native-only version?
| sectools-native:do_populate_sysroot \ | ||
| security-profiles-native:do_populate_sysroot" | ||
|
|
||
| create_qcomflash_vip_pkg() { |
There was a problem hiding this comment.
Okay, having a separate fstype kind of makes sense.
This RFC adds a build-time secure-boot signing pipeline for Qualcomm firmware. It introduces three -native recipes -
sectools-native,security-profiles-native, andqdl-native- together with aqcom-firmware-sign.bbclassthat any recipe can inherit to sign its deploy artefacts before they ship.A development ECDSA test-key set and a ci/secure-boot.yml kas overlay are included so a fresh checkout can exercise the full pipeline end-to-end without sourcing OEM signing material.
Taking into account that
qualcomm/security-profilesis still private, it can be tested with (so build likely to fail until the rep is opened):To test this build:
When this feature is enabled it generates:
deploy/.../<machine>/, for exampledeploy/images/rb3gen2-core-kit/qcm6490qcomflash-vip.tar.gz, which contains the full qcomflash artifact set under<image>-<machine>/ plus a vip-tables/DigestsToSign.bin.mbnOEM-signed via the same key chain. The package is ready to be flashed with QDL to the secured device with VIP engaged.