/** * * Performs checks on the header: * -Version check: Must be between 0 and 3 included * -Flags check: Checks that only valid active flags are set for given version. * Ver 0, 1 : Debug and compress * Ver 2 : Debug, compress, SDAT, Keys encrypted,.. * Ver 3: Debug, compress, SDAT, Keys encrypted... * -Metadata section hash: Checks that metadata section is valid (uses encryption key) * -Header hash: Checks that header is correct (uses encryption key) * @param rifKey * @param data * @param npd * @param in * @return * @throws IOException */ 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 Debug.WriteLine("Checking NPD Version:" + npd.getVersion()); if ((npd.getVersion() == 0) || (npd.getVersion() == 1)) { if ((data.getFlags() & 0x7FFFFFFE) != 0) { return(STATUS_ERROR_INCORRECT_FLAGS); } } else if (npd.getVersion() == 2) { if ((data.getFlags() & 0x7EFFFFE0) != 0) { return(STATUS_ERROR_INCORRECT_FLAGS); } } else if (npd.getVersion() == 3) { if ((data.getFlags() & 0x7EFFFFC0) != 0) { return(STATUS_ERROR_INCORRECT_FLAGS); } } else { return(STATUS_ERROR_INCORRECT_VERSION); } i.Read(header, 0, header.Length); i.Read(expectedHash, 0, expectedHash.Length); Debug.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) { Debug.WriteLine("Error verifying header. Is rifKey valid?."); return(STATUS_ERROR_HEADERCHECK); } Debug.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) { Debug.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 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); }
/** * * Performs checks on the header: * -Version check: Must be between 0 and 3 included * -Flags check: Checks that only valid active flags are set for given version. * Ver 0, 1 : Debug and compress * Ver 2 : Debug, compress, SDAT, Keys encrypted,.. * Ver 3: Debug, compress, SDAT, Keys encrypted... * -Metadata section hash: Checks that metadata section is valid (uses encryption key) * -Header hash: Checks that header is correct (uses encryption key) * @param rifKey * @param data * @param npd * @param in * @return * @throws IOException */ 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 Debug.WriteLine("Checking NPD Version:" + npd.getVersion()); if ((npd.getVersion() == 0) || (npd.getVersion() == 1)) { if ((data.getFlags() & 0x7FFFFFFE) != 0) return STATUS_ERROR_INCORRECT_FLAGS; } else if (npd.getVersion() == 2) { if ((data.getFlags() & 0x7EFFFFE0) != 0) return STATUS_ERROR_INCORRECT_FLAGS; } else if (npd.getVersion() == 3) { if ((data.getFlags() & 0x7EFFFFC0) != 0) return STATUS_ERROR_INCORRECT_FLAGS; } else return STATUS_ERROR_INCORRECT_VERSION; i.Read(header, 0, header.Length); i.Read(expectedHash, 0, expectedHash.Length); Debug.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) { Debug.WriteLine("Error verifying header. Is rifKey valid?."); return STATUS_ERROR_HEADERCHECK; } Debug.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) { Debug.WriteLine("Error verifying metadatasection. Data tampered"); return STATUS_ERROR_HEADERCHECK; } return STATUS_OK; }