exo2: implement SmcReencryptDeviceUniqueData

This commit is contained in:
Michael Scire 2020-06-08 05:17:52 -07:00 committed by SciresM
parent 95d38a1a94
commit bb6671a94a
6 changed files with 163 additions and 30 deletions

View file

@ -517,22 +517,7 @@ namespace ams::secmon::smc {
return SmcResult::Success; return SmcResult::Success;
} }
SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { SmcResult ValidateDeviceUniqueDataSize(DeviceUniqueData mode, size_t data_size) {
/* 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) { switch (mode) {
case DeviceUniqueData_DecryptDeviceUniqueData: case DeviceUniqueData_DecryptDeviceUniqueData:
{ {
@ -551,8 +536,29 @@ namespace ams::secmon::smc {
return SmcResult::InvalidArgument; 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<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);
SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size));
/* Decrypt the device unique data. */ /* 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)); }; ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); };
{ {
/* Map and copy in the encrypted data. */ /* Map and copy in the encrypted data. */
@ -593,6 +599,69 @@ namespace ams::secmon::smc {
return SmcResult::Success; 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<u32>(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<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>();
const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>();
/* 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) { SmcResult GetSecureDataImpl(SmcArguments &args) {
/* Decode arguments. */ /* Decode arguments. */
const auto which = static_cast<SecureData>(args.r[1]); const auto which = static_cast<SecureData>(args.r[1]);
@ -647,8 +716,7 @@ namespace ams::secmon::smc {
} }
SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) { SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) {
/* TODO */ return LockSecurityEngineAndInvoke(args, ReencryptDeviceUniqueDataImpl);
return SmcResult::NotImplemented;
} }
/* Legacy APIs. */ /* Legacy APIs. */

View file

@ -21,14 +21,19 @@ namespace ams::secmon::smc {
namespace { namespace {
constexpr inline size_t DeviceUniqueDataIvSize = se::AesBlockSize; void GenerateIv(void *dst, size_t dst_size) {
constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize; /* Flush the region we're about to fill to ensure consistency with the SE. */
constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64); hw::FlushDataCache(dst, dst_size);
constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize; hw::DataSynchronizationBarrierInnerShareable();
constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize; /* Generate random bytes. */
constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; se::GenerateRandomBytes(dst, dst_size);
constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize; 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) { 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. */ /* Derive the seal key. */
@ -79,6 +84,10 @@ namespace ams::secmon::smc {
return static_cast<u8>(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8))); return static_cast<u8>(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8)));
} }
constexpr u64 EncodeDeviceId(u8 device_id_high, u64 device_id_low) {
return (static_cast<u64>(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) { 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; 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<u8 *>(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<hw::DataCacheLineSize, DeviceUniqueDataIvSize> 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<u64 *>(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);
}
}
} }

View file

@ -19,6 +19,16 @@
namespace ams::secmon::smc { 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); 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);
} }

View file

@ -26,7 +26,7 @@ namespace ams::secmon::smc {
} }
SmcResult SmcWriteAddress(SmcArguments &args) { SmcResult SmcWriteAddress(SmcArguments &args) {
/* TODO */ /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */
return SmcResult::NotImplemented; return SmcResult::NotImplemented;
} }

View file

@ -8,7 +8,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64) ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64)
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE 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) CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
@ -20,7 +20,7 @@ CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
endif 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 \ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_throw \ -Wl,--wrap,__cxa_throw \

View file

@ -14,7 +14,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE 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) CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)