/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <android/hardware/keymaster/4.1/IKeymasterDevice.h>

#include <android-base/properties.h>

#include <KeymasterHidlTest.h>
#include <keymasterV4_1/authorization_set.h>

namespace android::hardware::keymaster::V4_1::test {

using V4_0::test::HidlBuf;

class Keymaster4_1HidlTest : public V4_0::test::KeymasterHidlTest {
  public:
    using super = V4_0::test::KeymasterHidlTest;

    ErrorCode convert(V4_0::ErrorCode error_code) { return static_cast<ErrorCode>(error_code); }

    // These methods hide the base class versions.
    void SetUp();
    IKeymasterDevice& keymaster() { return *keymaster41_; };

    struct KeyData {
        HidlBuf blob;
        KeyCharacteristics characteristics;
    };

    std::tuple<ErrorCode, KeyData> GenerateKeyData(const AuthorizationSet& keyDescription) {
        KeyData keyData;
        ErrorCode errorCode = convert(
                super::GenerateKey(keyDescription, &keyData.blob, &keyData.characteristics));
        return {errorCode, keyData};
    }

    void CheckedDeleteKeyData(KeyData* keyData) { CheckedDeleteKey(&keyData->blob); }

    template <typename TagType>
    std::tuple<KeyData /* aesKey */, KeyData /* hmacKey */, KeyData /* rsaKey */,
               KeyData /* ecdsaKey */>
    CreateTestKeys(TagType tagToTest, ErrorCode expectedReturn) {
        ErrorCode errorCode;

        /* AES */
        KeyData aesKeyData;
        std::tie(errorCode, aesKeyData) =
                GenerateKeyData(AuthorizationSetBuilder()
                                        .AesEncryptionKey(128)
                                        .Authorization(tagToTest)
                                        .BlockMode(BlockMode::ECB)
                                        .Padding(PaddingMode::NONE)
                                        .Authorization(TAG_NO_AUTH_REQUIRED));
        EXPECT_EQ(expectedReturn, errorCode);

        /* HMAC */
        KeyData hmacKeyData;
        std::tie(errorCode, hmacKeyData) =
                GenerateKeyData(AuthorizationSetBuilder()
                                        .HmacKey(128)
                                        .Authorization(tagToTest)
                                        .Digest(Digest::SHA_2_256)
                                        .Authorization(TAG_MIN_MAC_LENGTH, 128)
                                        .Authorization(TAG_NO_AUTH_REQUIRED));
        EXPECT_EQ(expectedReturn, errorCode);

        /* RSA */
        KeyData rsaKeyData;
        std::tie(errorCode, rsaKeyData) =
                GenerateKeyData(AuthorizationSetBuilder()
                                        .RsaSigningKey(2048, 65537)
                                        .Authorization(tagToTest)
                                        .Digest(Digest::NONE)
                                        .Padding(PaddingMode::NONE)
                                        .Authorization(TAG_NO_AUTH_REQUIRED));
        EXPECT_EQ(expectedReturn, errorCode);

        /* ECDSA */
        KeyData ecdsaKeyData;
        std::tie(errorCode, ecdsaKeyData) =
                GenerateKeyData(AuthorizationSetBuilder()
                                        .EcdsaSigningKey(256)
                                        .Authorization(tagToTest)
                                        .Digest(Digest::SHA_2_256)
                                        .Authorization(TAG_NO_AUTH_REQUIRED));
        EXPECT_EQ(expectedReturn, errorCode);

        return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData};
    }

    std::tuple<ErrorCode, std::string /* processedMessage */, AuthorizationSet /* out_params */>
    ProcessMessage(const HidlBuf& key_blob, KeyPurpose operation, const std::string& message,
                   const AuthorizationSet& in_params);

    ErrorCode UseAesKey(const HidlBuf& aesKeyBlob) {
        auto [result, ciphertext, out_params] = ProcessMessage(
                aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456",
                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE));
        return result;
    }

    ErrorCode UseHmacKey(const HidlBuf& hmacKeyBlob) {
        auto [result, mac, out_params] =
                ProcessMessage(hmacKeyBlob, KeyPurpose::SIGN, "1234567890123456",
                               AuthorizationSetBuilder()
                                       .Authorization(TAG_MAC_LENGTH, 128)
                                       .Digest(Digest::SHA_2_256));
        return result;
    }

    ErrorCode UseRsaKey(const HidlBuf& rsaKeyBlob) {
        std::string message(2048 / 8, 'a');
        auto [result, signature, out_params] = ProcessMessage(
                rsaKeyBlob, KeyPurpose::SIGN, message,
                AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
        return result;
    }

    ErrorCode UseEcdsaKey(const HidlBuf& ecdsaKeyBlob) {
        auto [result, signature, out_params] =
                ProcessMessage(ecdsaKeyBlob, KeyPurpose::SIGN, "a",
                               AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
        return result;
    }

    static std::vector<std::string> build_params() {
        auto params = android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor);
        return params;
    }

  private:
    sp<IKeymasterDevice> keymaster41_;
};

template <typename TypedTag>
bool contains(hidl_vec<KeyParameter>& set, TypedTag typedTag) {
    return std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
               return param.tag == static_cast<V4_0::Tag>(typedTag);
           }) != set.end();
}

#define INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(name)                                     \
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);                              \
    INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                       \
                             testing::ValuesIn(Keymaster4_1HidlTest::build_params()), \
                             android::hardware::PrintInstanceNameToString)

}  // namespace android::hardware::keymaster::V4_1::test

namespace android::hardware::keymaster::V4_0::test {

// If the given property is available, add it to the tag set under the given tag ID.
template <Tag tag>
void add_tag_from_prop(AuthorizationSetBuilder* tags, TypedTag<TagType::BYTES, tag> ttag,
                       const char* prop) {
    std::string prop_value = ::android::base::GetProperty(prop, /* default= */ "");
    if (!prop_value.empty()) {
        tags->Authorization(ttag, prop_value.data(), prop_value.size());
    }
}

}  // namespace android::hardware::keymaster::V4_0::test
