SDK for TESAIoT Dev Kit
API reference & tutorials (ModusToolbox)
Loading...
Searching...
No Matches

Functions

void bento_devmode_init (void)
 Idempotent; seeds the 32-byte RAM secret. Every public devmode entry self-calls it.
void bento_devmode_nonce_issue (uint8_t nonce_out[BENTO_DEVMODE_NONCE_LEN])
 Fill a fresh 16-byte nonce, overwriting any pending one; starts the TTL clock.
bool bento_devmode_unlock (const char *hmac_hex, size_t hmac_hex_len)
 Verify the HMAC against the pending nonce; consumes the nonce on any call (one-shot).
void bento_devmode_lock (void)
 Clear the unlocked flag.
bool bento_devmode_is_unlocked (void)
 Whether privileged verbs (bento.exec) are permitted right now.
size_t bento_devmode_secret_hex (char *out, size_t out_sz)
 The secret as 64 hex chars + NUL, or 0 if the buffer is too small; never log it to UART.
void bento_devmode_emit_provision (void)
 Emit the provision event once per boot on the CONNECTED transition; a failed send retries.
size_t bento_devmode_secret_fp_hex (char *out, size_t out_sz)
 First 4 bytes of SHA-256(secret) as 8 hex chars + NUL — safe to log.

Detailed Description

Eight functions: the developer-mode unlock, bento_devmode_*. Compiled only with ENABLE_PAGE_BENTO_BUDDY=1 (default 0, proj_cm33_ns/Makefile:64, :305); rebuild after make getlibs — Flag gate (read first).

Declarations: bento_devmode.h. Implementation bento_devmode.c is archived in libbento_secure.a. The shared secret is RAM-only and regenerated every boot (OPTIGA slot 0xE120 persistence is deferred, bento_devmode.c:96-97) — the desktop must re-provision after every reboot. Nonce TTL is BENTO_DEVMODE_NONCE_TTL_MS (60000); there is one in-flight challenge.

What this challenge/response does and does not protect.** Provisioning works by sending the secret in plaintext to the first peer that connects (Devmode, bento_devmode_emit_provision) over a TX characteristic that carries no GATTDB_PERM_AUTH_* bit, and the secret comes from an xorshift32 with under 32 bits of entropy. The HMAC step itself is sound; the net effect is a gate that keeps out a peer connecting second and no one else — a deterrent, not an enforcement boundary.

Variant
mtb-mpy and mtb-only

Function Documentation

◆ bento_devmode_init()

void bento_devmode_init ( void )

Idempotent; seeds the 32-byte RAM secret. Every public devmode entry self-calls it.

Contract
Idempotent (s_secret_ready guard, bento_devmode.c:92). No external caller is required — every public devmode entry point self-calls it (:103, :210, :221, :255). No locking. Seeds a PRNG and fills the 32-byte secret. Template call sites: 0 (archived: 4).

Quality of the secret.** The PRNG is xorshift32, seeded from the FreeRTOS tick, a stack address and one constant (bento_devmode.c:54-66) — under 32 bits of entropy, and the source comment says so itself: "not cryptographically strong". The OPTIGA RNG is not used. Moving to a chip-supplied seed is open work.

Variant
mtb-mpy and mtb-only
Origin
Lifted from BENTO-TESAIoT-libraries/claw/common/ble_nus/bento_devmode.c:90-108 (compiled into the prebuilt archive; not shipped as source).

◆ bento_devmode_nonce_issue()

void bento_devmode_nonce_issue ( uint8_t nonce_out[BENTO_DEVMODE_NONCE_LEN])

Fill a fresh 16-byte nonce, overwriting any pending one; starts the TTL clock.

