Secure Boot and Firmware Signing for Embedded Products: From Key Generation to Field Revocation

Four questions decide whether a product's secure boot is a defence or a decoration. Who may sign? What may run? What may never run again? What happens when a key leaks?
Teams usually answer the second question well, because it is the one the chip vendor's documentation describes: the device checks a signature before it runs the code. They answer the first poorly, because the signing key ends up on a build server. They forget the third until the day a signed, vulnerable version is reinstalled by an attacker. And they discover the fourth during an incident, when there is no way to revoke anything and the answer is a recall.
This post is a framework built on those four questions. It explains the chain of trust, shows the image format and the checks in code that runs, covers key custody, rollback protection, revocation, safe updates, factory provisioning and the details of verifying in place. It ends with a section on the algorithms to use in 2026, because an embedded product signed today may still be verifying firmware in 2040, and the guidance for firmware signing has a date in it. Where I rely on published guidance or public sources, I say so. The code is a software simulation of a bootloader's decision logic, intended to teach the structure, and not a substitute for the vendor's boot ROM or a certified implementation.
The Chain of Trust in Plain Terms
When a device powers on, the first code to run is a small program permanently stored in the processor, the boot ROM. It cannot be changed, so it is trusted by construction. Its job is to check the next piece of code, the first-stage bootloader, before running it. That bootloader checks the next stage, and so on, up to the operating system and the application. Each link verifies the one after it, which is why the arrangement is called a chain of trust.
The check works with digital signatures. The manufacturer holds a private key and signs each firmware image. The device holds the matching public key, or more commonly a hash of it, burned into one-time-programmable memory that cannot be rewritten. At boot, the device recomputes the image's hash, checks the signature against the public key and refuses to run the image if either fails.
Storing only a hash of the public key saves scarce fuses. The full key travels in the image header, and the ROM checks that its hash matches the fused value before using it. The result is a root of trust that nothing in writable storage can change.
Two terms are worth keeping apart. Verified boot, the subject of this post, stops unauthorised code from running. Measured boot records what did run, in a secure store, so that a remote party can check it later. They complement each other, and many products use both.
Secure boot does not stop an attacker from having a copy of your firmware. It stops the device from running anything you did not sign. Everything depends on who can sign.
What Secure Boot Does Not Protect
Secure boot has a precise job, and teams often credit it with more. It protects the integrity of what runs. It does not, on its own, protect confidentiality, and it does not protect a running system from attack.
Threat | Does secure boot stop it? | What else is needed |
Loading modified or unauthorised firmware | Yes | Nothing further, if keys and counters are sound |
Reinstalling an old vulnerable version | Only with anti-rollback | The security counter |
Copying the firmware to study or clone it | No | Encrypted images and read protection on the flash |
Exploiting a bug in the running application | No | Secure coding, memory protection, privilege separation, updates |
Stealing secrets stored in flash or memory | No | Hardware key storage, encrypted storage and debug lock |
Tampering with the device through its debug port | Only if the port is locked | Debug lock verified in production |
Counterfeit hardware running your firmware | No | Per-device identity and authentication |
Two consequences follow. First, a product with secure boot can still be taken over at run time through a flaw in its own code, which is why the update path and the vulnerability process matter as much as the boot chain. Second, if the firmware itself is a trade secret, signing does not hide it. Encrypting the image, with a per-device key, protects it from being read in transit and in the flash, at the cost of key management for the decryption key.
Secure boot decides what is allowed to start. Whatever starts must still be written safely, updated quickly and kept from leaking its secrets.
Question 1: Who May Sign? Keys, Custody and Roles
The private signing key is the most valuable secret in the product. Anyone who holds it can produce firmware that every device will accept, which is why it must never be an ordinary file.
The key belongs in a hardware security module, a tamper-resistant device that generates the key inside itself, signs on request and never exports the key. Public guidance on stateful hash-based signatures goes further and requires key and signature generation inside a module validated to a high assurance level, which in practice means a purpose-built device and not a software library. Whatever scheme you use, the principle is the same: the signing service can sign, and no human or build server can read the key.
Use more than one key, with different powers.
Key | Purpose | Who can use it | If it leaks |
Root key | Authorises the other keys, rarely used | Two or more named people, in a ceremony | The whole product line is at risk, so keep it offline |
Release signing key | Signs production firmware | The release pipeline, after approval | Revoke it and move to a backup key |
Development key | Signs test builds for lab devices | Engineers | Lab devices only, which must never trust production images |
Backup release key | Pre-provisioned replacement | Held offline | Used to recover |
Four practices make the custody real. Require two-person approval for any signing of production firmware. Log every signing request with the image hash, the requester and the approver. Separate the development key from the release key so that a leak in the lab cannot reach production. And rehearse recovery by practising the move to the backup key on test devices.
How it fails. The key sits on a build server so that the pipeline can sign automatically, and a compromise of the build server becomes a compromise of the product. The fix is a signing service that accepts a request, checks that it comes from an approved pipeline run on an approved commit, and signs inside the module.
Question 2: What May Run? The Image Format and the Check
An image has a header and a payload. The header carries a magic value, a version, a security counter used for rollback protection, and the hash of the payload. The signature covers the header, and because the header contains the payload hash, it covers the payload too.
The simulation below implements the logic of a minimal bootloader. It holds the hash of the trusted public key, as a fuse would, and a security counter. It accepts an image only if the key matches, the signature verifies, the payload matches its recorded hash and the counter is not older than the stored one.
import hashlib, json
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives import serialization
from cryptography.exceptions import InvalidSignature
def raw_pub(k):
return k.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
def make_image(payload, version, sec_counter, priv):
header = {"magic": "IMG1", "version": version, "sec_counter": sec_counter,
"payload_sha256": hashlib.sha256(payload).hexdigest()}
blob = json.dumps(header, sort_keys=True).encode()
return {"header": header, "pubkey": raw_pub(priv).hex(), "sig": priv.sign(blob).hex(), "payload": payload}
class Device:
def __init__(self, pubkey_hash_otp, counter_otp):
self.otp_hash, self.counter, self.running = pubkey_hash_otp, counter_otp, None
def verify(self, img):
pk = bytes.fromhex(img["pubkey"])
if hashlib.sha256(pk).hexdigest() != self.otp_hash:
return "REJECT: key not the one fused"
try:
Ed25519PublicKey.from_public_bytes(pk).verify(bytes.fromhex(img["sig"]),
json.dumps(img["header"], sort_keys=True).encode())
except InvalidSignature:
return "REJECT: bad signature"
if hashlib.sha256(img["payload"]).hexdigest() != img["header"]["payload_sha256"]:
return "REJECT: payload does not match header"
if img["header"]["sec_counter"] < self.counter:
return "REJECT: rollback"
return "OK"
def boot(self, img):
r = self.verify(img)
if r == "OK":
self.running = img
return r
def confirm(self): # called only after the new image proves healthy
if self.running:
self.counter = max(self.counter, self.running["header"]["sec_counter"])
good, evil = Ed25519PrivateKey.generate(), Ed25519PrivateKey.generate()
dev = Device(hashlib.sha256(raw_pub(good)).hexdigest(), 1)
v2 = make_image(b"firmware v2", "2.0.0", 2, good)
v1 = make_image(b"firmware v1 vulnerable", "1.0.0", 1, good)
print(dev.boot(v2)) # OK
tampered = dict(v2); tampered["payload"] = b"firmware v2 + implant"
print(dev.verify(tampered)) # REJECT: payload does not match header
print(dev.verify(make_image(b"evil", "9.9.9", 9, evil))) # REJECT: key not the one fused
Three attacks fail. The tampered payload is caught because its hash no longer matches the signed header. The attacker's own image, correctly signed with their own key, is caught because that key's hash is not the fused one. The legitimate image boots. The fourth question, rollback, is in the next section.
How it fails. Verifying the signature but not the payload hash, so a valid header can be paired with a different body. Or accepting the public key in the image without checking it against the fused hash, which lets anyone sign anything. Both are errors of omission, and the tests above exist to catch them.
Question 3: What May Never Run Again? Anti-Rollback
Signing proves that an image came from you. It does not prove that the image is the latest. An old firmware version, correctly signed and known to be vulnerable, will verify perfectly well. An attacker with physical or network access can reinstall it, and then exploit the flaw you fixed last year.
Rollback protection solves this with a monotonic counter in non-reversible storage, usually fuses. The image header carries a security counter. The bootloader refuses any image whose counter is lower than the stored value, and raises the stored value as the product moves forward. The rule that matters is when to raise it.
print(dev.verify(v1)) # OK, the counter has not advanced yet, so the old image is still acceptable
dev.confirm() # the new image has booted and passed its health checks
print(dev.counter) # 2
print(dev.verify(v1)) # REJECT: rollback
Before the new image is confirmed, the old one is still allowed. That is deliberate, because a failed update must be able to fall back. After confirmation, the counter moves to 2 and the vulnerable version is refused for good.
Raise the counter only after the new image has booted, run its health checks and, where possible, reported to a server. Raising it before confirmation turns a failed update into a permanently bricked device, with no way back. Raising it never means the product cannot go back to the good version that preceded the bad one, so keep a clean distinction between the version number, which can be anything, and the security counter, which moves only when a vulnerability has been fixed and the old image should be refused.
A signature says the code came from you. A counter says it is not a version you have since decided to distrust. A device needs both.
Question 4: What Happens When a Key Leaks?
Plan for the leak before it happens. The answer depends on what the device can do after a key is compromised.
The first design choice is to provision more than one key slot. Many boot ROMs allow several public-key hashes to be fused, and mark one as active and the others as spare. When the active key leaks, you sign a new image with a backup key, and the bootloader, which knows both hashes, accepts it. A device with only one fused key has no such option, and a leak means replacing hardware.
The second is revocation. A bootloader can hold a list of revoked keys, typically as fuses, and refuse images signed by any of them. The revocation is carried inside a signed update, so that an attacker cannot forge it.
Leak scenario | What the product needs | Without it |
Release key stolen, backup key intact | Backup slot and revocation of the release key | Recall or resign by hand |
Development key found in a repository | Production devices never trust it | Anyone can load firmware on lab units, and perhaps more |
Signing service breached for a day | Signing log to find what was signed | Every image since is suspect |
Root key compromised | Offline root, with a hardware root-of-trust redesign for next generation | The product line is finished |
Practise the recovery. On a set of test devices, rotate to the backup key, revoke the old one and confirm that the device accepts the new firmware and refuses the old. A plan that has never been run is a hope.
Updates That Do Not Brick the Device
Secure boot interacts with the update mechanism, and the two must be designed together.
The usual arrangement uses two firmware slots. The running image sits in one slot, and the update is written to the other. On the next boot, the bootloader verifies the new image and runs it in a trial state. If the image reports healthy within a set time, it is confirmed, the security counter advances and the old slot becomes the spare. If it fails to confirm, a watchdog resets the device and the bootloader falls back to the old slot.
A few details decide whether this works. The health check should test something meaningful, such as reaching the network, reading the sensors and completing a full cycle, and not just that the process started. The watchdog must be armed before the new image runs. Power loss during writing must leave the old slot intact. And a swap-based scheme, which exchanges the contents of two slots through a scratch area, needs special care so that an interruption mid-swap can be recovered.
How it fails. The update writes to the running slot, a power cut halfway leaves a half-written image, and the device cannot boot. Or the confirmation is too easy, so a firmware that boots but cannot talk to the network is confirmed and the unit goes silent in the field.
Debug Ports, Provisioning and the Factory
A chain of trust is only as strong as its weakest entry. Two entries are routinely left open.
The debug port is the first. A debug connection can read memory, halt the processor and write flash, which bypasses the chain entirely. Production devices must have debug access locked or permanently disabled, with the locking step part of the factory process and checked on a sample of every batch. Where a controlled debug route is needed for field returns, provide one that requires authentication.
Provisioning is the second. At manufacturing, each device receives its public key hash, its unique identity and any per-device secrets. The factory is a place where keys can leak and where devices can be built that are not what you think they are.
Factory step | The control | What goes wrong without it |
Programming the root-of-trust hash | Done by a controlled station, logged | Wrong key or no key fused |
Locking debug | Verified by a test | Unlocked devices shipped |
Generating device identities | Inside the device or a hardware module | Secrets pass through factory computers |
Recording which device got which keys | An auditable record per serial number | No way to scope a later problem |
Handling overproduction | Counted, scrapped or locked | Spare devices on the grey market |
If you use a contract manufacturer, write the controls into the agreement, audit them and limit what the factory can see. The factory should be able to program a device without being able to sign firmware.
Verify in Place, and Other Details
Four small details separate sound implementations from unsound ones.
Verify where the code will run, or verify the same bytes you will execute. A bootloader that copies an image into RAM, checks it and then runs a second copy invites an attack in the gap between the check and the use. Serious implementations verify in place, and re-check critical points after the jump.
Compare in constant time. Comparing a hash byte by byte and stopping at the first difference leaks, through timing, how many bytes matched. Use a constant-time comparison for secrets and hashes.
Treat faults as an attack. A glitch on the power supply or clock at the right moment can make a processor skip the instruction that rejects a bad image. Defences include redundant checks, randomised delays and double comparison of results, and the effort should match the value of what the device protects.
Keep the bootloader small and put it in your component list. The code that verifies everything else has the greatest need to be correct, and a flaw in it is invisible to monitoring if it is missing from the inventory. Our guide to firmware bills of materials explains why the bootloader belongs there.
Build the Bootloader or Adopt One
Writing a secure bootloader from scratch is a poor use of a small team's time and a risk to the product. The cryptography is subtle, the failure handling is intricate and the code that verifies everything else has the greatest need to be right.
Open-source bootloader frameworks for microcontrollers and embedded Linux implement image headers, signature checks, swap and overwrite update modes and rollback counters, and are reviewed by many eyes. Platform security architectures define how a root of trust, a verification chain and secure storage fit together. Adopting one gives you tested code, a community that finds and fixes flaws and a vocabulary your customers and assessors recognise.
Approach | Strength | Risk |
Adopt an established open-source bootloader | Reviewed, maintained, familiar to assessors | You must configure it correctly and track its advisories |
Use the silicon vendor's secure boot flow | Matches the hardware, with support | Tied to the vendor, closed in places |
Write your own | Exact fit | Unreviewed cryptography and recovery logic, a long road to trust |
Whatever you choose, list it in your bill of materials with its version, pin the version and subscribe to its security notices. A bootloader is a component like any other, and its flaws are the ones with the widest consequences.
Which Algorithms to Choose in 2026, and Why 2030 Matters
Firmware signing is unusual among uses of public-key cryptography, because the verification algorithm is often fixed when the device is made, in boot code or in the ROM, and cannot be changed in the field. A device signed with one algorithm in 2026 may still be verifying firmware in 2040.
That matters because a sufficiently large quantum computer would break the public-key algorithms widely used today. The United States National Security Agency's guidance, called CNSA 2.0, names software and firmware signing as the highest-priority use for the transition. Its timeline, as widely reported, is to prefer the new algorithms from 2025 and use them exclusively by 2030. The approved choices for firmware signing are two hash-based schemes standardised by the National Institute of Standards and Technology in Special Publication 800-208, called LMS and XMSS, and, in later guidance, a lattice-based scheme at its highest parameter set. A stateless hash-based scheme standardised separately is not included in the firmware-signing guidance.
Option | Strength | Cost |
Elliptic-curve signatures today | Small, fast, universally supported | Not quantum-safe |
LMS or XMSS | Quantum-safe, small public keys, fast verification | Stateful, so each one-time key may be used only once |
Lattice-based signature at the top parameter set | Quantum-safe, no state to manage | Larger keys and signatures, later in the guidance |
Hybrid of classical and quantum-safe | Safe against both kinds of failure | More code and larger images |
The stateful schemes deserve a warning. Each private key can sign only a fixed number of messages, and signing the same one-time key twice breaks the security. The signing system must track state reliably, with backups and audit, which is another reason the signing service must be a controlled hardware-backed system and not a script.
For a product with a long life and a government or critical-infrastructure customer, design for algorithm agility: a bootloader that can verify more than one scheme, a header that names the algorithm and a plan to move. For a short-lived consumer product, current elliptic-curve signatures remain reasonable today, and the decision should be written down with its rationale.
Link to the Cyber Resilience Act
The regulation's requirements for products with digital elements include secure update mechanisms, protection against unauthorised access and a record of what a product contains. Secure boot, signed updates and rollback protection address the first two directly, and the signing evidence, including the key custody records and the signing log, belongs in the technical file. The bootloader and its libraries belong in the bill of materials. Keep the three artefacts together: the SBOM says what is inside, the signing log says what was released and by whom, and the update records say what is running.
Ten Attacks to Run Before You Ship
Attack | Expected result |
Flip one byte in the payload | Rejected: hash mismatch |
Flip one byte in the header | Rejected: signature invalid |
Sign with a different key | Rejected: key not fused |
Replay an old, signed version after confirmation | Rejected: rollback |
Interrupt power during an update at ten different points | Device boots the old image or the new one, never neither |
Update with a corrupted but correctly signed image | Rejected by hash, or fails health check and rolls back |
Connect a debug probe to a production unit | No access |
Load the development-key image onto a production unit | Rejected |
Revoke the release key and load an image signed by it | Rejected |
Rotate to the backup key | Accepted after rotation |
Run the list on real hardware, record the results and keep the record with the release. Each line is a promise the product makes.
Testing the Chain on Real Hardware
The simulation shows the logic, and the product's behaviour is whatever the real bootloader on real silicon does. Testing on hardware is where most defects appear, and a routine makes it repeatable.
Build a fixture, not a procedure. The fixture is a small board or script that can program a device, cut its power at a chosen instant, hold it in reset, inject a corrupted image and read its serial output, all automatically. With it, the ten attacks listed above become an overnight run and not a week's manual effort.
The power-cut test deserves the most care. A device that loses power while writing its second slot, at any of hundreds of points, must still boot something valid afterwards. Step the cut time across the whole write in small increments, run the device after each cut, and require that it comes up on the old image or the new one every single time. A single failure in a thousand is a unit in the field that will not start.
Record every run with the image hash, the hardware revision and the result, and keep the record with the release, because the same test is run again after every bootloader change. Add fault-injection testing where the product's value justifies it: glitching the supply or the clock around the verification step on a sample device to see whether the check can be skipped.
A boot chain that passed on the bench has not passed until the power-cut test, the debug-lock test and the rollback test have run on production hardware.
A Composite: The Signing Key on a Build Server
This is a representative composite, genericised, from several product-security reviews. The numbers are illustrative.
A company shipped a connected controller with secure boot enabled. The boot ROM verified a signed bootloader, which verified a signed application. The team was proud of it, and the chip vendor's checklist was fully ticked.
A security review found three problems in a week. The release signing key was a file on the continuous integration server, readable by any job, because the pipeline signed automatically. The security counter was never advanced, because nobody had decided what should raise it, so every old version still verified. And the development key, used for lab units, was the same file as the release key, copied for convenience.
Any of the three alone would have defeated the system. Together they meant that anyone with access to the build server could sign firmware for every unit, and anyone who captured an old image could replay it.
The remediation took a quarter. The company moved signing into a hardware-backed service that signed only images from approved pipeline runs with two-person approval and logged every request. It defined the counter rule, raised on every release that fixed a security issue and only after confirmation. It generated a separate development key that production devices never trusted. And for units already in the field, which had one key slot and no revocation, it designed a signed update that installed a second key, since replacing the hardware was not an option. The costliest lesson was that the units lacking a backup slot could not be fully repaired.
Mistakes That Compromise a Whole Product Line
Mistake | Consequence | Fix |
Signing key on a build server | Build compromise becomes product compromise | Hardware-backed signing service with approval |
Same key for lab and production | Lab leak reaches the field | Separate keys, production never trusts development |
No rollback counter | Old vulnerable versions reinstalled | Monotonic counter, raised after confirmation |
Counter raised before confirmation | Failed updates brick devices | Raise only after health checks pass |
One fused key, no spare | A leak means a recall | Provision backup slots and revocation |
Debug port left open | The chain is bypassed | Lock and test every batch |
Verifying after copying to RAM | Check-then-use race | Verify in place |
Bootloader missing from the SBOM | Its flaws are invisible | List it with version and hash |
Fixed algorithm for a fifteen-year product | Stranded when the guidance changes | Algorithm agility and a documented plan |
A Ninety-Day Plan
Days one to fifteen: inventory every product and where its signing key lives today, and who can use it.
Days fifteen to thirty: move signing to a controlled service, with approval and logging, and separate the development key.
Days thirty to fifty: define the security counter rule and the confirmation step, and implement them in the bootloader.
Days fifty to seventy: add a backup key slot and a revocation path to the next hardware revision and, where possible, to units already in the field through a signed update.
Days seventy to ninety: run the ten attacks on real hardware, rehearse a key rotation, and write down the algorithm decision.
Four Questions Again
Who may sign: a small, audited, hardware-backed service. What may run: only images whose key is fused, whose signature and hash verify and whose counter is current. What may never run again: anything older than the counter. And what happens when a key leaks: a backup slot, a revocation path and a recovery you have already practised.
Answer all four on paper before the first unit ships, and test the answers on hardware. The chain of trust is only as strong as its least-answered question.
Related service: Quantum-safe Cryptography
Related reading: SBOM for Firmware: A Working Pipeline for the EU Cyber Resilience Act and Air-Gapped AI: How to Run a Private LLM With No Internet Connection.
EurthTech is an engineering-first product design and embedded systems company with a development center in Hyderabad. To discuss a project, write to connect@eurthtech.com or see https://www.eurthtech.com/case-studies.




Comments