SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (ModusToolbox)
Loading...
Searching...
No Matches

Functions

void trustm_update_state (trustm_state_t new_state, const char *status_code, const char *detail)
 เป็น setter ที่แนบเวลากำกับ ไม่ใช่ dispatcher; ให้ตั้งสถานะ WAITING_* ก่อนที่ transport จะเริ่ม
void trustm_reset_state (void)
 กลับสู่ IDLE และล้าง correlation id เป็นศูนย์ — นี่คือสิ่งที่จบรอบการทำงาน; ให้เรียกที่ทุกเส้นทางออก
const char * trustm_current_correlation_id (void)
 id ของรอบที่กำลังทำงานอยู่ หรือ NULL; NULL หมายถึงให้ทิ้ง bundle ที่เข้ามา — นี่คือการป้องกันการเล่นซ้ำ
uint16_t trustm_requested_target_oid (void)
 OID เป้าหมายที่คำขอครั้งล่าสุดระบุไว้; เป็น weak — ให้ตรวจ NULL และถอยไปใช้ 0xE0E1.
uint16_t trustm_requested_anchor_oid (void)
 trust anchor ที่คำขอครั้งล่าสุดระบุไว้; เป็น weak — รูปแบบเดียวกัน ค่าเริ่มต้น 0xE0E8

Detailed Description

5 ฟังก์ชัน หนึ่งตัวเขียน state machine หนึ่งตัวจบรอบการทำงาน และ 3 ตัวอ่านสิ่งที่คำขอที่กำลังทำงานอยู่ระบุไว้ สี่ใน 5 ตัวถูกใช้แบบ weak (symbol 6 ตัวที่ถูกใช้แบบ weak — ข้อกำหนดการตรวจ NULL) โดยมี trustm_update_state() เป็นข้อยกเว้น

variant ที่ใช้ได้
mtb-mpy และ mtb-only

Function Documentation

◆ trustm_update_state()

void trustm_update_state ( trustm_state_t new_state,
const char * status_code,
const char * detail )

เป็น setter ที่แนบเวลากำกับ ไม่ใช่ dispatcher; ให้ตั้งสถานะ WAITING_* ก่อนที่ transport จะเริ่ม

Move the state machine, and put it back at IDLE. status_code and detail are surfaced on the provisioning screen.

ข้อกำหนดการเรียกใช้
เป็น setter ที่แนบเวลากำกับ ไม่ใช่ dispatcher — เขียนตัวแปรสถานะและ tick stamp (tesaiot_optiga_trust_m.c:377-388) และไม่มี switch บน trustm_state_t อยู่ที่ใดเลยในทั้งสอง tree ไม่ต้องใช้ lock และไม่แตะชิป ไม่ใช่ weak ให้ตั้งสถานะ TRUSTM_STATE_WAITING_* ก่อนที่ transport จะเริ่ม มิฉะนั้น subscriber อาจประมวลผลคำตอบก่อนที่สถานะจะขยับ (ที่เดียวที่บันทึกเรื่องนี้ไว้คือไฟล์อ้างอิงที่ส่งมอบมาแต่ไม่ได้ถูกคอมไพล์ จึงระบุไว้ที่นี่แทนการยกข้อความมา) ทุกเส้นทางออกก่อนกำหนดจะได้การเปลี่ยนสถานะเป็น *_FAILED พร้อมข้อความ detail ตัว ingest ที่คอมไพล์จริงเดินผ่าน PROCESSING_JSON_BUNDLE (tesaiot_pu_ingest.c:663) → WRITING_TRUST_ANCHOR (:978) → VERIFYING_MANIFEST (:1736) → PROTECTED_UPDATE_SUCCESS (:1913) และใช้ PROTECTED_UPDATE_FAILED ที่ branch ของข้อผิดพลาด มี enumerator 4 ตัวที่ ประกาศไว้แต่ไม่มีการใช้งาน — ไม่มีผู้เขียนค่าในทั้งสอง tree ได้แก่ APPLYING_UPDATE, WAITING_FOR_CERTIFICATE, COMPLETE, APPLYING_FRAGMENTS ฟังก์ชันนี้ไม่ได้จบรอบการทำงาน มีเพียง trustm_reset_state() เท่านั้นที่ล้าง correlation id (รหัสจับคู่คำขอกับคำตอบ)
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* ...context: inside the Protected Update bundle ingest ... */
// State update: Processing JSON bundle
#if 0 /* TRACE messages disabled */
printf("%s [TRACE-2] State updated\n", LABEL_SUBSCRIBER);
fflush(stdout);
#endif
if (!subscriber_q_data.data) {
#if TESAIOT_DEBUG_VERBOSE_ENABLED
printf("%s ERROR: NULL bundle data\n", LABEL_SUBSCRIBER);
#endif /* TESAIOT_DEBUG_VERBOSE_ENABLED */
g_protected_update_active = false; // Reset flag on early exit
goto pu_done;
}

