Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESFormats additions and fixes #5601

Merged
merged 6 commits into from
Jun 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/Core/Core/IOS/DI/DI.cpp
Expand Up @@ -110,7 +110,7 @@ IPCCommandResult DI::IOCtlV(const IOCtlVRequest& request)

// Read TMD to the buffer
const IOS::ES::TMDReader tmd = DVDThread::GetTMD(partition);
const std::vector<u8> raw_tmd = tmd.GetRawTMD();
const std::vector<u8>& raw_tmd = tmd.GetBytes();
Memory::CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
ES::DIVerify(tmd, DVDThread::GetTicket(partition));

Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/IOS/ES/ES.cpp
Expand Up @@ -610,7 +610,7 @@ s32 ES::DIVerify(const IOS::ES::TMDReader& tmd, const IOS::ES::TicketReader& tic
if (!File::Exists(tmd_path))
{
File::IOFile tmd_file(tmd_path, "wb");
const std::vector<u8>& tmd_bytes = tmd.GetRawTMD();
const std::vector<u8>& tmd_bytes = tmd.GetBytes();
if (!tmd_file.WriteBytes(tmd_bytes.data(), tmd_bytes.size()))
ERROR_LOG(IOS_ES, "DIVerify failed to write disc TMD to NAND.");
}
Expand Down
248 changes: 193 additions & 55 deletions Source/Core/Core/IOS/ES/Formats.cpp
Expand Up @@ -10,6 +10,7 @@
#include <cstddef>
#include <cstring>
#include <locale>
#include <map>
#include <optional>
#include <string>
#include <utility>
Expand Down Expand Up @@ -59,31 +60,124 @@ bool Content::IsShared() const
return (type & 0x8000) != 0;
}

bool IsValidTMDSize(size_t size)
SignedBlobReader::SignedBlobReader(const std::vector<u8>& bytes) : m_bytes(bytes)
{
return size <= 0x49e4;
}

TMDReader::TMDReader(const std::vector<u8>& bytes) : m_bytes(bytes)
SignedBlobReader::SignedBlobReader(std::vector<u8>&& bytes) : m_bytes(std::move(bytes))
{
}

TMDReader::TMDReader(std::vector<u8>&& bytes) : m_bytes(std::move(bytes))
const std::vector<u8>& SignedBlobReader::GetBytes() const
{
return m_bytes;
}

void TMDReader::SetBytes(const std::vector<u8>& bytes)
void SignedBlobReader::SetBytes(const std::vector<u8>& bytes)
{
m_bytes = bytes;
}

void TMDReader::SetBytes(std::vector<u8>&& bytes)
void SignedBlobReader::SetBytes(std::vector<u8>&& bytes)
{
m_bytes = std::move(bytes);
}

bool SignedBlobReader::IsSignatureValid() const
{
// Too small for the certificate type.
if (m_bytes.size() < sizeof(Cert::type))
return false;

// Too small to contain the whole signature data.
const size_t signature_size = GetSignatureSize();
if (signature_size == 0 || m_bytes.size() < signature_size)
return false;

return true;
}

SignatureType SignedBlobReader::GetSignatureType() const
{
return static_cast<SignatureType>(Common::swap32(m_bytes.data()));
}

std::vector<u8> SignedBlobReader::GetSignatureData() const
{
switch (GetSignatureType())
{
case SignatureType::RSA4096:
{
const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA4096, sig);
return std::vector<u8>(signature_begin, signature_begin + sizeof(SignatureRSA4096::sig));
}
case SignatureType::RSA2048:
{
const auto signature_begin = m_bytes.begin() + offsetof(SignatureRSA2048, sig);
return std::vector<u8>(signature_begin, signature_begin + sizeof(SignatureRSA2048::sig));
}
default:
return {};
}
}

size_t SignedBlobReader::GetSignatureSize() const
{
switch (GetSignatureType())
{
case SignatureType::RSA4096:
return sizeof(SignatureRSA4096);
case SignatureType::RSA2048:
return sizeof(SignatureRSA2048);
default:
return 0;
}
}

std::string SignedBlobReader::GetIssuer() const
{
switch (GetSignatureType())
{
case SignatureType::RSA4096:
{
const char* issuer =
reinterpret_cast<const char*>(m_bytes.data() + offsetof(SignatureRSA4096, issuer));
return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA4096::issuer)));
}
case SignatureType::RSA2048:
{
const char* issuer =
reinterpret_cast<const char*>(m_bytes.data() + offsetof(SignatureRSA2048, issuer));
return std::string(issuer, strnlen(issuer, sizeof(SignatureRSA2048::issuer)));
}
default:
return "";
}
}

void SignedBlobReader::DoState(PointerWrap& p)
{
p.Do(m_bytes);
}

bool IsValidTMDSize(size_t size)
{
return size <= 0x49e4;
}

TMDReader::TMDReader(const std::vector<u8>& bytes) : SignedBlobReader(bytes)
{
}

TMDReader::TMDReader(std::vector<u8>&& bytes) : SignedBlobReader(std::move(bytes))
{
}

