/*
 * Copyright (c) Atmosphère-NX
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stratosphere.hpp>
#include "spl_secure_monitor_manager.hpp"

namespace ams::spl {

    void SecureMonitorManager::Initialize() {
        return impl::Initialize();
    }

    Result SecureMonitorManager::ModularExponentiate(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size) {
        R_RETURN(impl::ModularExponentiate(out, out_size, base, base_size, exp, exp_size, mod, mod_size));
    }

    Result SecureMonitorManager::GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option) {
        R_RETURN(impl::GenerateAesKek(out_access_key, key_source, generation, option));
    }

    Result SecureMonitorManager::LoadAesKey(s32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) {
        R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner));
        R_RETURN(impl::LoadAesKey(keyslot, access_key, key_source));
    }

    Result SecureMonitorManager::GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) {
        R_RETURN(impl::GenerateAesKey(out_key, access_key, key_source));
    }

    Result SecureMonitorManager::DecryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) {
        R_RETURN(impl::DecryptDeviceUniqueData(dst, dst_size, src, src_size, access_key, key_source, option));
    }

    Result SecureMonitorManager::ReencryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) {
        R_RETURN(impl::ReencryptDeviceUniqueData(dst, dst_size, src, src_size, access_key_dec, source_dec, access_key_enc, source_enc, option));
    }

    Result SecureMonitorManager::GetConfig(u64 *out, spl::ConfigItem key) {
        R_RETURN(impl::GetConfig(out, key));
    }

    Result SecureMonitorManager::SetConfig(spl::ConfigItem key, u64 value) {
        R_RETURN(impl::SetConfig(key, value));
    }

    Result SecureMonitorManager::GetPackage2Hash(void *dst, const size_t size) {
        R_RETURN(impl::GetPackage2Hash(dst, size));
    }

    Result SecureMonitorManager::GenerateRandomBytes(void *out, size_t size) {
        R_RETURN(impl::GenerateRandomBytes(out, size));
    }

    Result SecureMonitorManager::DecryptAndStoreGcKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) {
        R_RETURN(impl::DecryptAndStoreGcKey(src, src_size, access_key, key_source, option));
    }

    Result SecureMonitorManager::DecryptGcMessage(u32 *out_size, void *dst, size_t dst_size, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size) {
        R_RETURN(impl::DecryptGcMessage(out_size, dst, dst_size, base, base_size, mod, mod_size, label_digest, label_digest_size));
    }

    Result SecureMonitorManager::DecryptAndStoreSslClientCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) {
        R_RETURN(impl::DecryptAndStoreSslClientCertKey(src, src_size, access_key, key_source));
    }

    Result SecureMonitorManager::ModularExponentiateWithSslClientCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) {
        R_RETURN(impl::ModularExponentiateWithSslClientCertKey(out, out_size, base, base_size, mod, mod_size));
    }

    Result SecureMonitorManager::DecryptAndStoreDrmDeviceCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) {
        R_RETURN(impl::DecryptAndStoreDrmDeviceCertKey(src, src_size, access_key, key_source));
    }

    Result SecureMonitorManager::ModularExponentiateWithDrmDeviceCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) {
        R_RETURN(impl::ModularExponentiateWithDrmDeviceCertKey(out, out_size, base, base_size, mod, mod_size));
    }

    Result SecureMonitorManager::IsDevelopment(bool *out) {
        R_RETURN(impl::IsDevelopment(out));
    }

    Result SecureMonitorManager::GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which) {
        R_RETURN(impl::GenerateSpecificAesKey(out_key, key_source, generation, which));
    }

    Result SecureMonitorManager::DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) {
        R_RETURN(impl::DecryptAesKey(out_key, key_source, generation, option));
    }

    Result SecureMonitorManager::ComputeCtr(void *dst, size_t dst_size, s32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) {
        R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner));
        R_RETURN(impl::ComputeCtr(dst, dst_size, keyslot, src, src_size, iv_ctr));
    }

    Result SecureMonitorManager::ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *owner, const void *data, size_t size) {
        R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner));
        R_RETURN(impl::ComputeCmac(out_cmac, keyslot, data, size));
    }

    Result SecureMonitorManager::LoadEsDeviceKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) {
        R_RETURN(impl::LoadEsDeviceKey(src, src_size, access_key, key_source, option));
    }

    Result SecureMonitorManager::PrepareEsTitleKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation) {
        R_RETURN(impl::PrepareEsTitleKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation));
    }

    Result SecureMonitorManager::PrepareEsArchiveKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation) {
        R_RETURN(impl::PrepareEsArchiveKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation));
    }

    Result SecureMonitorManager::PrepareCommonEsTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation) {
        R_RETURN(impl::PrepareCommonEsTitleKey(out_access_key, key_source, generation));
    }

    Result SecureMonitorManager::LoadPreparedAesKey(s32 keyslot, const void *owner, const AccessKey &access_key) {
        R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner));
        R_RETURN(impl::LoadPreparedAesKey(keyslot, access_key));
    }

    Result SecureMonitorManager::AllocateAesKeySlot(s32 *out_keyslot, const void *owner) {
        /* Allocate a new virtual keyslot. */
        s32 keyslot;
        R_TRY(impl::AllocateAesKeySlot(std::addressof(keyslot)));

        /* Get the keyslot's index. */
        s32 index;
        bool virt;
        R_ABORT_UNLESS(impl::TestAesKeySlot(std::addressof(index), std::addressof(virt), keyslot));

        /* All allocated keyslots must be virtual. */
        AMS_ABORT_UNLESS(virt);

        m_aes_keyslot_owners[index] = owner;
        *out_keyslot                = keyslot;
        R_SUCCEED();
    }

    Result SecureMonitorManager::DeallocateAesKeySlot(s32 keyslot, const void *owner) {
        s32 index;
        R_TRY(this->TestAesKeySlot(std::addressof(index), keyslot, owner));

        m_aes_keyslot_owners[index] = nullptr;
        R_RETURN(impl::DeallocateAesKeySlot(keyslot));
    }

    void SecureMonitorManager::DeallocateAesKeySlots(const void *owner) {
        for (auto i = 0; i < impl::AesKeySlotCount; ++i) {
            if (m_aes_keyslot_owners[i] == owner) {
                m_aes_keyslot_owners[i] = nullptr;
                impl::DeallocateAesKeySlot(impl::AesKeySlotMin + i);
            }
        }
    }

    Result SecureMonitorManager::SetBootReason(BootReasonValue boot_reason) {
        R_RETURN(impl::SetBootReason(boot_reason));
    }

    Result SecureMonitorManager::GetBootReason(BootReasonValue *out) {
        R_RETURN(impl::GetBootReason(out));
    }

    os::SystemEvent *SecureMonitorManager::GetAesKeySlotAvailableEvent() {
        return impl::GetAesKeySlotAvailableEvent();
    }

    Result SecureMonitorManager::TestAesKeySlot(s32 *out_index, s32 keyslot, const void *owner) {
        /* Validate the keyslot (and get the index). */
        s32 index;
        bool virt;
        R_TRY(impl::TestAesKeySlot(std::addressof(index), std::addressof(virt), keyslot));

        /* Check that the keyslot is physical (for legacy compat) or owned by the request maker. */
        R_UNLESS(!virt || m_aes_keyslot_owners[index] == owner, spl::ResultInvalidKeySlot());

        /* Set output index. */
        if (out_index != nullptr) {
            *out_index = index;
        }
        R_SUCCEED();
    }


}
