Skip to main content

SoM Releases — Verification & Provisioning

Every SoM ships from production test with a flashed bootloader stack, a system image, and a programmed EEPROM identity — all drawn from a versioned, signed SoM-release bundle. This page covers what a bundle is, how to verify its provenance, and how a module is provisioned from one.

This is supply-chain provenance — it is not the device secure-boot chain, which signs bootable firmware images with a different, device-scoped key (MCUboot + OPTIGA Trust M).

The release bundle

A bundle is a directory with a manifest (bundle.json, schema som-release-bundle-v1) plus the component binaries. The manifest records:

FieldWhat it carries
sku / family / hw_revWhich module this release targets (e.g. E1M-V2N101, v2n, r1).
release_versionSemantic release id, som-X.Y.Z.
statuscomplete, or bootloader-only:image-pending-hw for early bundles.
components[]One row per artefact — role (bl2 / fip / system_image), file, sha256, size_bytes, and flash_target (xspi:mtd0 / xspi:mtd1 / emmc).
provenanceBuild reproducibility pins: toolchain, U-Boot / TF-A source revisions, defconfig, applied patches.
signatureECDSA-P256 provenance signature (or null on early, pre-signing bundles).

Verifying provenance

Bundles are signed with Alp Lab's ECDSA P-256 release-signing key. The public half is published in the SDK repo at keys/alp_release_signing_ecdsa_p256.pub.pem; the key_id in the signature block (first 16 hex chars of SHA-256(DER(public key)), currently 8be383f850354f40) lets verification match the right key across rotations.

The signature is computed over the manifest in canonical JSON form (with signature: null, sorted keys, compact separators), digested with SHA-256. Since the manifest already records the SHA-256 of every component, one signature transitively attests the whole release.

python scripts/check_som_bundle.py \
--bundle releases/E1M-V2N101/som-0.2.0/bundle.json \
--require-signature
  • Without --require-signature, an unsigned bundle still passes — early bundles such as som-0.1.0 predate signing. With it, unsigned is a failure.
  • --pubkey <PEM> verifies against a specific key instead of the published default (useful during key rotation).
  • A tampered manifest, an invalid signature, or an unloadable key fails with a clear FAIL … line and a non-zero exit code.

Programmatic verification (verify-only module, no private key involved):

import som_signing # scripts/som_signing.py
pub = som_signing.load_public_key_file("keys/alp_release_signing_ecdsa_p256.pub.pem")
assert som_signing.verify_signature(bundle_dict, pub)

Key rotation is non-disruptive: verify_with_keys(doc, [pub1, pub2, …]) accepts any trusted key, so already-shipped bundles stay verifiable against the retained old key during the overlap window.

Provisioning a module

scripts/provision_som.py provisions one module from a bundle in a linear, stop-on-first-failure sequence:

  1. Validate the bundle (check_som_bundle.py).
  2. Flashbl2/fip to xSPI via the xspi_flashwriter backend (Flash Writer SCIF over serial), system image to eMMC via yocto_wic. Skipped for bootloader-only bundles.
  3. EEPROM — allocate a serial number, build the 128-byte hardware-info manifest, write it to the on-module EEPROM, and read it back to verify.
  4. Power-on test — run the board's HIL smoke suite, gated on alp_hw_info_read() succeeding first.
  5. Record the unit to the production ledger.

Provisioning rewrites the bootloader, wipes eMMC, and programs the EEPROM — so the tool dry-runs by default, printing exactly what each step would do. Pass --execute to perform the real operations:

python scripts/provision_som.py --execute \
--bundle <bundle> --port COM24 \
--flash-writer <Flash_Writer_SCIF_RZV2N_DEV_LPDDR4X.mot> \
--emmc-device /dev/sdX --hil-spec tests/hil/e1m-v2n101-x-evk \
--station bench1 --by <technician>

Build provenance — a separate layer

Don't confuse the SoM-release bundle above with build provenance. The bundle attests what Alp Lab flashed onto a module in production. These attest what a build was made from — including your own builds:

ArtefactAttestsShape
build-receipt-v1One release build: SDK source revision, board.yaml / alp.lock / build-plan digests, toolchain identity, per-image SHA-256sJSON, metadata/schemas/build-receipt-v1.schema.json
SBOMThe dependency tree the build consumed — the SDK plus its west projects and curated librariesCycloneDX 1.5, from scripts/gen_sbom.py
alp.lockThe pinned resolution the other two are derived fromJSON, via west alp-lock

The design rule these share: no wall-clock. The receipt carries no timestamp field, and the SBOM's serial number is derived from the lock's own digest rather than the current time. So identical inputs yield a byte-identical receipt and SBOM — which is what makes them diffable and worth comparing across two builds that should have matched.

Releases build a byte-reproducible source tarball (git archive … | gzip -n, which suppresses the timestamp gzip would otherwise embed) and attach the SBOM plus SLSA provenance alongside it.

See also

Questions about this page? Discuss in Community Forum