mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-15 01:26:34 +00:00
libs: begin adding capacity for doing crypto on generic os (using externally-preset keys)
This commit is contained in:
parent
453e70bc6f
commit
a4895a1e79
28 changed files with 1305 additions and 33 deletions
|
@ -125,7 +125,7 @@ namespace ams::fssystem {
|
||||||
size_t GetBytesFromOrder(s32 order) const {
|
size_t GetBytesFromOrder(s32 order) const {
|
||||||
AMS_ASSERT(m_free_lists != nullptr);
|
AMS_ASSERT(m_free_lists != nullptr);
|
||||||
AMS_ASSERT(0 <= order);
|
AMS_ASSERT(0 <= order);
|
||||||
AMS_ASSERT(order < this->GetOrderMax());
|
AMS_ASSERT(order <= this->GetOrderMax());
|
||||||
return (this->GetBlockSize() << order);
|
return (this->GetBlockSize() << order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,11 @@ namespace ams::fssystem {
|
||||||
class NcaFileSystemDriver : public ::ams::fs::impl::Newable {
|
class NcaFileSystemDriver : public ::ams::fs::impl::Newable {
|
||||||
NON_COPYABLE(NcaFileSystemDriver);
|
NON_COPYABLE(NcaFileSystemDriver);
|
||||||
NON_MOVEABLE(NcaFileSystemDriver);
|
NON_MOVEABLE(NcaFileSystemDriver);
|
||||||
|
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||||
private:
|
private:
|
||||||
|
#else
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
struct StorageContext {
|
struct StorageContext {
|
||||||
bool open_raw_storage;
|
bool open_raw_storage;
|
||||||
std::shared_ptr<fs::IStorage> body_substorage;
|
std::shared_ptr<fs::IStorage> body_substorage;
|
||||||
|
@ -209,8 +213,11 @@ namespace ams::fssystem {
|
||||||
std::shared_ptr<fs::IStorage> fs_data_storage;
|
std::shared_ptr<fs::IStorage> fs_data_storage;
|
||||||
std::shared_ptr<fs::IStorage> compressed_storage_meta_storage;
|
std::shared_ptr<fs::IStorage> compressed_storage_meta_storage;
|
||||||
std::shared_ptr<fssystem::CompressedStorage> compressed_storage;
|
std::shared_ptr<fssystem::CompressedStorage> compressed_storage;
|
||||||
};
|
|
||||||
|
|
||||||
|
/* For tools. */
|
||||||
|
std::shared_ptr<fs::IStorage> external_original_storage;
|
||||||
|
};
|
||||||
|
private:
|
||||||
enum AlignmentStorageRequirement {
|
enum AlignmentStorageRequirement {
|
||||||
/* TODO */
|
/* TODO */
|
||||||
AlignmentStorageRequirement_CacheBlockSize = 0,
|
AlignmentStorageRequirement_CacheBlockSize = 0,
|
||||||
|
@ -235,7 +242,15 @@ namespace ams::fssystem {
|
||||||
AMS_ASSERT(m_hash_generator_factory_selector != nullptr);
|
AMS_ASSERT(m_hash_generator_factory_selector != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpenStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index);
|
Result OpenStorageWithContext(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx);
|
||||||
|
|
||||||
|
Result OpenStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index) {
|
||||||
|
/* Create a storage context. */
|
||||||
|
StorageContext ctx{};
|
||||||
|
|
||||||
|
/* Open the storage. */
|
||||||
|
R_RETURN(OpenStorageWithContext(out, out_splitter, out_header_reader, fs_index, std::addressof(ctx)));
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
Result OpenStorageImpl(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx);
|
Result OpenStorageImpl(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace ams::spl::smc {
|
||||||
Result GenerateRandomBytes(void *out, size_t size);
|
Result GenerateRandomBytes(void *out, size_t size);
|
||||||
Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option);
|
Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option);
|
||||||
Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source);
|
Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source);
|
||||||
Result ComputeAes(AsyncOperationKey *out_op, u32 dst_addr, u32 mode, const IvCtr &iv_ctr, u32 src_addr, size_t size);
|
Result ComputeAes(AsyncOperationKey *out_op, u64 dst_addr, u32 mode, const IvCtr &iv_ctr, u64 src_addr, size_t size);
|
||||||
Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which);
|
Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which);
|
||||||
Result ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size);
|
Result ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size);
|
||||||
Result ReencryptDeviceUniqueData(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option);
|
Result ReencryptDeviceUniqueData(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option);
|
||||||
|
@ -68,4 +68,8 @@ namespace ams::spl::smc {
|
||||||
return SetConfig(key, std::addressof(value), 1);
|
return SetConfig(key, std::addressof(value), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(ATMOSPHERE_OS_HORIZON)
|
||||||
|
void PresetInternalKey(const AesKey *key, u32 generation, bool device);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,8 +146,11 @@ hos_stratosphere_api.o: CXXFLAGS += -fno-lto
|
||||||
init_operator_new.o: CXXFLAGS += -fno-lto
|
init_operator_new.o: CXXFLAGS += -fno-lto
|
||||||
init_libnx_shim.os.horizon.o: CXXFLAGS += -fno-lto
|
init_libnx_shim.os.horizon.o: CXXFLAGS += -fno-lto
|
||||||
|
|
||||||
|
spl_secure_monitor_api.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
|
||||||
|
|
||||||
ifeq ($(ATMOSPHERE_OS_NAME),windows)
|
ifeq ($(ATMOSPHERE_OS_NAME),windows)
|
||||||
os_%.o: CXXFLAGS += -fno-lto
|
os_%.o: CXXFLAGS += -fno-lto
|
||||||
|
fssystem_%.o: CXXFLAGS += -fno-lto
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
template<typename BasePointer>
|
template<typename BasePointer>
|
||||||
AesXtsStorage<BasePointer>::AesXtsStorage(BasePointer base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : m_base_storage(std::move(base)), m_block_size(block_size), m_mutex() {
|
AesXtsStorage<BasePointer>::AesXtsStorage(BasePointer base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : m_base_storage(std::move(base)), m_block_size(block_size), m_mutex() {
|
||||||
AMS_ASSERT(base != nullptr);
|
AMS_ASSERT(m_base_storage != nullptr);
|
||||||
AMS_ASSERT(key1 != nullptr);
|
AMS_ASSERT(key1 != nullptr);
|
||||||
AMS_ASSERT(key2 != nullptr);
|
AMS_ASSERT(key2 != nullptr);
|
||||||
AMS_ASSERT(iv != nullptr);
|
AMS_ASSERT(iv != nullptr);
|
||||||
|
|
|
@ -302,16 +302,13 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NcaFileSystemDriver::OpenStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index) {
|
Result NcaFileSystemDriver::OpenStorageWithContext(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx) {
|
||||||
/* Create a storage context. */
|
|
||||||
StorageContext ctx{};
|
|
||||||
|
|
||||||
/* Open storage. */
|
/* Open storage. */
|
||||||
R_TRY(this->OpenStorageImpl(out, out_header_reader, fs_index, std::addressof(ctx)));
|
R_TRY(this->OpenStorageImpl(out, out_header_reader, fs_index, ctx));
|
||||||
|
|
||||||
/* If we have a compressed storage, use it as splitter. */
|
/* If we have a compressed storage, use it as splitter. */
|
||||||
if (ctx.compressed_storage != nullptr) {
|
if (ctx->compressed_storage != nullptr) {
|
||||||
*out_splitter = std::move(ctx.compressed_storage);
|
*out_splitter = std::move(ctx->compressed_storage);
|
||||||
} else {
|
} else {
|
||||||
/* Otherwise, allocate a default splitter. */
|
/* Otherwise, allocate a default splitter. */
|
||||||
*out_splitter = fssystem::AllocateShared<DefaultAsynchronousAccessSplitter>();
|
*out_splitter = fssystem::AllocateShared<DefaultAsynchronousAccessSplitter>();
|
||||||
|
@ -427,6 +424,9 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
/* Open original indirectable storage. */
|
/* Open original indirectable storage. */
|
||||||
R_TRY(original_driver.OpenIndirectableStorageAsOriginal(std::addressof(original_indirectable_storage), std::addressof(original_header_reader), ctx));
|
R_TRY(original_driver.OpenIndirectableStorageAsOriginal(std::addressof(original_indirectable_storage), std::addressof(original_header_reader), ctx));
|
||||||
|
} else if (ctx != nullptr && ctx->external_original_storage != nullptr) {
|
||||||
|
/* Use the external original storage. */
|
||||||
|
original_indirectable_storage = ctx->external_original_storage;
|
||||||
} else {
|
} else {
|
||||||
/* Allocate a dummy memory storage as original storage. */
|
/* Allocate a dummy memory storage as original storage. */
|
||||||
original_indirectable_storage = fssystem::AllocateShared<fs::MemoryStorage>(nullptr, 0);
|
original_indirectable_storage = fssystem::AllocateShared<fs::MemoryStorage>(nullptr, 0);
|
||||||
|
|
|
@ -229,8 +229,7 @@ namespace ams::spl::impl {
|
||||||
|
|
||||||
os::InitializeInterruptEvent(std::addressof(g_interrupt), g_interrupt_name, os::EventClearMode_AutoClear);
|
os::InitializeInterruptEvent(std::addressof(g_interrupt), g_interrupt_name, os::EventClearMode_AutoClear);
|
||||||
#else
|
#else
|
||||||
AMS_UNUSED(g_interrupt_name);
|
AMS_UNUSED(g_interrupt_name, g_interrupt);
|
||||||
AMS_ABORT("TODO: How should this work?");
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +276,9 @@ namespace ams::spl::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitOperation() {
|
void WaitOperation() {
|
||||||
|
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||||
os::WaitInterruptEvent(std::addressof(g_interrupt));
|
os::WaitInterruptEvent(std::addressof(g_interrupt));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
smc::Result WaitAndGetResult(smc::AsyncOperationKey op_key) {
|
smc::Result WaitAndGetResult(smc::AsyncOperationKey op_key) {
|
||||||
|
@ -310,6 +311,7 @@ namespace ams::spl::impl {
|
||||||
u8 out_buffer[crypto::AesEncryptor128::BlockSize];
|
u8 out_buffer[crypto::AesEncryptor128::BlockSize];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||||
auto &layout = *reinterpret_cast<DecryptAesLayout *>(g_work_buffer);
|
auto &layout = *reinterpret_cast<DecryptAesLayout *>(g_work_buffer);
|
||||||
|
|
||||||
layout.crypt_ctx.in.num_entries = 0;
|
layout.crypt_ctx.in.num_entries = 0;
|
||||||
|
@ -342,8 +344,33 @@ namespace ams::spl::impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
os::FlushDataCache(std::addressof(layout.out_buffer), sizeof(layout.out_buffer));
|
os::FlushDataCache(std::addressof(layout.out_buffer), sizeof(layout.out_buffer));
|
||||||
|
|
||||||
std::memcpy(dst, layout.out_buffer, sizeof(layout.out_buffer));
|
std::memcpy(dst, layout.out_buffer, sizeof(layout.out_buffer));
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
/* Set up buffers. */
|
||||||
|
u8 in_buffer[crypto::AesEncryptor128::BlockSize];
|
||||||
|
u8 out_buffer[crypto::AesEncryptor128::BlockSize];
|
||||||
|
std::memcpy(in_buffer, src, sizeof(in_buffer));
|
||||||
|
|
||||||
|
std::scoped_lock lk(g_operation_lock);
|
||||||
|
|
||||||
|
/* On generic os, we don't worry about the security engine. */
|
||||||
|
smc::AsyncOperationKey op_key;
|
||||||
|
const IvCtr iv_ctr = {};
|
||||||
|
const u32 mode = smc::GetComputeAesMode(smc::CipherMode::CbcDecrypt, GetPhysicalAesKeySlot(keyslot, true));
|
||||||
|
smc::Result res = smc::ComputeAes(std::addressof(op_key), reinterpret_cast<uintptr_t>(out_buffer), mode, iv_ctr, reinterpret_cast<uintptr_t>(in_buffer), sizeof(in_buffer));
|
||||||
|
if (res != smc::Result::Success) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = WaitAndGetResult(op_key);
|
||||||
|
if (res != smc::Result::Success) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(dst, out_buffer, sizeof(out_buffer));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return smc::Result::Success;
|
return smc::Result::Success;
|
||||||
}
|
}
|
||||||
|
@ -642,6 +669,7 @@ namespace ams::spl::impl {
|
||||||
R_UNLESS(src_size <= dst_size, spl::ResultInvalidBufferSize());
|
R_UNLESS(src_size <= dst_size, spl::ResultInvalidBufferSize());
|
||||||
R_UNLESS(util::IsAligned(src_size, AesBlockSize), spl::ResultInvalidBufferSize());
|
R_UNLESS(util::IsAligned(src_size, AesBlockSize), spl::ResultInvalidBufferSize());
|
||||||
|
|
||||||
|
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||||
/* We can only map 4_MB aligned buffers for the SE, so determine where to map our buffers. */
|
/* We can only map 4_MB aligned buffers for the SE, so determine where to map our buffers. */
|
||||||
const uintptr_t src_addr = reinterpret_cast<uintptr_t>(src);
|
const uintptr_t src_addr = reinterpret_cast<uintptr_t>(src);
|
||||||
const uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
|
const uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
|
||||||
|
@ -697,6 +725,25 @@ namespace ams::spl::impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
os::FlushDataCache(dst, dst_size);
|
os::FlushDataCache(dst, dst_size);
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
std::scoped_lock lk(g_operation_lock);
|
||||||
|
|
||||||
|
const u32 mode = smc::GetComputeAesMode(smc::CipherMode::Ctr, GetPhysicalAesKeySlot(keyslot, true));
|
||||||
|
|
||||||
|
/* On generic os, we don't worry about the security engine. */
|
||||||
|
smc::AsyncOperationKey op_key;
|
||||||
|
smc::Result res = smc::ComputeAes(std::addressof(op_key), reinterpret_cast<uintptr_t>(dst), mode, iv_ctr, reinterpret_cast<uintptr_t>(src), src_size);
|
||||||
|
if (res != smc::Result::Success) {
|
||||||
|
return smc::ConvertResult(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = WaitAndGetResult(op_key);
|
||||||
|
if (res != smc::Result::Success) {
|
||||||
|
return smc::ConvertResult(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,499 @@
|
||||||
|
/*
|
||||||
|
* 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 <exosphere/pkg1.hpp>
|
||||||
|
|
||||||
|
namespace ams::spl::smc {
|
||||||
|
|
||||||
|
#define SMC_R_SUCCEEEDED(res) (res == smc::Result::Success)
|
||||||
|
#define SMC_R_FAILED(res) (res != smc::Result::Success)
|
||||||
|
|
||||||
|
#define SMC_R_TRY(res_expr) ({ const auto _tmp_r_try_rc = (res_expr); if (SMC_R_FAILED(_tmp_r_try_rc)) { return _tmp_r_try_rc; } })
|
||||||
|
#define SMC_R_UNLESS(cond, RES) ({ if (!(cond)) { return smc::Result::RES; }})
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum SealKey {
|
||||||
|
SealKey_LoadAesKey = 0,
|
||||||
|
SealKey_DecryptDeviceUniqueData = 1,
|
||||||
|
SealKey_ImportLotusKey = 2,
|
||||||
|
SealKey_ImportEsDeviceKey = 3,
|
||||||
|
SealKey_ReencryptDeviceUniqueData = 4,
|
||||||
|
SealKey_ImportSslKey = 5,
|
||||||
|
SealKey_ImportEsClientCertKey = 6,
|
||||||
|
|
||||||
|
SealKey_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KeyType {
|
||||||
|
KeyType_Default = 0,
|
||||||
|
KeyType_NormalOnly = 1,
|
||||||
|
KeyType_RecoveryOnly = 2,
|
||||||
|
KeyType_NormalAndRecovery = 3,
|
||||||
|
|
||||||
|
KeyType_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GenerateAesKekOption {
|
||||||
|
using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>;
|
||||||
|
using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>;
|
||||||
|
using SealKeyIndex = util::BitPack32::Field<5, 3, SealKey>;
|
||||||
|
using Reserved = util::BitPack32::Field<8, 24, u32>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ComputeAesOption {
|
||||||
|
using KeySlot = util::BitPack32::Field<0, 3, int>;
|
||||||
|
using CipherModeIndex = util::BitPack32::Field<4, 2, CipherMode>;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 KeyTypeSources[KeyType_Count][crypto::AesEncryptor128::KeySize] = {
|
||||||
|
[KeyType_Default] = { 0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9 },
|
||||||
|
[KeyType_NormalOnly] = { 0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77 },
|
||||||
|
[KeyType_RecoveryOnly] = { 0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81 },
|
||||||
|
[KeyType_NormalAndRecovery] = { 0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 SealKeyMasks[SealKey_Count][crypto::AesEncryptor128::KeySize] = {
|
||||||
|
[SealKey_LoadAesKey] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||||
|
[SealKey_DecryptDeviceUniqueData] = { 0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74 },
|
||||||
|
[SealKey_ImportLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C },
|
||||||
|
[SealKey_ImportEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB },
|
||||||
|
[SealKey_ReencryptDeviceUniqueData] = { 0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31 },
|
||||||
|
[SealKey_ImportSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 },
|
||||||
|
[SealKey_ImportEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u64 InvalidAsyncKey = 0;
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_crypto_lock;
|
||||||
|
constinit u64 g_async_key = InvalidAsyncKey;
|
||||||
|
constinit u8 g_async_result_buffer[1_KB];
|
||||||
|
|
||||||
|
u64 GenerateRandomU64() {
|
||||||
|
/* TODO: Can/should we make this cryptographically secure? */
|
||||||
|
u64 v = -1;
|
||||||
|
os::GenerateRandomBytes(std::addressof(v), sizeof(v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
constinit u8 g_master_keys[pkg1::KeyGeneration_Max][crypto::AesEncryptor128::KeySize]{};
|
||||||
|
constinit u8 g_device_keys[pkg1::KeyGeneration_Max][crypto::AesEncryptor128::KeySize]{};
|
||||||
|
|
||||||
|
class KeySlotManager {
|
||||||
|
private:
|
||||||
|
u8 m_key_slot_contents[pkg1::AesKeySlot_Count][crypto::AesEncryptor256::KeySize];
|
||||||
|
public:
|
||||||
|
constexpr KeySlotManager() : m_key_slot_contents{} { /* ... */ }
|
||||||
|
public:
|
||||||
|
const u8 *GetKey(s32 slot) const {
|
||||||
|
return m_key_slot_contents[slot];
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadAesKey(s32 slot, const AccessKey &access_key, const KeySource &key_source) {
|
||||||
|
crypto::AesDecryptor128 aes;
|
||||||
|
aes.Initialize(std::addressof(access_key), sizeof(access_key));
|
||||||
|
aes.DecryptBlock(m_key_slot_contents[slot], crypto::AesEncryptor128::KeySize, std::addressof(key_source), sizeof(key_source));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadPreparedAesKey(s32 slot, const AccessKey &access_key) {
|
||||||
|
this->SetAesKey128(slot, std::addressof(access_key), sizeof(access_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PrepareDeviceMasterKey(s32 generation) {
|
||||||
|
constexpr s32 Slot = pkg1::AesKeySlot_Smc;
|
||||||
|
this->SetAesKey128(Slot, g_device_keys[generation], crypto::AesEncryptor128::KeySize);
|
||||||
|
return Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PrepareMasterKey(s32 generation) {
|
||||||
|
constexpr s32 Slot = pkg1::AesKeySlot_Smc;
|
||||||
|
this->SetAesKey128(Slot, g_master_keys[generation], crypto::AesEncryptor128::KeySize);
|
||||||
|
return Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAesKey128(s32 slot, const void *key, size_t key_size) {
|
||||||
|
std::memcpy(m_key_slot_contents[slot], key, key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetEncryptedAesKey128(s32 dst, s32 src, const void *key_source, size_t key_source_size) {
|
||||||
|
crypto::AesDecryptor128 aes;
|
||||||
|
aes.Initialize(this->GetKey(src), crypto::AesDecryptor128::KeySize);
|
||||||
|
aes.DecryptBlock(m_key_slot_contents[dst], crypto::AesEncryptor128::KeySize, key_source, key_source_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecryptAes128(void *dst, size_t dst_size, s32 slot, const void *src, size_t src_size) {
|
||||||
|
crypto::AesDecryptor128 aes;
|
||||||
|
aes.Initialize(this->GetKey(slot), crypto::AesDecryptor128::KeySize);
|
||||||
|
aes.DecryptBlock(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool IsUserAesKeySlot(s32 slot) {
|
||||||
|
return pkg1::IsUserAesKeySlot(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
constinit KeySlotManager g_key_slot_manager;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PresetInternalKey(const AesKey *key, u32 generation, bool device) {
|
||||||
|
if (device) {
|
||||||
|
std::memcpy(g_device_keys[generation], key, sizeof(*key));
|
||||||
|
} else {
|
||||||
|
std::memcpy(g_master_keys[generation], key, sizeof(*key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Result SetConfig(AsyncOperationKey *out_op, spl::ConfigItem key, const u64 *value, size_t num_qwords, const void *sign) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::SetConfig);
|
||||||
|
// args.r[1] = static_cast<u64>(key);
|
||||||
|
// args.r[2] = reinterpret_cast<u64>(sign);
|
||||||
|
//
|
||||||
|
// for (size_t i = 0; i < std::min(static_cast<size_t>(4), num_qwords); i++) {
|
||||||
|
// args.r[3 + i] = value[i];
|
||||||
|
// }
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// out_op->value = args.r[1];
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem key) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::GetConfig);
|
||||||
|
// args.r[1] = static_cast<u64>(key);
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// for (size_t i = 0; i < std::min(static_cast<size_t>(4), num_qwords); i++) {
|
||||||
|
// out[i] = args.r[1 + i];
|
||||||
|
// }
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
Result GetResult(Result *out, AsyncOperationKey op) {
|
||||||
|
SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation);
|
||||||
|
SMC_R_UNLESS(g_async_key == op.value, InvalidAsyncOperation);
|
||||||
|
|
||||||
|
g_async_key = InvalidAsyncKey;
|
||||||
|
|
||||||
|
*out = smc::Result::Success;
|
||||||
|
return smc::Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetResultData(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op) {
|
||||||
|
SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation);
|
||||||
|
SMC_R_UNLESS(g_async_key == op.value, InvalidAsyncOperation);
|
||||||
|
SMC_R_UNLESS(out_buf_size <= sizeof(g_async_result_buffer), InvalidArgument);
|
||||||
|
|
||||||
|
g_async_key = InvalidAsyncKey;
|
||||||
|
std::memcpy(out_buf, g_async_result_buffer, out_buf_size);
|
||||||
|
|
||||||
|
*out = smc::Result::Success;
|
||||||
|
return smc::Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Result ModularExponentiate(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::ModularExponentiate);
|
||||||
|
// args.r[1] = reinterpret_cast<u64>(base);
|
||||||
|
// args.r[2] = reinterpret_cast<u64>(exp);
|
||||||
|
// args.r[3] = reinterpret_cast<u64>(mod);
|
||||||
|
// args.r[4] = exp_size;
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// out_op->value = args.r[1];
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
Result GenerateRandomBytes(void *out, size_t size) {
|
||||||
|
/* TODO: Cryptographically secure? */
|
||||||
|
os::GenerateRandomBytes(out, size);
|
||||||
|
return smc::Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 user_generation, u32 option_value) {
|
||||||
|
std::scoped_lock lk(g_crypto_lock);
|
||||||
|
|
||||||
|
const int pkg1_generation = std::max<int>(static_cast<int>(user_generation) - 1, pkg1::KeyGeneration_1_0_0);
|
||||||
|
|
||||||
|
const util::BitPack32 option = { option_value };
|
||||||
|
const bool is_device_unique = option.Get<GenerateAesKekOption::IsDeviceUnique>();
|
||||||
|
const auto key_type = option.Get<GenerateAesKekOption::KeyTypeIndex>();
|
||||||
|
const auto seal_key = option.Get<GenerateAesKekOption::SealKeyIndex>();
|
||||||
|
const u32 reserved = option.Get<GenerateAesKekOption::Reserved>();
|
||||||
|
|
||||||
|
/* Validate arguments. */
|
||||||
|
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||||
|
|
||||||
|
if (is_device_unique) {
|
||||||
|
SMC_R_UNLESS(pkg1::IsValidDeviceUniqueKeyGeneration(pkg1_generation), InvalidArgument);
|
||||||
|
} else {
|
||||||
|
SMC_R_UNLESS(pkg1_generation <= pkg1::KeyGeneration_Max, InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
SMC_R_UNLESS(0 <= key_type && key_type < KeyType_Count, InvalidArgument);
|
||||||
|
SMC_R_UNLESS(0 <= seal_key && seal_key < SealKey_Count, InvalidArgument);
|
||||||
|
|
||||||
|
/* Here N might check if key type is normal or recovery only, but we're not going to enforce that. */
|
||||||
|
u8 static_source[crypto::AesEncryptor128::KeySize];
|
||||||
|
|
||||||
|
/* Derive the static source. */
|
||||||
|
for (size_t i = 0; i < sizeof(static_source); ++i) {
|
||||||
|
static_source[i] = KeyTypeSources[key_type][i] ^ SealKeyMasks[seal_key][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the slot. */
|
||||||
|
const int slot = is_device_unique ? g_key_slot_manager.PrepareDeviceMasterKey(pkg1_generation) : g_key_slot_manager.PrepareMasterKey(pkg1_generation);
|
||||||
|
|
||||||
|
/* Derive a static generation kek. */
|
||||||
|
g_key_slot_manager.SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, static_source, sizeof(static_source));
|
||||||
|
|
||||||
|
/* Decrypt the input using the static-derived key. */
|
||||||
|
g_key_slot_manager.DecryptAes128(out, sizeof(*out), pkg1::AesKeySlot_Smc, std::addressof(source), sizeof(source));
|
||||||
|
|
||||||
|
return smc::Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source) {
|
||||||
|
std::scoped_lock lk(g_crypto_lock);
|
||||||
|
|
||||||
|
/* Check args. */
|
||||||
|
SMC_R_UNLESS(IsUserAesKeySlot(keyslot), InvalidArgument);
|
||||||
|
|
||||||
|
/* Unseal the access key. */
|
||||||
|
g_key_slot_manager.SetAesKey128(pkg1::AesKeySlot_Smc, std::addressof(access_key), sizeof(access_key));
|
||||||
|
|
||||||
|
/* Derive the key. */
|
||||||
|
g_key_slot_manager.SetEncryptedAesKey128(keyslot, pkg1::AesKeySlot_Smc, std::addressof(source), sizeof(source));
|
||||||
|
|
||||||
|
return smc::Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ComputeAes(AsyncOperationKey *out_op, u64 dst_addr, u32 mode, const IvCtr &iv_ctr, u64 src_addr, size_t size) {
|
||||||
|
std::scoped_lock lk(g_crypto_lock);
|
||||||
|
|
||||||
|
/* Check size. */
|
||||||
|
SMC_R_UNLESS(util::IsAligned(size, crypto::AesEncryptor128::BlockSize), InvalidArgument);
|
||||||
|
|
||||||
|
const util::BitPack32 option = { mode };
|
||||||
|
const int slot = option.Get<ComputeAesOption::KeySlot>();
|
||||||
|
const auto cipher_mode = option.Get<ComputeAesOption::CipherModeIndex>();
|
||||||
|
SMC_R_UNLESS(IsUserAesKeySlot(slot), InvalidArgument);
|
||||||
|
|
||||||
|
/* Set a random async key. */
|
||||||
|
g_async_key = GenerateRandomU64();
|
||||||
|
|
||||||
|
switch (cipher_mode) {
|
||||||
|
case CipherMode::CbcEncrypt: crypto::EncryptAes128Cbc(reinterpret_cast<void *>(dst_addr), size, g_key_slot_manager.GetKey(slot), crypto::AesEncryptor128::KeySize, iv_ctr.data, sizeof(iv_ctr.data), reinterpret_cast<const void *>(src_addr), size); break;
|
||||||
|
case CipherMode::CbcDecrypt: crypto::DecryptAes128Cbc(reinterpret_cast<void *>(dst_addr), size, g_key_slot_manager.GetKey(slot), crypto::AesEncryptor128::KeySize, iv_ctr.data, sizeof(iv_ctr.data), reinterpret_cast<const void *>(src_addr), size); break;
|
||||||
|
case CipherMode::Ctr: crypto::EncryptAes128Ctr(reinterpret_cast<void *>(dst_addr), size, g_key_slot_manager.GetKey(slot), crypto::AesEncryptor128::KeySize, iv_ctr.data, sizeof(iv_ctr.data), reinterpret_cast<const void *>(src_addr), size); break;
|
||||||
|
default:
|
||||||
|
return smc::Result::InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_op = AsyncOperationKey{g_async_key};
|
||||||
|
return smc::Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::GenerateSpecificAesKey);
|
||||||
|
// args.r[1] = source.data64[0];
|
||||||
|
// args.r[2] = source.data64[1];
|
||||||
|
// args.r[3] = generation;
|
||||||
|
// args.r[4] = which;
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// out_key->data64[0] = args.r[1];
|
||||||
|
// out_key->data64[1] = args.r[2];
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::ComputeCmac);
|
||||||
|
// args.r[1] = keyslot;
|
||||||
|
// args.r[2] = reinterpret_cast<u64>(data);
|
||||||
|
// args.r[3] = size;
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// out_mac->data64[0] = args.r[1];
|
||||||
|
// out_mac->data64[1] = args.r[2];
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result ReencryptDeviceUniqueData(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::ReencryptDeviceUniqueData);
|
||||||
|
// args.r[1] = reinterpret_cast<u64>(std::addressof(access_key_dec));
|
||||||
|
// args.r[2] = reinterpret_cast<u64>(std::addressof(access_key_enc));
|
||||||
|
// args.r[3] = option;
|
||||||
|
// args.r[4] = reinterpret_cast<u64>(data);
|
||||||
|
// args.r[5] = size;
|
||||||
|
// args.r[6] = reinterpret_cast<u64>(std::addressof(source_dec));
|
||||||
|
// args.r[7] = reinterpret_cast<u64>(std::addressof(source_enc));
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result DecryptDeviceUniqueData(void *data, size_t size, const AccessKey &access_key, const KeySource &source, DeviceUniqueDataMode mode) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::DecryptDeviceUniqueData);
|
||||||
|
// args.r[1] = access_key.data64[0];
|
||||||
|
// args.r[2] = access_key.data64[1];
|
||||||
|
// args.r[3] = static_cast<u32>(mode);
|
||||||
|
// args.r[4] = reinterpret_cast<u64>(data);
|
||||||
|
// args.r[5] = size;
|
||||||
|
// args.r[6] = source.data64[0];
|
||||||
|
// args.r[7] = source.data64[1];
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result ModularExponentiateWithStorageKey(AsyncOperationKey *out_op, const void *base, const void *mod, ModularExponentiateWithStorageKeyMode mode) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::ModularExponentiateWithStorageKey);
|
||||||
|
// args.r[1] = reinterpret_cast<u64>(base);
|
||||||
|
// args.r[2] = reinterpret_cast<u64>(mod);
|
||||||
|
// args.r[3] = static_cast<u32>(mode);
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// out_op->value = args.r[1];
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result PrepareEsDeviceUniqueKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::PrepareEsDeviceUniqueKey);
|
||||||
|
// args.r[1] = reinterpret_cast<u64>(base);
|
||||||
|
// args.r[2] = reinterpret_cast<u64>(mod);
|
||||||
|
// std::memset(std::addressof(args.r[3]), 0, 4 * sizeof(args.r[3]));
|
||||||
|
// std::memcpy(std::addressof(args.r[3]), label_digest, std::min(static_cast<size_t>(4 * sizeof(args.r[3])), label_digest_size));
|
||||||
|
// args.r[7] = option;
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// out_op->value = args.r[1];
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
Result LoadPreparedAesKey(u32 keyslot, const AccessKey &access_key) {
|
||||||
|
std::scoped_lock lk(g_crypto_lock);
|
||||||
|
|
||||||
|
/* Check args. */
|
||||||
|
SMC_R_UNLESS(IsUserAesKeySlot(keyslot), InvalidArgument);
|
||||||
|
|
||||||
|
/* Unseal the key. */
|
||||||
|
g_key_slot_manager.SetAesKey128(keyslot, std::addressof(access_key), sizeof(access_key));
|
||||||
|
|
||||||
|
return smc::Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Result PrepareCommonEsTitleKey(AccessKey *out, const KeySource &source, u32 generation) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::PrepareCommonEsTitleKey);
|
||||||
|
// args.r[1] = source.data64[0];
|
||||||
|
// args.r[2] = source.data64[1];
|
||||||
|
// args.r[3] = generation;
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// out->data64[0] = args.r[1];
|
||||||
|
// out->data64[1] = args.r[2];
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//
|
||||||
|
///* Deprecated functions. */
|
||||||
|
//Result LoadEsDeviceKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::LoadEsDeviceKey);
|
||||||
|
// args.r[1] = access_key.data64[0];
|
||||||
|
// args.r[2] = access_key.data64[1];
|
||||||
|
// args.r[3] = option;
|
||||||
|
// args.r[4] = reinterpret_cast<u64>(data);
|
||||||
|
// args.r[5] = size;
|
||||||
|
// args.r[6] = source.data64[0];
|
||||||
|
// args.r[7] = source.data64[1];
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result DecryptDeviceUniqueData(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::DecryptDeviceUniqueData);
|
||||||
|
// args.r[1] = access_key.data64[0];
|
||||||
|
// args.r[2] = access_key.data64[1];
|
||||||
|
// args.r[3] = option;
|
||||||
|
// args.r[4] = reinterpret_cast<u64>(data);
|
||||||
|
// args.r[5] = size;
|
||||||
|
// args.r[6] = source.data64[0];
|
||||||
|
// args.r[7] = source.data64[1];
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// *out_size = static_cast<size_t>(args.r[1]);
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Result DecryptAndStoreGcKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
|
||||||
|
// svc::SecureMonitorArguments args;
|
||||||
|
//
|
||||||
|
// args.r[0] = static_cast<u64>(FunctionId::DecryptAndStoreGcKey);
|
||||||
|
// args.r[1] = access_key.data64[0];
|
||||||
|
// args.r[2] = access_key.data64[1];
|
||||||
|
// args.r[3] = option;
|
||||||
|
// args.r[4] = reinterpret_cast<u64>(data);
|
||||||
|
// args.r[5] = size;
|
||||||
|
// args.r[6] = source.data64[0];
|
||||||
|
// args.r[7] = source.data64[1];
|
||||||
|
// svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
//
|
||||||
|
// return static_cast<Result>(args.r[0]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
Result AtmosphereCopyToIram(uintptr_t, const void *, size_t ) {
|
||||||
|
AMS_ABORT("AtmosphereCopyToIram not supported on generic SecureMonitor api.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AtmosphereCopyFromIram(void *, uintptr_t, size_t) {
|
||||||
|
AMS_ABORT("AtmosphereCopyToIram not supported on generic SecureMonitor api.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AtmosphereReadWriteRegister(uint64_t, uint32_t, uint32_t, uint32_t *) {
|
||||||
|
AMS_ABORT("AtmosphereReadWriteRegister not supported on generic SecureMonitor api.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id) {
|
||||||
|
/* TODO: We actually probably should support this one on generic? */
|
||||||
|
AMS_UNUSED(out_config, out_paths, storage_id);
|
||||||
|
AMS_ABORT("AtmosphereGetEmummcConfig not supported on generic SecureMonitor api.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -126,15 +126,15 @@ namespace ams::spl::smc {
|
||||||
return static_cast<Result>(args.r[0]);
|
return static_cast<Result>(args.r[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ComputeAes(AsyncOperationKey *out_op, u32 dst_addr, u32 mode, const IvCtr &iv_ctr, u32 src_addr, size_t size) {
|
Result ComputeAes(AsyncOperationKey *out_op, u64 dst_addr, u32 mode, const IvCtr &iv_ctr, u64 src_addr, size_t size) {
|
||||||
svc::SecureMonitorArguments args;
|
svc::SecureMonitorArguments args;
|
||||||
|
|
||||||
args.r[0] = static_cast<u64>(FunctionId::ComputeAes);
|
args.r[0] = static_cast<u64>(FunctionId::ComputeAes);
|
||||||
args.r[1] = mode;
|
args.r[1] = mode;
|
||||||
args.r[2] = iv_ctr.data64[0];
|
args.r[2] = iv_ctr.data64[0];
|
||||||
args.r[3] = iv_ctr.data64[1];
|
args.r[3] = iv_ctr.data64[1];
|
||||||
args.r[4] = src_addr;
|
args.r[4] = static_cast<u32>(src_addr);
|
||||||
args.r[5] = dst_addr;
|
args.r[5] = static_cast<u32>(dst_addr);
|
||||||
args.r[6] = size;
|
args.r[6] = size;
|
||||||
svc::CallSecureMonitor(std::addressof(args));
|
svc::CallSecureMonitor(std::addressof(args));
|
||||||
|
|
||||||
|
|
106
libraries/libstratosphere/source/spl/spl_api.os.generic.cpp
Normal file
106
libraries/libstratosphere/source/spl/spl_api.os.generic.cpp
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
namespace ams::spl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool InitializeImpl() {
|
||||||
|
/* Initialize implementation api. */
|
||||||
|
impl::Initialize();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureInitialized() {
|
||||||
|
AMS_FUNCTION_LOCAL_STATIC(bool, s_initialized, InitializeImpl());
|
||||||
|
AMS_ABORT_UNLESS(s_initialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WaitAvailableKeySlotAndExecute(auto f) {
|
||||||
|
os::SystemEvent *event = nullptr;
|
||||||
|
while (true) {
|
||||||
|
R_TRY_CATCH(static_cast<::ams::Result>(f())) {
|
||||||
|
R_CATCH(spl::ResultNoAvailableKeySlot) {
|
||||||
|
if (event == nullptr) {
|
||||||
|
event = impl::GetAesKeySlotAvailableEvent();
|
||||||
|
}
|
||||||
|
event->Wait();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} R_END_TRY_CATCH;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AllocateAesKeySlot(s32 *out_slot) {
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
R_RETURN(WaitAvailableKeySlotAndExecute([&]() -> Result {
|
||||||
|
R_RETURN(impl::AllocateAesKeySlot(out_slot));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DeallocateAesKeySlot(s32 slot) {
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
R_RETURN(impl::DeallocateAesKeySlot(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GenerateAesKek(AccessKey *out_access_key, const void *key_source, size_t key_source_size, s32 generation, u32 option) {
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
|
/* Check key size (assumed valid). */
|
||||||
|
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||||
|
AMS_UNUSED(key_source_size);
|
||||||
|
|
||||||
|
/* AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option */
|
||||||
|
R_RETURN(impl::GenerateAesKek(out_access_key, *static_cast<const KeySource *>(key_source), generation, option));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LoadAesKey(s32 slot, const AccessKey &access_key, const void *key_source, size_t key_source_size) {
|
||||||
|
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||||
|
AMS_UNUSED(key_source_size);
|
||||||
|
|
||||||
|
R_RETURN(impl::LoadAesKey(slot, access_key, *static_cast<const KeySource *>(key_source)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GenerateAesKey(void *dst, size_t dst_size, const AccessKey &access_key, const void *key_source, size_t key_source_size) {
|
||||||
|
AMS_ASSERT(dst_size >= sizeof(AesKey));
|
||||||
|
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||||
|
AMS_UNUSED(dst_size, key_source_size);
|
||||||
|
|
||||||
|
R_RETURN(WaitAvailableKeySlotAndExecute([&]() -> Result {
|
||||||
|
R_RETURN(impl::GenerateAesKey(static_cast<AesKey *>(dst), access_key, *static_cast<const KeySource *>(key_source)));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ComputeCtr(void *dst, size_t dst_size, s32 slot, const void *src, size_t src_size, const void *iv, size_t iv_size) {
|
||||||
|
AMS_ASSERT(iv_size >= sizeof(IvCtr));
|
||||||
|
AMS_UNUSED(iv_size);
|
||||||
|
AMS_ASSERT(dst_size >= src_size);
|
||||||
|
|
||||||
|
R_RETURN(impl::ComputeCtr(dst, dst_size, slot, src, src_size, *static_cast<const IvCtr *>(iv)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LoadPreparedAesKey(s32 slot, const AccessKey &access_key) {
|
||||||
|
R_RETURN(impl::LoadPreparedAesKey(slot, access_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,9 +17,6 @@
|
||||||
|
|
||||||
namespace ams::spl {
|
namespace ams::spl {
|
||||||
|
|
||||||
/* BIG TODO: How to deal with this? */
|
|
||||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum class InitializeMode {
|
enum class InitializeMode {
|
||||||
|
@ -298,6 +295,4 @@ namespace ams::spl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -25,6 +25,7 @@
|
||||||
#include <vapours/crypto/crypto_sha3_generator.hpp>
|
#include <vapours/crypto/crypto_sha3_generator.hpp>
|
||||||
#include <vapours/crypto/crypto_aes_encryptor.hpp>
|
#include <vapours/crypto/crypto_aes_encryptor.hpp>
|
||||||
#include <vapours/crypto/crypto_aes_decryptor.hpp>
|
#include <vapours/crypto/crypto_aes_decryptor.hpp>
|
||||||
|
#include <vapours/crypto/crypto_aes_cbc_encryptor_decryptor.hpp>
|
||||||
#include <vapours/crypto/crypto_aes_ctr_encryptor_decryptor.hpp>
|
#include <vapours/crypto/crypto_aes_ctr_encryptor_decryptor.hpp>
|
||||||
#include <vapours/crypto/crypto_aes_xts_encryptor_decryptor.hpp>
|
#include <vapours/crypto/crypto_aes_xts_encryptor_decryptor.hpp>
|
||||||
#include <vapours/crypto/crypto_aes_gcm_encryptor.hpp>
|
#include <vapours/crypto/crypto_aes_gcm_encryptor.hpp>
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/common.hpp>
|
||||||
|
#include <vapours/assert.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/crypto/crypto_aes_encryptor.hpp>
|
||||||
|
#include <vapours/crypto/crypto_cbc_encryptor.hpp>
|
||||||
|
#include <vapours/crypto/crypto_cbc_decryptor.hpp>
|
||||||
|
|
||||||
|
namespace ams::crypto {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template<template<typename> typename _CbcImpl, typename _AesImpl>
|
||||||
|
class AesCbcCryptor {
|
||||||
|
NON_COPYABLE(AesCbcCryptor);
|
||||||
|
NON_MOVEABLE(AesCbcCryptor);
|
||||||
|
private:
|
||||||
|
using AesImpl = _AesImpl;
|
||||||
|
using CbcImpl = _CbcImpl<AesImpl>;
|
||||||
|
public:
|
||||||
|
static constexpr size_t KeySize = AesImpl::KeySize;
|
||||||
|
static constexpr size_t BlockSize = CbcImpl::BlockSize;
|
||||||
|
static constexpr size_t IvSize = CbcImpl::BlockSize;
|
||||||
|
private:
|
||||||
|
AesImpl m_aes_impl;
|
||||||
|
CbcImpl m_cbc_impl;
|
||||||
|
public:
|
||||||
|
AesCbcCryptor() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(const void *key, size_t key_size, const void *iv, size_t iv_size) {
|
||||||
|
AMS_ASSERT(key_size == KeySize);
|
||||||
|
AMS_ASSERT(iv_size == IvSize);
|
||||||
|
|
||||||
|
m_aes_impl.Initialize(key, key_size);
|
||||||
|
m_cbc_impl.Initialize(std::addressof(m_aes_impl), iv, iv_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
return m_cbc_impl.Update(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetBufferedDataSize() const {
|
||||||
|
return m_cbc_impl.GetBufferedDataSize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using Aes128CbcEncryptor = impl::AesCbcCryptor<CbcEncryptor, AesEncryptor128>;
|
||||||
|
using Aes192CbcEncryptor = impl::AesCbcCryptor<CbcEncryptor, AesEncryptor192>;
|
||||||
|
using Aes256CbcEncryptor = impl::AesCbcCryptor<CbcEncryptor, AesEncryptor256>;
|
||||||
|
|
||||||
|
using Aes128CbcDecryptor = impl::AesCbcCryptor<CbcDecryptor, AesDecryptor128>;
|
||||||
|
using Aes192CbcDecryptor = impl::AesCbcCryptor<CbcDecryptor, AesDecryptor192>;
|
||||||
|
using Aes256CbcDecryptor = impl::AesCbcCryptor<CbcDecryptor, AesDecryptor256>;
|
||||||
|
|
||||||
|
size_t EncryptAes128Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||||
|
size_t EncryptAes192Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||||
|
size_t EncryptAes256Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||||
|
|
||||||
|
size_t DecryptAes128Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||||
|
size_t DecryptAes192Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||||
|
size_t DecryptAes256Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/common.hpp>
|
||||||
|
#include <vapours/assert.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/crypto/impl/crypto_cbc_mode_impl.hpp>
|
||||||
|
|
||||||
|
namespace ams::crypto {
|
||||||
|
|
||||||
|
/* TODO: C++20 BlockCipher concept */
|
||||||
|
|
||||||
|
template<typename BlockCipher>
|
||||||
|
class CbcDecryptor {
|
||||||
|
NON_COPYABLE(CbcDecryptor);
|
||||||
|
NON_MOVEABLE(CbcDecryptor);
|
||||||
|
private:
|
||||||
|
using Impl = impl::CbcModeImpl<BlockCipher>;
|
||||||
|
public:
|
||||||
|
static constexpr size_t KeySize = Impl::KeySize;
|
||||||
|
static constexpr size_t BlockSize = Impl::BlockSize;
|
||||||
|
static constexpr size_t IvSize = Impl::IvSize;
|
||||||
|
private:
|
||||||
|
Impl m_impl;
|
||||||
|
public:
|
||||||
|
CbcDecryptor() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(const BlockCipher *cipher, const void *iv, size_t iv_size) {
|
||||||
|
m_impl.Initialize(cipher, iv, iv_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
return m_impl.UpdateDecryption(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetBufferedDataSize() const {
|
||||||
|
return m_impl.GetBufferedDataSize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/common.hpp>
|
||||||
|
#include <vapours/assert.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/crypto/impl/crypto_cbc_mode_impl.hpp>
|
||||||
|
|
||||||
|
namespace ams::crypto {
|
||||||
|
|
||||||
|
/* TODO: C++20 BlockCipher concept */
|
||||||
|
|
||||||
|
template<typename BlockCipher>
|
||||||
|
class CbcEncryptor {
|
||||||
|
NON_COPYABLE(CbcEncryptor);
|
||||||
|
NON_MOVEABLE(CbcEncryptor);
|
||||||
|
private:
|
||||||
|
using Impl = impl::CbcModeImpl<BlockCipher>;
|
||||||
|
public:
|
||||||
|
static constexpr size_t KeySize = Impl::KeySize;
|
||||||
|
static constexpr size_t BlockSize = Impl::BlockSize;
|
||||||
|
static constexpr size_t IvSize = Impl::IvSize;
|
||||||
|
private:
|
||||||
|
Impl m_impl;
|
||||||
|
public:
|
||||||
|
CbcEncryptor() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(const BlockCipher *cipher, const void *iv, size_t iv_size) {
|
||||||
|
m_impl.Initialize(cipher, iv, iv_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
return m_impl.UpdateEncryption(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetBufferedDataSize() const {
|
||||||
|
return m_impl.GetBufferedDataSize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -13,11 +13,11 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours/common.hpp>
|
#include <vapours/common.hpp>
|
||||||
#include <vapours/assert.hpp>
|
#include <vapours/assert.hpp>
|
||||||
#include <vapours/util.hpp>
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/crypto/impl/crypto_block_cipher.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace ams::crypto::impl {
|
namespace ams::crypto::impl {
|
||||||
|
@ -50,6 +50,8 @@ namespace ams::crypto::impl {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* static_assert(HashFunction<Sha1Impl>); */
|
static_assert(BlockCipher<AesImpl<16>>);
|
||||||
|
static_assert(BlockCipher<AesImpl<24>>);
|
||||||
|
static_assert(BlockCipher<AesImpl<32>>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/common.hpp>
|
||||||
|
#include <vapours/assert.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/crypto/crypto_memory_compare.hpp>
|
||||||
|
#include <vapours/crypto/crypto_memory_clear.hpp>
|
||||||
|
|
||||||
|
namespace ams::crypto::impl {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept BlockCipher = requires(T &t, const void *cv, void *v, size_t sz, bool b) {
|
||||||
|
{ T::BlockSize } -> std::convertible_to<size_t>;
|
||||||
|
{ t.EncryptBlock(v, sz, cv, sz) } -> std::same_as<void>;
|
||||||
|
{ t.DecryptBlock(v, sz, cv, sz) } -> std::same_as<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/common.hpp>
|
||||||
|
#include <vapours/assert.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
#include <vapours/crypto/crypto_memory_clear.hpp>
|
||||||
|
#include <vapours/crypto/crypto_aes_encryptor.hpp>
|
||||||
|
|
||||||
|
namespace ams::crypto::impl {
|
||||||
|
|
||||||
|
template<typename BlockCipher>
|
||||||
|
class CbcModeImpl {
|
||||||
|
NON_COPYABLE(CbcModeImpl);
|
||||||
|
NON_MOVEABLE(CbcModeImpl);
|
||||||
|
public:
|
||||||
|
static constexpr size_t KeySize = BlockCipher::KeySize;
|
||||||
|
static constexpr size_t BlockSize = BlockCipher::BlockSize;
|
||||||
|
static constexpr size_t IvSize = BlockCipher::BlockSize;
|
||||||
|
private:
|
||||||
|
enum State {
|
||||||
|
State_None,
|
||||||
|
State_Initialized,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
const BlockCipher *m_block_cipher;
|
||||||
|
u8 m_iv[IvSize];
|
||||||
|
u8 m_buffer[BlockSize];
|
||||||
|
size_t m_buffered_bytes;
|
||||||
|
State m_state;
|
||||||
|
public:
|
||||||
|
CbcModeImpl() : m_state(State_None) { /* ... */ }
|
||||||
|
|
||||||
|
~CbcModeImpl() {
|
||||||
|
ClearMemory(this, sizeof(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(const BlockCipher *block_cipher, const void *iv, size_t iv_size) {
|
||||||
|
AMS_ASSERT(iv_size == IvSize);
|
||||||
|
AMS_UNUSED(iv_size);
|
||||||
|
|
||||||
|
m_block_cipher = block_cipher;
|
||||||
|
std::memcpy(m_iv, iv, IvSize);
|
||||||
|
m_buffered_bytes = 0;
|
||||||
|
|
||||||
|
m_state = State_Initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetBufferedDataSize() const {
|
||||||
|
return m_buffered_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t UpdateEncryption(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
AMS_ASSERT(dst_size >= ((src_size + this->GetBufferedDataSize()) / BlockSize) * BlockSize);
|
||||||
|
AMS_ASSERT(m_state == State_Initialized);
|
||||||
|
|
||||||
|
return this->Update(dst, dst_size, src, src_size, [&] (u8 *d, u8 *i, const u8 *s, size_t n) ALWAYS_INLINE_LAMBDA {
|
||||||
|
this->EncryptBlocks(d, i, s, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t UpdateDecryption(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
AMS_ASSERT(dst_size >= ((src_size + this->GetBufferedDataSize()) / BlockSize) * BlockSize);
|
||||||
|
AMS_ASSERT(m_state == State_Initialized);
|
||||||
|
|
||||||
|
return this->Update(dst, dst_size, src, src_size, [&] (u8 *d, u8 *i, const u8 *s, size_t n) ALWAYS_INLINE_LAMBDA {
|
||||||
|
this->DecryptBlocks(d, i, s, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
size_t Update(void *_dst, size_t dst_size, const void *_src, size_t src_size, auto ProcessBlocks) {
|
||||||
|
AMS_UNUSED(dst_size);
|
||||||
|
|
||||||
|
u8 *dst = static_cast<u8 *>(_dst);
|
||||||
|
const u8 *src = static_cast<const u8 *>(_src);
|
||||||
|
size_t remaining = src_size;
|
||||||
|
size_t processed = 0;
|
||||||
|
|
||||||
|
if (m_buffered_bytes > 0) {
|
||||||
|
const size_t copy_size = std::min(BlockSize - m_buffered_bytes, remaining);
|
||||||
|
|
||||||
|
std::memcpy(m_buffer + m_buffered_bytes, src, copy_size);
|
||||||
|
src += copy_size;
|
||||||
|
remaining -= copy_size;
|
||||||
|
m_buffered_bytes += copy_size;
|
||||||
|
|
||||||
|
if (m_buffered_bytes == BlockSize) {
|
||||||
|
ProcessBlocks(dst, m_iv, m_buffer, 1);
|
||||||
|
processed += BlockSize;
|
||||||
|
dst += BlockSize;
|
||||||
|
|
||||||
|
m_buffered_bytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining >= BlockSize) {
|
||||||
|
const size_t num_blocks = remaining / BlockSize;
|
||||||
|
|
||||||
|
ProcessBlocks(dst, m_iv, src, num_blocks);
|
||||||
|
|
||||||
|
const size_t processed_size = num_blocks * BlockSize;
|
||||||
|
dst += processed_size;
|
||||||
|
src += processed_size;
|
||||||
|
remaining -= processed_size;
|
||||||
|
processed += processed_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining > 0) {
|
||||||
|
std::memcpy(m_buffer, src, remaining);
|
||||||
|
m_buffered_bytes = remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncryptBlocks(u8 *dst, u8 *iv, const u8 *src, size_t num_blocks) {
|
||||||
|
const u8 *cur_iv = iv;
|
||||||
|
|
||||||
|
u8 block[BlockSize];
|
||||||
|
while (num_blocks--) {
|
||||||
|
for (size_t i = 0; i < BlockSize; ++i) {
|
||||||
|
block[i] = src[i] ^ cur_iv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
m_block_cipher->EncryptBlock(dst, BlockSize, block, BlockSize);
|
||||||
|
|
||||||
|
cur_iv = dst;
|
||||||
|
src += BlockSize;
|
||||||
|
dst += BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iv != cur_iv) {
|
||||||
|
std::memcpy(iv, cur_iv, BlockSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecryptBlocks(u8 *dst, u8 *iv, const u8 *src, size_t num_blocks) {
|
||||||
|
u8 next_iv[BlockSize];
|
||||||
|
std::memcpy(next_iv, src + ((num_blocks - 1) * BlockSize), BlockSize);
|
||||||
|
|
||||||
|
if (src == dst) {
|
||||||
|
src = src + ((num_blocks - 1) * BlockSize);
|
||||||
|
dst = dst + ((num_blocks - 1) * BlockSize);
|
||||||
|
|
||||||
|
const u8 *cur_iv = (num_blocks == 1) ? iv : src - BlockSize;
|
||||||
|
|
||||||
|
while (num_blocks-- > 1) {
|
||||||
|
m_block_cipher->DecryptBlock(dst, BlockSize, src, BlockSize);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < BlockSize; ++i) {
|
||||||
|
dst[i] ^= cur_iv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_iv -= BlockSize;
|
||||||
|
src -= BlockSize;
|
||||||
|
dst -= BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_block_cipher->DecryptBlock(dst, BlockSize, src, BlockSize);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < BlockSize; ++i) {
|
||||||
|
dst[i] ^= iv[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const u8 *cur_iv = iv;
|
||||||
|
|
||||||
|
while (num_blocks-- > 0) {
|
||||||
|
m_block_cipher->DecryptBlock(dst, BlockSize, src, BlockSize);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < BlockSize; ++i) {
|
||||||
|
dst[i] ^= cur_iv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_iv = src;
|
||||||
|
src += BlockSize;
|
||||||
|
dst += BlockSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(iv, next_iv, BlockSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO: Optimized AES cbc impl specializations. */
|
||||||
|
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
namespace ams::crypto::impl {
|
namespace ams::crypto::impl {
|
||||||
|
|
||||||
template<typename Hash> /* requires HashFunction<Hash> */
|
template<HashFunction Hash>
|
||||||
class HmacImpl {
|
class HmacImpl {
|
||||||
NON_COPYABLE(HmacImpl);
|
NON_COPYABLE(HmacImpl);
|
||||||
NON_MOVEABLE(HmacImpl);
|
NON_MOVEABLE(HmacImpl);
|
||||||
|
@ -61,7 +61,7 @@ namespace ams::crypto::impl {
|
||||||
void GetMac(void *dst, size_t dst_size);
|
void GetMac(void *dst, size_t dst_size);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Hash>
|
template<HashFunction Hash>
|
||||||
inline void HmacImpl<Hash>::Initialize(const void *key, size_t key_size) {
|
inline void HmacImpl<Hash>::Initialize(const void *key, size_t key_size) {
|
||||||
/* Clear the key storage. */
|
/* Clear the key storage. */
|
||||||
std::memset(m_key, 0, sizeof(m_key));
|
std::memset(m_key, 0, sizeof(m_key));
|
||||||
|
@ -88,14 +88,14 @@ namespace ams::crypto::impl {
|
||||||
m_state = State_Initialized;
|
m_state = State_Initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Hash>
|
template<HashFunction Hash>
|
||||||
inline void HmacImpl<Hash>::Update(const void *data, size_t data_size) {
|
inline void HmacImpl<Hash>::Update(const void *data, size_t data_size) {
|
||||||
AMS_ASSERT(m_state == State_Initialized);
|
AMS_ASSERT(m_state == State_Initialized);
|
||||||
|
|
||||||
m_hash_function.Update(data, data_size);
|
m_hash_function.Update(data, data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Hash>
|
template<HashFunction Hash>
|
||||||
inline void HmacImpl<Hash>::GetMac(void *dst, size_t dst_size) {
|
inline void HmacImpl<Hash>::GetMac(void *dst, size_t dst_size) {
|
||||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||||
AMS_ASSERT(dst_size >= MacSize);
|
AMS_ASSERT(dst_size >= MacSize);
|
||||||
|
|
|
@ -56,6 +56,6 @@ namespace ams::crypto::impl {
|
||||||
void ProcessLastBlock();
|
void ProcessLastBlock();
|
||||||
};
|
};
|
||||||
|
|
||||||
/* static_assert(HashFunction<Md5Impl>); */
|
static_assert(HashFunction<Md5Impl>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
namespace ams::crypto::impl {
|
namespace ams::crypto::impl {
|
||||||
|
|
||||||
template<typename Hash> requires HashFunction<Hash>
|
template<HashFunction Hash>
|
||||||
class RsaOaepImpl {
|
class RsaOaepImpl {
|
||||||
NON_COPYABLE(RsaOaepImpl);
|
NON_COPYABLE(RsaOaepImpl);
|
||||||
NON_MOVEABLE(RsaOaepImpl);
|
NON_MOVEABLE(RsaOaepImpl);
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
namespace ams::crypto::impl {
|
namespace ams::crypto::impl {
|
||||||
|
|
||||||
template<typename Hash> requires HashFunction<Hash>
|
template<HashFunction Hash>
|
||||||
class RsaPssImpl {
|
class RsaPssImpl {
|
||||||
NON_COPYABLE(RsaPssImpl);
|
NON_COPYABLE(RsaPssImpl);
|
||||||
NON_MOVEABLE(RsaPssImpl);
|
NON_MOVEABLE(RsaPssImpl);
|
||||||
|
|
|
@ -55,6 +55,6 @@ namespace ams::crypto::impl {
|
||||||
void ProcessLastBlock();
|
void ProcessLastBlock();
|
||||||
};
|
};
|
||||||
|
|
||||||
/* static_assert(HashFunction<Sha1Impl>); */
|
static_assert(HashFunction<Sha1Impl>);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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 <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::crypto {
|
||||||
|
|
||||||
|
size_t EncryptAes128Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||||
|
Aes128CbcEncryptor aes;
|
||||||
|
aes.Initialize(key, key_size, iv, iv_size);
|
||||||
|
return aes.Update(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t EncryptAes192Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||||
|
Aes192CbcEncryptor aes;
|
||||||
|
aes.Initialize(key, key_size, iv, iv_size);
|
||||||
|
return aes.Update(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t EncryptAes256Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||||
|
Aes256CbcEncryptor aes;
|
||||||
|
aes.Initialize(key, key_size, iv, iv_size);
|
||||||
|
return aes.Update(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DecryptAes128Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||||
|
Aes128CbcDecryptor aes;
|
||||||
|
aes.Initialize(key, key_size, iv, iv_size);
|
||||||
|
return aes.Update(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DecryptAes192Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||||
|
Aes192CbcDecryptor aes;
|
||||||
|
aes.Initialize(key, key_size, iv, iv_size);
|
||||||
|
return aes.Update(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DecryptAes256Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||||
|
Aes256CbcDecryptor aes;
|
||||||
|
aes.Initialize(key, key_size, iv, iv_size);
|
||||||
|
return aes.Update(dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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 <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::crypto {
|
||||||
|
|
||||||
|
bool IsSameBytes(const void *lhs, const void *rhs, size_t size) {
|
||||||
|
/* TODO: Should the generic impl be constant time? */
|
||||||
|
volatile u8 diff = 0;
|
||||||
|
|
||||||
|
const volatile u8 *lhs8 = static_cast<const volatile u8 *>(lhs);
|
||||||
|
const volatile u8 *rhs8 = static_cast<const volatile u8 *>(rhs);
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
diff = diff | (lhs8[i] ^ rhs8[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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 <vapours.hpp>
|
||||||
|
#include "crypto_update_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::crypto::impl {
|
||||||
|
|
||||||
|
size_t XtsModeImpl::UpdateGeneric(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
AMS_ASSERT(m_state == State_Initialized || m_state == State_Processing);
|
||||||
|
|
||||||
|
return UpdateImpl<void>(this, dst, dst_size, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t XtsModeImpl::ProcessBlocksGeneric(u8 *dst, const u8 *src, size_t num_blocks) {
|
||||||
|
size_t processed = BlockSize * (num_blocks - 1);
|
||||||
|
|
||||||
|
if (m_state == State_Processing) {
|
||||||
|
this->ProcessBlock(dst, m_last_block);
|
||||||
|
dst += BlockSize;
|
||||||
|
processed += BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((--num_blocks) > 0) {
|
||||||
|
this->ProcessBlock(dst, src);
|
||||||
|
dst += BlockSize;
|
||||||
|
src += BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(m_last_block, src, BlockSize);
|
||||||
|
|
||||||
|
m_state = State_Processing;
|
||||||
|
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> size_t XtsModeImpl::Update<AesEncryptor128>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||||
|
template<> size_t XtsModeImpl::Update<AesEncryptor192>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||||
|
template<> size_t XtsModeImpl::Update<AesEncryptor256>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||||
|
|
||||||
|
template<> size_t XtsModeImpl::Update<AesDecryptor128>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||||
|
template<> size_t XtsModeImpl::Update<AesDecryptor192>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||||
|
template<> size_t XtsModeImpl::Update<AesDecryptor256>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||||
|
|
||||||
|
}
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* 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 <stratosphere.hpp>
|
||||||
|
|
||||||
namespace ams {
|
namespace ams {
|
||||||
|
|
|
@ -1,3 +1,18 @@
|
||||||
|
/*
|
||||||
|
* 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 <stratosphere.hpp>
|
||||||
|
|
||||||
namespace ams {
|
namespace ams {
|
||||||
|
|
Loading…
Reference in a new issue