GodMode9/source/gamecart/secure_ntr.c

343 lines
11 KiB
C
Raw Normal View History

2017-01-13 14:20:42 +01:00
/*
card_access.cpp
Copyright (C) 2010 yellow wood goblin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// modifyed by osilloscopion (2 Jul 2016)
#include "protocol_ntr.h"
#include "secure_ntr.h"
#include "card_ntr.h"
// #include "draw.h"
#include "delay.h"
#define BSWAP32(val) ((((val >> 24) & 0xFF)) | (((val >> 16) & 0xFF) << 8) | (((val >> 8) & 0xFF) << 16) | ((val & 0xFF) << 24))
extern u32 ReadDataFlags;
void NTR_CryptUp (u32* pCardHash, u32* aPtr)
{
u32 x = aPtr[1];
u32 y = aPtr[0];
u32 z;
for(int ii=0;ii<0x10;++ii)
{
z = pCardHash[ii] ^ x;
x = pCardHash[0x012 + ((z >> 24) & 0xff)];
x = pCardHash[0x112 + ((z >> 16) & 0xff)] + x;
x = pCardHash[0x212 + ((z >> 8) & 0xff)] ^ x;
x = pCardHash[0x312 + ((z >> 0) & 0xff)] + x;
x = y ^ x;
y = z;
}
aPtr[0] = x ^ pCardHash[0x10];
aPtr[1] = y ^ pCardHash[0x11];
}
void NTR_CryptDown(u32* pCardHash, u32* aPtr)
{
u32 x = aPtr[1];
u32 y = aPtr[0];
u32 z;
for(int ii=0x11;ii>0x01;--ii)
{
z = pCardHash[ii] ^ x;
x = pCardHash[0x012 + ((z >> 24) & 0xff)];
x = pCardHash[0x112 + ((z >> 16) & 0xff)] + x;
x = pCardHash[0x212 + ((z >> 8) & 0xff)] ^ x;
x = pCardHash[0x312 + ((z >> 0) & 0xff)] + x;
x = y ^ x;
y = z;
}
aPtr[0] = x ^ pCardHash[0x01];
aPtr[1] = y ^ pCardHash[0x00];
}
// chosen by fair dice roll.
// guaranteed to be random.
#define getRandomNumber() (rand())
2017-01-13 14:20:42 +01:00
void NTR_InitKey1 (u8* aCmdData, IKEY1* pKey1, int iCardDevice)
{
pKey1->iii = getRandomNumber() & 0x00000fff;
pKey1->jjj = getRandomNumber() & 0x00000fff;
pKey1->kkkkk = getRandomNumber() & 0x000fffff;
pKey1->llll = getRandomNumber() & 0x0000ffff;
pKey1->mmm = getRandomNumber() & 0x00000fff;
pKey1->nnn = getRandomNumber() & 0x00000fff;
if(iCardDevice) //DSi
aCmdData[7]=NTRCARD_CMD_ACTIVATE_BF2; //0x3D
else
aCmdData[7]=NTRCARD_CMD_ACTIVATE_BF;
aCmdData[6] = (u8)(pKey1->iii >> 4);
aCmdData[5] = (u8)((pKey1->iii << 4) | (pKey1->jjj >> 8));
aCmdData[4] = (u8)pKey1->jjj;
aCmdData[3] = (u8)(pKey1->kkkkk >> 16);
aCmdData[2] = (u8)(pKey1->kkkkk >> 8);
aCmdData[1] = (u8)pKey1->kkkkk;
aCmdData[0] = (u8)getRandomNumber();
}
void NTR_ApplyKey (u32* pCardHash, int nCardHash, u32* pKeyCode)
{
u32 scratch[2];
NTR_CryptUp (pCardHash, &pKeyCode[1]);
NTR_CryptUp (pCardHash, &pKeyCode[0]);
memset(scratch, 0, sizeof (scratch));
for(int ii=0;ii<0x12;++ii)
{
pCardHash[ii] = pCardHash[ii] ^ BSWAP32 (pKeyCode[ii%2]);
}
for(int ii=0;ii<nCardHash;ii+=2)
{
NTR_CryptUp (pCardHash, scratch);
pCardHash[ii] = scratch[1];
pCardHash[ii+1] = scratch[0];
}
}
void NTR_InitKey (u32 aGameCode, u32* pCardHash, int nCardHash, u32* pKeyCode, int level, int iCardDevice)
{
if(iCardDevice)
{
const u8* BlowfishTwl = (const u8*)0x01FFD3E0;
memcpy (pCardHash, BlowfishTwl, 0x1048);
}
else
{
const u8* BlowfishNtr = (const u8*)0x01FFE428;
memcpy (pCardHash, BlowfishNtr, 0x1048);
}
pKeyCode[0] = aGameCode;
pKeyCode[1] = aGameCode/2;
pKeyCode[2] = aGameCode*2;
if (level >= 1) NTR_ApplyKey (pCardHash, nCardHash, pKeyCode);
if (level >= 2) NTR_ApplyKey (pCardHash, nCardHash, pKeyCode);
pKeyCode[1] = pKeyCode[1]*2;
pKeyCode[2] = pKeyCode[2]/2;
if (level >= 3) NTR_ApplyKey (pCardHash, nCardHash, pKeyCode);
}
void NTR_CreateEncryptedCommand (u8 aCommand, u32* pCardHash, u8* aCmdData, IKEY1* pKey1, u32 aBlock)
{
u32 iii,jjj;
if(aCommand!=NTRCARD_CMD_SECURE_READ) aBlock=pKey1->llll;
if(aCommand==NTRCARD_CMD_ACTIVATE_SEC)
{
iii=pKey1->mmm;
jjj=pKey1->nnn;
}
else
{
iii=pKey1->iii;
jjj=pKey1->jjj;
}
aCmdData[7]=(u8)(aCommand|(aBlock>>12));
aCmdData[6]=(u8)(aBlock>>4);
aCmdData[5]=(u8)((aBlock<<4)|(iii>>8));
aCmdData[4]=(u8)iii;
aCmdData[3]=(u8)(jjj>>4);
aCmdData[2]=(u8)((jjj<<4)|(pKey1->kkkkk>>16));
aCmdData[1]=(u8)(pKey1->kkkkk>>8);
aCmdData[0]=(u8)pKey1->kkkkk;
NTR_CryptUp(pCardHash, (u32*)(void*)aCmdData);
pKey1->kkkkk+=1;
}
void NTR_DecryptSecureArea (u32 aGameCode, u32* pCardHash, int nCardHash, u32* pKeyCode, u32* pSecureArea, int iCardDevice)
{
NTR_InitKey (aGameCode, pCardHash, nCardHash, pKeyCode, 2, iCardDevice);
NTR_CryptDown(pCardHash, pSecureArea);
NTR_InitKey(aGameCode, pCardHash, nCardHash, pKeyCode, 3, iCardDevice);
for(int ii=0;ii<0x200;ii+=2) NTR_CryptDown (pCardHash, pSecureArea + ii);
}
// Causes the timer to count at (33.514 / 1024) Mhz.
#define TIMER_DIV_1024 (3)
#define TIMER0_DATA (*(vu16*)0x10003000)
#define TIMER0_CR (*(vu16*)0x10003002)
#define TIMER_ENABLE (1<<7)
void NTR_SecureDelay(u16 aTimeout)
{
/* Using a while loop to check the timeout,
so we have to wait until one before overflow.
This also requires an extra 1 for the timer data.
See GBATek for the normal formula used for card timeout.
*/
TIMER0_DATA=0x10000-(((aTimeout&0x3FFF)+3));
//TIMER0_CR=TIMER_DIV_256|TIMER_ENABLE;
TIMER0_CR=TIMER_DIV_1024|TIMER_ENABLE;
while(TIMER0_DATA!=0xFFFF);
// Clear out the timer registers
TIMER0_CR=0;
TIMER0_DATA=0;
}
2017-01-13 14:20:42 +01:00
u32 NTR_GetIDSafe (u32 flags, const u8* command)
2017-01-13 14:20:42 +01:00
{
u32 data = 0;
cardWriteCommand(command);
REG_NTRCARDROMCNT = flags | NTRCARD_BLK_SIZE(7);
do
{
if (REG_NTRCARDROMCNT & NTRCARD_DATA_READY)
{
data = REG_NTRCARDFIFO;
}
}
while(REG_NTRCARDROMCNT & NTRCARD_BUSY);
return data;
}
void NTR_CmdSecure (u32 flags, void* buffer, u32 length, u8* pcmd)
2017-01-13 14:20:42 +01:00
{
cardPolledTransfer (flags, buffer, length, pcmd);
}
bool NTR_Secure_Init (u8* header, u32 CartID, int iCardDevice)
{
u32 iGameCode;
u32 iCardHash[0x412] = {0};
u32 iKeyCode[3] = {0};
u32* secureArea=(u32*)(void*)(header + 0x4000);
u8 cmdData[8] __attribute__((aligned(32)));
const u8 cardSeedBytes[]={0xE8,0x4D,0x5A,0xB1,0x17,0x8F,0x99,0xD5};
IKEY1 iKey1 ={0};
bool iCheapCard = (CartID & 0x80000000) != 0;
u32 cardControl13 = *((vu32*)(void*)&header[0x60]);
u32 cardControlBF = *((vu32*)(void*)&header[0x64]);
u16 readTimeout = *((vu16*)(void*)&header[0x6E]);
2017-01-13 14:20:42 +01:00
u8 deviceType = header[0x13];
int nCardHash = sizeof (iCardHash) / sizeof (iCardHash[0]);
u32 flagsKey1=NTRCARD_ACTIVATE|NTRCARD_nRESET|(cardControl13&(NTRCARD_WR|NTRCARD_CLK_SLOW))|((cardControlBF&(NTRCARD_CLK_SLOW|NTRCARD_DELAY1(0x1FFF)))+((cardControlBF&NTRCARD_DELAY2(0x3F))>>16));
u32 flagsSec=(cardControlBF&(NTRCARD_CLK_SLOW|NTRCARD_DELAY1(0x1FFF)|NTRCARD_DELAY2(0x3F)))|NTRCARD_ACTIVATE|NTRCARD_nRESET|NTRCARD_SEC_EN|NTRCARD_SEC_DAT;
iGameCode = *((vu32*)(void*)&header[0x0C]);
2017-01-13 14:20:42 +01:00
ReadDataFlags = cardControl13 & ~ NTRCARD_BLK_SIZE(7);
NTR_InitKey (iGameCode, iCardHash, nCardHash, iKeyCode, iCardDevice?1:2, iCardDevice);
if(!iCheapCard) flagsKey1 |= NTRCARD_SEC_LARGE;
//Debug("iCheapCard=%d, readTimeout=%d", iCheapCard, readTimeout);
NTR_InitKey1 (cmdData, &iKey1, iCardDevice);
//Debug("cmdData=%02X %02X %02X %02X %02X %02X %02X %02X ", cmdData[0], cmdData[1], cmdData[2], cmdData[3], cmdData[4], cmdData[5], cmdData[6], cmdData[7]);
//Debug("iKey1=%08X %08X %08X", iKey1.iii, iKey1. jjj, iKey1. kkkkk);
//Debug("iKey1=%08X %08X %08X", iKey1. llll, iKey1. mmm, iKey1. nnn);
NTR_CmdSecure ((cardControl13&(NTRCARD_CLK_SLOW|NTRCARD_DELAY2(0x3f)|NTRCARD_DELAY1(0x1fff)))|NTRCARD_ACTIVATE|NTRCARD_nRESET, NULL, 0, cmdData);
2017-01-13 14:20:42 +01:00
NTR_CreateEncryptedCommand (NTRCARD_CMD_ACTIVATE_SEC, iCardHash, cmdData, &iKey1, 0);
//Debug("cmdData=%02X %02X %02X %02X %02X %02X %02X %02X ", cmdData[0], cmdData[1], cmdData[2], cmdData[3], cmdData[4], cmdData[5], cmdData[6], cmdData[7]);
if(iCheapCard)
{
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData);
NTR_SecureDelay(readTimeout);
2017-01-13 14:20:42 +01:00
}
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData);
2017-01-13 14:20:42 +01:00
REG_NTRCARDROMCNT = 0;
REG_NTRCARDSEEDX_L = cardSeedBytes[deviceType & 0x07] | (iKey1.nnn << 15) | (iKey1.mmm << 27) | 0x6000;
REG_NTRCARDSEEDY_L = 0x879b9b05;
REG_NTRCARDSEEDX_H = iKey1.mmm >> 5;
REG_NTRCARDSEEDY_H = 0x5c;
REG_NTRCARDROMCNT = NTRCARD_nRESET | NTRCARD_SEC_SEED | NTRCARD_SEC_EN | NTRCARD_SEC_DAT;
flagsKey1 |= NTRCARD_SEC_EN | NTRCARD_SEC_DAT;
NTR_CreateEncryptedCommand(NTRCARD_CMD_SECURE_CHIPID, iCardHash, cmdData, &iKey1, 0);
//Debug("cmdData=%02X %02X %02X %02X %02X %02X %02X %02X ", cmdData[0], cmdData[1], cmdData[2], cmdData[3], cmdData[4], cmdData[5], cmdData[6], cmdData[7]);
u32 SecureCartID = 0;
if(iCheapCard)
{
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData);
NTR_SecureDelay(readTimeout);
2017-01-13 14:20:42 +01:00
}
//NTR_CmdSecure (flagsKey1, &SecureCartID, sizeof (SecureCartID), cmdData);
SecureCartID = NTR_GetIDSafe (flagsKey1, cmdData);
2017-01-13 14:20:42 +01:00
if (SecureCartID != CartID)
{
// Debug("Invalid SecureCartID\n(%08X != %08X)", SecureCartID, CartID);
return false;
}
int secureAreaOffset = 0;
for(int secureBlockNumber=4;secureBlockNumber<8;++secureBlockNumber)
{
NTR_CreateEncryptedCommand (NTRCARD_CMD_SECURE_READ, iCardHash, cmdData, &iKey1, secureBlockNumber);
if (iCheapCard)
{
NTR_CmdSecure (flagsSec, NULL, 0, cmdData);
NTR_SecureDelay(readTimeout);
2017-01-13 14:20:42 +01:00
for(int ii=8;ii>0;--ii)
{
NTR_CmdSecure (flagsSec | NTRCARD_BLK_SIZE(1), secureArea + secureAreaOffset, 0x200, cmdData);
2017-01-13 14:20:42 +01:00
secureAreaOffset += 0x200 / sizeof (u32);
}
}
else
{
NTR_CmdSecure (flagsSec | NTRCARD_BLK_SIZE(4) | NTRCARD_SEC_LARGE, secureArea + secureAreaOffset, 0x1000, cmdData);
2017-01-13 14:20:42 +01:00
secureAreaOffset += 0x1000 / sizeof (u32);
}
}
NTR_CreateEncryptedCommand (NTRCARD_CMD_DATA_MODE, iCardHash, cmdData, &iKey1, 0);
if(iCheapCard)
{
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData);
NTR_SecureDelay(readTimeout);
2017-01-13 14:20:42 +01:00
}
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData);
2017-01-13 14:20:42 +01:00
if(!iCardDevice) //CycloDS doesn't like the dsi secure area being decrypted
{
NTR_DecryptSecureArea (iGameCode, iCardHash, nCardHash, iKeyCode, secureArea, iCardDevice);
}
//Debug("secure area %08X %08X", secureArea[0], secureArea[1]);
if(secureArea[0] == 0x72636e65/*'encr'*/ && secureArea[1] == 0x6a624f79/*'yObj'*/)
{
secureArea[0] = 0xe7ffdeff;
secureArea[1] = 0xe7ffdeff;
}
else
{
//Debug("Invalid secure area (%08X %08X)", secureArea[0], secureArea[1]);
//dragon quest 5 has invalid secure area. really.
//return false;
}
return true;
}