Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.io.RandomAccessFile;
- import java.io.UnsupportedEncodingException;
- import java.math.BigInteger;
- import java.security.InvalidAlgorithmParameterException;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import java.security.spec.AlgorithmParameterSpec;
- import javax.crypto.Cipher;
- import javax.crypto.Mac;
- import javax.crypto.NoSuchPaddingException;
- import javax.crypto.ShortBufferException;
- import javax.crypto.spec.IvParameterSpec;
- import javax.crypto.spec.SecretKeySpec;
- public class EDAT {
- public static final int STATUS_ERROR_INPUTFILE_IO = -100;
- public static final int STATUS_ERROR_HASHTITLEIDNAME = -1;
- public static final int STATUS_ERROR_HASHDEVKLIC = -2;
- public static final int STATUS_ERROR_MISSINGKEY = -3;
- public static final int STATUS_ERROR_HEADERCHECK = -4;
- public static final int STATUS_ERROR_DECRYPTING = -5;
- public static final int STATUS_ERROR_INCORRECT_FLAGS = -6;
- public static final int STATUS_ERROR_INCORRECT_VERSION = -7;
- public static final int STATUS_OK = 0;
- public static final long FLAG_COMPRESSED = 0x00000001L;
- public static final long FLAG_0x02 = 0x00000002L;
- public static final long FLAG_KEYENCRYPTED = 0x00000008L;
- public static final long FLAG_0x10 = 0x00000010L;
- public static final long FLAG_0x20 = 0x00000020L;
- public static final long FLAG_SDAT = 0x01000000L;
- public static final long FLAG_DEBUG = 0x80000000L;
- /**
- *
- * This function reads given file and decrypts it
- *
- * @param inFile EDAT file to decrypt
- * @param outFile Path to store the decrypted data
- * @param devKLic Authentication key. Also used for decryption on free content
- * @param keyFromRif Key obtained after decrypting rif60
- * @return Result of the operation
- */
- public int decryptFile(String inFile, String outFile, byte[] devKLic, byte[] keyFromRif) throws FileNotFoundException, IOException {
- File fin = new File(inFile);
- RandomAccessFile raf = null;
- raf = new RandomAccessFile(fin, "r");
- NPD[] ptr = new NPD[1]; //Ptr to Ptr
- int result = validateNPD(fin.getName(), devKLic, ptr, raf); //Validate NPD hashes
- if (result < 0) {
- return result;
- }
- NPD npd = ptr[0];
- EDATData data = getEDATData(raf); //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) {
- System.out.println("ERROR: Key for decryption is missing");
- return STATUS_ERROR_MISSINGKEY;
- } else {
- System.out.println("DECRYPTION KEY: " + ConversionUtils.getHexString(rifkey));
- }
- result = checkHeader(rifkey, data, npd, raf);
- if (result < 0) {
- return result;
- }
- RandomAccessFile out = new RandomAccessFile(outFile, "rw");
- result = decryptData(raf, out, npd, data, rifkey);
- if (result < 0) {
- return result;
- }
- raf.close();
- return STATUS_OK;
- }
- private static final int HEADER_MAX_BLOCKSIZE = 0x3C00;
- /**
- *
- * 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, RandomAccessFile in) throws IOException {
- in.seek(0);
- byte[] header = new byte[0xA0];
- byte[] out = new byte[0xA0];
- byte[] expectedHash = new byte[0x10];
- //Version check
- System.out.println("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;
- in.readFully(header);
- in.readFully(expectedHash);
- System.out.println("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
- boolean result = a.doAll(hashFlag, 0x00000001, header, 0, out, 0, header.length, new byte[0x10], new byte[0x10], rifKey, expectedHash, 0);
- if (!result) {
- System.out.println("Error verifying header. Is rifKey valid?.");
- return STATUS_ERROR_HEADERCHECK;
- }
- if ((data.getFlags() & FLAG_0x20) == 0) {
- System.out.println("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;
- //Determine the metadatasection total len
- int numBlocks = (int) ((data.getFileLen().intValue() + 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;
- in.seek(baseOffset + readed);
- byte[] content = new byte[lenToRead];
- out = new byte[lenToRead];
- in.readFully(content);
- a.doUpdate(content, 0, out, 0, lenToRead);
- readed += lenToRead;
- remaining -= lenToRead;
- }
- result = a.doFinal(header, 0x90);
- if (!result) {
- System.out.println("Error verifying metadatasection. Data tampered");
- return STATUS_ERROR_HEADERCHECK;
- }
- } else {
- System.out.println("Flag 0x20 detected. Ignore metadatasection hash");
- }
- return STATUS_OK;
- }
- private byte[] decryptMetadataSection(byte[] metadata) {
- byte[] result = new byte[0x10];
- result[0x00] = (byte) (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
- result[0x01] = (byte) (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
- result[0x02] = (byte) (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
- result[0x03] = (byte) (metadata[0xF] ^ metadata[0xB] ^ metadata[0x13]);
- result[0x04] = (byte) (metadata[0x4] ^ metadata[0x8] ^ metadata[0x14]);
- result[0x05] = (byte) (metadata[0x5] ^ metadata[0x9] ^ metadata[0x15]);
- result[0x06] = (byte) (metadata[0x6] ^ metadata[0xA] ^ metadata[0x16]);
- result[0x07] = (byte) (metadata[0x7] ^ metadata[0xB] ^ metadata[0x17]);
- result[0x08] = (byte) (metadata[0xC] ^ metadata[0x0] ^ metadata[0x18]);
- result[0x09] = (byte) (metadata[0xD] ^ metadata[0x1] ^ metadata[0x19]);
- result[0x0A] = (byte) (metadata[0xE] ^ metadata[0x2] ^ metadata[0x1A]);
- result[0x0B] = (byte) (metadata[0xF] ^ metadata[0x3] ^ metadata[0x1B]);
- result[0x0C] = (byte) (metadata[0x4] ^ metadata[0x0] ^ metadata[0x1C]);
- result[0x0D] = (byte) (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
- result[0x0E] = (byte) (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
- result[0x0F] = (byte) (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
- return result;
- }
- private EDATData getEDATData(RandomAccessFile in) throws IOException {
- in.seek(0x80);
- byte[] data = new byte[0x10];
- in.readFully(data);
- return EDATData.createEDATData(data);
- }
- private boolean compareBytes(byte[] value1, int offset1, byte[] value2, int offset2, int len) {
- boolean result = true;
- for (int i = 0; i < len; i++) {
- if (value1[i + offset1] != value2[i + offset2]) {
- result = false;
- break;
- }
- }
- return result;
- }
- private int validateNPD(String filename, byte[] devKLic, NPD[] npdPtr, RandomAccessFile in) throws IOException {
- in.seek(0);
- byte[] npd = new byte[0x80];
- in.readFully(npd);
- byte[] extraData = new byte[0x04];
- in.readFully(extraData);
- long flag = ConversionUtils.be32(extraData, 0);
- if ((flag & FLAG_SDAT) != 0) {
- System.out.println("INFO: SDAT detected. NPD header is not validated");
- } else if (!checkNPDHash1(filename, npd)) {
- return STATUS_ERROR_HASHTITLEIDNAME;
- } else if (devKLic == null) {
- System.out.println("WARNING: Can not validate devklic header");
- } else if (!checkNPDHash2(devKLic, npd)) {
- return STATUS_ERROR_HASHDEVKLIC;
- }
- npdPtr[0] = NPD.createNPD(npd);
- return STATUS_OK;
- }
- private boolean checkNPDHash1(String filename, byte[] npd) throws UnsupportedEncodingException {
- byte[] fileBytes = filename.getBytes("US-ASCII");
- byte data1[] = new byte[0x30 + fileBytes.length];
- System.arraycopy(npd, 0x10, data1, 0, 0x30);
- System.arraycopy(fileBytes, 0x00, data1, 0x30, fileBytes.length);
- byte[] hash1 = ToolsImpl.CMAC128(EDATKeys.npdrm_omac_key3, data1, 0, data1.length);
- boolean result1 = compareBytes(hash1, 0, npd, 0x50, 0x10);
- if (result1) {
- System.out.println("NPD hash 1 is valid (" + ConversionUtils.getHexString(hash1) + ")");
- }
- return result1;
- }
- private boolean checkNPDHash2(byte[] klicensee, byte[] npd) {
- byte[] xoredKey = new byte[0x10];
- ToolsImpl.XOR(xoredKey, klicensee, EDATKeys.npdrm_omac_key2);
- byte[] calculated = ToolsImpl.CMAC128(xoredKey, npd, 0, 0x60);
- boolean result2 = compareBytes(calculated, 0, npd, 0x60, 0x10);
- if (result2) {
- System.out.println("NPD hash 2 is valid (" + ConversionUtils.getHexString(calculated) + ")");
- }
- return result2;
- }
- 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 decryptData(RandomAccessFile in, RandomAccessFile out, NPD npd, EDATData data, byte[] rifkey) throws IOException {
- int numBlocks = (int) ((data.getFileLen().intValue() + 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++) {
- in.seek(baseOffset + i * metadataSectionSize);
- byte[] expectedHash = new byte[0x14];
- long offset;
- int len;
- int compressionEndBlock = 0;
- if ((data.getFlags() & FLAG_COMPRESSED) != 0) {
- byte[] metadata = new byte[0x20];
- in.readFully(metadata);
- byte[] result = decryptMetadataSection(metadata);
- offset = ConversionUtils.be64(result, 0).intValue(); // + offset (unknown)
- len = Long.valueOf(ConversionUtils.be32(result, 8)).intValue();
- compressionEndBlock = Long.valueOf(ConversionUtils.be32(result, 0xC)).intValue();
- System.arraycopy(metadata, 0, expectedHash, 0, 0x10);
- } else if ((data.getFlags() & FLAG_0x20) != 0) {
- byte[] metadata = new byte[0x20];
- in.seek(baseOffset + i * (metadataSectionSize + data.getBlockSize()));
- in.readFully(metadata);
- for (int j = 0; j<0x10;j++) {
- expectedHash[j] = (byte)(metadata[j] ^ metadata[j+0x10]);
- System.arraycopy(metadata, 0x10, expectedHash, 0x10, 0x4);
- }
- offset = baseOffset + i * (metadataSectionSize + data.getBlockSize()) + metadataSectionSize;
- len = Long.valueOf(data.getBlockSize()).intValue();
- if (i == numBlocks - 1) {
- len = data.getFileLen().mod(BigInteger.valueOf(data.getBlockSize())).intValue();
- }
- } else {
- in.readFully(expectedHash);
- offset = baseOffset + i * data.getBlockSize() + numBlocks * metadataSectionSize;
- len = Long.valueOf(data.getBlockSize()).intValue();
- if (i == numBlocks - 1) {
- len = data.getFileLen().mod(BigInteger.valueOf(data.getBlockSize())).intValue();
- }
- }
- int realLen = len;
- len = (len + 0xF) & 0xFFFFFFF0;
- System.out.printf("Offset: %016X, len: %08X, realLen: %08X, endCompress: %d\r\n", offset, len, realLen,compressionEndBlock);
- in.seek(offset);
- byte[] encryptedData = new byte[len];
- byte[] decryptedData = new byte[len];
- in.readFully(encryptedData);
- 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 {
- System.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();
- boolean result = a.doAll(hashFlag, cryptoFlag, encryptedData, 0, decryptedData, 0, encryptedData.length, key, iv, hash, expectedHash, 0);
- if (!result) {
- System.out.println("Error decrypting block " + i);
- 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 {
- out.write(decryptedData, 0, realLen);
- }
- }
- return STATUS_OK;
- }
- private byte[] calculateBlockKey(int blk, NPD npd) {
- byte[] baseKey = (npd.getVersion() <= 1)?(new byte[0x10]):npd.getDevHash();
- byte[] result = new byte[0x10];
- System.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;
- }
- class AppLoader {
- private Decryptor dec;
- private Hash hash;
- private boolean hashDebug = false;
- private boolean cryptoDebug = false; //NOT USED??
- 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) {
- doInit(hashFlag, cryptoFlag, key, iv, hash);
- doUpdate(in, inOffset, out, outOffset, len);
- return doFinal(expectedHash, hashOffset);
- }
- public void doInit(int hashFlag, int cryptoFlag, byte[] key, byte[] iv, byte[] hashKey) {
- byte[] calculatedKey = new byte[key.length];
- byte[] calculatedIV = new byte[iv.length];
- byte[] calculatedHash = new byte[hashKey.length];
- getCryptoKeys(cryptoFlag, calculatedKey, calculatedIV, key, iv);
- getHashKeys(hashFlag, calculatedHash, hashKey);
- setDecryptor(cryptoFlag);
- setHash(hashFlag);
- System.out.println("ERK: " + ConversionUtils.getHexString(calculatedKey));
- System.out.println("IV: " + ConversionUtils.getHexString(calculatedIV));
- System.out.println("HASH: " + ConversionUtils.getHexString(calculatedHash));
- dec.doInit(calculatedKey, calculatedIV);
- hash.doInit(calculatedHash);
- }
- public void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len) {
- hash.doUpdate(in, inOffset, len);
- dec.doUpdate(in, inOffset, out, outOffset, len);
- }
- public boolean doFinal(byte[] expectedhash, int hashOffset) {
- return hash.doFinal(expectedhash, hashOffset);
- }
- private void getCryptoKeys(int cryptoFlag, byte[] calculatedKey, byte[] calculatedIV, byte[] key, byte[] iv) {
- int mode = cryptoFlag & 0xF0000000;
- switch (mode) {
- case 0x10000000:
- ToolsImpl.aescbcDecrypt(EDATKeys.EDATKEY, EDATKeys.EDATIV, key, 0, calculatedKey, 0, calculatedKey.length);
- System.arraycopy(iv, 0, calculatedIV, 0, calculatedIV.length);
- System.out.println("MODE: Encrypted ERK");
- break;
- case 0x20000000:
- System.arraycopy(EDATKeys.EDATKEY, 0, calculatedKey, 0, calculatedKey.length);
- System.arraycopy(EDATKeys.EDATIV, 0, calculatedIV, 0, calculatedIV.length);
- System.out.println("MODE: Default ERK");
- break;
- case 0x00000000:
- System.arraycopy(key, 0, calculatedKey, 0, calculatedKey.length);
- System.arraycopy(iv, 0, calculatedIV, 0, calculatedIV.length);
- System.out.println("MODE: Unencrypted ERK");
- break;
- default:
- throw new IllegalStateException("Crypto mode is not valid: Undefined keys calculator");
- }
- }
- private void getHashKeys(int hashFlag, byte[] calculatedHash, byte[] hash) {
- int mode = hashFlag & 0xF0000000;
- switch (mode) {
- case 0x10000000:
- ToolsImpl.aescbcDecrypt(EDATKeys.EDATKEY, EDATKeys.EDATIV, hash, 0, calculatedHash, 0, calculatedHash.length);
- System.out.println("MODE: Encrypted HASHKEY");
- break;
- case 0x20000000:
- System.arraycopy(EDATKeys.EDATHASH, 0, calculatedHash, 0, calculatedHash.length);
- System.out.println("MODE: Default HASHKEY");
- break;
- case 0x00000000:
- System.arraycopy(hash, 0, calculatedHash, 0, calculatedHash.length);
- System.out.println("MODE: Unencrypted HASHKEY");
- break;
- default:
- throw new IllegalStateException("Hash mode is not valid: Undefined keys calculator");
- }
- }
- private void setDecryptor(int cryptoFlag) {
- int aux = cryptoFlag & 0xFF;
- switch (aux) {
- case 0x01:
- dec = new NoCrypt();
- System.out.println("MODE: Decryption Algorithm NONE");
- break;
- case 0x02:
- dec = new AESCBC128Decrypt();
- System.out.println("MODE: Decryption Algorithm AESCBC128");
- break;
- default:
- throw new IllegalStateException("Crypto mode is not valid: Undefined decryptor");
- }
- if ((cryptoFlag & 0x0F000000) != 0) cryptoDebug = true;
- }
- private void setHash(int hashFlag) {
- int aux = hashFlag & 0xFF;
- switch (aux) {
- case 0x01:
- hash = new HMAC();
- hash.setHashLen(0x14);
- System.out.println("MODE: Hash HMAC Len 0x14");
- break;
- case 0x02:
- hash = new CMAC();
- hash.setHashLen(0x10);
- System.out.println("MODE: Hash CMAC Len 0x10");
- break;
- case 0x04:
- hash = new HMAC();
- hash.setHashLen(0x10);
- System.out.println("MODE: Hash HMAC Len 0x10");
- break;
- default:
- throw new IllegalStateException("Hash mode is not valid: Undefined hash algorithm");
- }
- if ((hashFlag & 0x0F000000) != 0) hashDebug = true;
- }
- abstract class Decryptor {
- public abstract void doInit(byte[] key, byte[] iv);
- public abstract void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len);
- }
- class NoCrypt extends Decryptor {
- @Override
- public void doInit(byte[] key, byte[] iv) {
- //Do nothing
- }
- @Override
- public void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len) {
- System.arraycopy(in, inOffset, out, outOffset, len);
- }
- }
- class AESCBC128Decrypt extends Decryptor {
- Cipher c;
- @Override
- public void doInit(byte[] key, byte[] iv) {
- try {
- SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
- c = Cipher.getInstance("AES/CBC/NoPadding");
- AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
- c.init(Cipher.DECRYPT_MODE, skeySpec, paramSpec);
- } catch (InvalidKeyException ex) {
- throw new IllegalStateException(ex);
- } catch (InvalidAlgorithmParameterException ex) {
- throw new IllegalStateException(ex);
- } catch (NoSuchAlgorithmException ex) {
- throw new IllegalStateException(ex);
- } catch (NoSuchPaddingException ex) {
- throw new IllegalStateException(ex);
- }
- }
- @Override
- public void doUpdate(byte[] in, int inOffset, byte[] out, int outOffset, int len) {
- try {
- c.update(in, inOffset, len, out, outOffset);
- } catch (ShortBufferException ex) {
- throw new IllegalStateException(ex);
- }
- }
- }
- abstract class Hash {
- public abstract void setHashLen(int len);
- public abstract void doInit(byte[] key);
- public abstract void doUpdate(byte[] in, int inOffset, int len);
- public abstract boolean doFinal(byte[] expectedhash, int hashOffset);
- }
- class HMAC extends Hash {
- private int hashLen;
- private Mac mac;
- @Override
- public void setHashLen(int len) {
- if (len == 0x10 || len == 0x14) {
- hashLen = len;
- } else {
- throw new IllegalArgumentException("Hash len must be 0x10 or 0x14");
- }
- }
- @Override
- public void doInit(byte[] key) {
- try {
- SecretKeySpec skeySpec = new SecretKeySpec(key, "HmacSHA1");
- mac = Mac.getInstance("HmacSHA1");
- mac.init(skeySpec);
- } catch (InvalidKeyException ex) {
- throw new IllegalStateException(ex);
- } catch (NoSuchAlgorithmException ex) {
- throw new IllegalStateException(ex);
- }
- }
- @Override
- public void doUpdate(byte[] in, int inOffset, int len) {
- mac.update(in, inOffset, len);
- }
- @Override
- public boolean doFinal(byte[] expectedhash, int hashOffset) {
- byte[] result = mac.doFinal();
- if (hashDebug || compareBytes(expectedhash, hashOffset, result, 0, hashLen)) return true;
- else {
- System.out.printf("Error on hash. Expected %s. Obtained %s\r\n",ConversionUtils.getHexString(expectedhash), ConversionUtils.getHexString(result));
- return false;
- }
- }
- }
- class CMAC extends Hash {
- int hashLen;
- byte[] key;
- byte[] K1;
- byte[] K2;
- byte[] nonProcessed;
- byte[] previous;
- public CMAC() {
- hashLen = 0x10;
- }
- @Override
- public void setHashLen(int len) {
- if (len == 0x10) {
- hashLen = len;
- } else {
- throw new IllegalArgumentException("Hash len must be 0x10");
- }
- }
- @Override
- public void doInit(byte[] key) {
- this.key = key;
- K1 = new byte[0x10];
- K2 = new byte[0x10];
- calculateSubkey(key, K1, K2);
- nonProcessed = null;
- previous = new byte[0x10];
- }
- private void calculateSubkey(byte[] key, byte[] K1, byte[] K2) {
- byte[] zero = new byte[0x10];
- byte[] L = new byte[0x10];
- ToolsImpl.aesecbEncrypt(key, zero, 0, L, 0, zero.length);
- BigInteger aux = new BigInteger(1, L);
- if ((L[0] & 0x80) != 0) {
- //Case MSB is set
- aux = aux.shiftLeft(1).xor(BigInteger.valueOf(0x87));
- } else {
- aux = aux.shiftLeft(1);
- }
- byte[] aux1 = aux.toByteArray();
- if (aux1.length >= 0x10) {
- System.arraycopy(aux1, aux1.length - 0x10, K1, 0, 0x10);
- } else {
- System.arraycopy(zero, 0, K1, 0, zero.length);
- System.arraycopy(aux1, 0, K1, 0x10 - aux1.length, aux1.length);
- }
- aux = new BigInteger(1, K1);
- if ((K1[0] & 0x80) != 0) {
- aux = aux.shiftLeft(1).xor(BigInteger.valueOf(0x87));
- } else {
- aux = aux.shiftLeft(1);
- }
- aux1 = aux.toByteArray();
- if (aux1.length >= 0x10) {
- System.arraycopy(aux1, aux1.length - 0x10, K2, 0, 0x10);
- } else {
- System.arraycopy(zero, 0, K2, 0, zero.length);
- System.arraycopy(aux1, 0, K2, 0x10 - aux1.length, aux1.length);
- }
- }
- @Override
- public void doUpdate(byte[] in, int inOffset, int len) {
- byte[] data;
- if (nonProcessed != null) {
- int totalLen = len + nonProcessed.length;
- data = new byte[totalLen];
- System.arraycopy(nonProcessed, 0, data, 0, nonProcessed.length);
- System.arraycopy(in, inOffset, data, nonProcessed.length, len);
- } else {
- data = new byte[len];
- System.arraycopy(in, inOffset, data, 0, len);
- }
- int count = 0;
- while (count < data.length - 0x10) {
- byte[] aux = new byte[0x10];
- System.arraycopy(data, count, aux, 0, aux.length);
- ToolsImpl.XOR(aux, aux, previous);
- ToolsImpl.aesecbEncrypt(key, aux, 0, previous, 0, aux.length);
- count += 0x10;
- }
- nonProcessed = new byte[data.length - count];
- System.arraycopy(data, count, nonProcessed, 0, nonProcessed.length);
- }
- @Override
- public boolean doFinal(byte[] expectedhash, int hashOffset) {
- byte[] aux = new byte[0x10];
- System.arraycopy(nonProcessed, 0, aux, 0, nonProcessed.length);
- if (nonProcessed.length == 0x10) {
- ToolsImpl.XOR(aux, aux, K1);
- } else {
- aux[nonProcessed.length] = (byte) 0x80;
- ToolsImpl.XOR(aux, aux, K2);
- }
- ToolsImpl.XOR(aux, aux, previous);
- byte[] calculatedhash = new byte[0x10];
- ToolsImpl.aesecbEncrypt(key, aux, 0, calculatedhash, 0, aux.length);
- if (hashDebug || compareBytes(expectedhash, hashOffset, calculatedhash, 0, hashLen)) return true;
- else {
- System.out.printf("Error on hash. Expected %s. Obtained %s\r\n",ConversionUtils.getHexString(expectedhash), ConversionUtils.getHexString(calculatedhash));
- return false;
- }
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement