Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #8591 from JosJuice/wii-reencryption
DiscIO: Implement re-encryption of Wii partition data
  • Loading branch information
degasus committed Apr 24, 2020
2 parents 7e94d6e + 432f342 commit 0a71dda
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 42 deletions.
2 changes: 1 addition & 1 deletion Source/Core/DiscIO/Blob.h
Expand Up @@ -58,7 +58,7 @@ class BlobReader
}

virtual bool SupportsReadWiiDecrypted() const { return false; }
virtual bool ReadWiiDecrypted(u64 offset, u64 size, u8* out_ptr, u64 partition_offset)
virtual bool ReadWiiDecrypted(u64 offset, u64 size, u8* out_ptr, u64 partition_data_offset)
{
return false;
}
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/DiscIO/CMakeLists.txt
Expand Up @@ -39,6 +39,8 @@ add_library(discio
VolumeWii.h
WbfsBlob.cpp
WbfsBlob.h
WiiEncryptionCache.cpp
WiiEncryptionCache.h
WiiSaveBanner.cpp
WiiSaveBanner.h
)
Expand Down
78 changes: 65 additions & 13 deletions Source/Core/DiscIO/DirectoryBlob.cpp
Expand Up @@ -27,8 +27,10 @@
#include "Common/StringUtil.h"
#include "Common/Swap.h"
#include "Core/Boot/DolReader.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Blob.h"
#include "DiscIO/VolumeWii.h"
#include "DiscIO/WiiEncryptionCache.h"

namespace DiscIO
{
Expand Down Expand Up @@ -70,6 +72,11 @@ DiscContent::DiscContent(u64 offset, u64 size, const u8* data)
{
}

DiscContent::DiscContent(u64 offset, u64 size, DirectoryBlobReader* blob)
: m_offset(offset), m_size(size), m_content_source(blob)
{
}

DiscContent::DiscContent(u64 offset) : m_offset(offset)
{
}
Expand Down Expand Up @@ -107,11 +114,21 @@ bool DiscContent::Read(u64* offset, u64* length, u8** buffer) const
if (!file.Seek(offset_in_content, SEEK_SET) || !file.ReadBytes(*buffer, bytes_to_read))
return false;
}
else
else if (std::holds_alternative<const u8*>(m_content_source))
{
const u8* const content_pointer = std::get<const u8*>(m_content_source) + offset_in_content;
std::copy(content_pointer, content_pointer + bytes_to_read, *buffer);
}
else
{
DirectoryBlobReader* blob = std::get<DirectoryBlobReader*>(m_content_source);
const u64 decrypted_size = m_size * VolumeWii::BLOCK_DATA_SIZE / VolumeWii::BLOCK_TOTAL_SIZE;
if (!blob->EncryptPartitionData(offset_in_content, bytes_to_read, *buffer, m_offset,
decrypted_size))
{
return false;
}
}

*length -= bytes_to_read;
*buffer += bytes_to_read;
Expand All @@ -133,6 +150,12 @@ void DiscContentContainer::Add(u64 offset, u64 size, const u8* data)
m_contents.emplace(offset, size, data);
}

void DiscContentContainer::Add(u64 offset, u64 size, DirectoryBlobReader* blob)
{
if (size != 0)
m_contents.emplace(offset, size, blob);
}

u64 DiscContentContainer::CheckSizeAndAdd(u64 offset, const std::string& path)
{
const u64 size = File::GetSize(path);
Expand Down Expand Up @@ -332,6 +355,7 @@ std::unique_ptr<DirectoryBlobReader> DirectoryBlobReader::Create(const std::stri

DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root)
: m_encryption_cache(this)
{
DirectoryBlobPartition game_partition(game_partition_root, {});
m_is_wii = game_partition.IsWii();
Expand All @@ -340,6 +364,7 @@ DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
{
m_gamecube_pseudopartition = std::move(game_partition);
m_data_size = m_gamecube_pseudopartition.GetDataSize();
m_encrypted = false;
}
else
{
Expand Down Expand Up @@ -377,7 +402,6 @@ bool DirectoryBlobReader::Read(u64 offset, u64 length, u8* buffer)
if (offset + length > m_data_size)
return false;

// TODO: We don't handle raw access to the encrypted area of Wii discs correctly.
return (m_is_wii ? m_nonpartition_contents : m_gamecube_pseudopartition.GetContents())
.Read(offset, length, buffer);
}
Expand All @@ -387,12 +411,13 @@ bool DirectoryBlobReader::SupportsReadWiiDecrypted() const
return m_is_wii;
}

bool DirectoryBlobReader::ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_offset)
bool DirectoryBlobReader::ReadWiiDecrypted(u64 offset, u64 size, u8* buffer,
u64 partition_data_offset)
{
if (!m_is_wii)
return false;

auto it = m_partitions.find(partition_offset);
auto it = m_partitions.find(partition_data_offset);
if (it == m_partitions.end())
return false;

Expand All @@ -402,6 +427,21 @@ bool DirectoryBlobReader::ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64
return it->second.GetContents().Read(offset, size, buffer);
}

bool DirectoryBlobReader::EncryptPartitionData(u64 offset, u64 size, u8* buffer,
u64 partition_data_offset,
u64 partition_data_decrypted_size)
{
auto it = m_partitions.find(partition_data_offset);
if (it == m_partitions.end())
return false;

if (!m_encrypted)
return it->second.GetContents().Read(offset, size, buffer);

return m_encryption_cache.EncryptGroups(offset, size, buffer, partition_data_offset,
partition_data_decrypted_size, it->second.GetKey());
}

BlobType DirectoryBlobReader::GetBlobType() const
{
return BlobType::DIRECTORY;
Expand Down Expand Up @@ -439,6 +479,9 @@ void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& parti
if (header_bin_bytes_read < 0x61)
m_disc_header_nonpartition[0x61] = 0;

m_encrypted = std::all_of(m_disc_header_nonpartition.data() + 0x60,
m_disc_header_nonpartition.data() + 0x64, [](u8 x) { return x == 0; });

m_nonpartition_contents.Add(NONPARTITION_DISCHEADER_ADDRESS, m_disc_header_nonpartition);
}

Expand Down Expand Up @@ -511,12 +554,14 @@ void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partiti
Write32(static_cast<u32>(partitions[i].type), offset_in_table, &m_partition_table);
offset_in_table += 4;

SetPartitionHeader(partitions[i].partition, partition_address);
SetPartitionHeader(&partitions[i].partition, partition_address);

const u64 partition_data_size = partitions[i].partition.GetDataSize();
m_partitions.emplace(partition_address, std::move(partitions[i].partition));
const u64 data_size = partitions[i].partition.GetDataSize();
m_partitions.emplace(partition_address + PARTITION_DATA_OFFSET,
std::move(partitions[i].partition));
m_nonpartition_contents.Add(partition_address + PARTITION_DATA_OFFSET, data_size, this);
const u64 unaligned_next_partition_address = VolumeWii::EncryptedPartitionOffsetToRawOffset(
partition_data_size, Partition(partition_address), PARTITION_DATA_OFFSET);
data_size, Partition(partition_address), PARTITION_DATA_OFFSET);
partition_address = Common::AlignUp(unaligned_next_partition_address, 0x10000ull);
}
m_data_size = partition_address;
Expand All @@ -526,7 +571,7 @@ void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partiti