◆ trustm_reset_state()

void trustm_reset_state ( void )

กลับสู่ IDLE และล้าง correlation id เป็นศูนย์ — นี่คือสิ่งที่จบรอบการทำงาน; ให้เรียกที่ทุกเส้นทางออก

ข้อกำหนดการเรียกใช้
พาสถานะกลับไปที่ IDLE ล้าง correlation id เป็นศูนย์ และรีเซ็ต payload version — สิ่งนี้ ไม่ใช่ trustm_update_state(IDLE, …) คือสิ่งที่จบรอบการทำงาน ให้เรียกหลังจากปล่อยชิปแล้ว และก่อนที่ตัวนับการเสร็จสิ้นจะเพิ่มค่า เพื่อให้ผู้รอที่ตื่นขึ้นเพราะตัวนับไม่มีทางเห็น id ที่ยังพร้อมรับอยู่ ให้เรียกที่เส้นทางออกทุกเส้นของคำขอ ทั้งสำเร็จ ล้มเหลว และหมดเวลา: "a run that timed out leaves an id armed just as surely as one that succeeded" (ipc_hsm_handler.c:1964-1965) ตัว ingest ปลดสถานะพร้อมรับที่จุดเดียวที่ผู้เรียกทุกรายผ่าน เพราะผู้เรียกสองใน 3 รายลืมทำ ส่วน handler ยังรีเซ็ตรอบทุกเส้นทางออกของ prov_run_locked() เพิ่มอีกชั้นเป็นการกันพลาดซ้ำ — ที่จุดนั้นเป็น weak จึงต้องตรวจ NULL ของพอยน์เตอร์ (symbol 6 ตัวที่ถูกใช้แบบ weak — ข้อกำหนดการตรวจ NULL)
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* ...context: the single pu_done exit of the bundle ingest ... */
pu_done:
/* Announce completion here, not at the end of the write.
*
* The increment used to sit beside the success flag, roughly ten seconds
* before this function releases the chip: the acknowledgement publish and the
* 0xE0E1 read-back are both still ahead of it. The waiting provisioning task
* woke on that increment and went straight into optiga_verify_cert_key_pair(),
* which then queued behind this task's own chip gate - and if that wait
* exceeded its ten second ceiling the pair check ran anyway, alongside this
* task, which is the 0x0102 collision the gate exists to prevent.
*
* After the release, and after the touch hold, is the only point at which this
* task is genuinely finished with the chip. It matches what the certificate
* path already does. */
if (pu_handled_for_us)
{
/* Disarm before announcing. The request has been answered, so nothing is
* outstanding, and trustm_current_correlation_id() must stop matching.
*
* The platform RETAINS the last bundle, so one is redelivered on every
* subsequent connect. While the id stayed armed for the rest of the boot
* each of those redeliveries matched and was executed: a Protected Update
* nobody asked for, rewriting the target, re-locking its access condition
* on top of an unlock the operator had just performed, and consuming
* enough of the C heap that the MQTT publisher task could not be created -
* which then surfaced as an unrelated "could not publish" three layers
* away. Measured 2026-08-08.
*
* Disarming here rather than in each caller is deliberate. The callers -
* the HSM Security screens, tesaiot.protected_update() from MicroPython,
* and the reference menu loop - each have to remember otherwise, and two
* of the three did not. This is the one point every one of them passes
* through, and it is the point at which the statement "a request is
* outstanding" stops being true. */
g_optiga_ingest_events++;
}
/* ...context: tail of prov_run(), after prov_run_locked() returns ... */
/* Disarm before letting go. Nothing is outstanding once this returns.
*
* trustm_reset_state() clears the correlation id, and the ingest treats a
* NULL id as "nobody asked for this" - which is the only thing standing
* between the board and a replay. The platform retains the last Protected
* Update bundle, so one is delivered on every single connect; while the id
* stayed armed for the whole boot, that retained bundle matched, and the
* board silently re-ran a Protected Update nobody had asked for. Measured
* on 2026-08-08: an Enrol immediately after a successful Protect connected,
* received the retained bundle, re-locked 0xE0E1 on top of the unlock the
* operator had just performed, and exhausted the C heap far enough that the
* publisher task could not be created - so the enrolment then failed with
* "could not publish", three layers away from the cause.
*
* The function existed and did exactly this. It had no caller anywhere in
* the tree: the reference project drives it from its own menu loop, and
* that call was not carried across when these operations became screens.
*
* It goes here, around every exit of prov_run_locked() including the early
* failures, because a run that timed out leaves an id armed just as surely
* as one that succeeded. */
if (trustm_reset_state != NULL) {
}
prov_close_held();

