Merge pull request #5354 from leoetlino/iosc

IOS: Implement IOSC-like library (+ bug fixes)
This commit is contained in:
Léo Lam 2017-05-04 19:58:47 +02:00 committed by GitHub
commit 5088fac54b
18 changed files with 524 additions and 119 deletions

View File

@ -10,15 +10,30 @@ namespace Common
{ {
namespace AES namespace AES
{ {
std::vector<u8> Decrypt(const u8* key, u8* iv, const u8* src, size_t size) std::vector<u8> DecryptEncrypt(const u8* key, u8* iv, const u8* src, size_t size, Mode mode)
{ {
mbedtls_aes_context aes_ctx; mbedtls_aes_context aes_ctx;
std::vector<u8> buffer(size); std::vector<u8> buffer(size);
mbedtls_aes_setkey_dec(&aes_ctx, key, 128); if (mode == Mode::Encrypt)
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, size, iv, src, buffer.data()); mbedtls_aes_setkey_enc(&aes_ctx, key, 128);
else
mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
mbedtls_aes_crypt_cbc(&aes_ctx, mode == Mode::Encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT,
size, iv, src, buffer.data());
return buffer; return buffer;
} }
std::vector<u8> Decrypt(const u8* key, u8* iv, const u8* src, size_t size)
{
return DecryptEncrypt(key, iv, src, size, Mode::Decrypt);
}
std::vector<u8> Encrypt(const u8* key, u8* iv, const u8* src, size_t size)
{
return DecryptEncrypt(key, iv, src, size, Mode::Encrypt);
}
} // namespace AES } // namespace AES
} // namespace Common } // namespace Common

View File

@ -13,6 +13,15 @@ namespace Common
{ {
namespace AES namespace AES
{ {
enum class Mode
{
Decrypt,
Encrypt,
};
std::vector<u8> DecryptEncrypt(const u8* key, u8* iv, const u8* src, size_t size, Mode mode);
// Convenience functions
std::vector<u8> Decrypt(const u8* key, u8* iv, const u8* src, size_t size); std::vector<u8> Decrypt(const u8* key, u8* iv, const u8* src, size_t size);
std::vector<u8> Encrypt(const u8* key, u8* iv, const u8* src, size_t size);
} // namespace AES } // namespace AES
} // namespace Common } // namespace Common

View File

@ -148,6 +148,7 @@ set(SRCS
IOS/Device.cpp IOS/Device.cpp
IOS/DeviceStub.cpp IOS/DeviceStub.cpp
IOS/IOS.cpp IOS/IOS.cpp
IOS/IOSC.cpp
IOS/MemoryValues.cpp IOS/MemoryValues.cpp
IOS/MIOS.cpp IOS/MIOS.cpp
IOS/DI/DI.cpp IOS/DI/DI.cpp

View File

@ -175,6 +175,7 @@
<ClCompile Include="IOS\Device.cpp" /> <ClCompile Include="IOS\Device.cpp" />
<ClCompile Include="IOS\DeviceStub.cpp" /> <ClCompile Include="IOS\DeviceStub.cpp" />
<ClCompile Include="IOS\IOS.cpp" /> <ClCompile Include="IOS\IOS.cpp" />
<ClCompile Include="IOS\IOSC.cpp" />
<ClCompile Include="IOS\MemoryValues.cpp" /> <ClCompile Include="IOS\MemoryValues.cpp" />
<ClCompile Include="IOS\MIOS.cpp" /> <ClCompile Include="IOS\MIOS.cpp" />
<ClCompile Include="IOS\DI\DI.cpp" /> <ClCompile Include="IOS\DI\DI.cpp" />
@ -432,6 +433,7 @@
<ClInclude Include="IOS\Device.h" /> <ClInclude Include="IOS\Device.h" />
<ClInclude Include="IOS\DeviceStub.h" /> <ClInclude Include="IOS\DeviceStub.h" />
<ClInclude Include="IOS\IOS.h" /> <ClInclude Include="IOS\IOS.h" />
<ClInclude Include="IOS\IOSC.h" />
<ClInclude Include="IOS\MemoryValues.h" /> <ClInclude Include="IOS\MemoryValues.h" />
<ClInclude Include="IOS\MIOS.h" /> <ClInclude Include="IOS\MIOS.h" />
<ClInclude Include="IOS\DI\DI.h" /> <ClInclude Include="IOS\DI\DI.h" />

View File

@ -793,6 +793,9 @@
<ClCompile Include="IOS\IOS.cpp"> <ClCompile Include="IOS\IOS.cpp">
<Filter>IOS</Filter> <Filter>IOS</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="IOS\IOSC.cpp">
<Filter>IOS</Filter>
</ClCompile>
<ClCompile Include="IOS\MemoryValues.cpp"> <ClCompile Include="IOS\MemoryValues.cpp">
<Filter>IOS</Filter> <Filter>IOS</Filter>
</ClCompile> </ClCompile>
@ -1507,6 +1510,9 @@
<ClInclude Include="IOS\IOS.h"> <ClInclude Include="IOS\IOS.h">
<Filter>IOS</Filter> <Filter>IOS</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="IOS\IOSC.h">
<Filter>IOS</Filter>
</ClInclude>
<ClInclude Include="IOS\MemoryValues.h"> <ClInclude Include="IOS\MemoryValues.h">
<Filter>IOS</Filter> <Filter>IOS</Filter>
</ClInclude> </ClInclude>

View File

@ -23,6 +23,7 @@ enum ReturnCode : s32
IPC_EACCES = -1, // Permission denied IPC_EACCES = -1, // Permission denied
IPC_EEXIST = -2, // File exists IPC_EEXIST = -2, // File exists
IPC_EINVAL = -4, // Invalid argument or fd IPC_EINVAL = -4, // Invalid argument or fd
IPC_EMAX = -5, // Too many file descriptors open
IPC_ENOENT = -6, // File not found IPC_ENOENT = -6, // File not found
IPC_EQUEUEFULL = -8, // Queue full IPC_EQUEUEFULL = -8, // Queue full
IPC_EIO = -12, // ECC error IPC_EIO = -12, // ECC error
@ -83,6 +84,7 @@ struct Request
enum OpenMode : s32 enum OpenMode : s32
{ {
IOS_OPEN_NONE = 0,
IOS_OPEN_READ = 1, IOS_OPEN_READ = 1,
IOS_OPEN_WRITE = 2, IOS_OPEN_WRITE = 2,
IOS_OPEN_RW = (IOS_OPEN_READ | IOS_OPEN_WRITE) IOS_OPEN_RW = (IOS_OPEN_READ | IOS_OPEN_WRITE)
@ -106,14 +108,15 @@ struct ReadWriteRequest final : Request
explicit ReadWriteRequest(u32 address); explicit ReadWriteRequest(u32 address);
}; };
enum SeekMode : s32
{
IOS_SEEK_SET = 0,
IOS_SEEK_CUR = 1,
IOS_SEEK_END = 2,
};
struct SeekRequest final : Request struct SeekRequest final : Request
{ {
enum SeekMode
{
IOS_SEEK_SET = 0,
IOS_SEEK_CUR = 1,
IOS_SEEK_END = 2,
};
u32 offset = 0; u32 offset = 0;
SeekMode mode = IOS_SEEK_SET; SeekMode mode = IOS_SEEK_SET;
explicit SeekRequest(u32 address); explicit SeekRequest(u32 address);

View File

@ -49,9 +49,6 @@ public:
static void LoadWAD(const std::string& _rContentFile); static void LoadWAD(const std::string& _rContentFile);
bool LaunchTitle(u64 title_id, bool skip_reload = false); bool LaunchTitle(u64 title_id, bool skip_reload = false);
// Internal implementation of the ES_DECRYPT ioctlv.
static void DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv, u8* output);
void DoState(PointerWrap& p) override; void DoState(PointerWrap& p) override;
ReturnCode Open(const OpenRequest& request) override; ReturnCode Open(const OpenRequest& request) override;

View File

@ -16,12 +16,12 @@
#include "Common/Assert.h" #include "Common/Assert.h"
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Crypto/AES.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Common/Swap.h" #include "Common/Swap.h"
#include "Core/ec_wii.h" #include "Core/IOS/Device.h"
#include "Core/IOS/IOSC.h"
namespace IOS namespace IOS
{ {
@ -313,15 +313,18 @@ u64 TicketReader::GetTitleId() const
std::vector<u8> TicketReader::GetTitleKey() const std::vector<u8> TicketReader::GetTitleKey() const
{ {
const u8 common_key[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4,
0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7};
u8 iv[16] = {}; u8 iv[16] = {};
std::copy_n(&m_bytes[GetOffset() + offsetof(Ticket, title_id)], sizeof(Ticket::title_id), iv); std::copy_n(&m_bytes[GetOffset() + offsetof(Ticket, title_id)], sizeof(Ticket::title_id), iv);
return Common::AES::Decrypt(common_key, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], auto common_key_handle = m_bytes.at(GetOffset() + offsetof(Ticket, common_key_index)) == 0 ?
16); HLE::IOSC::HANDLE_COMMON_KEY :
} HLE::IOSC::HANDLE_NEW_COMMON_KEY;
constexpr s32 IOSC_OK = 0; std::vector<u8> key(16);
HLE::IOSC iosc;
iosc.Decrypt(common_key_handle, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16,
key.data(), HLE::PID_ES);
return key;
}
s32 TicketReader::Unpersonalise() s32 TicketReader::Unpersonalise()
{ {
@ -329,24 +332,38 @@ s32 TicketReader::Unpersonalise()
// IOS uses IOSC to compute an AES key from the peer public key and the device's private ECC key, // IOS uses IOSC to compute an AES key from the peer public key and the device's private ECC key,
// which is used the decrypt the title key. The IV is the ticket ID (8 bytes), zero extended. // which is used the decrypt the title key. The IV is the ticket ID (8 bytes), zero extended.
using namespace HLE;
IOSC iosc;
IOSC::Handle public_handle;
s32 ret = iosc.CreateObject(&public_handle, IOSC::TYPE_PUBLIC_KEY, IOSC::SUBTYPE_ECC233, PID_ES);
if (ret != IPC_SUCCESS)
return ret;
const auto public_key_iter = ticket_begin + offsetof(Ticket, server_public_key); const auto public_key_iter = ticket_begin + offsetof(Ticket, server_public_key);
EcWii::ECCKey public_key; ret = iosc.ImportPublicKey(public_handle, &*public_key_iter, PID_ES);
std::copy_n(public_key_iter, sizeof(Ticket::server_public_key), public_key.begin()); if (ret != IPC_SUCCESS)
return ret;
const EcWii& ec = EcWii::GetInstance(); IOSC::Handle key_handle;
const std::array<u8, 16> shared_secret = ec.GetSharedSecret(public_key); ret = iosc.CreateObject(&key_handle, IOSC::TYPE_SECRET_KEY, IOSC::SUBTYPE_AES128, PID_ES);
if (ret != IPC_SUCCESS)
return ret;
ret = iosc.ComputeSharedKey(key_handle, IOSC::HANDLE_CONSOLE_KEY, public_handle, PID_ES);
if (ret != IPC_SUCCESS)
return ret;
std::array<u8, 16> iv{}; std::array<u8, 16> iv{};
std::copy_n(ticket_begin + offsetof(Ticket, ticket_id), sizeof(Ticket::ticket_id), iv.begin()); std::copy_n(ticket_begin + offsetof(Ticket, ticket_id), sizeof(Ticket::ticket_id), iv.begin());
const std::vector<u8> key = std::array<u8, 16> key{};
Common::AES::Decrypt(shared_secret.data(), iv.data(), ret = iosc.Decrypt(key_handle, iv.data(), &*ticket_begin + offsetof(Ticket, title_key),
&*ticket_begin + offsetof(Ticket, title_key), sizeof(Ticket::title_key)); sizeof(Ticket::title_key), key.data(), PID_ES);
// Finally, IOS copies the decrypted title key back to the ticket buffer. // Finally, IOS copies the decrypted title key back to the ticket buffer.
std::copy(key.cbegin(), key.cend(), ticket_begin + offsetof(Ticket, title_key)); if (ret == IPC_SUCCESS)
return IOSC_OK; std::copy(key.cbegin(), key.cend(), ticket_begin + offsetof(Ticket, title_key));
return ret;
} }
struct SharedContentMap::Entry struct SharedContentMap::Entry

View File

@ -7,8 +7,6 @@
#include <cstring> #include <cstring>
#include <vector> #include <vector>
#include <mbedtls/aes.h>
#include "Common/Assert.h" #include "Common/Assert.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
@ -21,37 +19,6 @@ namespace HLE
{ {
namespace Device namespace Device
{ {
constexpr u8 s_key_sd[0x10] = {0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08,
0xaf, 0xba, 0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d};
constexpr u8 s_key_ecc[0x1e] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
constexpr u8 s_key_empty[0x10] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// default key table
constexpr const u8* s_key_table[11] = {
s_key_ecc, // ECC Private Key
s_key_empty, // Console ID
s_key_empty, // NAND AES Key
s_key_empty, // NAND HMAC
s_key_empty, // Common Key
s_key_empty, // PRNG seed
s_key_sd, // SD Key
s_key_empty, // Unknown
s_key_empty, // Unknown
s_key_empty, // Unknown
s_key_empty, // Unknown
};
void ES::DecryptContent(u32 key_index, u8* iv, u8* input, u32 size, u8* new_iv, u8* output)
{
mbedtls_aes_context AES_ctx;
mbedtls_aes_setkey_dec(&AES_ctx, s_key_table[key_index], 128);
memcpy(new_iv, iv, 16);
mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_DECRYPT, size, new_iv, input, output);
}
IPCCommandResult ES::GetConsoleID(const IOCtlVRequest& request) IPCCommandResult ES::GetConsoleID(const IOCtlVRequest& request)
{ {
if (!request.HasNumberOfValidVectors(0, 1)) if (!request.HasNumberOfValidVectors(0, 1))
@ -69,20 +36,15 @@ IPCCommandResult ES::Encrypt(u32 uid, const IOCtlVRequest& request)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
u32 keyIndex = Memory::Read_U32(request.in_vectors[0].address); u32 keyIndex = Memory::Read_U32(request.in_vectors[0].address);
u8* IV = Memory::GetPointer(request.in_vectors[1].address);
u8* source = Memory::GetPointer(request.in_vectors[2].address); u8* source = Memory::GetPointer(request.in_vectors[2].address);
u32 size = request.in_vectors[2].size; u32 size = request.in_vectors[2].size;
u8* newIV = Memory::GetPointer(request.io_vectors[0].address); u8* iv = Memory::GetPointer(request.io_vectors[0].address);
u8* destination = Memory::GetPointer(request.io_vectors[1].address); u8* destination = Memory::GetPointer(request.io_vectors[1].address);
mbedtls_aes_context AES_ctx; // TODO: Check whether the active title is allowed to encrypt.
mbedtls_aes_setkey_enc(&AES_ctx, s_key_table[keyIndex], 128);
memcpy(newIV, IV, 16);
mbedtls_aes_crypt_cbc(&AES_ctx, MBEDTLS_AES_ENCRYPT, size, newIV, source, destination);
_dbg_assert_msg_(IOS_ES, keyIndex == 6, const ReturnCode ret = m_ios.GetIOSC().Encrypt(keyIndex, iv, source, size, destination, PID_ES);
"IOCTL_ES_ENCRYPT: Key type is not SD, data will be crap"); return GetDefaultReply(ret);
return GetDefaultReply(IPC_SUCCESS);
} }
IPCCommandResult ES::Decrypt(u32 uid, const IOCtlVRequest& request) IPCCommandResult ES::Decrypt(u32 uid, const IOCtlVRequest& request)
@ -91,17 +53,15 @@ IPCCommandResult ES::Decrypt(u32 uid, const IOCtlVRequest& request)
return GetDefaultReply(ES_EINVAL); return GetDefaultReply(ES_EINVAL);
u32 keyIndex = Memory::Read_U32(request.in_vectors[0].address); u32 keyIndex = Memory::Read_U32(request.in_vectors[0].address);
u8* IV = Memory::GetPointer(request.in_vectors[1].address);
u8* source = Memory::GetPointer(request.in_vectors[2].address); u8* source = Memory::GetPointer(request.in_vectors[2].address);
u32 size = request.in_vectors[2].size; u32 size = request.in_vectors[2].size;
u8* newIV = Memory::GetPointer(request.io_vectors[0].address); u8* iv = Memory::GetPointer(request.io_vectors[0].address);
u8* destination = Memory::GetPointer(request.io_vectors[1].address); u8* destination = Memory::GetPointer(request.io_vectors[1].address);
DecryptContent(keyIndex, IV, source, size, newIV, destination); // TODO: Check whether the active title is allowed to decrypt.
_dbg_assert_msg_(IOS_ES, keyIndex == 6, const ReturnCode ret = m_ios.GetIOSC().Decrypt(keyIndex, iv, source, size, destination, PID_ES);
"IOCTL_ES_DECRYPT: Key type is not SD, data will be crap"); return GetDefaultReply(ret);
return GetDefaultReply(IPC_SUCCESS);
} }
IPCCommandResult ES::CheckKoreaRegion(const IOCtlVRequest& request) IPCCommandResult ES::CheckKoreaRegion(const IOCtlVRequest& request)

View File

@ -9,10 +9,10 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <mbedtls/aes.h>
#include <mbedtls/sha1.h> #include <mbedtls/sha1.h>
#include "Common/Align.h" #include "Common/Align.h"
#include "Common/Crypto/AES.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/NandPaths.h" #include "Common/NandPaths.h"
@ -207,9 +207,6 @@ IPCCommandResult ES::AddContentFinish(Context& context, const IOCtlVRequest& req
return GetDefaultReply(ES_NO_TICKET); return GetDefaultReply(ES_NO_TICKET);
} }
mbedtls_aes_context aes_ctx;
mbedtls_aes_setkey_dec(&aes_ctx, ticket.GetTitleKey().data(), 128);
// The IV for title content decryption is the lower two bytes of the // The IV for title content decryption is the lower two bytes of the
// content index, zero extended. // content index, zero extended.
IOS::ES::Content content_info; IOS::ES::Content content_info;
@ -220,9 +217,9 @@ IPCCommandResult ES::AddContentFinish(Context& context, const IOCtlVRequest& req
u8 iv[16] = {0}; u8 iv[16] = {0};
iv[0] = (content_info.index >> 8) & 0xFF; iv[0] = (content_info.index >> 8) & 0xFF;
iv[1] = content_info.index & 0xFF; iv[1] = content_info.index & 0xFF;
std::vector<u8> decrypted_data(context.title_import.content_buffer.size()); std::vector<u8> decrypted_data = Common::AES::Decrypt(ticket.GetTitleKey().data(), iv,
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, context.title_import.content_buffer.size(), context.title_import.content_buffer.data(),
iv, context.title_import.content_buffer.data(), decrypted_data.data()); context.title_import.content_buffer.size());
if (!CheckIfContentHashMatches(decrypted_data, content_info)) if (!CheckIfContentHashMatches(decrypted_data, content_info))
{ {
ERROR_LOG(IOS_ES, "AddContentFinish: Hash for content %08x doesn't match", content_info.id); ERROR_LOG(IOS_ES, "AddContentFinish: Hash for content %08x doesn't match", content_info.id);
@ -480,18 +477,10 @@ IPCCommandResult ES::ExportContentData(Context& context, const IOCtlVRequest& re
// IOS aligns the buffer to 32 bytes. Since we also need to align it to 16 bytes, // IOS aligns the buffer to 32 bytes. Since we also need to align it to 16 bytes,
// let's just follow IOS here. // let's just follow IOS here.
buffer.resize(Common::AlignUp(buffer.size(), 32)); buffer.resize(Common::AlignUp(buffer.size(), 32));
std::vector<u8> output(buffer.size());
mbedtls_aes_context aes_ctx; const std::vector<u8> output =
mbedtls_aes_setkey_enc(&aes_ctx, context.title_export.title_key.data(), 128); Common::AES::Encrypt(context.title_export.title_key.data(), iterator->second.iv.data(),
const int ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer.size(), buffer.data(), buffer.size());
iterator->second.iv.data(), buffer.data(), output.data());
if (ret != 0)
{
// XXX: proper error code when IOSC_Encrypt fails.
ERROR_LOG(IOS_ES, "ExportContentData: Failed to encrypt content.");
return GetDefaultReply(ES_EINVAL);
}
Memory::CopyToEmu(request.io_vectors[0].address, output.data(), output.size()); Memory::CopyToEmu(request.io_vectors[0].address, output.data(), output.size());
metadata.m_position += length; metadata.m_position += length;

View File

@ -169,15 +169,15 @@ IPCCommandResult FileIO::Seek(const SeekRequest& request)
u32 new_position = 0; u32 new_position = 0;
switch (request.mode) switch (request.mode)
{ {
case SeekRequest::IOS_SEEK_SET: case IOS_SEEK_SET:
new_position = request.offset; new_position = request.offset;
break; break;
case SeekRequest::IOS_SEEK_CUR: case IOS_SEEK_CUR:
new_position = m_SeekPos + request.offset; new_position = m_SeekPos + request.offset;
break; break;
case SeekRequest::IOS_SEEK_END: case IOS_SEEK_END:
new_position = file_size + request.offset; new_position = file_size + request.offset;
break; break;

View File

@ -546,6 +546,8 @@ void Kernel::DoState(PointerWrap& p)
p.Do(m_ppc_uid); p.Do(m_ppc_uid);
p.Do(m_ppc_gid); p.Do(m_ppc_gid);
m_iosc.DoState(p);
if (m_title_id == MIOS_TITLE_ID) if (m_title_id == MIOS_TITLE_ID)
return; return;
@ -615,6 +617,11 @@ void Kernel::DoState(PointerWrap& p)
} }
} }
IOSC& Kernel::GetIOSC()
{
return m_iosc;
}
void Init() void Init()
{ {
s_event_enqueue = CoreTiming::RegisterEvent("IPCEvent", [](u64 userdata, s64) { s_event_enqueue = CoreTiming::RegisterEvent("IPCEvent", [](u64 userdata, s64) {

View File

@ -15,6 +15,7 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Core/CoreTiming.h" #include "Core/CoreTiming.h"
#include "Core/HW/SystemTimers.h" #include "Core/HW/SystemTimers.h"
#include "Core/IOS/IOSC.h"
class PointerWrap; class PointerWrap;
@ -55,6 +56,30 @@ enum IPCCommandType : u32
IPC_REPLY = 8, IPC_REPLY = 8,
}; };
enum ProcessId : u32
{
PID_KERNEL = 0,
PID_ES = 1,
PID_FS = 2,
PID_DI = 3,
PID_OH0 = 4,
PID_OH1 = 5,
PID_EHCI = 6,
PID_SDI = 7,
PID_USBETH = 8,
PID_NET = 9,
PID_WD = 10,
PID_WL = 11,
PID_KD = 12,
PID_NCD = 13,
PID_STM = 14,
PID_PPCBOOT = 15,
PID_SSL = 16,
PID_USB = 17,
PID_P2P = 18,
PID_UNKNOWN = 19,
};
// HLE for the IOS kernel: IPC, device management, syscalls, and Dolphin-specific, IOS-wide calls. // HLE for the IOS kernel: IPC, device management, syscalls, and Dolphin-specific, IOS-wide calls.
class Kernel class Kernel
{ {
@ -84,6 +109,8 @@ public:
bool BootIOS(u64 ios_title_id); bool BootIOS(u64 ios_title_id);
u32 GetVersion() const; u32 GetVersion() const;
IOSC& GetIOSC();
private: private:
void ExecuteIPCCommand(u32 address); void ExecuteIPCCommand(u32 address);
IPCCommandResult HandleIPCCommand(const Request& request); IPCCommandResult HandleIPCCommand(const Request& request);
@ -109,6 +136,8 @@ private:
IPCMsgQueue m_reply_queue; // arm -> ppc IPCMsgQueue m_reply_queue; // arm -> ppc
IPCMsgQueue m_ack_queue; // arm -> ppc IPCMsgQueue m_ack_queue; // arm -> ppc
u64 m_last_reply_time = 0; u64 m_last_reply_time = 0;
IOSC m_iosc;
}; };
// Used for controlling and accessing an IOS instance that is tied to emulation. // Used for controlling and accessing an IOS instance that is tied to emulation.

View File

@ -0,0 +1,259 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <mbedtls/sha1.h>
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/Crypto/AES.h"
#include "Common/Crypto/ec.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/IOSC.h"
#include "Core/ec_wii.h"
namespace IOS
{
namespace HLE
{
IOSC::IOSC()
{
LoadDefaultEntries();
}
IOSC::~IOSC() = default;
ReturnCode IOSC::CreateObject(Handle* handle, ObjectType type, ObjectSubType subtype, u32 pid)
{
auto iterator = FindFreeEntry();
if (iterator == m_key_entries.end())
return IOSC_FAIL_ALLOC;
iterator->in_use = true;
iterator->type = type;
iterator->subtype = subtype;
iterator->owner_mask = 1 << pid;
*handle = GetHandleFromIterator(iterator);
return IPC_SUCCESS;
}
ReturnCode IOSC::DeleteObject(Handle handle, u32 pid)
{
if (IsDefaultHandle(handle) || !HasOwnership(handle, pid))
return IOSC_EACCES;
m_key_entries[handle].in_use = false;
m_key_entries[handle].data.clear();
return IPC_SUCCESS;
}
constexpr size_t AES128_KEY_SIZE = 0x10;
ReturnCode IOSC::ImportSecretKey(Handle dest_handle, Handle decrypt_handle, u8* iv,
const u8* encrypted_key, u32 pid)
{
if (!HasOwnership(dest_handle, pid) || !HasOwnership(decrypt_handle, pid) ||
IsDefaultHandle(dest_handle))
{
return IOSC_EACCES;
}
auto* dest_entry = &m_key_entries[dest_handle];
// TODO: allow other secret key subtypes
if (dest_entry->type != TYPE_SECRET_KEY || dest_entry->subtype != SUBTYPE_AES128)
return IOSC_INVALID_OBJTYPE;
dest_entry->data.resize(AES128_KEY_SIZE);
return Decrypt(decrypt_handle, iv, encrypted_key, AES128_KEY_SIZE, dest_entry->data.data(), pid);
}
constexpr size_t ECC233_PUBLIC_KEY_SIZE = 0x3c;
ReturnCode IOSC::ImportPublicKey(Handle dest_handle, const u8* public_key, u32 pid)
{
if (!HasOwnership(dest_handle, pid) || IsDefaultHandle(dest_handle))
return IOSC_EACCES;
auto* dest_entry = &m_key_entries[dest_handle];
// TODO: allow other public key subtypes
if (dest_entry->type != TYPE_PUBLIC_KEY || dest_entry->subtype != SUBTYPE_ECC233)
return IOSC_INVALID_OBJTYPE;
dest_entry->data.assign(public_key, public_key + ECC233_PUBLIC_KEY_SIZE);
return IPC_SUCCESS;
}
ReturnCode IOSC::ComputeSharedKey(Handle dest_handle, Handle private_handle, Handle public_handle,
u32 pid)
{
if (!HasOwnership(dest_handle, pid) || !HasOwnership(private_handle, pid) ||
!HasOwnership(public_handle, pid) || IsDefaultHandle(dest_handle))
{
return IOSC_EACCES;
}
auto* dest_entry = &m_key_entries[dest_handle];
const auto* private_entry = &m_key_entries[private_handle];
const auto* public_entry = &m_key_entries[public_handle];
if (dest_entry->type != TYPE_SECRET_KEY || dest_entry->subtype != SUBTYPE_AES128 ||
private_entry->type != TYPE_SECRET_KEY || private_entry->subtype != SUBTYPE_ECC233 ||
public_entry->type != TYPE_PUBLIC_KEY || public_entry->subtype != SUBTYPE_ECC233)
{
return IOSC_INVALID_OBJTYPE;
}
// Calculate the ECC shared secret.
std::array<u8, 0x3c> shared_secret;
point_mul(shared_secret.data(), private_entry->data.data(), public_entry->data.data());
std::array<u8, 20> sha1;
mbedtls_sha1(shared_secret.data(), shared_secret.size() / 2, sha1.data());
dest_entry->data.resize(AES128_KEY_SIZE);
std::copy_n(sha1.cbegin(), AES128_KEY_SIZE, dest_entry->data.begin());
return IPC_SUCCESS;
}
ReturnCode IOSC::DecryptEncrypt(Common::AES::Mode mode, Handle key_handle, u8* iv, const u8* input,
size_t size, u8* output, u32 pid) const
{
if (!HasOwnership(key_handle, pid))
return IOSC_EACCES;
const auto* entry = &m_key_entries[key_handle];
if (entry->type != TYPE_SECRET_KEY || entry->subtype != SUBTYPE_AES128)
return IOSC_INVALID_OBJTYPE;
if (entry->data.size() != AES128_KEY_SIZE)
return IOSC_FAIL_INTERNAL;
const std::vector<u8> data =
Common::AES::DecryptEncrypt(entry->data.data(), iv, input, size, mode);
std::memcpy(output, data.data(), data.size());
return IPC_SUCCESS;
}
ReturnCode IOSC::Encrypt(Handle key_handle, u8* iv, const u8* input, size_t size, u8* output,
u32 pid) const
{
return DecryptEncrypt(Common::AES::Mode::Encrypt, key_handle, iv, input, size, output, pid);
}
ReturnCode IOSC::Decrypt(Handle key_handle, u8* iv, const u8* input, size_t size, u8* output,
u32 pid) const
{
return DecryptEncrypt(Common::AES::Mode::Decrypt, key_handle, iv, input, size, output, pid);
}
ReturnCode IOSC::GetOwnership(Handle handle, u32* owner) const
{
if (handle < m_key_entries.size() && m_key_entries[handle].in_use)
{
*owner = m_key_entries[handle].owner_mask;
return IPC_SUCCESS;
}
return IOSC_EINVAL;
}
ReturnCode IOSC::SetOwnership(Handle handle, u32 new_owner, u32 pid)
{
if (!HasOwnership(handle, pid))
return IOSC_EACCES;
m_key_entries[handle].owner_mask = new_owner;
return IPC_SUCCESS;
}
void IOSC::LoadDefaultEntries()
{
// TODO: add support for loading and writing to a BootMii / SEEPROM and OTP dump.
const EcWii& ec = EcWii::GetInstance();
m_key_entries[HANDLE_CONSOLE_KEY] = {TYPE_SECRET_KEY, SUBTYPE_ECC233,
std::vector<u8>(ec.GetNGPriv(), ec.GetNGPriv() + 30), 3};
// Unimplemented.
m_key_entries[HANDLE_CONSOLE_ID] = {TYPE_DATA, SUBTYPE_DATA, std::vector<u8>(4), 0xFFFFFFF};
m_key_entries[HANDLE_FS_KEY] = {TYPE_SECRET_KEY, SUBTYPE_AES128, std::vector<u8>(16), 5};
m_key_entries[HANDLE_FS_MAC] = {TYPE_SECRET_KEY, SUBTYPE_MAC, std::vector<u8>(20), 5};
m_key_entries[HANDLE_COMMON_KEY] = {TYPE_SECRET_KEY,
SUBTYPE_AES128,
{{0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9,
0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}},
3};
// Unimplemented.
m_key_entries[HANDLE_PRNG_KEY] = {TYPE_SECRET_KEY, SUBTYPE_AES128, std::vector<u8>(16), 3};
m_key_entries[HANDLE_SD_KEY] = {TYPE_SECRET_KEY,
SUBTYPE_AES128,
{{0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08, 0xaf, 0xba,
0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d}},
3};
// Unimplemented.
m_key_entries[HANDLE_BOOT2_VERSION] = {TYPE_DATA, SUBTYPE_VERSION, std::vector<u8>(4), 3};
m_key_entries[HANDLE_UNKNOWN_8] = {TYPE_DATA, SUBTYPE_VERSION, std::vector<u8>(4), 3};
m_key_entries[HANDLE_UNKNOWN_9] = {TYPE_DATA, SUBTYPE_VERSION, std::vector<u8>(4), 3};
m_key_entries[HANDLE_FS_VERSION] = {TYPE_DATA, SUBTYPE_VERSION, std::vector<u8>(4), 3};
m_key_entries[HANDLE_NEW_COMMON_KEY] = {TYPE_SECRET_KEY,
SUBTYPE_AES128,
{{0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e, 0x13,
0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e}},
3};
}
IOSC::KeyEntry::KeyEntry() = default;
IOSC::KeyEntry::KeyEntry(ObjectType type_, ObjectSubType subtype_, std::vector<u8>&& data_,
u32 owner_mask_)
: in_use(true), type(type_), subtype(subtype_), data(std::move(data_)), owner_mask(owner_mask_)
{
}
IOSC::KeyEntries::iterator IOSC::FindFreeEntry()
{
return std::find_if(m_key_entries.begin(), m_key_entries.end(),
[](const auto& entry) { return !entry.in_use; });
}
IOSC::Handle IOSC::GetHandleFromIterator(IOSC::KeyEntries::iterator iterator) const
{
_assert_(iterator != m_key_entries.end());
return static_cast<Handle>(iterator - m_key_entries.begin());
}
bool IOSC::HasOwnership(Handle handle, u32 pid) const
{
u32 owner_mask;
return GetOwnership(handle, &owner_mask) == IPC_SUCCESS && ((1 << pid) & owner_mask) != 0;
}
bool IOSC::IsDefaultHandle(Handle handle) const
{
constexpr Handle last_default_handle = HANDLE_NEW_COMMON_KEY;
return handle <= last_default_handle;
}
void IOSC::DoState(PointerWrap& p)
{
for (auto& entry : m_key_entries)
entry.DoState(p);
}
void IOSC::KeyEntry::DoState(PointerWrap& p)
{
p.Do(in_use);
p.Do(type);
p.Do(subtype);
p.Do(data);
p.Do(owner_mask);
}
} // namespace HLE
} // namespace IOS

130
Source/Core/Core/IOS/IOSC.h Normal file
View File

@ -0,0 +1,130 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
// Implementation of an IOSC-like API, but much simpler since we only support actual keys.
#pragma once
#include <array>
#include <map>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Crypto/AES.h"
class PointerWrap;
namespace IOS
{
namespace HLE
{
enum ReturnCode : s32;
class IOSC final
{
public:
IOSC();
~IOSC();
using Handle = u32;
// We use the same default key handle IDs as the actual IOSC because there are ioctlvs
// that accept arbitrary key handles from the PPC, so the IDs must match.
// More information on default handles: https://wiibrew.org/wiki/IOS/Syscalls
enum DefaultHandle : u32
{
// ECC-233 private signing key (per-console)
HANDLE_CONSOLE_KEY = 0,
// Console ID
HANDLE_CONSOLE_ID = 1,
// NAND FS AES-128 key
HANDLE_FS_KEY = 2,
// NAND FS HMAC
HANDLE_FS_MAC = 3,
// Common key
HANDLE_COMMON_KEY = 4,
// PRNG seed
HANDLE_PRNG_KEY = 5,
// SD AES-128 key
HANDLE_SD_KEY = 6,
// boot2 version (writable)
HANDLE_BOOT2_VERSION = 7,
// Unknown
HANDLE_UNKNOWN_8 = 8,
// Unknown
HANDLE_UNKNOWN_9 = 9,
// Filesystem version (writable)
HANDLE_FS_VERSION = 10,
// New common key (aka Korean common key)
HANDLE_NEW_COMMON_KEY = 11,
};
enum ObjectType : u8
{
TYPE_SECRET_KEY = 0,
TYPE_PUBLIC_KEY = 1,
TYPE_DATA = 3,
};
enum ObjectSubType : u8
{
SUBTYPE_AES128 = 0,
SUBTYPE_MAC = 1,
SUBTYPE_ECC233 = 4,
SUBTYPE_DATA = 5,
SUBTYPE_VERSION = 6
};
// Create an object for use with the other functions that operate on objects.
ReturnCode CreateObject(Handle* handle, ObjectType type, ObjectSubType subtype, u32 pid);
// Delete an object. Built-in objects cannot be deleted.
ReturnCode DeleteObject(Handle handle, u32 pid);
// Import a secret, encrypted key into dest_handle, which will be decrypted using decrypt_handle.
ReturnCode ImportSecretKey(Handle dest_handle, Handle decrypt_handle, u8* iv,
const u8* encrypted_key, u32 pid);
// Import a public key.
ReturnCode ImportPublicKey(Handle dest_handle, const u8* public_key, u32 pid);
// Compute an AES key from an ECDH shared secret.
ReturnCode ComputeSharedKey(Handle dest_handle, Handle private_handle, Handle public_handle,
u32 pid);
// AES encrypt/decrypt.
ReturnCode Encrypt(Handle key_handle, u8* iv, const u8* input, size_t size, u8* output,
u32 pid) const;
ReturnCode Decrypt(Handle key_handle, u8* iv, const u8* input, size_t size, u8* output,
u32 pid) const;
// Ownership
ReturnCode GetOwnership(Handle handle, u32* owner) const;
ReturnCode SetOwnership(Handle handle, u32 owner, u32 pid);
void DoState(PointerWrap& p);
private:
struct KeyEntry
{
KeyEntry();
KeyEntry(ObjectType type_, ObjectSubType subtype_, std::vector<u8>&& data_, u32 owner_mask_);
void DoState(PointerWrap& p);
bool in_use = false;
ObjectType type;
ObjectSubType subtype;
std::vector<u8> data;
u32 owner_mask = 0;
};
// The Wii's IOSC is limited to 32 entries, including 12 built-in entries.
using KeyEntries = std::array<KeyEntry, 32>;
void LoadDefaultEntries();
KeyEntries::iterator FindFreeEntry();
Handle GetHandleFromIterator(KeyEntries::iterator iterator) const;
bool HasOwnership(Handle handle, u32 pid) const;
bool IsDefaultHandle(Handle handle) const;
ReturnCode DecryptEncrypt(Common::AES::Mode mode, Handle key_handle, u8* iv, const u8* input,
size_t size, u8* output, u32 pid) const;
KeyEntries m_key_entries;
};
} // namespace HLE
} // namespace IOS

View File

@ -71,7 +71,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread; static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system // Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 83; // Last changed in PR 5340 static const u32 STATE_VERSION = 84; // Last changed in PR 5354
// Maps savestate versions to Dolphin versions. // Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list, // Versions after 42 don't need to be added to this list,

View File

@ -9,7 +9,6 @@
#include "Core/ec_wii.h" #include "Core/ec_wii.h"
#include <algorithm>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
@ -180,19 +179,6 @@ const u8* EcWii::GetNGSig() const
return BootMiiKeysBin.ng_sig; return BootMiiKeysBin.ng_sig;
} }
std::array<u8, 16> EcWii::GetSharedSecret(const EcWii::ECCKey& peer_public_key) const
{
EcWii::ECCKey shared_secret;
point_mul(shared_secret.data(), GetNGPriv(), peer_public_key.data());
std::array<u8, 20> sha1;
mbedtls_sha1(shared_secret.data(), shared_secret.size() / 2, sha1.data());
std::array<u8, 16> aes_key;
std::copy_n(sha1.cbegin(), aes_key.size(), aes_key.begin());
return aes_key;
}
void EcWii::InitDefaults() void EcWii::InitDefaults()
{ {
memset(&BootMiiKeysBin, 0, sizeof(BootMiiKeysBin)); memset(&BootMiiKeysBin, 0, sizeof(BootMiiKeysBin));

View File

@ -26,8 +26,6 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include <array>
void MakeNGCert(u8* ng_cert_out, u32 NG_id, u32 NG_key_id, const u8* NG_priv, const u8* NG_sig); void MakeNGCert(u8* ng_cert_out, u32 NG_id, u32 NG_key_id, const u8* NG_priv, const u8* NG_sig);
void MakeAPSigAndCert(u8* sig_out, u8* ap_cert_out, u64 title_id, u8* data, u32 data_size, void MakeAPSigAndCert(u8* sig_out, u8* ap_cert_out, u64 title_id, u8* data, u32 data_size,
const u8* NG_priv, u32 NG_id); const u8* NG_priv, u32 NG_id);
@ -43,9 +41,6 @@ public:
const u8* GetNGPriv() const; const u8* GetNGPriv() const;
const u8* GetNGSig() const; const u8* GetNGSig() const;
using ECCKey = std::array<u8, 0x3c>;
std::array<u8, 16> GetSharedSecret(const ECCKey& peer_public_key) const;
private: private:
void InitDefaults(); void InitDefaults();