文章目錄
- 一、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() 的呼叫,
函式原型:
begin(KeyPurpose purpose, vec<uint8_t> keyBlob, vec<KeyParameter> inParams,
HardwareAuthToken authToken)
generates (ErrorCode error, vec<KeyParameter> outParams, OperationHandle operationHandle);
2.2 update函式
作用:向正在進行的加密操作提供資料并可能從begin()接收輸出,
函式原型:
update(OperationHandle operationHandle, vec<KeyParameter> inParams, vec<uint8_t> input,
HardwareAuthToken authToken, VerificationToken verificationToken)
generates (ErrorCode error, uint32_t inputConsumed, vec<KeyParameter> outParams,
vec<uint8_t> output);
說明:
1、為了為緩沖區處理提供更大的靈活性,此方法的實作具有使用比提供的更少的資料的選項,
呼叫者負責回圈到在后續呼叫中提供其余資料, 必須回傳consumed的輸入量在 inputConsumed 引數中,
實作必須始終至少消耗一個位元組,除非operation不能再接受;
如果提供的位元組多于0且0位元組是消耗,呼叫者必須認為這是一個錯誤并中止操作,
2、作為update的結果,實作還可以選擇回傳多少資料, 這僅與加密和解密操作相關,因為簽名和驗證在完成之前不會回傳任何資料, 建議盡早回傳資料,而不是緩沖它,
2.3 finish函式
作用:完成以 begin() 開始的加密操作并使 operationHandle 無效,此方法是操作中最后呼叫的方法,因此必須回傳所有處理過的資料,
函式原型:
finish(OperationHandle operationHandle, vec<KeyParameter> inParams, vec<uint8_t> input,
vec<uint8_t> signature, HardwareAuthToken authToken, VerificationToken verificationToken)
generates (ErrorCode error, vec<KeyParameter> outParams, vec<uint8_t> output);
2.4 abort函式
中止以 begin() 開始的加密操作,釋放所有內部資源并使操作句柄無效,
函式原型:
abort(OperationHandle operationHandle) generates (ErrorCode error);
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.IllegalBlockSizeException
at 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 TA
2.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)
- Framework KeyStore API
BlockCipherTestBase.java的assertDoFinalResetsCipher函式如下:
private void assertDoFinalResetsCipher(int opmode) throws Exception {
byte[] input = getKatInput(opmode);
byte[] expectedOutput = getKatOutput(opmode);
createCipher();
initKat(opmode);
assertEquals(expectedOutput, doFinal(input));
if ((opmode == Cipher.ENCRYPT_MODE) && (getKatIv() != null)) {
// Assert that this cipher cannot be reused (thus making IV reuse harder)
try {
doFinal(input);
fail();
} catch (IllegalStateException expected) {}
return;
}
// Assert that the same output is produced after the above reset
assertEquals(expectedOutput, doFinal(input));
assertEquals(expectedOutput, concat(
update(subarray(input, 0, getBlockSize() * 3 / 2)),
doFinal(subarray(input, getBlockSize() * 3 / 2, input.length))));
assertEquals(expectedOutput, doFinal(input));
// Assert that the IV with which the cipher was initialized is still there after the resets.
assertEquals(getKatIv(), mCipher.getIV());
assertAlgoritmParametersIv(getKatIv());
}
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
public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
OperationPromise promise = new OperationPromise();
try {
mBinder.asBinder().linkToDeath(promise, 0);
int errorCode = mBinder.update(promise, token, arguments, input);
if (errorCode == NO_ERROR) {
return interruptedPreservingGet(promise.getFuture());
} else {
return new OperationResult(errorCode);
}
} catch (RemoteException e) {
,,,
}
到這里的整個程序基本上都是AOSP的流程,所以問題一般不會出現在原生的代碼流程中,
需要繼續分析HAL層的呼叫,
Keystore服務端回傳的流程在key_store_service.cpp中,能看到AIDL相關內容了,
呼叫dev->update函式和HAL keymaster行程通訊,
@system/security/keystore/key_store_service.cpp
Status KeyStoreService::update(const ::android::sp<IKeystoreOperationResultCallback>& cb,
const ::android::sp<::android::IBinder>& token,
const ::android::security::keymaster::KeymasterArguments& params,
const ::std::vector<uint8_t>& input, int32_t* _aidl_return) {
ALOGE("key_store_service update entry");
if (!checkAllowedOperationParams(params.getParameters())) {
return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
}
auto dev = mKeyStore->getOperationDevice(token);
dev->update(token, params.getParameters(), input, [this, cb, token](OperationResult result_) {
if (!result_.resultCode.isOk()) {
mKeyStore->removeOperationDevice(token);
}
cb->onFinished(result_);
});
ALOGE("key_store_service update done");
return AIDL_RETURN(ResponseCode::NO_ERROR);
}
因為每家的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.063
OK (1 test)
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/401668.html
標籤:其他