◆ trustm_current_correlation_id()

const char * trustm_current_correlation_id ( void )

id ของรอบที่กำลังทำงานอยู่ หรือ NULL; NULL หมายถึงให้ทิ้ง bundle ที่เข้ามา — นี่คือการป้องกันการเล่นซ้ำ

ข้อกำหนดการเรียกใช้
id ที่จับคู่คำตอบของแพลตฟอร์มเข้ากับคำขอที่กำลังทำงานอยู่ เป็น NULL เมื่อไม่มีคำขอค้างอยู่ ค่านี้ถูกตั้งโดย publish_csr() และ tesaiot_publish_protected_update() และถูกล้างโดย trustm_reset_state() NULL ⇒ ให้ทิ้ง bundle ที่เข้ามา — นี่คือการป้องกันการเล่นซ้ำ (replay) ไม่ใช่การตรวจที่จะเลือกทำหรือไม่ก็ได้: แพลตฟอร์มเก็บ bundle ล่าสุดไว้และส่งมาให้ทุกครั้งที่เชื่อมต่อ ก่อนจะมีการตรวจนี้ ทุกการเชื่อมต่อจะนำ bundle นั้นไปใช้จริง (เขียนทับ target ล็อกเงื่อนไขการเข้าถึงของมันใหม่ และใช้ขั้นหนึ่งของตัวนับ anti-rollback ไป) พบ 3 ครั้งเมื่อ 2026-08-07 การเปรียบเทียบเป็นแบบตรงตัวและแยกตัวพิมพ์ใหญ่เล็ก ที่จุดของ handler ที่ใช้ค่านี้แบบ weak มีการตรวจ NULL สองจุด คือตัวพอยน์เตอร์ฟังก์ชัน แล้วจึงเป็น string ที่คืนกลับมา
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* ...context: inside the Protected Update bundle ingest, after JSON parse ... */
const char *expected_corr_id = trustm_current_correlation_id();
/* No outstanding request means nothing on this device asked for this. The
* platform retains the last bundle, so one is delivered on every connect —
* and until now every connect applied it. That is a replay: it rewrites the
* target, re-locks its access condition, and burns a step of the chip's
* anti-rollback counter, all without anyone asking. Observed three times on
* 2026-08-07, each one silently redoing the previous run. */
if (NULL == expected_corr_id) {
printf("%s Ignoring a Protected Update bundle nobody asked for. Retained "
"bundles arrive on every connect; call tesaiot.protected_update() "
"to arm one.\n", LABEL_SUBSCRIBER);
fflush(stdout);
g_protected_update_active = false;
vPortFree(json_copy);
goto pu_done;
}
if (expected_corr_id && parse_ctx.correlation_id) {
// Compare correlation_id (case-sensitive, exact match required)
bool match = (parse_ctx.correlation_id_len == strlen(expected_corr_id)) &&
(strncmp(parse_ctx.correlation_id, expected_corr_id, parse_ctx.correlation_id_len) == 0);
/* ...context: inside the provisioning status reply builder ... */
const char *c = trustm_current_correlation_id();
if (c) strncpy((char *)&resp->data[HSM_PROV_CORR_OFF], c, HSM_PROV_CORR_MAX - 1U);
}

◆ trustm_requested_target_oid()

uint16_t trustm_requested_target_oid ( void )

OID เป้าหมายที่คำขอครั้งล่าสุดระบุไว้; เป็น weak — ให้ตรวจ NULL และถอยไปใช้ 0xE0E1.

The OIDs the in-flight request named, and the id that correlates the platform's reply with it. NULL when nothing is in flight.

ข้อกำหนดการเรียกใช้
OID เป้าหมายที่คำขอครั้งล่าสุดระบุไว้ (ตั้งค่าที่ tesaiot_optiga_trust_m.c:1426) มีความหมายเฉพาะช่วงระหว่าง publish กับ trustm_reset_state() เท่านั้น เป็น weak: ให้ตรวจ NULL ของพอยน์เตอร์ และถอยไปใช้ 0xE0E1 (slot ของใบรับรอง (certificate) อันเป็น target ที่แพลตฟอร์มใช้เป็นค่าเริ่มต้น) ให้ห่อไว้ใน accessor ภายในแทนการเรียกที่จุดใช้งานแต่ละจุด อย่างที่ ingest ทำ
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* Which object this bundle is for — see the note on the definition. Weak so a
* build without the MQTT request path still links. */
extern uint16_t trustm_requested_target_oid(void) __attribute__((weak));
extern uint16_t trustm_requested_anchor_oid(void) __attribute__((weak));
/* The anchor the last request named. Everything below used to bake 0xE0E8 in,
* including the target's Change access condition — so asking for a different
* anchor was accepted, stored, and then quietly ignored, and the chip would
* verify the manifest against an object the platform had not signed for. That
* is 0x800F with no diagnostic, the same failure the target OID caused before
* it was made to follow the request. */
static uint16_t pu_anchor_oid(void)
{
: 0xE0E8U;
}
static uint16_t pu_target_oid(void)
{
return (trustm_requested_target_oid != NULL)
: 0xE0E1U; /* certificate slot — the platform's default target */
}

◆ trustm_requested_anchor_oid()

uint16_t trustm_requested_anchor_oid ( void )

trust anchor ที่คำขอครั้งล่าสุดระบุไว้; เป็น weak — รูปแบบเดียวกัน ค่าเริ่มต้น 0xE0E8

ข้อกำหนดการเรียกใช้
trust anchor ที่คำขอครั้งล่าสุดระบุไว้ (ตั้งค่าที่ tesaiot_optiga_trust_m.c:1427) มีอายุการใช้งานและรูปแบบ weak เหมือนกัน ค่าเริ่มต้นคือ 0xE0E8 ข้อเตือนใจอยู่ในคอมเมนต์ที่ยกมา: เดิมทีทุกจุดฝังค่า 0xE0E8 ไว้ตายตัว รวมถึงเงื่อนไขการเข้าถึงชนิด Change ของ target ด้วย การขอ anchor ตัวอื่นจึงถูกรับไว้ เก็บไว้ แล้วถูกเพิกเฉยอย่างเงียบ ๆ — จากนั้นชิปจะตรวจสอบ manifest เทียบกับอ็อบเจ็กต์ที่แพลตฟอร์มไม่ได้เซ็นให้ ได้ 0x800F โดยไม่มีข้อมูลวินิจฉัยใด ๆ ตาราง OID ของ mTLS ใน mqtt_mtls_setup.c คือตารางคู่กันที่บอกว่า slot ใดเก็บอะไร
variant ที่ใช้ได้
mtb-mpy และ mtb-only
/* Which object this bundle is for — see the note on the definition. Weak so a
* build without the MQTT request path still links. */
extern uint16_t trustm_requested_target_oid(void) __attribute__((weak));
extern uint16_t trustm_requested_anchor_oid(void) __attribute__((weak));
/* The anchor the last request named. Everything below used to bake 0xE0E8 in,
* including the target's Change access condition — so asking for a different
* anchor was accepted, stored, and then quietly ignored, and the chip would
* verify the manifest against an object the platform had not signed for. That
* is 0x800F with no diagnostic, the same failure the target OID caused before
* it was made to follow the request. */
static uint16_t pu_anchor_oid(void)
{
: 0xE0E8U;
}
static uint16_t pu_target_oid(void)
{
return (trustm_requested_target_oid != NULL)
: 0xE0E1U; /* certificate slot — the platform's default target */
}
/* ...context: inside mqtt_mtls_setup() ... */
/* Bootstrap on the Infineon factory pair, as the reference firmware does.
*
* This used to try the device pair (0xE0E1/0xE0F1) first and fall back to
* the factory pair if the read came back empty. That fallback can never
* run: reading an unprovisioned slot does not return zero bytes, it never
* completes, and the wait inside read_certificate_from_optiga() spins
* without a timeout — so a board that has not been enrolled yet hangs
* CM33_NS instead of falling back. Observed 2026-08-04 on a Dev Kit whose
* 0xE0E1 is empty.
*
* official_pse84_trustm_mTLS_tesaiot reads 0xE0E0 unconditionally at boot
* (main.c:493) for the same reason, and tesaiot_select_mqtt_certificate()
* there (tesaiot_optiga_trust_m.c:1349-1374) forces the factory pair even
* when a device cert exists, because after a reset the key in 0xE0F1 may no
* longer match the certificate in 0xE0E1 — which is exactly what happens
* once a CSR has generated a fresh key. Until enrolment installs a matching
* pair and something proves the match, the factory pair is the only one
* that can be trusted to work.
*
* Per the Infineon pre-provisioning map, 0xE0E0/0xE0F0 are the IFX-
* provisioned certificate and key; 0xE0E1/0xE0F1 are TESAIoT's device pair;
* 0xE0E9 holds the TESA CA. */
uint16_t cert_oid = 0xE0E0; /* IFX-provisioned factory certificate */
uint16_t key_oid = 0xE0F0; /* IFX-provisioned factory key */