private int validateNPD(string filename, byte[] devKLic, NPD[] npdPtr, FileStream i) { i.Seek(0L, SeekOrigin.Begin); byte[] buffer = new byte[0x80]; i.Read(buffer, 0, buffer.Length); byte[] buffer2 = new byte[4]; i.Read(buffer2, 0, buffer2.Length); if ((ConversionUtils.be32(buffer2, 0) & FLAG_SDAT) != 0L) { Console.WriteLine("INFO: SDAT detected. NPD header is not validated"); } else { if (!this.checkNPDHash1(filename, buffer)) { Console.WriteLine("ERROR: Hashing Title ID Name"); return(STATUS_ERROR_HASHTITLEIDNAME); } if (devKLic == null) { Console.WriteLine("WARNING: Can not validate devklic header"); } else if (!this.checkNPDHash2(devKLic, buffer)) { Console.WriteLine("ERROR: Hashing devklic"); return(STATUS_ERROR_HASHDEVKLIC); } } npdPtr[0] = NPD.createNPD(buffer); return(STATUS_OK); }
private int validateNPD(String filename, byte[] devKLic, NPD[] npdPtr, FileStream i) { i.Seek(0, SeekOrigin.Begin); byte[] npd = new byte[0x80]; i.Read(npd, 0, npd.Length); byte[] extraData = new byte[0x04]; i.Read(extraData, 0, extraData.Length); long flag = ConversionUtils.be32(extraData, 0); if ((flag & FLAG_SDAT) != 0) { Console.WriteLine("INFO: SDAT detected. NPD header is not validated"); } else if (!checkNPDHash1(filename, npd)) { Console.WriteLine("ERROR: Hashing Title ID Name"); return(STATUS_ERROR_HASHTITLEIDNAME); } else if (devKLic == null) { Console.WriteLine("WARNING: Can not validate devklic header"); } else if (!checkNPDHash2(devKLic, npd)) { Console.WriteLine("ERROR: Hashing devklic"); return(STATUS_ERROR_HASHDEVKLIC); } npdPtr[0] = NPD.createNPD(npd); return(STATUS_OK); }
private byte[] writeValidNPD(string filename, byte[] devKLic, NPD[] npdPtr, FileStream fin, byte[] contentID, byte[] flags, byte[] version, byte[] type) { int num; byte[] dest = new byte[0x80]; dest[0] = 0x4e; dest[1] = 80; dest[2] = 0x44; dest[3] = 0; dest[4] = 0; dest[5] = 0; dest[6] = 0; dest[7] = version[0]; dest[8] = 0; dest[9] = 0; dest[10] = 0; dest[11] = 3; dest[12] = 0; dest[13] = 0; dest[14] = 0; dest[15] = type[0]; for (num = 0; num < 0x30; num++) { dest[0x10 + num] = contentID[num]; } ConversionUtils.arraycopy(ConversionUtils.charsToByte("FixedLicenseEDAT".ToCharArray()), 0, dest, 0x40L, 0x10); ConversionUtils.arraycopy(this.createNPDHash1(filename, dest), 0, dest, 80L, 0x10); ConversionUtils.arraycopy(this.createNPDHash2(devKLic, dest), 0, dest, 0x60L, 0x10); for (num = 0; num < 0x10; num++) { dest[0x70 + num] = 0; } npdPtr[0] = NPD.createNPD(dest); return(dest); }
private byte[] calculateBlockKey(int blk, NPD npd) { byte[] src = (npd.getVersion() <= 1L) ? new byte[0x10] : npd.getDevHash(); byte[] dest = new byte[0x10]; ConversionUtils.arraycopy(src, 0, dest, 0L, 12); dest[12] = (byte)((blk >> 0x18) & 0xff); dest[13] = (byte)((blk >> 0x10) & 0xff); dest[14] = (byte)((blk >> 8) & 0xff); dest[15] = (byte)(blk & 0xff); return(dest); }
private byte[] calculateBlockKey(int blk, NPD npd) { byte[] baseKey = (npd.getVersion() <= 1) ? (new byte[0x10]) : npd.getDevHash(); byte[] result = new byte[0x10]; ConversionUtils.arraycopy(baseKey, 0, result, 0, 0xC); result[0xC] = (byte)(blk >> 24 & 0xFF); result[0xD] = (byte)(blk >> 16 & 0xFF); result[0xE] = (byte)(blk >> 8 & 0xFF); result[0xF] = (byte)(blk & 0xFF); return(result); }
/* KDSBEST END */ public int decryptFile(String inFile, String outFile, byte[] devKLic, byte[] keyFromRif) { FileStream fin = File.Open(inFile, FileMode.Open); string[] fn = fin.Name.Split('\\'); //string[] fn = fin.Name.Split('/'); Console.WriteLine(fn[fn.Length - 1]); NPD[] ptr = new NPD[1]; //Ptr to Ptr int result = validateNPD(fn[fn.Length - 1], devKLic, ptr, fin); //Validate NPD hashes if (result < 0) { fin.Close(); return(result); } NPD npd = ptr[0]; EDATData data = getEDATData(fin); //Get flags, blocksize and file len byte[] rifkey = getKey(npd, data, devKLic, keyFromRif); //Obtain the key for decryption (result of sc471 or sdatkey) if (rifkey == null) { Console.WriteLine("ERROR: Key for decryption is missing"); fin.Close(); return(STATUS_ERROR_MISSINGKEY); } else { Console.WriteLine("DECRYPTION KEY: " + ConversionUtils.getHexString(rifkey)); } result = checkHeader(rifkey, data, npd, fin); if (result < 0) { fin.Close(); return(result); } FileStream o = File.Open(outFile, FileMode.Create); result = decryptData(fin, o, npd, data, rifkey); if (result < 0) { fin.Close(); return(result); } fin.Close(); o.Close(); Console.WriteLine("COMPLETE: File Written to disk"); return(STATUS_OK); }
private int encryptData(FileStream ii, FileStream o, NPD npd, EDATData data, byte[] rifkey) { int num = (int)(((data.getFileLen() + data.getBlockSize()) - 1) / data.getBlockSize()); byte[] dest = new byte[num * 0x10]; byte[] buffer2 = new byte[ii.Length + 15L]; for (int i = 0; i < num; i++) { long offset = i * data.getBlockSize(); ii.Seek(offset, SeekOrigin.Begin); int length = (int)data.getBlockSize(); if (i == (num - 1)) { length = (int)(data.getFileLen() % new BigInteger(data.getBlockSize())); } int num5 = length; length = (length + 15) & -16; byte[] buffer3 = new byte[length]; byte[] buffer4 = new byte[length]; for (int j = num5; j > 0; j -= ii.Read(buffer4, num5 - j, j)) { } for (int k = num5; k < length; k++) { buffer4[k] = 0; } byte[] buffer5 = new byte[0x10]; byte[] buffer6 = new byte[0x10]; byte[] buffer7 = this.calculateBlockKey(i, npd); ToolsImpl.aesecbEncrypt(rifkey, buffer7, 0, buffer5, 0, buffer7.Length); ConversionUtils.arraycopy(buffer5, 0, buffer6, 0L, buffer5.Length); int cryptoFlag = 2; int hashFlag = 2; AppLoaderReverse reverse = new AppLoaderReverse(); byte[] iv = npd.getDigest(); byte[] generatedHash = new byte[0x10]; reverse.doAll(hashFlag, cryptoFlag, buffer4, 0, buffer3, 0, buffer4.Length, buffer5, iv, buffer6, generatedHash, 0); ConversionUtils.arraycopy(buffer3, 0, buffer2, offset, length); ConversionUtils.arraycopy(generatedHash, 0, dest, (long)(i * 0x10), 0x10); } byte[] buffer = ConversionUtils.getByteArray("4D6164652062792052325220546F6F6C"); o.Write(dest, 0, dest.Length); o.Write(buffer2, 0, buffer2.Length - 15); o.Write(buffer, 0, buffer.Length); return(STATUS_OK); }
public int decryptFile(string inFile, string outFile, byte[] devKLic, byte[] keyFromRif) { FileStream i = File.Open(inFile, FileMode.Open); string[] strArray = i.Name.Split(new char[] { '\\' }); Console.WriteLine(strArray[strArray.Length - 1]); NPD[] npdPtr = new NPD[1]; int num = this.validateNPD(strArray[strArray.Length - 1], devKLic, npdPtr, i); if (num < 0) { i.Close(); return(num); } NPD npd = npdPtr[0]; EDATData data = this.getEDATData(i); byte[] raw = this.getKey(npd, data, devKLic, keyFromRif); if (raw == null) { Console.WriteLine("ERROR: Key for decryption is missing"); i.Close(); return(STATUS_ERROR_MISSINGKEY); } Console.WriteLine("DECRYPTION KEY: " + ConversionUtils.getHexString(raw)); num = this.checkHeader(raw, data, npd, i); if (num < 0) { i.Close(); return(num); } FileStream o = File.Open(outFile, FileMode.Create); num = this.decryptData(i, o, npd, data, raw); if (num < 0) { i.Close(); return(num); } i.Close(); o.Close(); Console.WriteLine("COMPLETE: File Written to disk"); return(STATUS_OK); }
private byte[] getKey(NPD npd, EDATData data, byte[] devKLic, byte[] keyFromRif) { byte[] output = null; if ((data.getFlags() & FLAG_SDAT) != 0L) { output = new byte[0x10]; ToolsImpl.XOR(output, npd.getDevHash(), EDATKeys.SDATKEY); return(output); } if (npd.getLicense() == 3L) { return(devKLic); } if (npd.getLicense() == 2L) { output = keyFromRif; } return(output); }
public static NPD createNPD(byte[] npd) { NPD npd2 = new NPD(); ConversionUtils.arraycopy(npd, 0, npd2.magic, 0L, 4); npd2.version = ConversionUtils.be32(npd, 4); npd2.license = ConversionUtils.be32(npd, 8); npd2.type = ConversionUtils.be32(npd, 12); ConversionUtils.arraycopy(npd, 0x10, npd2.content_id, 0L, 0x30); ConversionUtils.arraycopy(npd, 0x40, npd2.digest, 0L, 0x10); ConversionUtils.arraycopy(npd, 80, npd2.titleHash, 0L, 0x10); ConversionUtils.arraycopy(npd, 0x60, npd2.devHash, 0L, 0x10); npd2.unknown3 = ConversionUtils.be64(npd, 0x70); npd2.unknown4 = ConversionUtils.be64(npd, 120); if (!npd2.validate()) { npd2 = null; } return npd2; }
public static NPD createNPD(byte[] npd) { NPD npd2 = new NPD(); ConversionUtils.arraycopy(npd, 0, npd2.magic, 0L, 4); npd2.version = ConversionUtils.be32(npd, 4); npd2.license = ConversionUtils.be32(npd, 8); npd2.type = ConversionUtils.be32(npd, 12); ConversionUtils.arraycopy(npd, 0x10, npd2.content_id, 0L, 0x30); ConversionUtils.arraycopy(npd, 0x40, npd2.digest, 0L, 0x10); ConversionUtils.arraycopy(npd, 80, npd2.titleHash, 0L, 0x10); ConversionUtils.arraycopy(npd, 0x60, npd2.devHash, 0L, 0x10); npd2.unknown3 = ConversionUtils.be64(npd, 0x70); npd2.unknown4 = ConversionUtils.be64(npd, 120); if (!npd2.validate()) { npd2 = null; } return(npd2); }
public static NPD createNPD(byte[] npd) { NPD result = new NPD(); ConversionUtils.arraycopy(npd, 0, result.magic, 0, 4); result.version = ConversionUtils.be32(npd, 4); result.license = ConversionUtils.be32(npd, 8); result.type = ConversionUtils.be32(npd, 0xC); ConversionUtils.arraycopy(npd, 0x10, result.content_id, 0, 0x30); ConversionUtils.arraycopy(npd, 0x40, result.digest, 0, 0x10); ConversionUtils.arraycopy(npd, 0x50, result.titleHash, 0, 0x10); ConversionUtils.arraycopy(npd, 0x60, result.devHash, 0, 0x10); result.unknown3 = ConversionUtils.be64(npd, 0x70); result.unknown4 = ConversionUtils.be64(npd, 0x78); if (!result.validate()) { result = null; } return(result); }
public int decryptFile(string inFile, string outFile, byte[] devKLic, byte[] keyFromRif) { FileStream i = File.Open(inFile, FileMode.Open); string[] strArray = i.Name.Split(new char[] { '\\' }); Console.WriteLine(strArray[strArray.Length - 1]); NPD[] npdPtr = new NPD[1]; int num = this.validateNPD(strArray[strArray.Length - 1], devKLic, npdPtr, i); if (num < 0) { i.Close(); return num; } NPD npd = npdPtr[0]; EDATData data = this.getEDATData(i); byte[] raw = this.getKey(npd, data, devKLic, keyFromRif); if (raw == null) { Console.WriteLine("ERROR: Key for decryption is missing"); i.Close(); return STATUS_ERROR_MISSINGKEY; } Console.WriteLine("DECRYPTION KEY: " + ConversionUtils.getHexString(raw)); num = this.checkHeader(raw, data, npd, i); if (num < 0) { i.Close(); return num; } FileStream o = File.Open(outFile, FileMode.Create); num = this.decryptData(i, o, npd, data, raw); if (num < 0) { i.Close(); return num; } i.Close(); o.Close(); Console.WriteLine("COMPLETE: File Written to disk"); return STATUS_OK; }
private byte[] getKey(NPD npd, EDATData data, byte[] devKLic, byte[] keyFromRif) { byte[] result = null; if ((data.getFlags() & FLAG_SDAT) != 0) { //Case SDAT result = new byte[0x10]; ToolsImpl.XOR(result, npd.getDevHash(), EDATKeys.SDATKEY); } else { //Case EDAT if (npd.getLicense() == 0x03) { result = devKLic; } else if (npd.getLicense() == 0x02) { result = keyFromRif; } } return(result); }
private int encryptData(FileStream ii, FileStream o, NPD npd, EDATData data, byte[] rifkey) { int numBlocks = (int)((data.getFileLen() + data.getBlockSize() - 1) / data.getBlockSize()); byte[] expectedHashForFile = new byte[numBlocks * 0x10]; byte[] encryptedDataForFile = new byte[ii.Length + 0xF]; // File Format: // ALL HASHES // Encrypted Data for (int i = 0; i < numBlocks; i++) { long offset; int len; offset = i * data.getBlockSize(); ii.Seek(offset, SeekOrigin.Begin); len = (int)(data.getBlockSize()); if (i == numBlocks - 1) { len = (int)(data.getFileLen() % (new BigInteger(data.getBlockSize()))); } int realLen = len; len = (int)((uint)(len + 0x0F) & 0xFFFFFFF0); byte[] encryptedData = new byte[len]; byte[] decryptedData = new byte[len]; int toRead = realLen; while (toRead > 0) { toRead -= ii.Read(decryptedData, realLen - toRead, toRead); } for (int ai = realLen; ai < len; ai++) { decryptedData[ai] = 0x00; } byte[] key = new byte[0x10]; byte[] hash = new byte[0x10]; byte[] blockKey = calculateBlockKey(i, npd); ToolsImpl.aesecbEncrypt(rifkey, blockKey, 0, key, 0, blockKey.Length); ConversionUtils.arraycopy(key, 0, hash, 0, key.Length); int cryptoFlag = 0x2; int hashFlag = 0x02; AppLoaderReverse a = new AppLoaderReverse(); byte[] iv = npd.getDigest(); byte[] generatedHash = new byte[0x10]; a.doAll(hashFlag, cryptoFlag, decryptedData, 0, encryptedData, 0, decryptedData.Length, key, iv, hash, generatedHash, 0); ConversionUtils.arraycopy(encryptedData, 0, encryptedDataForFile, offset, len); ConversionUtils.arraycopy(generatedHash, 0, expectedHashForFile, i * 0x10, 0x10); } byte[] EDATAVersion = ConversionUtils.getByteArray("4D6164652062792052325220546F6F6C"); o.Write(expectedHashForFile, 0, expectedHashForFile.Length); o.Write(encryptedDataForFile, 0, encryptedDataForFile.Length - 0xf); o.Write(EDATAVersion, 0, EDATAVersion.Length); return(STATUS_OK); }
/* KDSBEST START */ public int encryptFile(String inFile, String outFile, byte[] devKLic, byte[] keyFromRif, byte[] contentID, byte[] flags, byte[] type, byte[] version) { FileStream fin = File.Open(inFile, FileMode.Open); // MemoryMappedFile fin1 = MemoryMappedFile.CreateFromFile(inFile, FileMode.Open); NPD[] ptr = new NPD[1]; //Ptr to Ptr FileStream o = File.Open(outFile, FileMode.Create); string[] fn = o.Name.Split('\\'); byte[] npd = writeValidNPD(fn[fn.Length - 1], devKLic, ptr, fin, contentID, flags, version, type); o.Write(npd, 0, npd.Length); byte[] buffer = new byte[4]; // FLAGS buffer[0] = 0x00; buffer[1] = 0x00; buffer[2] = 0x00; buffer[3] = 0x00; o.Write(buffer, 0, 4); // blocksize 0x00004000 buffer[2] = 0x40; o.Write(buffer, 0, 4); long len = fin.Length; byte[] lenBuf = BitConverter.GetBytes(len); byte[] rLenBuf = new byte[8]; for (int i = 0; i < 8; i++) { rLenBuf[i] = 0x00; } for (int i = 0; i < lenBuf.Length; i++) { rLenBuf[7 - i] = lenBuf[i]; } o.Write(rLenBuf, 0, 8); // Fill the rest 0x10 bytes with dummy we generate the metasection hash later! // the bytes till 0x100 are unknown buffer[0] = 0x00; while (o.Length < 0x100) { o.Write(buffer, 0, 1); } EDATData data = new EDATData(); data.flags = 0x00000000; data.blockSize = 0x00004000; data.fileLen = new BigInteger(len); byte[] rifkey = getKey(ptr[0], data, devKLic, keyFromRif); //Obtain the key for decryption (result of sc471 or sdatkey) int hashFlag = 0x00000002; encryptData(fin, o, ptr[0], data, rifkey); o.Seek(0x90, SeekOrigin.Begin); AppLoader aa = new AppLoader(); aa.doInit(hashFlag, 0x00000001, new byte[0x10], new byte[0x10], rifkey); int sectionSize = ((data.getFlags() & FLAG_COMPRESSED) != 0) ? 0x20 : 0x010; //BUG??? What about FLAG0x20?? //Determine the metadatasection total len int numBlocks = (int)((data.getFileLen() + data.getBlockSize() - 11) / data.getBlockSize()); int readed = 0; int baseOffset = 0x100; //baseOffset += modifier; //There is an unknown offset to add to the metadatasection... value seen 0 long remaining = sectionSize * numBlocks; while (remaining > 0) { int lenToRead = (HEADER_MAX_BLOCKSIZE > remaining) ? (int)remaining : HEADER_MAX_BLOCKSIZE; o.Seek(baseOffset + readed, SeekOrigin.Begin); byte[] content = new byte[lenToRead]; byte[] ooo = new byte[lenToRead]; o.Read(content, 0, content.Length); aa.doUpdate(content, 0, ooo, 0, lenToRead); readed += lenToRead; remaining -= lenToRead; } byte[] headerHash = new byte[0x10]; aa.doFinalButGetHash(headerHash); o.Seek(0x90, SeekOrigin.Begin); o.Write(headerHash, 0, headerHash.Length); // Header Complete // Generate Header Hash o.Seek(0, SeekOrigin.Begin); byte[] header = new byte[0xA0]; byte[] headerODummy = new byte[0xA0]; o.Read(header, 0, header.Length); AppLoaderReverse a = new AppLoaderReverse(); byte[] generatedHash = new byte[0x10]; bool result = a.doAll(hashFlag, 0x00000001, header, 0, headerODummy, 0, header.Length, new byte[0x10], new byte[0x10], rifkey, generatedHash, 0); o.Seek(0xA0, SeekOrigin.Begin); o.Write(generatedHash, 0, generatedHash.Length); //KDSBest We don't know the DATA 0xB0 to 0x100!!! while (o.Length < 0x100) { o.Write(buffer, 0, 1); } o.Close(); fin.Close(); return(STATUS_OK); }
private int decryptData(FileStream ii, FileStream o, NPD npd, EDATData data, byte[] rifkey) { int num = (int)(((data.getFileLen() + data.getBlockSize()) - 1) / data.getBlockSize()); int num2 = (((data.getFlags() & FLAG_COMPRESSED) != 0L) || ((data.getFlags() & FLAG_0x20) != 0L)) ? 0x20 : 0x10; int num3 = 0x100; for (int i = 0; i < num; i++) { long num5; int num6; byte[] buffer2; int num11; ii.Seek((long)(num3 + (i * num2)), SeekOrigin.Begin); byte[] dest = new byte[0x10]; int num7 = 0; if ((data.getFlags() & FLAG_COMPRESSED) != 0L) { buffer2 = new byte[0x20]; ii.Read(buffer2, 0, buffer2.Length); byte[] buffer3 = this.decryptMetadataSection(buffer2); num5 = (int)ConversionUtils.be64(buffer3, 0); num6 = (int)ConversionUtils.be32(buffer3, 8); num7 = (int)ConversionUtils.be32(buffer3, 12); ConversionUtils.arraycopy(buffer2, 0, dest, 0L, 0x10); } else if ((data.getFlags() & FLAG_0x20) != 0L) { buffer2 = new byte[0x20]; ii.Read(buffer2, 0, buffer2.Length); for (int j = 0; j < 0x10; j++) { dest[j] = (byte)(buffer2[j] ^ buffer2[j + 0x10]); } num5 = (num3 + (i * data.getBlockSize())) + (num * num2); num6 = (int)data.getBlockSize(); if (i == (num - 1)) { num6 = (int)(data.getFileLen() % new BigInteger(data.getBlockSize())); } } else { ii.Read(dest, 0, dest.Length); num5 = (num3 + (i * data.getBlockSize())) + (num * num2); num6 = (int)data.getBlockSize(); if (i == (num - 1)) { num6 = (int)(data.getFileLen() % new BigInteger(data.getBlockSize())); } } int count = num6; num6 = (num6 + 15) & -16; Debug.Print("Offset: %016X, len: %08X, realLen: %08X, endCompress: %d\r\n", new object[] { num5, num6, count, num7 }); ii.Seek(num5, SeekOrigin.Begin); byte[] buffer = new byte[num6]; byte[] buffer5 = new byte[num6]; ii.Read(buffer, 0, buffer.Length); byte[] buffer6 = new byte[0x10]; byte[] buffer7 = new byte[0x10]; byte[] buffer8 = this.calculateBlockKey(i, npd); ToolsImpl.aesecbEncrypt(rifkey, buffer8, 0, buffer6, 0, buffer8.Length); if ((data.getFlags() & FLAG_0x10) != 0L) { ToolsImpl.aesecbEncrypt(rifkey, buffer6, 0, buffer7, 0, buffer6.Length); } else { ConversionUtils.arraycopy(buffer6, 0, buffer7, 0L, buffer6.Length); } int cryptoFlag = ((data.getFlags() & FLAG_0x02) == 0L) ? 2 : 1; if ((data.getFlags() & FLAG_0x10) == 0L) { num11 = 2; } else if ((data.getFlags() & FLAG_0x20) == 0L) { num11 = 4; } else { num11 = 1; } if ((data.getFlags() & FLAG_KEYENCRYPTED) != 0L) { cryptoFlag |= 0x10000000; num11 |= 0x10000000; } if ((data.getFlags() & FLAG_DEBUG) != 0L) { cryptoFlag |= 0x1000000; num11 |= 0x1000000; } AppLoader loader = new AppLoader(); byte[] buffer9 = (npd.getVersion() <= 1L) ? new byte[0x10] : npd.getDigest(); if (!loader.doAll(num11, cryptoFlag, buffer, 0, buffer5, 0, buffer.Length, buffer6, npd.getDigest(), buffer7, dest, 0)) { Debug.WriteLine("Error decrypting block " + i); } if ((data.getFlags() & FLAG_COMPRESSED) == 0L) { o.Write(buffer5, 0, count); } } return(STATUS_OK); }
public int encryptFile(string inFile, string outFile, byte[] devKLic, byte[] keyFromRif, byte[] contentID, byte[] flags, byte[] type, byte[] version) { int num2; int num9; FileStream fin = File.Open(inFile, FileMode.Open); NPD[] npdPtr = new NPD[1]; FileStream o = File.Open(outFile, FileMode.Create); string[] strArray = o.Name.Split(new char[] { '\\' }); byte[] buffer = this.writeValidNPD(strArray[strArray.Length - 1], devKLic, npdPtr, fin, contentID, flags, version, type); o.Write(buffer, 0, buffer.Length); byte[] buffer2 = new byte[] { 0, 0, 0, 0 }; o.Write(buffer2, 0, 4); buffer2[2] = 0x40; o.Write(buffer2, 0, 4); long length = fin.Length; byte[] bytes = BitConverter.GetBytes(length); byte[] buffer4 = new byte[8]; for (num2 = 0; num2 < 8; num2++) { buffer4[num2] = 0; } for (num2 = 0; num2 < bytes.Length; num2++) { buffer4[7 - num2] = bytes[num2]; } o.Write(buffer4, 0, 8); buffer2[0] = 0; while (o.Length < 0x100L) { o.Write(buffer2, 0, 1); } EDATData data = new EDATData { flags = 0L, blockSize = 0x4000L, fileLen = new BigInteger(length) }; byte[] rifkey = this.getKey(npdPtr[0], data, devKLic, keyFromRif); int hashFlag = 2; this.encryptData(fin, o, npdPtr[0], data, rifkey); o.Seek(0x90L, SeekOrigin.Begin); AppLoader loader = new AppLoader(); loader.doInit(hashFlag, 1, new byte[0x10], new byte[0x10], rifkey); int num4 = ((data.getFlags() & FLAG_COMPRESSED) != 0L) ? 0x20 : 0x10; int num5 = (int)(((data.getFileLen() + data.getBlockSize()) - 11) / data.getBlockSize()); int num6 = 0; int num7 = 0x100; for (long i = num4 * num5; i > 0L; i -= num9) { num9 = (HEADER_MAX_BLOCKSIZE > i) ? ((int)i) : HEADER_MAX_BLOCKSIZE; o.Seek((long)(num7 + num6), SeekOrigin.Begin); byte[] buffer6 = new byte[num9]; byte[] buffer7 = new byte[num9]; o.Read(buffer6, 0, buffer6.Length); loader.doUpdate(buffer6, 0, buffer7, 0, num9); num6 += num9; } byte[] generatedHash = new byte[0x10]; loader.doFinalButGetHash(generatedHash); o.Seek(0x90L, SeekOrigin.Begin); o.Write(generatedHash, 0, generatedHash.Length); o.Seek(0L, SeekOrigin.Begin); byte[] buffer9 = new byte[160]; byte[] buffer10 = new byte[160]; o.Read(buffer9, 0, buffer9.Length); AppLoaderReverse reverse = new AppLoaderReverse(); byte[] buffer11 = new byte[0x10]; bool flag = reverse.doAll(hashFlag, 1, buffer9, 0, buffer10, 0, buffer9.Length, new byte[0x10], new byte[0x10], rifkey, buffer11, 0); o.Seek(160L, SeekOrigin.Begin); o.Write(buffer11, 0, buffer11.Length); while (o.Length < 0x100L) { o.Write(buffer2, 0, 1); } o.Close(); fin.Close(); return(STATUS_OK); }
private int checkHeader(byte[] rifKey, EDATData data, NPD npd, FileStream i) { i.Seek(0, SeekOrigin.Begin); byte[] header = new byte[0xA0]; byte[] o = new byte[0xA0]; byte[] expectedHash = new byte[0x10]; //Version check Console.WriteLine("Checking NPD Version:" + npd.getVersion()); if ((npd.getVersion() == 0) || (npd.getVersion() == 1)) { if ((data.getFlags() & 0x7FFFFFFE) != 0) { Console.WriteLine("ERROR: Incorrect Header Flags"); return(STATUS_ERROR_INCORRECT_FLAGS); } } else if (npd.getVersion() == 2) { if ((data.getFlags() & 0x7EFFFFE0) != 0) { Console.WriteLine("ERROR: Incorrect Header Flags"); return(STATUS_ERROR_INCORRECT_FLAGS); } } else if (npd.getVersion() == 3 || (npd.getVersion() == 4)) { if ((data.getFlags() & 0x7EFFFFC0) != 0) { Console.WriteLine("ERROR: Incorrect Header Flags"); return(STATUS_ERROR_INCORRECT_FLAGS); } } else { Console.WriteLine("ERROR: Unsupported EDAT version (need keys)"); return(STATUS_ERROR_INCORRECT_VERSION); } { int keyIndex = 0; if (npd.getVersion() == 4) { keyIndex = 1; } i.Read(header, 0, header.Length); i.Read(expectedHash, 0, expectedHash.Length); Console.WriteLine("Checking header hash:"); AppLoader a = new AppLoader(); int hashFlag = ((data.getFlags() & FLAG_KEYENCRYPTED) == 0) ? 0x00000002 : 0x10000002; if ((data.getFlags() & FLAG_DEBUG) != 0) { hashFlag |= 0x01000000; } //Veryfing header bool result = a.doAll(hashFlag, 0x00000001, header, 0, o, 0, header.Length, new byte[0x10], new byte[0x10], rifKey, expectedHash, 0); if (!result) { Console.WriteLine("Error verifying header. Is rifKey valid?."); return(STATUS_ERROR_HEADERCHECK); } Console.WriteLine("Checking metadata hash:"); a = new AppLoader(); a.doInit(hashFlag, 0x00000001, new byte[0x10], new byte[0x10], rifKey); int sectionSize = ((data.getFlags() & FLAG_COMPRESSED) != 0) ? 0x20 : 0x010; //BUG??? What about FLAG0x20?? //Determine the metadatasection total len int numBlocks = (int)((data.getFileLen() + data.getBlockSize() - 11) / data.getBlockSize()); int readed = 0; int baseOffset = 0x100; //baseOffset += modifier; //There is an unknown offset to add to the metadatasection... value seen 0 long remaining = sectionSize * numBlocks; while (remaining > 0) { int lenToRead = (HEADER_MAX_BLOCKSIZE > remaining) ? (int)remaining : HEADER_MAX_BLOCKSIZE; i.Seek(baseOffset + readed, SeekOrigin.Begin); byte[] content = new byte[lenToRead]; o = new byte[lenToRead]; i.Read(content, 0, content.Length); a.doUpdate(content, 0, o, 0, lenToRead); readed += lenToRead; remaining -= lenToRead; } result = a.doFinal(header, 0x90); if (!result) { Console.WriteLine("Error verifying metadatasection. Data tampered"); return(STATUS_ERROR_HEADERCHECK); } return(STATUS_OK); } }
private int checkHeader(byte[] rifKey, EDATData data, NPD npd, FileStream i) { int num8; i.Seek(0L, SeekOrigin.Begin); byte[] buffer = new byte[160]; byte[] o = new byte[160]; byte[] buffer3 = new byte[0x10]; Console.WriteLine("Checking NPD Version:" + npd.getVersion()); if ((npd.getVersion() == 0L) || (npd.getVersion() == 1L)) { if ((data.getFlags() & 0x7ffffffeL) != 0L) { Console.WriteLine("ERROR: Incorrect Header Flags"); return STATUS_ERROR_INCORRECT_FLAGS; } } else if (npd.getVersion() == 2L) { if ((data.getFlags() & 0x7effffe0L) != 0L) { Console.WriteLine("ERROR: Incorrect Header Flags"); return STATUS_ERROR_INCORRECT_FLAGS; } } else if ((npd.getVersion() == 3L) || (npd.getVersion() == 4L)) { if ((data.getFlags() & 0x7effffc0L) != 0L) { Console.WriteLine("ERROR: Incorrect Header Flags"); return STATUS_ERROR_INCORRECT_FLAGS; } } else { Console.WriteLine("ERROR: Unsupported EDAT version (need keys)"); return STATUS_ERROR_INCORRECT_VERSION; } if (npd.getVersion() == 4L) { } i.Read(buffer, 0, buffer.Length); i.Read(buffer3, 0, buffer3.Length); Console.WriteLine("Checking header hash:"); AppLoader loader = new AppLoader(); int hashFlag = ((data.getFlags() & FLAG_KEYENCRYPTED) == 0L) ? 2 : 0x10000002; if ((data.getFlags() & FLAG_DEBUG) != 0L) { hashFlag |= 0x1000000; } if (!loader.doAll(hashFlag, 1, buffer, 0, o, 0, buffer.Length, new byte[0x10], new byte[0x10], rifKey, buffer3, 0)) { Console.WriteLine("Error verifying header. Is rifKey valid?."); return STATUS_ERROR_HEADERCHECK; } Console.WriteLine("Checking metadata hash:"); loader = new AppLoader(); loader.doInit(hashFlag, 1, new byte[0x10], new byte[0x10], rifKey); int num3 = ((data.getFlags() & FLAG_COMPRESSED) != 0L) ? 0x20 : 0x10; int num4 = (int) (((data.getFileLen() + data.getBlockSize()) - 11) / data.getBlockSize()); int num5 = 0; int num6 = 0x100; for (long j = num3 * num4; j > 0L; j -= num8) { num8 = (HEADER_MAX_BLOCKSIZE > j) ? ((int) j) : HEADER_MAX_BLOCKSIZE; i.Seek((long) (num6 + num5), SeekOrigin.Begin); byte[] buffer4 = new byte[num8]; o = new byte[num8]; i.Read(buffer4, 0, buffer4.Length); loader.doUpdate(buffer4, 0, o, 0, num8); num5 += num8; } if (!loader.doFinal(buffer, 0x90)) { Console.WriteLine("Error verifying metadatasection. Data tampered"); return STATUS_ERROR_HEADERCHECK; } return STATUS_OK; }
private int encryptData(FileStream ii, FileStream o, NPD npd, EDATData data, byte[] rifkey) { int num = (int) (((data.getFileLen() + data.getBlockSize()) - 1) / data.getBlockSize()); byte[] dest = new byte[num * 0x10]; byte[] buffer2 = new byte[ii.Length + 15L]; for (int i = 0; i < num; i++) { long offset = i * data.getBlockSize(); ii.Seek(offset, SeekOrigin.Begin); int length = (int) data.getBlockSize(); if (i == (num - 1)) { length = (int) (data.getFileLen() % new BigInteger(data.getBlockSize())); } int num5 = length; length = (length + 15) & -16; byte[] buffer3 = new byte[length]; byte[] buffer4 = new byte[length]; for (int j = num5; j > 0; j -= ii.Read(buffer4, num5 - j, j)) { } for (int k = num5; k < length; k++) { buffer4[k] = 0; } byte[] buffer5 = new byte[0x10]; byte[] buffer6 = new byte[0x10]; byte[] buffer7 = this.calculateBlockKey(i, npd); ToolsImpl.aesecbEncrypt(rifkey, buffer7, 0, buffer5, 0, buffer7.Length); ConversionUtils.arraycopy(buffer5, 0, buffer6, 0L, buffer5.Length); int cryptoFlag = 2; int hashFlag = 2; AppLoaderReverse reverse = new AppLoaderReverse(); byte[] iv = npd.getDigest(); byte[] generatedHash = new byte[0x10]; reverse.doAll(hashFlag, cryptoFlag, buffer4, 0, buffer3, 0, buffer4.Length, buffer5, iv, buffer6, generatedHash, 0); ConversionUtils.arraycopy(buffer3, 0, buffer2, offset, length); ConversionUtils.arraycopy(generatedHash, 0, dest, (long) (i * 0x10), 0x10); } byte[] buffer = ConversionUtils.getByteArray("4D6164652062792052325220546F6F6C"); o.Write(dest, 0, dest.Length); o.Write(buffer2, 0, buffer2.Length - 15); o.Write(buffer, 0, buffer.Length); return STATUS_OK; }
private byte[] writeValidNPD(string filename, byte[] devKLic, NPD[] npdPtr, FileStream fin, byte[] contentID, byte[] flags, byte[] version, byte[] type) { int num; byte[] dest = new byte[0x80]; dest[0] = 0x4e; dest[1] = 80; dest[2] = 0x44; dest[3] = 0; dest[4] = 0; dest[5] = 0; dest[6] = 0; dest[7] = version[0]; dest[8] = 0; dest[9] = 0; dest[10] = 0; dest[11] = 3; dest[12] = 0; dest[13] = 0; dest[14] = 0; dest[15] = type[0]; for (num = 0; num < 0x30; num++) { dest[0x10 + num] = contentID[num]; } ConversionUtils.arraycopy(ConversionUtils.charsToByte("FixedLicenseEDAT".ToCharArray()), 0, dest, 0x40L, 0x10); ConversionUtils.arraycopy(this.createNPDHash1(filename, dest), 0, dest, 80L, 0x10); ConversionUtils.arraycopy(this.createNPDHash2(devKLic, dest), 0, dest, 0x60L, 0x10); for (num = 0; num < 0x10; num++) { dest[0x70 + num] = 0; } npdPtr[0] = NPD.createNPD(dest); return dest; }
public int encryptFile(string inFile, string outFile, byte[] devKLic, byte[] keyFromRif, byte[] contentID, byte[] flags, byte[] type, byte[] version) { int num2; int num9; FileStream fin = File.Open(inFile, FileMode.Open); NPD[] npdPtr = new NPD[1]; FileStream o = File.Open(outFile, FileMode.Create); string[] strArray = o.Name.Split(new char[] { '\\' }); byte[] buffer = this.writeValidNPD(strArray[strArray.Length - 1], devKLic, npdPtr, fin, contentID, flags, version, type); o.Write(buffer, 0, buffer.Length); byte[] buffer2 = new byte[] { 0, 0, 0, 0 }; o.Write(buffer2, 0, 4); buffer2[2] = 0x40; o.Write(buffer2, 0, 4); long length = fin.Length; byte[] bytes = BitConverter.GetBytes(length); byte[] buffer4 = new byte[8]; for (num2 = 0; num2 < 8; num2++) { buffer4[num2] = 0; } for (num2 = 0; num2 < bytes.Length; num2++) { buffer4[7 - num2] = bytes[num2]; } o.Write(buffer4, 0, 8); buffer2[0] = 0; while (o.Length < 0x100L) { o.Write(buffer2, 0, 1); } EDATData data = new EDATData { flags = 0L, blockSize = 0x4000L, fileLen = new BigInteger(length) }; byte[] rifkey = this.getKey(npdPtr[0], data, devKLic, keyFromRif); int hashFlag = 2; this.encryptData(fin, o, npdPtr[0], data, rifkey); o.Seek(0x90L, SeekOrigin.Begin); AppLoader loader = new AppLoader(); loader.doInit(hashFlag, 1, new byte[0x10], new byte[0x10], rifkey); int num4 = ((data.getFlags() & FLAG_COMPRESSED) != 0L) ? 0x20 : 0x10; int num5 = (int) (((data.getFileLen() + data.getBlockSize()) - 11) / data.getBlockSize()); int num6 = 0; int num7 = 0x100; for (long i = num4 * num5; i > 0L; i -= num9) { num9 = (HEADER_MAX_BLOCKSIZE > i) ? ((int) i) : HEADER_MAX_BLOCKSIZE; o.Seek((long) (num7 + num6), SeekOrigin.Begin); byte[] buffer6 = new byte[num9]; byte[] buffer7 = new byte[num9]; o.Read(buffer6, 0, buffer6.Length); loader.doUpdate(buffer6, 0, buffer7, 0, num9); num6 += num9; } byte[] generatedHash = new byte[0x10]; loader.doFinalButGetHash(generatedHash); o.Seek(0x90L, SeekOrigin.Begin); o.Write(generatedHash, 0, generatedHash.Length); o.Seek(0L, SeekOrigin.Begin); byte[] buffer9 = new byte[160]; byte[] buffer10 = new byte[160]; o.Read(buffer9, 0, buffer9.Length); AppLoaderReverse reverse = new AppLoaderReverse(); byte[] buffer11 = new byte[0x10]; bool flag = reverse.doAll(hashFlag, 1, buffer9, 0, buffer10, 0, buffer9.Length, new byte[0x10], new byte[0x10], rifkey, buffer11, 0); o.Seek(160L, SeekOrigin.Begin); o.Write(buffer11, 0, buffer11.Length); while (o.Length < 0x100L) { o.Write(buffer2, 0, 1); } o.Close(); fin.Close(); return STATUS_OK; }
private int validateNPD(string filename, byte[] devKLic, NPD[] npdPtr, FileStream i) { i.Seek(0L, SeekOrigin.Begin); byte[] buffer = new byte[0x80]; i.Read(buffer, 0, buffer.Length); byte[] buffer2 = new byte[4]; i.Read(buffer2, 0, buffer2.Length); if ((ConversionUtils.be32(buffer2, 0) & FLAG_SDAT) != 0L) { Console.WriteLine("INFO: SDAT detected. NPD header is not validated"); } else { if (!this.checkNPDHash1(filename, buffer)) { Console.WriteLine("ERROR: Hashing Title ID Name"); return STATUS_ERROR_HASHTITLEIDNAME; } if (devKLic == null) { Console.WriteLine("WARNING: Can not validate devklic header"); } else if (!this.checkNPDHash2(devKLic, buffer)) { Console.WriteLine("ERROR: Hashing devklic"); return STATUS_ERROR_HASHDEVKLIC; } } npdPtr[0] = NPD.createNPD(buffer); return STATUS_OK; }
private byte[] getKey(NPD npd, EDATData data, byte[] devKLic, byte[] keyFromRif) { byte[] output = null; if ((data.getFlags() & FLAG_SDAT) != 0L) { output = new byte[0x10]; ToolsImpl.XOR(output, npd.getDevHash(), EDATKeys.SDATKEY); return output; } if (npd.getLicense() == 3L) { return devKLic; } if (npd.getLicense() == 2L) { output = keyFromRif; } return output; }
private int checkHeader(byte[] rifKey, EDATData data, NPD npd, FileStream i) { int num8; i.Seek(0L, SeekOrigin.Begin); byte[] buffer = new byte[160]; byte[] o = new byte[160]; byte[] buffer3 = new byte[0x10]; Console.WriteLine("Checking NPD Version:" + npd.getVersion()); if ((npd.getVersion() == 0L) || (npd.getVersion() == 1L)) { if ((data.getFlags() & 0x7ffffffeL) != 0L) { Console.WriteLine("ERROR: Incorrect Header Flags"); return(STATUS_ERROR_INCORRECT_FLAGS); } } else if (npd.getVersion() == 2L) { if ((data.getFlags() & 0x7effffe0L) != 0L) { Console.WriteLine("ERROR: Incorrect Header Flags"); return(STATUS_ERROR_INCORRECT_FLAGS); } } else if ((npd.getVersion() == 3L) || (npd.getVersion() == 4L)) { if ((data.getFlags() & 0x7effffc0L) != 0L) { Console.WriteLine("ERROR: Incorrect Header Flags"); return(STATUS_ERROR_INCORRECT_FLAGS); } } else { Console.WriteLine("ERROR: Unsupported EDAT version (need keys)"); return(STATUS_ERROR_INCORRECT_VERSION); } if (npd.getVersion() == 4L) { } i.Read(buffer, 0, buffer.Length); i.Read(buffer3, 0, buffer3.Length); Console.WriteLine("Checking header hash:"); AppLoader loader = new AppLoader(); int hashFlag = ((data.getFlags() & FLAG_KEYENCRYPTED) == 0L) ? 2 : 0x10000002; if ((data.getFlags() & FLAG_DEBUG) != 0L) { hashFlag |= 0x1000000; } if (!loader.doAll(hashFlag, 1, buffer, 0, o, 0, buffer.Length, new byte[0x10], new byte[0x10], rifKey, buffer3, 0)) { Console.WriteLine("Error verifying header. Is rifKey valid?."); return(STATUS_ERROR_HEADERCHECK); } Console.WriteLine("Checking metadata hash:"); loader = new AppLoader(); loader.doInit(hashFlag, 1, new byte[0x10], new byte[0x10], rifKey); int num3 = ((data.getFlags() & FLAG_COMPRESSED) != 0L) ? 0x20 : 0x10; int num4 = (int)(((data.getFileLen() + data.getBlockSize()) - 11) / data.getBlockSize()); int num5 = 0; int num6 = 0x100; for (long j = num3 * num4; j > 0L; j -= num8) { num8 = (HEADER_MAX_BLOCKSIZE > j) ? ((int)j) : HEADER_MAX_BLOCKSIZE; i.Seek((long)(num6 + num5), SeekOrigin.Begin); byte[] buffer4 = new byte[num8]; o = new byte[num8]; i.Read(buffer4, 0, buffer4.Length); loader.doUpdate(buffer4, 0, o, 0, num8); num5 += num8; } if (!loader.doFinal(buffer, 0x90)) { Console.WriteLine("Error verifying metadatasection. Data tampered"); return(STATUS_ERROR_HEADERCHECK); } return(STATUS_OK); }
private int decryptData(FileStream ii, FileStream o, NPD npd, EDATData data, byte[] rifkey) { int numBlocks = (int)((data.getFileLen() + data.getBlockSize() - 1) / data.getBlockSize()); int metadataSectionSize = ((data.getFlags() & FLAG_COMPRESSED) != 0 || (data.getFlags() & FLAG_0x20) != 0) ? 0x20 : 0x10; int baseOffset = 0x100; //+ offset (unknown) for (int i = 0; i < numBlocks; i++) { ii.Seek(baseOffset + i * metadataSectionSize, SeekOrigin.Begin); byte[] expectedHash = new byte[0x10]; long offset; int len; int compressionEndBlock = 0; if ((data.getFlags() & FLAG_COMPRESSED) != 0) { byte[] metadata = new byte[0x20]; ii.Read(metadata, 0, metadata.Length); byte[] result = decryptMetadataSection(metadata); offset = (int)(ConversionUtils.be64(result, 0)); // + offset (unknown) len = (int)(ConversionUtils.be32(result, 8)); compressionEndBlock = (int)(ConversionUtils.be32(result, 0xC)); ConversionUtils.arraycopy(metadata, 0, expectedHash, 0, 0x10); } else if ((data.getFlags() & FLAG_0x20) != 0) { //NOT TESTED: CASE WHERE METADATASECTION IS 0x20 BYTES LONG byte[] metadata = new byte[0x20]; ii.Read(metadata, 0, metadata.Length); for (int j = 0; j < 0x10; j++) { expectedHash[j] = (byte)(metadata[j] ^ metadata[j + 0x10]); } offset = baseOffset + i * data.getBlockSize() + numBlocks * metadataSectionSize; len = (int)(data.getBlockSize()); if (i == numBlocks - 1) { len = (int)(data.getFileLen() % (new BigInteger(data.getBlockSize()))); } } else { ii.Read(expectedHash, 0, expectedHash.Length); offset = baseOffset + i * data.getBlockSize() + numBlocks * metadataSectionSize; len = (int)(data.getBlockSize()); if (i == numBlocks - 1) { len = (int)(data.getFileLen() % (new BigInteger(data.getBlockSize()))); } } int realLen = len; len = (int)((uint)(len + 0xF) & 0xFFFFFFF0); Debug.Print("Offset: %016X, len: %08X, realLen: %08X, endCompress: %d\r\n", offset, len, realLen, compressionEndBlock); ii.Seek(offset, SeekOrigin.Begin); byte[] encryptedData = new byte[len]; byte[] decryptedData = new byte[len]; ii.Read(encryptedData, 0, encryptedData.Length); byte[] key = new byte[0x10]; byte[] hash = new byte[0x10]; byte[] blockKey = calculateBlockKey(i, npd); ToolsImpl.aesecbEncrypt(rifkey, blockKey, 0, key, 0, blockKey.Length); if ((data.getFlags() & FLAG_0x10) != 0) { ToolsImpl.aesecbEncrypt(rifkey, key, 0, hash, 0, key.Length); } else { ConversionUtils.arraycopy(key, 0, hash, 0, key.Length); } int cryptoFlag = ((data.getFlags() & FLAG_0x02) == 0) ? 0x2 : 0x1; int hashFlag; if ((data.getFlags() & FLAG_0x10) == 0) { hashFlag = 0x02; } else if ((data.getFlags() & FLAG_0x20) == 0) { hashFlag = 0x04; } else { hashFlag = 0x01; } if ((data.getFlags() & FLAG_KEYENCRYPTED) != 0) { cryptoFlag |= 0x10000000; hashFlag |= 0x10000000; } if ((data.getFlags() & FLAG_DEBUG) != 0) { cryptoFlag |= 0x01000000; hashFlag |= 0x01000000; } AppLoader a = new AppLoader(); byte[] iv = (npd.getVersion() <= 1) ? (new byte[0x10]) : npd.getDigest(); bool rresult = a.doAll(hashFlag, cryptoFlag, encryptedData, 0, decryptedData, 0, encryptedData.Length, key, npd.getDigest(), hash, expectedHash, 0); if (!rresult) { Debug.WriteLine("Error decrypting block " + i); // KDSBest find out why block 30 errors //return STATUS_ERROR_DECRYPTING; } if ((data.getFlags() & FLAG_COMPRESSED) != 0) { //byte[] decompress = new byte[Long.valueOf(data.getBlockSize()).intValue()]; //DECOMPRESS: MISSING ALGORITHM //out.write(decompress, 0, data.getBlockSize()); } else { o.Write(decryptedData, 0, realLen); } } return(STATUS_OK); }
private byte[] writeValidNPD(String filename, byte[] devKLic, NPD[] npdPtr, FileStream fin, byte[] contentID, byte[] flags, byte[] version, byte[] type) { byte[] npd = new byte[0x80]; //NPD Magic //ConversionUtils.arraycopy(npd, 0, result.magic, 0, 4); npd[0] = 0x4E; npd[1] = 0x50; npd[2] = 0x44; npd[3] = 0x00; //Version 3 //result.version = ConversionUtils.be32(npd, 4); npd[4] = 0x00; npd[5] = 0x00; npd[6] = 0x00; npd[7] = version[0]; //License 2 ref 3 klic /* 1 network, 2 local, 3 free */ //result.license = ConversionUtils.be32(npd, 8); npd[8] = 0x00; npd[9] = 0x00; npd[10] = 0x00; npd[11] = 0x03; //Type /* 1 exec, 21 update */ //result.type = ConversionUtils.be32(npd, 0xC); npd[12] = 0x00; npd[13] = 0x00; npd[14] = 0x00; npd[15] = type[0]; //No Idea where I get the content_id //ConversionUtils.arraycopy(npd, 0x10, result.content_id, 0, 0x30 for (int i = 0; i < 0x30; i++) { npd[0x10 + i] = contentID[i]; } //Used to create IV //ConversionUtils.arraycopy(npd, 0x40, result.digest, 0, 0x10); byte[] iv = ConversionUtils.charsToByte(("FixedLicenseEDAT").ToCharArray()); ConversionUtils.arraycopy(iv, 0, npd, 0x40, 0x10); //I guess it's a full file hash //ConversionUtils.arraycopy(npd, 0x50, result.titleHash, 0, 0x10); byte[] hash = createNPDHash1(filename, npd); ConversionUtils.arraycopy(hash, 0x00, npd, 0x50, 0x10); //Used to create Blockkey //ConversionUtils.arraycopy(npd, 0x60, result.devHash, 0, 0x10); byte[] devHash = createNPDHash2(devKLic, npd); ConversionUtils.arraycopy(devHash, 0, npd, 0x60, 0x10); //NPD EOF?!?!?! //result.unknown3 = ConversionUtils.be64(npd, 0x70); //result.unknown4 = ConversionUtils.be64(npd, 0x78); for (int i = 0; i < 16; i++) { npd[0x70 + i] = 0x00; } npdPtr[0] = NPD.createNPD(npd); return(npd); }
private byte[] calculateBlockKey(int blk, NPD npd) { byte[] src = (npd.getVersion() <= 1L) ? new byte[0x10] : npd.getDevHash(); byte[] dest = new byte[0x10]; ConversionUtils.arraycopy(src, 0, dest, 0L, 12); dest[12] = (byte) ((blk >> 0x18) & 0xff); dest[13] = (byte) ((blk >> 0x10) & 0xff); dest[14] = (byte) ((blk >> 8) & 0xff); dest[15] = (byte) (blk & 0xff); return dest; }
private int decryptData(FileStream ii, FileStream o, NPD npd, EDATData data, byte[] rifkey) { int num = (int) (((data.getFileLen() + data.getBlockSize()) - 1) / data.getBlockSize()); int num2 = (((data.getFlags() & FLAG_COMPRESSED) != 0L) || ((data.getFlags() & FLAG_0x20) != 0L)) ? 0x20 : 0x10; int num3 = 0x100; for (int i = 0; i < num; i++) { long num5; int num6; byte[] buffer2; int num11; ii.Seek((long) (num3 + (i * num2)), SeekOrigin.Begin); byte[] dest = new byte[0x10]; int num7 = 0; if ((data.getFlags() & FLAG_COMPRESSED) != 0L) { buffer2 = new byte[0x20]; ii.Read(buffer2, 0, buffer2.Length); byte[] buffer3 = this.decryptMetadataSection(buffer2); num5 = (int) ConversionUtils.be64(buffer3, 0); num6 = (int) ConversionUtils.be32(buffer3, 8); num7 = (int) ConversionUtils.be32(buffer3, 12); ConversionUtils.arraycopy(buffer2, 0, dest, 0L, 0x10); } else if ((data.getFlags() & FLAG_0x20) != 0L) { buffer2 = new byte[0x20]; ii.Read(buffer2, 0, buffer2.Length); for (int j = 0; j < 0x10; j++) { dest[j] = (byte) (buffer2[j] ^ buffer2[j + 0x10]); } num5 = (num3 + (i * data.getBlockSize())) + (num * num2); num6 = (int) data.getBlockSize(); if (i == (num - 1)) { num6 = (int) (data.getFileLen() % new BigInteger(data.getBlockSize())); } } else { ii.Read(dest, 0, dest.Length); num5 = (num3 + (i * data.getBlockSize())) + (num * num2); num6 = (int) data.getBlockSize(); if (i == (num - 1)) { num6 = (int) (data.getFileLen() % new BigInteger(data.getBlockSize())); } } int count = num6; num6 = (num6 + 15) & -16; Debug.Print("Offset: %016X, len: %08X, realLen: %08X, endCompress: %d\r\n", new object[] { num5, num6, count, num7 }); ii.Seek(num5, SeekOrigin.Begin); byte[] buffer = new byte[num6]; byte[] buffer5 = new byte[num6]; ii.Read(buffer, 0, buffer.Length); byte[] buffer6 = new byte[0x10]; byte[] buffer7 = new byte[0x10]; byte[] buffer8 = this.calculateBlockKey(i, npd); ToolsImpl.aesecbEncrypt(rifkey, buffer8, 0, buffer6, 0, buffer8.Length); if ((data.getFlags() & FLAG_0x10) != 0L) { ToolsImpl.aesecbEncrypt(rifkey, buffer6, 0, buffer7, 0, buffer6.Length); } else { ConversionUtils.arraycopy(buffer6, 0, buffer7, 0L, buffer6.Length); } int cryptoFlag = ((data.getFlags() & FLAG_0x02) == 0L) ? 2 : 1; if ((data.getFlags() & FLAG_0x10) == 0L) { num11 = 2; } else if ((data.getFlags() & FLAG_0x20) == 0L) { num11 = 4; } else { num11 = 1; } if ((data.getFlags() & FLAG_KEYENCRYPTED) != 0L) { cryptoFlag |= 0x10000000; num11 |= 0x10000000; } if ((data.getFlags() & FLAG_DEBUG) != 0L) { cryptoFlag |= 0x1000000; num11 |= 0x1000000; } AppLoader loader = new AppLoader(); byte[] buffer9 = (npd.getVersion() <= 1L) ? new byte[0x10] : npd.getDigest(); if (!loader.doAll(num11, cryptoFlag, buffer, 0, buffer5, 0, buffer.Length, buffer6, npd.getDigest(), buffer7, dest, 0)) { Debug.WriteLine("Error decrypting block " + i); } if ((data.getFlags() & FLAG_COMPRESSED) == 0L) { o.Write(buffer5, 0, count); } } return STATUS_OK; }