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

Functions

int publish_csr (uint8_t *csr, size_t csr_length, uint16_t target_oid, uint16_t trust_anchor_oid, uint32_t payload_version)
 Publish an already-built CSR for the platform to sign; weak — NULL-check first. Arms the ingest.
int tesaiot_publish_protected_update (const char *target_oid, const char *trust_anchor_oid, uint32_t payload_version, bool with_csr)
 Ask the platform for a Protected Update (OIDs as hex strings); weak — NULL-check first.

Detailed Description

Two functions: the publishers that arm a run — they generate the correlation id (Three rules that recur on every topic rule 3). Both are weak-consumed (The six weak-consumed symbols — NULL-check contract): NULL-check the pointer first.

Variant
mtb-mpy and mtb-only

Function Documentation

◆ publish_csr()

int publish_csr ( uint8_t * csr,
size_t csr_length,
uint16_t target_oid,
uint16_t trust_anchor_oid,
uint32_t payload_version )

Publish an already-built CSR for the platform to sign; weak — NULL-check first. Arms the ingest.

Publish a CSR the caller has already built, for the platform to sign. target_oid is the slot the resulting certificate belongs in and trust_anchor_oid the anchor that will authorise writing it.

Contract
Publishes a CSR the caller has already built — under a touch hold, prov_make_csr_held() in the shipped flow — for the platform to sign. Weak: NULL-check before calling (The six weak-consumed symbols — NULL-check contract). Returns 0 on success, meaning queued; requires a live broker session. Wire: topic device/<id>/commands/csr, direct publish bypassing the publisher queue, QoS 0 (tesaiot_optiga_trust_m.c:1044-1049). It arms the ingest — generates the correlation id and clears g_protected_update_just_completed (tesaiot_optiga_trust_m.c:906-919) — so snapshot g_optiga_ingest_events before publishing to tell your own completion from an earlier one. OIDs are uint16_t here (0xE0E1, 0xE0E8), unlike the hex strings the Protected Update publisher takes.
Variant
mtb-mpy and mtb-only
extern int publish_csr(uint8_t *csr, size_t csr_length, uint16_t target_oid,
uint16_t trust_anchor_oid, uint32_t payload_version)
__attribute__((weak));
/* ...context: inside prov_run_locked() ... */
if (op != HSM_PROV_OP_PU) {
if (prov_manifest_anchor_held(s_prov.target_oid) != 0U) {
/* Say what is true. The manifest requirement is a metadata field,
* not a fuse: writing D0 back to E1 FC 07 clears it, which this
* firmware already does to key slots on every key generation, and
* which was measured on this board on 2026-08-08: D0 on 0xE0E1
* read 21 e0 e8 before the write and e1 fc 07 after. Calling it
* permanent would teach the operator something false about their
* own hardware. */
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_NONE,
"This slot takes signed manifests only. Use Protect, or "
"clear the requirement first. Nothing was changed.");
return;
}
if (publish_csr == NULL) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_NONE,
"CSR enrolment is not built into this firmware");
return;
}
if (!prov_make_csr_held(key_oid)) return;
}
char t[8], a[8];
(void)snprintf(t, sizeof(t), "%04X", s_prov.target_oid);
(void)snprintf(a, sizeof(a), "%04X", s_prov.anchor_oid);
/* Read the completion counter before publishing. Anything that finishes
* after this point is an answer to this request; anything that finished
* before it is not, no matter what a flag says. */
uint32_t events_before = g_optiga_ingest_events;
if (op == HSM_PROV_OP_PU) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_PUBLISH,
"Protected Update is not built into this firmware");
return;
}
prov_say(HSM_PROV_STATE_BUSY, HSM_PROV_STEP_PUBLISH,
"Asking the platform for a signed manifest");
events_before = g_optiga_ingest_events;
if (prov_publish_pu_held(t, a) != 0) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_PUBLISH,
"Could not publish the request - is the broker connected?");
return;
}
} else {
/* Plain enrolment publishes the CSR and the platform answers on
* commands/certificate, which the subscriber installs with an ordinary
* write. Once Protected Update has run against this object the chip
* refuses ordinary writes for good, so that path is simply gone - and
* saying so is more use than letting it fail inside the vendor library
* with a bare status code. */
prov_say(HSM_PROV_STATE_BUSY, HSM_PROV_STEP_PUBLISH,
"Sending the request to the platform");
events_before = g_optiga_ingest_events;
if (publish_csr((uint8_t *)s_prov.csr, strlen(s_prov.csr),
s_prov.target_oid, s_prov.anchor_oid, 1U) != 0) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_PUBLISH,
"Could not publish - is the broker connected?");
return;
}
}

