diff --git a/exosphere2/program/source/secmon_key_storage.hpp b/exosphere2/program/source/secmon_key_storage.hpp index 39d34c9da..703ae53a7 100644 --- a/exosphere2/program/source/secmon_key_storage.hpp +++ b/exosphere2/program/source/secmon_key_storage.hpp @@ -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, diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 7d1796838..65af2f083 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -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(static_cast(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(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(); + const auto reserved = option.Get(); + + /* 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(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) { diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp index 067b4bf6b..a0ad3adb5 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -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); } diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp index b9ac92699..df4a227d4 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -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(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(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(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(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(static_cast(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; } } diff --git a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp index 1c36ec1f8..92fc5b509 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp @@ -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); } diff --git a/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp b/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp new file mode 100644 index 000000000..d71f72440 --- /dev/null +++ b/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp @@ -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 . + */ +#include + +namespace ams::crypto::impl { + + namespace { + + constexpr bool IsSupportedKeySize(size_t size) { + return size == 16 || size == 24 || size == 32; + } + + } + + template + AesImpl::~AesImpl() { + ClearMemory(this, sizeof(*this)); + } + + template + void AesImpl::Initialize(const void *key, size_t key_size, bool is_encrypt) { + static_assert(IsSupportedKeySize(KeySize)); + + /* Set the security engine keyslot. */ + this->slot = *static_cast(key); + } + + template + void AesImpl::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>::value); + } + } + + template + void AesImpl::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>::value); + } + } + + + /* Explicitly instantiate the three supported key sizes. */ + template class AesImpl<16>; + template class AesImpl<24>; + template class AesImpl<32>; + +} diff --git a/libraries/libvapours/include/vapours/crypto.hpp b/libraries/libvapours/include/vapours/crypto.hpp index 2aa531f67..0126dc99d 100644 --- a/libraries/libvapours/include/vapours/crypto.hpp +++ b/libraries/libvapours/include/vapours/crypto.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.hpp new file mode 100644 index 000000000..326d738bb --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_aes_gcm_encryptor.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 . + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace ams::crypto { + + namespace impl { + + template + class AesGcmEncryptor { + NON_COPYABLE(AesGcmEncryptor); + NON_MOVEABLE(AesGcmEncryptor); + private: + using AesImpl = _AesImpl; + using GcmImpl = GcmEncryptor; + 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; + /* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes192GcmEncryptor = impl::AesGcmEncryptor; */ + /* TODO: Validate AAD/GMAC is same for non-128 bit key using Aes256GcmEncryptor = impl::AesGcmEncryptor; */ + +} diff --git a/libraries/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp b/libraries/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp new file mode 100644 index 000000000..547bd739e --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/crypto_gcm_encryptor.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include +#include + +namespace ams::crypto { + + /* TODO: C++20 BlockCipher concept */ + + template + class GcmEncryptor { + NON_COPYABLE(GcmEncryptor); + NON_MOVEABLE(GcmEncryptor); + private: + using Impl = impl::GcmModeImpl; + 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); + } + }; + +} diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp index d99b14774..51391721a 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp @@ -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(this->round_keys); } + #endif }; /* static_assert(HashFunction); */ diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp new file mode 100644 index 000000000..fbe974f23 --- /dev/null +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_gcm_mode_impl.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace ams::crypto::impl { + + template + 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::value); + static_assert(sizeof(Block128) == 0x10); + + union Block { + Block128 block_128; + u32 block_32[4]; + u8 block_8[16]; + }; + static_assert(util::is_pod::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(ctx)->EncryptBlock(dst_block, BlockSize, src_block, BlockSize); + } + + void InitializeHashKey(); + void ComputeMac(bool encrypt); + }; + +} diff --git a/libraries/libvapours/include/vapours/util/util_endian.hpp b/libraries/libvapours/include/vapours/util/util_endian.hpp index f269796d2..147a73f80 100644 --- a/libraries/libvapours/include/vapours/util/util_endian.hpp +++ b/libraries/libvapours/include/vapours/util/util_endian.hpp @@ -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::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::type; - *ptr = static_cast(SwapBytes(static_cast(*ptr))); + *ptr = static_cast(SwapBytes(static_cast(*ptr))); } template requires std::integral @@ -90,7 +89,7 @@ namespace ams::util { return static_cast(static_cast(val)); } else { static_assert(IsLittleEndian()); - return static_cast(SwapBytes(static_cast(val))); + return static_cast(SwapBytes(static_cast(val))); } } @@ -99,7 +98,7 @@ namespace ams::util { using U = typename std::make_unsigned::type; if constexpr (IsBigEndian()) { - return static_cast(SwapBytes(static_cast(val))); + return static_cast(SwapBytes(static_cast(val))); } else { static_assert(IsLittleEndian()); return static_cast(static_cast(val)); @@ -136,22 +135,22 @@ namespace ams::util { template requires std::integral constexpr ALWAYS_INLINE T LoadBigEndian(const T *ptr) { - return ConvertToBigEndian(*ptr); + return ConvertToBigEndian(*ptr); } template requires std::integral constexpr ALWAYS_INLINE T LoadLittleEndian(const T *ptr) { - return ConvertToLittleEndian(*ptr); + return ConvertToLittleEndian(*ptr); } template requires std::integral constexpr ALWAYS_INLINE void StoreBigEndian(T *ptr, T val) { - *ptr = ConvertToBigEndian(val); + *ptr = ConvertToBigEndian(val); } template requires std::integral constexpr ALWAYS_INLINE void StoreLittleEndian(T *ptr, T val) { - *ptr = ConvertToLittleEndian(val); + *ptr = ConvertToLittleEndian(val); } } diff --git a/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp b/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp index 8c7465bed..e1ad2c81f 100644 --- a/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp +++ b/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp @@ -111,6 +111,8 @@ namespace ams::crypto::impl { #else + /* NOTE: Exosphere defines this in libexosphere. */ + /* TODO: Non-EL0 implementation. */ #endif diff --git a/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp b/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp new file mode 100644 index 000000000..1b7c2995c --- /dev/null +++ b/libraries/libvapours/source/crypto/impl/crypto_gcm_mode_impl.arch.arm64.cpp @@ -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 . + */ +#include + +#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(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(static_cast(x_block)); + u64 *y_64 = static_cast(static_cast(y_block)); + u64 *out_64 = static_cast(static_cast(out)); + + /* Initialize our work blocks. */ + for (size_t i = 0; i < BlockSize; ++i) { + x_block[i] = static_cast(x)[BlockSize - 1 - i]; + y_block[i] = static_cast(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(dst)[i] = out[BlockSize - 1 - i]; + } + } + + } + + template + void GcmModeImpl::Initialize(const BlockCipher *block_cipher) { + /* Set member variables. */ + this->block_cipher = block_cipher; + this->cipher_func = std::addressof(GcmModeImpl::ProcessBlock); + + /* Pre-calculate values to speed up galois field multiplications later. */ + this->InitializeHashKey(); + + /* Note that we're initialized. */ + this->state = State_Initialized; + } + + template + void GcmModeImpl::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(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 + void GcmModeImpl::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(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(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 size_t GcmModeImpl::UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */ + + /* TODO: template size_t GcmModeImpl::UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */ + + template + void GcmModeImpl::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 + void GcmModeImpl::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 + void GcmModeImpl::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; + +} + +#endif