// This function sets the header that's shortly before the start of the encrypted
// area, not the header that's right at the beginning of the encrypted area
void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& partition,
void DirectoryBlobReader::SetPartitionHeader(DirectoryBlobPartition* partition,
u64 partition_address)
{
constexpr u32 TICKET_OFFSET = 0x0;
Expand All @@ -536,10 +581,10 @@ void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& parti
constexpr u32 H3_OFFSET = 0x4000;
constexpr u32 H3_SIZE = 0x18000;

const std::string& partition_root = partition.GetRootDirectory();
const std::string& partition_root = partition->GetRootDirectory();

m_nonpartition_contents.CheckSizeAndAdd(partition_address + TICKET_OFFSET, TICKET_SIZE,
partition_root + "ticket.bin");
const u64 ticket_size = m_nonpartition_contents.CheckSizeAndAdd(
partition_address + TICKET_OFFSET, TICKET_SIZE, partition_root + "ticket.bin");

const u64 tmd_size = m_nonpartition_contents.CheckSizeAndAdd(
partition_address + TMD_OFFSET, MAX_TMD_SIZE, partition_root + "tmd.bin");
Expand All @@ -553,7 +598,7 @@ void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& parti
partition_root + "h3.bin");

constexpr u32 PARTITION_HEADER_SIZE = 0x1c;
const u64 data_size = Common::AlignUp(partition.GetDataSize(), 0x7c00) / 0x7c00 * 0x8000;
const u64 data_size = Common::AlignUp(partition->GetDataSize(), 0x7c00) / 0x7c00 * 0x8000;
m_partition_headers.emplace_back(PARTITION_HEADER_SIZE);
std::vector<u8>& partition_header = m_partition_headers.back();
Write32(static_cast<u32>(tmd_size), 0x0, &partition_header);
Expand All @@ -565,6 +610,13 @@ void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& parti
Write32(static_cast<u32>(data_size >> 2), 0x18, &partition_header);

m_nonpartition_contents.Add(partition_address + TICKET_SIZE, partition_header);

std::vector<u8> ticket_buffer(ticket_size);
m_nonpartition_contents.Read(partition_address + TICKET_OFFSET, ticket_size,
ticket_buffer.data());
IOS::ES::TicketReader ticket(std::move(ticket_buffer));
if (ticket.IsValid())
partition->SetKey(ticket.GetTitleKey());
}

DirectoryBlobPartition::DirectoryBlobPartition(const std::string& root_directory,
Expand Down
28 changes: 25 additions & 3 deletions Source/Core/DiscIO/DirectoryBlob.h
Expand Up @@ -4,6 +4,7 @@

#pragma once

#include <array>
#include <cstddef>
#include <map>
#include <memory>
Expand All @@ -16,6 +17,7 @@
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "DiscIO/Blob.h"
#include "DiscIO/WiiEncryptionCache.h"

namespace File
{
Expand All @@ -27,16 +29,23 @@ namespace DiscIO
{
enum class PartitionType : u32;

class DirectoryBlobReader;

// Returns true if the path is inside a DirectoryBlob and doesn't represent the DirectoryBlob itself
bool ShouldHideFromGameList(const std::string& volume_path);

class DiscContent
{
public:
using ContentSource = std::variant<std::string, const u8*>;
using ContentSource =
std::variant<std::string, // File
const u8*, // Memory
DirectoryBlobReader* // Partition (which one it is is determined by m_offset)
>;

DiscContent(u64 offset, u64 size, const std::string& path);
DiscContent(u64 offset, u64 size, const u8* data);
DiscContent(u64 offset, u64 size, DirectoryBlobReader* blob);

// Provided because it's convenient when searching for DiscContent in an std::set
explicit DiscContent(u64 offset);
Expand Down Expand Up @@ -69,6 +78,7 @@ class DiscContentContainer
}
void Add(u64 offset, u64 size, const std::string& path);
void Add(u64 offset, u64 size, const u8* data);
void Add(u64 offset, u64 size, DirectoryBlobReader* blob);
u64 CheckSizeAndAdd(u64 offset, const std::string& path);
u64 CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path);

Expand Down Expand Up @@ -96,6 +106,9 @@ class DirectoryBlobPartition
const std::vector<u8>& GetHeader() const { return m_disc_header; }
const DiscContentContainer& GetContents() const { return m_contents; }

const std::array<u8, VolumeWii::AES_KEY_SIZE>& GetKey() const { return m_key; }
void SetKey(std::array<u8, VolumeWii::AES_KEY_SIZE> key) { m_key = key; }

private:
void SetDiscHeaderAndDiscType(std::optional<bool> is_wii);
void SetBI2();
Expand All @@ -120,6 +133,8 @@ class DirectoryBlobPartition
std::vector<u8> m_apploader;
std::vector<u8> m_fst_data;

std::array<u8, VolumeWii::AES_KEY_SIZE> m_key;

std::string m_root_directory;
bool m_is_wii = false;
// GameCube has no shift, Wii has 2 bit shift
Expand All @@ -130,6 +145,8 @@ class DirectoryBlobPartition

class DirectoryBlobReader : public BlobReader
{
friend DiscContent;

public:
static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);

Expand All @@ -141,7 +158,7 @@ class DirectoryBlobReader : public BlobReader

bool Read(u64 offset, u64 length, u8* buffer) override;
bool SupportsReadWiiDecrypted() const override;
bool ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_offset) override;
bool ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_data_offset) override;

