Skip to content

Acceleration story for NRF52 #165

Description

@ivmarkov

(Generated by Fable.)

Crypto acceleration notes: nRF52 (and RP2040/RP2350)

Research notes for extending mbedtls-rs-sys' hooking mechanism beyond the ESP32 family.
Status: research/decision record (2026-07); no nRF/RP backend implemented yet.

TL;DR

On nRF52, only P-256 is worth accelerating, and the best vehicle is not the CC310
crypto engine
but hand-written Cortex-M4 assembly (Emill/P256-Cortex-M4, wrapped for
Rust by ycrypto/p256-cortex-m4), plugged in behind ECDSA/ECDH-level _ALT hooks.
The CC310's measured P-256 performance is within what the assembly achieves on the CPU
itself, so the hardware buys nothing where it matters — a conclusion Nordic's own Matter
stack implicitly agrees with (it runs all Matter crypto in software on nRF52).

What is worth accelerating

Matter/Thread session establishment (PASE/CASE, commissioning, ECJPAKE joining) is
dominated by P-256 point multiplications: roughly 6–12 per handshake, at tens to a few
hundred ms each in portable software on a 64 MHz Cortex-M4. Everything else is noise:

  • AES-CCM / SHA-256 per packet is sub-millisecond in software — irrelevant next to
    radio/network latency.
  • The documented ECB peripheral (AES-128, encrypt-only, ~7.2 µs/block) is roughly at
    parity with good software AES — a marginal win at best.
  • The CCM peripheral is hard-wired to the BLE on-air frame format (1-byte AAD, 4-byte
    MIC) and cannot do Matter-style CCM (13-byte nonce, arbitrary AAD).
  • The RNG/TRNG is trivially available and already the right entropy source.

The options, ranked

1. p256-cortex-m4 assembly behind ECDSA/ECDH _ALT hooks (recommended)

Emill/P256-Cortex-M4: MIT-licensed, ~8.9 kB code, ≤2 kB stack, constant-time
(constant code-access pattern; optional constant RAM-access mode). Measured on nRF52840
@ 64 MHz: keygen 5.1 ms, ECDSA sign 5.9 ms, verify 15.3 ms, ECDH 14.2 ms — i.e. it
matches or beats the CC310 hardware (~18–21 ms/op end-to-end, measured via wolfSSL and
the EWSN 2021 study).

Why it is this fast: the inner loops are hand-scheduled around the ARMv7E-M DSP
UMAAL/UMULL instructions, keeping the 256-bit operands register-resident. Compilers
do not generate comparable code from portable C/Rust — this is why no pure-Rust
implementation (RustCrypto p256 included) is in the same class.

Integration path in mbedtls-rs-sys: the vendored Espressif fork supports the upstream
MBEDTLS_ECDSA_SIGN_ALT / MBEDTLS_ECDSA_VERIFY_ALT / MBEDTLS_ECDH_COMPUTE_SHARED_ALT
(and ECDSA_GENKEY_ALT) options — the same hook pattern already used for SHA/AES/ECP/exp-mod
can be extended with these.

Caveats:

  • The public API is protocol-level only (keygen/sign/verify/ECDH + point compression).
    No raw k*P / point addition is exposed, so SPAKE2+/ECJPAKE (which need arbitrary
    point math with full point results) stay on the software path unless the internal
    scalar-mult is exposed via a small upstream patch. CASE and cert validation get the
    full benefit; PASE only partially.
  • Maintenance status: dormant since ~2021 (the Rust wrapper is 0.1.0-alpha.5). This is
    less alarming than it sounds for a dependency-free, ISA-frozen, finished artifact — but
    it has had no formal verification and no public third-party audit.
  • Verification status (better than expected): the upstream repo ships a Wycheproof-based
    test harness in-tree (testgen.js pulls the ecdsa_secp256r1*/ecdh_secp256r1* vector
    sets and contains an independent slow reference implementation for differential test
    generation; runs on nRF52840). The Rust wrapper additionally cross-tests keygen/ECDH/
    sign/verify against RustCrypto p256. Zero correctness/timing issues filed in 5+ years
    while shipping in FIDO2 security keys (SoloKeys Solo 2, Nitrokey 3 via Trussed).
    Remaining diligence before adoption: regenerate the vectors against current
    C2SP/wycheproof and re-run on the exact GCC-converted build we'd ship; the
    constant-time claim still rests on design-by-construction + code review.

