凖備你的環境
本次課程為了統一開發環境,我們會使用虛擬機運行 Ubuntu 作為測試環境,然後在虛擬機中執行 qemu 運行 OP-TEE 的系統。
使用凖備好的VM
1. 下載VM映像檔,並匯入virtualbox:
- https://github.com/NTHU-SCOPELAB/cr-tee-lab/releases/latest
- 使用者帳號:
ubuntu
- 使用者密碼:
ubuntu
- 下載所有zip檔之後解壓就可以得到一個ova檔,再進行匯入即可。
映像檔匯入VirtualBox教學(點我展開)
系統簡介
圖一:ARM v8 系統設計
在圖一展示的 ARM 系統中,橘底的方形標示出傳統系統的部分。隨著 non-secure OS(又稱 rich OS)的功能增強,漏洞也隨之增加。一旦rich OS(ROS)遭攻破,攻擊者便可操控所有分配給 ROS 記憶體與周邊設備,威脅所有程式的機密性與完整性。信任執行環境(TEE)為了解決此問題,確保系統安全,在系統中加入 TrustZone 的硬體進行隔離,並且為了避免攻擊面擴大,只有重視安全的服務會被分配到 Secure 的環境,其他的功能仍然是透過 ROS 完成。
圖二:OP-TEE 系統設計
OP-TEE 是個基於上述概念實作的專案。在這個環境下,應用開發將分為非安全的 client app(CA)和安全的 trusted app(TA)兩部分。 CA 可以使用 ROS 系統呼叫提供一般功能,另外,可以透過 TEE 客戶端 API 與 TEE 交互;TA 則會使用 TEE 內部 API,處理敏感數據與安全操作,如加密、認證等。
應用開發
圖三:TEE 交互
CA 和 TA 的通訊會依序使用圖三列出的 api,由 CA 主動呼叫,最終調用 TA 對應的函式。
- TEEC_InitializaContext/TA_CreateEntryPoint:TOS 對 TA 進行驗證,然後動態將執行檔載入記憶體
- TEEC_OpenSession/TA_OpenSessionEntryPoint:CA 和 TA 以 session 的形式建立溝通,此步驟進行登入、驗證
- TEEC_InvokeCommand/TA_InvokeCommandEntryPoint:使用 TA 提供的各項功能,可能重複多次
更進一步解釋,TA 會根據TEEC_Result TEEC_InvokeCommand( TEEC_Session* session, uint32_t commandID, TEEC_Operation* operation, uint32_t* returnOrigin)
commandID
判斷 CA 請求的服務,實際的參數則透過operation
傳遞 - 其他負責完成執行以後,清理的部分
傳遞參數
typedef union {
TEEC_TempMemoryReference tmpref;
TEEC_RegisteredMemoryReference memref;
TEEC_Value value;
} TEEC_Parameter;
typedef struct {
uint32_t started;
uint32_t paramTypes;
TEEC_Parameter params[4];
<Implementation-Defined Type> imp;
} TEEC_Operation;
總共可以傳遞四個參數,每個 TEEC_Parameter
參數可能代表 TEEC_TempMemoryReference
、TEEC_RegisteredMemoryReference
或 TEEC_Value
的其中之一。
TEEC_RegisteredMemoryReference
:使用 api 申請或註冊的記憶體,作業不會用到TEEC_TempMemoryReference
:不用 api 申請或註冊,CA 中的任意一塊記憶體TEEC_Value
:小的資料,例如uint32_t
在 TEEC_Operation
中,另一個重要的成員就是 paramTypes
,type 除了上面三種分類,另外還會依據輸入輸出分類,總共有以下九類
-
TEEC_RegisteredMemoryReference
Parameter Type Field to use TEEC_MEMREF_WHOLE_INPUT memref TEEC_MEMREF_PARTIAL_OUTPUT memref TEEC_MEMREF_PARTIAL_INOUT memref -
TEEC_TempMemoryReference
Parameter Type Field to use TEEC_MEMREF_TEMP_INPUT tmpref TEEC_MEMREF_TEMP_OUTPUT tmpref TEEC_MEMREF_TEMP_INOUT tmpref -
TEEC_Value
Parameter Type Field to use TEEC_VALUE_INPUT value TEEC_VALUE_OUTPUT value TEEC_VALUE_INOUT value
執行OP-TEE系統
課程提供的 OP-TEE 專案位於目錄 optee/,先建置需要的目標檔案
$ cd ~/optee/build
$ make toolchains -j$(nproc)
$ make -j$(nproc)
- 可能會跑一段時間,經過測試4核心的CPU大約需要1小時左右。
專案建置完成後,可以啟動 qemu 運行 OP-TEE
# 在 ~/optee/build/
$ make run-only
如果要新增檔案到虛擬機的檔案系統,需要重新執行 buildroot 的建置再啟動 qemu,命令則要改成:
# 在 ~/optee/build/
$ make run
接著,輸入 make
的終端會進入 qemu 的 CLI,並且會生成兩個新的終端機,分別是安全世界(Secure World)和正常世界(Normal World)。qemu CLI 會輸出提示等待用戶反應,用戶輸入 c 後,其他的終端會開始輸出 OP-TEE 開機過程的日誌
(qemu) c
接著在正常世界的終端,可以輸入登入的使用者。完成登入後,就可以執行 OP-TEE 專案包含的測試和範例
# 測試
$ xtest
# 範例
$ optee_example_hello_world
作業說明
本次作業分為實作與簡答兩部分。繳交時,實作部分只需要截圖表示;回答問題時,需簡要說明答案思路。最後,將兩部分合併為一份 PDF,上傳至 eeclass。
檔名格式請按照: HW1_<學號>
。e.g. HW1_113062595
。
實作
課程提供的 aes 專案位於目錄~/aes
。aes 依賴於前面我們編譯完的 OP-TEE 專案,並且 aes/Makefile 需要設定 OP-TEE 的正確路徑,否則會無法編譯 aes 專案。
aes 專案的結構如下:
- host/:CA 的目錄
- Makefile:不能直接使用
- main.c:CA 程式,需要同學修改
- ta/:TA 的目錄
- Makefile:不能直接使用
- aes_ta.c:TA 程式,需要同學修改
- ...
- Makefile:生成或清除 aes 專案的目標檔案
- ...
a. 實作目標
參考ta/include/aes_ta.h
的檔案內容。e.g.
並完成host/main.c
, ta/aes_ta.c
中指定的TODO修改。
b. 測試
程式修改完以後,可以在 qemu 內執行測試。首先使用生成目標檔案
# 在aes專案目錄下
$ make
然後將檔案放到 buildroot 對應的路徑
# 在aes專案目錄下
$ make install
重新執行 buildroot 的建置再啟動 qemu
# 在 optee/build/
$ make run
在Normal World中登入之後,應該就會出現下列指令可以使用來測試我們修改完的 aes 專案:
$ optee_example_aes
簡答
- 提供
$ optee_example_aes
的執行輸出的截圖。 - 說明 aes 實作部分,修改的程式目的是什麼?以及如何依據
ta/include/aes_ta.h
內容進行修改? - 說明 aes CA 的程式流程,每個步驟是如何操作 TA?
- 參考 api 文件,說明 aes TA 使用 key/operation handle 的時候,呼叫 api 的流程以及使用的參數。
- OP-TEE 支援 Value parameter, Memory reference 中哪些形式的參數? TA_InvokeCommandEntryPoint 的 Value parameter 最多能傳入幾個 32-bit 的參數
其它資源
簡介
OP-TEE 文件:REE Filesystem TA
TEE 的系統其實需仰賴 TEE 和 REE 協作來運行,舉例來說,物件可以存放在 REE 檔案系統,但在需要信任的情況下,藉由簽名和加密的方式,保證物件的機密性和完整性,確保 TEE 獲取的資源都是可信任。
實際上,其中一種 TA ---- REE Filesystem TA(REE FS TA)就是將 TA 儲存在REE 檔案系統上。 TA 以 <UUID>.ta
名稱儲存在 REE 的磁碟上,例如 Linux 檔案系統,檔案內容包含 ELF 檔、簽名,並且可以選擇對其加密。這比將 TA 燒錄在 secure flash(如 RPMB 或 secure storage)要靈活許多,特別適合開發階段使用。
REE FS TA 格式
共分為以下三種格式:
- Legacy TA:簽章、不加密。自 OP-TEE 3.7.0 起已不支援自動產生
hash = H(<struct shdr> || <stripped ELF>) signature = RSA-Sign(hash) legacy_binary = <struct shdr> || <hash> || <signature> || <stripped ELF>
- Bootstrap TA:簽章、不加密
hash = H(<struct shdr> || <struct shdr_bootstrap_ta> || <stripped ELF>) signature = RSA-Sign(<hash>) bootstrap_binary = <struct shdr> || <hash> || <signature> || <struct shdr_bootstrap_ta> || <stripped ELF>
- Encrypted TA:先進行簽章、加密,再做 MAC 驗證
nonce = <unique random value> ciphertext, tag = AES_GCM(<stripped ELF>) hash = H(<struct shdr> || <struct shdr_bootstrap_ta> || <struct shdr_encrypted_ta> || <nonce> || <tag> || <stripped ELF>) signature = RSA-Sign(<hash>) encrypted_binary = <struct shdr> || <hash> || <signature> || <struct shdr_bootstrap_ta> || <struct shdr_encrypted_ta> || <nonce> || <tag> || <ciphertext>
Subkey驗證及載入流程
Subkey 驗證
OP-TEE 支援使用 subkey(子金鑰)或 subkey 鏈(chain of subkeys) 來驗證 TA,這使得可以在不洩漏 root key 的情況下,將簽章權限授予第三方,支援多方獨立簽署 TA。另外,因為子金鑰簽署的 TA 必須位於該子金鑰的 UUID-V5 命名空間,這種方式還能防止 TA UUID 混淆,避免 UUID 衝突。
REE FS TA 載入流程
以下為從 REE 載入 REE FS TA 的流程:
- REE 世界的 Client App 請求啟動 TA
- TEE Core 收到請求後,透過 RPC 要求 tee-supplicant 幫忙讀取 TA
- supplicant 根據 UUID 在
/lib/optee_armtz/
中找對應的.ta
檔案 - supplicant 將整個
.ta
檔案讀入 shared memory,並傳給 TEE Core - TEE Core 對 TA 映像進行簽章驗證
- 驗證通過後,TA 被載入並初始化執行
TA簽署流程
OP-TEE 文件:Signing of TAs
所有 REE FS TA 都必須簽章。OP-TEE 的原始碼中,預設包含一把私鑰用於對 TA 簽名,對應的公鑰則會被編進 optee_os 的二進位中。每次載入 TA 時,核心都會用這把公鑰驗證該 TA 的簽章是否合法。
簽署相關元件介紹
在 optee_os 下有底下元件
- keys/default_ta.pem:預設私鑰
- scripts/sign_encrypt.py:用來簽署的腳本。使用說明可以查看
$ sign_encrypt.py -h
$ sign_encrypt.py <command> -h
- ta/mk/ta_dev_kit.mk:cross compile 的 makefile,用來編譯執行在 optee_os 上 TA,會生成 TA 的
.stripped.elf
和.ta
檔。
ta_dev_kit.mk 雖然會生成 .ta
,但是簽署的私鑰是採用 default_ta.pem,產品環境需要改以私人金鑰對 .stripped.elf
簽署
簽署操作流程
0. 在安全環境中產生 RSA 金鑰(2048 或 4096 bit)並匯出公鑰
$ openssl genrsa -out <privkey>.pem 2048
$ openssl rsa -in <privkey>.pem -pubout -out <pubkey>.pem
1. 編譯 OP-TEE OS 並指定公鑰。沒設定 TA_PUBLIC_KEY
的情況會自動生成和使用 default_ta.pem 對應的公鑰
$ TA_PUBLIC_KEY=<pubkey>.pem make all
2. 編譯、生成 TA 的 .stripped.elf
3. 設置 UUID 與金鑰變數
$ export TA_SIGN_KEY=<privkey>.pem
$ export TA_PUBLIC_KEY=<pubkey>.pem
$ export UUID=<uuid>
4. 產生 digest
$ sign_encrypt.py digest --key $TA_PUBLIC_KEY --uuid $UUID \
--in $UUID.stripped.elf --dig $UUID.dig
5. 離線簽署 digest
$ base64 --decode $UUID.dig | \
openssl pkeyutl -sign -inkey $TA_SIGN_KEY \
-pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss \
-pkeyopt rsa_pss_saltlen:digest -pkeyopt rsa_mgf1_md:sha256 | \
base64 > $UUID.sig
6. 合併 TA 與簽章
$ sign_encrypt.py stitch --key $TA_PUBLIC_KEY --uuid $UUID \
--in $UUID.stripped.elf --sig $UUID.sig --out $UUID.ta
作業說明
本次作業分為實作與簡答兩部分。繳交時,實作部分只需要截圖表示;回答問題時,需簡要說明答案思路。最後,將兩部分合併為一份 PDF,上傳至 eeclass。
檔名格式請按照: HW2_<學號>
。e.g. HW2_113062595
。
實作
aes 專案的結構如下:
- host/:CA 的目錄
- ...
- ta/:TA 的目錄
- include/
- aes_ta.h:修改 TA_AES_UUID
- Makefile:修改 BINARY
- ...
- include/
- ...
使用下方的 UUID 修改 LAB 1 的 aes 專案
aed564f1-960e-4117-8bb4-417b4e79c130
#define TA_UUID
{ 0xaed564f1, 0x960e, 0x4117, \
{ 0x8b, 0xb4, 0x41, 0x7b, 0x4e, 0x79, 0xc1, 0x30} }
根據簽署操作流程生成 .ta
。私鑰使用 default_ta.pem,因此「編譯 OP-TEE OS」的步驟可以跳過。
簡答
-
提供簽署過程的截圖,包含所有終端執行的命令,以及 sign_encrypt.py 兩個命令的輸出。
$ sign_encrypt.py display --in <.ta> $ sign_encrypt.py verify --uuid <uuid> --in <.ta> --key <default_ta.pem>
參考客戶 api第三章底下的相關章節,回答以下問題:
- CA 與 TA 使用的共享記憶體屬於安全記憶體還是非安全記憶體?
- 共享記憶體區塊是否可以在多個會話(Sessions)中重複使用?
- TEEC_AllocateSharedMemory、TEEC_RegisterSharedMemory 和 TEEC_TempMemoryReference 三種記憶體,可以實現 zero-copy 的機會從高到低排序是?