Contract
Fills a fresh 16-byte nonce, overwriting any pending one, and starts the TTL clock. Self-inits (the excerpt under bento_devmode_init() shows it). Template call sites: 0; archived call site nus_commands.c:1475 (bento_devmode_nonce_issue(nonce); — the bento.devmode.nonce verb; compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ bento_devmode_unlock()

bool bento_devmode_unlock ( const char * hmac_hex,
size_t hmac_hex_len )

Verify the HMAC against the pending nonce; consumes the nonce on any call (one-shot).

Contract
Verifies an HMAC-SHA256 hex string against the pending nonce and the secret; true on match and unexpired TTL, which sets the unlocked flag. Consumes the nonce on any call (one-shot, replay protection). Template call sites: 0; archived call site nus_commands.c:1506 (if (bento_devmode_unlock(json + t->start, hlen)) { — the hex string is passed straight from the JSON token, not NUL-terminated; compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ bento_devmode_lock()

void bento_devmode_lock ( void )

Clear the unlocked flag.

Contract
Clears the unlocked flag. Template call sites: 0; archived call site nus_commands.c:1531 (bento_devmode_lock(); — compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ bento_devmode_is_unlocked()

bool bento_devmode_is_unlocked ( void )

Whether privileged verbs (bento.exec) are permitted right now.

Contract
Whether privileged verbs (bento.exec) are permitted right now. Template call sites: 0; archived call sites nus_commands.c:1538 (if (!bento_devmode_is_unlocked()) { — the gate-check form) and :1335 (status JSON) — compiled into libbento_secure.a, not shipped as source.
Variant
mtb-mpy and mtb-only

◆ bento_devmode_secret_hex()

size_t bento_devmode_secret_hex ( char * out,
size_t out_sz )

The secret as 64 hex chars + NUL, or 0 if the buffer is too small; never log it to UART.

Contract
Writes the secret as 64 hex chars + NUL; returns the length written or 0 if the buffer is too small — check for 0. The caller must not log this to UART. Template call sites: 0; archived call site bento_devmode.c:261 (if (bento_devmode_secret_hex(secret_hex, sizeof(secret_hex)) == 0) { inside bento_devmode_emit_provision — compiled into libbento_secure.a, not shipped as source).
Variant
mtb-mpy and mtb-only

◆ bento_devmode_emit_provision()

void bento_devmode_emit_provision ( void )

Emit the provision event once per boot on the CONNECTED transition; a failed send retries.

Contract
Emits {"evt":"bento.devmode.provision","secret":"<64 hex>"} over NUS once per boot (static already-sent flag; a failed send leaves it unset so the next CONNECTED transition retries). Wired into the CONNECTED transition in ble_nus_lazy.c, paired with bento_fw_emit_boot_complete(). Without this emit every bento.devmode.unlock answers not_permitted. Template call sites: 0; archived call site ble_nus_lazy.c:101 (bento_devmode_emit_provision();, comment :37 "SPEC §7.3 step 2" — compiled into libbento_secure.a, not shipped as source).

State the security consequence exactly.** This function sends the secret in plaintext to the first peer that connects after boot, and the TX characteristic carries no GATTDB_PERM_AUTH_* bit (see GATT data), so an unpaired peer receives it. The HMAC-SHA256 step itself is sound, but possession of the key is not restricted to an authorised desktop. The gate keeps out a peer that connects second and no one else — a deterrent, not an enforcement boundary.

Variant
mtb-mpy and mtb-only

◆ bento_devmode_secret_fp_hex()

size_t bento_devmode_secret_fp_hex ( char * out,
size_t out_sz )

First 4 bytes of SHA-256(secret) as 8 hex chars + NUL — safe to log.

Contract
First 4 bytes of SHA-256(secret) as 8 hex chars + NUL — safe to log; lets desktop and firmware confirm they hold the same secret without leaking it. Returns length written, 0 if the buffer is too small. Template call sites: 0 (archived: 3 — bento_devmode.c:277, nus_commands.c:1511, :1522).
Variant
mtb-mpy and mtb-only
Origin
Lifted from BENTO-TESAIoT-libraries/claw/common/ble_nus/bento_devmode.c:269-285 (compiled into the prebuilt archive; not shipped as source).