View difference between Paste ID: E9ghYKj0 and SuAukd8B
SHOW: | | - or go back to the newest paste.
1-
package unself;
1+
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-
        EDATData data = getEDATData(raf);
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-
        result = checkHeader(rifkey, data, raf);
65+
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 int checkHeader(byte[] rifKey, EDATData data, RandomAccessFile in) throws IOException {
79+
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-
        //if ((data.getFlags() & DEBUG_FLAG) != 0) hashFlag |= 0x01000000;  //Debug check not implemented
91+
     * @param rifKey
92
     * @param data
93
     * @param npd
94
     * @param in
95
     * @return
96-
            System.out.println("Error verifying header. Is rifKey valid?");
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-
        int sectionSize = ((data.getFlags() & FLAG_COMPRESSED) != 0) ? 0x20 : 0x010;
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-
        npdPtr[0] = NPD.createNPD(npd);;
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-
        byte[] hash1 = ToolsImpl.CMAC128(npdrm_omac_key3, data1, 0, data1.length);
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-
        ToolsImpl.XOR(xoredKey, klicensee, npdrm_omac_key2);
203+
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-
            ToolsImpl.XOR(result, npd.getDevHash(), SDATKEY);
218+
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-
    
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-
                System.arraycopy(metadata, 0, expectedHash, 0, 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[] blockKey = calculateBlockKey(i, npd.getDevHash());
265+
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-
    private byte[] calculateBlockKey(int blk, byte[] baseKey) {
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-
                    ToolsImpl.aescbcDecrypt(EDATKEY, EDATIV, key, 0, calculatedKey, 0, calculatedKey.length);
356+
357
        return result;
358
    }
359
360
    class AppLoader {
361-
                    System.arraycopy(EDATKEY, 0, calculatedKey, 0, calculatedKey.length);
361+
362-
                    System.arraycopy(EDATIV, 0, calculatedIV, 0, calculatedIV.length);
362+
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-
                    ToolsImpl.aescbcDecrypt(EDATKEY, EDATIV, hash, 0, calculatedHash, 0, calculatedHash.length);
379+
380
            setHash(hashFlag);
381
            System.out.println("ERK:  " + ConversionUtils.getHexString(calculatedKey));
382
            System.out.println("IV:   " + ConversionUtils.getHexString(calculatedIV));
383-
                    System.arraycopy(EDATHASH, 0, calculatedHash, 0, calculatedHash.length);
383+
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-
                return compareBytes(result, 0, expectedhash, hashOffset, hashLen);
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-
                return compareBytes(expectedhash, hashOffset, calculatedhash, 0, hashLen);
640+
641
                aux1 = aux.toByteArray();
642
                if (aux1.length >= 0x10) {
643
                    System.arraycopy(aux1, aux1.length - 0x10, K2, 0, 0x10);
644-
}
644+
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