Exemple #1
0
const QByteArray Wad::Content( quint16 i )
{
    if( tmdData.isEmpty() || tikData.isEmpty() )
    {
        Err( "Can't decryte data without a TMD and ticket" );
        return QByteArray();
    }
    Ticket ticket( tikData );
    Tmd t( tmdData );
    if( partsEnc.size() != t.Count() || i >= partsEnc.size() )
    {
        Err( "I dont know whats going on some number is out of range and i dont like it" );
        return QByteArray();
    }
    QByteArray encData = partsEnc.at( i );

    AesSetKey( ticket.DecryptedKey() );

	QByteArray decData = AesDecrypt( t.Index( i ), encData );
    decData.resize( t.Size( i ) );
    QByteArray realHash = GetSha1( decData );
    if( realHash != t.Hash( i ) )
    {
        Err( QString( "hash doesnt match for content %1" ).arg( i ) );
        return QByteArray();
    }
    return decData;
}
Exemple #2
0
TEST(ec, VerifyRealWorldSignature)
{
  static constexpr std::array<u8, 60> MS_PUBKEY = {
      {0x00, 0xfd, 0x56, 0x04, 0x18, 0x2c, 0xf1, 0x75, 0x09, 0x21, 0x00, 0xc3, 0x08, 0xae, 0x48,
       0x39, 0x91, 0x1b, 0x6f, 0x9f, 0xa1, 0xd5, 0x3a, 0x95, 0xaf, 0x08, 0x33, 0x49, 0x47, 0x2b,
       0x00, 0x01, 0x71, 0x31, 0x69, 0xb5, 0x91, 0xff, 0xd3, 0x0c, 0xbf, 0x73, 0xda, 0x76, 0x64,
       0xba, 0x8d, 0x0d, 0xf9, 0x5b, 0x4d, 0x11, 0x04, 0x44, 0x64, 0x35, 0xc0, 0xed, 0xa4, 0x2f}};

  static constexpr std::array<u8, 0x180> DEVICE_CERT = {
      {0x00, 0x01, 0x00, 0x02, 0x00, 0x54, 0xe3, 0x9a, 0x0f, 0xe6, 0xe1, 0x61, 0xb6, 0x2f, 0x9d,
       0x0c, 0xaa, 0x1e, 0xc5, 0x58, 0x85, 0xa1, 0xeb, 0x93, 0xa5, 0x1e, 0xf4, 0x06, 0x99, 0x77,
       0x9a, 0x46, 0x76, 0x01, 0x00, 0xb7, 0xe4, 0x72, 0x10, 0x6e, 0xa2, 0x21, 0x57, 0xe0, 0xe3,
       0xbe, 0x48, 0x9d, 0x7b, 0xa5, 0x2d, 0x46, 0x2f, 0x33, 0x93, 0xae, 0xb0, 0x4b, 0x53, 0xcb,
       0xb9, 0xef, 0x16, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6f, 0x6f, 0x74, 0x2d, 0x43, 0x41,
       0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2d, 0x4d, 0x53, 0x30, 0x30, 0x30, 0x30,
       0x30, 0x30, 0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x02, 0x4e, 0x47, 0x30, 0x34, 0x65, 0x35, 0x34, 0x32, 0x31, 0x64, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x1e, 0x5f, 0x58, 0x01, 0xa8, 0x1a, 0x89, 0x8d, 0x04,
       0xe4, 0x0e, 0x44, 0x6c, 0x99, 0x52, 0xef, 0xe8, 0xe9, 0x8a, 0xec, 0x2b, 0x73, 0xea, 0x13,
       0x56, 0x93, 0xf5, 0x1a, 0xd8, 0x53, 0xa8, 0xc5, 0xf2, 0x00, 0x41, 0xe9, 0x5e, 0x0a, 0x5d,
       0x0c, 0xdf, 0xf0, 0xc6, 0x96, 0x2c, 0x98, 0x96, 0xa9, 0x0f, 0xf0, 0x2e, 0x1f, 0x0d, 0x1a,
       0xcf, 0xa8, 0x35, 0x52, 0x74, 0x36, 0x13, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};

  const auto device_cert = IOS::ES::CertReader{{std::cbegin(DEVICE_CERT), std::cend(DEVICE_CERT)}};
  EXPECT_TRUE(Common::ec::VerifySignature(MS_PUBKEY.data(), device_cert.GetSignatureData().data(),
                                          device_cert.GetSha1().data()));
}
SHA1EngineExt::~SHA1EngineExt()
{
	GetSha1(m_refId);
}
Exemple #4
0
Wad::Wad( const QByteArray &stuff )
{
	ok = false;
	if( !stuff.size() )//prevent error text when it isnt required
		return;
	if( stuff.size() < 0x80 )//less than this and there is definitely nothing there
	{
		Err( "Size is < 0x80" );
		return;
	}

	QByteArray copy = stuff;
	QBuffer b( &copy );
	b.open( QIODevice::ReadOnly );

	quint32 tmp;
	if(b.read( (char*)&tmp, 4 ) != 4)
	{
		b.close();
		Err( "Can't read header size" );
		return;
	}
	if( qFromBigEndian( tmp ) != 0x20 )
	{
		b.close();
		hexdump(stuff, 0, 0x10);
		Err( "Bad header size" );
		return;
	}
	b.read( (char*)&tmp, 4 );
	tmp = qFromBigEndian( tmp );
	if( tmp != 0x49730000 &&
		tmp != 0x69620000 &&
		tmp != 0x426b0000 )
	{
		b.close();
		hexdump( stuff, 0, 0x40 );
		Err( "Bad file magic word" );
		return;
	}

	quint32 certSize;
	quint32 tikSize;
	quint32 tmdSize;
	quint32 appSize;
	quint32 footerSize;

	b.read( (char*)&certSize, 4 );
	certSize = qFromBigEndian( certSize );

	b.seek( 0x10 );
	b.read( (char*)&tikSize, 4 );
	tikSize = qFromBigEndian( tikSize );
	b.read( (char*)&tmdSize, 4 );
	tmdSize = qFromBigEndian( tmdSize );
	b.read( (char*)&appSize, 4 );
	appSize = qFromBigEndian( appSize );
	b.read( (char*)&footerSize, 4 );
	footerSize = qFromBigEndian( footerSize );

	b.close();//close the buffer, the rest of the data can be checked without it

	//sanity check this thing
	quint32 s = stuff.size();
	if( s < ( RU( certSize, 0x40 ) + RU( tikSize, 0x40 ) + RU( tmdSize, 0x40 ) + RU( appSize, 0x40 ) + RU( footerSize, 0x40 ) ) )
	{
		Err( "Total size is less than the combined sizes of all the parts that it is supposed to contain" );
		return;
	}

	quint32 pos = 0x40;
	certData = stuff.mid( pos, certSize );
	pos += RU( certSize, 0x4 );
	tikData = stuff.mid( pos, tikSize );
	pos += RU( tikSize, 0x40 );
	tmdData = stuff.mid( pos, tmdSize );
	pos += RU( tmdSize, 0x40 );

	Ticket ticket( tikData );
	Tmd t( tmdData );

	//the constructor for Ticket may have fixed a bad key index.  replace the data just incase it did
	tikData = ticket.Data();

	//hexdump( tikData );
	//hexdump( tmdData );

	if( ticket.Tid() != t.Tid() )
		qWarning() << "wad contains 2 different TIDs";

	quint32 cnt = t.Count();
	//qDebug() << "Wad contains" << hex << cnt << "contents";

	//another quick sanity check
	quint32 totalSize = 0;
	for( quint32 i = 0; i < cnt; i++ )
		totalSize += t.Size( i );

	if( totalSize > appSize )
	{
		Err( "Size of all the apps in the tmd is greater than the size in the wad header" );
		return;
	}
	//read all the contents, check the hash, and remember the data ( still encrypted )
	for( quint32 i = 0; i < cnt; i++ )
	{

		quint32 s = RU( t.Size( i ), 0x40 );
		//qDebug() << "content" << i << "is at" << hex << pos
		//        << "with size" << s;
		QByteArray encData = stuff.mid( pos, s );
		pos += s;

		//doing this here in case there is some other object that
		//is using the AES that would change the key on us
		AesSetKey( ticket.DecryptedKey() );

		QByteArray decData = AesDecrypt( t.Index( i ), encData );
		decData.resize( t.Size( i ) );
		QByteArray realHash = GetSha1( decData );
		if( realHash != t.Hash( i ) )
		{
			Err( QString( "hash doesnt match for content %1" ).arg( i ) );
		}
		partsEnc << encData;
	}
	//wtf?  some VC titles have a full banner as the footer?  maybe somebody's wad packer is busted
	//QByteArray footer = stuff.mid( pos, stuff.size() - pos );
	//qDebug() << "footer";
	//hexdump( footer );
	ok = true;
}
Exemple #5
0
Wad::Wad( QDir dir )
{
	ok = false;
	QFileInfoList tmds = dir.entryInfoList( QStringList() << "*.tmd" << "tmd.*", QDir::Files );
	if( tmds.isEmpty() )
	{
		Err( "TMD not found" );
		return;
	}
	tmdData = ReadFile( tmds.at( 0 ).absoluteFilePath() );
	if( tmdData.isEmpty() )
		return;
	QFileInfoList tiks = dir.entryInfoList( QStringList() << "*.tik" << "cetk", QDir::Files );
	if( tiks.isEmpty() )
	{
		Err( "Ticket not found" );
		return;
	}
	tikData = ReadFile( tiks.at( 0 ).absoluteFilePath() );
	if( tikData.isEmpty() )
		return;

	Tmd t( tmdData );
	Ticket ticket( tikData );

	//make sure to only add the tmd & ticket without all the cert mumbo jumbo
	tmdData = t.Data();
	tikData = ticket.Data();
	t = Tmd( tmdData );
	ticket = Ticket( tikData );

	quint16 cnt = t.Count();

	bool tmdChanged = false;
	for( quint16 i = 0; i < cnt; i++ )
	{
		QByteArray appD = ReadFile( dir.absoluteFilePath( t.Cid( i ) + ".app" ) );
		if( appD.isEmpty() )
		{
			Err( t.Cid( i ) + ".app not found" );
			return;
		}

		if( (quint32)appD.size() != t.Size( i ) )
		{
			t.SetSize( i, appD.size() );
			tmdChanged = true;
		}
		QByteArray realHash = GetSha1( appD );
		if( t.Hash( i ) != realHash )
		{
			t.SetHash( i, realHash );
			tmdChanged = true;
		}
		AesSetKey( ticket.DecryptedKey() );
		appD = PaddedByteArray( appD, 0x40 );
		QByteArray encData = AesEncrypt( t.Index( i ), appD );
		partsEnc << encData;
	}
	//if something in the tmd changed, fakesign it
	if( tmdChanged )
	{
		if( !t.FakeSign() )
		{
			Err( "Error signing the wad" );
			return;
		}
		else
		{
			tmdData = t.Data();
		}
	}
	QFileInfoList certs = dir.entryInfoList( QStringList() << "*.cert", QDir::Files );
	if( !certs.isEmpty() )
	{
		certData = ReadFile( certs.at( 0 ).absoluteFilePath() );
	}
	ok = true;
}