BlobType GetBlobType() const override;
u64 GetRawSize() const override;
Expand All @@ -163,20 +180,25 @@ class DirectoryBlobReader : public BlobReader
explicit DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root);

bool EncryptPartitionData(u64 offset, u64 size, u8* buffer, u64 partition_data_offset,
u64 partition_data_decrypted_size);

void SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
const std::string& game_partition_root);
void SetWiiRegionData(const std::string& game_partition_root);
void SetPartitions(std::vector<PartitionWithType>&& partitions);
void SetPartitionHeader(const DirectoryBlobPartition& partition, u64 partition_address);
void SetPartitionHeader(DirectoryBlobPartition* partition, u64 partition_address);

// For GameCube:
DirectoryBlobPartition m_gamecube_pseudopartition;

// For Wii:
DiscContentContainer m_nonpartition_contents;
std::map<u64, DirectoryBlobPartition> m_partitions;
WiiEncryptionCache m_encryption_cache;

bool m_is_wii;
bool m_encrypted;

std::vector<u8> m_disc_header_nonpartition;
std::vector<u8> m_partition_table;
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/DiscIO/DiscIO.vcxproj
Expand Up @@ -64,6 +64,7 @@
<ClCompile Include="VolumeWad.cpp" />
<ClCompile Include="VolumeWii.cpp" />
<ClCompile Include="WbfsBlob.cpp" />
<ClCompile Include="WiiEncryptionCache.cpp" />
<ClCompile Include="WiiSaveBanner.cpp" />
</ItemGroup>
<ItemGroup>
Expand All @@ -87,6 +88,7 @@
<ClInclude Include="VolumeWad.h" />
<ClInclude Include="VolumeWii.h" />
<ClInclude Include="WbfsBlob.h" />
<ClInclude Include="WiiEncryptionCache.h" />
<ClInclude Include="WiiSaveBanner.h" />
</ItemGroup>
<ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/DiscIO/DiscIO.vcxproj.filters
Expand Up @@ -84,6 +84,9 @@
<ClCompile Include="VolumeVerifier.cpp">
<Filter>Volume</Filter>
</ClCompile>
<ClCompile Include="WiiEncryptionCache.cpp">
<Filter>Volume\Blob</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="DiscScrubber.h">
Expand Down Expand Up @@ -149,6 +152,9 @@
<ClInclude Include="VolumeVerifier.h">
<Filter>Volume</Filter>
</ClInclude>
<ClInclude Include="WiiEncryptionCache.h">
<Filter>Volume\Blob</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
Expand Down

0 comments on commit 0a71dda

Please sign in to comment.