Merge pull request #5456 from JosJuice/wii-disc-formats

VolumeWiiCrypted: Use Formats.cpp more
This commit is contained in:
Leo Lam 2017-05-20 22:39:52 +02:00 committed by GitHub
commit dc8bc95a6b
10 changed files with 119 additions and 94 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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};

View file

@ -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;

View file

@ -32,6 +32,9 @@ static const unsigned int WII_BANNER_HEIGHT = 64;
static const unsigned int WII_BANNER_SIZE = WII_BANNER_WIDTH * WII_BANNER_HEIGHT * 2; static const unsigned int WII_BANNER_SIZE = WII_BANNER_WIDTH * WII_BANNER_HEIGHT * 2;
static const unsigned int WII_BANNER_OFFSET = 0xA0; static const unsigned int WII_BANNER_OFFSET = 0xA0;
const IOS::ES::TicketReader IVolume::INVALID_TICKET{};
const IOS::ES::TMDReader IVolume::INVALID_TMD{};
std::vector<u32> IVolume::GetWiiBanner(int* width, int* height, u64 title_id) std::vector<u32> IVolume::GetWiiBanner(int* width, int* height, u64 title_id)
{ {
*width = 0; *width = 0;

View file

@ -55,8 +55,11 @@ public:
virtual Partition GetGamePartition() const { return PARTITION_NONE; } virtual Partition GetGamePartition() const { return PARTITION_NONE; }
bool GetTitleID(u64* buffer) const { return GetTitleID(buffer, GetGamePartition()); } bool GetTitleID(u64* buffer) const { return GetTitleID(buffer, GetGamePartition()); }
virtual bool GetTitleID(u64* buffer, const Partition& partition) const { return false; } virtual bool GetTitleID(u64* buffer, const Partition& partition) const { return false; }
virtual IOS::ES::TicketReader GetTicket(const Partition& partition) const { return {}; } virtual const IOS::ES::TicketReader& GetTicket(const Partition& partition) const
virtual IOS::ES::TMDReader GetTMD(const Partition& partition) const { return {}; } {
return INVALID_TICKET;
}
virtual const IOS::ES::TMDReader& GetTMD(const Partition& partition) const { return INVALID_TMD; }
std::string GetGameID() const { return GetGameID(GetGamePartition()); } std::string GetGameID() const { return GetGameID(GetGamePartition()); }
virtual std::string GetGameID(const Partition& partition) const = 0; virtual std::string GetGameID(const Partition& partition) const = 0;
std::string GetMakerID() const { return GetMakerID(GetGamePartition()); } std::string GetMakerID() const { return GetMakerID(GetGamePartition()); }
@ -111,6 +114,9 @@ protected:
static const size_t NAME_STRING_LENGTH = 42; static const size_t NAME_STRING_LENGTH = 42;
static const size_t NAME_BYTES_LENGTH = NAME_STRING_LENGTH * sizeof(u16); static const size_t NAME_BYTES_LENGTH = NAME_STRING_LENGTH * sizeof(u16);
static const size_t NAMES_TOTAL_BYTES = NAME_BYTES_LENGTH * NUMBER_OF_LANGUAGES; static const size_t NAMES_TOTAL_BYTES = NAME_BYTES_LENGTH * NUMBER_OF_LANGUAGES;
static const IOS::ES::TicketReader INVALID_TICKET;
static const IOS::ES::TMDReader INVALID_TMD;
}; };
std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename); std::unique_ptr<IVolume> CreateVolumeFromFilename(const std::string& filename);

View file

@ -83,7 +83,7 @@ Country CVolumeWAD::GetCountry(const Partition& partition) const
return CountrySwitch(country_code); return CountrySwitch(country_code);
} }
IOS::ES::TMDReader CVolumeWAD::GetTMD(const Partition& partition) const const IOS::ES::TMDReader& CVolumeWAD::GetTMD(const Partition& partition) const
{ {
return m_tmd; return m_tmd;
} }

View file

@ -34,7 +34,7 @@ public:
bool Read(u64 offset, u64 length, u8* buffer, bool Read(u64 offset, u64 length, u8* buffer,
const Partition& partition = PARTITION_NONE) const override; const Partition& partition = PARTITION_NONE) const override;
bool GetTitleID(u64* buffer, const Partition& partition = PARTITION_NONE) const override; bool GetTitleID(u64* buffer, const Partition& partition = PARTITION_NONE) const override;
IOS::ES::TMDReader GetTMD(const Partition& partition = PARTITION_NONE) const override; const IOS::ES::TMDReader& GetTMD(const Partition& partition = PARTITION_NONE) const override;
std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
u16 GetRevision(const Partition& partition = PARTITION_NONE) const override; u16 GetRevision(const Partition& partition = PARTITION_NONE) const override;

View file

