optee km4.0 VTS: VerificationOperationsTest.HmacSigningKeyCannotVerify failed
写在前面:
一言难尽呐!
VTS测试下来的结果,就是一眼看不到头的Fail。
只能逐个分析,挨个解决了。
Fail项:
# ./VtsHalKeymasterV4_0TargetTest --gtest_filter=PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default
Note: Google Test filter = PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from PerInstance/VerificationOperationsTest
[ RUN ] PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default
hardware/interfaces/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp:542: Failure
Expected equality of these values:
ErrorCode::OK
Which is: OK
Finish(op_handle_, finish_params, message.substr(consumed), signature, &finish_out_params, &output)
Which is: VERIFICATION_FAILED
Google Test trace:
hardware/interfaces/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp:517: VerifyMessage
[ FAILED ] PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default, where GetParam() = "default" (5289 ms)
[----------] 1 test from PerInstance/VerificationOperationsTest (5290 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (5291 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default, where GetParam() = "default"
1 FAILED TEST
用例代码:
hardware/interfaces/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
/* * VerificationOperationsTest.HmacSigningKeyCannotVerify * * Verifies HMAC signing and verification, but that a signing key cannot be used to verify. */ TEST_P(VerificationOperationsTest, HmacSigningKeyCannotVerify) { string key_material = "HelloThisIsAKey"; HidlBuf signing_key, verification_key; KeyCharacteristics signing_key_chars, verification_key_chars; ALOGD("1. HmacSigningKeyCannotVerify ==> ImportKey, expected OK"); EXPECT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .Authorization(TAG_ALGORITHM, Algorithm::HMAC) .Authorization(TAG_PURPOSE, KeyPurpose::SIGN) .Digest(Digest::SHA_2_256) .Authorization(TAG_MIN_MAC_LENGTH, 160), KeyFormat::RAW, key_material, &signing_key, &signing_key_chars)); ALOGD("2. HmacSigningKeyCannotVerify ==> ImportKey, expected OK"); EXPECT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder() .Authorization(TAG_NO_AUTH_REQUIRED) .Authorization(TAG_ALGORITHM, Algorithm::HMAC) .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY) .Digest(Digest::SHA_2_256) .Authorization(TAG_MIN_MAC_LENGTH, 160), KeyFormat::RAW, key_material, &verification_key, &verification_key_chars)); string message = "This is a message."; ALOGD("2-3. HmacSigningKeyCannotVerify ==> SignMessage"); string signature = SignMessage( signing_key, message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 160)); // Signing key should not work. AuthorizationSet out_params; ALOGD("3. HmacSigningKeyCannotVerify ==> Begin, expected INCOMPATIBLE PURPOSE"); EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, Begin(KeyPurpose::VERIFY, signing_key, AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &out_params, &op_handle_)); // Verification key should work. ALOGD("4. HmacSigningKeyCannotVerify ==> VerifyMessage"); ALOGD("signature: %s, size: %zu", signature.c_str(), signature.size()); VerifyMessage(verification_key, message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)); ALOGD("5. HmacSigningKeyCannotVerify ==> CheckedDeleteKey"); CheckedDeleteKey(&signing_key); ALOGD("6. HmacSigningKeyCannotVerify ==> CheckedDeleteKey"); CheckedDeleteKey(&verification_key); }
hardware/interfaces/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
string KeymasterHidlTest::ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation, const string& message, const AuthorizationSet& in_params, AuthorizationSet* out_params) { SCOPED_TRACE("ProcessMessage"); AuthorizationSet begin_out_params; ALOGD("ProcessMessage ==> Begin"); EXPECT_EQ(ErrorCode::OK, Begin(operation, key_blob, in_params, &begin_out_params, &op_handle_)); string output; size_t consumed = 0; AuthorizationSet update_params; AuthorizationSet update_out_params; ALOGD("ProcessMessage ==> Update"); EXPECT_EQ(ErrorCode::OK, Update(op_handle_, update_params, message, &update_out_params, &output, &consumed)); string unused; AuthorizationSet finish_params; AuthorizationSet finish_out_params; ALOGD("ProcessMessage ==> Finish"); EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message.substr(consumed), unused, &finish_out_params, &output)); op_handle_ = kOpHandleSentinel; out_params->push_back(begin_out_params); out_params->push_back(finish_out_params); return output; } string KeymasterHidlTest::SignMessage(const HidlBuf& key_blob, const string& message, const AuthorizationSet& params) { SCOPED_TRACE("SignMessage"); AuthorizationSet out_params; ALOGD("SignMessage ==> ProcessMessage"); string signature = ProcessMessage(key_blob, KeyPurpose::SIGN, message, params, &out_params); EXPECT_TRUE(out_params.empty()); return signature; }
hardware/interfaces/keymaster/4.0/vts/functional/KeymasterHidlTest.cpp
void KeymasterHidlTest::VerifyMessage(const HidlBuf& key_blob, const string& message, const string& signature, const AuthorizationSet& params) { SCOPED_TRACE("VerifyMessage"); AuthorizationSet begin_out_params; ALOGD("1. VerifyMessage ==> Begin, expected OK"); ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::VERIFY, key_blob, params, &begin_out_params, &op_handle_)); string output; AuthorizationSet update_params; AuthorizationSet update_out_params; size_t consumed; ALOGD("2. VerifyMessage ==> Update, expected OK"); ASSERT_EQ(ErrorCode::OK, Update(op_handle_, update_params, message, &update_out_params, &output, &consumed)); EXPECT_TRUE(output.empty()); EXPECT_GT(consumed, 0U); string unused; AuthorizationSet finish_params; AuthorizationSet finish_out_params; ALOGD("3. VerifyMessage ==> Finish, expected OK"); ALOGD("consumed: %zu", consumed); ALOGD("message: %s", message.c_str()); ALOGD("substr: %s, size = %zu", message.substr(consumed).c_str(), message.substr(consumed).size()); ALOGD("signature: %s, size = %zu", signature.c_str(), signature.size()); EXPECT_EQ(ErrorCode::OK, Finish(op_handle_, finish_params, message.substr(consumed), signature, &finish_out_params, &output));//Fail在此操作处 op_handle_ = kOpHandleSentinel; ALOGD("4. VerifyMessage expected output empty"); EXPECT_TRUE(output.empty()); }
Fail原因:
从代码来看,Fail处大致是一个先签名再验证的过程,所以log中能看到两个Begin-Update-Finish的流程。
第一个Begin-Update-Finish是签名,第二个Begin-Update-Finish为验证。Fail在验证流程的Finish中。
HMAC-SHA256输出长度为32字节,但因为限制了mac_length为160即20字节,所以signature为20字节。这20个字节的signature作为验证流程的输入。
在TA侧,验证时先计算mac值,因为使用了HMAC-SHA256算法,则mac值长度为32字节,和输入的signature 20字节不匹配,被判定异常。
看下TA侧的log:
00058 D/TA: TA_InvokeCommandEntryPoint:2235 KM_IMPORT_KEY
00177 D/TA: TA_InvokeCommandEntryPoint:2235 KM_IMPORT_KEY
00296 D/TA: TA_InvokeCommandEntryPoint:2256 KM_BEGIN
00443 D/TA: TA_InvokeCommandEntryPoint:2259 KM_UPDATE
00539 D/TA: TA_InvokeCommandEntryPoint:2262 KM_FINISH
00646 D/TA: TA_InvokeCommandEntryPoint:2256 KM_BEGIN
00754 D/TA: TA_InvokeCommandEntryPoint:2256 KM_BEGIN
00902 D/TA: TA_InvokeCommandEntryPoint:2259 KM_UPDATE
01001 D/TA: TA_InvokeCommandEntryPoint:2262 KM_FINISH
01101 E/TA: TEE_MACCompareFinal:1289 computed_mac_size = 32, macLen = 20 //这个是在libutee中添加的log
01102 D/TA: TA_finish:1517 TEE_MACCompareFinal res = 0xffff3071
01103 E/TA: TA_finish:1524 Finish operation failed with error code ffffffe2
01104 D/TA: TA_serialize_rsp_err:476 res: -30
在SignMessage中调用的Finish操作中,TA中对HMAC-SHA256输出的32字节做了截短:
default: /* HMAC */ if (operation.purpose == KM_PURPOSE_SIGN) { TEE_MACComputeFinal(*operation.operation, input.data, input.data_length, output.data, &out_size); /*Trim out size to KM_TAG_MAC_LENGTH*/ if (operation.mac_length != UNDEFINED) {//mac_length=160,out_size为32字节,做截短处理,所以用例中的signature长度为20字节 if (out_size > operation.mac_length / 8) { DMSG("Trim HMAC out size to %d", operation.mac_length); out_size = operation.mac_length / 8; } }
而在VerifyMessage中调用Finish操作时,TA中用了TEE_MACCompareFinal来实现KM_PURPOSE_VERIFY功能,看下这个函数的实现:
TEE_Result TEE_MACCompareFinal(TEE_OperationHandle operation, const void *message, uint32_t messageLen, const void *mac, uint32_t macLen) { TEE_Result res; uint8_t computed_mac[TEE_MAX_HASH_SIZE]; uint32_t computed_mac_size = TEE_MAX_HASH_SIZE; if (operation->info.operationClass != TEE_OPERATION_MAC) { res = TEE_ERROR_BAD_PARAMETERS; goto out; } if ((operation->info.handleState & TEE_HANDLE_FLAG_INITIALIZED) == 0) { res = TEE_ERROR_BAD_PARAMETERS; goto out; } if (operation->operationState != TEE_OPERATION_STATE_ACTIVE) { res = TEE_ERROR_BAD_PARAMETERS; goto out; } res = TEE_MACComputeFinal(operation, message, messageLen, computed_mac, &computed_mac_size); if (res != TEE_SUCCESS) { EMSG("TEE_MACComputeFinal failed, res = 0x%x", res); goto out; } if (computed_mac_size != macLen) {//如果计算出来的mac size和输入的mac len不一致,则返回错误,这里HMAC-SHA256计算出来的mac size为32,而入参macLen为20,被判定为异常,所以校验失败 EMSG("computed_mac_size = %u, macLen = %u", computed_mac_size, macLen); res = TEE_ERROR_MAC_INVALID; goto out; } if (consttime_memcmp(mac, computed_mac, computed_mac_size) != 0) { EMSG("compare failed"); res = TEE_ERROR_MAC_INVALID; goto out; } operation->operationState = TEE_OPERATION_STATE_INITIAL; out: if (res != TEE_SUCCESS && res != TEE_ERROR_MAC_INVALID) TEE_Panic(res); return res; }
修复思路:
针对KM_PURPOSE_VERIFY,需要考虑有传入mac-length的情况,所以不直接调用TEE_MACCompareFinal,而是先通过TEE_MACComputeFinal计算出hash,然后再根据mac_length值,进行截短比较。
TA_finish函数:
default: /* HMAC */ if (operation.purpose == KM_PURPOSE_SIGN) { TEE_MACComputeFinal(*operation.operation, input.data, input.data_length, output.data, &out_size); /*Trim out size to KM_TAG_MAC_LENGTH*/ if (operation.mac_length != UNDEFINED) { if (out_size > operation.mac_length / 8) { DMSG("Trim HMAC out size to %d", operation.mac_length); out_size = operation.mac_length / 8; } } } else {/* KM_PURPOSE_VERIFY */ uint8_t computed_mac[TEE_MAX_HASH_SIZE]; uint32_t computed_mac_size = TEE_MAX_HASH_SIZE; res = TEE_MACComputeFinal(*operation.operation, input.data, input.data_length, computed_mac, &computed_mac_size); if (res == TEE_SUCCESS && operation.mac_length != UNDEFINED) { if (computed_mac_size > operation.mac_length / 8) { if (TEE_MemCompare(signature.data, computed_mac, signature.data_length) != 0) { res = TEE_ERROR_MAC_INVALID; } } } //HMAC-SHA256, mac_length = 160, signature length is 20 //TEE_MACCompareFinal will fail. //Final operation's output is 32 bytes, it doesn't equals 20 //So Compare fails directly // res = TEE_MACCompareFinal(*operation.operation, // input.data, // input.data_length, // signature.data, // signature.data_length); out_size = 0; /* Convert error code to Android style */ if (res == (int) TEE_ERROR_MAC_INVALID) { res = KM_ERROR_VERIFICATION_FAILED; } }
测试结果:
./VtsHalKeymasterV4_0TargetTest --gtest_filter=PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default
Note: Google Test filter = PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from PerInstance/VerificationOperationsTest
[ RUN ] PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default
[ OK ] PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default (5279 ms)
[----------] 1 test from PerInstance/VerificationOperationsTest (5279 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (5280 ms total)
[ PASSED ] 1 test.