Optee KM4.0 VTSテスト: VerificationOperationsTest.HmacSigningKeyCannotVerifyの失敗

VTSテストの結果として、複数のテストケースが失敗しました。特に、VerificationOperationsTest.HmacSigningKeyCannotVerifyというテストケースがエラーを発生しています。

失敗したテストケース:

./VtsHalKeymasterV4_0TargetTest --gtest_filter=PerInstance/VerificationOperationsTest.HmacSigningKeyCannotVerify/0_default

テストログから明らかなように、HMAC署名鍵が検証に使用されることを確認するテストが失敗しています。エラーメッセージによると、Finish操作時にVERIFICATION_FAILEDが返されています。

テストケースコード:

hardware/interfaces/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp

/*
 * VerificationOperationsTest.HmacSigningKeyCannotVerify
 *
 * HMAC署名と検証を確認し、署名鍵が検証に使用できないことをテストする。
 */
TEST_P(VerificationOperationsTest, HmacSigningKeyCannotVerify) {
    string key_data = "HelloThisIsAKey";

    HidlBuf sign_key, verify_key;
    KeyCharacteristics sign_key_info, verify_key_info;
    ALOGD("1. HmacSigningKeyCannotVerify ==> キーインポート");
    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_data, &sign_key, &sign_key_info));

    ALOGD("2. HmacSigningKeyCannotVerify ==> キーインポート");
    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_data, &verify_key, &verify_key_info));

    string message = "This is a message.";
    ALOGD("2-3. HmacSigningKeyCannotVerify ==> 署名");
    string signature = SignMessage(
        sign_key, message,
        AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 160));

    // 署名鍵は検証に使用できない
    AuthorizationSet out_params;
    ALOGD("3. HmacSigningKeyCannotVerify ==> Begin, expected INCOMPATIBLE PURPOSE");
    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
              Begin(KeyPurpose::VERIFY, sign_key, AuthorizationSetBuilder().Digest(Digest::SHA_2_256),
                    &out_params, &op_handle_));

    // 検証鍵は使用可能
    ALOGD("4. HmacSigningKeyCannotVerify ==> 検証");
    ALOGD("signature: %s, size: %zu", signature.c_str(), signature.size());
    VerifyMessage(verify_key, message, signature,
                  AuthorizationSetBuilder().Digest(Digest::SHA_2_256));

    ALOGD("5. HmacSigningKeyCannotVerify ==> キー削除");
    CheckedDeleteKey(&sign_key);

    ALOGD("6. HmacSigningKeyCannotVerify ==> キー削除");
    CheckedDeleteKey(&verify_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());
}

失敗原因:

テストケースでは、HMAC-SHA256アルゴリズムを使用して署名と検証を行います。しかし、署名時に指定されたMAC長(160ビット=20バイト)に対して、検証時に32バイトのHMAC出力を比較しようとしているため、一致しないエラーが発生します。

TA側のログ: 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 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

署名処理では、HMAC-SHA256の出力サイズを160ビット(20バイト)に切り捨てていますが、検証処理では32バイトのHMAC出力と20バイトの署名を比較しているため、マッチングに失敗します。

修正方法:

検証処理では、TEE_MACComputeFinalを使用してHMACを計算し、MAC長に応じて切り捨てた後、署名と比較することで解決します。

修正後のTA側コード:

    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;
                    }
                }
            }
            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 テストが成功しました。

タグ: Keymaster HMAC TEE VTS

6月6日 20:41 投稿