nod/lib/DiscWii.cpp

400 lines
11 KiB
C++
Raw Normal View History

2015-06-28 05:43:53 +00:00
#include <stdio.h>
2015-06-30 06:46:19 +00:00
#include <string.h>
2015-07-02 18:33:55 +00:00
#include "NOD/DiscWii.hpp"
#include "NOD/aes.hpp"
2015-06-26 19:30:03 +00:00
namespace NOD
{
/* Not much of a secret anymore I suppose */
2015-07-04 22:37:50 +00:00
static const uint8_t COMMON_KEYS[2][16] =
{
/* Normal */
{0xeb, 0xe4, 0x2a, 0x22,
0x5e, 0x85, 0x93, 0xe4,
0x48, 0xd9, 0xc5, 0x45,
0x73, 0x81, 0xaa, 0xf7},
/* Korean */
{0x63, 0xb8, 0x2b, 0xb4,
0xf4, 0x61, 0x4e, 0x2e,
0x13, 0xf2, 0xfe, 0xfb,
0xba, 0x4c, 0x9b, 0x7e}
};
2015-06-29 05:35:25 +00:00
2015-06-28 05:43:53 +00:00
class PartitionWii : public DiscBase::IPartition
{
2015-11-21 01:15:33 +00:00
enum class SigType : uint32_t
2015-06-30 06:46:19 +00:00
{
2015-11-21 01:15:33 +00:00
RSA_4096 = 0x00010000,
RSA_2048 = 0x00010001,
ELIPTICAL_CURVE = 0x00010002
2015-06-30 06:46:19 +00:00
};
2015-11-21 01:15:33 +00:00
enum class KeyType : uint32_t
2015-06-30 06:46:19 +00:00
{
2015-11-21 01:15:33 +00:00
RSA_4096 = 0x00000000,
RSA_2048 = 0x00000001
2015-06-30 06:46:19 +00:00
};
2015-06-28 05:43:53 +00:00
struct Ticket
{
uint32_t sigType;
char sig[256];
char padding[60];
char sigIssuer[64];
char ecdh[60];
char padding1[3];
unsigned char encKey[16];
char padding2;
char ticketId[8];
char consoleId[4];
char titleId[8];
char padding3[2];
uint16_t ticketVersion;
uint32_t permittedTitlesMask;
uint32_t permitMask;
char titleExportAllowed;
char commonKeyIdx;
char padding4[48];
char contentAccessPermissions[64];
char padding5[2];
struct TimeLimit
{
uint32_t enableTimeLimit;
uint32_t timeLimit;
} timeLimits[8];
void read(IDiscIO::IReadStream& s)
{
s.read(this, 676);
sigType = SBig(sigType);
ticketVersion = SBig(ticketVersion);
permittedTitlesMask = SBig(permittedTitlesMask);
permitMask = SBig(permitMask);
for (size_t t=0 ; t<8 ; ++t)
{
timeLimits[t].enableTimeLimit = SBig(timeLimits[t].enableTimeLimit);
timeLimits[t].timeLimit = SBig(timeLimits[t].timeLimit);
}
}
} m_ticket;
struct TMD
{
2015-06-30 06:46:19 +00:00
SigType sigType;
2015-06-28 05:43:53 +00:00
char sig[256];
char padding[60];
char sigIssuer[64];
char version;
char caCrlVersion;
char signerCrlVersion;
char padding1;
uint32_t iosIdMajor;
uint32_t iosIdMinor;
uint32_t titleIdMajor;
2015-06-30 06:46:19 +00:00
char titleIdMinor[4];
2015-06-28 05:43:53 +00:00
uint32_t titleType;
uint16_t groupId;
char padding2[62];
uint32_t accessFlags;
uint16_t titleVersion;
uint16_t numContents;
uint16_t bootIdx;
uint16_t padding3;
struct Content
{
uint32_t id;
uint16_t index;
uint16_t type;
uint64_t size;
char hash[20];
void read(IDiscIO::IReadStream& s)
{
s.read(this, 36);
id = SBig(id);
index = SBig(index);
type = SBig(type);
size = SBig(size);
}
};
std::vector<Content> contents;
void read(IDiscIO::IReadStream& s)
{
s.read(this, 484);
2015-11-21 01:15:33 +00:00
sigType = SigType(SBig(uint32_t(sigType)));
2015-06-28 05:43:53 +00:00
iosIdMajor = SBig(iosIdMajor);
iosIdMinor = SBig(iosIdMinor);
titleIdMajor = SBig(titleIdMajor);
titleType = SBig(titleType);
groupId = SBig(groupId);
accessFlags = SBig(accessFlags);
titleVersion = SBig(titleVersion);
numContents = SBig(numContents);
bootIdx = SBig(bootIdx);
2015-07-16 03:20:36 +00:00
contents.clear();
contents.reserve(numContents);
for (uint16_t c=0 ; c<numContents ; ++c)
2015-06-28 05:43:53 +00:00
{
contents.emplace_back();
contents.back().read(s);
}
}
} m_tmd;
struct Certificate
{
2015-06-30 06:46:19 +00:00
SigType sigType;
2015-06-28 05:43:53 +00:00
char sig[512];
char issuer[64];
2015-06-30 06:46:19 +00:00
KeyType keyType;
2015-06-28 05:43:53 +00:00
char subject[64];
char key[512];
uint32_t modulus;
uint32_t pubExp;
void read(IDiscIO::IReadStream& s)
{
s.read(&sigType, 4);
2015-11-21 01:15:33 +00:00
sigType = SigType(SBig(uint32_t(sigType)));
if (sigType == SigType::RSA_4096)
2015-06-28 05:43:53 +00:00
s.read(sig, 512);
2015-11-21 01:15:33 +00:00
else if (sigType == SigType::RSA_2048)
2015-06-28 05:43:53 +00:00
s.read(sig, 256);
2015-11-21 01:15:33 +00:00
else if (sigType == SigType::ELIPTICAL_CURVE)
2015-06-28 05:43:53 +00:00
s.read(sig, 64);
s.seek(60, SEEK_CUR);
s.read(issuer, 64);
s.read(&keyType, 4);
s.read(subject, 64);
2015-11-21 01:15:33 +00:00
keyType = KeyType(SBig(uint32_t(keyType)));
if (keyType == KeyType::RSA_4096)
2015-06-28 05:43:53 +00:00
s.read(key, 512);
2015-11-21 01:15:33 +00:00
else if (keyType == KeyType::RSA_2048)
2015-06-28 05:43:53 +00:00
s.read(key, 256);
s.read(&modulus, 8);
modulus = SBig(modulus);
pubExp = SBig(pubExp);
s.seek(52, SEEK_CUR);
}
};
Certificate m_caCert;
Certificate m_tmdCert;
Certificate m_ticketCert;
uint64_t m_dataOff;
2015-06-29 05:35:25 +00:00
uint8_t m_decKey[16];
2015-06-28 05:43:53 +00:00
public:
PartitionWii(const DiscWii& parent, Kind kind, uint64_t offset)
2015-06-28 05:43:53 +00:00
: IPartition(parent, kind, offset)
{
std::unique_ptr<IDiscIO::IReadStream> s = parent.getDiscIO().beginReadStream(offset);
2015-08-05 21:45:25 +00:00
m_ticket.read(*s);
2015-06-28 05:43:53 +00:00
uint32_t tmdSize;
s->read(&tmdSize, 4);
tmdSize = SBig(tmdSize);
uint32_t tmdOff;
s->read(&tmdOff, 4);
tmdOff = SBig(tmdOff) << 2;
uint32_t certChainSize;
s->read(&certChainSize, 4);
certChainSize = SBig(certChainSize);
uint32_t certChainOff;
s->read(&certChainOff, 4);
certChainOff = SBig(certChainOff) << 2;
uint32_t globalHashTableOff;
s->read(&globalHashTableOff, 4);
globalHashTableOff = SBig(globalHashTableOff) << 2;
uint32_t dataOff;
s->read(&dataOff, 4);
dataOff = SBig(dataOff) << 2;
2015-06-29 05:35:25 +00:00
m_dataOff = offset + dataOff;
2015-06-28 05:43:53 +00:00
uint32_t dataSize;
s->read(&dataSize, 4);
dataSize = SBig(dataSize) << 2;
s->seek(offset + tmdOff);
2015-08-05 21:45:25 +00:00
m_tmd.read(*s);
2015-06-28 05:43:53 +00:00
s->seek(offset + certChainOff);
2015-08-05 21:45:25 +00:00
m_caCert.read(*s);
m_tmdCert.read(*s);
m_ticketCert.read(*s);
2015-06-28 05:43:53 +00:00
2015-06-29 05:35:25 +00:00
/* Decrypt title key */
std::unique_ptr<IAES> aes = NewAES();
uint8_t iv[16] = {};
memcpy(iv, m_ticket.titleId, 8);
2015-07-04 22:37:50 +00:00
aes->setKey(COMMON_KEYS[(int)m_ticket.commonKeyIdx]);
2015-06-29 05:35:25 +00:00
aes->decrypt(iv, m_ticket.encKey, m_decKey, 16);
2015-06-30 00:07:46 +00:00
/* Wii-specific header reads (now using title key to decrypt) */
2015-06-30 06:46:19 +00:00
std::unique_ptr<IPartReadStream> ds = beginReadStream(0x420);
2015-06-30 00:07:46 +00:00
uint32_t vals[3];
ds->read(vals, 12);
m_dolOff = SBig(vals[0]) << 2;
m_fstOff = SBig(vals[1]) << 2;
m_fstSz = SBig(vals[2]) << 2;
ds->seek(0x2440 + 0x14);
ds->read(vals, 8);
2015-07-04 17:30:00 +00:00
m_apploaderSz = 32 + SBig(vals[0]) + SBig(vals[1]);
2015-06-30 00:07:46 +00:00
/* Yay files!! */
2015-08-05 21:45:25 +00:00
parseFST(*ds);
2015-07-10 03:11:30 +00:00
/* Also make DOL header and size handy */
ds->seek(m_dolOff);
2015-08-05 21:45:25 +00:00
parseDOL(*ds);
2015-06-29 05:35:25 +00:00
}
2015-06-30 06:46:19 +00:00
class PartReadStream : public IPartReadStream
2015-06-29 05:35:25 +00:00
{
std::unique_ptr<IAES> m_aes;
const PartitionWii& m_parent;
uint64_t m_baseOffset;
uint64_t m_offset;
2015-06-29 05:35:25 +00:00
std::unique_ptr<IDiscIO::IReadStream> m_dio;
2015-06-30 06:46:19 +00:00
size_t m_curBlock = SIZE_MAX;
2015-06-29 05:35:25 +00:00
uint8_t m_encBuf[0x8000];
uint8_t m_decBuf[0x7c00];
void decryptBlock()
{
m_dio->read(m_encBuf, 0x8000);
m_aes->decrypt(&m_encBuf[0x3d0], &m_encBuf[0x400], m_decBuf, 0x7c00);
}
public:
PartReadStream(const PartitionWii& parent, uint64_t baseOffset, uint64_t offset)
2015-06-29 05:35:25 +00:00
: m_aes(NewAES()), m_parent(parent), m_baseOffset(baseOffset), m_offset(offset)
{
m_aes->setKey(parent.m_decKey);
size_t block = m_offset / 0x7c00;
m_dio = m_parent.m_parent.getDiscIO().beginReadStream(m_baseOffset + block * 0x8000);
2015-06-30 06:46:19 +00:00
decryptBlock();
m_curBlock = block;
2015-06-29 05:35:25 +00:00
}
void seek(int64_t offset, int whence)
2015-06-30 00:07:46 +00:00
{
if (whence == SEEK_SET)
m_offset = offset;
else if (whence == SEEK_CUR)
m_offset += offset;
else
return;
size_t block = m_offset / 0x7c00;
2015-06-30 06:46:19 +00:00
if (block != m_curBlock)
{
m_dio->seek(m_baseOffset + block * 0x8000);
2015-06-30 06:46:19 +00:00
decryptBlock();
m_curBlock = block;
}
2015-06-30 00:07:46 +00:00
}
2015-07-07 03:22:19 +00:00
uint64_t position() const {return m_offset;}
uint64_t read(void* buf, uint64_t length)
2015-06-29 05:35:25 +00:00
{
2015-06-30 06:46:19 +00:00
size_t block = m_offset / 0x7c00;
2015-06-29 05:35:25 +00:00
size_t cacheOffset = m_offset % 0x7c00;
uint64_t cacheSize;
2015-07-03 01:57:31 +00:00
uint64_t rem = length;
2015-06-29 05:35:25 +00:00
uint8_t* dst = (uint8_t*)buf;
2015-07-03 01:57:31 +00:00
while (rem)
2015-06-29 05:35:25 +00:00
{
2015-06-30 06:46:19 +00:00
if (block != m_curBlock)
{
decryptBlock();
m_curBlock = block;
}
2015-06-29 05:35:25 +00:00
2015-07-03 01:57:31 +00:00
cacheSize = rem;
2015-06-29 05:35:25 +00:00
if (cacheSize + cacheOffset > 0x7c00)
cacheSize = 0x7c00 - cacheOffset;
memcpy(dst, m_decBuf + cacheOffset, cacheSize);
dst += cacheSize;
2015-07-03 01:57:31 +00:00
rem -= cacheSize;
2015-06-29 05:35:25 +00:00
cacheOffset = 0;
2015-06-30 06:46:19 +00:00
++block;
2015-06-29 05:35:25 +00:00
}
m_offset += length;
return dst - (uint8_t*)buf;
}
};
std::unique_ptr<IPartReadStream> beginReadStream(uint64_t offset) const
2015-06-29 05:35:25 +00:00
{
2015-06-30 06:46:19 +00:00
return std::unique_ptr<IPartReadStream>(new PartReadStream(*this, m_dataOff, offset));
2015-06-28 05:43:53 +00:00
}
uint64_t normalizeOffset(uint64_t anOffset) const {return anOffset << 2;}
2015-06-28 05:43:53 +00:00
};
DiscWii::DiscWii(std::unique_ptr<IDiscIO>&& dio)
: DiscBase(std::move(dio))
{
/* Read partition info */
2015-11-21 01:15:33 +00:00
struct PartInfo
{
2015-06-28 05:43:53 +00:00
uint32_t partCount;
uint32_t partInfoOff;
struct Part
{
uint32_t partDataOff;
2015-11-21 01:15:33 +00:00
IPartition::Kind partType;
2015-06-28 05:43:53 +00:00
} parts[4];
PartInfo(IDiscIO& dio)
{
std::unique_ptr<IDiscIO::IReadStream> s = dio.beginReadStream(0x40000);
s->read(this, 32);
partCount = SBig(partCount);
partInfoOff = SBig(partInfoOff);
s->seek(partInfoOff << 2);
for (uint32_t p=0 ; p<partCount && p<4 ; ++p)
{
s->read(&parts[p], 8);
parts[p].partDataOff = SBig(parts[p].partDataOff);
2015-11-21 01:15:33 +00:00
parts[p].partType = IPartition::Kind(SBig(uint32_t(parts[p].partType)));
2015-06-28 05:43:53 +00:00
}
}
2015-08-05 21:45:25 +00:00
} partInfo(*m_discIO);
2015-06-28 05:43:53 +00:00
/* Iterate for data partition */
m_partitions.reserve(partInfo.partCount);
for (uint32_t p=0 ; p<partInfo.partCount && p<4 ; ++p)
{
PartInfo::Part& part = partInfo.parts[p];
2015-11-21 01:15:33 +00:00
IPartition::Kind kind;
switch (part.partType)
{
case IPartition::Kind::Data:
case IPartition::Kind::Update:
case IPartition::Kind::Channel:
kind = part.partType;
break;
default:
LogModule.report(LogVisor::FatalError, "invalid partition type %d", part.partType);
}
2015-06-28 05:43:53 +00:00
m_partitions.emplace_back(new PartitionWii(*this, kind, part.partDataOff << 2));
}
}
bool DiscWii::commit()
{
return false;
}
2015-06-26 19:30:03 +00:00
}