【網絡通訊與網絡安全】網絡通訊中的隨機數如果不隨機會怎么樣?(下)
5 問題修復
當一切的分析都站穩了腳跟的時候,修復問題便是水到渠成的事情。
這里的修復,其實主要是兩個方面:
5.1 解決鏈接標準C庫的rand函數的問題
在原生的lwip組件代碼中,隨機數的適配本身就是移植的一部分,很遺憾,在我們出問題的芯片上看到的隨機數還是原生態的rand函數。
要想解決掉這個問題,有兩個思路:
一個是直接在lwip組件中的arch.h里宏定義LWIP_RAND的地方直接切換成芯片的硬隨機數接口xxx_rand,這種是在預編譯階段就完成的;
還有一種就是在鏈接階段處理的,把原本鏈接rand函數直接替換鏈接成芯片平臺的硬隨機數接口xxx_rand,gcc編譯就一個這樣的鏈接選項支持這樣的功能。
為了最大化保持lwip組件的代碼完整,我們不想改它一行代碼,所以我們采用第二種解決思路,只需要在編譯構建的全局鏈接參數中加上以下參數即可:
# enable hal-rand GLOBAL_LDFLAGS += -Wl,--wrap=rand
同時在hal層的C文件中,添加實現一個叫**__warp_rand**的函數即可。
/* wrap hal TRNG function */ int __wrap_rand(void) { extern int xxx_trng_rand(void); int ret = (int)xxx_trng_rand(); //call TRNG API return ret; }
經以上修改之后,lwip組件中調用的rand函數,最終就會調到xxx_trng_rand接口了,基本就解決了隨機數的問題。
5.2 解決芯片硬件隨機數不隨機的問題
但是,上面的分析部分,我們也提到了,這個芯片居然還出現了硬件隨機數不隨機的問題,準確說,它獲取的不是隨機數,而是一個無序的存儲序列,這無疑是一個重大bug。
就像這樣:
第一次開機:隨便獲取8個隨機數,初步得到 12345 2345 3456 6789 5678 9867 234 567
第二次開機:隨便獲取8個隨機數,還是得到 12345 2345 3456 6789 5678 9867 234 567
第三次開機:隨便獲取8個隨機數,依然得到 12345 2345 3456 6789 5678 9867 234 567
這顯然是不隨機的,是會出問題的。由于我們沒有芯片的datasheet以及不能完全掌握其TRNG的工作原理,我們把問題拋給了原廠,幸運的是,原廠很快給我們打了個小patch。
這個小patch說簡單是真的簡單,就僅僅是加了一個延時;但是這個延時,代價有點大!!!
以下是偽代碼,但足以展示這個代價的威力!
uint32_t xxx_trng_rand(void) { //enable TRNG register //patch here msleep(10); //dealy 10ms //read TRNG register data uint32_t ret = read_TRNG(); //disable TRNG register return ret; }
what???每獲取一個隨機數都要延時10ms?那我在某次網絡通訊中,可能要獲取成百上千個隨機數啊?這積累的延時簡直不能接受啊!
按理說,芯片不能“弱”成這樣,也沒有這么不合理的設計!看它這個延時,無非的意思就是說,我的TRNG寄存器不是一上電就可以工作的,你得先給它預熱下,稍后再來取嘛。
OK,既然你是這樣的特性,那么我們可不可以在驅動初始化的時候就給你預熱呢?獲取隨機數的時候就不預熱了哇?
試試看,于是有了這樣的偽代碼:
void sys_driver_init(void) { //normal driver init //special for TRNG warm up //step1. enable TRNG register //step2. msleep(10); //dealy 10ms //step3. disable TRNG register } uint32_t xxx_trng_rand(void) { //enable TRNG register //read TRNG register data uint32_t ret = read_TRNG(); //disable TRNG register return ret; }
這樣代碼一測試,完美!至少不需要每次獲取隨機數都dealy啊!其實在寫這段熱身代碼的時候,也踩了些坑的,比如沒有先enable TRNG寄存器就去delay,這無疑是delay了個寂寞啊?
至少,所以需要修正的代碼已經修正完成。值得注意的是,我們沒有改一行應用層及組件層的代碼,那么修復后的情況究竟如何,下一章節我們來驗證驗證。
6 問題驗證
6.1 隨機數的問題驗證
這里的驗證,其實是要一層層來驗證,由于問題的根源在于隨機數的不隨機導致,那么我們有限要驗證的應該是芯片TRNG的隨機性。
幸運的是,通過第5部分的patch代碼,我們有效地看到了TRNG的隨機性基本滿足了我們的要求,我們的驗證方法很簡單,就是開機完成初始化之后就獲取一組隨機數,然后就重啟;不斷地測試,觀察1000左右的數據。
從1000的數據,初步是可以看出去隨機性的,但如果需要過隨機數認證的話,還得使用NIST專門的測試工具做更進一步的驗證測試,這里就不展開論述了,有興趣的可以自行去了解下,像金融領域的PCI安全認證,隨機數的測試是非常關鍵的一環。
6.2 偶發的網絡掉線問題驗證
這個驗證就要回到issue本身了,雖然我們在上面的分析階段,其實也做了部分邊分析邊修正邊驗證的工作,但上面的場景更加側重的是在不清楚穩定的復現路徑的情況下不停地試錯。
所以,回歸驗證這個issue還是需要根據穩定的復現路徑,做一些控制變量來單項驗證,比如每次重啟后就固定使用12345端口發起MQTT鏈接,觀察其復現情況;恢復正常修復后的隨機端口號,觀測其情況。
同時,還得把壓測環境搭建好,同步進行壓力測試,觀測其情況。
只有以上幾點都通過驗證后,我們才有扎實的信心說:“這個issue可以close了”!
7 經驗總結
當你對一個網絡問題靠邏輯思考解決不了的時候,第一要想到的方法就是抓包分析;
抓包分析有方法,優先排查上層網絡協議的報文,比如TCP/TLS等;當上層協議包分析不出問題的時候,嘗試抓空口包;
TCP鏈接的狀態切換是所有基于TCP/IP協議的網絡通訊的基礎,重點分析它有助于打開你的分析思路;
所有偶現的問題,一定有復現路勁;如果你還沒出現,僅僅是你的測試次數還不夠多;
嵌入式里面的C庫,往往不是標準的,不能太輕易相對傳統意義上的C標準接口,多持懷疑的態度;
隨機數的隨機性在網絡通訊中非常重要,當你的網絡通訊超出你的想象的時候,不妨想想隨機數的可能性;
lwip協議棧的實現在嵌入式設備中太常用了,關于它的移植不能簡單的”拿來主義“,需要系統地、全面地了解其工作原理、代碼架構和適配的基本工作,了解其可能出問題的點和常規的解決方法;
技術偷懶使不得,技術債遲早都是要還的;
疑難bug的解決要及時總結和復盤,形成一定的分析方法論,指不定哪天就幫助你解決其他相關的疑難問題。
8 參考鏈接
在排查過程,我參考了一些輔助資料文檔,也請教了一些網路大神,這里不一一列舉,僅把一些極具參考價值的文檔鏈接歸放在這里,以表感謝,感興趣的可以自行研究研究。
基于RT-Thread 使用 wireshark 抓取 HTTPS 數據包
C++的RAND函數生成的值為什么存在嚴重的不隨機性?
lwip協議棧的init分析
lwip的實現梳理
TCP狀態轉換圖分析
TCP標志位詳解
已處于鏈接狀態的TCP鏈路收到SYN報文,服務器會怎么樣?
圖解TLS握手鏈接
MQTT-v3.1.1規范英文版本
MQTT-v3.1.1規范中文版本
9 更多分享
歡迎關注我的github倉庫01workstation,日常分享一些開發筆記和項目實戰,歡迎指正問題。
同時也非常歡迎關注我的CSDN主頁和專欄:
【CSDN主頁:架構師李肯】
【RT-Thread主頁:架構師李肯】
【C/C++語言編程專欄】
【GCC專欄】
【信息安全專欄】
【RT-Thread開發筆記】
【freeRTOS開發筆記】
【BLE藍牙開發筆記】
【ARM開發筆記】
【RISC-V開發筆記】
有問題的話,可以跟我討論,知無不答,謝謝大家。
單片機 網絡
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。