Generating HAB keys with NXP's CST
Last time I wrote about the theory — the CA, the four SRK keys, the CSF/IMG pairs underneath each one.
Last time I wrote about the theory — the CA, the four SRK keys, the CSF/IMG pairs underneath each one. This post is about actually generating them, using NXP's own tool for this, called CST (Code Signing Tool).
Getting the tool
CST isn't something you can just apt install. It's a download from NXP's own
site, and you need a free account with them to get it. A bit of friction, but
that's normal for this kind of vendor-specific silicon tooling. Once downloaded,
it's just a tarball you extract somewhere on your build machine — I keep mine in a
folder that lives completely outside any of my git repositories, for the same
reason I'll explain below about the keys themselves: nothing that touches this
process should ever end up in a repository's history, even by accident.
Inside the extracted folder, there's a script called hab4_pki_tree.sh. That's the
one that actually builds the whole key family I described in the last post.
The one manual step I insisted on doing myself
Before running that script, it wants a small text file with a passphrase in it, used to password-protect every private key it generates. I want to be very clear about this part: I typed that passphrase myself, directly into a file, in my own terminal. Not somewhere it could be seen or logged, not something I'd ever hand off to any tool or automation to choose for me. It's exactly the kind of thing where you want a real, memorable-to-you-only decision made by a human, once, and then protected properly afterward.
(Small tip if you ever do this yourself: read the script's own source before running it. I found out that if that passphrase file doesn't exist, the script just quietly creates one itself with the password literally set to the word "test." Harmless if you catch it, mildly terrifying if you don't.)
Actually running it
Once the passphrase file existed, generating the whole tree was one command, with every choice spelled out as a flag rather than answered interactively:
./hab4_pki_tree.sh -existing-ca n -kt rsa -kl 4096 -duration 20 -num-srk 4 -srk-ca y
Reading that left to right: no existing CA to reuse (this is a fresh tree), RSA keys (not the elliptic-curve option — RSA is the far more common, better documented choice for this, and I didn't want to be the first person debugging an edge case), 4096-bit keys, a 20-year certificate validity (which, funnily enough, doesn't actually matter for security here — more on that in a moment), four SRK keys, and yes, give the SRK keys the ability to certify keys underneath them, since that's exactly what the CSF/IMG pairs need.
It ran for a little while — generating that many 4096-bit RSA keys isn't instant — and finished cleanly. What came out the other end was exactly the family tree from the last post: one CA, four SRK keys, and under each SRK, one CSF and one IMG key. Thirteen key pairs in total, all with their private half locked down at the filesystem level so only my own account can even read them.
About that 20-year certificate validity
I mentioned it doesn't matter for security, and that surprised me a little at first. Here's why: the chip's boot ROM, at the moment it checks a signature, has no concept of "today's date" — there's no real-time clock involved in that check at all. Certificate expiry is a completely normal-world PKI concept (like your browser checking a website's certificate hasn't expired), and it simply doesn't apply to a chip checking a signature at power-on. So the "20 years" is really just there to keep the certificate-generation tooling itself happy; it has zero effect on how the actual boot check behaves.
The one thing every device eventually needs: the fuse hash
Once the certificates existed, a second small tool from the same package, called
srktool, builds two files out of the four SRK public certificates:
- A table containing all four public keys, in the exact binary shape the chip's boot ROM expects. This one travels along inside every signed image later.
- A 256-bit fingerprint of that table — eight 32-bit numbers. This is the one thing that eventually gets permanently written into the chip itself, much later in this project, and only once I'm confident everything else works.
I double-checked those eight numbers with a second, completely unrelated tool (just a generic hex-dump utility, nothing CST-specific) reading the same raw file, and got the identical result both times. It's a small thing, but two independent tools agreeing on the same value is a much better feeling than trusting one tool's output blindly, especially right before those numbers become permanent.
Nothing got burned into any chip in this post — these are still just files sitting on a disk, and numbers printed to a terminal. That's an important distinction I kept reminding myself of throughout this whole phase: generating keys and printing values is completely harmless and fully reversible. The next post is actually about backups, because before I let myself get anywhere near an irreversible step, I wanted several independent, verified copies of absolutely everything.