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;
            }
        }
View Code

 

测试结果:

./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.

posted @ 2023-01-12 11:25  xiululu  阅读(162)  评论(0编辑  收藏  举报