
最近調了一塊STM32U575的板子因為產品要防抄板、又要支持售后返修時不丟校準數據我研究了一下“RDP降級 OEM Key保護”這套玩法。標題里帶的這個詞組“RDP”在MCU語境里不是遠程桌面而是Read-out Protection讀保護“OEM Key”是ST在U5系列新增的硬件級密鑰保護機制所謂降級指的是把讀保護從Level 1退回到Level 0讓調試器重新能訪問Flash。這個筆記把我的試驗過程和踩坑點都整理出來了給正在做U5產品安全設計、或者被“RDP一降級Flash就全空”折磨過的工程師一個參考。1. 這個筆記解決什么問題1.1 從一次慘痛經歷說起早年在做F4系列產品的時候我遇到過這么一件事客戶的一臺設備運行異常需要我遠程指導現場的工程師接上ST-Link看一下日志和故障現場。結果現場工程師剛把RDP從Level 1降到Level 0準備讀數據整片Flash全被擦光了。固件沒了校準數據沒了最后只能返廠重刷。那次的根源就是F4等老平臺的規則很簡單粗暴——RDP Level 1降到Level 0時芯片內部強制觸發全片擦除沒有任何商量余地。這個設計是為了防止有人通過降級來繞過讀保護代價是正規售后也一起遭殃。STM32U5系列把這個問題解決得比較優雅。它保留了一級降級保護的底線同時允許你在OTP區域里固化一把OEM Key。只要降級時輸入的密鑰和芯片內部存儲的OEM Key匹配Flash內容就能原封不動保留調試器恢復訪問。如果不匹配則照舊全片擦除。這就等于給“售后解鎖”開了一條正規門同時把非法讀取的路徹底堵死。1.2 先把RDP和OEM Key的關系理清楚RDP在STM32上分為三級這個基礎概念值得再強調一遍Level 0沒有讀保護。調試口隨意訪問Flash隨便讀。Level 1禁止調試工具通過SWD/JTAG讀取Flash也禁止從RAM或System Memory啟動來執行代碼讀Flash。CPU內核正常運行不受影響。Level 2最高保護調試口徹底鎖死選項字節部分永久不可改沒有降級通道。在Level 1狀態下你還想用調試器調試唯一的正規退路就是降級到Level 0。老平臺降級必然擦片而U5支持“帶OEM Key的降級”其實就是給降級過程加一道密鑰校驗密鑰對Flash保留密鑰不對Flash清空。ST這樣設計的目的也很明確既保護了廠商的固件不被輕易抄走又給了正規授權維修一個體面的入口。1.3 這個特性適合誰、不適合誰如果你做的是消費類小產品出廠后基本不回收調試那OEM Key的意義不大老老實實開Level 1甚至Level 2就行。但如果你的產品包含以下幾種情況強烈建議研究一下這個方案設備內置了傳感器校準參數、生產序列號、業務密鑰等不可再生數據售后維修時不能丟產品支持現場升級升級失敗后需要恢復調試接口來排查但不能把整個Flash抹掉重來你希望允許售后人員在有授權的情況下打開調試口同時保證沒有密鑰的人拿到設備也讀不出固件。U5的定位就是帶TrustZone的高安全低功耗MCU選它做產品基本都會碰到這些安全需求。把OEM Key這個機制吃透等于給產品上了一道帶鑰匙的安全門。2. U5相比老芯片多了什么OEM Key的核心設計2.1 老方案為什么會“一刀切”老平臺的RDP降級策略是一個純二進制的決定要么保護要么全擦。從安全角度講它沒毛病因為如果降級不擦Flash攻擊者就能把Level 1的設備隨便降到Level 0然后隨便讀那讀保護形同虛設。但從工程實踐講這個策略太粗暴了它默認所有降級操作都是惡意的結果就是正常的售后維修也被誤傷。我見過不少團隊為了遷就這個機制干脆不開RDP Level 1導致固件直接被抄。也見過團隊開了保護但維修流程極其痛苦每次返廠都要重燒固件再貼標定。這兩種狀態都談不上好的產品設計。2.2 OEM Key是怎么工作的U5系列的OEM Key是一個固化在OTPOne-Time Programmable一次性可編程區域的機密。它有這幾個特征密鑰區域屬于一次性寫入OTP物理特性決定了只能從1寫成0不可逆所以Key一旦燒錯了基本只能換芯片密鑰是全局性的不是每個用戶程序隨便改的它在芯片安全域里被保護起來用戶代碼實際上讀取不到原始值防止被程序反讀泄露它和RDP降級流程硬件聯動由BootROM芯片出廠固件在復位流程中完成校驗不是靠用戶代碼判斷。整個校驗過程大致是這樣的芯片處于RDP Level 1同時使能了OEM Key保護。調試工具發起降級請求把RDP等級改為Level 0芯片復位后BootROM發現等級變化需要產生密鑰校驗。這時調試工具必須把OEM Key的值提交上來BootROM把提交值和OTP存儲值逐位比對一致則跳轉到正常用戶Flash啟動不一致則執行全片擦除。從用戶視角看降級動作本身和以前一樣就是寫Option Bytes后再復位。不同的是多了一步“提交密鑰”的過程CubeProgrammer、ST-LINK Utility或命令行工具都支持這個動作。2.3 什么時候用多Bank和TrustZone要注意U5的Option Bytes比老平臺復雜得多除了RDP之外還涉及DBANK雙Bank配置、SWAP_BANK、PA13/PA14調試引腳復用等。我的建議是第一次試驗不要碰雙Bank就用默認單Bank配置先把OEM Key這條鏈路跑通再去研究Bank切換帶來的影響。雙Bank模式下芯片的啟動行為和非易失性存儲操作會有差異OEM Key校驗邏輯也可能跟著Option Bytes的組合變化數據手冊里這些細節寫得很細踩坑成本高沒必要一上來就挑戰。另外如果同時開啟了TrustZone那調試口、Flash讀寫、RDP等級還會帶上安全/非安全狀態的概念CubeProgrammer里會出現TZEN相關選項。OEM Key本身不依賴TrustZone但TrustZone一旦啟用很多操作行為和純非安全模式不一致。我的實驗是在不啟用TrustZone的情況下完成的如果你的產品必須開TrustZone建議把TZEN的調試規則一起捋清楚再動RDP。3. 動手前準備與關鍵配置解讀3.1 軟硬件環境我實驗用的板子是NUCLEO-U575ZI-Q這是ST官方評估板板載ST-LINK算是U5系列里最容易上手的載體。軟件方面用了兩個必須的東西STM32CubeProgrammer版本建議6.4以上U5的OEM Key選項在老版本里確實不完整STM32CubeIDE用來寫測試代碼通過HAL接口讀取Option Bytes狀態、觸發降級復位等邏輯。你完全可以用軟件仿真或者用一塊裸板加外部ST-LINK來做但我不建議在前期用芯片本身內部BootROM做實驗因為一旦OEM Key配置不對會有概率把芯片搞到無法連接帶板載ST-LINK的評估板至少還能用UR模式搶救一下。3.2 幾個必須提前知道的Option Bytes選項在CubeProgrammer的OBOption Bytes配置界面里U5會列出大量選項初學者很容易看懵。和本主題相關的只需要關注這幾項RDP Level當前讀保護等級可切換Level 0 / Level 1 / Level 2OBKey0到OBKey7等OTP中保存OEM Key的寄存器每個32位通常8個組合成256位密鑰與OEM Key相關的鎖定開關把“寫入后鎖定”的選項打開防止之后被意外改寫啟動BANK相關配置保持默認單Bank即可。還要留意一點CubeProgrammer把OEM Key存儲在OB界面里的位置不同版本顯示的名稱有細微差別。我這份實驗里它在“OB Key”區域但換一個版本可能在“OTP”區域下。別慌按寄存器名字找“OBKey”關鍵字或者在界面上用關鍵字搜“Key”基本能定位。3.3 我對當前項目的配置規劃做實驗之前我先明確了自己的目標這很重要不然很容易隨手點設置結果把板子鎖死。我的目標規劃是這樣出廠時芯片處于RDP Level 1固件正常跑但調試口讀不了Flash燒錄一段已知的OEM Key到OTP并鎖定售后維修時通過ST-LINK配合正確的OEM Key執行降級到Level 0Flash內容保留密鑰錯誤時Flash應當全片擦除確保固件不被竊取。為了保證實驗可復現我在Flash里預寫了幾個內容不同的數據塊分別是0xA5、0x5A、0x3C等明顯特征值用來快速判斷降級后哪些區域被擦掉了。4. 完整實操從燒Key到降級驗證4.1 階段A對照組實驗不燒OEM Key直接降級先從最原始的方式開始目的是確認老行為在U5上是否還保留。我先把RDP從Level 0切到Level 1然后用CubeProgrammer的“Read Unprotect”操作執行降級。CubeProgrammer會彈出一條警告提示“可能擦除Flash”點擊確認后芯片復位Flash全空讀取到的內容全是0xFF。這個結果說明如果沒有OEM Key信息U5的行為和老平臺完全一致降級即擦除。這一步的意義在于給后面的對照實驗打底我能清晰地判斷出“保留Flash”的效果確實是OEM Key帶來的而不是我操作有誤。4.2 階段B燒錄OEM Key并開啟RDP Level 1接下來是正式實驗。先把RDP切回Level 0保證調試口完全打開然后在CubeProgrammer的OB界面里找到OBKey相關的寄存器填入一組測試用的256位密鑰比如我填的是OK0 0x01234567 OK1 0x89ABCDEF OK2 0x13579BDF OK3 0x2468ACE0 OK4 0xFEDCBA98 OK5 0x76543210 OK6 0x0F1E2D3C OK7 0x4B5A6978注意這把測試Key只是演示用途真正產品里別用這種順序規律太強的值。填完以后點寫OBCubeProgrammer會把OBKey寫入OTP區域。這里有一個很容易被忽略的步驟寫完之后要把“OBKey鎖定”選項一并打開并再次寫OB。如果不鎖理論上后續還可以繼續改寫OTP安全性打了折扣鎖定之后這組Key就徹底焊死在芯片上了。然后我把RDP切到Level 1同樣寫OB。此時芯片進入保護模式。為了確認效果我斷開ST-LINK重新連接嘗試讀取Flash讀出來的地址全部被屏蔽或報錯這是Level 1的典型表現說明保護生效了。4.3 階段C三種降級方式逐個驗證接下來才是關鍵環節。第一次我用正確的OEM Key降級。CubeProgrammer里選擇“Read Unprotect”操作時界面上會出現一個輸入密鑰的區域把剛才寫入的8個32位值逐個填進去然后執行。復位完成后我立刻讀Flash之前寫的0xA5、0x5A、0x3C這些特征值全部還在。也就是說密鑰匹配時RDP從Level 1降到Level 0Flash沒有被擦除。第二次我用錯誤的密鑰降級比如把最后一個字改成0x00000000其他值不變。CubeProgrammer同樣執行了降級流程但復位后再讀Flash所有內容變成0xFF全片被擦除。第三次我做了個更狠的測試在RDP Level 1下故意讓程序跑飛然后通過ST-LINK的connect under reset模式連接直接發起降級但不提供密鑰。結果是芯片照樣擦除了Flash保護邏輯被強制執行。這個對照實驗很直觀地說明了OEM Key的真正作用它不是阻止降級而是決定降級后Flash的去留。密鑰對了解鎖成功且不丟數據密鑰錯了或沒有密鑰解鎖成功但Flash保不住。4.4 在用戶代碼里讀取RDP狀態前面幾步都是通過CubeProgrammer操作實際產品中可能需要在設備端做狀態檢查比如開機自檢時確認當前RDP等級是否符合預期。用STM32CubeU5的HAL庫可以直接讀代碼非常簡單。#include stm32u5xx_hal.h void check_rdp_status(void) { FLASH_OBProgramInitTypeDef sOBCfg; HAL_FLASHEx_OBGetConfig(sOBCfg); if (sOBCfg.RDPLevel OB_RDP_LEVEL_0) { // 未開啟讀保護注意調試口可訪問 } else if (sOBCfg.RDPLevel OB_RDP_LEVEL_1) { // 讀保護開啟調試口無法訪問Flash } else if (sOBCfg.RDPLevel OB_RDP_LEVEL_2) { // 最高保護不可回退 } }這個函數在驗證量產固件是否正確燒錄時很有用。產品上線前可以在初始化日志里打一次RDP等級確保產線沒有漏設保護。另外用戶代碼也可以通過HAL接口把RDP等級往下降比如在條件滿足的情況下執行固件自更新流程void rdp_downgrade_with_key(void) { FLASH_OBProgramInitTypeDef sOBCfg; sOBCfg.OptionType OPTIONBYTE_RDP; sOBCfg.RDPLevel OB_RDP_LEVEL_0; HAL_FLASH_Unlock(); HAL_FLASHEx_OBUnlock(); HAL_FLASHEx_OBProgram(sOBCfg); HAL_FLASHEx_OBLaunch(); }但注意代碼里無法傳入OEM Key密鑰校驗仍然是由芯片BootROM在復位后執行而且用戶代碼無法直接讀取OTP里的Key原文。這個設計是有意的防止惡意程序把Key讀到后通過網絡傳出。所以固件觸發降級時實際上是觸發了一次“需要外部輸入密鑰”的復位流程如果你在無密鑰工具輔助的設備上直接調用HAL_FLASHEx_OBLaunch大概率結果就是Flash被擦掉。這一點務必想清楚不要以為寫了代碼就等于拿到了密鑰。5. 原理深入降級時芯片內部發生了什么5.1 從RDP寫入到復位校驗的完整流程把降級過程拆開看它其實不是“擦除”或“不擦除”這么簡單而是BootROM在執行一個安全狀態機。我把我的理解整理成時間線調試工具或用戶代碼把Option Bytes中的RDP字段從Level 1改為Level 0芯片收到OB Launch命令觸發一次系統復位BootROM上電讀取RDP字段和OEM Key相關鎖定狀態如果檢測到“RDP等級正在從高往低變化”進入密鑰校驗流程調試工具必須在下一條連接命令中提交OEM KeyBootROM將提交值和OTP中固化的Key逐位比較匹配則跳轉到用戶Flash執行啟動允許后續調試訪問不匹配則執行Flash全片擦除然后跳轉到空Flash狀態。第5步是整個機制的核心。它把“授權降級”從軟件邏輯層面搬到了硬件啟動流程里任何繞過應用代碼的手段都繞不開這一步因為這是BootROM的固定邏輯不是用戶代碼能改的。這也是我比較認可ST這個設計的地方它沒有把所有安全寄托在應用層而是放在了芯片最底層。5.2 為什么OTP一旦燒錯Key就只能換芯片OTP存儲器的物理特性是每一位出廠時為1寫入操作只能把1變成0不能把0還原為1。所以當你發現某一位寫錯了想要改回來基本是不可能的。我這次實驗里填Key的時候特別謹慎就是因為一旦把Key鎖定如果填錯任何值之后無論怎么操作都無法糾正只能換一塊芯片。更惡心的是鎖定之后你可能都沒機會驗證Key對不對直到某天售后人員拿著錯誤Key去降級Flash直接被擦成空白你才發現問題。所以量產流程里一定要有Key校驗環節。建議的做法是在寫Key之前先讀一遍OTP的原始值確認全1然后再寫寫完后立即讀回比對。CubeProgrammer支持讀回OTP區數據這是個很好的自檢手段。對于大批量生產最好讓產線工具自動生成密鑰、寫Key、讀回比對、再鎖Key一步到位不要人工手輸。5.3 設計一個產線友好的密鑰管理方案OEM Key在芯片里是固化的在產線上卻是可以生成的這就帶來一個管理問題每塊板子用同一個Key雖然管理簡單但一顆芯片的Key泄露等于全線產品裸奔每塊板子用不同Key安全性高但產線要維護密鑰數據庫售后也要按序列號匹配密鑰。我見過一些團隊的做法是同一批次產品用一個統一的OEM Key批量燒錄時從保密庫讀取產線不落地明文只在燒錄工具內存中使用。密鑰一旦泄露可以靠固件遠程升級換一批新的密鑰固件來緩解但OTP里舊的Key換不掉所以這種方案只適合對安全性要求不極端的產品。如果產品安全等級要求高建議把密鑰管理交給HSM硬件安全模塊或專門的密鑰管理系統出廠時按設備唯一ID生成密鑰并寫入同時把密鑰副本導入售后系統。這樣即使某個Key泄露影響面也只是單臺設備。當然這樣做的成本和復雜度會高不少具體是否值得要結合產品形態來決定。6. 常見問題排查與經驗6.1 問題速查表這段整理了我這次實驗和以往項目里常見的問題不一定全部發生在U5上但排查思路是通用的。現象可能原因處理方法降級后Flash全空Key沒起作用沒有正確輸入OEM Key或Key未使能確認Key已寫入OTP并鎖定確認在降級時提交了正確密鑰降級后Flash保留但重啟后不能運行FAP/TrustZone或啟動Bank配置被意外改動檢查Option Bytes里PA13/PA14、SWAP_BANK、雙Bank配置恢復到出廠前值讀保護從L1降到L0后程序跳飛校準數據區保留但啟動地址不對檢查代碼偏移確認該降級方式不會修改用戶代碼區布局連接ST-LINK報錯“Connection error”芯片已處于RDP Level 1或Level 2調試口受限使用connect under reset模式或先用UR模式連接后再操作寫OEM Key時發現OTP已有殘留值芯片被燒過多次OTP不可完全擦除只能換芯片或者接受殘留值設計專用密鑰CubeProgrammer無法識別OBKey區域版本過舊沒有U5配置文件升級CubeProgrammer確保安裝包包含U5系列支持Level 2鎖定后還想調試無解這是設計的終點只能換新芯片千萬別在量產驗證時開Level 26.2 幾條保命經驗第一永遠先備份原始Option Bytes。CubeProgrammer的OB界面里可以直接導出當前OB配置到文件花十秒鐘做一次備份能在你手滑配置錯之后省下大量時間。我現在的習慣是每次調安全相關選項之前先導出一份等實驗做完再對比差異。第二實驗階段不要直接上Level 2。Level 1至少還有降級通道Level 2是真的鎖死。很多剛接觸RDP的人容易搞混以為保護等級越高越好結果把工程板鎖成磚。第三連接方式建議使用connect under reset。處于Level 1的設備調試口本來就不允許直接讀Flash但連接階段如果芯片正在跑自己的代碼ST-LINK和芯片之間可能握手不成功。用復位連接模式讓芯片停在復位狀態再發起連接是成功率最高的方式。第四遇到“降級失敗”先查密鑰鎖定狀態不要急著重復操作。有伙伴在燒Key之后沒有點鎖定位導致后續的Option Bytes寫入把Key區域的一部分改成了其他值Key已經不正確了之后再怎么輸入原始Key也對不上。這種問題用CubeProgrammer讀回OTP區就能發現。6.3 一塊板子被誤鎖的搶救經歷最后分享一個我踩過的坑。早期測試時我在一塊U5板子上開過Level 2想驗證最高保護下到底會不會影響正常啟動。驗證結果是芯片運行正常但之后我想用ST-LINK連接看日志發現SWD口完全沒反應。由于Level 2下連Option Bytes都改不了這塊板子徹底淪為“只能跑不能調”的孤島。最后我只能把它當普通運行設備用不再嘗試調試。后來我把這塊板子上的TagAll功能測試完就扔一邊了再也不敢拿它做其他實驗。這個經歷教會我一個原則Level 2只適合最終產品不適合開發板。開發階段如果需要保護用Level 1加OEM Key就足夠了既安全又留了后路。7. 結語我在實際調試中最大的感受是OEM Key并不是一個“加了就安全”的開關它需要和產品流程配合。Key的生成、燒錄、保管、售后使用每一環都要有清晰規范否則要么Key泄露形同虛設要么Key遺失售后束手無策。我建議你把這塊板子的Key燒錄流程寫成腳本固化到產線工具里并且建立一把獨立的測試Key用于研發調試和量產Key分開管理。這樣研發和售后各用各的權限互不影響。如果你也在做U5相關的安全設計歡迎一起交流折騰經驗。