From 810419f2da7b6596ef2e10208c435039ab3bb826 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 20 May 2020 06:03:07 -0700 Subject: [PATCH] exo2: implement SmcPrepareEsDeviceUniqueKey, SmcPrepareEsCommonTitleKey, SmcLoadPreparedAesKey --- libexosphere/include/exosphere/se.hpp | 1 + libexosphere/include/exosphere/se/se_oaep.hpp | 23 ++++ libexosphere/include/exosphere/se/se_rsa.hpp | 4 + libexosphere/source/se/se_execute.cpp | 19 +++ libexosphere/source/se/se_execute.hpp | 1 + libexosphere/source/se/se_oaep.cpp | 122 ++++++++++++++++++ libexosphere/source/se/se_rsa.cpp | 53 ++++++++ 7 files changed, 223 insertions(+) create mode 100644 libexosphere/include/exosphere/se/se_oaep.hpp create mode 100644 libexosphere/source/se/se_oaep.cpp diff --git a/libexosphere/include/exosphere/se.hpp b/libexosphere/include/exosphere/se.hpp index c4669381..a86396cc 100644 --- a/libexosphere/include/exosphere/se.hpp +++ b/libexosphere/include/exosphere/se.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/libexosphere/include/exosphere/se/se_oaep.hpp b/libexosphere/include/exosphere/se/se_oaep.hpp new file mode 100644 index 00000000..b7488bd7 --- /dev/null +++ b/libexosphere/include/exosphere/se/se_oaep.hpp @@ -0,0 +1,23 @@ +/* + * 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 + +namespace ams::se { + + size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size); + +} diff --git a/libexosphere/include/exosphere/se/se_rsa.hpp b/libexosphere/include/exosphere/se/se_rsa.hpp index e632f67a..c9733c5e 100644 --- a/libexosphere/include/exosphere/se/se_rsa.hpp +++ b/libexosphere/include/exosphere/se/se_rsa.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include namespace ams::se { @@ -27,5 +28,8 @@ namespace ams::se { void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size); void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler); + + void GetRsaResult(void *dst, size_t dst_size); } diff --git a/libexosphere/source/se/se_execute.cpp b/libexosphere/source/se/se_execute.cpp index 47252e77..4f811ee8 100644 --- a/libexosphere/source/se/se_execute.cpp +++ b/libexosphere/source/se/se_execute.cpp @@ -131,6 +131,25 @@ namespace ams::se { std::memcpy(dst, aligned, dst_size); } + void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size) { + /* Set the linked list entry. */ + LinkedListEntry src_entry; + SetLinkedListEntry(std::addressof(src_entry), src, src_size); + + /* Ensure the linked list entry data is seen correctly. */ + hw::FlushDataCache(std::addressof(src_entry), sizeof(src_entry)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the linked list addresses. */ + reg::Write(SE->SE_IN_LL_ADDR, static_cast(GetPhysicalAddress(std::addressof(src_entry)))); + + /* Start the operation. */ + StartOperation(SE, SE_OPERATION_OP_START); + + /* Ensure the operation is started. */ + EnsureOperationStarted(SE); + } + void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address) { /* Configure the linked list addresses. */ reg::Write(SE->SE_IN_LL_ADDR, in_ll_address); diff --git a/libexosphere/source/se/se_execute.hpp b/libexosphere/source/se/se_execute.hpp index 5b242c59..cc28d3eb 100644 --- a/libexosphere/source/se/se_execute.hpp +++ b/libexosphere/source/se/se_execute.hpp @@ -23,6 +23,7 @@ namespace ams::se { void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size); void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size); + void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size); void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address); void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler); diff --git a/libexosphere/source/se/se_oaep.cpp b/libexosphere/source/se/se_oaep.cpp new file mode 100644 index 00000000..debbeec5 --- /dev/null +++ b/libexosphere/source/se/se_oaep.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "se_execute.hpp" + +namespace ams::se { + + /* NOTE: This implementation is mostly copy/pasted from crypto::impl::RsaOaepImpl. */ + + namespace { + + constexpr inline size_t HashSize = sizeof(Sha256Hash); + + constexpr inline u8 HeadMagic = 0x00; + + void ApplyMGF1(u8 *dst, size_t dst_size, const void *src, size_t src_size) { + /* Check our pre-conditions. */ + AMS_ABORT_UNLESS(src_size <= RsaSize - (1 + HashSize)); + + /* Create a buffer. */ + util::AlignedBuffer buf; + u32 counter = 0; + + while (dst_size > 0) { + /* Setup the current hash buffer. */ + const size_t cur_size = std::min(HashSize, dst_size); + std::memcpy(static_cast(buf), src, src_size); + { + u32 counter_be; + util::StoreBigEndian(std::addressof(counter_be), counter++); + std::memcpy(static_cast(buf) + src_size, std::addressof(counter_be), sizeof(counter_be)); + } + + /* Ensure se sees correct data. */ + hw::FlushDataCache(buf, src_size + sizeof(u32)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Calculate the hash. */ + Sha256Hash hash; + se::CalculateSha256(std::addressof(hash), buf, src_size + sizeof(u32)); + + /* Mask the current output. */ + const u8 *mask = hash.bytes; + for (size_t i = 0; i < cur_size; ++i) { + *(dst++) ^= *(mask++); + } + + /* Advance. */ + dst_size -= cur_size; + } + } + + } + + size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size) { + /* Check our preconditions. */ + AMS_ABORT_UNLESS(src_size == RsaSize); + AMS_ABORT_UNLESS(label_digest_size == HashSize); + + /* Get a byte-readable copy of the input. */ + u8 *buf = static_cast(src); + + /* Validate sanity byte. */ + bool is_valid = buf[0] == HeadMagic; + + /* Decrypt seed and masked db. */ + size_t db_len = src_size - HashSize - 1; + u8 *seed = buf + 1; + u8 *db = seed + HashSize; + ApplyMGF1(seed, HashSize, db, db_len); + ApplyMGF1(db, db_len, seed, HashSize); + + /* Check the label digest. */ + is_valid &= crypto::IsSameBytes(label_digest, db, HashSize); + + /* Skip past the label digest. */ + db += HashSize; + db_len -= HashSize; + + /* Verify that DB is of the form 0000...0001 < message > */ + s32 msg_ofs = 0; + { + int looking_for_one = 1; + int invalid_db_padding = 0; + int is_zero; + int is_one; + for (size_t i = 0; i < db_len; /* ... */) { + is_zero = (db[i] == 0); + is_one = (db[i] == 1); + msg_ofs += (looking_for_one & is_one) * (static_cast(++i)); + looking_for_one &= ~is_one; + invalid_db_padding |= (looking_for_one & ~is_zero); + } + + is_valid &= (invalid_db_padding == 0); + } + + /* If we're invalid, return zero size. */ + const size_t valid_msg_size = db_len - msg_ofs; + const size_t msg_size = std::min(dst_size, static_cast(is_valid) * valid_msg_size); + + /* Copy to output. */ + std::memcpy(dst, db + msg_ofs, msg_size); + + /* Return copied size. */ + return msg_size; + } + +} diff --git a/libexosphere/source/se/se_rsa.cpp b/libexosphere/source/se/se_rsa.cpp index 99ec5e90..9fc47393 100644 --- a/libexosphere/source/se/se_rsa.cpp +++ b/libexosphere/source/se/se_rsa.cpp @@ -67,6 +67,10 @@ namespace ams::se { } } + void WaitForInputReadComplete(volatile SecurityEngineRegisters *SE) { + while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_IN_DONE, CLEAR))) { /* ... */ } + } + } void ClearRsaKeySlot(int slot) { @@ -174,4 +178,53 @@ namespace ams::se { GetRsaResult(SE, dst, dst_size); } + void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler) { + /* Validate the slot and size. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + AMS_ABORT_UNLESS(src_size <= RsaSize); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Create a work buffer. */ + u8 work[RsaSize]; + util::ClearMemory(work, sizeof(work)); + + /* Copy the input into the work buffer (reversing endianness). */ + const u8 *src_u8 = static_cast(src); + for (size_t i = 0; i < src_size; ++i) { + work[src_size - 1 - i] = src_u8[i]; + } + + /* Flush the work buffer to ensure the SE sees correct results. */ + hw::FlushDataCache(work, sizeof(work)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the engine to perform RSA encryption. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, RSA), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, RSA_REG)); + + /* Configure the engine to use the keyslot and correct modulus/exp sizes. */ + const auto &info = g_rsa_key_infos[slot]; + reg::Write(SE->SE_RSA_CONFIG, SE_REG_BITS_VALUE(RSA_CONFIG_KEY_SLOT, slot)); + reg::Write(SE->SE_RSA_KEY_SIZE, info.modulus_size_val); + reg::Write(SE->SE_RSA_EXP_SIZE, info.exponent_size_val); + + /* Set the done handler. */ + SetDoneHandler(SE, handler); + + /* Trigger the input operation. */ + StartInputOperation(SE, work, src_size); + + /* Wait for input to be read by the se. */ + WaitForInputReadComplete(SE); + } + + void GetRsaResult(void *dst, size_t dst_size) { + GetRsaResult(GetRegisters(), dst, dst_size); + } + }