summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorLeo Lam <leolino.lam@gmail.com>2017-07-23 14:47:10 +0800
committerGitHub <noreply@github.com>2017-07-23 14:47:10 +0800
commit81abecbf4684dfdf3d2bb57d65aec43e1f35ecf3 (patch)
tree50134f21b5c59d5a41110faf0aa670a9d7ee6335 /Source
parent3cbf56a9adb8247eec6787737cfd1388740d2b61 (diff)
parentfbcc6bbd5719e37b8e1dc1a68e3b3bdd5b1cf7a7 (diff)
Merge pull request #5739 from leoetlino/correct-key
IOS/ES: Use the correct import/export key (fix DLC)
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Core/IOS/ES/ES.h3
-rw-r--r--Source/Core/Core/IOS/ES/Formats.h2
-rw-r--r--Source/Core/Core/IOS/ES/TitleManagement.cpp118
-rw-r--r--Source/Core/Core/IOS/IOSC.cpp25
-rw-r--r--Source/Core/Core/IOS/IOSC.h2
-rw-r--r--Source/Core/Core/ec_wii.cpp5
-rw-r--r--Source/Core/Core/ec_wii.h3
7 files changed, 117 insertions, 41 deletions
diff --git a/Source/Core/Core/IOS/ES/ES.h b/Source/Core/Core/IOS/ES/ES.h
index 929638ecb5..6352eec917 100644
--- a/Source/Core/Core/IOS/ES/ES.h
+++ b/Source/Core/Core/IOS/ES/ES.h
@@ -13,6 +13,7 @@
#include "Core/IOS/Device.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/IOS/IOS.h"
+#include "Core/IOS/IOSC.h"
class PointerWrap;
@@ -61,7 +62,7 @@ public:
bool valid = false;
IOS::ES::TMDReader tmd;
- std::array<u8, 16> key{};
+ IOSC::Handle key_handle = 0;
struct ContentContext
{
bool valid = false;
diff --git a/Source/Core/Core/IOS/ES/Formats.h b/Source/Core/Core/IOS/ES/Formats.h
index 5f01e7eef6..f1d3a0c83a 100644
--- a/Source/Core/Core/IOS/ES/Formats.h
+++ b/Source/Core/Core/IOS/ES/Formats.h
@@ -49,6 +49,8 @@ enum TitleFlags : u32
TITLE_TYPE_0x4 = 0x4,
// Used for DLC titles.
TITLE_TYPE_DATA = 0x8,
+ // Unknown.
+ TITLE_TYPE_0x10 = 0x10,
// Appears to be used for WFS titles.
TITLE_TYPE_WFS_MAYBE = 0x20,
// Unknown.
diff --git a/Source/Core/Core/IOS/ES/TitleManagement.cpp b/Source/Core/Core/IOS/ES/TitleManagement.cpp
index 7066eacfb4..1ff653c936 100644
--- a/Source/Core/Core/IOS/ES/TitleManagement.cpp
+++ b/Source/Core/Core/IOS/ES/TitleManagement.cpp
@@ -13,12 +13,12 @@
#include <mbedtls/sha1.h>
#include "Common/Align.h"
-#include "Common/Crypto/AES.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
+#include "Core/CommonTitles.h"
#include "Core/HW/Memmap.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/ec_wii.h"
@@ -48,7 +48,7 @@ static ReturnCode WriteTicket(const IOS::ES::TicketReader& ticket)
void ES::TitleImportExportContext::DoState(PointerWrap& p)
{
p.Do(valid);
- p.Do(key);
+ p.Do(key_handle);
tmd.DoState(p);
p.Do(content.valid);
p.Do(content.id);
@@ -105,11 +105,43 @@ IPCCommandResult ES::ImportTicket(const IOCtlVRequest& request)
return GetDefaultReply(ImportTicket(bytes, cert_chain));
}
+constexpr std::array<u8, 16> NULL_KEY{};
+
+// Used for exporting titles and importing them back (ImportTmd and ExportTitleInit).
+static ReturnCode InitBackupKey(const IOS::ES::TMDReader& tmd, IOSC& iosc, IOSC::Handle* key)
+{
+ // Some versions of IOS have a bug that causes it to use a zeroed key instead of the PRNG key.
+ // When Nintendo decided to fix it, they added checks to keep using the zeroed key only in
+ // affected titles to avoid making existing exports useless.
+
+ // Ignore the region byte.
+ const u64 title_id = tmd.GetTitleId() | 0xff;
+ const u32 title_flags = tmd.GetTitleFlags();
+ const u32 affected_type = IOS::ES::TITLE_TYPE_0x10 | IOS::ES::TITLE_TYPE_DATA;
+ if (title_id == Titles::SYSTEM_MENU || (title_flags & affected_type) != affected_type ||
+ !(title_id == 0x00010005735841ff || title_id - 0x00010005735a41ff <= 0x700))
+ {
+ *key = IOSC::HANDLE_PRNG_KEY;
+ return IPC_SUCCESS;
+ }
+
+ // Otherwise, use a null key.
+ ReturnCode ret = iosc.CreateObject(key, IOSC::TYPE_SECRET_KEY, IOSC::SUBTYPE_AES128, PID_ES);
+ return ret == IPC_SUCCESS ? iosc.ImportSecretKey(*key, NULL_KEY.data(), PID_ES) : ret;
+}
+
+static void ResetTitleImportContext(ES::Context* context, IOSC& iosc)
+{
+ if (context->title_import_export.key_handle)
+ iosc.DeleteObject(context->title_import_export.key_handle, PID_ES);
+ context->title_import_export = {};
+}
+
ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
{
// Ioctlv 0x2b writes the TMD to /tmp/title.tmd (for imports) and doesn't seem to write it
// to either /import or /title. So here we simply have to set the import TMD.
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
context.title_import_export.tmd.SetBytes(tmd_bytes);
if (!context.title_import_export.tmd.IsValid())
return ES_EINVAL;
@@ -127,11 +159,10 @@ ReturnCode ES::ImportTmd(Context& context, const std::vector<u8>& tmd_bytes)
if (!InitImport(context.title_import_export.tmd.GetTitleId()))
return ES_EIO;
- // FIXME: ImportTmd does not use the ticket or the title key.
- const auto ticket = DiscIO::FindSignedTicket(context.title_import_export.tmd.GetTitleId());
- if (!ticket.IsValid())
- return ES_NO_TICKET;
- context.title_import_export.key = ticket.GetTitleKey(m_ios.GetIOSC());
+ ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(),
+ &context.title_import_export.key_handle);
+ if (ret != IPC_SUCCESS)
+ return ret;
context.title_import_export.valid = true;
return IPC_SUCCESS;
@@ -150,11 +181,29 @@ IPCCommandResult ES::ImportTmd(Context& context, const IOCtlVRequest& request)
return GetDefaultReply(ImportTmd(context, tmd));
}
+static ReturnCode InitTitleImportKey(const std::vector<u8>& ticket_bytes, IOSC& iosc,
+ IOSC::Handle* handle)
+{
+ ReturnCode ret = iosc.CreateObject(handle, IOSC::TYPE_SECRET_KEY, IOSC::SUBTYPE_AES128, PID_ES);
+ if (ret != IPC_SUCCESS)
+ return ret;
+
+ std::array<u8, 16> iv{};
+ std::copy_n(&ticket_bytes[offsetof(IOS::ES::Ticket, title_id)], sizeof(u64), iv.begin());
+ const u8 index = ticket_bytes[offsetof(IOS::ES::Ticket, common_key_index)];
+ if (index > 1)
+ return ES_INVALID_TICKET;
+
+ return iosc.ImportSecretKey(
+ *handle, index == 0 ? IOSC::HANDLE_COMMON_KEY : IOSC::HANDLE_NEW_COMMON_KEY, iv.data(),
+ &ticket_bytes[offsetof(IOS::ES::Ticket, title_key)], PID_ES);
+}
+
ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_bytes,
const std::vector<u8>& cert_chain)
{
INFO_LOG(IOS_ES, "ImportTitleInit");
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
context.title_import_export.tmd.SetBytes(tmd_bytes);
if (!context.title_import_export.tmd.IsValid())
{
@@ -174,8 +223,6 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_byte
if (!ticket.IsValid())
return ES_NO_TICKET;
- context.title_import_export.key = ticket.GetTitleKey(m_ios.GetIOSC());
-
std::vector<u8> cert_store;
ret = ReadCertStore(&cert_store);
if (ret != IPC_SUCCESS)
@@ -186,6 +233,11 @@ ReturnCode ES::ImportTitleInit(Context& context, const std::vector<u8>& tmd_byte
if (ret != IPC_SUCCESS)
return ret;
+ ret = InitTitleImportKey(ticket.GetBytes(), m_ios.GetIOSC(),
+ &context.title_import_export.key_handle);
+ if (ret != IPC_SUCCESS)
+ return ret;
+
if (!InitImport(context.title_import_export.tmd.GetTitleId()))
return ES_EIO;
@@ -300,10 +352,14 @@ ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
if (!context.title_import_export.valid || !context.title_import_export.content.valid)
return ES_EINVAL;
- std::vector<u8> decrypted_data = Common::AES::Decrypt(
- context.title_import_export.key.data(), context.title_import_export.content.iv.data(),
+ std::vector<u8> decrypted_data(context.title_import_export.content.buffer.size());
+ const ReturnCode decrypt_ret = m_ios.GetIOSC().Decrypt(
+ context.title_import_export.key_handle, context.title_import_export.content.iv.data(),
context.title_import_export.content.buffer.data(),
- context.title_import_export.content.buffer.size());
+ context.title_import_export.content.buffer.size(), decrypted_data.data(), PID_ES);
+ if (decrypt_ret != IPC_SUCCESS)
+ return decrypt_ret;
+
IOS::ES::Content content_info;
context.title_import_export.tmd.FindContentById(context.title_import_export.content.id,
&content_info);
@@ -391,7 +447,7 @@ ReturnCode ES::ImportTitleDone(Context& context)
return ES_EIO;
INFO_LOG(IOS_ES, "ImportTitleDone: title %016" PRIx64, title_id);
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
return IPC_SUCCESS;
}
@@ -417,7 +473,7 @@ ReturnCode ES::ImportTitleCancel(Context& context)
INFO_LOG(IOS_ES, "ImportTitleCancel: title %016" PRIx64, title_id);
}
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
return IPC_SUCCESS;
}
@@ -579,17 +635,13 @@ ReturnCode ES::ExportTitleInit(Context& context, u64 title_id, u8* tmd_bytes, u3
if (!tmd.IsValid())
return FS_ENOENT;
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
context.title_import_export.tmd = tmd;
- const auto ticket = DiscIO::FindSignedTicket(context.title_import_export.tmd.GetTitleId());
- if (!ticket.IsValid())
- return ES_NO_TICKET;
- if (ticket.GetTitleId() != context.title_import_export.tmd.GetTitleId())
- return ES_EINVAL;
-
- // FIXME: this is wrong. The title key is *not* used here. Key #5 or a null key is.
- context.title_import_export.key = ticket.GetTitleKey(m_ios.GetIOSC());
+ const ReturnCode ret = InitBackupKey(GetTitleContext().tmd, m_ios.GetIOSC(),
+ &context.title_import_export.key_handle);
+ if (ret != IPC_SUCCESS)
+ return ret;
const std::vector<u8>& raw_tmd = context.title_import_export.tmd.GetBytes();
if (tmd_size != raw_tmd.size())
@@ -633,7 +685,7 @@ ReturnCode ES::ExportContentBegin(Context& context, u64 title_id, u32 content_id
const s32 ret = OpenContent(context.title_import_export.tmd, content_info.index, 0);
if (ret < 0)
{
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
return static_cast<ReturnCode>(ret);
}
@@ -671,7 +723,7 @@ ReturnCode ES::ExportContentData(Context& context, u32 content_fd, u8* data, u32
if (read_size < 0)
{
CloseContent(content_fd, 0);
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
return ES_SHORT_READ;
}
@@ -679,9 +731,13 @@ ReturnCode ES::ExportContentData(Context& context, u32 content_fd, u8* data, u32
// let's just follow IOS here.
buffer.resize(Common::AlignUp(buffer.size(), 32));
- const std::vector<u8> output = Common::AES::Encrypt(context.title_import_export.key.data(),
- context.title_import_export.content.iv.data(),
- buffer.data(), buffer.size());
+ std::vector<u8> output(buffer.size());
+ const ReturnCode decrypt_ret = m_ios.GetIOSC().Encrypt(
+ context.title_import_export.key_handle, context.title_import_export.content.iv.data(),
+ buffer.data(), buffer.size(), output.data(), PID_ES);
+ if (decrypt_ret != IPC_SUCCESS)
+ return decrypt_ret;
+
std::copy(output.cbegin(), output.cend(), data);
return IPC_SUCCESS;
}
@@ -719,7 +775,7 @@ IPCCommandResult ES::ExportContentEnd(Context& context, const IOCtlVRequest& req
ReturnCode ES::ExportTitleDone(Context& context)
{
- context.title_import_export = {};
+ ResetTitleImportContext(&context, m_ios.GetIOSC());
return IPC_SUCCESS;
}
diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp
index 16d1734a29..edb6fdf734 100644
--- a/Source/Core/Core/IOS/IOSC.cpp
+++ b/Source/Core/Core/IOS/IOSC.cpp
@@ -85,11 +85,19 @@ 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))
- {
+ std::array<u8, AES128_KEY_SIZE> decrypted_key;
+ const ReturnCode ret =
+ Decrypt(decrypt_handle, iv, encrypted_key, AES128_KEY_SIZE, decrypted_key.data(), pid);
+ if (ret != IPC_SUCCESS)
+ return ret;
+
+ return ImportSecretKey(dest_handle, decrypted_key.data(), pid);
+}
+
+ReturnCode IOSC::ImportSecretKey(Handle dest_handle, const u8* decrypted_key, u32 pid)
+{
+ if (!HasOwnership(dest_handle, pid) || IsDefaultHandle(dest_handle))
return IOSC_EACCES;
- }
KeyEntry* dest_entry = FindEntry(dest_handle);
if (!dest_entry)
@@ -99,8 +107,8 @@ ReturnCode IOSC::ImportSecretKey(Handle dest_handle, Handle decrypt_handle, u8*
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);
+ dest_entry->data = std::vector<u8>(decrypted_key, decrypted_key + AES128_KEY_SIZE);
+ return IPC_SUCCESS;
}
ReturnCode IOSC::ImportPublicKey(Handle dest_handle, const u8* public_key,
@@ -422,8 +430,9 @@ void IOSC::LoadDefaultEntries(ConsoleType console_type)
break;
}
- // 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>(ec.GetBackupKey(), ec.GetBackupKey() + AES128_KEY_SIZE), 3};
m_key_entries[HANDLE_SD_KEY] = {TYPE_SECRET_KEY,
SUBTYPE_AES128,
diff --git a/Source/Core/Core/IOS/IOSC.h b/Source/Core/Core/IOS/IOSC.h
index 653b7a3a01..33ddc3ded2 100644
--- a/Source/Core/Core/IOS/IOSC.h
+++ b/Source/Core/Core/IOS/IOSC.h
@@ -172,6 +172,8 @@ public:
// 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 secret key that is already decrypted.
+ ReturnCode ImportSecretKey(Handle dest_handle, const u8* decrypted_key, u32 pid);
// Import a public key. public_key_exponent must be passed for RSA keys.
ReturnCode ImportPublicKey(Handle dest_handle, const u8* public_key,
const u8* public_key_exponent, u32 pid);
diff --git a/Source/Core/Core/ec_wii.cpp b/Source/Core/Core/ec_wii.cpp
index 28ffd0dabd..6411852403 100644
--- a/Source/Core/Core/ec_wii.cpp
+++ b/Source/Core/Core/ec_wii.cpp
@@ -181,6 +181,11 @@ const u8* EcWii::GetNGSig() const
return BootMiiKeysBin.ng_sig;
}
+const u8* EcWii::GetBackupKey() const
+{
+ return BootMiiKeysBin.backup_key;
+}
+
void EcWii::InitDefaults()
{
memset(&BootMiiKeysBin, 0, sizeof(BootMiiKeysBin));
diff --git a/Source/Core/Core/ec_wii.h b/Source/Core/Core/ec_wii.h
index eb4395a9a7..2a9ec003a8 100644
--- a/Source/Core/Core/ec_wii.h
+++ b/Source/Core/Core/ec_wii.h
@@ -42,6 +42,7 @@ public:
u32 GetNGKeyID() const;
const u8* GetNGPriv() const;
const u8* GetNGSig() const;
+ const u8* GetBackupKey() const;
private:
void InitDefaults();
@@ -82,7 +83,7 @@ private:
};
};
u8 nand_key[0x10]; // 0x158
- u8 rng_key[0x10]; // 0x168
+ u8 backup_key[0x10]; // 0x168
u32 unk1; // 0x178
u32 unk2; // 0x17C
u8 eeprom_pad[0x80]; // 0x180