Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,980 +0,0 @@
|
||||
/*
|
||||
* 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_ctr_drbg.hpp"
|
||||
#include "spl_device_address_mapper.hpp"
|
||||
#include "spl_key_slot_cache.hpp"
|
||||
|
||||
namespace ams::hos {
|
||||
|
||||
void InitializeVersionInternal(bool allow_approximate);
|
||||
|
||||
}
|
||||
|
||||
namespace ams::spl::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Drbg type. */
|
||||
using Drbg = CtrDrbg<crypto::AesEncryptor128, AesKeySize, false>;
|
||||
|
||||
/* Convenient defines. */
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
constexpr size_t DeviceAddressSpaceAlign = 4_MB;
|
||||
|
||||
constexpr u32 WorkBufferBase = 0x80000000u;
|
||||
constexpr u32 ComputeAesInMapBase = 0x90000000u;
|
||||
constexpr u32 ComputeAesOutMapBase = 0xC0000000u;
|
||||
constexpr size_t ComputeAesSizeMax = static_cast<size_t>(ComputeAesOutMapBase - ComputeAesInMapBase);
|
||||
#endif
|
||||
|
||||
constexpr size_t DeviceUniqueDataIvSize = 0x10;
|
||||
constexpr size_t DeviceUniqueDataPaddingSize = 0x08;
|
||||
constexpr size_t DeviceUniqueDataDeviceIdSize = 0x08;
|
||||
constexpr size_t DeviceUniqueDataGmacSize = 0x10;
|
||||
|
||||
constexpr size_t DeviceUniqueDataPlainMetaDataSize = DeviceUniqueDataIvSize + DeviceUniqueDataGmacSize;
|
||||
constexpr size_t DeviceUniqueDataMetaDataSize = DeviceUniqueDataPlainMetaDataSize + DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize;
|
||||
|
||||
constexpr size_t Rsa2048BlockSize = 0x100;
|
||||
constexpr size_t LabelDigestSizeMax = 0x20;
|
||||
|
||||
constexpr size_t WorkBufferSizeMax = 0x800;
|
||||
|
||||
constexpr const KeySource KeyGenerationSource = {
|
||||
.data = { 0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8 }
|
||||
};
|
||||
|
||||
constexpr const KeySource AesKeyDecryptionSource = {
|
||||
.data = { 0x11, 0x70, 0x24, 0x2B, 0x48, 0x69, 0x11, 0xF1, 0x11, 0xB0, 0x0C, 0x47, 0x7C, 0xC3, 0xEF, 0x7E }
|
||||
};
|
||||
|
||||
constexpr s32 PhysicalAesKeySlotCount = 6;
|
||||
|
||||
/* KeySlot management. */
|
||||
constinit AesKeySlotCache g_aes_keyslot_cache;
|
||||
constinit util::optional<AesKeySlotCacheEntry> g_aes_keyslot_cache_entry[PhysicalAesKeySlotCount] = {};
|
||||
|
||||
constinit bool g_is_physical_keyslot_allowed = false;
|
||||
constinit bool g_is_modern_device_unique_data = true;
|
||||
|
||||
constexpr inline bool IsVirtualAesKeySlot(s32 keyslot) {
|
||||
return AesKeySlotMin <= keyslot && keyslot <= AesKeySlotMax;
|
||||
}
|
||||
|
||||
constexpr inline bool IsPhysicalAesKeySlot(s32 keyslot) {
|
||||
return keyslot < PhysicalAesKeySlotCount;
|
||||
}
|
||||
|
||||
constexpr inline s32 GetVirtualAesKeySlotIndex(s32 keyslot) {
|
||||
AMS_ASSERT(IsVirtualAesKeySlot(keyslot));
|
||||
return keyslot - AesKeySlotMin;
|
||||
}
|
||||
|
||||
constexpr inline s32 MakeVirtualAesKeySlot(s32 index) {
|
||||
const s32 virt_slot = index + AesKeySlotMin;
|
||||
AMS_ASSERT(IsVirtualAesKeySlot(virt_slot));
|
||||
return virt_slot;
|
||||
}
|
||||
|
||||
enum class AesKeySlotContentType {
|
||||
None = 0,
|
||||
AesKey = 1,
|
||||
PreparedKey = 2,
|
||||
};
|
||||
|
||||
struct AesKeySlotContents {
|
||||
AesKeySlotContentType type;
|
||||
union {
|
||||
struct {
|
||||
AccessKey access_key;
|
||||
KeySource key_source;
|
||||
} aes_key;
|
||||
struct {
|
||||
AccessKey access_key;
|
||||
} prepared_key;
|
||||
};
|
||||
};
|
||||
|
||||
constinit bool g_is_aes_keyslot_allocated[AesKeySlotCount];
|
||||
constinit AesKeySlotContents g_aes_keyslot_contents[AesKeySlotCount] = {};
|
||||
constinit AesKeySlotContents g_aes_physical_keyslot_contents_for_backwards_compatibility[PhysicalAesKeySlotCount] = {};
|
||||
|
||||
void ClearPhysicalAesKeySlot(s32 keyslot) {
|
||||
AMS_ASSERT(IsPhysicalAesKeySlot(keyslot));
|
||||
|
||||
AccessKey access_key = {};
|
||||
KeySource key_source = {};
|
||||
smc::LoadAesKey(keyslot, access_key, key_source);
|
||||
}
|
||||
|
||||
s32 GetPhysicalAesKeySlot(s32 keyslot, bool load) {
|
||||
s32 phys_slot = -1;
|
||||
AesKeySlotContents *contents = nullptr;
|
||||
|
||||
if (g_is_physical_keyslot_allowed && IsPhysicalAesKeySlot(keyslot)) {
|
||||
/* On 1.0.0, we allow the use of physical keyslots. */
|
||||
phys_slot = keyslot;
|
||||
contents = std::addressof(g_aes_physical_keyslot_contents_for_backwards_compatibility[phys_slot]);
|
||||
|
||||
/* If the physical slot is already loaded, we're good. */
|
||||
if (g_aes_keyslot_cache.FindPhysical(phys_slot)) {
|
||||
return phys_slot;
|
||||
}
|
||||
} else {
|
||||
/* This should be a virtual keyslot. */
|
||||
AMS_ASSERT(IsVirtualAesKeySlot(keyslot));
|
||||
|
||||
/* Try to find a physical slot in the cache. */
|
||||
if (g_aes_keyslot_cache.Find(std::addressof(phys_slot), keyslot)) {
|
||||
return phys_slot;
|
||||
}
|
||||
|
||||
/* Allocate a physical slot. */
|
||||
phys_slot = g_aes_keyslot_cache.Allocate(keyslot);
|
||||
contents = std::addressof(g_aes_keyslot_contents[GetVirtualAesKeySlotIndex(keyslot)]);
|
||||
}
|
||||
|
||||
/* Ensure the contents of the keyslot. */
|
||||
if (load) {
|
||||
switch (contents->type) {
|
||||
case AesKeySlotContentType::None:
|
||||
ClearPhysicalAesKeySlot(phys_slot);
|
||||
break;
|
||||
case AesKeySlotContentType::AesKey:
|
||||
R_ABORT_UNLESS(smc::ConvertResult(smc::LoadAesKey(phys_slot, contents->aes_key.access_key, contents->aes_key.key_source)));
|
||||
break;
|
||||
case AesKeySlotContentType::PreparedKey:
|
||||
R_ABORT_UNLESS(smc::ConvertResult(smc::LoadPreparedAesKey(phys_slot, contents->prepared_key.access_key)));
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
return phys_slot;
|
||||
}
|
||||
|
||||
/* Type definitions. */
|
||||
class ScopedAesKeySlot {
|
||||
private:
|
||||
s32 m_slot_index;
|
||||
bool m_allocated;
|
||||
public:
|
||||
ScopedAesKeySlot() : m_slot_index(-1), m_allocated(false) {
|
||||
/* ... */
|
||||
}
|
||||
~ScopedAesKeySlot() {
|
||||
if (m_allocated) {
|
||||
DeallocateAesKeySlot(m_slot_index);
|
||||
}
|
||||
}
|
||||
|
||||
s32 GetIndex() const {
|
||||
return m_slot_index;
|
||||
}
|
||||
|
||||
Result Allocate() {
|
||||
R_TRY(AllocateAesKeySlot(std::addressof(m_slot_index)));
|
||||
m_allocated = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
struct SeLinkedListEntry {
|
||||
u32 num_entries;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct SeCryptContext {
|
||||
SeLinkedListEntry in;
|
||||
SeLinkedListEntry out;
|
||||
};
|
||||
|
||||
/* Global variables. */
|
||||
alignas(os::MemoryPageSize) constinit u8 g_work_buffer[WorkBufferSizeMax];
|
||||
constinit util::TypedStorage<Drbg> g_drbg = {};
|
||||
constinit os::InterruptName g_interrupt_name;
|
||||
constinit os::InterruptEventType g_interrupt = {};
|
||||
constinit util::TypedStorage<os::SystemEvent> g_aes_keyslot_available_event = {};
|
||||
constinit os::SdkMutex g_operation_lock;
|
||||
constinit dd::DeviceAddressSpaceType g_device_address_space = {};
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
constinit u32 g_work_buffer_mapped_address;
|
||||
#else
|
||||
constinit uintptr_t g_work_buffer_mapped_address;
|
||||
#endif
|
||||
|
||||
constinit BootReasonValue g_boot_reason;
|
||||
constinit bool g_is_boot_reason_initialized;
|
||||
|
||||
/* Initialization functionality. */
|
||||
void InitializeAsyncOperation() {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
u64 interrupt_number;
|
||||
impl::GetConfig(std::addressof(interrupt_number), ConfigItem::SecurityEngineInterruptNumber);
|
||||
g_interrupt_name = static_cast<os::InterruptName>(interrupt_number);
|
||||
|
||||
os::InitializeInterruptEvent(std::addressof(g_interrupt), g_interrupt_name, os::EventClearMode_AutoClear);
|
||||
#else
|
||||
AMS_UNUSED(g_interrupt_name, g_interrupt);
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitializeDeviceAddressSpace() {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
/* Create device address space. */
|
||||
R_ABORT_UNLESS(dd::CreateDeviceAddressSpace(std::addressof(g_device_address_space), 0, (1ul << 32)));
|
||||
|
||||
/* Attach to the security engine. */
|
||||
R_ABORT_UNLESS(dd::AttachDeviceAddressSpace(std::addressof(g_device_address_space), dd::DeviceName_Se));
|
||||
|
||||
/* Map work buffer into the device. */
|
||||
const uintptr_t work_buffer_address = reinterpret_cast<uintptr_t>(g_work_buffer);
|
||||
g_work_buffer_mapped_address = WorkBufferBase + (work_buffer_address % DeviceAddressSpaceAlign);
|
||||
|
||||
R_ABORT_UNLESS(dd::MapDeviceAddressSpaceAligned(std::addressof(g_device_address_space), dd::GetCurrentProcessHandle(), work_buffer_address, dd::DeviceAddressSpaceMemoryRegionAlignment, g_work_buffer_mapped_address, dd::MemoryPermission_ReadWrite));
|
||||
#else
|
||||
/* Just set the work buffer address directly. */
|
||||
AMS_UNUSED(g_device_address_space);
|
||||
g_work_buffer_mapped_address = reinterpret_cast<uintptr_t>(g_work_buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitializeCtrDrbg() {
|
||||
u8 seed[Drbg::SeedSize];
|
||||
AMS_ABORT_UNLESS(smc::GenerateRandomBytes(seed, sizeof(seed)) == smc::Result::Success);
|
||||
|
||||
util::ConstructAt(g_drbg);
|
||||
util::GetReference(g_drbg).Initialize(seed, sizeof(seed), nullptr, 0, nullptr, 0);
|
||||
}
|
||||
|
||||
void InitializeKeySlots() {
|
||||
const auto fw_ver = hos::GetVersion();
|
||||
g_is_physical_keyslot_allowed = fw_ver < hos::Version_2_0_0;
|
||||
g_is_modern_device_unique_data = fw_ver >= hos::Version_5_0_0;
|
||||
|
||||
for (s32 i = 0; i < PhysicalAesKeySlotCount; i++) {
|
||||
g_aes_keyslot_cache_entry[i].emplace(i);
|
||||
g_aes_keyslot_cache.AddEntry(std::addressof(g_aes_keyslot_cache_entry[i].value()));
|
||||
}
|
||||
|
||||
util::ConstructAt(g_aes_keyslot_available_event, os::EventClearMode_ManualClear, true);
|
||||
util::GetReference(g_aes_keyslot_available_event).Signal();
|
||||
}
|
||||
|
||||
void WaitOperation() {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
os::WaitInterruptEvent(std::addressof(g_interrupt));
|
||||
#endif
|
||||
}
|
||||
|
||||
smc::Result WaitAndGetResult(smc::AsyncOperationKey op_key) {
|
||||
WaitOperation();
|
||||
|
||||
smc::Result async_res;
|
||||
if (const smc::Result res = smc::GetResult(std::addressof(async_res), op_key); res != smc::Result::Success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return async_res;
|
||||
}
|
||||
|
||||
smc::Result WaitAndGetResultData(void *dst, size_t size, smc::AsyncOperationKey op_key) {
|
||||
WaitOperation();
|
||||
|
||||
smc::Result async_res;
|
||||
if (const smc::Result res = smc::GetResultData(std::addressof(async_res), dst, size, op_key); res != smc::Result::Success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return async_res;
|
||||
}
|
||||
|
||||
smc::Result DecryptAes(void *dst, s32 keyslot, const void *src) {
|
||||
struct DecryptAesLayout {
|
||||
SeCryptContext crypt_ctx;
|
||||
u8 padding[8];
|
||||
u8 in_buffer[crypto::AesEncryptor128::BlockSize];
|
||||
u8 out_buffer[crypto::AesEncryptor128::BlockSize];
|
||||
};
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
auto &layout = *reinterpret_cast<DecryptAesLayout *>(g_work_buffer);
|
||||
|
||||
layout.crypt_ctx.in.num_entries = 0;
|
||||
layout.crypt_ctx.in.address = g_work_buffer_mapped_address + AMS_OFFSETOF(DecryptAesLayout, in_buffer);
|
||||
layout.crypt_ctx.in.size = sizeof(layout.in_buffer);
|
||||
layout.crypt_ctx.out.num_entries = 0;
|
||||
layout.crypt_ctx.out.address = g_work_buffer_mapped_address + AMS_OFFSETOF(DecryptAesLayout, out_buffer);
|
||||
layout.crypt_ctx.out.size = sizeof(layout.out_buffer);
|
||||
|
||||
std::memcpy(layout.in_buffer, src, sizeof(layout.in_buffer));
|
||||
|
||||
os::FlushDataCache(std::addressof(layout), sizeof(layout));
|
||||
{
|
||||
std::scoped_lock lk(g_operation_lock);
|
||||
|
||||
smc::AsyncOperationKey op_key;
|
||||
const IvCtr iv_ctr = {};
|
||||
const u32 mode = smc::GetComputeAesMode(smc::CipherMode::CbcDecrypt, GetPhysicalAesKeySlot(keyslot, true));
|
||||
const u32 dst_ll_addr = g_work_buffer_mapped_address + AMS_OFFSETOF(DecryptAesLayout, crypt_ctx.out);
|
||||
const u32 src_ll_addr = g_work_buffer_mapped_address + AMS_OFFSETOF(DecryptAesLayout, crypt_ctx.in);
|
||||
|
||||
smc::Result res = smc::ComputeAes(std::addressof(op_key), dst_ll_addr, mode, iv_ctr, src_ll_addr, sizeof(layout.out_buffer));
|
||||
if (res != smc::Result::Success) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = WaitAndGetResult(op_key);
|
||||
if (res != smc::Result::Success) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
os::FlushDataCache(std::addressof(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;
|
||||
}
|
||||
|
||||
Result GenerateRandomBytesImpl(void *out, size_t size) {
|
||||
AMS_ASSERT(size <= Drbg::RequestSizeMax);
|
||||
|
||||
if (!util::GetReference(g_drbg).Generate(out, size, nullptr, 0)) {
|
||||
/* We need to reseed. */
|
||||
{
|
||||
u8 seed[Drbg::SeedSize];
|
||||
|
||||
if (smc::Result res = smc::GenerateRandomBytes(seed, sizeof(seed)); res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
|
||||
util::GetReference(g_drbg).Reseed(seed, sizeof(seed), nullptr, 0);
|
||||
}
|
||||
util::GetReference(g_drbg).Generate(out, size, nullptr, 0);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DecryptAndStoreDeviceUniqueKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) {
|
||||
struct DecryptAndStoreDeviceUniqueKeyLayout {
|
||||
u8 data[DeviceUniqueDataMetaDataSize + 2 * Rsa2048BlockSize + 0x10];
|
||||
};
|
||||
auto &layout = *reinterpret_cast<DecryptAndStoreDeviceUniqueKeyLayout *>(g_work_buffer);
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(src_size <= sizeof(DecryptAndStoreDeviceUniqueKeyLayout), spl::ResultInvalidBufferSize());
|
||||
|
||||
std::memcpy(layout.data, src, src_size);
|
||||
|
||||
if (g_is_modern_device_unique_data) {
|
||||
R_RETURN(smc::ConvertResult(smc::DecryptDeviceUniqueData(layout.data, src_size, access_key, key_source, static_cast<smc::DeviceUniqueDataMode>(option))));
|
||||
} else {
|
||||
R_RETURN(smc::ConvertResult(smc::DecryptAndStoreGcKey(layout.data, src_size, access_key, key_source, option)));
|
||||
}
|
||||
}
|
||||
|
||||
Result ModularExponentiateWithStorageKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size, smc::ModularExponentiateWithStorageKeyMode mode) {
|
||||
struct ModularExponentiateWithStorageKeyLayout {
|
||||
u8 base[Rsa2048BlockSize];
|
||||
u8 mod[Rsa2048BlockSize];
|
||||
};
|
||||
auto &layout = *reinterpret_cast<ModularExponentiateWithStorageKeyLayout *>(g_work_buffer);
|
||||
|
||||
/* Validate sizes. */
|
||||
R_UNLESS(base_size <= sizeof(layout.base), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(mod_size <= sizeof(layout.mod), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(out_size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize());
|
||||
|
||||
/* Copy data into work buffer. */
|
||||
const size_t base_ofs = sizeof(layout.base) - base_size;
|
||||
const size_t mod_ofs = sizeof(layout.mod) - mod_size;
|
||||
|
||||
std::memset(layout.base, 0, sizeof(layout.base));
|
||||
std::memset(layout.mod, 0, sizeof(layout.mod));
|
||||
|
||||
std::memcpy(layout.base + base_ofs, base, base_size);
|
||||
std::memcpy(layout.mod + mod_ofs, mod, mod_size);
|
||||
|
||||
/* Do exp mod operation. */
|
||||
{
|
||||
std::scoped_lock lk(g_operation_lock);
|
||||
|
||||
smc::AsyncOperationKey op_key;
|
||||
smc::Result res = smc::ModularExponentiateWithStorageKey(std::addressof(op_key), layout.base, layout.mod, mode);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
|
||||
res = WaitAndGetResultData(g_work_buffer, out_size, op_key);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy result. */
|
||||
if (out != g_work_buffer) {
|
||||
std::memcpy(out, g_work_buffer, out_size);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result PrepareEsDeviceUniqueKey(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, smc::EsDeviceUniqueKeyType type, u32 generation) {
|
||||
struct PrepareEsDeviceUniqueKeyLayout {
|
||||
u8 base[Rsa2048BlockSize];
|
||||
u8 mod[Rsa2048BlockSize];
|
||||
};
|
||||
auto &layout = *reinterpret_cast<PrepareEsDeviceUniqueKeyLayout *>(g_work_buffer);
|
||||
|
||||
/* Validate sizes. */
|
||||
R_UNLESS(base_size <= sizeof(layout.base), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(mod_size <= sizeof(layout.mod), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(label_digest_size <= LabelDigestSizeMax, spl::ResultInvalidBufferSize());
|
||||
|
||||
/* Copy data into work buffer. */
|
||||
const size_t base_ofs = sizeof(layout.base) - base_size;
|
||||
const size_t mod_ofs = sizeof(layout.mod) - mod_size;
|
||||
|
||||
std::memset(layout.base, 0, sizeof(layout.base));
|
||||
std::memset(layout.mod, 0, sizeof(layout.mod));
|
||||
|
||||
std::memcpy(layout.base + base_ofs, base, base_size);
|
||||
std::memcpy(layout.mod + mod_ofs, mod, mod_size);
|
||||
|
||||
/* Do exp mod operation. */
|
||||
{
|
||||
std::scoped_lock lk(g_operation_lock);
|
||||
|
||||
smc::AsyncOperationKey op_key;
|
||||
smc::Result res = smc::PrepareEsDeviceUniqueKey(std::addressof(op_key), layout.base, layout.mod, label_digest, label_digest_size, smc::GetPrepareEsDeviceUniqueKeyOption(type, generation));
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
|
||||
res = WaitAndGetResultData(g_work_buffer, sizeof(*out_access_key), op_key);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(out_access_key, g_work_buffer, sizeof(*out_access_key));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Initialization. */
|
||||
void Initialize() {
|
||||
/* Initialize async operation. */
|
||||
InitializeAsyncOperation();
|
||||
|
||||
/* Initialize device address space for the SE. */
|
||||
InitializeDeviceAddressSpace();
|
||||
|
||||
/* Initialize the Drbg. */
|
||||
InitializeCtrDrbg();
|
||||
|
||||
/* Initialize the keyslot cache. */
|
||||
InitializeKeySlots();
|
||||
}
|
||||
|
||||
/* General. */
|
||||
Result GetConfig(u64 *out, ConfigItem key) {
|
||||
/* Nintendo explicitly blacklists package2 hash, which must be gotten via bespoke api. */
|
||||
R_UNLESS(key != ConfigItem::Package2Hash, spl::ResultInvalidArgument());
|
||||
|
||||
smc::Result res = smc::GetConfig(out, 1, key);
|
||||
|
||||
/* Nintendo has some special handling here for hardware type/hardware state. */
|
||||
if (key == ConfigItem::HardwareType && res == smc::Result::InvalidArgument) {
|
||||
*out = static_cast<u64>(HardwareType::Icosa);
|
||||
res = smc::Result::Success;
|
||||
}
|
||||
if (key == ConfigItem::HardwareState && res == smc::Result::InvalidArgument) {
|
||||
*out = HardwareState_Development;
|
||||
res = smc::Result::Success;
|
||||
}
|
||||
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
|
||||
Result 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) {
|
||||
struct ModularExponentiateLayout {
|
||||
u8 base[Rsa2048BlockSize];
|
||||
u8 exp[Rsa2048BlockSize];
|
||||
u8 mod[Rsa2048BlockSize];
|
||||
};
|
||||
auto &layout = *reinterpret_cast<ModularExponentiateLayout *>(g_work_buffer);
|
||||
|
||||
/* Validate sizes. */
|
||||
R_UNLESS(base_size <= sizeof(layout.base), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(exp_size <= sizeof(layout.exp), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(mod_size <= sizeof(layout.mod), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(out_size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize());
|
||||
|
||||
/* Copy data into work buffer. */
|
||||
const size_t base_ofs = sizeof(layout.base) - base_size;
|
||||
const size_t mod_ofs = sizeof(layout.mod) - mod_size;
|
||||
|
||||
std::memset(layout.base, 0, sizeof(layout.base));
|
||||
std::memset(layout.mod, 0, sizeof(layout.mod));
|
||||
|
||||
std::memcpy(layout.base + base_ofs, base, base_size);
|
||||
std::memcpy(layout.mod + mod_ofs, mod, mod_size);
|
||||
|
||||
std::memcpy(layout.exp, exp, exp_size);
|
||||
|
||||
/* Do exp mod operation. */
|
||||
{
|
||||
std::scoped_lock lk(g_operation_lock);
|
||||
|
||||
smc::AsyncOperationKey op_key;
|
||||
smc::Result res = smc::ModularExponentiate(std::addressof(op_key), layout.base, layout.exp, exp_size, layout.mod);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
|
||||
res = WaitAndGetResultData(g_work_buffer, out_size, op_key);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(out, g_work_buffer, out_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetConfig(ConfigItem key, u64 value) {
|
||||
R_TRY(smc::ConvertResult(smc::SetConfig(key, value)));
|
||||
|
||||
/* Work around for temporary version. */
|
||||
if (key == ConfigItem::ExosphereApiVersion) {
|
||||
hos::InitializeVersionInternal(false);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GenerateRandomBytes(void *out, size_t size) {
|
||||
for (size_t offset = 0; offset < size; offset += Drbg::RequestSizeMax) {
|
||||
R_TRY(GenerateRandomBytesImpl(static_cast<u8 *>(out) + offset, std::min(size - offset, Drbg::RequestSizeMax)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IsDevelopment(bool *out) {
|
||||
u64 hardware_state;
|
||||
R_TRY(impl::GetConfig(std::addressof(hardware_state), ConfigItem::HardwareState));
|
||||
|
||||
*out = (hardware_state == HardwareState_Development);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetBootReason(BootReasonValue boot_reason) {
|
||||
R_UNLESS(!g_is_boot_reason_initialized, spl::ResultBootReasonAlreadyInitialized());
|
||||
|
||||
g_boot_reason = boot_reason;
|
||||
g_is_boot_reason_initialized = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetBootReason(BootReasonValue *out) {
|
||||
R_UNLESS(g_is_boot_reason_initialized, spl::ResultBootReasonNotInitialized());
|
||||
|
||||
*out = g_boot_reason;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Crypto. */
|
||||
Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option) {
|
||||
R_RETURN(smc::ConvertResult(smc::GenerateAesKek(out_access_key, key_source, generation, option)));
|
||||
}
|
||||
|
||||
Result LoadAesKey(s32 keyslot, const AccessKey &access_key, const KeySource &key_source) {
|
||||
/* Ensure we can load into the slot. */
|
||||
const s32 phys_slot = GetPhysicalAesKeySlot(keyslot, false);
|
||||
R_TRY(smc::ConvertResult(smc::LoadAesKey(phys_slot, access_key, key_source)));
|
||||
|
||||
/* Update our contents. */
|
||||
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
|
||||
|
||||
g_aes_keyslot_contents[index].type = AesKeySlotContentType::AesKey;
|
||||
g_aes_keyslot_contents[index].aes_key.access_key = access_key;
|
||||
g_aes_keyslot_contents[index].aes_key.key_source = key_source;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) {
|
||||
ScopedAesKeySlot keyslot_holder;
|
||||
R_TRY(keyslot_holder.Allocate());
|
||||
|
||||
R_TRY(LoadAesKey(keyslot_holder.GetIndex(), access_key, KeyGenerationSource));
|
||||
|
||||
R_RETURN(smc::ConvertResult(DecryptAes(out_key, keyslot_holder.GetIndex(), std::addressof(key_source))));
|
||||
}
|
||||
|
||||
Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) {
|
||||
AccessKey access_key;
|
||||
R_TRY(GenerateAesKek(std::addressof(access_key), AesKeyDecryptionSource, generation, option));
|
||||
|
||||
R_RETURN(GenerateAesKey(out_key, access_key, key_source));
|
||||
}
|
||||
|
||||
Result ComputeCtr(void *dst, size_t dst_size, s32 keyslot, const void *src, size_t src_size, const IvCtr &iv_ctr) {
|
||||
/* Succeed immediately if there's nothing to compute. */
|
||||
R_SUCCEED_IF(src_size == 0);
|
||||
|
||||
/* Validate sizes. */
|
||||
R_UNLESS(src_size <= dst_size, 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. */
|
||||
const uintptr_t src_addr = reinterpret_cast<uintptr_t>(src);
|
||||
const uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
|
||||
|
||||
const uintptr_t src_addr_aligned = util::AlignDown(src_addr, dd::DeviceAddressSpaceMemoryRegionAlignment);
|
||||
const uintptr_t dst_addr_aligned = util::AlignDown(dst_addr, dd::DeviceAddressSpaceMemoryRegionAlignment);
|
||||
|
||||
const size_t src_size_aligned = util::AlignUp(src_addr + src_size, dd::DeviceAddressSpaceMemoryRegionAlignment) - src_addr_aligned;
|
||||
const size_t dst_size_aligned = util::AlignUp(dst_addr + dst_size, dd::DeviceAddressSpaceMemoryRegionAlignment) - dst_addr_aligned;
|
||||
|
||||
const u32 src_se_map_addr = ComputeAesInMapBase + (src_addr_aligned % DeviceAddressSpaceAlign);
|
||||
const u32 dst_se_map_addr = ComputeAesOutMapBase + (dst_addr_aligned % DeviceAddressSpaceAlign);
|
||||
|
||||
const u32 src_se_addr = ComputeAesInMapBase + (src_addr % DeviceAddressSpaceAlign);
|
||||
const u32 dst_se_addr = ComputeAesOutMapBase + (dst_addr % DeviceAddressSpaceAlign);
|
||||
|
||||
/* Validate aligned sizes. */
|
||||
R_UNLESS(src_size_aligned <= ComputeAesSizeMax, spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(dst_size_aligned <= ComputeAesSizeMax, spl::ResultInvalidBufferSize());
|
||||
|
||||
/* Helpers for mapping/unmapping. */
|
||||
DeviceAddressMapper src_mapper(std::addressof(g_device_address_space), src_addr_aligned, src_size_aligned, src_se_map_addr, dd::MemoryPermission_ReadOnly);
|
||||
DeviceAddressMapper dst_mapper(std::addressof(g_device_address_space), dst_addr_aligned, dst_size_aligned, dst_se_map_addr, dd::MemoryPermission_WriteOnly);
|
||||
|
||||
/* Setup SE linked list entries. */
|
||||
auto &crypt_ctx = *reinterpret_cast<SeCryptContext *>(g_work_buffer);
|
||||
crypt_ctx.in.num_entries = 0;
|
||||
crypt_ctx.in.address = src_se_addr;
|
||||
crypt_ctx.in.size = src_size;
|
||||
crypt_ctx.out.num_entries = 0;
|
||||
crypt_ctx.out.address = dst_se_addr;
|
||||
crypt_ctx.out.size = dst_size;
|
||||
|
||||
os::FlushDataCache(std::addressof(crypt_ctx), sizeof(crypt_ctx));
|
||||
os::FlushDataCache(src, src_size);
|
||||
os::FlushDataCache(dst, dst_size);
|
||||
{
|
||||
std::scoped_lock lk(g_operation_lock);
|
||||
|
||||
const u32 mode = smc::GetComputeAesMode(smc::CipherMode::Ctr, GetPhysicalAesKeySlot(keyslot, true));
|
||||
const u32 dst_ll_addr = g_work_buffer_mapped_address + AMS_OFFSETOF(SeCryptContext, out);
|
||||
const u32 src_ll_addr = g_work_buffer_mapped_address + AMS_OFFSETOF(SeCryptContext, in);
|
||||
|
||||
smc::AsyncOperationKey op_key;
|
||||
smc::Result res = smc::ComputeAes(std::addressof(op_key), dst_ll_addr, mode, iv_ctr, src_ll_addr, src_size);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
|
||||
res = WaitAndGetResult(op_key);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
|
||||
res = WaitAndGetResult(op_key);
|
||||
if (res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(res));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *data, size_t size) {
|
||||
R_UNLESS(size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize());
|
||||
|
||||
std::memcpy(g_work_buffer, data, size);
|
||||
R_RETURN(smc::ConvertResult(smc::ComputeCmac(out_cmac, GetPhysicalAesKeySlot(keyslot, true), g_work_buffer, size)));
|
||||
}
|
||||
|
||||
Result AllocateAesKeySlot(s32 *out_keyslot) {
|
||||
/* Find an unused keyslot. */
|
||||
for (s32 i = 0; i < AesKeySlotCount; ++i) {
|
||||
if (!g_is_aes_keyslot_allocated[i]) {
|
||||
g_is_aes_keyslot_allocated[i] = true;
|
||||
g_aes_keyslot_contents[i].type = AesKeySlotContentType::None;
|
||||
*out_keyslot = MakeVirtualAesKeySlot(i);
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
util::GetReference(g_aes_keyslot_available_event).Clear();
|
||||
R_THROW(spl::ResultNoAvailableKeySlot());
|
||||
}
|
||||
|
||||
Result DeallocateAesKeySlot(s32 keyslot) {
|
||||
/* Only virtual keyslots can be freed. */
|
||||
R_UNLESS(IsVirtualAesKeySlot(keyslot), spl::ResultInvalidKeySlot());
|
||||
|
||||
/* Check that the virtual keyslot is allocated. */
|
||||
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
|
||||
R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot());
|
||||
|
||||
/* Clear the physical keyslot, if we're cached. */
|
||||
s32 phys_slot;
|
||||
if (g_aes_keyslot_cache.Release(std::addressof(phys_slot), keyslot)) {
|
||||
ClearPhysicalAesKeySlot(phys_slot);
|
||||
}
|
||||
|
||||
/* Clear the virtual keyslot. */
|
||||
g_aes_keyslot_contents[index].type = AesKeySlotContentType::None;
|
||||
g_is_aes_keyslot_allocated[index] = false;
|
||||
|
||||
util::GetReference(g_aes_keyslot_available_event).Signal();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result TestAesKeySlot(s32 *out_index, bool *out_virtual, s32 keyslot) {
|
||||
if (g_is_physical_keyslot_allowed && IsPhysicalAesKeySlot(keyslot)) {
|
||||
*out_index = keyslot;
|
||||
*out_virtual = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
R_UNLESS(IsVirtualAesKeySlot(keyslot), spl::ResultInvalidKeySlot());
|
||||
|
||||
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
|
||||
R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot());
|
||||
|
||||
*out_index = index;
|
||||
*out_virtual = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
os::SystemEvent *GetAesKeySlotAvailableEvent() {
|
||||
return util::GetPointer(g_aes_keyslot_available_event);
|
||||
}
|
||||
|
||||
/* RSA. */
|
||||
Result DecryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) {
|
||||
struct DecryptDeviceUniqueDataLayout {
|
||||
u8 data[Rsa2048BlockSize + DeviceUniqueDataMetaDataSize];
|
||||
};
|
||||
auto &layout = *reinterpret_cast<DecryptDeviceUniqueDataLayout *>(g_work_buffer);
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(src_size >= DeviceUniqueDataMetaDataSize, spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(src_size <= sizeof(DecryptDeviceUniqueDataLayout), spl::ResultInvalidBufferSize());
|
||||
|
||||
std::memcpy(layout.data, src, src_size);
|
||||
|
||||
smc::Result smc_res;
|
||||
size_t copy_size = 0;
|
||||
if (g_is_modern_device_unique_data) {
|
||||
copy_size = std::min(dst_size, src_size - DeviceUniqueDataMetaDataSize);
|
||||
smc_res = smc::DecryptDeviceUniqueData(layout.data, src_size, access_key, key_source, static_cast<smc::DeviceUniqueDataMode>(option));
|
||||
} else {
|
||||
smc_res = smc::DecryptDeviceUniqueData(std::addressof(copy_size), layout.data, src_size, access_key, key_source, option);
|
||||
copy_size = std::min(dst_size, copy_size);
|
||||
}
|
||||
|
||||
if (smc_res == smc::Result::Success) {
|
||||
std::memcpy(dst, layout.data, copy_size);
|
||||
}
|
||||
|
||||
R_RETURN(smc::ConvertResult(smc_res));
|
||||
}
|
||||
|
||||
/* SSL */
|
||||
Result DecryptAndStoreSslClientCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) {
|
||||
R_RETURN(DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, static_cast<u32>(smc::DeviceUniqueDataMode::DecryptAndStoreSslKey)));
|
||||
}
|
||||
|
||||
Result ModularExponentiateWithSslClientCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) {
|
||||
R_RETURN(ModularExponentiateWithStorageKey(out, out_size, base, base_size, mod, mod_size, smc::ModularExponentiateWithStorageKeyMode::Ssl));
|
||||
}
|
||||
|
||||
/* ES */
|
||||
Result LoadEsDeviceKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) {
|
||||
if (g_is_modern_device_unique_data) {
|
||||
R_RETURN(DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, option));
|
||||
} else {
|
||||
struct LoadEsDeviceKeyLayout {
|
||||
u8 data[DeviceUniqueDataMetaDataSize + 2 * Rsa2048BlockSize + 0x10];
|
||||
};
|
||||
auto &layout = *reinterpret_cast<LoadEsDeviceKeyLayout *>(g_work_buffer);
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(src_size <= sizeof(layout.data), spl::ResultInvalidBufferSize());
|
||||
|
||||
std::memcpy(layout.data, src, src_size);
|
||||
|
||||
R_RETURN(smc::ConvertResult(smc::LoadEsDeviceKey(layout.data, src_size, access_key, key_source, option)));
|
||||
}
|
||||
}
|
||||
|
||||
Result 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(PrepareEsDeviceUniqueKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, smc::EsDeviceUniqueKeyType::TitleKey, generation));
|
||||
}
|
||||
|
||||
Result PrepareCommonEsTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation) {
|
||||
R_RETURN(smc::ConvertResult(smc::PrepareCommonEsTitleKey(out_access_key, key_source, generation)));
|
||||
}
|
||||
|
||||
Result DecryptAndStoreDrmDeviceCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) {
|
||||
R_RETURN(DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, static_cast<u32>(smc::DeviceUniqueDataMode::DecryptAndStoreDrmDeviceCertKey)));
|
||||
}
|
||||
|
||||
Result ModularExponentiateWithDrmDeviceCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) {
|
||||
R_RETURN(ModularExponentiateWithStorageKey(out, out_size, base, base_size, mod, mod_size, smc::ModularExponentiateWithStorageKeyMode::DrmDeviceCert));
|
||||
}
|
||||
|
||||
Result 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(PrepareEsDeviceUniqueKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, smc::EsDeviceUniqueKeyType::ArchiveKey, generation));
|
||||
}
|
||||
|
||||
Result PrepareEsUnknown2Key(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(PrepareEsDeviceUniqueKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, smc::EsDeviceUniqueKeyType::Unknown2, generation));
|
||||
}
|
||||
|
||||
/* FS */
|
||||
Result DecryptAndStoreGcKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) {
|
||||
R_RETURN(DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, option));
|
||||
}
|
||||
|
||||
Result 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) {
|
||||
/* Validate sizes. */
|
||||
R_UNLESS(dst_size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(label_digest_size == LabelDigestSizeMax, spl::ResultInvalidBufferSize());
|
||||
|
||||
/* Nintendo doesn't check this result code, but we will. */
|
||||
R_TRY(ModularExponentiateWithStorageKey(g_work_buffer, Rsa2048BlockSize, base, base_size, mod, mod_size, smc::ModularExponentiateWithStorageKeyMode::Gc));
|
||||
|
||||
const auto data_size = crypto::DecodeRsa2048OaepSha256(dst, dst_size, label_digest, label_digest_size, g_work_buffer, Rsa2048BlockSize);
|
||||
R_UNLESS(data_size > 0, spl::ResultDecryptionFailed());
|
||||
|
||||
*out_size = static_cast<u32>(data_size);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which) {
|
||||
R_RETURN(smc::ConvertResult(smc::GenerateSpecificAesKey(out_key, key_source, generation, which)));
|
||||
}
|
||||
|
||||
Result LoadPreparedAesKey(s32 keyslot, const AccessKey &access_key) {
|
||||
/* Ensure we can load into the slot. */
|
||||
const s32 phys_slot = GetPhysicalAesKeySlot(keyslot, false);
|
||||
R_TRY(smc::ConvertResult(smc::LoadPreparedAesKey(phys_slot, access_key)));
|
||||
|
||||
/* Update our contents. */
|
||||
const s32 index = GetVirtualAesKeySlotIndex(keyslot);
|
||||
|
||||
g_aes_keyslot_contents[index].type = AesKeySlotContentType::PreparedKey;
|
||||
g_aes_keyslot_contents[index].prepared_key.access_key = access_key;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetPackage2Hash(void *dst, const size_t size) {
|
||||
u64 hash[4];
|
||||
R_UNLESS(size >= sizeof(hash), spl::ResultInvalidBufferSize());
|
||||
|
||||
const smc::Result smc_res = smc::GetConfig(hash, 4, ConfigItem::Package2Hash);
|
||||
if (smc_res != smc::Result::Success) {
|
||||
R_RETURN(smc::ConvertResult(smc_res));
|
||||
}
|
||||
|
||||
std::memcpy(dst, hash, sizeof(hash));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Manu. */
|
||||
Result 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) {
|
||||
struct ReencryptDeviceUniqueDataLayout {
|
||||
u8 data[DeviceUniqueDataMetaDataSize + 2 * Rsa2048BlockSize + 0x10];
|
||||
AccessKey access_key_dec;
|
||||
KeySource source_dec;
|
||||
AccessKey access_key_enc;
|
||||
KeySource source_enc;
|
||||
};
|
||||
auto &layout = *reinterpret_cast<ReencryptDeviceUniqueDataLayout *>(g_work_buffer);
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(src_size > DeviceUniqueDataMetaDataSize, spl::ResultInvalidBufferSize());
|
||||
R_UNLESS(src_size <= sizeof(layout.data), spl::ResultInvalidBufferSize());
|
||||
|
||||
std::memcpy(layout.data, src, src_size);
|
||||
layout.access_key_dec = access_key_dec;
|
||||
layout.source_dec = source_dec;
|
||||
layout.access_key_enc = access_key_enc;
|
||||
layout.source_enc = source_enc;
|
||||
|
||||
const smc::Result smc_res = smc::ReencryptDeviceUniqueData(layout.data, src_size, layout.access_key_dec, layout.source_dec, layout.access_key_enc, layout.source_enc, option);
|
||||
if (smc_res == smc::Result::Success) {
|
||||
std::memcpy(dst, layout.data, std::min(dst_size, src_size));
|
||||
}
|
||||
|
||||
R_RETURN(smc::ConvertResult(smc_res));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::spl::impl {
|
||||
|
||||
constexpr inline int BitsPerByte = BITSIZEOF(u8);
|
||||
|
||||
/* Nintendo implements CTR_DRBG for their csrng. We will do the same. */
|
||||
template<typename BlockCipher, size_t KeySize, bool UseDerivation>
|
||||
class CtrDrbg {
|
||||
public:
|
||||
static constexpr int KeyLen = KeySize * BitsPerByte;
|
||||
static constexpr int OutLen = BlockCipher::BlockSize * BitsPerByte;
|
||||
static constexpr int SeedLen = KeyLen + OutLen;
|
||||
static constexpr int MaxNumberOfBitsPerRequest = (1 << 19);
|
||||
static constexpr int ReseedInterval = 0x7FFFFFF0;
|
||||
|
||||
static constexpr size_t OutSize = OutLen / BitsPerByte;
|
||||
static constexpr size_t SeedSize = SeedLen / BitsPerByte;
|
||||
static constexpr size_t RequestSizeMax = MaxNumberOfBitsPerRequest / BitsPerByte;
|
||||
|
||||
static_assert(SeedSize % OutSize == 0);
|
||||
private:
|
||||
class Bcc {
|
||||
private:
|
||||
u8 *m_buffer;
|
||||
const BlockCipher *m_cipher;
|
||||
size_t m_offset;
|
||||
public:
|
||||
Bcc(u8 *buffer, const BlockCipher *cipher) : m_buffer(buffer), m_cipher(cipher), m_offset(0) { /* ... */ }
|
||||
|
||||
void Process(const void *data, size_t size) {
|
||||
const u8 *data_8 = static_cast<const u8 *>(data);
|
||||
size_t remaining = size;
|
||||
|
||||
while (m_offset + remaining >= OutSize) {
|
||||
const size_t xor_size = OutSize - m_offset;
|
||||
|
||||
Xor(m_buffer + m_offset, data_8, xor_size);
|
||||
m_cipher->EncryptBlock(m_buffer, OutSize, m_buffer, OutSize);
|
||||
|
||||
data_8 += xor_size;
|
||||
remaining -= xor_size;
|
||||
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
Xor(m_buffer + m_offset, data_8, remaining);
|
||||
m_offset += remaining;
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (m_offset != 0) {
|
||||
m_cipher->EncryptBlock(m_buffer, OutSize, m_buffer, OutSize);
|
||||
m_offset = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
private:
|
||||
BlockCipher m_block_cipher;
|
||||
u8 m_v[OutSize];
|
||||
u8 m_key[KeySize];
|
||||
u8 m_work1[SeedSize];
|
||||
u8 m_work2[SeedSize];
|
||||
int m_reseed_counter;
|
||||
private:
|
||||
static void Xor(void *dst, const void *src, size_t size) {
|
||||
const u8 *src_u8 = static_cast<const u8 *>(src);
|
||||
u8 *dst_u8 = static_cast<u8 *>(dst);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
dst_u8[i] ^= src_u8[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void Increment(void *v) {
|
||||
u8 *v_8 = static_cast<u8 *>(v);
|
||||
|
||||
for (int i = OutSize - 1; i >= 0; --i) {
|
||||
if ((++v_8[i]) != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
void DeriveSeed(void *seed, const void *a, size_t a_size, const void *b, size_t b_size, const void *c, size_t c_size) {
|
||||
/* Determine sizes. */
|
||||
const u32 in_size = a_size + b_size + c_size;
|
||||
const u32 out_size = SeedSize;
|
||||
|
||||
/* Create header/footer. */
|
||||
u32 header[2];
|
||||
util::StoreBigEndian(header + 0, in_size);
|
||||
util::StoreBigEndian(header + 1, out_size);
|
||||
const u8 footer = 0x80;
|
||||
|
||||
/* Create seed as 000102... */
|
||||
u8 *seed_8 = static_cast<u8 *>(seed);
|
||||
for (size_t i = 0; i < KeySize; ++i) {
|
||||
seed_8[i] = i;
|
||||
}
|
||||
|
||||
/* Initialize block cipher. */
|
||||
m_block_cipher.Initialize(seed_8, KeySize);
|
||||
|
||||
/* Perform derivation. */
|
||||
for (u32 block = 0; block < SeedSize / OutSize; ++block) {
|
||||
/* Create the block index value. */
|
||||
u32 block_value;
|
||||
util::StoreBigEndian(std::addressof(block_value), block);
|
||||
|
||||
/* Get the target block. */
|
||||
u8 *target = seed_8 + block * OutSize;
|
||||
std::memset(target, 0, OutSize);
|
||||
|
||||
/* Create block processor. */
|
||||
Bcc bcc(target, std::addressof(m_block_cipher));
|
||||
|
||||
/* Process block value. */
|
||||
bcc.Process(std::addressof(block_value), sizeof(block_value));
|
||||
bcc.Flush();
|
||||
|
||||
/* Process header/data. */
|
||||
bcc.Process(header, sizeof(header));
|
||||
bcc.Process(a, a_size);
|
||||
bcc.Process(b, b_size);
|
||||
bcc.Process(c, c_size);
|
||||
bcc.Process(footer, std::addressof(footer));
|
||||
bcc.Flush();
|
||||
}
|
||||
|
||||
/* Initialize block cipher. */
|
||||
m_block_cipher.Initialize(seed_8, KeySize);
|
||||
|
||||
/* Encrypt seed. */
|
||||
m_block_cipher.EncryptBlock(seed_8, OutSize, seed_8 + KeySize, OutSize);
|
||||
for (size_t offset = 0; offset < SeedSize - OutSize; offset += OutSize) {
|
||||
m_block_cipher.EncryptBlock(seed_8 + offset + OutSize, OutSize, seed_8 + offset, OutSize);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateStates(void *key, void *v, const void *provided_data) {
|
||||
/* Initialize block cipher. */
|
||||
m_block_cipher.Initialize(key, KeySize);
|
||||
|
||||
/* Update work. */
|
||||
for (size_t offset = 0; offset < SeedSize; offset += OutSize) {
|
||||
Increment(v);
|
||||
m_block_cipher.EncryptBlock(std::addressof(m_work2[offset]), OutSize, v, OutSize);
|
||||
}
|
||||
|
||||
/* Xor work with provided data. */
|
||||
Xor(m_work2, provided_data, SeedSize);
|
||||
|
||||
/* Copy to key/v. */
|
||||
std::memcpy(key, m_work2 + 0, KeySize);
|
||||
std::memcpy(v, m_work2 + KeySize, OutSize);
|
||||
}
|
||||
public:
|
||||
constexpr CtrDrbg() = default;
|
||||
|
||||
void Initialize(const void *entropy, size_t entropy_size, const void *nonce, size_t nonce_size, const void *personalization, size_t personalization_size) {
|
||||
/* Handle init. */
|
||||
if constexpr (UseDerivation) {
|
||||
this->DeriveSeed(m_work1, entropy, entropy_size, nonce, nonce_size, personalization, personalization_size);
|
||||
} else {
|
||||
AMS_ASSERT(entropy_size == SeedSize);
|
||||
AMS_ASSERT(nonce_size == 0);
|
||||
AMS_ASSERT(personalization_size <= SeedSize);
|
||||
AMS_UNUSED(entropy_size, nonce, nonce_size);
|
||||
|
||||
std::memcpy(m_work1, entropy, SeedSize);
|
||||
Xor(m_work1, personalization, personalization_size);
|
||||
}
|
||||
|
||||
/* Clear key/v. */
|
||||
std::memset(m_key, 0, sizeof(m_key));
|
||||
std::memset(m_v, 0, sizeof(m_v));
|
||||
|
||||
/* Set key/v. */
|
||||
this->UpdateStates(m_key, m_v, m_work1);
|
||||
|
||||
/* Set reseed counter. */
|
||||
m_reseed_counter = 1;
|
||||
}
|
||||
|
||||
void Reseed(const void *entropy, size_t entropy_size, const void *addl, size_t addl_size) {
|
||||
/* Handle init. */
|
||||
if constexpr (UseDerivation) {
|
||||
this->DeriveSeed(m_work1, entropy, entropy_size, addl, addl_size, nullptr, 0);
|
||||
} else {
|
||||
AMS_ASSERT(entropy_size == SeedSize);
|
||||
AMS_ASSERT(addl_size <= SeedSize);
|
||||
AMS_UNUSED(entropy_size);
|
||||
|
||||
std::memcpy(m_work1, entropy, SeedSize);
|
||||
Xor(m_work1, addl, addl_size);
|
||||
}
|
||||
|
||||
/* Set key/v. */
|
||||
this->UpdateStates(m_key, m_v, m_work1);
|
||||
|
||||
/* Set reseed counter. */
|
||||
m_reseed_counter = 1;
|
||||
}
|
||||
|
||||
bool Generate(void *out, size_t size, const void *addl, size_t addl_size) {
|
||||
/* Check that the request is small enough. */
|
||||
if (size > RequestSizeMax) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if we need reseed. */
|
||||
if (m_reseed_counter > ReseedInterval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Clear work buffer. */
|
||||
std::memset(m_work1, 0, sizeof(m_work1));
|
||||
|
||||
/* Process additional input, if we have any. */
|
||||
if (addl_size > 0) {
|
||||
if constexpr (UseDerivation) {
|
||||
this->DeriveSeed(m_work1, addl, addl_size, nullptr, 0, nullptr, 0);
|
||||
} else {
|
||||
AMS_ASSERT(addl_size <= SeedSize);
|
||||
std::memcpy(m_work1, addl, addl_size);
|
||||
}
|
||||
|
||||
/* Set key/v. */
|
||||
this->UpdateStates(m_key, m_v, m_work1);
|
||||
}
|
||||
|
||||
/* Get buffer and aligned size. */
|
||||
u8 *out_8 = static_cast<u8 *>(out);
|
||||
const size_t aligned_size = util::AlignDown(size, OutSize);
|
||||
|
||||
/* Generate ctr bytes. */
|
||||
m_block_cipher.Initialize(m_key, KeySize);
|
||||
for (size_t offset = 0; offset < aligned_size; offset += OutSize) {
|
||||
Increment(m_v);
|
||||
m_block_cipher.EncryptBlock(out_8 + offset, OutSize, m_v, OutSize);
|
||||
}
|
||||
|
||||
/* Handle any unaligned data. */
|
||||
if (size > aligned_size) {
|
||||
u8 temp[OutSize];
|
||||
Increment(m_v);
|
||||
m_block_cipher.EncryptBlock(temp, sizeof(temp), m_v, OutSize);
|
||||
std::memcpy(out_8 + aligned_size, temp, size - aligned_size);
|
||||
}
|
||||
|
||||
/* Set key/v. */
|
||||
this->UpdateStates(m_key, m_v, m_work1);
|
||||
|
||||
/* Increment reseed counter. */
|
||||
++m_reseed_counter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::spl::impl {
|
||||
|
||||
class DeviceAddressMapper {
|
||||
private:
|
||||
dd::DeviceAddressSpaceType *m_das;
|
||||
u64 m_process_address;
|
||||
size_t m_size;
|
||||
dd::DeviceVirtualAddress m_device_address;
|
||||
public:
|
||||
DeviceAddressMapper(dd::DeviceAddressSpaceType *das, u64 process_address, size_t size, dd::DeviceVirtualAddress device_address, dd::MemoryPermission permission)
|
||||
: m_das(das), m_process_address(process_address), m_size(size), m_device_address(device_address)
|
||||
{
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
R_ABORT_UNLESS(dd::MapDeviceAddressSpaceAligned(m_das, dd::GetCurrentProcessHandle(), m_process_address, m_size, m_device_address, permission));
|
||||
#else
|
||||
AMS_UNUSED(permission);
|
||||
#endif
|
||||
}
|
||||
|
||||
~DeviceAddressMapper() {
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
dd::UnmapDeviceAddressSpace(m_das, dd::GetCurrentProcessHandle(), m_process_address, m_size, m_device_address);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* 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 <stratosphere.hpp>
|
||||
|
||||
namespace ams::spl::impl {
|
||||
|
||||
class AesKeySlotCacheEntry : public util::IntrusiveListBaseNode<AesKeySlotCacheEntry> {
|
||||
NON_COPYABLE(AesKeySlotCacheEntry);
|
||||
NON_MOVEABLE(AesKeySlotCacheEntry);
|
||||
private:
|
||||
friend class AesKeySlotCache;
|
||||
public:
|
||||
static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize;
|
||||
private:
|
||||
const s32 m_aes_keyslot_index;
|
||||
s32 m_virtual_aes_keyslot;
|
||||
public:
|
||||
explicit AesKeySlotCacheEntry(s32 idx) : m_aes_keyslot_index(idx), m_virtual_aes_keyslot(-1) { /* ... */ }
|
||||
|
||||
bool Contains(s32 virtual_slot) const {
|
||||
return virtual_slot == m_virtual_aes_keyslot;
|
||||
}
|
||||
|
||||
s32 GetPhysicalAesKeySlotIndex() const { return m_aes_keyslot_index; }
|
||||
|
||||
s32 GetVirtualAesKeySlotIndex() const { return m_virtual_aes_keyslot; }
|
||||
|
||||
void SetVirtualAesKeySlot(s32 virtual_slot) {
|
||||
m_virtual_aes_keyslot = virtual_slot;
|
||||
}
|
||||
|
||||
void ClearVirtualAesKeySlot() {
|
||||
m_virtual_aes_keyslot = -1;
|
||||
}
|
||||
};
|
||||
|
||||
class AesKeySlotCache {
|
||||
NON_COPYABLE(AesKeySlotCache);
|
||||
NON_MOVEABLE(AesKeySlotCache);
|
||||
private:
|
||||
using AesKeySlotCacheEntryList = util::IntrusiveListBaseTraits<AesKeySlotCacheEntry>::ListType;
|
||||
private:
|
||||
AesKeySlotCacheEntryList m_mru_list;
|
||||
public:
|
||||
constexpr AesKeySlotCache() : m_mru_list() { /* ... */ }
|
||||
|
||||
s32 Allocate(s32 virtual_slot) {
|
||||
return this->AllocateFromLru(virtual_slot);
|
||||
}
|
||||
|
||||
bool Find(s32 *out, s32 virtual_slot) {
|
||||
for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) {
|
||||
if (it->Contains(virtual_slot)) {
|
||||
*out = it->GetPhysicalAesKeySlotIndex();
|
||||
|
||||
this->UpdateMru(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Release(s32 *out, s32 virtual_slot) {
|
||||
for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) {
|
||||
if (it->Contains(virtual_slot)) {
|
||||
*out = it->GetPhysicalAesKeySlotIndex();
|
||||
it->ClearVirtualAesKeySlot();
|
||||
|
||||
this->UpdateLru(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindPhysical(s32 physical_slot) {
|
||||
for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) {
|
||||
if (it->GetPhysicalAesKeySlotIndex() == physical_slot) {
|
||||
this->UpdateMru(it);
|
||||
|
||||
if (it->GetVirtualAesKeySlotIndex() == physical_slot) {
|
||||
return true;
|
||||
} else {
|
||||
it->SetVirtualAesKeySlot(physical_slot);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
void AddEntry(AesKeySlotCacheEntry *entry) {
|
||||
m_mru_list.push_front(*entry);
|
||||
}
|
||||
private:
|
||||
s32 AllocateFromLru(s32 virtual_slot) {
|
||||
AMS_ASSERT(!m_mru_list.empty());
|
||||
|
||||
auto it = m_mru_list.rbegin();
|
||||
it->SetVirtualAesKeySlot(virtual_slot);
|
||||
|
||||
auto *entry = std::addressof(*it);
|
||||
m_mru_list.pop_back();
|
||||
m_mru_list.push_front(*entry);
|
||||
|
||||
return entry->GetPhysicalAesKeySlotIndex();
|
||||
}
|
||||
|
||||
void UpdateMru(AesKeySlotCacheEntryList::iterator it) {
|
||||
auto *entry = std::addressof(*it);
|
||||
m_mru_list.erase(it);
|
||||
m_mru_list.push_front(*entry);
|
||||
}
|
||||
|
||||
void UpdateLru(AesKeySlotCacheEntryList::iterator it) {
|
||||
auto *entry = std::addressof(*it);
|
||||
m_mru_list.erase(it);
|
||||
m_mru_list.push_back(*entry);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,530 +0,0 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
enum EsCommonKeyType {
|
||||
EsCommonKeyType_TitleKey = 0,
|
||||
EsCommonKeyType_ArchiveKey = 1,
|
||||
EsCommonKeyType_Unknown2 = 2,
|
||||
|
||||
EsCommonKeyType_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 const u8 EsCommonKeySources[EsCommonKeyType_Count][AesKeySize] = {
|
||||
[EsCommonKeyType_TitleKey] = { 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B },
|
||||
[EsCommonKeyType_ArchiveKey] = { 0x3B, 0x78, 0xF2, 0x61, 0x0F, 0x9D, 0x5A, 0xE2, 0x7B, 0x4E, 0x45, 0xAF, 0xCB, 0x0B, 0x67, 0x4D },
|
||||
[EsCommonKeyType_Unknown2] = { 0x42, 0x64, 0x0B, 0xE3, 0x5F, 0xC6, 0xBE, 0x47, 0xC7, 0xB4, 0x84, 0xC5, 0xEB, 0x63, 0xAA, 0x02 },
|
||||
};
|
||||
|
||||
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() {
|
||||
u64 v = -1;
|
||||
crypto::GenerateCryptographicallyRandomBytes(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 DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(dst_size == crypto::AesEncryptor128::KeySize);
|
||||
AMS_ASSERT(src_size == crypto::AesEncryptor128::KeySize);
|
||||
AMS_ASSERT(0 <= type && type < EsCommonKeyType_Count);
|
||||
|
||||
/* Prepare the master key for the generation. */
|
||||
const int slot = g_key_slot_manager.PrepareMasterKey(generation);
|
||||
|
||||
/* Derive the es common key. */
|
||||
g_key_slot_manager.SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, EsCommonKeySources[type], crypto::AesEncryptor128::KeySize);
|
||||
|
||||
/* Decrypt the input using the common key. */
|
||||
g_key_slot_manager.DecryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
crypto::GenerateCryptographicallyRandomBytes(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) {
|
||||
/* Decode arguments. */
|
||||
const int pkg1_gen = std::max<int>(pkg1::KeyGeneration_1_0_0, static_cast<int>(generation) - 1);
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(pkg1_gen < pkg1::KeyGeneration_Max, InvalidArgument);
|
||||
|
||||
/* Derive the key. */
|
||||
u8 key[crypto::AesEncryptor128::KeySize];
|
||||
DecryptWithEsCommonKey(key, sizeof(key), std::addressof(source), sizeof(source), EsCommonKeyType_TitleKey, pkg1_gen);
|
||||
|
||||
/* Copy the access key to the output. */
|
||||
std::memcpy(out, key, sizeof(key));
|
||||
|
||||
return smc::Result::Success;
|
||||
}
|
||||
|
||||
//
|
||||
///* 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.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
/*
|
||||
* 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::smc {
|
||||
|
||||
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) {
|
||||
svc::SecureMonitorArguments args;
|
||||
|
||||
args.r[0] = static_cast<u64>(FunctionId::GetResult);
|
||||
args.r[1] = op.value;
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
*out = static_cast<Result>(args.r[1]);
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
Result GetResultData(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op) {
|
||||
svc::SecureMonitorArguments args;
|
||||
|
||||
args.r[0] = static_cast<u64>(FunctionId::GetResultData);
|
||||
args.r[1] = op.value;
|
||||
args.r[2] = reinterpret_cast<u64>(out_buf);
|
||||
args.r[3] = out_buf_size;
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
*out = static_cast<Result>(args.r[1]);
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
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) {
|
||||
svc::SecureMonitorArguments args;
|
||||
|
||||
args.r[0] = static_cast<u64>(FunctionId::GenerateRandomBytes);
|
||||
args.r[1] = size;
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
if (args.r[0] == static_cast<u64>(Result::Success) && (size <= sizeof(args) - sizeof(args.r[0]))) {
|
||||
std::memcpy(out, std::addressof(args.r[1]), size);
|
||||
}
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option) {
|
||||
svc::SecureMonitorArguments args;
|
||||
|
||||
args.r[0] = static_cast<u64>(FunctionId::GenerateAesKek);
|
||||
args.r[1] = source.data64[0];
|
||||
args.r[2] = source.data64[1];
|
||||
args.r[3] = generation;
|
||||
args.r[4] = option;
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
out->data64[0] = args.r[1];
|
||||
out->data64[1] = args.r[2];
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source) {
|
||||
svc::SecureMonitorArguments args;
|
||||
|
||||
args.r[0] = static_cast<u64>(FunctionId::LoadAesKey);
|
||||
args.r[1] = keyslot;
|
||||
args.r[2] = access_key.data64[0];
|
||||
args.r[3] = access_key.data64[1];
|
||||
args.r[4] = source.data64[0];
|
||||
args.r[5] = source.data64[1];
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
Result ComputeAes(AsyncOperationKey *out_op, u64 dst_addr, u32 mode, const IvCtr &iv_ctr, u64 src_addr, size_t size) {
|
||||
svc::SecureMonitorArguments args;
|
||||
|
||||
args.r[0] = static_cast<u64>(FunctionId::ComputeAes);
|
||||
args.r[1] = mode;
|
||||
args.r[2] = iv_ctr.data64[0];
|
||||
args.r[3] = iv_ctr.data64[1];
|
||||
args.r[4] = static_cast<u32>(src_addr);
|
||||
args.r[5] = static_cast<u32>(dst_addr);
|
||||
args.r[6] = size;
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
out_op->value = args.r[1];
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
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) {
|
||||
svc::SecureMonitorArguments args;
|
||||
|
||||
args.r[0] = static_cast<u64>(FunctionId::LoadPreparedAesKey);
|
||||
args.r[1] = keyslot;
|
||||
args.r[2] = access_key.data64[0];
|
||||
args.r[3] = access_key.data64[1];
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
/* Atmosphere functions. */
|
||||
namespace {
|
||||
|
||||
enum class IramCopyDirection {
|
||||
FromIram = 0,
|
||||
ToIram = 1,
|
||||
};
|
||||
|
||||
inline Result AtmosphereIramCopy(uintptr_t dram_address, uintptr_t iram_address, size_t size, IramCopyDirection direction) {
|
||||
svc::SecureMonitorArguments args;
|
||||
args.r[0] = static_cast<u64>(FunctionId::AtmosphereIramCopy);
|
||||
args.r[1] = dram_address;
|
||||
args.r[2] = iram_address;
|
||||
args.r[3] = size;
|
||||
args.r[4] = static_cast<u64>(direction);
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result AtmosphereCopyToIram(uintptr_t iram_dst, const void *dram_src, size_t size) {
|
||||
return AtmosphereIramCopy(reinterpret_cast<uintptr_t>(dram_src), iram_dst, size, IramCopyDirection::ToIram);
|
||||
}
|
||||
|
||||
Result AtmosphereCopyFromIram(void *dram_dst, uintptr_t iram_src, size_t size) {
|
||||
return AtmosphereIramCopy(reinterpret_cast<uintptr_t>(dram_dst), iram_src, size, IramCopyDirection::FromIram);
|
||||
}
|
||||
|
||||
Result AtmosphereReadWriteRegister(uint64_t address, uint32_t mask, uint32_t value, uint32_t *out_value) {
|
||||
svc::SecureMonitorArguments args;
|
||||
args.r[0] = static_cast<u64>(FunctionId::AtmosphereReadWriteRegister);
|
||||
args.r[1] = address;
|
||||
args.r[2] = mask;
|
||||
args.r[3] = value;
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
*out_value = static_cast<uint32_t>(args.r[1]);
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id) {
|
||||
const u64 paths = reinterpret_cast<u64>(out_paths);
|
||||
AMS_ABORT_UNLESS(util::IsAligned(paths, os::MemoryPageSize));
|
||||
|
||||
svc::SecureMonitorArguments args = {};
|
||||
args.r[0] = static_cast<u64>(FunctionId::AtmosphereGetEmummcConfig);
|
||||
args.r[1] = storage_id;
|
||||
args.r[2] = paths;
|
||||
svc::CallSecureMonitor(std::addressof(args));
|
||||
|
||||
std::memcpy(out_config, std::addressof(args.r[1]), sizeof(args) - sizeof(args.r[0]));
|
||||
return static_cast<Result>(args.r[0]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* 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) {
|
||||
EnsureInitialized();
|
||||
|
||||
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) {
|
||||
EnsureInitialized();
|
||||
|
||||
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) {
|
||||
EnsureInitialized();
|
||||
|
||||
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 DecryptAesKey(void *dst, size_t dst_size, const void *key_source, size_t key_source_size, s32 generation, u32 option) {
|
||||
EnsureInitialized();
|
||||
|
||||
AMS_ASSERT(dst_size >= crypto::AesEncryptor128::KeySize);
|
||||
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||
AMS_UNUSED(dst_size, key_source_size);
|
||||
|
||||
R_RETURN(WaitAvailableKeySlotAndExecute([&]() -> Result {
|
||||
R_RETURN(impl::DecryptAesKey(static_cast<AesKey *>(dst), *static_cast<const KeySource *>(key_source), static_cast<u32>(generation), option));
|
||||
}));
|
||||
}
|
||||
|
||||
Result LoadPreparedAesKey(s32 slot, const AccessKey &access_key) {
|
||||
EnsureInitialized();
|
||||
|
||||
R_RETURN(impl::LoadPreparedAesKey(slot, access_key));
|
||||
}
|
||||
|
||||
Result PrepareCommonEsTitleKey(AccessKey *out, const void *key_source, const size_t key_source_size, int generation) {
|
||||
EnsureInitialized();
|
||||
|
||||
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||
AMS_UNUSED(key_source_size);
|
||||
|
||||
R_RETURN(impl::PrepareCommonEsTitleKey(out, *static_cast<const KeySource *>(key_source), generation));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
enum class InitializeMode {
|
||||
None,
|
||||
General,
|
||||
Crypto,
|
||||
Ssl,
|
||||
Es,
|
||||
Fs,
|
||||
Manu
|
||||
};
|
||||
|
||||
constinit os::SdkMutex g_mutex;
|
||||
constinit s32 g_initialize_count = 0;
|
||||
constinit InitializeMode g_initialize_mode = InitializeMode::None;
|
||||
|
||||
Result AllocateAesKeySlotImpl(s32 *out) {
|
||||
R_RETURN(serviceDispatchOut(splCryptoGetServiceSession(), 21, *out));
|
||||
}
|
||||
|
||||
Result DeallocateAesKeySlotImpl(s32 slot) {
|
||||
R_RETURN(serviceDispatchIn(splCryptoGetServiceSession(), 22, slot));
|
||||
}
|
||||
|
||||
Result GetAesKeySlotAvailableEventImpl(os::NativeHandle *out) {
|
||||
R_RETURN(serviceDispatch(splCryptoGetServiceSession(), 23,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||
.out_handles = out,
|
||||
));
|
||||
}
|
||||
|
||||
void GetAesKeySlotAvailableEvent(os::SystemEvent *out) {
|
||||
/* Get event handle. */
|
||||
os::NativeHandle handle;
|
||||
R_ABORT_UNLESS(GetAesKeySlotAvailableEventImpl(std::addressof(handle)));
|
||||
|
||||
/* Attach to event. */
|
||||
out->AttachReadableHandle(handle, true, os::EventClearMode_ManualClear);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
Result WaitAvailableKeySlotAndExecute(F f) {
|
||||
os::SystemEvent event;
|
||||
auto is_event_initialized = false;
|
||||
while (true) {
|
||||
R_TRY_CATCH(static_cast<::ams::Result>(f())) {
|
||||
R_CATCH(spl::ResultNoAvailableKeySlot) {
|
||||
if (!is_event_initialized) {
|
||||
GetAesKeySlotAvailableEvent(std::addressof(event));
|
||||
is_event_initialized = true;
|
||||
}
|
||||
event.Wait();
|
||||
continue;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void Initialize(InitializeMode mode, F f) {
|
||||
std::scoped_lock lk(g_mutex);
|
||||
|
||||
AMS_ASSERT(g_initialize_count >= 0);
|
||||
AMS_ABORT_UNLESS(mode != InitializeMode::None);
|
||||
|
||||
if (g_initialize_count == 0) {
|
||||
AMS_ABORT_UNLESS(g_initialize_mode == InitializeMode::None);
|
||||
f();
|
||||
g_initialize_mode = mode;
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(g_initialize_mode == mode);
|
||||
}
|
||||
|
||||
++g_initialize_count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
return Initialize(InitializeMode::General, [&]() {
|
||||
R_ABORT_UNLESS(splInitialize());
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeForCrypto() {
|
||||
return Initialize(InitializeMode::Crypto, [&]() {
|
||||
R_ABORT_UNLESS(splCryptoInitialize());
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeForSsl() {
|
||||
return Initialize(InitializeMode::Ssl, [&]() {
|
||||
R_ABORT_UNLESS(splSslInitialize());
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeForEs() {
|
||||
return Initialize(InitializeMode::Es, [&]() {
|
||||
R_ABORT_UNLESS(splEsInitialize());
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeForFs() {
|
||||
return Initialize(InitializeMode::Fs, [&]() {
|
||||
R_ABORT_UNLESS(splFsInitialize());
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeForManu() {
|
||||
return Initialize(InitializeMode::Manu, [&]() {
|
||||
R_ABORT_UNLESS(splManuInitialize());
|
||||
});
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
std::scoped_lock lk(g_mutex);
|
||||
AMS_ASSERT(g_initialize_count > 0);
|
||||
AMS_ABORT_UNLESS(g_initialize_mode != InitializeMode::None);
|
||||
|
||||
if ((--g_initialize_count) == 0) {
|
||||
switch (g_initialize_mode) {
|
||||
case InitializeMode::General: splExit(); break;
|
||||
case InitializeMode::Crypto: splCryptoExit(); break;
|
||||
case InitializeMode::Ssl: splSslExit(); break;
|
||||
case InitializeMode::Es: splEsExit(); break;
|
||||
case InitializeMode::Fs: splFsExit(); break;
|
||||
case InitializeMode::Manu: splManuExit(); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
g_initialize_mode = InitializeMode::None;
|
||||
}
|
||||
}
|
||||
|
||||
Result AllocateAesKeySlot(s32 *out_slot) {
|
||||
R_RETURN(WaitAvailableKeySlotAndExecute([&]() -> Result {
|
||||
R_RETURN(AllocateAesKeySlotImpl(out_slot));
|
||||
}));
|
||||
}
|
||||
|
||||
Result DeallocateAesKeySlot(s32 slot) {
|
||||
R_RETURN(DeallocateAesKeySlotImpl(slot));
|
||||
}
|
||||
|
||||
Result GenerateAesKek(AccessKey *access_key, const void *key_source, size_t key_source_size, s32 generation, u32 option) {
|
||||
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||
AMS_UNUSED(key_source_size);
|
||||
|
||||
R_RETURN(splCryptoGenerateAesKek(key_source, generation, option, static_cast<void *>(access_key)));
|
||||
}
|
||||
|
||||
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(splCryptoLoadAesKey(std::addressof(access_key), key_source, static_cast<u32>(slot)));
|
||||
}
|
||||
|
||||
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 >= crypto::AesEncryptor128::KeySize);
|
||||
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||
AMS_UNUSED(dst_size, key_source_size);
|
||||
|
||||
R_RETURN(WaitAvailableKeySlotAndExecute([&]() -> Result {
|
||||
R_RETURN(splCryptoGenerateAesKey(std::addressof(access_key), key_source, dst));
|
||||
}));
|
||||
}
|
||||
|
||||
Result GenerateSpecificAesKey(void *dst, size_t dst_size, const void *key_source, size_t key_source_size, s32 generation, u32 option) {
|
||||
AMS_ASSERT(dst_size >= crypto::AesEncryptor128::KeySize);
|
||||
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||
AMS_UNUSED(dst_size, key_source_size);
|
||||
|
||||
R_RETURN(splFsGenerateSpecificAesKey(key_source, static_cast<u32>(generation), option, dst));
|
||||
}
|
||||
|
||||
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 >= 0x10);
|
||||
AMS_ASSERT(dst_size >= src_size);
|
||||
AMS_UNUSED(dst_size, iv_size);
|
||||
|
||||
R_RETURN(splCryptoCryptAesCtr(src, dst, src_size, static_cast<s32>(slot), iv));
|
||||
}
|
||||
|
||||
Result DecryptAesKey(void *dst, size_t dst_size, const void *key_source, size_t key_source_size, s32 generation, u32 option) {
|
||||
AMS_ASSERT(dst_size >= crypto::AesEncryptor128::KeySize);
|
||||
AMS_ASSERT(key_source_size == sizeof(KeySource));
|
||||
AMS_UNUSED(dst_size, key_source_size);
|
||||
|
||||
R_RETURN(WaitAvailableKeySlotAndExecute([&]() -> Result {
|
||||
R_RETURN(splCryptoDecryptAesKey(key_source, static_cast<u32>(generation), option, dst));
|
||||
}));
|
||||
}
|
||||
|
||||
Result GetConfig(u64 *out, ConfigItem item) {
|
||||
R_RETURN(splGetConfig(static_cast<::SplConfigItem>(item), out));
|
||||
}
|
||||
|
||||
Result SetConfig(ConfigItem item, u64 v) {
|
||||
R_RETURN(splSetConfig(static_cast<::SplConfigItem>(item), v));
|
||||
}
|
||||
|
||||
bool IsDevelopment() {
|
||||
bool is_dev;
|
||||
R_ABORT_UNLESS(splIsDevelopment(std::addressof(is_dev)));
|
||||
return is_dev;
|
||||
}
|
||||
|
||||
MemoryArrangement GetMemoryArrangement() {
|
||||
u64 mode = 0;
|
||||
R_ABORT_UNLESS(spl::GetConfig(std::addressof(mode), spl::ConfigItem::MemoryMode));
|
||||
switch (mode & 0x3F) {
|
||||
case 2:
|
||||
return MemoryArrangement_StandardForAppletDev;
|
||||
case 3:
|
||||
return MemoryArrangement_StandardForSystemDev;
|
||||
case 17:
|
||||
return MemoryArrangement_Expanded;
|
||||
case 18:
|
||||
return MemoryArrangement_ExpandedForAppletDev;
|
||||
default:
|
||||
return MemoryArrangement_Standard;
|
||||
}
|
||||
}
|
||||
|
||||
Result SetBootReason(BootReasonValue boot_reason) {
|
||||
static_assert(sizeof(boot_reason) == sizeof(u32));
|
||||
|
||||
u32 v;
|
||||
std::memcpy(std::addressof(v), std::addressof(boot_reason), sizeof(v));
|
||||
|
||||
R_RETURN(splSetBootReason(v));
|
||||
}
|
||||
|
||||
Result GetBootReason(BootReasonValue *out) {
|
||||
static_assert(sizeof(*out) == sizeof(u32));
|
||||
|
||||
u32 v;
|
||||
R_TRY(splGetBootReason(std::addressof(v)));
|
||||
|
||||
std::memcpy(out, std::addressof(v), sizeof(*out));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
SocType GetSocType() {
|
||||
switch (GetHardwareType()) {
|
||||
case HardwareType::Icosa:
|
||||
case HardwareType::Copper:
|
||||
return SocType_Erista;
|
||||
case HardwareType::Hoag:
|
||||
case HardwareType::Iowa:
|
||||
case HardwareType::Aula:
|
||||
return SocType_Mariko;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
Result GetPackage2Hash(void *dst, size_t dst_size) {
|
||||
AMS_ASSERT(dst_size >= crypto::Sha256Generator::HashSize);
|
||||
AMS_UNUSED(dst_size);
|
||||
R_RETURN(splFsGetPackage2Hash(dst));
|
||||
}
|
||||
|
||||
Result GenerateRandomBytes(void *out, size_t buffer_size) {
|
||||
R_RETURN(splGetRandomBytes(out, buffer_size));
|
||||
}
|
||||
|
||||
Result LoadPreparedAesKey(s32 slot, const AccessKey &access_key) {
|
||||
if (g_initialize_mode == InitializeMode::Fs) {
|
||||
R_RETURN(splFsLoadTitlekey(std::addressof(access_key), static_cast<u32>(slot)));
|
||||
} else {
|
||||
/* TODO: libnx binding not available. */
|
||||
/* R_RETURN(splEsLoadTitlekey(std::addressof(access_key), static_cast<u32>(slot))); */
|
||||
AMS_ABORT_UNLESS(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user