Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • The NanoSoC Tour
  • Reading Path
  • SoC Glossary10
  • SoC Metrics Reference
  • SoC Cheatsheet
  • What SoC Integration Is12
  • Architecture and Budgets21
  • Memory Maps and Address Decode26
  • Interconnect and Bus Fabrics26
  • Clock and Reset Architecture24
  • Power Architecture21
  • Interrupts, DMA and Peripherals15
  • Memories, Boot and Security21
    • Memory Subsystem Integration
    • ECC and Error Handling
    • External Memory Controller Integration
    • Boot ROM and Boot Modes
    • eFuse and OTP
    • Secure Boot and Root of Trust
      • Chain of Trust and Immutable ROM
      • Image Authentication, Conceptually
      • Failure Handling and Recovery
    • Security Architecture
  • IP Integration and Handoff21
  • Top-Level Verification and Bring-up21
  • Package, Pads and I/O18
  • Chip Signoff and Tapeout18
  • SoC Interview Prep16
  • Capstone: Integrate NanoSoC14
Loading lesson

←Secure Boot and Root of Trust
  1. Topics
  2. SoC Design
  3. Memories, Boot and Security
  4. Secure Boot and Root of Trust
  5. Image Authentication, Conceptually
On this page

    Part 8 of 14, lesson 17 of 21Part 8 of 14/Memories, Boot and Security/Lesson 17 of 21All levels

    Image Authentication, Conceptually

    Manifests, versions and rollback: verifying an image with no cryptographic parameters.

    Why it matters

    This lesson has one job: say what image authentication guarantees without leaning on algorithm names. The ROM must confirm the image came from its owner, has not been altered, is allowed to run on this device, and is not an older version being replayed. Each guarantee maps to a check on the manifest plus the image bytes; the mathematics underneath is deliberately out of scope, and nothing here names parameters, key sizes or algorithms.

    The core idea

    Authentication checks four things in order. Origin asks whether the manifest was issued by the owner, answered by the fused key. Integrity asks whether the loaded bytes match the manifest, answered over the 8 KB image the SPI loader placed in SRAM0. Freshness asks whether the manifest version is at least the minimum held in the fuse version field of the previous topic (a thermometer-coded counter that can only rise), which defeats replay of an older signed image. Binding asks whether the manifest names this device class and these memory ranges, so an image for another product or another load address is refused. Only all four passing sets the pass flag; the checksum lesson's 192-microsecond read is the mechanical analogue, proving the bytes match, while these checks prove the bytes are authorised.

    The script models the policy logic, which is the integrator-owned part: version comparison against a fused minimum, containment of the manifest's declared load address and device class, and the all-four conjunction. The load window is the SRAM0 image area, 0x2000_0000 to 0x2000_FFFF; SRAM1 is secure-only and is not an image target. Each case carries the manifest's own load base and device class, so binding is computed from manifest fields and can refuse. The cryptography that produces the pass inputs is a black box here by design.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    fused_min_version = 7
    this_device_class = "NANO-A"
    window_lo, window_hi = 0x2000_0000, 0x2000_FFFF
    image_len = 8 * 1024
    # (name, origin ok, integrity ok, version, load_base, device_class, expected verdict)
    cases = [
        ("good", True, True, 7, 0x2000_0000, "NANO-A", True),
        ("bad origin", False, True, 9, 0x2000_0000, "NANO-A", False),
        ("bad integrity", True, False, 9, 0x2000_0000, "NANO-A", False),
        ("rollback", True, True, 6, 0x2000_0000, "NANO-A", False),
        ("wrong product", True, True, 9, 0x2000_0000, "NANO-B", False),
        ("wrong load address", True, True, 9, 0x2000_F000, "NANO-A", False),
        ("targets SRAM1", True, True, 9, 0x2001_0000, "NANO-A", False),
    ]
    refused_by = {}
    for name, origin, integrity, version, load_base, device_class, expect in cases:
        fresh = version >= fused_min_version
        bound = (device_class == this_device_class
                 and window_lo <= load_base and load_base + image_len - 1 <= window_hi)
        verdict = origin and integrity and fresh and bound
        print(f"{name}: version {version}, fresh {fresh}, bound {bound} -> {verdict}")
        assert verdict == expect, name
        if not verdict:
            refused_by[name] = [c for c, ok in (("origin", origin), ("integrity", integrity), ("freshness", fresh), ("binding", bound)) if not ok]
    assert refused_by["wrong product"] == ["binding"]
    assert refused_by["wrong load address"] == ["binding"]
    assert refused_by["rollback"] == ["freshness"]
    # inject a bug: a verifier that never checks binding must be caught by the new cases
    def buggy(origin, integrity, version):
        return origin and integrity and version >= fused_min_version
    escapes = [name for name, o, i, v, base, cls, expect in cases if buggy(o, i, v) and not expect]
    assert escapes == ["wrong product", "wrong load address", "targets SRAM1"], escapes
    print("PASS: only the all-checks-pass image is authorised; a verifier without binding admits", escapes)

    What the review looks for

    • The manifest format lists all four checks with the store each draws on: keys, image bytes, version minimum, device binding.

    • Rollback has a monotonic minimum that software cannot lower: the thermometer-coded version field in the fuse array, with its owner named.

    • The load address is part of the authorised manifest, not an independent loader decision.

    Verify and debug

    Present four images: correct, wrong signer, one flipped bit, and an older version. Confirm exactly one boots. Then present a correct, authorised image built for a different product and another built for a different load address, and confirm both are refused: binding is the check most often skipped, which is why the script above carries a case for each. If all four boot, the policy logic defaults to pass and the black box was never consulted.

    Rollback accepted: the version check compares against the image's own claimed minimum instead of fused storage. Symptom: an old, buggy, signed image boots fine. Caught by presenting a superseded image in the test plan. Fix: store the minimum where the image cannot rewrite it and compare there.

    If asked what authentication proves, list the four answers: origin, integrity, freshness, binding. Then say which one most designs forget, which is binding, and how you would test it.

    Check yourself Integrity passes and origin passes, but the image targets a different product. Which check refuses it, and why does that check matter?

    Binding refuses it: the manifest names a device class and load window the ROM enforces. Without binding, a valid image for one product becomes a valid attack on another.

    Related topics

    • Chain of Trust and Immutable ROM

    • Next: Failure Handling and Recovery

    Previous

    167 / 289

    Next: Failure Handling and Recovery

    Up next

    Up nextFailure Handling and Recovery

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error