|
SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (ModusToolbox)
|
OPTIGA Trust M ถูกใช้ร่วมกันโดยทุก task ที่ต้องการข้อมูลรับรอง ลายเซ็นดิจิทัล หรือใบรับรอง (certificate) และยังใช้บล็อก I2C ร่วมกับตัวควบคุมการสัมผัส (touch) บน CM55 ด้วย มีกลไก 3 อย่างที่กันไม่ให้เรื่องนี้กลายเป็นการชนกันบนบัส และแต่ละอย่างมีกฎการจับคู่ที่แน่นอน:
| กลไก | API | ตอบคำถามใด | การจับคู่ |
|---|---|---|---|
| Gate | optiga_chip_enter() / optiga_chip_exit() | "ขณะนี้มี task อื่นถือชิปอยู่หรือไม่" | หนึ่ง exit ต่อหนึ่ง enter ที่สำเร็จ |
| Lock | optiga_manager_lock() / optiga_manager_unlock() | "manager ขึ้นแล้วหรือยัง และขอใช้ได้หรือไม่" | หนึ่ง unlock ต่อหนึ่ง lock ที่สำเร็จ และเป็นชื่อเรียกซ้อนบน gate |
| Touch hold | optiga_manager_touch_hold[_reason]() / optiga_manager_touch_release() | "กัน CM55 ออกจากบล็อก I2C ที่ใช้ร่วมกัน" | นับจำนวน และต้องเป็น 1:1 อย่างเคร่งครัดบนทุกเส้นทางขาออก |
นอกจากนี้ยังมีคู่ acquire/release — optiga_manager_acquire() คืนค่า optiga_util_t * ที่ใช้สั่งงานชิป และ optiga_manager_release() เป็นชื่อเรียกซ้อนของ optiga_chip_exit()
ลายเซ็นของความล้มเหลวที่ต้องหัดจดจำคือ OPTIGA_COMMS_ERROR (0x0102): ตัวควบคุมการสัมผัสกับ secure element (ชิปนิรภัยแยกส่วน) ถูกสั่งงานบน SCB เดียวกันในเวลาเดียวกัน
optiga_chip_enter() เป็นแบบ re-entrant ต่อหนึ่ง task ของ FreeRTOS (การซ้อนชั้นไม่มีต้นทุน) และคืนค่า false ด้วยเหตุผลเดียวเท่านั้น คือมี task อื่นถือชิปอยู่ ซึ่งเป็นความล้มเหลวขั้นเด็ดขาด ให้คืนค่าความผิดพลาดออกไป ห้ามเดินหน้าต่อ สิ่งที่ฟังก์ชันนี้ ไม่ใช่ คือการตรวจสอบว่าเริ่มใช้งานแล้วหรือยัง เมื่อ manager ไม่เคยถูกเริ่มใช้งาน ฟังก์ชันนี้จงใจคืนค่า true (tesaiot_optiga_manager.c:157-169): "Reporting failure here is what made every call site fail open, because a caller could not tell 'no lock exists' from 'somebody else has it'." การสำเร็จเมื่อยังไม่มีอะไรอยู่เลย และล้มเหลวเฉพาะตอนมีการแย่งกัน ทำให้การปฏิเสธมีความหมายเพียงอย่างเดียว
optiga_manager_lock() เป็นตรงกันข้าม: มันคืนค่า false เมื่อไม่เคยถูกเริ่มใช้งาน จึงเป็นการทดสอบที่ถูกต้องสำหรับคำถาม "manager ขึ้นแล้วหรือยัง" การข้าม optiga_manager_init() ก่อน TLS ทำให้ CertificateVerify ล้มเหลวทุกครั้ง เพราะ trustm_ecdsa_sign() เริ่มต้นด้วย optiga_manager_lock() (บท C4, mqtt_mtls_setup.c:184-201) การสลับ enter กับ lock ผิดด้านคือสาเหตุที่มีบันทึกไว้ของข้อบกพร่องประเภทเข้าถึงชิปโดยไม่ผ่าน gate หลายกรณี
ห้ามเรียก optiga_chip_exit() หลังจาก enter ที่ล้มเหลว — เส้นทางคืนค่าเร็วของ deferred-close มีอยู่เพื่อกิ่งนั้นโดยเฉพาะ gate คือตัวนับความลึกต่อหนึ่ง task ที่เป็นเจ้าของ และ exit ที่ไม่จับคู่จะทำให้ตัวนับเสียหายสำหรับทุกคน
นี่คือ trustm_ecdsa_sign() ซึ่งเป็นฟังก์ชันปลายทางของการจับมือ (handshake) ของ TLS ทุกครั้ง จับ lock ก่อน แล้วจึงจับ touch ไว้ตลอดทั้ง transaction — "the secure element and the touch controller share the bus, and the TLS handshake signs after the mTLS setup has already resumed touch polling."
ปล่อย hold ก่อนปลด lock ลำดับเดียวกันนี้ถูกใช้ในการตรวจสอบโมเดลแบบเป็นขั้น (staged) ด้วย:
รูปแบบ lock/unlock ที่ชัดเจนที่สุดอย่างในตำรา ซึ่งทุกเส้นทางคืนค่าเร็วถูกจับคู่ครบ อยู่ใน tesaiot_crypto.c — เป็นเอกสารอ้างอิงที่ส่งมอบมา แต่ proj_cm33_ns ไม่ได้คอมไพล์ (ไม่อยู่ในรายการ SOURCES ใดเลย) ให้อ่านเป็นสำนวนการเขียน ไม่ใช่ในฐานะ call site จริง:
อีกหนึ่งกฎเรื่องลำดับจากไฟล์ตระกูลเดียวกัน: ห้ามเปิด application ของ OPTIGA ขณะที่ถือ lock อยู่ ให้เปิดก่อน แล้วจึงจับ lock (optiga_trust_helpers.c:4604-4622)
optiga_manager_acquire() คืนค่า NULL จนกว่า optiga_manager_init() จะได้ทำงาน เมื่อได้ NULL ห้ามเรียก release() เพราะ implementation ออกจาก gate ไปแล้ว เมื่อได้ค่าที่ไม่ใช่ NULL ให้เรียก release() หนึ่งครั้งต่อหนึ่งเส้นทางขาออก รวมถึงเส้นทางความผิดพลาดด้วย — ฟังก์ชันที่ส่งมอบมามีทางออกสามทาง และปล่อยครบทั้งสามทาง รูปแบบแบบไม่ประสานเวลาจะตั้ง optiga_lib_status = OPTIGA_LIB_BUSY ก่อนเรียก แล้ววนถามหลังจากนั้น
hold เป็นแบบนับจำนวน (s_touch_holds) การซ้อนชั้นภายในฟังก์ชันที่ถูกเรียกซึ่งจับ hold ด้วยจึงถูกต้องและจำเป็น hold ครั้งแรกจะหน่วง 50 ms เพื่อให้การรับส่งข้อมูลของ touch ที่ค้างอยู่ทำจนจบได้ ให้จับ hold ไว้ตลอด ทั้งบทสนทนากับชิป ไม่ใช่ทีละปฏิบัติการ: ตัว ingest นี้ผ่านความล้มเหลวนั้นมาแล้วด้วยราคาที่แพง เมื่อการเขียนใบรับรองขนาด 580 ไบต์เสียบัส SCB5 กลางการรับส่งข้อมูลและล้มเหลวด้วย 0x0102 ทุกเส้นทางขาออกถูกรวบให้ผ่านป้าย pu_done: เพียงป้ายเดียวซึ่งเป็นที่ปล่อย hold
prov_open_held, prov_close_held, prov_make_csr_held, prov_manifest_anchor_held, prov_publish_pu_held: แต่ละตัวจับ hold พร้อมสตริงเหตุผล ทำงานหนึ่งอย่าง แล้วปล่อย คอมเมนต์เหนือกลุ่มนี้บันทึกเหตุผลที่ต้องมีไว้ว่า "of the eleven chip operations on this path only four" จับ touch เอง ส่วนการสร้างกุญแจและการลงลายเซ็น CSR ซึ่งเป็นสอง transaction ที่ยาวที่สุด กลับทำงานขณะที่ CM55 วนถาม FT5406 บน SCB เดียวกัน "which is what returns `OPTIGA_COMMS_ERROR (0x0102)` and then leaves the next call meeting `OPTIGA_UTIL_ERROR_INSTANCE_IN_USE (0x0305)`." และ: "Nothing holds across the 60 second wait for the platform: no chip traffic happens there, and freezing the screen for a minute would be its own bug."
สตริงเหตุผลไม่ใช่ของประดับ มันเดินทางไปยัง CM55 และถูกแสดงบนพาเนลระหว่างที่การสัมผัสถูกปิดอยู่ — "Opening the secure element", "Generating a key and signing the request", "Reading the secure element", "Closing the secure element"
จับ hold พร้อมเหตุผล แล้วเรียก optiga_chip_enter() เมื่อ enter ล้มเหลว ให้ ปล่อย hold และคืน mutex ก่อนคืนค่าออกไป mqtt_mtls_setup.c คือรูปแบบขยายของวินัยเดียวกันนี้ — ปล่อย 7 จุดต่อการจับ hold หนึ่งครั้ง
CM55 ปฏิบัติต่อ IPC_CMD_TOUCH_RESUME เป็น touch_disabled = false แบบไม่มีเงื่อนไข — มันไม่ใช่ตัวนับ (ipc_hsm_handler.c:1393-1405) การส่ง resume ดิบจาก task ใดก็ตามจะยกเลิก hold ที่ task อื่นกำลังพึ่งพาอยู่ และ touchpad_read() ครั้งถัดไปบน CM55 จะปิดแล้วเปิดบล็อก SCB ที่ใช้ร่วมกันใหม่ ทั้งที่ CM33_NS ยังมีการรับส่งข้อมูลค้างอยู่ นั่นคือ 0x0102 แบบเดียวกับที่การวนถามของ provisioning บนหน้านี้เองเคยก่อขึ้น "about 150 times per enrolment, at 400 ms intervals" ให้เรียกผ่าน optiga_manager_touch_hold_reason() / optiga_manager_touch_release() เสมอ และ ห้ามส่งคำสั่งดิบนั้นเด็ดขาด (ภาคผนวก X ข้อ 2)
ขั้นตอนเหล่านี้ใช้หน้า HSM เพราะหน้านี้ใช้กลไกครบทั้ง 3 อย่าง สิ่งที่สังเกตได้คือพาเนลและ UART
Home → HSM Security → Enrol Certificate (หรือสั่งอ่านข้อมูลรับรองด้วยวิธีใดก็ได้)
สิ่งที่ควรสังเกต การสัมผัสบนหน้าจอหยุดตอบสนอง และมีสตริงเหตุผลปรากฏขึ้น หนึ่งในสตริงข้างต้น เช่น Opening the secure element แล้วตามด้วย Generating a key and signing the request เมื่อ wrapper ปล่อย hold การสัมผัสจะกลับมา ไม่มีบรรทัด UART สำหรับ hold/release
เริ่มการลงทะเบียน (enrolment) แล้วขณะที่สตริงเหตุผลของมันยังอยู่บนหน้าจอ ให้สั่งอ่านข้อมูลรับรองจากบอร์ดเดียวกัน (จะเป็นการกระทำที่สองบนหน้า HSM หรือเรียกโมดูล optiga จาก REPL บน mtb-mpy ก็ได้)
สิ่งที่ควรสังเกต ปฏิบัติการที่ 2 รายงานความล้มเหลวโดยไม่ได้แตะชิปเลย เพราะ gate คืนค่า false เนื่องจากมี task อื่นถืออยู่ บน UART นั้น ipc_hsm_cred_read_sync ไม่พิมพ์อะไรบนเส้นทางนั้น ส่วน mutex ที่หมดเวลาจะพิมพ์ [HSM] cred_read_sync: mutex timeout (slot u) (ipc_hsm_handler.c:2408) ปฏิบัติการแรกทำงานจนจบตามปกติ นี่คือ "การปฏิเสธมีความหมายเพียงอย่างเดียว": การแย่งกัน ไม่ใช่ "ยังไม่ได้เริ่มใช้งาน"
ไม่ได้ขอให้ทำให้เกิดขึ้น เพราะโค้ดที่ส่งมอบมาถูกออกแบบให้ก่อกรณีนี้จาก UI ไม่ได้ แต่ให้จดจำไว้: ปฏิบัติการใดกับชิปที่คืนค่า OPTIGA_COMMS_ERROR (0x0102) และมักตามด้วย OPTIGA_UTIL_ERROR_INSTANCE_IN_USE (0x0305) ในการเรียก ครั้งถัดไป หมายความว่ามี transaction กับชิปทำงานขณะที่ CM55 กำลังวนถาม touch บน SCB เดียวกัน ให้มองหา hold ที่ขาดไป การส่ง IPC_CMD_TOUCH_RESUME ดิบ หรือการปล่อยที่ไม่จับคู่ซึ่งทำให้ตัวนับลงถึงศูนย์ก่อนเวลา บนเส้นทาง mTLS อาการคือ [PSA-Sign] ERROR: trustm_ecdsa_sign status=0x0102 (บท C4)
สิ่งที่ควรสังเกต พาเนลยังไม่ตอบสนองแม้ปฏิบัติการจบไปแล้ว — "an unbalanced release leaves the panel dead" ให้นับ hold และ release บนทุกเส้นทางขาออกของฟังก์ชันที่เพิ่งทำงานไป
ฟังก์ชัน optiga_manager_* / optiga_chip_* ทั้ง 10 ตัวอยู่ใน libbento_hsm.a ซึ่งไม่ต้องใช้ symbol ของ MicroPython เลย ส่วน optiga_trust_helpers.c, tesaiot_pu_ingest.c และ ipc_hsm_handler.c ส่งมอบมาเป็นซอร์สและคอมไพล์ได้ทั้งสอง variant ส่วน IPC ฝั่ง touch ที่อยู่ข้างใต้ — ipc_hsm_touch_pause, ipc_hsm_touch_pause_reason, ipc_hsm_touch_resume — อยู่ในรายการ consumer_must_provide.txt ของ archive (บท D3) กล่าวคือเทมเพลตเป็นผู้จัดหาให้ และผู้ใช้ไลบรารีรายใดที่เปลี่ยน HSM handler ของเทมเพลตก็ต้องจัดหาเองเช่นกัน