mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-26 15:55:31 +01:00
IOS/ES: Drop code handling non-RSA2048 tickets
* Supporting other ticket types makes the logic slightly more complex. * There have been no such non-RSA2048 tickets seen during the Wii's lifetime. * The Wii's IOS doesn't even have support for them.
This commit is contained in:
parent
5088fac54b
commit
a7680a3d1a
@ -239,19 +239,7 @@ void TicketReader::SetBytes(std::vector<u8>&& bytes)
|
|||||||
|
|
||||||
bool TicketReader::IsValid() const
|
bool TicketReader::IsValid() const
|
||||||
{
|
{
|
||||||
// Too small for the signature type.
|
return m_bytes.size() % sizeof(Ticket) == 0;
|
||||||
if (m_bytes.size() < sizeof(u32))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
u32 ticket_offset = GetOffset();
|
|
||||||
if (ticket_offset == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Too small for the ticket itself.
|
|
||||||
if (m_bytes.size() < ticket_offset + sizeof(Ticket))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TicketReader::DoState(PointerWrap& p)
|
void TicketReader::DoState(PointerWrap& p)
|
||||||
@ -259,23 +247,9 @@ void TicketReader::DoState(PointerWrap& p)
|
|||||||
p.Do(m_bytes);
|
p.Do(m_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 TicketReader::GetNumberOfTickets() const
|
size_t TicketReader::GetNumberOfTickets() const
|
||||||
{
|
{
|
||||||
return static_cast<u32>(m_bytes.size() / (GetOffset() + sizeof(Ticket)));
|
return m_bytes.size() / sizeof(Ticket);
|
||||||
}
|
|
||||||
|
|
||||||
u32 TicketReader::GetOffset() const
|
|
||||||
{
|
|
||||||
u32 signature_type = Common::swap32(m_bytes.data());
|
|
||||||
if (signature_type == 0x10000) // RSA4096
|
|
||||||
return 576;
|
|
||||||
if (signature_type == 0x10001) // RSA2048
|
|
||||||
return 320;
|
|
||||||
if (signature_type == 0x10002) // ECDSA
|
|
||||||
return 128;
|
|
||||||
|
|
||||||
ERROR_LOG(COMMON, "Invalid ticket signature type: %08x", signature_type);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<u8>& TicketReader::GetRawTicket() const
|
const std::vector<u8>& TicketReader::GetRawTicket() const
|
||||||
@ -286,7 +260,7 @@ const std::vector<u8>& TicketReader::GetRawTicket() const
|
|||||||
std::vector<u8> TicketReader::GetRawTicketView(u32 ticket_num) const
|
std::vector<u8> TicketReader::GetRawTicketView(u32 ticket_num) const
|
||||||
{
|
{
|
||||||
// A ticket view is composed of a version + part of a ticket starting from the ticket_id field.
|
// A ticket view is composed of a version + part of a ticket starting from the ticket_id field.
|
||||||
const auto ticket_start = m_bytes.cbegin() + GetOffset() + sizeof(Ticket) * ticket_num;
|
const auto ticket_start = m_bytes.cbegin() + sizeof(Ticket) * ticket_num;
|
||||||
const auto view_start = ticket_start + offsetof(Ticket, ticket_id);
|
const auto view_start = ticket_start + offsetof(Ticket, ticket_id);
|
||||||
|
|
||||||
// Copy the ticket version to the buffer (a single byte extended to 4).
|
// Copy the ticket version to the buffer (a single byte extended to 4).
|
||||||
@ -303,32 +277,32 @@ std::vector<u8> TicketReader::GetRawTicketView(u32 ticket_num) const
|
|||||||
|
|
||||||
u32 TicketReader::GetDeviceId() const
|
u32 TicketReader::GetDeviceId() const
|
||||||
{
|
{
|
||||||
return Common::swap32(m_bytes.data() + GetOffset() + offsetof(Ticket, device_id));
|
return Common::swap32(m_bytes.data() + offsetof(Ticket, device_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 TicketReader::GetTitleId() const
|
u64 TicketReader::GetTitleId() const
|
||||||
{
|
{
|
||||||
return Common::swap64(m_bytes.data() + GetOffset() + offsetof(Ticket, title_id));
|
return Common::swap64(m_bytes.data() + offsetof(Ticket, title_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> TicketReader::GetTitleKey() const
|
std::vector<u8> TicketReader::GetTitleKey() const
|
||||||
{
|
{
|
||||||
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[offsetof(Ticket, title_id)], sizeof(Ticket::title_id), iv);
|
||||||
auto common_key_handle = m_bytes.at(GetOffset() + offsetof(Ticket, common_key_index)) == 0 ?
|
auto common_key_handle = m_bytes.at(offsetof(Ticket, common_key_index)) == 0 ?
|
||||||
HLE::IOSC::HANDLE_COMMON_KEY :
|
HLE::IOSC::HANDLE_COMMON_KEY :
|
||||||
HLE::IOSC::HANDLE_NEW_COMMON_KEY;
|
HLE::IOSC::HANDLE_NEW_COMMON_KEY;
|
||||||
|
|
||||||
std::vector<u8> key(16);
|
std::vector<u8> key(16);
|
||||||
HLE::IOSC iosc;
|
HLE::IOSC iosc;
|
||||||
iosc.Decrypt(common_key_handle, iv, &m_bytes[GetOffset() + offsetof(Ticket, title_key)], 16,
|
iosc.Decrypt(common_key_handle, iv, &m_bytes[offsetof(Ticket, title_key)], 16, key.data(),
|
||||||
key.data(), HLE::PID_ES);
|
HLE::PID_ES);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 TicketReader::Unpersonalise()
|
s32 TicketReader::Unpersonalise()
|
||||||
{
|
{
|
||||||
const auto ticket_begin = m_bytes.begin() + GetOffset();
|
const auto ticket_begin = m_bytes.begin();
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -97,8 +97,14 @@ struct TicketView
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(TicketView) == 0xd8, "TicketView has the wrong size");
|
static_assert(sizeof(TicketView) == 0xd8, "TicketView has the wrong size");
|
||||||
|
|
||||||
|
// This structure is used for (signed) tickets. Technically, there are other types of tickets
|
||||||
|
// (RSA4096, ECDSA, ...). However, only RSA2048 tickets have ever been seen and these are also
|
||||||
|
// the only ticket type that is supported by the Wii's IOS.
|
||||||
struct Ticket
|
struct Ticket
|
||||||
{
|
{
|
||||||
|
u32 signature_type;
|
||||||
|
u8 signature[256];
|
||||||
|
u8 unused[60];
|
||||||
u8 signature_issuer[0x40];
|
u8 signature_issuer[0x40];
|
||||||
u8 server_public_key[0x3c];
|
u8 server_public_key[0x3c];
|
||||||
u8 version;
|
u8 version;
|
||||||
@ -118,7 +124,7 @@ struct Ticket
|
|||||||
u8 content_access_permissions[0x40];
|
u8 content_access_permissions[0x40];
|
||||||
TimeLimit time_limits[8];
|
TimeLimit time_limits[8];
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Ticket) == 356, "Ticket has the wrong size");
|
static_assert(sizeof(Ticket) == 0x2A4, "Ticket has the wrong size");
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
class TMDReader final
|
class TMDReader final
|
||||||
@ -175,8 +181,7 @@ public:
|
|||||||
void DoState(PointerWrap& p);
|
void DoState(PointerWrap& p);
|
||||||
|
|
||||||
const std::vector<u8>& GetRawTicket() const;
|
const std::vector<u8>& GetRawTicket() const;
|
||||||
u32 GetNumberOfTickets() const;
|
size_t GetNumberOfTickets() const;
|
||||||
u32 GetOffset() const;
|
|
||||||
|
|
||||||
// Returns a "raw" ticket view, without byte swapping. Intended for use from ES.
|
// Returns a "raw" ticket view, without byte swapping. Intended for use from ES.
|
||||||
// Theoretically, a ticket file can contain one or more tickets. In practice, most (all?)
|
// Theoretically, a ticket file can contain one or more tickets. In practice, most (all?)
|
||||||
|
@ -48,7 +48,7 @@ IPCCommandResult ES::GetTicketViewCount(const IOCtlVRequest& request)
|
|||||||
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
u64 TitleID = Memory::Read_U64(request.in_vectors[0].address);
|
||||||
|
|
||||||
const IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(TitleID);
|
const IOS::ES::TicketReader ticket = DiscIO::FindSignedTicket(TitleID);
|
||||||
u32 view_count = ticket.IsValid() ? ticket.GetNumberOfTickets() : 0;
|
u32 view_count = ticket.IsValid() ? static_cast<u32>(ticket.GetNumberOfTickets()) : 0;
|
||||||
|
|
||||||
if (ShouldReturnFakeViewsForIOSes(TitleID, GetTitleContext()))
|
if (ShouldReturnFakeViewsForIOSes(TitleID, GetTitleContext()))
|
||||||
{
|
{
|
||||||
@ -75,7 +75,7 @@ IPCCommandResult ES::GetTicketViews(const IOCtlVRequest& request)
|
|||||||
|
|
||||||
if (ticket.IsValid())
|
if (ticket.IsValid())
|
||||||
{
|
{
|
||||||
u32 number_of_views = std::min(maxViews, ticket.GetNumberOfTickets());
|
u32 number_of_views = std::min(maxViews, static_cast<u32>(ticket.GetNumberOfTickets()));
|
||||||
for (u32 view = 0; view < number_of_views; ++view)
|
for (u32 view = 0; view < number_of_views; ++view)
|
||||||
{
|
{
|
||||||
const std::vector<u8> ticket_view = ticket.GetRawTicketView(view);
|
const std::vector<u8> ticket_view = ticket.GetRawTicketView(view);
|
||||||
@ -235,7 +235,7 @@ IPCCommandResult ES::DIGetTicketView(const IOCtlVRequest& request)
|
|||||||
return GetDefaultReply(ES_EINVAL);
|
return GetDefaultReply(ES_EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool has_ticket_vector = request.in_vectors[0].size == 0x2A4;
|
const bool has_ticket_vector = request.in_vectors[0].size == sizeof(IOS::ES::Ticket);
|
||||||
|
|
||||||
// This ioctlv takes either a signed ticket or no ticket, in which case the ticket size must be 0.
|
// This ioctlv takes either a signed ticket or no ticket, in which case the ticket size must be 0.
|
||||||
if (!has_ticket_vector && request.in_vectors[0].size != 0)
|
if (!has_ticket_vector && request.in_vectors[0].size != 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user