◆ tesaiot_publish_protected_update()

int tesaiot_publish_protected_update ( const char * target_oid,
const char * trust_anchor_oid,
uint32_t payload_version,
bool with_csr )

Ask the platform for a Protected Update (OIDs as hex strings); weak — NULL-check first.

Ask the platform for a Protected Update of target_oid, optionally enrolling a fresh key with a CSR in the same exchange. The OIDs are hex strings, e.g. "E0E1".

Contract
Asks the platform for a Protected Update of target_oid, optionally enrolling a fresh key with a CSR in the same exchange. Weak: NULL-check first (The six weak-consumed symbols — NULL-check contract). Four arguments — a shipped-but-dead reference file calls it with three against this prototype and would not compile; it is not the shipping path and is not cited. with_csr = true generates a key pair on-chip (destructive by necessity), so wrap the call in a touch hold as prov_publish_pu_held() does. OIDs are hex strings — "E0E1", "E0E8", formatted with snprintf("%04X") at ipc_hsm_handler.c:2183-2184. Snapshot g_optiga_ingest_events first; the call arms the correlation id and the caller must later disarm via trustm_reset_state(). The implementation refuses when the chip's stored manifest anchor disagrees with the one named (tesaiot_optiga_trust_m.c:1440-1449). A second compiled exemplar — the MicroPython tesaiot.protected_update() binding with the same weak declaration and NULL guard — is bento_libs/claw/common/mpy/modtesaiot.c:730-743 and :760-762 (mtb-mpy only; that file is not in the mtb-only package).
Variant
mtb-mpy and mtb-only
static int prov_publish_pu_held(const char *target, const char *anchor)
{
int rc;
optiga_manager_touch_hold_reason("Generating a key and signing the request");
rc = tesaiot_publish_protected_update(target, anchor, 1U, true);
return rc;
}
/* ...context: inside prov_run_locked() ... */
if (op != HSM_PROV_OP_PU) {
if (prov_manifest_anchor_held(s_prov.target_oid) != 0U) {
/* Say what is true. The manifest requirement is a metadata field,
* not a fuse: writing D0 back to E1 FC 07 clears it, which this
* firmware already does to key slots on every key generation, and
* which was measured on this board on 2026-08-08: D0 on 0xE0E1
* read 21 e0 e8 before the write and e1 fc 07 after. Calling it
* permanent would teach the operator something false about their
* own hardware. */
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_NONE,
"This slot takes signed manifests only. Use Protect, or "
"clear the requirement first. Nothing was changed.");
return;
}
if (publish_csr == NULL) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_NONE,
"CSR enrolment is not built into this firmware");
return;
}
if (!prov_make_csr_held(key_oid)) return;
}
char t[8], a[8];
(void)snprintf(t, sizeof(t), "%04X", s_prov.target_oid);
(void)snprintf(a, sizeof(a), "%04X", s_prov.anchor_oid);
/* Read the completion counter before publishing. Anything that finishes
* after this point is an answer to this request; anything that finished
* before it is not, no matter what a flag says. */
uint32_t events_before = g_optiga_ingest_events;
if (op == HSM_PROV_OP_PU) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_PUBLISH,
"Protected Update is not built into this firmware");
return;
}
prov_say(HSM_PROV_STATE_BUSY, HSM_PROV_STEP_PUBLISH,
"Asking the platform for a signed manifest");
events_before = g_optiga_ingest_events;
if (prov_publish_pu_held(t, a) != 0) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_PUBLISH,
"Could not publish the request - is the broker connected?");
return;
}
} else {
/* Plain enrolment publishes the CSR and the platform answers on
* commands/certificate, which the subscriber installs with an ordinary
* write. Once Protected Update has run against this object the chip
* refuses ordinary writes for good, so that path is simply gone - and
* saying so is more use than letting it fail inside the vendor library
* with a bare status code. */
prov_say(HSM_PROV_STATE_BUSY, HSM_PROV_STEP_PUBLISH,
"Sending the request to the platform");
events_before = g_optiga_ingest_events;
if (publish_csr((uint8_t *)s_prov.csr, strlen(s_prov.csr),
s_prov.target_oid, s_prov.anchor_oid, 1U) != 0) {
prov_say(HSM_PROV_STATE_FAILED, HSM_PROV_STEP_PUBLISH,
"Could not publish - is the broker connected?");
return;
}
}