Android KeyStore流程
文章目錄
- 一、Keystore
- 二、Keystore架構及接口函數
- 1. Keystore組件架構
- 2. IKeymasterDevice.hal中的幾個重要接口函數
- 2.1 begin函數
- 2.2 update函數
- 2.3 finish函數
- 2.4 abort函數
- 3. Keymaster TA
- 4. 對稱密碼函數API
- 三、從Keystore到Keymaster的完整分析
- 1. cts問題
- 2. 代碼流程分析
- 2.1 模塊調用關系
- 2.2 代碼分析
一、Keystore
keystore主要是對密鑰庫的控制操作,包括密鑰的生成導入導出、加解密、簽名驗簽、訪問控制等。
概念的詳細介紹就請看看google官網的介紹。本篇主要是想總結一下keystore的使用流程,貌似其他博客講這個流程的不多,就整理一篇出來。
原創不易,轉載請標明出處 https://blog.csdn.net/jackone12347/article/details/122252644
二、Keystore架構及接口函數
1. Keystore組件架構
keystore涉及到的模塊之間的關系,一共有四個角色:
Andriod Keystore: 提供Android framework API,封裝密鑰庫相關的接口; Keystore:守護進程,通過Binder提供密鑰庫功能,通過AIDL與Framework keystore通訊; HIDL Keymaster: HIDL進程,封裝調用keymaster TA的密鑰函數接口;如android.hardware.keymaster@xxx-xxx KeyMaster TA: TEE中的TA,提供安全的密鑰庫操作和安全環境架構圖如下:
2. IKeymasterDevice.hal中的幾個重要接口函數
需要看一下這幾個相關的接口,HAL層的IKeymasterDevice.hal都有介紹,下面是我理解的幾個,因為和接下來的章節中分析CTS問題是相關的。
2.1 begin函數
作用:使用指定的密鑰開始加密操作。 如果一切順利,begin() 必須返回 ErrorCode::OK 并創建一個操作句柄,該句柄必須傳遞給后續對 update()、finish() 或 abort() 的調用。
函數原型:
2.2 update函數
作用:向正在進行的加密操作提供數據并可能從begin()接收輸出。
函數原型:
說明:
1、為了為緩沖區處理提供更大的靈活性,此方法的實現具有使用比提供的更少的數據的選項。
調用者負責循環到在后續調用中提供其余數據。 必須返回consumed的輸入量在 inputConsumed 參數中。
實現必須始終至少消耗一個字節,除非operation不能再接受;
如果提供的字節多于0且0字節是消耗,調用者必須認為這是一個錯誤并中止操作。
2、作為update的結果,實現還可以選擇返回多少數據。 這僅與加密和解密操作相關,因為簽名和驗證在完成之前不會返回任何數據。 建議盡早返回數據,而不是緩沖它。
2.3 finish函數
作用:完成以 begin() 開始的加密操作并使 operationHandle 無效。此方法是操作中最后調用的方法,因此必須返回所有處理過的數據。
函數原型:
2.4 abort函數
中止以 begin() 開始的加密操作,釋放所有內部資源并使操作句柄無效。
函數原型:
3. Keymaster TA
keymaster_operation_t結構體,TA中會反復用到這個結構體中的數據。
@ ta/include/operations.h typedef struct {uint8_t key_id[TAG_LENGTH];keymaster_key_blob_t *key;keymaster_blob_t nonce;keymaster_blob_t last_block;keymaster_operation_handle_t op_handle;keymaster_purpose_t purpose;keymaster_padding_t padding;keymaster_block_mode_t mode;keymaster_blob_list_item_t *sf_item;/*sign/verify data*/TEE_Time *last_access;TEE_OperationHandle *operation;TEE_OperationHandle *digest_op;size_t prev_in_size;uint32_t min_sec;uint32_t mac_length;uint32_t digestLength;uint32_t a_data_length;uint8_t *a_data;bool do_auth;bool got_input;bool buffering;bool padded;bool first; } keymaster_operation_t;keymaster_blob_t結構體
typedef struct {uint8_t* data;size_t data_length; } keymaster_blob_t;4. 對稱密碼函數API
Cryptographic API調用流程
- some_function() (Trusted App) - [1] TEE_*() User space (libutee.a) ------- utee_*() ---------------------------------------------- [2] tee_svc_*() Kernel space [3] crypto_*() (libtomcrypt.a and crypto.c) [4] /* LibTomCrypt */ (libtomcrypt.a)對稱密碼函數定義了執行對稱密碼操作(例如AES)的方式,涵蓋分組密碼和流密碼。
Cryptographic Operations API - Symmetric Cipher Functions
void TEE_CipherInit(TEE_OperationHandle operation, const void *IV,uint32_t IVLen)該函數啟動對稱密碼操作,操作必須關聯一個密鑰。
TEE_Result TEE_CipherUpdate(TEE_OperationHandle operation, const void *srcData,uint32_t srcLen, void *destData, uint32_t *destLen)該函數用來加密或解密輸入數據,輸入數據不必是塊大小的倍數,除非對此函數的一個或多個調用提供了足夠的輸入數據,否則不會生成任何輸出。
TEE_Result TEE_CipherDoFinal(TEE_OperationHandle operation,const void *srcData, uint32_t srcLen,void *destData, uint32_t *destLen)完成密碼操作,處理以前未通過調用TEE_CipherUpdate函數處理的數據以及srcData中提供的數據。隨后操作句柄可以重用或重新初始化。
三、從Keystore到Keymaster的完整分析
直接干巴巴地看源碼,有時候也不是很直觀地看出這幾大模塊是如何串聯起來 及如何完成從上到下的功能串調。
我們帶著一個cts的問題來分析一下相關的流程吧。
1. cts問題
運行
adb shell am instrument -r -e class android.keystore.cts.AES128CBCNoPaddingCipherTest#testDoFinalResets -w android.keystore.cts/androidx.test.runner.AndroidJUnitRunner報錯:
javax.crypto.IllegalBlockSizeExceptionat android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:490)at javax.crypto.Cipher.doFinal(Cipher.java:2055)at android.keystore.cts.BlockCipherTestBase.doFinal(BlockCipherTestBase.java:1400)at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:594)at android.keystore.cts.BlockCipherTestBase.testDoFinalResets(BlockCipherTestBase.java:563)at android.keystore.cts.AES128CBCNoPaddingCipherTest.testDoFinalResets(AES128CBCNoPaddingCipherTest.java:19)at java.lang.reflect.Method.invoke(Native Method)at junit.framework.TestCase.runTest(TestCase.java:168)at junit.framework.TestCase.runBare(TestCase.java:134)at junit.framework.TestResult$1.protect(TestResult.java:115)at androidx.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:73)at junit.framework.TestResult.run(TestResult.java:118)at androidx.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:51)at junit.framework.TestCase.run(TestCase.java:124)at androidx.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:62)at androidx.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:101)at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)at java.lang.Thread.run(Thread.java:923) Caused by: android.security.KeyStoreException: Keystore consumed 0 of 8 bytes provided.at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:143)at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338)at javax.crypto.Cipher.update(Cipher.java:1682)at android.keystore.cts.BlockCipherTestBase.update(BlockCipherTestBase.java:1454)at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:593)2. 代碼流程分析
下面針對以上報錯內容,進行詳細的分析,包含涉及到哪些模塊,以及是怎么完成這項AES測試的。
2.1 模塊調用關系
根據前面的內容得知一共有四個角色,其實還應該包含一個角色,就是JAVA應用程序APK,即keystore調用者。
所以五個角色為如下:
應用程序APK android.keystore.cts
Framework Keystore API
Keystore守護進程
Keymaster: HIDL進程:android.hardware.keymaster@xxx-xxx
Keymaster TA程序
五個角色的調用關系:
cts apk進程通過API ==> Framework Keystore API Framework Keystore API 通過Binder調用 ==> Keystore進程 Keystore進程通過HIDL ==> Keymaster HAL進程 Keymaster HAL進程 ==> 調用TEE中的keymaster TA2.2 代碼分析
順著cts報錯,我們分析一下相關代碼。
報錯內容:
12-29 13:39:04.818 3206 3228 E TestRunner: Caused by: android.security.KeyStoreException: Keystore consumed 0 of 8 bytes provided. 12-29 13:39:04.818 3206 3228 E TestRunner: at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:143) 12-29 13:39:04.818 3206 3228 E TestRunner: at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338) 12-29 13:39:04.818 3206 3228 E TestRunner: at javax.crypto.Cipher.update(Cipher.java:1682) 12-29 13:39:04.818 3206 3228 E TestRunner: at android.keystore.cts.BlockCipherTestBase.update(BlockCipherTestBase.java:1454) 12-29 13:39:04.818 3206 3228 E TestRunner: at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:593)BlockCipherTestBase.java的assertDoFinalResetsCipher函數如下:
getBlockSize()返回的是16字節,剛好是AES128的128bit, concat將兩個24字節update和final送入,一共48個字節。
關于AES算法以及工作模式,請參考我寫的另一博客AES及其工作模式詳解
如果送入的數據是48字節,剛好是16字節的整數倍,沒有問題;
但如果一次送入的數據是24字節,這樣需要分多次送入,第一次先送入16字節,然后第二次送入8字節進行存儲,然后再送入8字節,能拼成一個Blocksize 16字節進行處理。
這項cts測試大概就是這么個目的,想看一下設備中能否處理這種不是16字節整數倍的情況。
=》調用 mCipher.update函數
protected byte[] update(byte[] input) {byte[] output = mCipher.update(input);assertUpdateOutputSize((input != null) ? input.length : 0, (output != null) ? output.length : 0);return output;}內部的調用流程如下:
=》android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate
=》KeyStoreCryptoOperationChunkedStreamer.update
=》內部類MainDataStream的update實現
=》調用KeyStore.java的update方法
KeyStore》java的update方法,開始binder調用到守護進程Keystore
到這里的整個過程基本上都是AOSP的流程,所以問題一般不會出現在原生的代碼流程中。
需要繼續分析HAL層的調用。
Keystore服務端返回的流程在key_store_service.cpp中,能看到AIDL相關內容了。
調用dev->update函數和HAL keymaster進程通訊。
因為每家的keymaster方案,基本上都屬于定制,為什么呢?因為HAL封裝了和keymaster TA交互的接口。所以每家遇到的CTS問題可能都不大相同,需要具體問題具體分析了。
但各家基本上都會參考GP標準來開發
CA調用接口
TEEC_InvokeCommand(&sess, cmd, &op, &err_origin);
TA調用接口
TEE_CipherUpdate
最后這個CTS的修改是在keymaster TA中修復,具體code就不方便貼了,遇到類似問題時請具體看各家的keymaster TA及TEE中對應的libutee中封裝的函數實現。
修復后的復測結果:
# adb shell am instrument -r -e class android.keystore.cts.AES128CBCNoPaddingCipherTest#testDoFinalResets -w android.keystore.cts/androidx.test.runner.AndroidJUnitRunner INSTRUMENTATION_STATUS: class=android.keystore.cts.AES128CBCNoPaddingCipherTest INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: numtests=1 INSTRUMENTATION_STATUS: stream= android.keystore.cts.AES128CBCNoPaddingCipherTest: INSTRUMENTATION_STATUS: test=testDoFinalResets INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: class=android.keystore.cts.AES128CBCNoPaddingCipherTest INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: numtests=1 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: test=testDoFinalResets INSTRUMENTATION_STATUS_CODE: 0 INSTRUMENTATION_RESULT: stream=Time: 8.063OK (1 test)總結
以上是生活随笔為你收集整理的Android KeyStore流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试资料三
- 下一篇: android timer后函数继续执行