VolumeWiiCrypted: Get title keys from TicketReader
This commit is contained in:
parent
26f5b53ecb
commit
1575020c3a
|
@ -279,6 +279,13 @@ std::vector<u8> TicketReader::GetRawTicketView(u32 ticket_num) const
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TicketReader::GetIssuer() const
|
||||||
|
{
|
||||||
|
const char* bytes =
|
||||||
|
reinterpret_cast<const char*>(m_bytes.data() + offsetof(Ticket, signature_issuer));
|
||||||
|
return std::string(bytes, strnlen(bytes, sizeof(Ticket::signature_issuer)));
|
||||||
|
}
|
||||||
|
|
||||||
u32 TicketReader::GetDeviceId() const
|
u32 TicketReader::GetDeviceId() const
|
||||||
{
|
{
|
||||||
return Common::swap32(m_bytes.data() + offsetof(Ticket, device_id));
|
return Common::swap32(m_bytes.data() + offsetof(Ticket, device_id));
|
||||||
|
@ -303,8 +310,12 @@ std::vector<u8> TicketReader::GetTitleKey() const
|
||||||
GetTitleId(), index);
|
GetTitleId(), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool is_rvt = (GetIssuer() == "Root-CA00000002-XS00000006");
|
||||||
|
const HLE::IOSC::ConsoleType console_type =
|
||||||
|
is_rvt ? HLE::IOSC::ConsoleType::RVT : HLE::IOSC::ConsoleType::Retail;
|
||||||
|
|
||||||
std::vector<u8> key(16);
|
std::vector<u8> key(16);
|
||||||
HLE::IOSC iosc;
|
HLE::IOSC iosc(console_type);
|
||||||
iosc.Decrypt(common_key_handle, iv, &m_bytes[offsetof(Ticket, title_key)], 16, key.data(),
|
iosc.Decrypt(common_key_handle, iv, &m_bytes[offsetof(Ticket, title_key)], 16, key.data(),
|
||||||
HLE::PID_ES);
|
HLE::PID_ES);
|
||||||
return key;
|
return key;
|
||||||
|
|
|
@ -189,6 +189,7 @@ public:
|
||||||
// more than just one ticket and generate ticket views for them, so we implement it too.
|
// more than just one ticket and generate ticket views for them, so we implement it too.
|
||||||
std::vector<u8> GetRawTicketView(u32 ticket_num) const;
|
std::vector<u8> GetRawTicketView(u32 ticket_num) const;
|
||||||
|
|
||||||
|
std::string GetIssuer() const;
|
||||||
u32 GetDeviceId() const;
|
u32 GetDeviceId() const;
|
||||||
u64 GetTitleId() const;
|
u64 GetTitleId() const;
|
||||||
std::vector<u8> GetTitleKey() const;
|
std::vector<u8> GetTitleKey() const;
|
||||||
|
|
|
@ -19,9 +19,9 @@ namespace IOS
|
||||||
{
|
{
|
||||||
namespace HLE
|
namespace HLE
|
||||||
{
|
{
|
||||||
IOSC::IOSC()
|
IOSC::IOSC(ConsoleType console_type)
|
||||||
{
|
{
|
||||||
LoadDefaultEntries();
|
LoadDefaultEntries(console_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
IOSC::~IOSC() = default;
|
IOSC::~IOSC() = default;
|
||||||
|
@ -167,7 +167,7 @@ ReturnCode IOSC::SetOwnership(Handle handle, u32 new_owner, u32 pid)
|
||||||
return IPC_SUCCESS;
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOSC::LoadDefaultEntries()
|
void IOSC::LoadDefaultEntries(ConsoleType console_type)
|
||||||
{
|
{
|
||||||
// TODO: add support for loading and writing to a BootMii / SEEPROM and OTP dump.
|
// TODO: add support for loading and writing to a BootMii / SEEPROM and OTP dump.
|
||||||
|
|
||||||
|
@ -181,11 +181,26 @@ void IOSC::LoadDefaultEntries()
|
||||||
m_key_entries[HANDLE_FS_KEY] = {TYPE_SECRET_KEY, SUBTYPE_AES128, std::vector<u8>(16), 5};
|
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_FS_MAC] = {TYPE_SECRET_KEY, SUBTYPE_MAC, std::vector<u8>(20), 5};
|
||||||
|
|
||||||
m_key_entries[HANDLE_COMMON_KEY] = {TYPE_SECRET_KEY,
|
switch (console_type)
|
||||||
SUBTYPE_AES128,
|
{
|
||||||
{{0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9,
|
case ConsoleType::Retail:
|
||||||
0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}},
|
m_key_entries[HANDLE_COMMON_KEY] = {TYPE_SECRET_KEY,
|
||||||
3};
|
SUBTYPE_AES128,
|
||||||
|
{{0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48,
|
||||||
|
0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7}},
|
||||||
|
3};
|
||||||
|
break;
|
||||||
|
case ConsoleType::RVT:
|
||||||
|
m_key_entries[HANDLE_COMMON_KEY] = {TYPE_SECRET_KEY,
|
||||||
|
SUBTYPE_AES128,
|
||||||
|
{{0xa1, 0x60, 0x4a, 0x6a, 0x71, 0x23, 0xb5, 0x29, 0xae,
|
||||||
|
0x8b, 0xec, 0x32, 0xc8, 0x16, 0xfc, 0xaa}},
|
||||||
|
3};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_assert_msg_(IOS, false, "Unknown console type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Unimplemented.
|
// Unimplemented.
|
||||||
m_key_entries[HANDLE_PRNG_KEY] = {TYPE_SECRET_KEY, SUBTYPE_AES128, std::vector<u8>(16), 3};
|
m_key_entries[HANDLE_PRNG_KEY] = {TYPE_SECRET_KEY, SUBTYPE_AES128, std::vector<u8>(16), 3};
|
||||||
|
|
|
@ -24,10 +24,14 @@ enum ReturnCode : s32;
|
||||||
class IOSC final
|
class IOSC final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IOSC();
|
|
||||||
~IOSC();
|
|
||||||
|
|
||||||
using Handle = u32;
|
using Handle = u32;
|
||||||
|
|
||||||
|
enum class ConsoleType
|
||||||
|
{
|
||||||
|
Retail,
|
||||||
|
RVT,
|
||||||
|
};
|
||||||
|
|
||||||
// We use the same default key handle IDs as the actual IOSC because there are ioctlvs
|
// 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.
|
// that accept arbitrary key handles from the PPC, so the IDs must match.
|
||||||
// More information on default handles: https://wiibrew.org/wiki/IOS/Syscalls
|
// More information on default handles: https://wiibrew.org/wiki/IOS/Syscalls
|
||||||
|
@ -75,6 +79,9 @@ public:
|
||||||
SUBTYPE_VERSION = 6
|
SUBTYPE_VERSION = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IOSC(ConsoleType console_type = ConsoleType::Retail);
|
||||||
|
~IOSC();
|
||||||
|
|
||||||
// Create an object for use with the other functions that operate on objects.
|
// Create an object for use with the other functions that operate on objects.
|
||||||
ReturnCode CreateObject(Handle* handle, ObjectType type, ObjectSubType subtype, u32 pid);
|
ReturnCode CreateObject(Handle* handle, ObjectType type, ObjectSubType subtype, u32 pid);
|
||||||
// Delete an object. Built-in objects cannot be deleted.
|
// Delete an object. Built-in objects cannot be deleted.
|
||||||
|
@ -116,7 +123,7 @@ private:
|
||||||
// The Wii's IOSC is limited to 32 entries, including 12 built-in entries.
|
// The Wii's IOSC is limited to 32 entries, including 12 built-in entries.
|
||||||
using KeyEntries = std::array<KeyEntry, 32>;
|
using KeyEntries = std::array<KeyEntry, 32>;
|
||||||
|
|
||||||
void LoadDefaultEntries();
|
void LoadDefaultEntries(ConsoleType console_type);
|
||||||
KeyEntries::iterator FindFreeEntry();
|
KeyEntries::iterator FindFreeEntry();
|
||||||
Handle GetHandleFromIterator(KeyEntries::iterator iterator) const;
|
Handle GetHandleFromIterator(KeyEntries::iterator iterator) const;
|
||||||
bool HasOwnership(Handle handle, u32 pid) const;
|
bool HasOwnership(Handle handle, u32 pid) const;
|
||||||
|
|
|
@ -70,6 +70,8 @@ CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader)
|
||||||
if (!m_pReader->Read(partition_offset, ticket_buffer.size(), ticket_buffer.data()))
|
if (!m_pReader->Read(partition_offset, ticket_buffer.size(), ticket_buffer.data()))
|
||||||
continue;
|
continue;
|
||||||
IOS::ES::TicketReader ticket{std::move(ticket_buffer)};
|
IOS::ES::TicketReader ticket{std::move(ticket_buffer)};
|
||||||
|
if (!ticket.IsValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
// Read TMD
|
// Read TMD
|
||||||
u32 tmd_size = 0;
|
u32 tmd_size = 0;
|
||||||
|
@ -93,58 +95,17 @@ CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader)
|
||||||
continue;
|
continue;
|
||||||
IOS::ES::TMDReader tmd{std::move(tmd_buffer)};
|
IOS::ES::TMDReader tmd{std::move(tmd_buffer)};
|
||||||
|
|
||||||
// All of the code below is for getting the decryption key
|
// Get the decryption key
|
||||||
|
const std::vector<u8> key = ticket.GetTitleKey();
|
||||||
u8 sub_key[16];
|
if (key.size() != 16)
|
||||||
if (!m_pReader->Read(partition_offset + 0x1bf, 16, sub_key))
|
|
||||||
continue;
|
continue;
|
||||||
|
std::unique_ptr<mbedtls_aes_context> aes_context = std::make_unique<mbedtls_aes_context>();
|
||||||
u8 iv[16];
|
mbedtls_aes_setkey_dec(aes_context.get(), key.data(), 128);
|
||||||
memset(iv, 0, 16);
|
|
||||||
if (!m_pReader->Read(partition_offset + 0x44c, 8, iv))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
static const u8 common_key_standard[16] = {0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4,
|
|
||||||
0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7};
|
|
||||||
static const u8 common_key_korean[16] = {0x63, 0xb8, 0x2b, 0xb4, 0xf4, 0x61, 0x4e, 0x2e,
|
|
||||||
0x13, 0xf2, 0xfe, 0xfb, 0xba, 0x4c, 0x9b, 0x7e};
|
|
||||||
static const u8 common_key_rvt[16] = {0xa1, 0x60, 0x4a, 0x6a, 0x71, 0x23, 0xb5, 0x29,
|
|
||||||
0xae, 0x8b, 0xec, 0x32, 0xc8, 0x16, 0xfc, 0xaa};
|
|
||||||
static const char issuer_rvt[] = "Root-CA00000002-XS00000006";
|
|
||||||
|
|
||||||
const u8* common_key;
|
|
||||||
|
|
||||||
u8 issuer[sizeof(issuer_rvt)];
|
|
||||||
if (!m_pReader->Read(partition_offset + 0x140, sizeof(issuer), issuer))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!memcmp(issuer, issuer_rvt, sizeof(issuer_rvt)))
|
|
||||||
{
|
|
||||||
// RVT issuer. Use the RVT (debug) master key.
|
|
||||||
common_key = common_key_rvt;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u8 key_number = 0;
|
|
||||||
if (!m_pReader->ReadSwapped(partition_offset + 0x1f1, &key_number))
|
|
||||||
continue;
|
|
||||||
common_key = (key_number == 1) ? common_key_korean : common_key_standard;
|
|
||||||
}
|
|
||||||
|
|
||||||
mbedtls_aes_context aes_context;
|
|
||||||
mbedtls_aes_setkey_dec(&aes_context, common_key, 128);
|
|
||||||
|
|
||||||
u8 volume_key[16];
|
|
||||||
mbedtls_aes_crypt_cbc(&aes_context, MBEDTLS_AES_DECRYPT, 16, iv, sub_key, volume_key);
|
|
||||||
|
|
||||||
std::unique_ptr<mbedtls_aes_context> partition_AES_context =
|
|
||||||
std::make_unique<mbedtls_aes_context>();
|
|
||||||
mbedtls_aes_setkey_dec(partition_AES_context.get(), volume_key, 128);
|
|
||||||
|
|
||||||
// We've read everything. Time to store it! (The reason we don't store anything
|
// We've read everything. Time to store it! (The reason we don't store anything
|
||||||
// earlier is because we want to be able to skip adding the partition if an error occurs.)
|
// earlier is because we want to be able to skip adding the partition if an error occurs.)
|
||||||
const Partition partition(partition_offset);
|
const Partition partition(partition_offset);
|
||||||
m_partition_keys[partition] = std::move(partition_AES_context);
|
m_partition_keys[partition] = std::move(aes_context);
|
||||||
m_partition_tickets[partition] = std::move(ticket);
|
m_partition_tickets[partition] = std::move(ticket);
|
||||||
m_partition_tmds[partition] = std::move(tmd);
|
m_partition_tmds[partition] = std::move(tmd);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue