From bb6671a94a45dea9cfb251118b1404fc3a42d918 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Jun 2020 05:17:52 -0700 Subject: [PATCH] exo2: implement SmcReencryptDeviceUniqueData --- .../program/source/smc/secmon_smc_aes.cpp | 106 ++++++++++++++---- .../smc/secmon_smc_device_unique_data.cpp | 69 ++++++++++-- .../smc/secmon_smc_device_unique_data.hpp | 10 ++ .../source/smc/secmon_smc_memory_access.cpp | 2 +- libraries/config/templates/exosphere.mk | 4 +- libraries/libexosphere/arm64.mk | 2 +- 6 files changed, 163 insertions(+), 30 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index aab91f842..a3790c0fd 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -517,22 +517,7 @@ 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); + SmcResult ValidateDeviceUniqueDataSize(DeviceUniqueData mode, size_t data_size) { switch (mode) { case DeviceUniqueData_DecryptDeviceUniqueData: { @@ -551,8 +536,29 @@ namespace ams::secmon::smc { return SmcResult::InvalidArgument; } + 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); + SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size)); + /* Decrypt the device unique data. */ - u8 work_buffer[DeviceUniqueDataSizeMax]; + alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax]; ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); }; { /* Map and copy in the encrypted data. */ @@ -593,6 +599,69 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult ReencryptDeviceUniqueDataImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key_dec[se::AesBlockSize]; + u8 access_key_enc[se::AesBlockSize]; + u8 key_source_dec[se::AesBlockSize]; + u8 key_source_enc[se::AesBlockSize]; + + const uintptr_t access_key_dec_address = args.r[1]; + const uintptr_t access_key_enc_address = args.r[2]; + 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]; + const uintptr_t key_source_dec_address = args.r[6]; + const uintptr_t key_source_enc_address = args.r[7]; + + const auto mode = option.Get(); + const auto reserved = option.Get(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size)); + + /* Decrypt the device unique data. */ + alignas(8) 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); + SMC_R_UNLESS(mapper.CopyFromUser(access_key_dec, access_key_dec_address, sizeof(access_key_dec)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(access_key_enc, access_key_enc_address, sizeof(access_key_enc)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(key_source_dec, key_source_dec_address, sizeof(key_source_dec)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(key_source_enc, key_source_enc_address, sizeof(key_source_enc)), InvalidArgument); + + /* Decrypt the data. */ + u8 device_id_high; + { + /* Determine the seal key to use. */ + const u8 * const seal_key_source = SealKeySources[SealKey_ReencryptDeviceUniqueData]; + + if (!DecryptDeviceUniqueData(work_buffer, data_size, std::addressof(device_id_high), seal_key_source, se::AesBlockSize, access_key_dec, sizeof(access_key_dec), key_source_dec, sizeof(key_source_dec), work_buffer, data_size)) { + return SmcResult::InvalidArgument; + } + } + + /* Reencrypt the data. */ + { + /* Determine the seal key to use. */ + const auto seal_key_type = DeviceUniqueDataToSealKey[mode]; + const u8 * const seal_key_source = SealKeySources[seal_key_type]; + + /* Encrypt the data. */ + EncryptDeviceUniqueData(work_buffer, data_size, seal_key_source, se::AesBlockSize, access_key_enc, sizeof(access_key_enc), key_source_enc, sizeof(key_source_enc), work_buffer, data_size - DeviceUniqueDataTotalMetaSize, device_id_high); + } + + /* Copy the reencrypted data back to user. */ + SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument); + } + + return SmcResult::Success; + } + SmcResult GetSecureDataImpl(SmcArguments &args) { /* Decode arguments. */ const auto which = static_cast(args.r[1]); @@ -647,8 +716,7 @@ namespace ams::secmon::smc { } SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, ReencryptDeviceUniqueDataImpl); } /* Legacy APIs. */ 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 e14dfbe18..6505daa52 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.cpp @@ -21,14 +21,19 @@ namespace ams::secmon::smc { 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; + void GenerateIv(void *dst, size_t dst_size) { + /* Flush the region we're about to fill to ensure consistency with the SE. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); - constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize; - constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; - constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; + /* Generate random bytes. */ + se::GenerateRandomBytes(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Flush to ensure the CPU sees consistent data for the region. */ + hw::FlushDataCache(dst, dst_size); + hw::DataSynchronizationBarrierInnerShareable(); + } 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. */ @@ -79,6 +84,10 @@ namespace ams::secmon::smc { return static_cast(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8))); } + constexpr u64 EncodeDeviceId(u8 device_id_high, u64 device_id_low) { + return (static_cast(device_id_high) << (BITSIZEOF(u64) - BITSIZEOF(u8))) | device_id_low; + } + } 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) { @@ -143,4 +152,50 @@ namespace ams::secmon::smc { return true; } + void EncryptDeviceUniqueData(void *dst, size_t dst_size, 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, u8 device_id_high) { + /* Determine metadata locations. */ + u8 * const dst_iv = static_cast(dst); + u8 * const dst_data = dst_iv + DeviceUniqueDataIvSize; + u8 * const dst_pad = dst_data + src_size; + u8 * const dst_did = dst_pad + DeviceUniqueDataPaddingSize; + u8 * const dst_mac = dst_did + DeviceUniqueDataDeviceIdSize; + + /* Verify that our sizes are okay. */ + const size_t enc_size = src_size + DeviceUniqueDataInnerMetaSize; + const size_t res_size = src_size + DeviceUniqueDataTotalMetaSize; + AMS_ABORT_UNLESS(res_size <= dst_size); + + /* Layout the image as expected. */ + { + /* Generate a random iv. */ + util::AlignedBuffer iv; + GenerateIv(iv, DeviceUniqueDataIvSize); + + /* Move the data to the output image. */ + std::memmove(dst_data, src, src_size); + + /* Copy the iv. */ + std::memcpy(dst_iv, iv, DeviceUniqueDataIvSize); + + /* Clear the padding. */ + std::memset(dst_pad, 0, DeviceUniqueDataPaddingSize); + + /* Store the device id. */ + util::StoreBigEndian(reinterpret_cast(dst_did), EncodeDeviceId(device_id_high, fuse::GetDeviceId())); + } + + /* Encrypt and mac. */ + { + + /* Prepare the key used to encrypt the data. */ + PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size); + + /* Compute the gmac. */ + ComputeGmac(dst_mac, DeviceUniqueDataMacSize, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); + + /* Encrypt the data. */ + ComputeAes128Ctr(dst_data, enc_size, pkg1::AesKeySlot_Smc, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize); + } + } + } 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 92fc5b509..a8e790151 100644 --- a/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp +++ b/exosphere2/program/source/smc/secmon_smc_device_unique_data.hpp @@ -19,6 +19,16 @@ namespace ams::secmon::smc { + 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 + DeviceUniqueDataMacSize; + constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; + constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; + 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); + void EncryptDeviceUniqueData(void *dst, size_t dst_size, 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, u8 device_id_high); } diff --git a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp index d68dcf879..a4071a832 100644 --- a/exosphere2/program/source/smc/secmon_smc_memory_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_memory_access.cpp @@ -26,7 +26,7 @@ namespace ams::secmon::smc { } SmcResult SmcWriteAddress(SmcArguments &args) { - /* TODO */ + /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */ return SmcResult::NotImplemented; } diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk index 163626659..7025e615e 100644 --- a/libraries/config/templates/exosphere.mk +++ b/libraries/config/templates/exosphere.mk @@ -8,7 +8,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk #--------------------------------------------------------------------------------- ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64) DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Werror -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) @@ -20,7 +20,7 @@ CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) endif -export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now +export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-exceptions -fno-rtti -fno-use-cxa-atexit -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ -Wl,--wrap,__cxa_throw \ diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk index c9a17e60d..35492a4c9 100644 --- a/libraries/libexosphere/arm64.mk +++ b/libraries/libexosphere/arm64.mk @@ -14,7 +14,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -O2 -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Werror -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)