/* 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 . */ // modifyed by osilloscopion (2 Jul 2016) #include "protocol_ntr.h" #include "secure_ntr.h" #include "card_ntr.h" // #include "draw.h" #include "timer.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()) 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= 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); } // Count timeout at (33.514 / 1024) Mhz. void NTR_SecureDelay(u16 aTimeout) { u64 tTimeout = (((u64)(aTimeout&0x3FFF)+3)<<10); u64 timer = timer_start(); while (timer_ticks(timer) < tTimeout); } /*// 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; }*/ u32 NTR_GetIDSafe (u32 flags, const u8* command) { 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) { 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]); u32 nds9Offset = *((vu32*)(void*)&header[0x20]); 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]); 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); 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); } NTR_CmdSecure (flagsKey1, NULL, 0, cmdData); 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); } //NTR_CmdSecure (flagsKey1, &SecureCartID, sizeof (SecureCartID), cmdData); SecureCartID = NTR_GetIDSafe (flagsKey1, cmdData); 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); for(int ii=8;ii>0;--ii) { NTR_CmdSecure (flagsSec | NTRCARD_BLK_SIZE(1), secureArea + secureAreaOffset, 0x200, cmdData); secureAreaOffset += 0x200 / sizeof (u32); } } else { NTR_CmdSecure (flagsSec | NTRCARD_BLK_SIZE(4) | NTRCARD_SEC_LARGE, secureArea + secureAreaOffset, 0x1000, cmdData); 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); } NTR_CmdSecure (flagsKey1, NULL, 0, cmdData); //CycloDS doesn't like the dsi secure area being decrypted if(!iCardDevice && ((nds9Offset != 0x4000) || secureArea[0] || secureArea[1])) { 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; }