Want more features on Pastebin? Sign Up, it's FREE!
Guest

Untitled

By: a guest on Dec 14th, 2011  |  syntax: None  |  size: 30.02 KB  |  views: 1,108  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
This paste has a previous version, view the difference. Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. import java.io.File;
  2. import java.io.FileNotFoundException;
  3. import java.io.IOException;
  4. import java.io.RandomAccessFile;
  5. import java.io.UnsupportedEncodingException;
  6. import java.math.BigInteger;
  7. import java.security.InvalidAlgorithmParameterException;
  8. import java.security.InvalidKeyException;
  9. import java.security.NoSuchAlgorithmException;
  10. import java.security.spec.AlgorithmParameterSpec;
  11. import javax.crypto.Cipher;
  12. import javax.crypto.Mac;
  13. import javax.crypto.NoSuchPaddingException;
  14. import javax.crypto.ShortBufferException;
  15. import javax.crypto.spec.IvParameterSpec;
  16. import javax.crypto.spec.SecretKeySpec;
  17.  
  18. public class EDAT {
  19.  
  20.     public static final int STATUS_ERROR_INPUTFILE_IO = -100;
  21.     public static final int STATUS_ERROR_HASHTITLEIDNAME = -1;
  22.     public static final int STATUS_ERROR_HASHDEVKLIC = -2;
  23.     public static final int STATUS_ERROR_MISSINGKEY = -3;
  24.     public static final int STATUS_ERROR_HEADERCHECK = -4;
  25.     public static final int STATUS_ERROR_DECRYPTING = -5;
  26.     public static final int STATUS_ERROR_INCORRECT_FLAGS = -6;
  27.     public static final int STATUS_ERROR_INCORRECT_VERSION = -7;
  28.     public static final int STATUS_OK = 0;
  29.     public static final long FLAG_COMPRESSED = 0x00000001L;
  30.     public static final long FLAG_0x02 = 0x00000002L;
  31.     public static final long FLAG_KEYENCRYPTED = 0x00000008L;
  32.     public static final long FLAG_0x10 = 0x00000010L;
  33.     public static final long FLAG_0x20 = 0x00000020L;
  34.     public static final long FLAG_SDAT = 0x01000000L;
  35.     public static final long FLAG_DEBUG = 0x80000000L;
  36.  
  37.  
  38.  
  39.     /**
  40.      *
  41.      * This function reads given file and decrypts it
  42.      *
  43.      * @param inFile EDAT file to decrypt
  44.      * @param outFile Path to store the decrypted data
  45.      * @param devKLic Authentication key. Also used for decryption on free content
  46.      * @param keyFromRif Key obtained after decrypting rif60
  47.      * @return Result of the operation
  48.      */
  49.     public int decryptFile(String inFile, String outFile, byte[] devKLic, byte[] keyFromRif) throws FileNotFoundException, IOException {
  50.         File fin = new File(inFile);
  51.         RandomAccessFile raf = null;
  52.         raf = new RandomAccessFile(fin, "r");
  53.         NPD[] ptr = new NPD[1]; //Ptr to Ptr
  54.         int result = validateNPD(fin.getName(), devKLic, ptr, raf); //Validate NPD hashes
  55.         if (result < 0) {
  56.             return result;
  57.         }
  58.         NPD npd = ptr[0];
  59.         EDATData data = getEDATData(raf); //Get flags, blocksize and file len
  60.         byte[] rifkey = getKey(npd, data, devKLic, keyFromRif); //Obtain the key for decryption (result of sc471 or sdatkey)
  61.         if (rifkey == null) {
  62.             System.out.println("ERROR: Key for decryption is missing");
  63.             return STATUS_ERROR_MISSINGKEY;
  64.         } else {
  65.             System.out.println("DECRYPTION KEY: " + ConversionUtils.getHexString(rifkey));
  66.         }
  67.         result = checkHeader(rifkey, data, npd, raf);
  68.         if (result < 0) {
  69.             return result;
  70.         }
  71.         RandomAccessFile out = new RandomAccessFile(outFile, "rw");
  72.         result = decryptData(raf, out, npd, data, rifkey);
  73.         if (result < 0) {
  74.             return result;
  75.         }
  76.         raf.close();
  77.         return STATUS_OK;
  78.     }
  79.     private static final int HEADER_MAX_BLOCKSIZE = 0x3C00;
  80.  
  81.     /**
  82.      *
  83.      * Performs checks on the header:
  84.      * -Version check: Must be between 0 and 3 included
  85.      * -Flags check: Checks that only valid active flags are set for given version.
  86.      *  Ver 0, 1 : Debug and compress
  87.      *  Ver 2 : Debug, compress, SDAT, Keys encrypted,..
  88.      *  Ver 3: Debug, compress, SDAT, Keys encrypted...
  89.      * -Metadata section hash: Checks that metadata section is valid (uses encryption key)
  90.      * -Header hash: Checks that header is correct (uses encryption key)
  91.      * @param rifKey
  92.      * @param data
  93.      * @param npd
  94.      * @param in
  95.      * @return
  96.      * @throws IOException
  97.      */
  98.     private int checkHeader(byte[] rifKey, EDATData data, NPD npd, RandomAccessFile in) throws IOException {
  99.         in.seek(0);
  100.         byte[] header = new byte[0xA0];
  101.         byte[] out = new byte[0xA0];
  102.         byte[] expectedHash = new byte[0x10];
  103.         //Version check
  104.         System.out.println("Checking NPD Version:" + npd.getVersion());
  105.         if ((npd.getVersion() == 0) || (npd.getVersion() == 1)) {
  106.             if ((data.getFlags() & 0x7FFFFFFE) != 0) return STATUS_ERROR_INCORRECT_FLAGS;            
  107.         } else if (npd.getVersion() == 2) {
  108.             if ((data.getFlags() & 0x7EFFFFE0) != 0) return STATUS_ERROR_INCORRECT_FLAGS;
  109.         } else if (npd.getVersion() == 3) {
  110.             if ((data.getFlags() & 0x7EFFFFC0) != 0) return STATUS_ERROR_INCORRECT_FLAGS;
  111.         } else return STATUS_ERROR_INCORRECT_VERSION;
  112.  
  113.         in.readFully(header);
  114.         in.readFully(expectedHash);
  115.         System.out.println("Checking header hash:");
  116.         AppLoader a = new AppLoader();
  117.         int hashFlag = ((data.getFlags() & FLAG_KEYENCRYPTED) == 0) ? 0x00000002 : 0x10000002;
  118.         if ((data.getFlags() & FLAG_DEBUG) != 0) hashFlag |= 0x01000000;
  119.  
  120.         //Veryfing header
  121.         boolean result = a.doAll(hashFlag, 0x00000001, header, 0, out, 0, header.length, new byte[0x10], new byte[0x10], rifKey, expectedHash, 0);
  122.         if (!result) {
  123.             System.out.println("Error verifying header. Is rifKey valid?.");
  124.             return STATUS_ERROR_HEADERCHECK;
  125.         }
  126.         System.out.println("Checking metadata hash:");
  127.         a = new AppLoader();
  128.         a.doInit(hashFlag, 0x00000001, new byte[0x10], new byte[0x10], rifKey);
  129.  
  130.         int sectionSize = ((data.getFlags() & FLAG_COMPRESSED) != 0) ? 0x20 : 0x010; //BUG??? What about FLAG0x20??
  131.         //Determine the metadatasection total len
  132.         int numBlocks = (int) ((data.getFileLen().intValue() + data.getBlockSize() - 11) / data.getBlockSize());
  133.  
  134.         int readed = 0;
  135.         int baseOffset = 0x100;
  136.         //baseOffset +=  modifier; //There is an unknown offset to add to the metadatasection... value seen 0
  137.         long remaining = sectionSize * numBlocks;
  138.         while (remaining > 0) {
  139.             int lenToRead = (HEADER_MAX_BLOCKSIZE > remaining) ? (int) remaining : HEADER_MAX_BLOCKSIZE;
  140.             in.seek(baseOffset + readed);
  141.             byte[] content = new byte[lenToRead];
  142.             out = new byte[lenToRead];
  143.             in.readFully(content);
  144.             a.doUpdate(content, 0, out, 0, lenToRead);
  145.             readed += lenToRead;
  146.             remaining -= lenToRead;
  147.         }
  148.         result = a.doFinal(header, 0x90);
  149.  
  150.  
  151.         if (!result) {
  152.             System.out.println("Error verifying metadatasection. Data tampered");
  153.             return STATUS_ERROR_HEADERCHECK;
  154.         }
  155.         return STATUS_OK;
  156.     }
  157.  
  158.     private byte[] decryptMetadataSection(byte[] metadata) {
  159.         byte[] result = new byte[0x10];
  160.         result[0x00] = (byte) (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
  161.         result[0x01] = (byte) (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
  162.         result[0x02] = (byte) (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
  163.         result[0x03] = (byte) (metadata[0xF] ^ metadata[0xB] ^ metadata[0x13]);
  164.         result[0x04] = (byte) (metadata[0x4] ^ metadata[0x8] ^ metadata[0x14]);
  165.         result[0x05] = (byte) (metadata[0x5] ^ metadata[0x9] ^ metadata[0x15]);
  166.         result[0x06] = (byte) (metadata[0x6] ^ metadata[0xA] ^ metadata[0x16]);
  167.         result[0x07] = (byte) (metadata[0x7] ^ metadata[0xB] ^ metadata[0x17]);
  168.         result[0x08] = (byte) (metadata[0xC] ^ metadata[0x0] ^ metadata[0x18]);
  169.         result[0x09] = (byte) (metadata[0xD] ^ metadata[0x1] ^ metadata[0x19]);
  170.         result[0x0A] = (byte) (metadata[0xE] ^ metadata[0x2] ^ metadata[0x1A]);
  171.         result[0x0B] = (byte) (metadata[0xF] ^ metadata[0x3] ^ metadata[0x1B]);
  172.         result[0x0C] = (byte) (metadata[0x4] ^ metadata[0x0] ^ metadata[0x1C]);
  173.         result[0x0D] = (byte) (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
  174.         result[0x0E] = (byte) (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
  175.         result[0x0F] = (byte) (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
  176.         return result;
  177.     }
  178.  
  179.     private EDATData getEDATData(RandomAccessFile in) throws IOException {
  180.         in.seek(0x80);
  181.         byte[] data = new byte[0x10];
  182.         in.readFully(data);
  183.         return EDATData.createEDATData(data);
  184.     }
  185.  
  186.     private boolean compareBytes(byte[] value1, int offset1, byte[] value2, int offset2, int len) {
  187.         boolean result = true;
  188.         for (int i = 0; i < len; i++) {
  189.             if (value1[i + offset1] != value2[i + offset2]) {
  190.                 result = false;
  191.                 break;
  192.             }
  193.         }
  194.         return result;
  195.     }
  196.  
  197.     private int validateNPD(String filename, byte[] devKLic, NPD[] npdPtr, RandomAccessFile in) throws IOException {
  198.         in.seek(0);
  199.         byte[] npd = new byte[0x80];
  200.         in.readFully(npd);
  201.         byte[] extraData = new byte[0x04];
  202.         in.readFully(extraData);
  203.         long flag = ConversionUtils.be32(extraData, 0);
  204.         if ((flag & FLAG_SDAT) != 0) {
  205.             System.out.println("INFO: SDAT detected. NPD header is not validated");
  206.         } else if (!checkNPDHash1(filename, npd)) {
  207.             return STATUS_ERROR_HASHTITLEIDNAME;
  208.         } else if (devKLic == null) {
  209.             System.out.println("WARNING: Can not validate devklic header");
  210.         } else if (!checkNPDHash2(devKLic, npd)) {
  211.             return STATUS_ERROR_HASHDEVKLIC;
  212.         }
  213.         npdPtr[0] = NPD.createNPD(npd);
  214.         return STATUS_OK;
  215.     }
  216.  
  217.     private boolean checkNPDHash1(String filename, byte[] npd) throws UnsupportedEncodingException {
  218.         byte[] fileBytes = filename.getBytes("US-ASCII");
  219.         byte data1[] = new byte[0x30 + fileBytes.length];
  220.         System.arraycopy(npd, 0x10, data1, 0, 0x30);
  221.         System.arraycopy(fileBytes, 0x00, data1, 0x30, fileBytes.length);
  222.         byte[] hash1 = ToolsImpl.CMAC128(EDATKeys.npdrm_omac_key3, data1, 0, data1.length);
  223.         boolean result1 = compareBytes(hash1, 0, npd, 0x50, 0x10);
  224.         if (result1) {
  225.             System.out.println("NPD hash 1 is valid (" + ConversionUtils.getHexString(hash1) + ")");
  226.         }
  227.         return result1;
  228.     }
  229.  
  230.     private boolean checkNPDHash2(byte[] klicensee, byte[] npd) {
  231.         byte[] xoredKey = new byte[0x10];
  232.         ToolsImpl.XOR(xoredKey, klicensee, EDATKeys.npdrm_omac_key2);
  233.         byte[] calculated = ToolsImpl.CMAC128(xoredKey, npd, 0, 0x60);
  234.         boolean result2 = compareBytes(calculated, 0, npd, 0x60, 0x10);
  235.         if (result2) {
  236.             System.out.println("NPD hash 2 is valid (" + ConversionUtils.getHexString(calculated) + ")");
  237.         }
  238.         return result2;
  239.     }
  240.  
  241.  
  242.     private byte[] getKey(NPD npd, EDATData data, byte[] devKLic, byte[] keyFromRif) {
  243.         byte[] result = null;
  244.         if ((data.getFlags() & FLAG_SDAT) != 0) {
  245.             //Case SDAT
  246.             result = new byte[0x10];
  247.             ToolsImpl.XOR(result, npd.getDevHash(), EDATKeys.SDATKEY);
  248.         } else {
  249.             //Case EDAT
  250.             if (npd.getLicense() == 0x03) {
  251.                 result = devKLic;
  252.             } else if (npd.getLicense() == 0x02) {
  253.                 result = keyFromRif;
  254.             }
  255.         }
  256.         return result;
  257.     }
  258.  
  259.     private int decryptData(RandomAccessFile in, RandomAccessFile out, NPD npd, EDATData data, byte[] rifkey) throws IOException {
  260.         int numBlocks = (int) ((data.getFileLen().intValue() + data.getBlockSize() - 1) / data.getBlockSize());
  261.         int metadataSectionSize = ((data.getFlags() & FLAG_COMPRESSED) != 0 || (data.getFlags() & FLAG_0x20) != 0) ? 0x20 : 0x10;
  262.         int baseOffset = 0x100; //+ offset (unknown)
  263.         for (int i = 0; i < numBlocks; i++) {
  264.             in.seek(baseOffset + i * metadataSectionSize);
  265.             byte[] expectedHash = new byte[0x10];
  266.             long offset;
  267.             int len;
  268.             int compressionEndBlock = 0;
  269.             if ((data.getFlags() & FLAG_COMPRESSED) != 0) {
  270.                 byte[] metadata = new byte[0x20];
  271.                 in.readFully(metadata);
  272.                 byte[] result = decryptMetadataSection(metadata);
  273.                 offset = ConversionUtils.be64(result, 0).intValue(); // + offset (unknown)
  274.                 len = Long.valueOf(ConversionUtils.be32(result, 8)).intValue();
  275.                 compressionEndBlock = Long.valueOf(ConversionUtils.be32(result, 0xC)).intValue();
  276.                 System.arraycopy(metadata, 0, expectedHash, 0, 0x10);
  277.             } else if ((data.getFlags() & FLAG_0x20) != 0) {
  278.                 //NOT TESTED: CASE WHERE METADATASECTION IS 0x20 BYTES LONG                
  279.                 byte[] metadata = new byte[0x20];
  280.                 in.readFully(metadata);
  281.                 for (int j = 0; j<0x10;j++) {
  282.                     expectedHash[j] = (byte)(metadata[j] ^ metadata[j+0x10]);
  283.                 }
  284.                 offset = baseOffset + i * data.getBlockSize() + numBlocks * metadataSectionSize;
  285.                 len = Long.valueOf(data.getBlockSize()).intValue();
  286.                 if (i == numBlocks - 1) {
  287.                     len = data.getFileLen().mod(BigInteger.valueOf(data.getBlockSize())).intValue();
  288.                 }
  289.             } else {
  290.                 in.readFully(expectedHash);
  291.                 offset = baseOffset + i * data.getBlockSize() + numBlocks * metadataSectionSize;
  292.                 len = Long.valueOf(data.getBlockSize()).intValue();
  293.                 if (i == numBlocks - 1) {
  294.                     len = data.getFileLen().mod(BigInteger.valueOf(data.getBlockSize())).intValue();
  295.                 }
  296.             }
  297.             int realLen = len;
  298.             len = (len + 0xF) & 0xFFFFFFF0;
  299.             System.out.printf("Offset: %016X, len: %08X, realLen: %08X, endCompress: %d\r\n", offset, len, realLen,compressionEndBlock);
  300.             in.seek(offset);
  301.             byte[] encryptedData = new byte[len];
  302.             byte[] decryptedData = new byte[len];
  303.             in.readFully(encryptedData);
  304.             byte[] key = new byte[0x10];
  305.             byte[] hash = new byte[0x10];
  306.             byte[] blockKey = calculateBlockKey(i, npd);
  307.  
  308.             ToolsImpl.aesecbEncrypt(rifkey, blockKey, 0, key, 0, blockKey.length);
  309.             if ((data.getFlags() & FLAG_0x10) != 0) {
  310.                 ToolsImpl.aesecbEncrypt(rifkey, key, 0, hash, 0, key.length);
  311.             } else {
  312.                 System.arraycopy(key, 0, hash, 0, key.length);
  313.             }
  314.             int cryptoFlag = ((data.getFlags() & FLAG_0x02) == 0) ? 0x2 : 0x1;
  315.             int hashFlag;
  316.             if ((data.getFlags() & FLAG_0x10) == 0) {
  317.                 hashFlag = 0x02;
  318.             } else if ((data.getFlags() & FLAG_0x20) == 0) {
  319.                 hashFlag = 0x04;
  320.             } else {
  321.                 hashFlag = 0x01;
  322.             }
  323.             if ((data.getFlags() & FLAG_KEYENCRYPTED) != 0) {
  324.                 cryptoFlag |= 0x10000000;
  325.                 hashFlag |= 0x10000000;
  326.             }
  327.             if ((data.getFlags() & FLAG_DEBUG) != 0) {
  328.                 cryptoFlag |= 0x01000000;
  329.                 hashFlag |= 0x01000000;
  330.             }            
  331.             AppLoader a = new AppLoader();
  332.             byte[] iv = (npd.getVersion() <= 1)?(new byte[0x10]):npd.getDigest();
  333.             boolean result = a.doAll(hashFlag, cryptoFlag, encryptedData, 0, decryptedData, 0, encryptedData.length, key, npd.getDigest(), hash, expectedHash, 0);
  334.             if (!result) {
  335.                 System.out.println("Error decrypting block " + i);
  336.                 return STATUS_ERROR_DECRYPTING;
  337.             }
  338.             if ((data.getFlags() & FLAG_COMPRESSED) != 0) {
  339.                 //byte[] decompress = new byte[Long.valueOf(data.getBlockSize()).intValue()];
  340.                 //DECOMPRESS: MISSING ALGORITHM                
  341.                 //out.write(decompress, 0, data.getBlockSize());
  342.             } else {
  343.                 out.write(decryptedData, 0, realLen);
  344.             }
  345.         }
  346.         return STATUS_OK;
  347.     }
  348.  
  349.     private byte[] calculateBlockKey(int blk, NPD npd) {
  350.         byte[] baseKey = (npd.getVersion() <= 1)?(new byte[0x10]):npd.getDevHash();
  351.         byte[] result = new byte[0x10];
  352.         System.arraycopy(baseKey, 0, result, 0, 0xC);
  353.         result[0xC] = (byte) (blk >> 24 & 0xFF);
  354.         result[0xD] = (byte) (blk >> 16 & 0xFF);
  355.         result[0xE] = (byte) (blk >> 8 & 0xFF);
  356.         result[0xF] = (byte) (blk & 0xFF);
  357.         return result;
  358.     }
  359.  
  360.     class AppLoader {
  361.  
  362.         private Decryptor dec;
  363.         private Hash hash;
  364.         private boolean hashDebug = false;
  365.         private boolean cryptoDebug = false; //NOT USED??
  366.  
  367.         public boolean doAll(int hashFlag, int cryptoFlag, byte[] in, int inOffset, byte[] out, int outOffset, int len, byte[] key, byte[] iv, byte[] hash, byte[] expectedHash, int hashOffset) {
  368.             doInit(hashFlag, cryptoFlag, key, iv, hash);
  369.             doUpdate(in, inOffset, out, outOffset, len);
  370.             return doFinal(expectedHash, hashOffset);
  371.         }
  372.  
  373.         public void doInit(int hashFlag, int cryptoFlag, byte[] key, byte[] iv, byte[] hashKey) {
  374.             byte[] calculatedKey = new byte[key.length];
  375.             byte[] calculatedIV = new byte[iv.length];
  376.             byte[] calculatedHash = new byte[hashKey.length];
  377.             getCryptoKeys(cryptoFlag, calculatedKey, calculatedIV, key, iv);
  378.             getHashKeys(hashFlag, calculatedHash, hashKey);
  379.             setDecryptor(cryptoFlag);
  380.             setHash(hashFlag);
  381.             System.out.println("ERK:  " + ConversionUtils.getHexString(calculatedKey));
  382.             System.out.println("IV:   " + ConversionUtils.getHexString(calculatedIV));
  383.             System.out.println("HASH: " + ConversionUtils.getHexString(calculatedHash));
  384.             dec.doInit(calculatedKey, calculatedIV);
  385.             hash.doInit(calculatedHash);
  386.         }
  387.  
  388.         public void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len) {
  389.             hash.doUpdate(in, inOffset, len);
  390.             dec.doUpdate(in, inOffset, out, outOffset, len);
  391.         }
  392.  
  393.         public boolean doFinal(byte[] expectedhash, int hashOffset) {
  394.             return hash.doFinal(expectedhash, hashOffset);
  395.         }
  396.  
  397.         private void getCryptoKeys(int cryptoFlag, byte[] calculatedKey, byte[] calculatedIV, byte[] key, byte[] iv) {
  398.             int mode = cryptoFlag & 0xF0000000;
  399.             switch (mode) {
  400.                 case 0x10000000:
  401.                     ToolsImpl.aescbcDecrypt(EDATKeys.EDATKEY, EDATKeys.EDATIV, key, 0, calculatedKey, 0, calculatedKey.length);
  402.                     System.arraycopy(iv, 0, calculatedIV, 0, calculatedIV.length);
  403.                     System.out.println("MODE: Encrypted ERK");
  404.                     break;
  405.                 case 0x20000000:
  406.                     System.arraycopy(EDATKeys.EDATKEY, 0, calculatedKey, 0, calculatedKey.length);
  407.                     System.arraycopy(EDATKeys.EDATIV, 0, calculatedIV, 0, calculatedIV.length);
  408.                     System.out.println("MODE: Default ERK");
  409.                     break;
  410.                 case 0x00000000:
  411.                     System.arraycopy(key, 0, calculatedKey, 0, calculatedKey.length);
  412.                     System.arraycopy(iv, 0, calculatedIV, 0, calculatedIV.length);
  413.                     System.out.println("MODE: Unencrypted ERK");
  414.                     break;
  415.                 default:
  416.                     throw new IllegalStateException("Crypto mode is not valid: Undefined keys calculator");
  417.             }
  418.         }
  419.  
  420.         private void getHashKeys(int hashFlag, byte[] calculatedHash, byte[] hash) {
  421.             int mode = hashFlag & 0xF0000000;
  422.             switch (mode) {
  423.                 case 0x10000000:
  424.                     ToolsImpl.aescbcDecrypt(EDATKeys.EDATKEY, EDATKeys.EDATIV, hash, 0, calculatedHash, 0, calculatedHash.length);
  425.                     System.out.println("MODE: Encrypted HASHKEY");
  426.                     break;
  427.                 case 0x20000000:
  428.                     System.arraycopy(EDATKeys.EDATHASH, 0, calculatedHash, 0, calculatedHash.length);
  429.                     System.out.println("MODE: Default HASHKEY");
  430.                     break;
  431.                 case 0x00000000:
  432.                     System.arraycopy(hash, 0, calculatedHash, 0, calculatedHash.length);
  433.                     System.out.println("MODE: Unencrypted HASHKEY");
  434.                     break;
  435.                 default:
  436.                     throw new IllegalStateException("Hash mode is not valid: Undefined keys calculator");
  437.             }
  438.         }
  439.  
  440.         private void setDecryptor(int cryptoFlag) {
  441.             int aux = cryptoFlag & 0xFF;
  442.             switch (aux) {
  443.                 case 0x01:
  444.                     dec = new NoCrypt();
  445.                     System.out.println("MODE: Decryption Algorithm NONE");
  446.                     break;
  447.                 case 0x02:
  448.                     dec = new AESCBC128Decrypt();
  449.                     System.out.println("MODE: Decryption Algorithm AESCBC128");
  450.                     break;
  451.                 default:
  452.                     throw new IllegalStateException("Crypto mode is not valid: Undefined decryptor");
  453.  
  454.             }
  455.             if ((cryptoFlag & 0x0F000000) != 0) cryptoDebug = true;
  456.  
  457.         }
  458.  
  459.         private void setHash(int hashFlag) {
  460.             int aux = hashFlag & 0xFF;
  461.             switch (aux) {
  462.                 case 0x01:
  463.                     hash = new HMAC();
  464.                     hash.setHashLen(0x14);
  465.                     System.out.println("MODE: Hash HMAC Len 0x14");
  466.                     break;
  467.                 case 0x02:
  468.                     hash = new CMAC();
  469.                     hash.setHashLen(0x10);
  470.                     System.out.println("MODE: Hash CMAC Len 0x10");
  471.                     break;
  472.                 case 0x04:
  473.                     hash = new HMAC();
  474.                     hash.setHashLen(0x10);
  475.                     System.out.println("MODE: Hash HMAC Len 0x10");
  476.                     break;
  477.                 default:
  478.                     throw new IllegalStateException("Hash mode is not valid: Undefined hash algorithm");
  479.             }
  480.             if ((hashFlag & 0x0F000000) != 0) hashDebug = true;
  481.         }
  482.  
  483.         abstract class Decryptor {
  484.  
  485.             public abstract void doInit(byte[] key, byte[] iv);
  486.  
  487.             public abstract void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len);
  488.         }
  489.  
  490.         class NoCrypt extends Decryptor {
  491.  
  492.             @Override
  493.             public void doInit(byte[] key, byte[] iv) {
  494.                 //Do nothing
  495.             }
  496.  
  497.             @Override
  498.             public void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len) {
  499.                 System.arraycopy(in, inOffset, out, outOffset, len);
  500.             }
  501.         }
  502.  
  503.         class AESCBC128Decrypt extends Decryptor {
  504.  
  505.             Cipher c;
  506.  
  507.             @Override
  508.             public void doInit(byte[] key, byte[] iv) {
  509.                 try {
  510.                     SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
  511.                     c = Cipher.getInstance("AES/CBC/NoPadding");
  512.                     AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
  513.                     c.init(Cipher.DECRYPT_MODE, skeySpec, paramSpec);
  514.                 } catch (InvalidKeyException ex) {
  515.                     throw new IllegalStateException(ex);
  516.                 } catch (InvalidAlgorithmParameterException ex) {
  517.                     throw new IllegalStateException(ex);
  518.                 } catch (NoSuchAlgorithmException ex) {
  519.                     throw new IllegalStateException(ex);
  520.                 } catch (NoSuchPaddingException ex) {
  521.                     throw new IllegalStateException(ex);
  522.                 }
  523.             }
  524.  
  525.             @Override
  526.             public void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len) {
  527.                 try {
  528.                     c.update(in, inOffset, len, out, outOffset);
  529.                 } catch (ShortBufferException ex) {
  530.                     throw new IllegalStateException(ex);
  531.                 }
  532.             }
  533.         }
  534.  
  535.         abstract class Hash {
  536.  
  537.             public abstract void setHashLen(int len);
  538.  
  539.             public abstract void doInit(byte[] key);
  540.  
  541.             public abstract void doUpdate(byte[] in, int inOffset, int len);
  542.  
  543.             public abstract boolean doFinal(byte[] expectedhash, int hashOffset);
  544.         }
  545.  
  546.         class HMAC extends Hash {
  547.  
  548.             private int hashLen;
  549.             private Mac mac;
  550.  
  551.             @Override
  552.             public void setHashLen(int len) {
  553.                 if (len == 0x10 || len == 0x14) {
  554.                     hashLen = len;
  555.                 } else {
  556.                     throw new IllegalArgumentException("Hash len must be 0x10 or 0x14");
  557.                 }
  558.             }
  559.  
  560.             @Override
  561.             public void doInit(byte[] key) {
  562.                 try {
  563.                     SecretKeySpec skeySpec = new SecretKeySpec(key, "HmacSHA1");
  564.                     mac = Mac.getInstance("HmacSHA1");
  565.                     mac.init(skeySpec);
  566.                 } catch (InvalidKeyException ex) {
  567.                     throw new IllegalStateException(ex);
  568.                 } catch (NoSuchAlgorithmException ex) {
  569.                     throw new IllegalStateException(ex);
  570.                 }
  571.             }
  572.  
  573.             @Override
  574.             public void doUpdate(byte[] in, int inOffset, int len) {
  575.                 mac.update(in, inOffset, len);
  576.             }
  577.  
  578.             @Override
  579.             public boolean doFinal(byte[] expectedhash, int hashOffset) {
  580.                 byte[] result = mac.doFinal();
  581.                 return (hashDebug || compareBytes(result, 0, expectedhash, hashOffset, hashLen));
  582.             }
  583.         }
  584.  
  585.         class CMAC extends Hash {
  586.  
  587.             int hashLen;
  588.             byte[] key;
  589.             byte[] K1;
  590.             byte[] K2;
  591.             byte[] nonProcessed;
  592.             byte[] previous;
  593.  
  594.             public CMAC() {
  595.                 hashLen = 0x10;
  596.             }
  597.  
  598.             @Override
  599.             public void setHashLen(int len) {
  600.                 if (len == 0x10) {
  601.                     hashLen = len;
  602.                 } else {
  603.                     throw new IllegalArgumentException("Hash len must be 0x10");
  604.                 }
  605.             }
  606.  
  607.             @Override
  608.             public void doInit(byte[] key) {
  609.                 this.key = key;
  610.                 K1 = new byte[0x10];
  611.                 K2 = new byte[0x10];
  612.                 calculateSubkey(key, K1, K2);
  613.                 nonProcessed = null;
  614.                 previous = new byte[0x10];
  615.             }
  616.  
  617.             private void calculateSubkey(byte[] key, byte[] K1, byte[] K2) {
  618.                 byte[] zero = new byte[0x10];
  619.                 byte[] L = new byte[0x10];
  620.                 ToolsImpl.aesecbEncrypt(key, zero, 0, L, 0, zero.length);
  621.                 BigInteger aux = new BigInteger(1, L);
  622.                 if ((L[0] & 0x80) != 0) {
  623.                     //Case MSB is set
  624.                     aux = aux.shiftLeft(1).xor(BigInteger.valueOf(0x87));
  625.                 } else {
  626.                     aux = aux.shiftLeft(1);
  627.                 }
  628.                 byte[] aux1 = aux.toByteArray();
  629.                 if (aux1.length >= 0x10) {
  630.                     System.arraycopy(aux1, aux1.length - 0x10, K1, 0, 0x10);
  631.                 } else {
  632.                     System.arraycopy(zero, 0, K1, 0, zero.length);
  633.                     System.arraycopy(aux1, 0, K1, 0x10 - aux1.length, aux1.length);
  634.                 }
  635.                 aux = new BigInteger(1, K1);
  636.                 if ((K1[0] & 0x80) != 0) {
  637.                     aux = aux.shiftLeft(1).xor(BigInteger.valueOf(0x87));
  638.                 } else {
  639.                     aux = aux.shiftLeft(1);
  640.                 }
  641.                 aux1 = aux.toByteArray();
  642.                 if (aux1.length >= 0x10) {
  643.                     System.arraycopy(aux1, aux1.length - 0x10, K2, 0, 0x10);
  644.                 } else {
  645.                     System.arraycopy(zero, 0, K2, 0, zero.length);
  646.                     System.arraycopy(aux1, 0, K2, 0x10 - aux1.length, aux1.length);
  647.                 }
  648.             }
  649.  
  650.             @Override
  651.             public void doUpdate(byte[] in, int inOffset, int len) {
  652.                 byte[] data;
  653.                 if (nonProcessed != null) {
  654.                     int totalLen = len + nonProcessed.length;
  655.                     data = new byte[totalLen];
  656.                     System.arraycopy(nonProcessed, 0, data, 0, nonProcessed.length);
  657.                     System.arraycopy(in, inOffset, data, nonProcessed.length, len);
  658.                 } else {
  659.                     data = new byte[len];
  660.                     System.arraycopy(in, inOffset, data, 0, len);
  661.                 }
  662.                 int count = 0;
  663.                 while (count < data.length - 0x10) {
  664.                     byte[] aux = new byte[0x10];
  665.                     System.arraycopy(data, count, aux, 0, aux.length);
  666.                     ToolsImpl.XOR(aux, aux, previous);
  667.                     ToolsImpl.aesecbEncrypt(key, aux, 0, previous, 0, aux.length);
  668.                     count += 0x10;
  669.                 }
  670.                 nonProcessed = new byte[data.length - count];
  671.                 System.arraycopy(data, count, nonProcessed, 0, nonProcessed.length);
  672.             }
  673.  
  674.             @Override
  675.             public boolean doFinal(byte[] expectedhash, int hashOffset) {
  676.                 byte[] aux = new byte[0x10];
  677.                 System.arraycopy(nonProcessed, 0, aux, 0, nonProcessed.length);
  678.                 if (nonProcessed.length == 0x10) {
  679.                     ToolsImpl.XOR(aux, aux, K1);
  680.                 } else {
  681.                     aux[nonProcessed.length] = (byte) 0x80;
  682.                     ToolsImpl.XOR(aux, aux, K2);
  683.                 }
  684.                 ToolsImpl.XOR(aux, aux, previous);
  685.                 byte[] calculatedhash = new byte[0x10];
  686.                 ToolsImpl.aesecbEncrypt(key, aux, 0, calculatedhash, 0, aux.length);
  687.                 return (hashDebug || compareBytes(expectedhash, hashOffset, calculatedhash, 0, hashLen));
  688.             }
  689.         }
  690.     }
  691. }
  692.  
clone this paste RAW Paste Data