bool TMDReader::IsValid() const
{
if (!IsSignatureValid())
return false;

if (m_bytes.size() < sizeof(TMDHeader))
{
// TMD is too small to contain its base fields.
Expand All @@ -99,16 +193,6 @@ bool TMDReader::IsValid() const
return true;
}

const std::vector<u8>& TMDReader::GetRawTMD() const
{
return m_bytes;
}

std::vector<u8> TMDReader::GetRawHeader() const
{
return std::vector<u8>(m_bytes.begin(), m_bytes.begin() + sizeof(TMDHeader));
}

std::vector<u8> TMDReader::GetRawView() const
{
// Base fields
Expand Down Expand Up @@ -231,49 +315,24 @@ bool TMDReader::FindContentById(u32 id, Content* content) const
return false;
}

void TMDReader::DoState(PointerWrap& p)
{
p.Do(m_bytes);
}

TicketReader::TicketReader(const std::vector<u8>& bytes) : m_bytes(bytes)
TicketReader::TicketReader(const std::vector<u8>& bytes) : SignedBlobReader(bytes)
{
}

TicketReader::TicketReader(std::vector<u8>&& bytes) : m_bytes(std::move(bytes))
TicketReader::TicketReader(std::vector<u8>&& bytes) : SignedBlobReader(std::move(bytes))
{
}

void TicketReader::SetBytes(const std::vector<u8>& bytes)
{
m_bytes = bytes;
}

void TicketReader::SetBytes(std::vector<u8>&& bytes)
{
m_bytes = std::move(bytes);
}

bool TicketReader::IsValid() const
{
return !m_bytes.empty() && m_bytes.size() % sizeof(Ticket) == 0;
}

void TicketReader::DoState(PointerWrap& p)
{
p.Do(m_bytes);
return IsSignatureValid() && !m_bytes.empty() && m_bytes.size() % sizeof(Ticket) == 0;
}

size_t TicketReader::GetNumberOfTickets() const
{
return m_bytes.size() / sizeof(Ticket);
}

const std::vector<u8>& TicketReader::GetRawTicket() const
{
return m_bytes;
}

std::vector<u8> TicketReader::GetRawTicket(u64 ticket_id_to_find) const
{
for (size_t i = 0; i < GetNumberOfTickets(); ++i)
Expand Down Expand Up @@ -304,13 +363,6 @@ std::vector<u8> TicketReader::GetRawTicketView(u32 ticket_num) const
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
{
return Common::swap32(m_bytes.data() + offsetof(Ticket, device_id));
Expand Down Expand Up @@ -481,10 +533,12 @@ bool SharedContentMap::WriteEntries() const
File::CreateFullPath(temp_path);

// Atomically write the new content map.
File::IOFile file(temp_path, "w+b");
if (!file.WriteArray(m_entries.data(), m_entries.size()))
return false;
File::CreateFullPath(m_file_path);
{
File::IOFile file(temp_path, "w+b");
if (!file.WriteArray(m_entries.data(), m_entries.size()))
return false;
File::CreateFullPath(m_file_path);
}
return File::RenameSync(temp_path, m_file_path);
}

Expand Down Expand Up @@ -563,5 +617,89 @@ u32 UIDSys::GetOrInsertUIDForTitle(const u64 title_id)

return uid;
}

CertReader::CertReader(std::vector<u8>&& bytes) : SignedBlobReader(std::move(bytes))
{
if (!IsSignatureValid())
return;

switch (GetSignatureType())
{
case SignatureType::RSA4096:
if (m_bytes.size() < sizeof(CertRSA4096))
return;
m_bytes.resize(sizeof(CertRSA4096));
break;

case SignatureType::RSA2048:
if (m_bytes.size() < sizeof(CertRSA2048))
return;
m_bytes.resize(sizeof(CertRSA2048));
break;

default:
return;
}

m_is_valid = true;
}

bool CertReader::IsValid() const
{
return m_is_valid;
}

u32 CertReader::GetId() const
{
const size_t offset = GetSignatureSize() + offsetof(CertHeader, id);
return Common::swap32(m_bytes.data() + offset);
}

std::string CertReader::GetName() const
{
const char* name = reinterpret_cast<const char*>(m_bytes.data() + GetSignatureSize() +
offsetof(CertHeader, name));
return std::string(name, strnlen(name, sizeof(CertHeader::name)));
}

PublicKeyType CertReader::GetPublicKeyType() const
{
const size_t offset = GetSignatureSize() + offsetof(CertHeader, public_key_type);
return static_cast<PublicKeyType>(Common::swap32(m_bytes.data() + offset));
}

std::vector<u8> CertReader::GetPublicKey() const
{
static const std::map<SignatureType, std::pair<size_t, size_t>> type_to_key_info = {{

This comment was marked as off-topic.

This comment was marked as off-topic.

{SignatureType::RSA4096,
{offsetof(CertRSA4096, public_key),
sizeof(CertRSA4096::public_key) + sizeof(CertRSA4096::exponent)}},
{SignatureType::RSA2048,
{offsetof(CertRSA2048, public_key),
sizeof(CertRSA2048::public_key) + sizeof(CertRSA2048::exponent)}},
}};

const auto info = type_to_key_info.at(GetSignatureType());
const auto key_begin = m_bytes.begin() + info.first;
return std::vector<u8>(key_begin, key_begin + info.second);
}

std::map<std::string, CertReader> ParseCertChain(const std::vector<u8>& chain)
{
std::map<std::string, CertReader> certs;

size_t processed = 0;
while (processed != chain.size())
{
CertReader cert_reader{std::vector<u8>(chain.begin() + processed, chain.end())};
if (!cert_reader.IsValid())
return certs;

processed += cert_reader.GetBytes().size();
const std::string name = cert_reader.GetName();
certs.emplace(std::move(name), std::move(cert_reader));
}
return certs;
}
} // namespace ES
} // namespace IOS