2. Status quo (portable software)

Matches the reference platform behavior: Nordic's own Matter stack (NCS) sets
PSA_CRYPTO_DRIVER_CC3XX default n on nRF52/53 and runs everything — including
SPAKE2+/ECJPAKE — on the nrf_oberon software driver, keeping CC310 only for the TRNG
(and AEAD key derivation). rs-matter with either software backend is ~5–10× slower than
NCS's Oberon on P-256 (Oberon is heavily optimized closed-source C); cheap partial
remedy for the mbedtls backend: raise MBEDTLS_ECP_WINDOW_SIZE (upstream default is 2)
for ~1.5–2× at a couple kB of heap.

3. Nordic binary blobs (nrf_oberon / nrf_cc3xx) — not recommended

Legally redistributable (Nordic-5-Clause permits binary redistribution with the license
text; the nrfxlib-sys crate is live precedent) and ocrypto 3.0.19 is even built against
Mbed TLS 3.6.5 (the exact version vendored here), with SPAKE2+/ECJPAKE P-256 APIs. But:
the APIs are protocol-level (don't fit the ecp_mul hook granularity), the mbedtls _ALT
shim layer was removed upstream (NCS is PSA-only now), the license restricts use to Nordic
silicon, and it taints an otherwise fully-open stack.

4. CC310 register-level drivers — now possible, poor ROI

The historical "only the ENABLE register is documented" situation is over: current Nordic
SVDs (nrfx, mirrored in the BSD-3 nrf-pac) expose the full CC_AES / CC_HASH / CC_PKA /
CC_RNG / CC_CTL register blocks, the live nRF52840 product spec documents them, and
embassy-nrf already ships a register-level CryptoCell TRNG driver. So blob-free drivers
are possible — but the engine only wins big on bulk SHA-256 (~30×) and bulk AES (~9×),
neither of which matters for Matter; its P-256 (~20 ms/op incl. driver overhead, blocking,
single-context, RAM-only inputs) does not beat option 1; and a PKA (EC engine) driver
would be a reverse-engineering project. The nRF54L's CRACEN is the properly-documented
future (full SVD + source-available Nordic drivers).

RP2040 / RP2350 (Pico) portability

  • RP2040 (Cortex-M0+, thumbv6m): no. The assembly technique fundamentally does not
    port: ARMv6-M has no long-multiply instructions at all (no UMULL/UMLAL/UMAAL,
    only 32×32→32 MULS), and the whole design is built around UMAAL chains. RP2040 also
    has zero crypto peripherals. Portable software (RustCrypto / p256-m class) is the only
    option; the 133 MHz+ clock partially compensates. mbedtls-rs-sys already ships
    thumbv6m-none-eabi prebuilts for exactly this path.
  • RP2350 (Pico 2, Cortex-M33): yes, most likely. Its M33 cores ship with the DSP
    extension (confirm in the datasheet), which is an explicitly supported target of the
    assembly. RP2350 additionally has a hardware SHA-256 accelerator and TRNG, so it
    would merit a SHA hook backend as well.

Reference numbers (nRF52840 @ 64 MHz unless noted)

Engine ECDSA sign ECDSA verify ECDH
P256-Cortex-M4 asm (measured, author) 5.9 ms 15.3 ms 14.2 ms
CC310 hardware (measured, wolfSSL/EWSN) ~20 ms ~21 ms ~18 ms
nrf_oberon (estimate; no public table) ~10–40 ms/op
mbedTLS software, default knobs ~100–300 ms/op
RustCrypto p256 (no published M4 numbers) portable-C class, as above
p256-m (size-first C, M4 @ 100 MHz) 155 ms 309 ms 144 ms

Sources

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions