|
SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (ModusToolbox)
|
ปุ่ม → IPC → worker → ชิป → broker → ingest → ผลตัดสิน พร้อมจุดที่แน่นอนซึ่ง secure element (ชิปนิรภัยแยกส่วน) — ไม่ใช่ host — เป็นผู้ตรวจลายเซ็นดิจิทัลของแพลตฟอร์ม ระหว่างทางมีข้อเท็จจริง 3 ข้อที่ header ของ SDK เองอาจทำให้เข้าใจผิดได้:
hsm_enrol_open() / hsm_protect_open() เป็นฟังก์ชันของ cm55_core ที่ถูกส่งออก ส่วน overlay ที่เปิดขึ้น (hsm_provision_ui.c) ไม่ได้ส่งมอบมาเป็นซอร์ส เพราะอยู่ใน libbento_cm55.a header บอกข้อกำหนดการเรียกใช้ไว้ว่า IPC_CMD_HSM_PROVISION คืนค่าทันที และมี lv_timer เป็นตัววนถาม หน้าที่เป็นเจ้าของต้องจับคู่การเปิดนี้กับการรื้อใน destroy callback ของตน:
ต้องเป็นคำสั่งสุดท้ายของ destroy_cb หลังจากลบตัวจับเวลาของหน้านั้นแล้ว มิฉะนั้นตัวจับเวลาที่วนถาม provisioning จะยิงหลังจากที่ widget ถูกคืนหน่วยความจำไปแล้ว
IPC_CMD_HSM_PROVISION มีค่า 0xBE (ipc_communication.h:184) ปฏิบัติการที่มี: HSM_PROV_OP_POLL 0, HSM_PROV_OP_CSR 1 (Enrol), HSM_PROV_OP_PU 2 (Protect), HSM_PROV_OP_FETCH_CSR 3, HSM_PROV_OP_UNLOCK 4 สถานะมี IDLE/BUSY/DONE/FAILED ส่วนขั้นตอนมี KEYGEN 1, CSR 2, PUBLISH 3, WAIT 4, INSTALL 5, VERIFY 6 (:253-271) และมีรหัสเหตุการณ์หนึ่งตัว:
TESAIOT_PU_CHIP_VERIFIED_MANIFEST คือช่วงเวลาที่ secure element ตรวจลายเซ็นดิจิทัล ของแพลตฟอร์มเทียบกับ trust anchor ของตนเองด้วยตัวมันเอง เป็นขั้นเดียวในกระบวนการนี้ที่ host ซึ่งถูกเจาะแล้วปลอมไม่ได้ และเป็นเหตุผลที่ทำให้ทั้งหมดนี้มีค่ามากกว่าการเขียน ธรรมดา (ipc_communication.h:273-277)
handle_hsm_provision() ปฏิเสธด้วย HSM_PROV_REJECTED_BUSY หากมีรอบงานทำอยู่ ตั้งค่าปริยายของ target_oid เป็น 0xE0E1 และ anchor_oid เป็น 0xE0E8 แล้วตั้ง pending_op ส่วน prov_task วนถามค่านั้นทุก 50 ms และเรียก prov_run(op)
prov_open_held() — เรียก optiga_manager_init() แล้ว optiga_trust_open_application() ภายใต้ touch hold (บท D1) — จากนั้น prov_run_locked(op) แล้ว trustm_reset_state() บนทุกเส้นทางขาออก แล้วจึง prov_close_held() การรีเซ็ตนี้เป็นการกันไว้ 2 ชั้น เพราะตัว ingest ปลดสถานะรอของตนเองอยู่แล้ว (ขั้นที่ 10) แต่รอบงานที่หมดเวลาก็ทิ้ง id ค้างอยู่ในสถานะรอไม่ต่างจากรอบงานที่สำเร็จ และจุดนี้คือจุดเดียวที่ทุกรอบงานผ่าน
prov_run_locked ตัดการเชื่อมต่อแล้วเริ่ม MQTT ใหม่ รอ mqtt_is_started() (ไม่ใช่ mqtt_is_connected()) ผลัก "Connecting to the platform" ไปยังหน้าจอ ลองเชื่อมต่อ 2 ครั้ง ครั้งละ 15 s แล้วรอให้ data_received_event_group มีอยู่จริง — task ของผู้รับสมัครสมาชิกเป็นผู้สร้างมันขึ้น และการที่มันมีอยู่คือหลักฐานเดียวที่หาได้ ณ จุดนี้ว่ามีบางสิ่งกำลังรอฟังคำตอบอยู่ (ipc_hsm_handler.c:2023-2114) ข้อความเมื่อล้มเหลว: "No answer from the platform after 30 seconds."
มี 3 เรื่องในบล็อกนั้น:
การสร้างกุญแจและ CSR ทำงานภายใน prov_make_csr_held() ภายใต้เหตุผล "Generating a key and signing the request" ส่วนหน้าจอได้รับข้อความ "Generating a key pair inside the secure element" แล้วตามด้วย "Signing the request with the key that never leaves the chip" (ipc_hsm_handler.c:1807-1840) ส่วน subject คือ CN=<mqtt username>,O=TESAIoT
การ publish มีสองกิ่ง:
tesaiot_publish_protected_update(target, anchor, version, with_csr) — สี่อาร์กิวเมนต์ โดย OID เป็น สตริงเลขฐานสิบหก ("E0E1", "E0E8" ผ่าน snprintf "%04X") ค่า with_csr = true ทำให้เกิดการสร้างคู่กุญแจบนชิป ซึ่งเป็นเหตุผลที่ wrapper ต้องจับ touch ไว้ ตัว archive บันทึก OID ที่ร้องขอไว้ (อ่านย้อนหลังได้ผ่าน trustm_requested_target_oid() / trustm_requested_anchor_oid()) ปฏิเสธหากชิปผูกเป้าหมายไว้กับ anchor คนละตัว อยู่แล้ว อ่านตัวนับ anti-rollback มาเป็น current_version แล้ว publish ไปยัง device/<id>/commands/request ส่วน binding ฝั่ง MicroPython เป็นผู้เรียกที่คอมไพล์แล้วรายที่สอง — tesaiot_protected_update_py() ที่ modtesaiot.c:730-768 marker [hsm_publish_pu_mpy_binding] (มีเฉพาะใน zip ของ mtb-mpy — ไฟล์นี้ไม่อยู่ในแพ็กเกจ mtb-only) มันตั้งค่าปริยายของ target/anchor เป็น "E0E1"/"E0E8" และเรียกด้วยสี่อาร์กิวเมนต์แบบเดียวกัน:
หลังจาก publish ไม่ว่ากิ่งใด: บัฟเฟอร์ของ CSR จะถูกคืนหน่วยความจำ (1,600 ไบต์ที่ขั้นถัดไปต้องใช้) ข้อความ "Waiting for the platform" ไปขึ้นบนหน้าจอ และ worker วนถาม g_optiga_ingest_events != events_before เป็นเวลา 60 s โดยไม่จับ touch hold ค้างข้ามการรอนั้น
เมื่อแพลตฟอร์มลงลายเซ็น CSR แล้ว มันจะ publish ใบรับรองตรงไปยัง device/<id>/commands/certificate ตัวแยกเส้นทางตาม suffix (บท C3) จะส่งต่อไปยัง tesaiot_pu_ingest_certificate ซึ่งเป็น weak:
ติดตั้งภายใต้ touch hold หากติดตั้งแล้วให้ตั้ง g_protected_update_just_completed หากตอบแล้วให้เรียก trustm_reset_state() และเพิ่มค่า g_optiga_ingest_events การเพิ่มค่านั้นคือสิ่งที่ปลุก worker
ตัวแยกเส้นทาง → tesaiot_pu_ingest_bundle() (tesaiot_pu_ingest.c ซอร์สที่ส่งมอบมาขนาดราว 90 KB เป็นบันได goto pu_done แบบเส้นตรง) touch hold ถูกจับไว้ตั้งแต่ต้นตลอดทั้งการ ingest (บท D1) จากนั้น:
trustm_update_state(PROCESSING_JSON_BUNDLE) เป็นเพียง setter ลำดับที่คอมไพล์ไว้ในไฟล์นี้คือ PROCESSING_JSON_BUNDLE → WRITING_TRUST_ANCHOR → VERIFYING_MANIFEST → PROTECTED_UPDATE_SUCCESS โดยมีการเปลี่ยนไปเป็น *_FAILED และสตริงรายละเอียดบนทุกเส้นทางคืนค่าเร็ว ไม่มีอะไรอ่านตัวแปรนี้เพื่อตัดสินว่าจะทำอะไรต่อ บันได goto ต่างหากที่ทำหน้าที่นั้น
จากนั้นคือการป้องกัน replay:
trustm_current_correlation_id() ที่คืนค่า NULL หมายความว่า ไม่มีสิ่งใดบนอุปกรณ์นี้ร้องขอ bundle นี้ ให้ทิ้ง bundle นั้น และนี่ไม่ใช่ทางเลือก: แพลตฟอร์มเก็บ bundle ชุดล่าสุดไว้และส่งซ้ำในทุกครั้งที่เชื่อมต่อ ก่อนจะมีการตรวจนี้ ทุกครั้งที่เชื่อมต่อจึงมีการนำ bundle มาใช้ — "a replay: it rewrites the target, re-locks its access condition, and burns a step of the chip's anti-rollback counter" ซึ่งสังเกตได้ 3 ครั้งเมื่อ 2026-08-07 ส่วน id ที่ไม่ใช่ NULL จะถูกเทียบกับ correlation_id ของ bundle แบบตรงตัวและแยกตัวพิมพ์ใหญ่-เล็ก
anchor และเป้าหมายที่ bundle นี้มุ่งไปหา มาจากคำขอ ผ่านตัวเข้าถึงชนิด weak ที่มีค่าปริยายตามเอกสาร:
(เรื่องเตือนใจอยู่ในคอมเมนต์: เดิม anchor ถูกฝังไว้ตายตัวเป็น 0xE0E8 ดังนั้นคำขอที่ระบุ anchor ตัวอื่นจึงถูกรับไว้แล้วละเลยไปอย่างเงียบ ๆ และชิปก็ตรวจ manifest เทียบกับ object ที่แพลตฟอร์มไม่ได้ลงลายเซ็นให้ — ได้ 0x800F โดยไม่มีข้อมูลวินิจฉัยใด ๆ)
จากนั้นคืองานฝั่งชิป: optiga_manager_acquire(); เขียน metadata ของ trust anchor (0xE8 0x01 0x11, Data Object Type = Trust Anchor); STEP 3 — ถอดรหัส base64 ของ fragment_0..2 แล้วต่อกัน; เขียน metadata MUD บนเป้าหมาย; STEP 4 — optiga_util_protected_update_start(me, 1, manifest, len) ซึ่งเป็นจุดที่ ชิป ตรวจลายเซ็นดิจิทัลของ manifest เทียบกับ trust anchor เมื่อสำเร็จ:
(tesaiot_pu_ingest.c อยู่ถัดจากบรรทัด [4.1] Manifest verification OK ทันที และถูกใช้โดย ipc_hsm_handler.c:2258-2260 เพื่อขับขั้น INSTALL บนหน้าจอ) จากนั้น fragment ถูกนำไปใช้ด้วย protected_update_final ตั้งแฟล็กความสำเร็จ ตั้งบิตของ event group publish ACK ไปยัง device/<id>/telemetry/system และท้ายที่สุด:
ปล่อยชิปและ touch hold ก่อน จากนั้นจึงเรียก trustm_reset_state() และเพิ่มค่าตัวนับ เฉพาะกรณีที่ bundle นี้เป็นของเราเท่านั้น ลำดับนี้มีความหมาย: เดิมการเพิ่มค่าอยู่ก่อนหน้านี้สิบวินาที worker ที่รออยู่จึงตื่นขึ้นเข้าสู่ optiga_verify_cert_key_pair() แล้วไปต่อคิวอยู่หลัง gate ของ task นี้เอง — เป็นการชนกันแบบ 0x0102 ที่ gate มีอยู่เพื่อป้องกัน
ipc_hsm_handler.c:2234-2262: "Checking the certificate against the key in the chip" → optiga_verify_cert_key_pair(target, key) → ได้อย่างใดอย่างหนึ่งใน "The device can prove it holds the key this certificate names" (DONE), "Installed, but the certificate does not belong to this chip's key" (FAILED), "Installed; the pair check could not run"
สิ่งที่ต้องมีก่อน: WiFi (C1/C2) การเชื่อมต่อแบบ server TLS หรือ mTLS ที่ใช้งานได้ (C3/C4) device_id ของบอร์ดที่ลงทะเบียนไว้บนแพลตฟอร์มแล้ว และ build ที่ตั้ง ENABLE_OPTIGA_CLM=1 (เป็นค่าปริยาย — บท D3)
บน mtb-mpy: import optiga; optiga.read_metadata(0xE0E1) ให้จดค่าแท็ก C0 (สถานะ life-cycle) และแท็ก D0 (เงื่อนไขการเข้าถึงสำหรับการเปลี่ยนแปลง) ทุกบอร์ดบนโต๊ะทดสอบควรอ่านได้ C0 = 01 (Creation) ห้ามเขียนแท็ก C0
สิ่งที่ควรสังเกต TLV ของ metadata บน REPL วินัยของบท D1 ทำงานอยู่ใต้การเรียกนี้
Home → HSM Security → Enrol Certificate
สิ่งที่ควรสังเกต บนหน้าจอ: Connecting to the platform → Generating a key pair inside the secure element → Signing the request with the key that never leaves the chip → Sending the request to the platform → Waiting for the platform → Checking the certificate against the key in the chip → The device can prove it holds the key this certificate names ทั้งเจ็ดประโยคนี้ถูกผลักมาจาก CM33_NS (prov_say) และเป็นสตริงในซอร์สที่ตรวจสอบแล้ว บน UART พิมพ์จริง: ลำดับการเชื่อมต่อ [MQTT] ใหม่จากบท C3 จากนั้น [CSR] Using DIRECT PUBLISH (bypassing publisher_task queue) และ [DirectPub] Publishing u bytes to 's' (จาก tesaiot_optiga_trust_m.c ที่อยู่ใน archive ไม่มีการปิดเสียง) แล้วเมื่อได้รับคำตอบ [Subscriber] Certificate from platform (d bytes) (subscriber_task.c:217 รูปแบบ (printf) จึงพิมพ์จริง)
Home → HSM Security → Protected Update
สิ่งที่ควรสังเกต บนหน้าจอ: Connecting to the platform → Asking the platform for a signed manifest → Waiting for the platform → ขั้น INSTALL → ขั้น VERIFY และผลตัดสินของมัน บน UART พิมพ์จริง ตามลำดับ:
(subscriber_task.c:205 ผ่าน (printf); tesaiot_pu_ingest.c ไม่มีการปิดเสียง — LABEL_SUBSCRIBER มีค่าเป็น "[PU-Ingest]") บรรทัด [4.1] Manifest verification OK ตามมาด้วย tesaiot_pu_progress(TESAIOT_PU_CHIP_VERIFIED_MANIFEST) ในโค้ด — นี่คือ เหตุการณ์เดียวที่ปลอมไม่ได้ การตรวจลายเซ็นดิจิทัลเกิดขึ้นภายใน Trust M เทียบกับ trust anchor ของตัวมันเอง ทุกอย่างก่อนหน้านั้น host ที่ถูกเจาะแล้วพิมพ์ออกมาเองได้
optiga.read_metadata(0xE0E1)
สิ่งที่ควรสังเกต แท็ก D0 ตอนนี้กำหนดให้ต้องใช้ manifest ที่ลงลายเซ็นแล้ว (ค่ารูป 21 e0 e8 ซึ่งระบุ anchor) จากเดิมที่เป็น e1 fc 07 ส่วนแท็ก C0 ไม่เปลี่ยน คงเป็น 01 หาก C0 ขยับ ให้หยุดและรายงาน เพราะไม่มีอะไรในกระบวนการนี้เขียนค่านั้น และ optiga.write_metadata() ปฏิเสธ TLV ที่มีแท็กนั้นอยู่
ตัดไฟแล้วจ่ายไฟใหม่ เชื่อมต่อ WiFi แล้วเรียก tesaiot.connect() (หรือใช้หน้าจอ) broker จะส่ง bundle ที่เก็บค้างไว้ซ้ำ
สิ่งที่ควรสังเกต [Subscriber] Protected Update bundle (d bytes) ตามด้วย [PU-Ingest] Ignoring a Protected Update bundle nobody asked for. … และไม่มี OPTIGA acquired ไม่มีบรรทัด STEP ใด ๆ ไม่มีการเขียนใดเกิดขึ้น หากกลับเห็นลำดับ STEP 3/STEP 4 ครบทั้งที่ไม่ได้กดปุ่มใด แสดงว่า correlation id ถูกตั้งค้างไว้รอคำตอบทั้งที่ไม่ควรเป็นเช่นนั้น
สั่ง Enrol อีกครั้ง ตอนที่ 0xE0E1 กำหนดให้ต้องใช้ manifest แล้ว
สิ่งที่ควรสังเกต บนหน้าจอ: This slot takes signed manifests only. Use Protect, or clear the requirement first. Nothing was changed. — โดยยังไม่มีการสร้างกุญแจใด ๆ บนบอร์ดที่อยู่ในสถานะ Creation การเลือก HSM Security → Unlock (HSM_PROV_OP_UNLOCK) จะล้างข้อกำหนดนั้นและ Enrol กลับมาใช้งานได้อีกครั้ง
libbento_hsm.a ลิงก์ได้ทั้งสอง variant ส่วน ingest, helper และ handler คอมไพล์ได้ทั้งสอง variant เช่นกัน mtb-mpy เพิ่ม tesaiot.protected_update() และโมดูล optiga สำหรับขั้นที่ 1 และ 4 ส่วนบน mtb-only ให้อ่าน metadata ผ่าน optiga_util_read_metadata() จาก task ของตนเอง ภายใต้วินัยของบท D1 ปุ่ม Enrol/Protect และ overlay บน CM55 เหมือนกันทั้งสอง variant