Skip to content

feat(ukisign): sign an unsigned ISO with cryptosctl image sign - #384

Merged
Bugs5382 merged 1 commit into
mainfrom
feat/83-sign-iso
Oct 16, 2026
Merged

Bugs5382 merged 1 commit into
mainfrom
feat/83-sign-iso

Conversation

@Bugs5382

Copy link
Copy Markdown
Contributor

What and why

The release attaches unsigned ISOs next to the unsigned UKIs, and cryptosctl image sign only took a UKI. To install from my own signed, anchored ISO I had to sign the UKI and rebuild the ISO around it with xorriso. Now image sign does the ISO too, with no root, no xorriso and no rebuild.

  • cryptosctl image sign --image <unsigned.iso>: an ISO is detected by content (CD001 at sector 16), not by name. It signs the UKI inside exactly as the UKI path does, writes it back over every copy, and writes three files: the signed ISO (default: the name without -unsigned, else .signed.iso), the signed UKI beside it (<out without .iso>.uki) and its .sig, so the same key stages upgrades. Nothing is written unless the self-check passes.
  • internal/isoimage (new), a contained reader and writer for the layout build/iso/build.sh produces, not a general library:
    • ISO 9660: every volume descriptor's directory tree (Rock Ridge NM and Joliet names), and the El Torito catalog (default entry and sections);
    • finds the UKI as EFI/BOOT/BOOTX64.EFI (or BOOTAA64.EFI) in each FAT boot image, and every tree file with the same content (/cryptos.uki);
    • FAT12/16/32: frees the old cluster chain, allocates a new one (contiguous when it can), updates every FAT copy, the entry and the FAT32 FSInfo hint, so the FAT image keeps its size and place. A full FAT is refused (ErrFATFull);
    • a tree copy that still fits its extent is rewritten in place; one that grows moves to new sectors at the end of the volume (or the start of a GPT backup inside it). The old sectors are cleared, every record of that file in every tree is repointed, and the volume space size, MBR partitions and the GPT (partitions, last usable LBA, backup moved to the new end, both CRCs) follow.
  • internal/ukisign: SignISO and CheckISO (re-open the output, the boot image holds the signed UKI, the copy count matches the input, then the existing Check), DefaultISOOut, ISOUKIPath.
  • internal/isoimage/isotest: builds synthetic ISOs for tests, written byte by byte (FAT12/16/32 boot images, Rock Ridge names, a subdirectory, El Torito default or section entry, optional MBR or GPT with the backup outside or inside the volume).
  • No new dependency. None of my packages covers ISO 9660 or FAT, and a general library would bring far more than this narrow layout needs.
  • Docs: docs/image-sign.md gets a "Sign an ISO" section; the README line mentions ISOs.

Refs CryptOS-PKI/cryptos-appliance#83

Verification

  • internal/isoimage, over eight layouts (FAT12, FAT16, FAT32, with GPT, GPT inside the volume, MBR, EFI image in a catalog section):
    • Open finds the boot image copy and /cryptos.uki;
    • a UKI that grows past its extent and its last cluster is written to both. The input is untouched. The image grows by exactly the new extent and the volume size follows in both byte orders. /cryptos.uki sits at the old insertion point with its old sectors zero. The other FAT file and a file in a subdirectory are intact, and the boot image keeps its place and size. FAT32 FSInfo matches the free count. The GPT is read back on its own terms (header and entry CRCs, backup in the last block and identical to the primary, last usable LBA, ISO partition grown, EFI partition unchanged, protective MBR size). The MBR partitions follow the same rules;
    • a same-size UKI stays in place and leaves the system area untouched; an ISO with only the boot copy grows nothing; a full FAT and an empty UKI are refused; a non-ISO, a missing El Torito catalog and a missing BOOTX64.EFI are refused.
  • internal/ukisign: SignISO on synthetic ISOs carrying the test UKI signs both copies, Check and imageupgrade.Verify pass, a non-ISO and a non-UKI boot image are refused, and CheckISO catches a missing copy, a different UKI and another certificate. Default output names are table-tested.
  • cmd/cryptosctl: an ISO whose boot image isn't a UKI is refused and none of the three outputs is written.

How this was verified

CI is the proof. I built and tested nothing locally, and I did not run the doc's command blocks locally. The appliance's QEMU suite will sign an ISO from the real release build with this command, check both copies with sbverify --cert, install a node from it and upgrade it (cryptos-appliance#83).

cryptosctl image sign now takes an unsigned ISO as well as a UKI. It signs the UKI inside it the same way and writes it back over every copy, the El Torito EFI boot image's BOOTX64.EFI and the ISO 9660 file the installer copies, in pure Go with no root and no xorriso. The signed UKI and its .sig are written beside the ISO for staging upgrades.

Refs CryptOS-PKI/cryptos-appliance#83

Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
@Bugs5382 Bugs5382 added this to the v0.1.0 milestone Oct 16, 2026
@Bugs5382 Bugs5382 added the enhancement New feature (feat). Minor version bump. label Oct 16, 2026
@Bugs5382 Bugs5382 self-assigned this Oct 16, 2026
@Bugs5382
Bugs5382 merged commit a4b9bae into main Oct 16, 2026
15 checks passed
@Bugs5382
Bugs5382 deleted the feat/83-sign-iso branch October 16, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature (feat). Minor version bump.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant