Skip to main content

Bare-metal Write-Side Custody enforcement for MicroPython sensor nodes with hardware crypto abstraction

Project description

sovereign-sensor — Phase 9

A lightweight, MicroPython-compatible Hardware Abstraction Layer (HAL) that enforces data custody at the Point of Genesis by sealing each sensor observation into a versioned, tamper-evident, replay-protected JSON transmission envelope before any network transit or cloud ingestion occurs.

Zero external dependencies. Internal library code is restricted to standard MicroPython built-ins (json, sys, hashlib, hmac, binascii). Targets ESP32 and Raspberry Pi Pico via MicroPython; fully exercisable on CPython 3.12 for desktop CI.


Architecture

Hardware Abstraction Layer

SovereignCryptoDriver (interface.py) defines a three-method contract:

Method Contract
initialize_hardware() -> None Load key material; configure accelerator subsystem.
sign(payload: bytes) -> bytes Return raw binary signature bytes (no encoding).
algorithm() -> str Return a canonical algorithm identifier string.

Two concrete drivers are provided:

  • SoftwareFallbackDriver — HMAC-SHA256 over a VFS-resident binary key file. Used on all non-ESP32 targets (desktop CI, Raspberry Pi Pico, etc.). The class-level MOCK_KEY_SENTINEL = "/mock/test_gateway.key" opts into a deterministic stub key for desktop testing; every other path that cannot be opened raises RuntimeError immediately, eliminating silent key substitution. A zero-byte key file raises ValueError to prevent HMAC keyed with b"".

  • ESP32HardwareDriver — Skeleton placeholder for the on-chip ECC accelerator. initialize_hardware() raises NotImplementedError until register-level engineering is complete; bootstrap_sensor_node() catches this and falls back to SoftwareFallbackDriver automatically so the sealing and VFS layers remain exercisable on the workbench.

Seven-Step Sealing Pipeline (SovereignEnvelope.seal())

  1. Monotonic sequence counter — computed transiently as _sequence + 1 and bound into the preimage. The in-memory counter and VFS file (default .sovereign_sequence) are advanced only after signing succeeds, so a sign() failure never consumes a sequence position or introduces a gap in the on-disk custody timeline. A truncated (0-byte) file resets to 0; a negative stored value is clamped to 0. VFS write failures degrade gracefully to RAM-only tracking.

  2. Algorithm identifier — queried from the active driver via algorithm() and embedded in the authenticated preimage, providing protocol agility without a schema change.

  3. Canonical payload serializationjson.dumps(payload, separators=(",", ":"), sort_keys=True, ensure_ascii=False) guarantees a byte-identical preimage for semantically equivalent payloads regardless of key insertion order. ensure_ascii=False forces raw UTF-8 output for all characters, eliminating the \uXXXX-vs-raw-UTF-8 split-brain divergence that would cause cross-platform HMAC verification to fail silently on any payload containing characters outside U+007F.

  4. UTF-8 byte-count-prefixed preimage assemblynode_id, timestamp, and the algorithm identifier are each encoded to UTF-8 independently; the prefix for each field is the UTF-8 byte count (not the Unicode character count). The preimage is assembled from raw byte slices:

    1|{len(node_bytes)}:{node_id}|{len(time_bytes)}:{timestamp}|{seq}|{len(algo_bytes)}:{algo}|{canonical}
    

    Byte-count prefixes close all variable-length field injection surfaces: delimiter injection (any two inputs that differ only in where a | character falls produce identical naive pipe-joined preimage bytes without prefixes) and multi-byte encoding ambiguity (a receiver using character-count semantics parses field boundaries at the wrong byte offset for any non-ASCII field value).

  5. Driver signing — raw preimage bytes traverse driver.sign(), returning raw binary output from the underlying cryptographic primitive.

  6. Hex encodingbinascii.hexlify() maps all byte values 0x00–0xFF to the lowercase alphanumeric characters 0–9, a–f, preventing UnicodeDecodeError on constrained MicroPython silicon.

  7. Wire frame serialization — all seven envelope fields (v, n, t, q, alg, d, s) are packed into a dict and serialized with json.dumps(..., separators=(",", ":"), sort_keys=True, ensure_ascii=False), freezing the alphabetical key sequence and enforcing raw UTF-8 wire encoding independently of MicroPython allocator-driven insertion order.


Quick Start

from sovereign_sensor import bootstrap_sensor_node

# Auto-selects ESP32HardwareDriver or SoftwareFallbackDriver at runtime.
# Falls back to SoftwareFallbackDriver with a warning if hardware crypto
# is not yet implemented on the target.
envelope = bootstrap_sensor_node(
    node_id="node-temperature-01",
    private_key_path="/flash/keys/node.key",
    sequence_file="/flash/.sovereign_sequence",
)

observation = {"sensor": "temperature", "value": 21.4, "unit": "C"}
wire_bytes = envelope.seal("2026-06-16T12:00:00Z", observation)
# → b'{"alg":"hmac-sha256","d":{"sensor":"temperature","unit":"C","value":21.4},'
#     '"n":"node-temperature-01","q":1,"s":"<64-char hex>","t":"2026-06-16T12:00:00Z","v":1}'

Desktop / CI (mock key sentinel)

from sovereign_sensor import bootstrap_sensor_node
from sovereign_sensor.drivers.software_fallback import SoftwareFallbackDriver

envelope = bootstrap_sensor_node(
    node_id="ci-node-001",
    private_key_path=SoftwareFallbackDriver.MOCK_KEY_SENTINEL,
)
wire = envelope.seal("2026-06-16T00:00:00Z", {"ping": True})

Invariants

Property Guarantee
Replay protection Monotonic q counter persisted to VFS; resumes across reboots.
Sequence atomicity Counter advanced only after sign() succeeds; a signing failure leaves the on-disk counter unchanged with no gap.
Key material safety Missing or empty key file raises immediately; no silent substitution.
Preimage determinism sort_keys=True and ensure_ascii=False on payload; byte-count length prefixes on all three variable-length fields.
Wire frame determinism sort_keys=True and ensure_ascii=False on the outer frame; byte-identical UTF-8 output across CPython and MicroPython builds.
Encoding safety binascii.hexlify prevents UnicodeDecodeError on raw binary digest bytes.
Zero dependencies No network calls, no PyTorch, no external packages at runtime.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sovereign_sdk_sensor-1.3.0.tar.gz (18.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sovereign_sdk_sensor-1.3.0-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file sovereign_sdk_sensor-1.3.0.tar.gz.

File metadata

File hashes

Hashes for sovereign_sdk_sensor-1.3.0.tar.gz
Algorithm Hash digest
SHA256 f476e6b0983b0c76347c12935762e142ba575822c217dbcf2e25895e844c9ec0
MD5 fb05bfc4800c7ee4c4cacfa09dc89a3d
BLAKE2b-256 c782638d662438cc75a6eee51c543ec912728b08bb387e7ee1a773bf2c908d8b

See more details on using hashes here.

File details

Details for the file sovereign_sdk_sensor-1.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for sovereign_sdk_sensor-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 367368000c80c9d2f9a6e0866bc708972c4695f9bae6fd7e5eb5685528e1faf0
MD5 8e02de5006671045bf7cc94ffe43c157
BLAKE2b-256 96cb178dc96e287b25f6ec42669af6b7ac9974c98a6f32ae78e3adfd7e0cea9b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page