@ -35,7 +35,7 @@ CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader)
{ {
_assert_(m_pReader); _assert_(m_pReader);
// Get decryption keys for all partitions // Get tickets, TMDs, and decryption keys for all partitions
for (u32 partition_group = 0; partition_group < 4; ++partition_group) for (u32 partition_group = 0; partition_group < 4; ++partition_group)
{ {
u32 number_of_partitions; u32 number_of_partitions;
@ -49,10 +49,12 @@ CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader)
for (u32 i = 0; i < number_of_partitions; i++) for (u32 i = 0; i < number_of_partitions; i++)
{ {
// Read the partition offset
if (!m_pReader->ReadSwapped(partition_table_offset + (i * 8), &read_buffer)) if (!m_pReader->ReadSwapped(partition_table_offset + (i * 8), &read_buffer))
continue; continue;
const u64 partition_offset = (u64)read_buffer << 2; const u64 partition_offset = (u64)read_buffer << 2;
// Set m_game_partition if this is the game partition
if (m_game_partition == PARTITION_NONE) if (m_game_partition == PARTITION_NONE)
{ {
u32 partition_type; u32 partition_type;
@ -63,52 +65,49 @@ CVolumeWiiCrypted::CVolumeWiiCrypted(std::unique_ptr<IBlobReader> reader)
m_game_partition = Partition(partition_offset); m_game_partition = Partition(partition_offset);
} }
u8 sub_key[16]; // Read ticket
if (!m_pReader->Read(partition_offset + 0x1bf, 16, sub_key)) std::vector<u8> ticket_buffer(sizeof(IOS::ES::Ticket));
if (!m_pReader->Read(partition_offset, ticket_buffer.size(), ticket_buffer.data()))
continue;
IOS::ES::TicketReader ticket{std::move(ticket_buffer)};
if (!ticket.IsValid())
continue; continue;
u8 iv[16]; // Read TMD
memset(iv, 0, 16); u32 tmd_size = 0;
if (!m_pReader->Read(partition_offset + 0x44c, 8, iv)) u32 tmd_address = 0;
if (!m_pReader->ReadSwapped(partition_offset + 0x2a4, &tmd_size))
continue; continue;
if (!m_pReader->ReadSwapped(partition_offset + 0x2a8, &tmd_address))
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; continue;
tmd_address <<= 2;
if (!memcmp(issuer, issuer_rvt, sizeof(issuer_rvt))) if (tmd_size > 1024 * 1024 * 4)
{ {
// RVT issuer. Use the RVT (debug) master key. // The size is checked so that a malicious or corrupt ISO
common_key = common_key_rvt; // can't force Dolphin to allocate up to 4 GiB of memory.
} // 4 MiB should be much bigger than the size of TMDs and much smaller
else // than the amount of RAM in a computer that can run Dolphin.
{ PanicAlert("TMD > 4 MiB");
u8 key_number = 0; continue;
if (!m_pReader->ReadSwapped(partition_offset + 0x1f1, &key_number))
continue;
common_key = (key_number == 1) ? common_key_korean : common_key_standard;
} }
std::vector<u8> tmd_buffer(tmd_size);
if (!m_pReader->Read(partition_offset + tmd_address, tmd_size, tmd_buffer.data()))
continue;
IOS::ES::TMDReader tmd{std::move(tmd_buffer)};
mbedtls_aes_context aes_context; // Get the decryption key
mbedtls_aes_setkey_dec(&aes_context, common_key, 128); const std::vector<u8> key = ticket.GetTitleKey();
if (key.size() != 16)
continue;
std::unique_ptr<mbedtls_aes_context> aes_context = std::make_unique<mbedtls_aes_context>();
mbedtls_aes_setkey_dec(aes_context.get(), key.data(), 128);
u8 volume_key[16]; // We've read everything. Time to store it! (The reason we don't store anything
mbedtls_aes_crypt_cbc(&aes_context, MBEDTLS_AES_DECRYPT, 16, iv, sub_key, volume_key); // earlier is because we want to be able to skip adding the partition if an error occurs.)
const Partition partition(partition_offset);
std::unique_ptr<mbedtls_aes_context> partition_AES_context = m_partition_keys[partition] = std::move(aes_context);
std::make_unique<mbedtls_aes_context>(); m_partition_tickets[partition] = std::move(ticket);
mbedtls_aes_setkey_dec(partition_AES_context.get(), volume_key, 128); m_partition_tmds[partition] = std::move(tmd);
m_partitions[Partition(partition_offset)] = std::move(partition_AES_context);
} }
} }
} }
@ -124,8 +123,8 @@ bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer,
return m_pReader->Read(_ReadOffset, _Length, _pBuffer); return m_pReader->Read(_ReadOffset, _Length, _pBuffer);
// Get the decryption key for the partition // Get the decryption key for the partition
auto it = m_partitions.find(partition); auto it = m_partition_keys.find(partition);
if (it == m_partitions.end()) if (it == m_partition_keys.end())
return false; return false;
mbedtls_aes_context* aes_context = it->second.get(); mbedtls_aes_context* aes_context = it->second.get();
@ -174,7 +173,7 @@ bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer,
std::vector<Partition> CVolumeWiiCrypted::GetPartitions() const std::vector<Partition> CVolumeWiiCrypted::GetPartitions() const
{ {
std::vector<Partition> partitions; std::vector<Partition> partitions;
for (const auto& pair : m_partitions) for (const auto& pair : m_partition_keys)
partitions.push_back(pair.first); partitions.push_back(pair.first);
return partitions; return partitions;
} }
@ -186,42 +185,23 @@ Partition CVolumeWiiCrypted::GetGamePartition() const
bool CVolumeWiiCrypted::GetTitleID(u64* buffer, const Partition& partition) const bool CVolumeWiiCrypted::GetTitleID(u64* buffer, const Partition& partition) const
{ {
return m_pReader->ReadSwapped(partition.offset + 0x1DC, buffer); const IOS::ES::TicketReader& ticket = GetTicket(partition);
if (!ticket.IsValid())
return false;
*buffer = ticket.GetTitleId();
return true;
} }
IOS::ES::TicketReader CVolumeWiiCrypted::GetTicket(const Partition& partition) const const IOS::ES::TicketReader& CVolumeWiiCrypted::GetTicket(const Partition& partition) const
{ {
std::vector<u8> buffer(0x2a4); auto it = m_partition_tickets.find(partition);
m_pReader->Read(partition.offset, buffer.size(), buffer.data()); return it != m_partition_tickets.end() ? it->second : INVALID_TICKET;
return IOS::ES::TicketReader{std::move(buffer)};
} }
IOS::ES::TMDReader CVolumeWiiCrypted::GetTMD(const Partition& partition) const const IOS::ES::TMDReader& CVolumeWiiCrypted::GetTMD(const Partition& partition) const
{ {
u32 tmd_size = 0; auto it = m_partition_tmds.find(partition);
u32 tmd_address = 0; return it != m_partition_tmds.end() ? it->second : INVALID_TMD;
if (!m_pReader->ReadSwapped(partition.offset + 0x2a4, &tmd_size))
return {};
if (!m_pReader->ReadSwapped(partition.offset + 0x2a8, &tmd_address))
return {};
tmd_address <<= 2;
if (tmd_size > 1024 * 1024 * 4)
{
// The size is checked so that a malicious or corrupt ISO
// can't force Dolphin to allocate up to 4 GiB of memory.
// 4 MiB should be much bigger than the size of TMDs and much smaller
// than the amount of RAM in a computer that can run Dolphin.
PanicAlert("TMD > 4 MiB");
tmd_size = 1024 * 1024 * 4;
}
std::vector<u8> buffer(tmd_size);
if (!m_pReader->Read(partition.offset + tmd_address, tmd_size, buffer.data()))
return {};
return IOS::ES::TMDReader{std::move(buffer)};
} }
u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset, const Partition& partition) u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset, const Partition& partition)
@ -368,8 +348,8 @@ u64 CVolumeWiiCrypted::GetRawSize() const
bool CVolumeWiiCrypted::CheckIntegrity(const Partition& partition) const bool CVolumeWiiCrypted::CheckIntegrity(const Partition& partition) const
{ {
// Get the decryption key for the partition // Get the decryption key for the partition
auto it = m_partitions.find(partition); auto it = m_partition_keys.find(partition);
if (it == m_partitions.end()) if (it == m_partition_keys.end())
return false; return false;
mbedtls_aes_context* aes_context = it->second.get(); mbedtls_aes_context* aes_context = it->second.get();

View file

@ -34,8 +34,8 @@ public:
std::vector<Partition> GetPartitions() const override; std::vector<Partition> GetPartitions() const override;
Partition GetGamePartition() const override; Partition GetGamePartition() const override;
bool GetTitleID(u64* buffer, const Partition& partition) const override; bool GetTitleID(u64* buffer, const Partition& partition) const override;
IOS::ES::TicketReader GetTicket(const Partition& partition) const override; const IOS::ES::TicketReader& GetTicket(const Partition& partition) const override;
IOS::ES::TMDReader GetTMD(const Partition& partition) const override; const IOS::ES::TMDReader& GetTMD(const Partition& partition) const override;
std::string GetGameID(const Partition& partition) const override; std::string GetGameID(const Partition& partition) const override;
std::string GetMakerID(const Partition& partition) const override; std::string GetMakerID(const Partition& partition) const override;
u16 GetRevision(const Partition& partition) const override; u16 GetRevision(const Partition& partition) const override;
@ -64,7 +64,9 @@ public:
private: private:
std::unique_ptr<IBlobReader> m_pReader; std::unique_ptr<IBlobReader> m_pReader;
std::map<Partition, std::unique_ptr<mbedtls_aes_context>> m_partitions; std::map<Partition, std::unique_ptr<mbedtls_aes_context>> m_partition_keys;
std::map<Partition, IOS::ES::TicketReader> m_partition_tickets;
std::map<Partition, IOS::ES::TMDReader> m_partition_tmds;
Partition m_game_partition; Partition m_game_partition;
mutable u64 m_last_decrypted_block; mutable u64 m_last_decrypted_block;