mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
exo2: Implement (untested) SmcDecryptDeviceUniqueData
This commit is contained in:
parent
4fe42eb997
commit
91e0bbd9d7
14 changed files with 925 additions and 46 deletions
|
@ -18,9 +18,11 @@
|
|||
|
||||
namespace ams::secmon {
|
||||
|
||||
/* NOTE: Lotus and EsDrmCert are switched here versus official enum, */
|
||||
/* however, this considerably simplifies logic. */
|
||||
enum ImportRsaKey {
|
||||
ImportRsaKey_EsDrmCert = 0,
|
||||
ImportRsaKey_Lotus = 1,
|
||||
ImportRsaKey_Lotus = 0,
|
||||
ImportRsaKey_EsDrmCert = 1,
|
||||
ImportRsaKey_Ssl = 2,
|
||||
ImportRsaKey_EsClientCert = 3,
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "../secmon_key_storage.hpp"
|
||||
#include "../secmon_misc.hpp"
|
||||
#include "secmon_smc_aes.hpp"
|
||||
#include "secmon_smc_device_unique_data.hpp"
|
||||
#include "secmon_smc_se_lock.hpp"
|
||||
#include "secmon_user_page_mapper.hpp"
|
||||
|
||||
|
@ -25,17 +26,19 @@ namespace ams::secmon::smc {
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr inline auto AesKeySize = se::AesBlockSize;
|
||||
constexpr inline size_t CmacSizeMax = 1_KB;
|
||||
constexpr inline auto AesKeySize = se::AesBlockSize;
|
||||
constexpr inline size_t CmacSizeMax = 1_KB;
|
||||
constexpr inline size_t DeviceUniqueDataSizeMin = 0x130;
|
||||
constexpr inline size_t DeviceUniqueDataSizeMax = 0x240;
|
||||
|
||||
enum SealKey {
|
||||
SealKey_LoadAesKey = 0,
|
||||
SealKey_DecryptDeviceUniqueData = 1,
|
||||
SealKey_LoadLotusKey = 2,
|
||||
SealKey_LoadEsDeviceKey = 3,
|
||||
SealKey_ImportLotusKey = 2,
|
||||
SealKey_ImportEsDeviceKey = 3,
|
||||
SealKey_ReencryptDeviceUniqueData = 4,
|
||||
SealKey_LoadSslKey = 5,
|
||||
SealKey_LoadEsClientCertKey = 6,
|
||||
SealKey_ImportSslKey = 5,
|
||||
SealKey_ImportEsClientCertKey = 6,
|
||||
|
||||
SealKey_Count,
|
||||
};
|
||||
|
@ -63,6 +66,30 @@ namespace ams::secmon::smc {
|
|||
SpecificAesKey_Count,
|
||||
};
|
||||
|
||||
enum DeviceUniqueData {
|
||||
DeviceUniqueData_DecryptDeviceUniqueData = 0,
|
||||
DeviceUniqueData_ImportLotusKey = 1,
|
||||
DeviceUniqueData_ImportEsDeviceKey = 2,
|
||||
DeviceUniqueData_ImportSslKey = 3,
|
||||
DeviceUniqueData_ImportEsClientCertKey = 4,
|
||||
|
||||
DeviceUniqueData_Count,
|
||||
};
|
||||
|
||||
/* Ensure that our "subtract one" simplification is valid for the cases we care about. */
|
||||
static_assert(DeviceUniqueData_ImportLotusKey - 1 == ImportRsaKey_Lotus);
|
||||
static_assert(DeviceUniqueData_ImportEsDeviceKey - 1 == ImportRsaKey_EsDrmCert);
|
||||
static_assert(DeviceUniqueData_ImportSslKey - 1 == ImportRsaKey_Ssl);
|
||||
static_assert(DeviceUniqueData_ImportEsClientCertKey - 1 == ImportRsaKey_EsClientCert);
|
||||
|
||||
constexpr ImportRsaKey ConvertToImportRsaKey(DeviceUniqueData data) {
|
||||
/* Not necessary, but if this is invoked at compile-time this will force a compile-time error. */
|
||||
AMS_ASSUME(data != DeviceUniqueData_DecryptDeviceUniqueData);
|
||||
AMS_ASSUME(data < DeviceUniqueData_Count);
|
||||
|
||||
return static_cast<ImportRsaKey>(static_cast<int>(data) - 1);
|
||||
}
|
||||
|
||||
enum SecureData {
|
||||
SecureData_Calibration = 0,
|
||||
SecureData_SafeMode = 1,
|
||||
|
@ -84,14 +111,19 @@ namespace ams::secmon::smc {
|
|||
using CipherModeIndex = util::BitPack32::Field<4, 2, CipherMode>;
|
||||
};
|
||||
|
||||
struct DecryptDeviceUniqueDataOption {
|
||||
using DeviceUniqueDataIndex = util::BitPack32::Field<0, 3, DeviceUniqueData>;
|
||||
using Reserved = util::BitPack32::Field<3, 29, u32>;
|
||||
};
|
||||
|
||||
constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = {
|
||||
[SealKey_LoadAesKey] = { 0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6 },
|
||||
[SealKey_DecryptDeviceUniqueData] = { 0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06 },
|
||||
[SealKey_LoadLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 },
|
||||
[SealKey_LoadEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D },
|
||||
[SealKey_ImportLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 },
|
||||
[SealKey_ImportEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D },
|
||||
[SealKey_ReencryptDeviceUniqueData] = { 0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE },
|
||||
[SealKey_LoadSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 },
|
||||
[SealKey_LoadEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B },
|
||||
[SealKey_ImportSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 },
|
||||
[SealKey_ImportEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B },
|
||||
};
|
||||
|
||||
constexpr const u8 KeyTypeSources[KeyType_Count][AesKeySize] = {
|
||||
|
@ -104,11 +136,19 @@ namespace ams::secmon::smc {
|
|||
constexpr const u8 SealKeyMasks[SealKey_Count][AesKeySize] = {
|
||||
[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_LoadLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C },
|
||||
[SealKey_LoadEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB },
|
||||
[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_LoadSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 },
|
||||
[SealKey_LoadEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 },
|
||||
[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 SealKey DeviceUniqueDataToSealKey[DeviceUniqueData_Count] = {
|
||||
[DeviceUniqueData_DecryptDeviceUniqueData] = SealKey_DecryptDeviceUniqueData,
|
||||
[DeviceUniqueData_ImportLotusKey] = SealKey_ImportLotusKey,
|
||||
[DeviceUniqueData_ImportEsDeviceKey] = SealKey_ImportEsDeviceKey,
|
||||
[DeviceUniqueData_ImportSslKey] = SealKey_ImportSslKey,
|
||||
[DeviceUniqueData_ImportEsClientCertKey] = SealKey_ImportEsClientCertKey,
|
||||
};
|
||||
|
||||
constexpr const u8 CalibrationKeySource[AesKeySize] = {
|
||||
|
@ -392,7 +432,7 @@ namespace ams::secmon::smc {
|
|||
/* Decode arguments. */
|
||||
const int slot = args.r[1];
|
||||
const uintptr_t data_address = args.r[2];
|
||||
const uintptr_t data_size = args.r[3];
|
||||
const size_t data_size = args.r[3];
|
||||
|
||||
/* Declare buffer for user data. */
|
||||
alignas(8) u8 user_data[CmacSizeMax];
|
||||
|
@ -423,6 +463,81 @@ namespace ams::secmon::smc {
|
|||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 access_key[se::AesBlockSize];
|
||||
u8 key_source[se::AesBlockSize];
|
||||
|
||||
std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key));
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[3]) };
|
||||
const uintptr_t data_address = args.r[4];
|
||||
const size_t data_size = args.r[5];
|
||||
std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source));
|
||||
|
||||
const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>();
|
||||
const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||
switch (mode) {
|
||||
case DeviceUniqueData_DecryptDeviceUniqueData:
|
||||
{
|
||||
SMC_R_UNLESS(data_size < DeviceUniqueDataSizeMax, InvalidArgument);
|
||||
}
|
||||
break;
|
||||
case DeviceUniqueData_ImportLotusKey:
|
||||
case DeviceUniqueData_ImportEsDeviceKey:
|
||||
case DeviceUniqueData_ImportSslKey:
|
||||
case DeviceUniqueData_ImportEsClientCertKey:
|
||||
{
|
||||
SMC_R_UNLESS(DeviceUniqueDataSizeMin <= data_size && data_size <= DeviceUniqueDataSizeMax, InvalidArgument);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
/* Decrypt the device unique data. */
|
||||
u8 work_buffer[DeviceUniqueDataSizeMax];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); };
|
||||
{
|
||||
/* Map and copy in the encrypted data. */
|
||||
UserPageMapper mapper(data_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(work_buffer, data_address, data_size), InvalidArgument);
|
||||
|
||||
/* Determine the seal key to use. */
|
||||
const auto seal_key_type = DeviceUniqueDataToSealKey[mode];
|
||||
const u8 * const seal_key_source = SealKeySources[seal_key_type];
|
||||
|
||||
/* Decrypt the data. */
|
||||
if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, sizeof(access_key), key_source, sizeof(key_source), work_buffer, data_size)) {
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
/* Either output the key, or import it. */
|
||||
switch (mode) {
|
||||
case DeviceUniqueData_DecryptDeviceUniqueData:
|
||||
{
|
||||
SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument);
|
||||
}
|
||||
break;
|
||||
case DeviceUniqueData_ImportLotusKey:
|
||||
case DeviceUniqueData_ImportSslKey:
|
||||
ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
|
||||
break;
|
||||
case DeviceUniqueData_ImportEsDeviceKey:
|
||||
case DeviceUniqueData_ImportEsClientCertKey:
|
||||
ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
|
||||
ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult GetSecureDataImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const auto which = static_cast<SecureData>(args.r[1]);
|
||||
|
@ -442,6 +557,7 @@ namespace ams::secmon::smc {
|
|||
|
||||
}
|
||||
|
||||
/* Aes functionality. */
|
||||
SmcResult SmcGenerateAesKek(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, GenerateAesKekImpl);
|
||||
}
|
||||
|
@ -467,6 +583,27 @@ namespace ams::secmon::smc {
|
|||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
/* Device unique data functionality. */
|
||||
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, DecryptDeviceUniqueDataImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
/* Legacy APIs. */
|
||||
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
/* 'Tis the last rose of summer, / Left blooming alone; */
|
||||
/* Oh! who would inhabit / This bleak world alone? */
|
||||
SmcResult SmcGetSecureData(SmcArguments &args) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
/* General Aes functionality. */
|
||||
SmcResult SmcGenerateAesKek(SmcArguments &args);
|
||||
SmcResult SmcLoadAesKey(SmcArguments &args);
|
||||
SmcResult SmcComputeAes(SmcArguments &args);
|
||||
|
@ -26,6 +27,15 @@ namespace ams::secmon::smc {
|
|||
SmcResult SmcComputeCmac(SmcArguments &args);
|
||||
SmcResult SmcLoadPreparedAesKey(SmcArguments &args);
|
||||
|
||||
/* Device unique data functionality. */
|
||||
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args);
|
||||
SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args);
|
||||
|
||||
/* Legacy device unique data functionality. */
|
||||
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args);
|
||||
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args);
|
||||
|
||||
/* The last rose of summer. */
|
||||
SmcResult SmcGetSecureData(SmcArguments &args);
|
||||
|
||||
}
|
||||
|
|
|
@ -19,25 +19,113 @@
|
|||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
namespace {
|
||||
|
||||
constexpr inline size_t DeviceUniqueDataIvSize = se::AesBlockSize;
|
||||
constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize;
|
||||
constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64);
|
||||
constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize;
|
||||
|
||||
constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize;
|
||||
constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataMacSize + DeviceUniqueDataDeviceIdSize + DeviceUniqueDataPaddingSize;
|
||||
constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize;
|
||||
|
||||
void PrepareDeviceUniqueDataKey(const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size) {
|
||||
/* Derive the seal key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, seal_key_source_size);
|
||||
|
||||
/* Derive the device unique data kek. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, access_key_size);
|
||||
|
||||
/* Derive the actual device unique data key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, key_source, key_source_size);
|
||||
}
|
||||
|
||||
void ComputeGmac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *iv, size_t iv_size) {
|
||||
/* Declare keyslot (as encryptor will need to take it by pointer/reference). */
|
||||
constexpr int Slot = pkg1::AesKeySlot_Smc;
|
||||
|
||||
/* Calculate the mac. */
|
||||
crypto::Aes128GcmEncryptor gcm;
|
||||
gcm.Initialize(std::addressof(Slot), sizeof(Slot), iv, iv_size);
|
||||
gcm.UpdateAad(data, data_size);
|
||||
gcm.GetMac(dst, dst_size);
|
||||
}
|
||||
|
||||
constexpr u64 GetDeviceIdLow(u64 device_id) {
|
||||
/* Mask out the top byte. */
|
||||
constexpr u64 ByteMask = (static_cast<u64>(1) << BITSIZEOF(u8)) - 1;
|
||||
constexpr u64 LowMask = ~(ByteMask << (BITSIZEOF(u64) - BITSIZEOF(u8)));
|
||||
return device_id & LowMask;
|
||||
}
|
||||
|
||||
constexpr u8 GetDeviceIdHigh(u64 device_id) {
|
||||
/* Get the top byte. */
|
||||
return static_cast<u8>(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) {
|
||||
/* Determine how much decrypted data there will be. */
|
||||
const size_t enc_size = src_size - DeviceUniqueDataInnerMetaSize;
|
||||
const size_t dec_size = src_size - DeviceUniqueDataOuterMetaSize;
|
||||
|
||||
/* Legacy APIs. */
|
||||
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
/* Ensure that our sizes are allowed. */
|
||||
AMS_ABORT_UNLESS(src_size > DeviceUniqueDataTotalMetaSize);
|
||||
AMS_ABORT_UNLESS(dst_size >= enc_size);
|
||||
|
||||
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
/* Determine the extents of the data. */
|
||||
const u8 * const iv = static_cast<const u8 *>(src);
|
||||
const u8 * const enc = iv + DeviceUniqueDataIvSize;
|
||||
const u8 * const mac = enc + enc_size;
|
||||
|
||||
/* Decrypt the data. */
|
||||
{
|
||||
/* Declare temporaries. */
|
||||
u8 temp_iv[DeviceUniqueDataIvSize];
|
||||
u8 calc_mac[DeviceUniqueDataMacSize];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(temp_iv, sizeof(temp_iv)); crypto::ClearMemory(calc_mac, sizeof(calc_mac)); };
|
||||
|
||||
/* Prepare the key used to decrypt the data. */
|
||||
PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size);
|
||||
|
||||
/* Copy the iv to stack. */
|
||||
std::memcpy(temp_iv, iv, sizeof(temp_iv));
|
||||
|
||||
/* Decrypt the data. */
|
||||
se::ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize);
|
||||
|
||||
/* Compute the gmac. */
|
||||
ComputeGmac(calc_mac, DeviceUniqueDataMacSize, dst, enc_size, temp_iv, DeviceUniqueDataIvSize);
|
||||
|
||||
/* Validate the gmac. */
|
||||
if (!crypto::IsSameBytes(mac, calc_mac, sizeof(calc_mac))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate device id, output device id if needed. */
|
||||
{
|
||||
/* Locate the device id in the decryption output. */
|
||||
const u8 * const padding = static_cast<const u8 *>(dst) + dec_size;
|
||||
const u8 * const device_id = padding + DeviceUniqueDataPaddingSize;
|
||||
|
||||
/* Load the big endian device id. */
|
||||
const u64 device_id_val = util::LoadBigEndian(static_cast<const u64 *>(static_cast<const void *>(device_id)));
|
||||
|
||||
/* Validate that the device id low matches the value in fuses. */
|
||||
if (GetDeviceIdLow(device_id_val) != fuse::GetDeviceId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the output device id high, if needed. */
|
||||
if (out_device_id_high != nullptr) {
|
||||
*out_device_id_high = GetDeviceIdHigh(device_id_val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,11 +19,6 @@
|
|||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args);
|
||||
SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args);
|
||||
|
||||
/* Legacy APIs. */
|
||||
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args);
|
||||
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args);
|
||||
bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsSupportedKeySize(size_t size) {
|
||||
return size == 16 || size == 24 || size == 32;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
AesImpl<KeySize>::~AesImpl() {
|
||||
ClearMemory(this, sizeof(*this));
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::Initialize(const void *key, size_t key_size, bool is_encrypt) {
|
||||
static_assert(IsSupportedKeySize(KeySize));
|
||||
|
||||
/* Set the security engine keyslot. */
|
||||
this->slot = *static_cast<const int *>(key);
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
static_assert(IsSupportedKeySize(KeySize));
|
||||
AMS_ASSERT(src_size >= BlockSize);
|
||||
AMS_ASSERT(dst_size >= BlockSize);
|
||||
|
||||
if constexpr (KeySize == 16) {
|
||||
/* Aes 128. */
|
||||
se::EncryptAes128(dst, dst_size, this->slot, src, src_size);
|
||||
} else if constexpr (KeySize == 24) {
|
||||
/* Aes 192. */
|
||||
/* TODO: se::EncryptAes192(dst, dst_size, this->slot, src, src_size); */
|
||||
} else if constexpr (KeySize == 32) {
|
||||
/* Aes 256. */
|
||||
/* TODO: se::EncryptAes256(dst, dst_size, this->slot, src, src_size); */
|
||||
} else {
|
||||
/* Invalid key size. */
|
||||
static_assert(!std::is_same<AesImpl<KeySize>, AesImpl<KeySize>>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
static_assert(IsSupportedKeySize(KeySize));
|
||||
AMS_ASSERT(src_size >= BlockSize);
|
||||
AMS_ASSERT(dst_size >= BlockSize);
|
||||
|
||||
if constexpr (KeySize == 16) {
|
||||
/* Aes 128. */
|
||||
se::DecryptAes128(dst, dst_size, this->slot, src, src_size);
|
||||
} else if constexpr (KeySize == 24) {
|
||||
/* Aes 192. */
|
||||
/* TODO: se::DecryptAes192(dst, dst_size, this->slot, src, src_size); */
|
||||
} else if constexpr (KeySize == 32) {
|
||||
/* Aes 256. */
|
||||
/* TODO: se::DecryptAes256(dst, dst_size, this->slot, src, src_size); */
|
||||
} else {
|
||||
/* Invalid key size. */
|
||||
static_assert(!std::is_same<AesImpl<KeySize>, AesImpl<KeySize>>::value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Explicitly instantiate the three supported key sizes. */
|
||||
template class AesImpl<16>;
|
||||
template class AesImpl<24>;
|
||||
template class AesImpl<32>;
|
||||
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
#include <vapours/crypto/crypto_aes_decryptor.hpp>
|
||||
#include <vapours/crypto/crypto_aes_ctr_encryptor_decryptor.hpp>
|
||||
#include <vapours/crypto/crypto_aes_xts_encryptor_decryptor.hpp>
|
||||
#include <vapours/crypto/crypto_aes_gcm_encryptor.hpp>
|
||||
#include <vapours/crypto/crypto_rsa_pss_sha256_verifier.hpp>
|
||||
#include <vapours/crypto/crypto_rsa_oaep_sha256_decoder.hpp>
|
||||
#include <vapours/crypto/crypto_rsa_oaep_sha256_decryptor.hpp>
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/crypto/crypto_aes_encryptor.hpp>
|
||||
#include <vapours/crypto/crypto_gcm_encryptor.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename _AesImpl>
|
||||
class AesGcmEncryptor {
|
||||
NON_COPYABLE(AesGcmEncryptor);
|
||||
NON_MOVEABLE(AesGcmEncryptor);
|
||||
private:
|
||||
using AesImpl = _AesImpl;
|
||||
using GcmImpl = GcmEncryptor<AesImpl>;
|
||||
public:
|
||||
static constexpr size_t KeySize = AesImpl::KeySize;
|
||||
static constexpr size_t BlockSize = AesImpl::BlockSize;
|
||||
static constexpr size_t MacSize = AesImpl::BlockSize;
|
||||
private:
|
||||
AesImpl aes_impl;
|
||||
GcmImpl gcm_impl;
|
||||
public:
|
||||
AesGcmEncryptor() { /* ... */ }
|
||||
|
||||
void Initialize(const void *key, size_t key_size, const void *iv, size_t iv_size) {
|
||||
this->aes_impl.Initialize(key, key_size);
|
||||
this->gcm_impl.Initialize(std::addressof(this->aes_impl), iv, iv_size);
|
||||
}
|
||||
|
||||
void Reset(const void *iv, size_t iv_size) {
|
||||
this->gcm_impl.Reset(iv, iv_size);
|
||||
}
|
||||
|
||||
size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
return this->gcm_impl.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
void UpdateAad(const void *aad, size_t aad_size) {
|
||||
return this->gcm_impl.UpdateAad(aad, aad_size);
|
||||
}
|
||||
|
||||
void GetMac(void *dst, size_t dst_size) {
|
||||
return this->gcm_impl.GetMac(dst, dst_size);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using Aes128GcmEncryptor = impl::AesGcmEncryptor<AesEncryptor128>;
|
||||
/* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes192GcmEncryptor = impl::AesGcmEncryptor<AesEncryptor192>; */
|
||||
/* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes256GcmEncryptor = impl::AesGcmEncryptor<AesEncryptor256>; */
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/crypto/impl/crypto_gcm_mode_impl.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
/* TODO: C++20 BlockCipher concept */
|
||||
|
||||
template<typename BlockCipher>
|
||||
class GcmEncryptor {
|
||||
NON_COPYABLE(GcmEncryptor);
|
||||
NON_MOVEABLE(GcmEncryptor);
|
||||
private:
|
||||
using Impl = impl::GcmModeImpl<BlockCipher>;
|
||||
public:
|
||||
static constexpr size_t KeySize = Impl::KeySize;
|
||||
static constexpr size_t BlockSize = Impl::BlockSize;
|
||||
static constexpr size_t MacSize = Impl::MacSize;
|
||||
private:
|
||||
Impl impl;
|
||||
public:
|
||||
GcmEncryptor() { /* ... */ }
|
||||
|
||||
void Initialize(const BlockCipher *cipher, const void *iv, size_t iv_size) {
|
||||
this->impl.Initialize(cipher);
|
||||
this->impl.Reset(iv, iv_size);
|
||||
}
|
||||
|
||||
void Reset(const void *iv, size_t iv_size) {
|
||||
this->impl.Reset(iv, iv_size);
|
||||
}
|
||||
|
||||
size_t Update(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
return this->impl.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
void UpdateAad(const void *aad, size_t aad_size) {
|
||||
return this->impl.UpdateAad(aad, aad_size);
|
||||
}
|
||||
|
||||
void GetMac(void *dst, size_t dst_size) {
|
||||
return this->impl.GetMac(dst, dst_size);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -30,16 +30,24 @@ namespace ams::crypto::impl {
|
|||
static constexpr s32 RoundCount = (KeySize / 4) + 6;
|
||||
static constexpr size_t RoundKeySize = BlockSize * (RoundCount + 1);
|
||||
private:
|
||||
#ifdef ATMOSPHERE_IS_EXOSPHERE
|
||||
int slot;
|
||||
#endif
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
u32 round_keys[RoundKeySize / sizeof(u32)];
|
||||
#endif
|
||||
public:
|
||||
~AesImpl();
|
||||
|
||||
void Initialize(const void *key, size_t key_size, bool is_encrypt);
|
||||
void EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const;
|
||||
void DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const;
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
const u8 *GetRoundKey() const {
|
||||
return reinterpret_cast<const u8 *>(this->round_keys);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/* static_assert(HashFunction<Sha1Impl>); */
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/crypto/crypto_memory_clear.hpp>
|
||||
#include <vapours/crypto/crypto_aes_encryptor.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
template<typename BlockCipher>
|
||||
class GcmModeImpl {
|
||||
NON_COPYABLE(GcmModeImpl);
|
||||
NON_MOVEABLE(GcmModeImpl);
|
||||
public:
|
||||
static constexpr size_t KeySize = BlockCipher::KeySize;
|
||||
static constexpr size_t BlockSize = BlockCipher::BlockSize;
|
||||
static constexpr size_t MacSize = BlockCipher::BlockSize;
|
||||
private:
|
||||
enum State {
|
||||
State_None,
|
||||
State_Initialized,
|
||||
State_ProcessingAad,
|
||||
State_Encrypting,
|
||||
State_Decrypting,
|
||||
State_Done,
|
||||
};
|
||||
|
||||
struct Block128 {
|
||||
u64 hi;
|
||||
u64 lo;
|
||||
|
||||
ALWAYS_INLINE void Clear() {
|
||||
this->hi = 0;
|
||||
this->lo = 0;
|
||||
}
|
||||
};
|
||||
static_assert(util::is_pod<Block128>::value);
|
||||
static_assert(sizeof(Block128) == 0x10);
|
||||
|
||||
union Block {
|
||||
Block128 block_128;
|
||||
u32 block_32[4];
|
||||
u8 block_8[16];
|
||||
};
|
||||
static_assert(util::is_pod<Block>::value);
|
||||
static_assert(sizeof(Block) == 0x10);
|
||||
|
||||
using CipherFunction = void (*)(void *dst_block, const void *src_block, const void *ctx);
|
||||
private:
|
||||
State state;
|
||||
const BlockCipher *block_cipher;
|
||||
CipherFunction cipher_func;
|
||||
u8 pad[sizeof(u64)];
|
||||
Block block_x;
|
||||
Block block_y;
|
||||
Block block_ek;
|
||||
Block block_ek0;
|
||||
Block block_tmp;
|
||||
size_t aad_size;
|
||||
size_t msg_size;
|
||||
u32 aad_remaining;
|
||||
u32 msg_remaining;
|
||||
u32 counter;
|
||||
Block h_mult_blocks[16];
|
||||
public:
|
||||
GcmModeImpl() : state(State_None) { /* ... */ }
|
||||
|
||||
~GcmModeImpl() {
|
||||
ClearMemory(this, sizeof(*this));
|
||||
}
|
||||
|
||||
void Initialize(const BlockCipher *block_cipher);
|
||||
|
||||
void Reset(const void *iv, size_t iv_size);
|
||||
|
||||
void UpdateAad(const void *aad, size_t aad_size);
|
||||
size_t UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
size_t UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
|
||||
void GetMac(void *dst, size_t dst_size);
|
||||
private:
|
||||
static void ProcessBlock(void *dst_block, const void *src_block, const void *ctx) {
|
||||
static_cast<const BlockCipher *>(ctx)->EncryptBlock(dst_block, BlockSize, src_block, BlockSize);
|
||||
}
|
||||
|
||||
void InitializeHashKey();
|
||||
void ComputeMac(bool encrypt);
|
||||
};
|
||||
|
||||
}
|
|
@ -43,7 +43,6 @@ namespace ams::util {
|
|||
((u & (ByteMask << 16)) << 24) |
|
||||
((u & (ByteMask << 8)) << 40) |
|
||||
((u & (ByteMask << 0)) << 56);
|
||||
|
||||
} else if constexpr (std::is_same<U, u32>::value) {
|
||||
return ((u & (ByteMask << 24)) >> 24) |
|
||||
((u & (ByteMask << 16)) >> 8) |
|
||||
|
@ -79,7 +78,7 @@ namespace ams::util {
|
|||
constexpr ALWAYS_INLINE void SwapBytes(T *ptr) {
|
||||
using U = typename std::make_unsigned<T>::type;
|
||||
|
||||
*ptr = static_cast<T>(SwapBytes(static_cast<U>(*ptr)));
|
||||
*ptr = static_cast<T>(SwapBytes<U>(static_cast<U>(*ptr)));
|
||||
}
|
||||
|
||||
template<typename T> requires std::integral<T>
|
||||
|
@ -90,7 +89,7 @@ namespace ams::util {
|
|||
return static_cast<T>(static_cast<U>(val));
|
||||
} else {
|
||||
static_assert(IsLittleEndian());
|
||||
return static_cast<T>(SwapBytes(static_cast<U>(val)));
|
||||
return static_cast<T>(SwapBytes<U>(static_cast<U>(val)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +98,7 @@ namespace ams::util {
|
|||
using U = typename std::make_unsigned<T>::type;
|
||||
|
||||
if constexpr (IsBigEndian()) {
|
||||
return static_cast<T>(SwapBytes(static_cast<U>(val)));
|
||||
return static_cast<T>(SwapBytes<U>(static_cast<U>(val)));
|
||||
} else {
|
||||
static_assert(IsLittleEndian());
|
||||
return static_cast<T>(static_cast<U>(val));
|
||||
|
@ -136,22 +135,22 @@ namespace ams::util {
|
|||
|
||||
template<typename T> requires std::integral<T>
|
||||
constexpr ALWAYS_INLINE T LoadBigEndian(const T *ptr) {
|
||||
return ConvertToBigEndian(*ptr);
|
||||
return ConvertToBigEndian<T>(*ptr);
|
||||
}
|
||||
|
||||
template<typename T> requires std::integral<T>
|
||||
constexpr ALWAYS_INLINE T LoadLittleEndian(const T *ptr) {
|
||||
return ConvertToLittleEndian(*ptr);
|
||||
return ConvertToLittleEndian<T>(*ptr);
|
||||
}
|
||||
|
||||
template<typename T> requires std::integral<T>
|
||||
constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) {
|
||||
*ptr = ConvertToBigEndian(val);
|
||||
*ptr = ConvertToBigEndian<T>(val);
|
||||
}
|
||||
|
||||
template<typename T> requires std::integral<T>
|
||||
constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) {
|
||||
*ptr = ConvertToLittleEndian(val);
|
||||
*ptr = ConvertToLittleEndian<T>(val);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -111,6 +111,8 @@ namespace ams::crypto::impl {
|
|||
|
||||
#else
|
||||
|
||||
/* NOTE: Exosphere defines this in libexosphere. */
|
||||
|
||||
/* TODO: Non-EL0 implementation. */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* 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 <vapours.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
/* TODO: EL0 implementation. */
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* EL1+ implementation. */
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u64 GetMultiplyFactor(u8 value) {
|
||||
constexpr size_t Shift = BITSIZEOF(u8) - 1;
|
||||
constexpr u8 Mask = (1u << Shift);
|
||||
return (value & Mask) >> Shift;
|
||||
}
|
||||
|
||||
/* TODO: Big endian support, eventually? */
|
||||
constexpr void GaloisShiftLeft(u64 *block) {
|
||||
/* Shift the block left by one. */
|
||||
block[1] <<= 1;
|
||||
block[1] |= (block[0] & (static_cast<u64>(1) << (BITSIZEOF(u64) - 1))) >> (BITSIZEOF(u64) - 1);
|
||||
block[0] <<= 1;
|
||||
}
|
||||
|
||||
constexpr u8 GaloisShiftRight(u64 *block) {
|
||||
/* Determine the mask to return. */
|
||||
constexpr u8 GaloisFieldMask = 0xE1;
|
||||
const u8 mask = (block[0] & 1) * GaloisFieldMask;
|
||||
|
||||
/* Shift the block right by one. */
|
||||
block[0] >>= 1;
|
||||
block[0] |= (block[1] & 1) << (BITSIZEOF(u64) - 1);
|
||||
block[1] >>= 1;
|
||||
|
||||
/* Return the mask. */
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* Multiply two 128-bit numbers X, Y in the GF(128) Galois Field. */
|
||||
void GaloisFieldMult(void *dst, const void *x, const void *y) {
|
||||
/* Our block size is 16 bytes (for a 128-bit integer). */
|
||||
constexpr size_t BlockSize = 16;
|
||||
constexpr size_t FieldSize = 128;
|
||||
|
||||
/* Declare work blocks for us to store temporary values. */
|
||||
u8 x_block[BlockSize];
|
||||
u8 y_block[BlockSize];
|
||||
u8 out[BlockSize];
|
||||
|
||||
/* Declare 64-bit pointers for our convenience. */
|
||||
u64 *x_64 = static_cast<u64 *>(static_cast<void *>(x_block));
|
||||
u64 *y_64 = static_cast<u64 *>(static_cast<void *>(y_block));
|
||||
u64 *out_64 = static_cast<u64 *>(static_cast<void *>(out));
|
||||
|
||||
/* Initialize our work blocks. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
x_block[i] = static_cast<const u8 *>(x)[BlockSize - 1 - i];
|
||||
y_block[i] = static_cast<const u8 *>(y)[BlockSize - 1 - i];
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
/* Perform multiplication on each bit in y. */
|
||||
for (size_t i = 0; i < FieldSize; ++i) {
|
||||
/* Get the multiply factor for this bit. */
|
||||
const auto y_mult = GetMultiplyFactor(y_block[BlockSize - 1]);
|
||||
|
||||
/* Multiply x by the factor. */
|
||||
out_64[0] ^= x_64[0] * y_mult;
|
||||
out_64[1] ^= x_64[1] * y_mult;
|
||||
|
||||
/* Shift left y by one. */
|
||||
GaloisShiftLeft(y_64);
|
||||
|
||||
/* Shift right x by one, and mask appropriately. */
|
||||
const u8 x_mask = GaloisShiftRight(x_64);
|
||||
x_block[BlockSize - 1] ^= x_mask;
|
||||
}
|
||||
|
||||
/* Copy out our result. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
static_cast<u8 *>(dst)[i] = out[BlockSize - 1 - i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::Initialize(const BlockCipher *block_cipher) {
|
||||
/* Set member variables. */
|
||||
this->block_cipher = block_cipher;
|
||||
this->cipher_func = std::addressof(GcmModeImpl<BlockCipher>::ProcessBlock);
|
||||
|
||||
/* Pre-calculate values to speed up galois field multiplications later. */
|
||||
this->InitializeHashKey();
|
||||
|
||||
/* Note that we're initialized. */
|
||||
this->state = State_Initialized;
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::Reset(const void *iv, size_t iv_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(this->state >= State_Initialized);
|
||||
|
||||
/* Reset blocks. */
|
||||
this->block_x.block_128.Clear();
|
||||
this->block_tmp.block_128.Clear();
|
||||
|
||||
/* Clear sizes. */
|
||||
this->aad_size = 0;
|
||||
this->msg_size = 0;
|
||||
this->aad_remaining = 0;
|
||||
this->msg_remaining = 0;
|
||||
|
||||
/* Update our state. */
|
||||
this->state = State_ProcessingAad;
|
||||
|
||||
/* Set our iv. */
|
||||
if (iv_size == 12) {
|
||||
/* If our iv is the correct size, simply copy in the iv, and set the magic bit. */
|
||||
std::memcpy(std::addressof(this->block_ek0), iv, iv_size);
|
||||
util::StoreBigEndian(this->block_ek0.block_32 + 3, static_cast<u32>(1));
|
||||
} else {
|
||||
/* Clear our ek0 block. */
|
||||
this->block_ek0.block_128.Clear();
|
||||
|
||||
/* Update using the iv as aad. */
|
||||
this->UpdateAad(iv, iv_size);
|
||||
|
||||
/* Treat the iv as fake msg for the mac that will become our iv. */
|
||||
this->msg_size = this->aad_size;
|
||||
this->aad_size = 0;
|
||||
|
||||
/* Compute a non-final mac. */
|
||||
this->ComputeMac(false);
|
||||
|
||||
/* Set our ek0 block to our calculated mac block. */
|
||||
this->block_ek0 = this->block_x;
|
||||
|
||||
/* Clear our calculated mac block. */
|
||||
this->block_x.block_128.Clear();
|
||||
|
||||
/* Reset our state. */
|
||||
this->msg_size = 0;
|
||||
this->aad_size = 0;
|
||||
this->msg_remaining = 0;
|
||||
this->aad_remaining = 0;
|
||||
}
|
||||
|
||||
/* Set the working block to the iv. */
|
||||
this->block_ek = this->block_ek0;
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::UpdateAad(const void *aad, size_t aad_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(this->state == State_ProcessingAad);
|
||||
AMS_ASSERT(this->msg_size == 0);
|
||||
|
||||
/* Update our aad size. */
|
||||
this->aad_size += aad_size;
|
||||
|
||||
/* Define a working tracker variable. */
|
||||
const u8 *cur_aad = static_cast<const u8 *>(aad);
|
||||
|
||||
/* Process any leftover aad data from a previous invocation. */
|
||||
if (this->aad_remaining > 0) {
|
||||
while (aad_size > 0) {
|
||||
/* Copy in a byte of the aad to our partial block. */
|
||||
this->block_x.block_8[BlockSize - 1 - this->aad_remaining] ^= *(cur_aad++);
|
||||
|
||||
/* Note that we consumed a byte. */
|
||||
--aad_size;
|
||||
|
||||
/* Increment our partial block size. */
|
||||
this->aad_remaining = (this->aad_remaining + 1) % BlockSize;
|
||||
|
||||
/* If we have a complete block, process it and move onward. */
|
||||
GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Process as many blocks as we can. */
|
||||
while (aad_size >= BlockSize) {
|
||||
/* Xor the current aad into our work block. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++);
|
||||
}
|
||||
|
||||
/* Multiply the blocks in our galois field. */
|
||||
GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0]));
|
||||
|
||||
/* Note that we've processed a block. */
|
||||
aad_size -= BlockSize;
|
||||
}
|
||||
|
||||
/* Update our state with whatever aad is left over. */
|
||||
if (aad_size > 0) {
|
||||
/* Note how much left over data we have. */
|
||||
this->aad_remaining = static_cast<u32>(aad_size);
|
||||
|
||||
/* Xor the data in. */
|
||||
for (size_t i = 0; i < aad_size; ++i) {
|
||||
this->block_x.block_8[BlockSize - 1 - i] ^= *(cur_aad++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: template<class BlockCipher> size_t GcmModeImpl<BlockCipher>::UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */
|
||||
|
||||
/* TODO: template<class BlockCipher> size_t GcmModeImpl<BlockCipher>::UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::GetMac(void *dst, size_t dst_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(State_ProcessingAad <= this->state && this->state <= State_Done);
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(dst_size >= MacSize);
|
||||
AMS_ASSERT(this->aad_remaining == 0);
|
||||
AMS_ASSERT(this->msg_remaining == 0);
|
||||
|
||||
/* If we haven't already done so, compute the final mac. */
|
||||
if (this->state != State_Done) {
|
||||
this->ComputeMac(true);
|
||||
this->state = State_Done;
|
||||
}
|
||||
|
||||
static_assert(sizeof(this->block_x) == MacSize);
|
||||
std::memcpy(dst, std::addressof(this->block_x), MacSize);
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::InitializeHashKey() {
|
||||
/* We want to encrypt an empty block to use for intermediate calculations. */
|
||||
/* NOTE: Non-EL1 implementations will do multiple encryptions ahead of time, */
|
||||
/* to speed up galois field arithmetic. */
|
||||
constexpr const Block EmptyBlock = {};
|
||||
|
||||
this->ProcessBlock(std::addressof(this->h_mult_blocks[0]), std::addressof(EmptyBlock), this->block_cipher);
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::ComputeMac(bool encrypt) {
|
||||
/* If we have leftover data, process it. */
|
||||
if (this->aad_remaining > 0 || this->msg_remaining > 0) {
|
||||
GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0]));
|
||||
}
|
||||
|
||||
/* Setup the last block. */
|
||||
Block last_block = Block{ .block_128 = { this->msg_size, this->aad_size } };
|
||||
|
||||
/* Multiply the last block by 8 to account for bit vs byte sizes. */
|
||||
static_assert(offsetof(Block128, hi) == 0);
|
||||
GaloisShiftLeft(std::addressof(last_block.block_128.hi));
|
||||
GaloisShiftLeft(std::addressof(last_block.block_128.hi));
|
||||
GaloisShiftLeft(std::addressof(last_block.block_128.hi));
|
||||
|
||||
/* Xor the data in. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
this->block_x.block_8[BlockSize - 1 - i] ^= last_block.block_8[i];
|
||||
}
|
||||
|
||||
/* Perform the final multiplication. */
|
||||
GaloisFieldMult(std::addressof(this->block_x), std::addressof(this->block_x), std::addressof(this->h_mult_blocks[0]));
|
||||
|
||||
/* If we need to do an encryption, do so. */
|
||||
{
|
||||
/* Encrypt the iv. */
|
||||
u8 enc_result[BlockSize];
|
||||
this->ProcessBlock(enc_result, std::addressof(this->block_ek0), this->block_cipher);
|
||||
|
||||
/* Xor the iv in. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
this->block_x.block_8[i] ^= enc_result[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Explicitly instantiate the valid template classes. */
|
||||
template class GcmModeImpl<AesEncryptor128>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue