mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
exo2: implement SmcPrepareEsDeviceUniqueKey, SmcPrepareEsCommonTitleKey, SmcLoadPreparedAesKey
This commit is contained in:
parent
985e97cf78
commit
ccba70abfe
13 changed files with 461 additions and 50 deletions
|
@ -155,6 +155,15 @@ namespace ams::secmon::smc {
|
||||||
0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95
|
0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 EsSealKeySource[AesKeySize] = {
|
||||||
|
0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76
|
||||||
|
};
|
||||||
|
|
||||||
constexpr const u8 SecureDataSource[AesKeySize] = {
|
constexpr const u8 SecureDataSource[AesKeySize] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
};
|
};
|
||||||
|
@ -463,6 +472,51 @@ namespace ams::secmon::smc {
|
||||||
return SmcResult::Success;
|
return SmcResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SmcResult LoadPreparedAesKeyImpl(SmcArguments &args) {
|
||||||
|
/* Decode arguments. */
|
||||||
|
u8 access_key[AesKeySize];
|
||||||
|
|
||||||
|
const int slot = args.r[1];
|
||||||
|
std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key));
|
||||||
|
|
||||||
|
/* Validate arguments. */
|
||||||
|
SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
|
||||||
|
|
||||||
|
/* Derive the seal key. */
|
||||||
|
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource));
|
||||||
|
|
||||||
|
/* Unseal the key. */
|
||||||
|
se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key));
|
||||||
|
|
||||||
|
return SmcResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmcResult PrepareEsCommonTitleKeyImpl(SmcArguments &args) {
|
||||||
|
/* Declare variables. */
|
||||||
|
u8 key_source[se::AesBlockSize];
|
||||||
|
u8 key[se::AesBlockSize];
|
||||||
|
u8 access_key[se::AesBlockSize];
|
||||||
|
|
||||||
|
/* Decode arguments. */
|
||||||
|
std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source));
|
||||||
|
const int generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, static_cast<int>(args.r[3]) - 1) : 0;
|
||||||
|
|
||||||
|
/* Validate arguments. */
|
||||||
|
SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
|
||||||
|
SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument);
|
||||||
|
|
||||||
|
/* Derive the key. */
|
||||||
|
DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), EsCommonKeyType_TitleKey, generation);
|
||||||
|
|
||||||
|
/* Prepare the aes key. */
|
||||||
|
PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key));
|
||||||
|
|
||||||
|
/* Copy the access key to output. */
|
||||||
|
std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key));
|
||||||
|
|
||||||
|
return SmcResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) {
|
SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) {
|
||||||
/* Decode arguments. */
|
/* Decode arguments. */
|
||||||
u8 access_key[se::AesBlockSize];
|
u8 access_key[se::AesBlockSize];
|
||||||
|
@ -530,6 +584,7 @@ namespace ams::secmon::smc {
|
||||||
case DeviceUniqueData_ImportEsClientCertKey:
|
case DeviceUniqueData_ImportEsClientCertKey:
|
||||||
ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
|
ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
|
||||||
ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize);
|
ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize);
|
||||||
|
CommitRsaKeyModulus(ConvertToImportRsaKey(mode));
|
||||||
break;
|
break;
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
}
|
}
|
||||||
|
@ -579,8 +634,11 @@ namespace ams::secmon::smc {
|
||||||
}
|
}
|
||||||
|
|
||||||
SmcResult SmcLoadPreparedAesKey(SmcArguments &args) {
|
SmcResult SmcLoadPreparedAesKey(SmcArguments &args) {
|
||||||
/* TODO */
|
return LockSecurityEngineAndInvoke(args, LoadPreparedAesKeyImpl);
|
||||||
return SmcResult::NotImplemented;
|
}
|
||||||
|
|
||||||
|
SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args) {
|
||||||
|
return LockSecurityEngineAndInvoke(args, PrepareEsCommonTitleKeyImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Device unique data functionality. */
|
/* Device unique data functionality. */
|
||||||
|
@ -604,6 +662,35 @@ namespace ams::secmon::smc {
|
||||||
return SmcResult::NotImplemented;
|
return SmcResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Es encryption utilities. */
|
||||||
|
void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation) {
|
||||||
|
/* Validate pre-conditions. */
|
||||||
|
AMS_ABORT_UNLESS(dst_size == AesKeySize);
|
||||||
|
AMS_ABORT_UNLESS(src_size == AesKeySize);
|
||||||
|
AMS_ABORT_UNLESS(0 <= type && type < EsCommonKeyType_Count);
|
||||||
|
|
||||||
|
/* Prepare the master key for the generation. */
|
||||||
|
const int slot = PrepareMasterKey(generation);
|
||||||
|
|
||||||
|
/* Derive the es common key. */
|
||||||
|
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, EsCommonKeySources[type], AesKeySize);
|
||||||
|
|
||||||
|
/* Decrypt the input using the common key. */
|
||||||
|
se::DecryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
/* Validate pre-conditions. */
|
||||||
|
AMS_ABORT_UNLESS(dst_size == AesKeySize);
|
||||||
|
AMS_ABORT_UNLESS(src_size == AesKeySize);
|
||||||
|
|
||||||
|
/* Derive the seal key. */
|
||||||
|
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource));
|
||||||
|
|
||||||
|
/* Seal the key. */
|
||||||
|
se::EncryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size);
|
||||||
|
}
|
||||||
|
|
||||||
/* 'Tis the last rose of summer, / Left blooming alone; */
|
/* 'Tis the last rose of summer, / Left blooming alone; */
|
||||||
/* Oh! who would inhabit / This bleak world alone? */
|
/* Oh! who would inhabit / This bleak world alone? */
|
||||||
SmcResult SmcGetSecureData(SmcArguments &args) {
|
SmcResult SmcGetSecureData(SmcArguments &args) {
|
||||||
|
|
|
@ -19,6 +19,13 @@
|
||||||
|
|
||||||
namespace ams::secmon::smc {
|
namespace ams::secmon::smc {
|
||||||
|
|
||||||
|
enum EsCommonKeyType {
|
||||||
|
EsCommonKeyType_TitleKey = 0,
|
||||||
|
EsCommonKeyType_ArchiveKey = 1,
|
||||||
|
|
||||||
|
EsCommonKeyType_Count,
|
||||||
|
};
|
||||||
|
|
||||||
/* General Aes functionality. */
|
/* General Aes functionality. */
|
||||||
SmcResult SmcGenerateAesKek(SmcArguments &args);
|
SmcResult SmcGenerateAesKek(SmcArguments &args);
|
||||||
SmcResult SmcLoadAesKey(SmcArguments &args);
|
SmcResult SmcLoadAesKey(SmcArguments &args);
|
||||||
|
@ -26,6 +33,7 @@ namespace ams::secmon::smc {
|
||||||
SmcResult SmcGenerateSpecificAesKey(SmcArguments &args);
|
SmcResult SmcGenerateSpecificAesKey(SmcArguments &args);
|
||||||
SmcResult SmcComputeCmac(SmcArguments &args);
|
SmcResult SmcComputeCmac(SmcArguments &args);
|
||||||
SmcResult SmcLoadPreparedAesKey(SmcArguments &args);
|
SmcResult SmcLoadPreparedAesKey(SmcArguments &args);
|
||||||
|
SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args);
|
||||||
|
|
||||||
/* Device unique data functionality. */
|
/* Device unique data functionality. */
|
||||||
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args);
|
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args);
|
||||||
|
@ -35,6 +43,10 @@ namespace ams::secmon::smc {
|
||||||
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args);
|
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args);
|
||||||
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args);
|
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args);
|
||||||
|
|
||||||
|
/* Es encryption utilities. */
|
||||||
|
void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation);
|
||||||
|
void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||||
|
|
||||||
/* The last rose of summer. */
|
/* The last rose of summer. */
|
||||||
SmcResult SmcGetSecureData(SmcArguments &args);
|
SmcResult SmcGetSecureData(SmcArguments &args);
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 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 <exosphere.hpp>
|
|
||||||
#include "../secmon_error.hpp"
|
|
||||||
#include "secmon_smc_es.hpp"
|
|
||||||
|
|
||||||
namespace ams::secmon::smc {
|
|
||||||
|
|
||||||
SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) {
|
|
||||||
/* TODO */
|
|
||||||
return SmcResult::NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
SmcResult SmcPrepareEsCommonKey(SmcArguments &args) {
|
|
||||||
/* TODO */
|
|
||||||
return SmcResult::NotImplemented;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include "secmon_smc_carveout.hpp"
|
#include "secmon_smc_carveout.hpp"
|
||||||
#include "secmon_smc_device_unique_data.hpp"
|
#include "secmon_smc_device_unique_data.hpp"
|
||||||
#include "secmon_smc_error.hpp"
|
#include "secmon_smc_error.hpp"
|
||||||
#include "secmon_smc_es.hpp"
|
|
||||||
#include "secmon_smc_info.hpp"
|
#include "secmon_smc_info.hpp"
|
||||||
#include "secmon_smc_memory_access.hpp"
|
#include "secmon_smc_memory_access.hpp"
|
||||||
#include "secmon_smc_power_management.hpp"
|
#include "secmon_smc_power_management.hpp"
|
||||||
|
@ -120,7 +119,7 @@ namespace ams::secmon::smc {
|
||||||
{ 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
|
{ 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
|
||||||
{ 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
|
{ 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
|
||||||
{ 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
|
{ 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
|
||||||
{ 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonKey }
|
{ 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey }
|
||||||
};
|
};
|
||||||
|
|
||||||
constinit HandlerInfo g_kern_handlers[] = {
|
constinit HandlerInfo g_kern_handlers[] = {
|
||||||
|
@ -233,8 +232,8 @@ namespace ams::secmon::smc {
|
||||||
|
|
||||||
constinit std::atomic<int> g_logged = 0;
|
constinit std::atomic<int> g_logged = 0;
|
||||||
|
|
||||||
constexpr int LogMin = 0x4000;
|
constexpr int LogMin = 0x1000000;
|
||||||
constexpr int LogMax = 0x4200;
|
constexpr int LogMax = 0x1000000;
|
||||||
|
|
||||||
constexpr size_t LogBufSize = 0x5000;
|
constexpr size_t LogBufSize = 0x5000;
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,158 @@
|
||||||
*/
|
*/
|
||||||
#include <exosphere.hpp>
|
#include <exosphere.hpp>
|
||||||
#include "../secmon_error.hpp"
|
#include "../secmon_error.hpp"
|
||||||
|
#include "../secmon_key_storage.hpp"
|
||||||
|
#include "secmon_smc_aes.hpp"
|
||||||
#include "secmon_smc_rsa.hpp"
|
#include "secmon_smc_rsa.hpp"
|
||||||
|
#include "secmon_smc_se_lock.hpp"
|
||||||
|
#include "secmon_page_mapper.hpp"
|
||||||
|
|
||||||
namespace ams::secmon::smc {
|
namespace ams::secmon::smc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct PrepareEsDeviceUniqueKeyOption {
|
||||||
|
using KeyGeneration = util::BitPack32::Field<0, 6, int>;
|
||||||
|
using Type = util::BitPack32::Field<6, 1, EsCommonKeyType>;
|
||||||
|
using Reserved = util::BitPack32::Field<7, 25, u32>;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrepareEsDeviceUniqueKeyAsyncArguments {
|
||||||
|
private:
|
||||||
|
int generation;
|
||||||
|
EsCommonKeyType type;
|
||||||
|
u8 label_digest[crypto::Sha256Generator::HashSize];
|
||||||
|
public:
|
||||||
|
void Set(int gen, EsCommonKeyType t, const u8 ld[crypto::Sha256Generator::HashSize]) {
|
||||||
|
this->generation = gen;
|
||||||
|
this->type = t;
|
||||||
|
std::memcpy(this->label_digest, ld, sizeof(this->label_digest));
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetKeyGeneration() const { return this->generation; }
|
||||||
|
EsCommonKeyType GetCommonKeyType() const { return this->type; }
|
||||||
|
void GetLabelDigest(u8 dst[crypto::Sha256Generator::HashSize]) const { std::memcpy(dst, this->label_digest, sizeof(this->label_digest)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModularExponentiateByStorageKeyAsyncArguments {
|
||||||
|
private:
|
||||||
|
u8 msg[se::RsaSize];
|
||||||
|
public:
|
||||||
|
void Set(const void *m, size_t m_size) {
|
||||||
|
std::memcpy(this->msg, m, sizeof(this->msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetMessage(void *dst, size_t dst_size) const { std::memcpy(dst, this->msg, sizeof(this->msg)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit bool g_exp_mod_completed = false;
|
||||||
|
|
||||||
|
constinit union {
|
||||||
|
ModularExponentiateByStorageKeyAsyncArguments modular_exponentiate_by_storage_key;
|
||||||
|
PrepareEsDeviceUniqueKeyAsyncArguments prepare_es_device_unique_key;
|
||||||
|
} g_async_arguments;
|
||||||
|
|
||||||
|
ALWAYS_INLINE ModularExponentiateByStorageKeyAsyncArguments &GetModularExponentiateByStorageKeyAsyncArguments() {
|
||||||
|
return g_async_arguments.modular_exponentiate_by_storage_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE PrepareEsDeviceUniqueKeyAsyncArguments &GetPrepareEsDeviceUniqueKeyAsyncArguments() {
|
||||||
|
return g_async_arguments.prepare_es_device_unique_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SecurityEngineDoneHandler() {
|
||||||
|
/* End the asynchronous operation. */
|
||||||
|
g_exp_mod_completed = true;
|
||||||
|
EndAsyncOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
SmcResult PrepareEsDeviceUniqueKeyImpl(SmcArguments &args) {
|
||||||
|
/* Decode arguments. */
|
||||||
|
u8 label_digest[crypto::Sha256Generator::HashSize];
|
||||||
|
|
||||||
|
const uintptr_t msg_address = args.r[1];
|
||||||
|
const uintptr_t mod_address = args.r[2];
|
||||||
|
std::memcpy(label_digest, std::addressof(args.r[3]), sizeof(label_digest));
|
||||||
|
const util::BitPack32 option = { static_cast<u32>(args.r[7]) };
|
||||||
|
|
||||||
|
const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get<PrepareEsDeviceUniqueKeyOption::KeyGeneration>() - 1) : 0;
|
||||||
|
const auto type = option.Get<PrepareEsDeviceUniqueKeyOption::Type>();
|
||||||
|
const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>();
|
||||||
|
|
||||||
|
/* Validate arguments. */
|
||||||
|
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||||
|
SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
|
||||||
|
SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument);
|
||||||
|
SMC_R_UNLESS(type < EsCommonKeyType_Count, InvalidArgument);
|
||||||
|
|
||||||
|
/* Copy the message and modulus from the user. */
|
||||||
|
alignas(8) u8 msg[se::RsaSize];
|
||||||
|
alignas(8) u8 mod[se::RsaSize];
|
||||||
|
{
|
||||||
|
UserPageMapper mapper(msg_address);
|
||||||
|
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||||
|
SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument);
|
||||||
|
SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're performing an operation, so the operation is not completed. */
|
||||||
|
g_exp_mod_completed = false;
|
||||||
|
|
||||||
|
/* Set the async arguments. */
|
||||||
|
GetPrepareEsDeviceUniqueKeyAsyncArguments().Set(generation, type, label_digest);
|
||||||
|
|
||||||
|
/* Load the es drm key into the security engine. */
|
||||||
|
SMC_R_UNLESS(LoadRsaKey(pkg1::RsaKeySlot_Temporary, ImportRsaKey_EsDrmCert), NotInitialized);
|
||||||
|
|
||||||
|
/* Trigger the asynchronous modular exponentiation. */
|
||||||
|
se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler);
|
||||||
|
|
||||||
|
return SmcResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmcResult GetPrepareEsDeviceUniqueKeyResult(void *dst, size_t dst_size) {
|
||||||
|
/* Declare variables. */
|
||||||
|
u8 key_source[se::AesBlockSize];
|
||||||
|
u8 key[se::AesBlockSize];
|
||||||
|
u8 access_key[se::AesBlockSize];
|
||||||
|
|
||||||
|
/* Validate state. */
|
||||||
|
SMC_R_UNLESS(g_exp_mod_completed, Busy);
|
||||||
|
SMC_R_UNLESS(dst_size == sizeof(access_key), InvalidArgument);
|
||||||
|
|
||||||
|
/* We want to relinquish our security engine lock at the end of scope. */
|
||||||
|
ON_SCOPE_EXIT { UnlockSecurityEngine(); };
|
||||||
|
|
||||||
|
/* Get the async args. */
|
||||||
|
const auto &async_args = GetPrepareEsDeviceUniqueKeyAsyncArguments();
|
||||||
|
|
||||||
|
/* Get the exponentiation output. */
|
||||||
|
alignas(8) u8 msg[se::RsaSize];
|
||||||
|
se::GetRsaResult(msg, sizeof(msg));
|
||||||
|
|
||||||
|
/* Decode the key. */
|
||||||
|
{
|
||||||
|
/* Get the label digest. */
|
||||||
|
u8 label_digest[crypto::Sha256Generator::HashSize];
|
||||||
|
async_args.GetLabelDigest(label_digest);
|
||||||
|
|
||||||
|
/* Decode the key source. */
|
||||||
|
const size_t key_source_size = se::DecodeRsaOaepSha256(key_source, sizeof(key_source), msg, sizeof(msg), label_digest, sizeof(label_digest));
|
||||||
|
SMC_R_UNLESS(key_source_size == sizeof(key_source), InvalidArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrypt the key. */
|
||||||
|
DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), async_args.GetCommonKeyType(), async_args.GetKeyGeneration());
|
||||||
|
PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key));
|
||||||
|
|
||||||
|
/* Copy the access key to output. */
|
||||||
|
std::memcpy(dst, access_key, sizeof(access_key));
|
||||||
|
|
||||||
|
return SmcResult::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
SmcResult SmcModularExponentiate(SmcArguments &args) {
|
SmcResult SmcModularExponentiate(SmcArguments &args) {
|
||||||
/* TODO */
|
/* TODO */
|
||||||
return SmcResult::NotImplemented;
|
return SmcResult::NotImplemented;
|
||||||
|
@ -29,4 +177,8 @@ namespace ams::secmon::smc {
|
||||||
return SmcResult::NotImplemented;
|
return SmcResult::NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) {
|
||||||
|
return LockSecurityEngineAndInvokeAsync(args, PrepareEsDeviceUniqueKeyImpl, GetPrepareEsDeviceUniqueKeyResult);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,6 @@ namespace ams::secmon::smc {
|
||||||
SmcResult SmcModularExponentiate(SmcArguments &args);
|
SmcResult SmcModularExponentiate(SmcArguments &args);
|
||||||
SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args);
|
SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args);
|
||||||
|
|
||||||
|
SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <exosphere/se/se_management.hpp>
|
#include <exosphere/se/se_management.hpp>
|
||||||
#include <exosphere/se/se_aes.hpp>
|
#include <exosphere/se/se_aes.hpp>
|
||||||
#include <exosphere/se/se_hash.hpp>
|
#include <exosphere/se/se_hash.hpp>
|
||||||
|
#include <exosphere/se/se_oaep.hpp>
|
||||||
#include <exosphere/se/se_rsa.hpp>
|
#include <exosphere/se/se_rsa.hpp>
|
||||||
#include <exosphere/se/se_rng.hpp>
|
#include <exosphere/se/se_rng.hpp>
|
||||||
#include <exosphere/se/se_suspend.hpp>
|
#include <exosphere/se/se_suspend.hpp>
|
||||||
|
|
|
@ -14,19 +14,10 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <exosphere.hpp>
|
#include <vapours.hpp>
|
||||||
#include "secmon_smc_common.hpp"
|
|
||||||
|
|
||||||
namespace ams::secmon::smc {
|
namespace ams::se {
|
||||||
|
|
||||||
enum EsKeyType {
|
size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size);
|
||||||
EsKeyType_TitleKey = 0,
|
|
||||||
EsKeyType_ArchiveKey = 1,
|
|
||||||
|
|
||||||
EsKeyType_Count = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args);
|
|
||||||
SmcResult SmcPrepareEsCommonKey(SmcArguments &args);
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
|
#include <exosphere/se/se_common.hpp>
|
||||||
|
|
||||||
namespace ams::se {
|
namespace ams::se {
|
||||||
|
|
||||||
|
@ -27,5 +28,8 @@ namespace ams::se {
|
||||||
void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size);
|
void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size);
|
||||||
|
|
||||||
void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size);
|
void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size);
|
||||||
|
void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler);
|
||||||
|
|
||||||
|
void GetRsaResult(void *dst, size_t dst_size);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,25 @@ namespace ams::se {
|
||||||
std::memcpy(dst, aligned, dst_size);
|
std::memcpy(dst, aligned, dst_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size) {
|
||||||
|
/* Set the linked list entry. */
|
||||||
|
LinkedListEntry src_entry;
|
||||||
|
SetLinkedListEntry(std::addressof(src_entry), src, src_size);
|
||||||
|
|
||||||
|
/* Ensure the linked list entry data is seen correctly. */
|
||||||
|
hw::FlushDataCache(std::addressof(src_entry), sizeof(src_entry));
|
||||||
|
hw::DataSynchronizationBarrierInnerShareable();
|
||||||
|
|
||||||
|
/* Configure the linked list addresses. */
|
||||||
|
reg::Write(SE->SE_IN_LL_ADDR, static_cast<u32>(GetPhysicalAddress(std::addressof(src_entry))));
|
||||||
|
|
||||||
|
/* Start the operation. */
|
||||||
|
StartOperation(SE, SE_OPERATION_OP_START);
|
||||||
|
|
||||||
|
/* Ensure the operation is started. */
|
||||||
|
EnsureOperationStarted(SE);
|
||||||
|
}
|
||||||
|
|
||||||
void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address) {
|
void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address) {
|
||||||
/* Configure the linked list addresses. */
|
/* Configure the linked list addresses. */
|
||||||
reg::Write(SE->SE_IN_LL_ADDR, in_ll_address);
|
reg::Write(SE->SE_IN_LL_ADDR, in_ll_address);
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace ams::se {
|
||||||
void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size);
|
void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||||
void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size);
|
void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||||
|
|
||||||
|
void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size);
|
||||||
void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address);
|
void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address);
|
||||||
void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler);
|
void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler);
|
||||||
|
|
||||||
|
|
122
libraries/libexosphere/source/se/se_oaep.cpp
Normal file
122
libraries/libexosphere/source/se/se_oaep.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 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 <exosphere.hpp>
|
||||||
|
#include "se_execute.hpp"
|
||||||
|
|
||||||
|
namespace ams::se {
|
||||||
|
|
||||||
|
/* NOTE: This implementation is mostly copy/pasted from crypto::impl::RsaOaepImpl. */
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline size_t HashSize = sizeof(Sha256Hash);
|
||||||
|
|
||||||
|
constexpr inline u8 HeadMagic = 0x00;
|
||||||
|
|
||||||
|
void ApplyMGF1(u8 *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||||
|
/* Check our pre-conditions. */
|
||||||
|
AMS_ABORT_UNLESS(src_size <= RsaSize - (1 + HashSize));
|
||||||
|
|
||||||
|
/* Create a buffer. */
|
||||||
|
util::AlignedBuffer<hw::DataCacheLineSize, RsaSize - (1 + HashSize) + sizeof(u32)> buf;
|
||||||
|
u32 counter = 0;
|
||||||
|
|
||||||
|
while (dst_size > 0) {
|
||||||
|
/* Setup the current hash buffer. */
|
||||||
|
const size_t cur_size = std::min(HashSize, dst_size);
|
||||||
|
std::memcpy(static_cast<u8 *>(buf), src, src_size);
|
||||||
|
{
|
||||||
|
u32 counter_be;
|
||||||
|
util::StoreBigEndian(std::addressof(counter_be), counter++);
|
||||||
|
std::memcpy(static_cast<u8 *>(buf) + src_size, std::addressof(counter_be), sizeof(counter_be));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure se sees correct data. */
|
||||||
|
hw::FlushDataCache(buf, src_size + sizeof(u32));
|
||||||
|
hw::DataSynchronizationBarrierInnerShareable();
|
||||||
|
|
||||||
|
/* Calculate the hash. */
|
||||||
|
Sha256Hash hash;
|
||||||
|
se::CalculateSha256(std::addressof(hash), buf, src_size + sizeof(u32));
|
||||||
|
|
||||||
|
/* Mask the current output. */
|
||||||
|
const u8 *mask = hash.bytes;
|
||||||
|
for (size_t i = 0; i < cur_size; ++i) {
|
||||||
|
*(dst++) ^= *(mask++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance. */
|
||||||
|
dst_size -= cur_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size) {
|
||||||
|
/* Check our preconditions. */
|
||||||
|
AMS_ABORT_UNLESS(src_size == RsaSize);
|
||||||
|
AMS_ABORT_UNLESS(label_digest_size == HashSize);
|
||||||
|
|
||||||
|
/* Get a byte-readable copy of the input. */
|
||||||
|
u8 *buf = static_cast<u8 *>(src);
|
||||||
|
|
||||||
|
/* Validate sanity byte. */
|
||||||
|
bool is_valid = buf[0] == HeadMagic;
|
||||||
|
|
||||||
|
/* Decrypt seed and masked db. */
|
||||||
|
size_t db_len = src_size - HashSize - 1;
|
||||||
|
u8 *seed = buf + 1;
|
||||||
|
u8 *db = seed + HashSize;
|
||||||
|
ApplyMGF1(seed, HashSize, db, db_len);
|
||||||
|
ApplyMGF1(db, db_len, seed, HashSize);
|
||||||
|
|
||||||
|
/* Check the label digest. */
|
||||||
|
is_valid &= crypto::IsSameBytes(label_digest, db, HashSize);
|
||||||
|
|
||||||
|
/* Skip past the label digest. */
|
||||||
|
db += HashSize;
|
||||||
|
db_len -= HashSize;
|
||||||
|
|
||||||
|
/* Verify that DB is of the form 0000...0001 < message > */
|
||||||
|
s32 msg_ofs = 0;
|
||||||
|
{
|
||||||
|
int looking_for_one = 1;
|
||||||
|
int invalid_db_padding = 0;
|
||||||
|
int is_zero;
|
||||||
|
int is_one;
|
||||||
|
for (size_t i = 0; i < db_len; /* ... */) {
|
||||||
|
is_zero = (db[i] == 0);
|
||||||
|
is_one = (db[i] == 1);
|
||||||
|
msg_ofs += (looking_for_one & is_one) * (static_cast<s32>(++i));
|
||||||
|
looking_for_one &= ~is_one;
|
||||||
|
invalid_db_padding |= (looking_for_one & ~is_zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid &= (invalid_db_padding == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're invalid, return zero size. */
|
||||||
|
const size_t valid_msg_size = db_len - msg_ofs;
|
||||||
|
const size_t msg_size = std::min(dst_size, static_cast<size_t>(is_valid) * valid_msg_size);
|
||||||
|
|
||||||
|
/* Copy to output. */
|
||||||
|
std::memcpy(dst, db + msg_ofs, msg_size);
|
||||||
|
|
||||||
|
/* Return copied size. */
|
||||||
|
return msg_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -67,6 +67,10 @@ namespace ams::se {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WaitForInputReadComplete(volatile SecurityEngineRegisters *SE) {
|
||||||
|
while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_IN_DONE, CLEAR))) { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearRsaKeySlot(int slot) {
|
void ClearRsaKeySlot(int slot) {
|
||||||
|
@ -174,4 +178,53 @@ namespace ams::se {
|
||||||
GetRsaResult(SE, dst, dst_size);
|
GetRsaResult(SE, dst, dst_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler) {
|
||||||
|
/* Validate the slot and size. */
|
||||||
|
AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount);
|
||||||
|
AMS_ABORT_UNLESS(src_size <= RsaSize);
|
||||||
|
|
||||||
|
/* Get the engine. */
|
||||||
|
auto *SE = GetRegisters();
|
||||||
|
|
||||||
|
/* Create a work buffer. */
|
||||||
|
u8 work[RsaSize];
|
||||||
|
util::ClearMemory(work, sizeof(work));
|
||||||
|
|
||||||
|
/* Copy the input into the work buffer (reversing endianness). */
|
||||||
|
const u8 *src_u8 = static_cast<const u8 *>(src);
|
||||||
|
for (size_t i = 0; i < src_size; ++i) {
|
||||||
|
work[src_size - 1 - i] = src_u8[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush the work buffer to ensure the SE sees correct results. */
|
||||||
|
hw::FlushDataCache(work, sizeof(work));
|
||||||
|
hw::DataSynchronizationBarrierInnerShareable();
|
||||||
|
|
||||||
|
/* Configure the engine to perform RSA encryption. */
|
||||||
|
reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128),
|
||||||
|
SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128),
|
||||||
|
SE_REG_BITS_ENUM(CONFIG_ENC_ALG, RSA),
|
||||||
|
SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP),
|
||||||
|
SE_REG_BITS_ENUM(CONFIG_DST, RSA_REG));
|
||||||
|
|
||||||
|
/* Configure the engine to use the keyslot and correct modulus/exp sizes. */
|
||||||
|
const auto &info = g_rsa_key_infos[slot];
|
||||||
|
reg::Write(SE->SE_RSA_CONFIG, SE_REG_BITS_VALUE(RSA_CONFIG_KEY_SLOT, slot));
|
||||||
|
reg::Write(SE->SE_RSA_KEY_SIZE, info.modulus_size_val);
|
||||||
|
reg::Write(SE->SE_RSA_EXP_SIZE, info.exponent_size_val);
|
||||||
|
|
||||||
|
/* Set the done handler. */
|
||||||
|
SetDoneHandler(SE, handler);
|
||||||
|
|
||||||
|
/* Trigger the input operation. */
|
||||||
|
StartInputOperation(SE, work, src_size);
|
||||||
|
|
||||||
|
/* Wait for input to be read by the se. */
|
||||||
|
WaitForInputReadComplete(SE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetRsaResult(void *dst, size_t dst_size) {
|
||||||
|
GetRsaResult(GetRegisters(), dst, dst_size);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue