mirror of
https://github.com/codestation/mhtools
synced 2024-12-04 17:09:20 +00:00
Added savedata encryption (untested) and KIRK decryption/encryption
(untested too xD), also added game 16-byte gamekey.
This commit is contained in:
parent
83a07c8296
commit
2ef55f18a9
12 changed files with 2397 additions and 16 deletions
|
@ -2,5 +2,6 @@
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
|
<classpathentry kind="lib" path="bcprov-jdk16-146.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
9
README
9
README
|
@ -1,4 +1,4 @@
|
||||||
MH string tables unpacker/rebuilder/encrypter/decrypter.
|
MH utilities - codestation
|
||||||
|
|
||||||
encoder/decoder number meaning to use in binary files:
|
encoder/decoder number meaning to use in binary files:
|
||||||
|
|
||||||
|
@ -49,4 +49,9 @@ To pack images into a TMH container
|
||||||
java -jar mhtrans.jar --rebuild /home/user/unpacked_countainer_dir 5
|
java -jar mhtrans.jar --rebuild /home/user/unpacked_countainer_dir 5
|
||||||
|
|
||||||
To unpack a .pak file
|
To unpack a .pak file
|
||||||
java -jar mhtrans.jar --extract file.pak 6
|
java -jar mhtrans.jar --extract file.pak 6
|
||||||
|
|
||||||
|
The KIRK related functions are untested and requires the Bouncy Castle Crypto API
|
||||||
|
that can be downloaded from http://www.bouncycastle.org/download/bcprov-jdk16-146.jar
|
||||||
|
|
||||||
|
The PSP Crypto engine and AES wrapper comes from the Jpcsp project.
|
|
@ -1,4 +1,5 @@
|
||||||
Manifest-Version: 1.0
|
Manifest-Version: 1.0
|
||||||
Sealed: true
|
Sealed: true
|
||||||
Main-Class: base.Mhtrans
|
Main-Class: base.Mhtrans
|
||||||
|
Class-Path: bcprov-jdk16-146.jar
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.io.IOException;
|
||||||
|
|
||||||
import crypt.Decrypter;
|
import crypt.Decrypter;
|
||||||
import crypt.Encrypter;
|
import crypt.Encrypter;
|
||||||
|
import crypt.KirkCypher;
|
||||||
import crypt.QuestCypher;
|
import crypt.QuestCypher;
|
||||||
import crypt.SavedataCypher;
|
import crypt.SavedataCypher;
|
||||||
import dec.ExtractPluginA;
|
import dec.ExtractPluginA;
|
||||||
|
@ -113,7 +114,7 @@ public class Mhtrans {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("mhtrans v2.0 - MHP2G/MHFU/MHP3 xxxx.bin language table extractor/rebuilder");
|
System.out.println("mhtrans v2.0 - MHP2G/MHFU/MHP3 utils");
|
||||||
System.out.println();
|
System.out.println();
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
System.err.println("Usage: java -jar mhtrans.jar --extract <path to xxxx.bin> <decoder number>");
|
System.err.println("Usage: java -jar mhtrans.jar --extract <path to xxxx.bin> <decoder number>");
|
||||||
|
@ -127,9 +128,11 @@ public class Mhtrans {
|
||||||
System.err.println(" java -jar mhtrans.jar --create-patch <xxxx.bin.enc> [ ... <xxxx.bin.enc>] <output_file>");
|
System.err.println(" java -jar mhtrans.jar --create-patch <xxxx.bin.enc> [ ... <xxxx.bin.enc>] <output_file>");
|
||||||
System.err.println(" java -jar mhtrans.jar --decrypt-quest <mxxxxx.mib>");
|
System.err.println(" java -jar mhtrans.jar --decrypt-quest <mxxxxx.mib>");
|
||||||
System.err.println(" java -jar mhtrans.jar --encrypt-quest <mxxxxx.mib>");
|
System.err.println(" java -jar mhtrans.jar --encrypt-quest <mxxxxx.mib>");
|
||||||
//System.err.println(" java -jar mhtrans.jar --update-sha1 <mxxxxx.mib>");
|
System.err.println(" java -jar mhtrans.jar --update-sha1 <mxxxxx.mib>");
|
||||||
System.err.println(" java -jar mhtrans.jar --decrypt-save <xxxxx.bin>");
|
System.err.println(" java -jar mhtrans.jar --decrypt-save <xxxxx.bin>");
|
||||||
//System.err.println(" java -jar mhtrans.jar --encrypt-save <xxxxx.bin>");
|
System.err.println(" java -jar mhtrans.jar --encrypt-save <xxxxx.bin>");
|
||||||
|
System.err.println(" java -jar mhtrans.jar --decrypt-kirk <xxxxx.bin>");
|
||||||
|
System.err.println(" java -jar mhtrans.jar --encrypt-kirk <xxxxx.bin>");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
} else {
|
} else {
|
||||||
if (args[0].equals("--extract")) {
|
if (args[0].equals("--extract")) {
|
||||||
|
@ -185,12 +188,16 @@ public class Mhtrans {
|
||||||
new QuestCypher().encrypt(args[1]);
|
new QuestCypher().encrypt(args[1]);
|
||||||
} else if(args[0].equals("--decrypt-quest")) {
|
} else if(args[0].equals("--decrypt-quest")) {
|
||||||
new QuestCypher().decrypt(args[1]);
|
new QuestCypher().decrypt(args[1]);
|
||||||
//} else if(args[0].equals("--update-sha1")) {
|
} else if(args[0].equals("--update-sha1")) {
|
||||||
// new QuestCypher().update_sha1(args[1]);
|
new QuestCypher().update_sha1(args[1]);
|
||||||
//} else if(args[0].equals("--encrypt-save")) {
|
} else if(args[0].equals("--encrypt-save")) {
|
||||||
// new SavedataCypher().encrypt(args[1]);
|
new SavedataCypher().encrypt(args[1]);
|
||||||
} else if(args[0].equals("--decrypt-save")) {
|
} else if(args[0].equals("--decrypt-save")) {
|
||||||
new SavedataCypher().decrypt(args[1]);
|
new SavedataCypher().decrypt(args[1]);
|
||||||
|
} else if(args[0].equals("--encrypt-kirk")) {
|
||||||
|
new KirkCypher().encrypt(args[1]);
|
||||||
|
} else if(args[0].equals("--decrypt-kirk")) {
|
||||||
|
new KirkCypher().decrypt(args[1]);
|
||||||
} else {
|
} else {
|
||||||
System.err.println("Unknown parameter: " + args[0]);
|
System.err.println("Unknown parameter: " + args[0]);
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
|
|
64
src/crypt/KirkCypher.java
Normal file
64
src/crypt/KirkCypher.java
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/* MHTrans - KIRK savedata decrypter/encrypter
|
||||||
|
Copyright (C) 2011 Codestation
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package crypt;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
|
||||||
|
import jpcsp.crypto.CryptoEngine;
|
||||||
|
import keys.GameKeys;
|
||||||
|
|
||||||
|
public class KirkCypher implements GameKeys {
|
||||||
|
|
||||||
|
public void decrypt(String file) {
|
||||||
|
try {
|
||||||
|
RandomAccessFile fd = new RandomAccessFile(file, "rw");
|
||||||
|
byte byte_bt[] = new byte[(int)fd.length()];
|
||||||
|
fd.read(byte_bt);
|
||||||
|
fd.seek(0);
|
||||||
|
System.out.print("Decrypting savedata (KIRK engine)");
|
||||||
|
byte out[] = new CryptoEngine().DecryptSavedata(byte_bt, byte_bt.length, gamekey, 1);
|
||||||
|
fd.write(out);
|
||||||
|
fd.close();
|
||||||
|
System.out.println("Finished");
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encrypt(String file) {
|
||||||
|
try {
|
||||||
|
RandomAccessFile fd = new RandomAccessFile(file, "rw");
|
||||||
|
byte byte_bt[] = new byte[(int)fd.length()];
|
||||||
|
fd.read(byte_bt);
|
||||||
|
fd.seek(0);
|
||||||
|
System.out.println("Encrypting savedata (KIRK engine)");
|
||||||
|
byte out[] = new CryptoEngine().EncryptSavedata(byte_bt, byte_bt.length, gamekey, 0);
|
||||||
|
fd.write(out);
|
||||||
|
fd.close();
|
||||||
|
System.out.println("Finished");
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,12 +47,14 @@ public class QuestCypher implements QuestKeys {
|
||||||
ShortBuffer sb = bt.asShortBuffer();
|
ShortBuffer sb = bt.asShortBuffer();
|
||||||
short short_bt[] = new short[byte_bt.length/2];
|
short short_bt[] = new short[byte_bt.length/2];
|
||||||
sb.get(short_bt);
|
sb.get(short_bt);
|
||||||
|
System.out.println("Decrypting quest file");
|
||||||
decrypt_quest(short_bt);
|
decrypt_quest(short_bt);
|
||||||
sb.rewind();
|
sb.rewind();
|
||||||
sb.put(short_bt);
|
sb.put(short_bt);
|
||||||
FileOutputStream out = new FileOutputStream(filein + ".dec");
|
FileOutputStream out = new FileOutputStream(filein + ".dec");
|
||||||
out.write(byte_bt);
|
out.write(byte_bt);
|
||||||
out.close();
|
out.close();
|
||||||
|
System.out.println("finished.");
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -68,18 +70,21 @@ public class QuestCypher implements QuestKeys {
|
||||||
byte byte_bt[] = new byte[(int)fd.length()];
|
byte byte_bt[] = new byte[(int)fd.length()];
|
||||||
in.read(byte_bt);
|
in.read(byte_bt);
|
||||||
in.close();
|
in.close();
|
||||||
|
System.out.println("Updating sha1 hash");
|
||||||
update_sha1(byte_bt);
|
update_sha1(byte_bt);
|
||||||
ByteBuffer bt = ByteBuffer.wrap(byte_bt);
|
ByteBuffer bt = ByteBuffer.wrap(byte_bt);
|
||||||
bt.order(ByteOrder.LITTLE_ENDIAN);
|
bt.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
ShortBuffer sb = bt.asShortBuffer();
|
ShortBuffer sb = bt.asShortBuffer();
|
||||||
short short_bt[] = new short[byte_bt.length/2];
|
short short_bt[] = new short[byte_bt.length/2];
|
||||||
sb.get(short_bt);
|
sb.get(short_bt);
|
||||||
|
System.out.println("Encrypting quest file");
|
||||||
encrypt_quest(short_bt);
|
encrypt_quest(short_bt);
|
||||||
sb.rewind();
|
sb.rewind();
|
||||||
sb.put(short_bt);
|
sb.put(short_bt);
|
||||||
FileOutputStream out = new FileOutputStream(filein + ".enc");
|
FileOutputStream out = new FileOutputStream(filein + ".enc");
|
||||||
out.write(byte_bt);
|
out.write(byte_bt);
|
||||||
out.close();
|
out.close();
|
||||||
|
System.out.println("Finished");
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -122,10 +127,11 @@ public class QuestCypher implements QuestKeys {
|
||||||
byte byte_bt[] = new byte[(int)fd.length()];
|
byte byte_bt[] = new byte[(int)fd.length()];
|
||||||
fd.read(byte_bt);
|
fd.read(byte_bt);
|
||||||
fd.seek(0);
|
fd.seek(0);
|
||||||
|
System.out.println("Updating sha1 hash");
|
||||||
update_sha1(byte_bt);
|
update_sha1(byte_bt);
|
||||||
fd.write(byte_bt);
|
fd.write(byte_bt);
|
||||||
fd.close();
|
fd.close();
|
||||||
|
System.out.println("Finished");
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -135,14 +141,14 @@ public class QuestCypher implements QuestKeys {
|
||||||
|
|
||||||
private void update_sha1(byte buf[]) {
|
private void update_sha1(byte buf[]) {
|
||||||
int len = ((buf[8+3] << 24) & 0xFFFFFFFF) + ((buf[8+2] << 16) & 0xFFFFFF) + ((buf[8+1] << 8) & 0xFFFF) + ((buf[8+0] << 0) & 0xFF);
|
int len = ((buf[8+3] << 24) & 0xFFFFFFFF) + ((buf[8+2] << 16) & 0xFFFFFF) + ((buf[8+1] << 8) & 0xFFFF) + ((buf[8+0] << 0) & 0xFF);
|
||||||
len += 0x10;
|
len += quest_sha1_key.length();
|
||||||
byte buffer[] = new byte[len];
|
byte buffer[] = new byte[len];
|
||||||
System.arraycopy(buf, 0x20, buffer, 0, len-0x10);
|
System.arraycopy(buf, 0x20, buffer, 0, len-quest_sha1_key.length());
|
||||||
System.arraycopy(quest_sha1_key.getBytes(), 0, buffer, len-0x10, 0x10);
|
System.arraycopy(quest_sha1_key.getBytes(), 0, buffer, len-quest_sha1_key.length(), quest_sha1_key.length());
|
||||||
try {
|
try {
|
||||||
MessageDigest md = MessageDigest.getInstance("sha-1");
|
MessageDigest md = MessageDigest.getInstance("sha-1");
|
||||||
byte digest[] = md.digest(buffer);
|
byte digest[] = md.digest(buffer);
|
||||||
System.arraycopy(digest, 0, buf, 12, 0x10);
|
System.arraycopy(digest, 0, buf, 12, digest.length);
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ package crypt;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import keys.SavedataKeys;
|
import keys.SavedataKeys;
|
||||||
|
|
||||||
|
@ -50,21 +52,22 @@ public class SavedataCypher extends DecryptUtils implements SavedataKeys {
|
||||||
return mod_b;
|
return mod_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean decrypt(String file) {
|
public void decrypt(String file) {
|
||||||
try {
|
try {
|
||||||
RandomAccessFile fd = new RandomAccessFile(file, "rw");
|
RandomAccessFile fd = new RandomAccessFile(file, "rw");
|
||||||
byte byte_bt[] = new byte[(int)fd.length()];
|
byte byte_bt[] = new byte[(int)fd.length()];
|
||||||
fd.read(byte_bt);
|
fd.read(byte_bt);
|
||||||
fd.seek(0);
|
fd.seek(0);
|
||||||
|
System.out.println("Decrypting savedata");
|
||||||
decrypt_buffer(byte_bt);
|
decrypt_buffer(byte_bt);
|
||||||
fd.write(byte_bt);
|
fd.write(byte_bt);
|
||||||
fd.close();
|
fd.close();
|
||||||
|
System.out.println("Finished");
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decrypt_buffer(byte buffer[]) {
|
private void decrypt_buffer(byte buffer[]) {
|
||||||
|
@ -84,4 +87,58 @@ public class SavedataCypher extends DecryptUtils implements SavedataKeys {
|
||||||
set_table_data(buffer, i);
|
set_table_data(buffer, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void encrypt(String file) {
|
||||||
|
try {
|
||||||
|
RandomAccessFile fd = new RandomAccessFile(file, "rw");
|
||||||
|
byte byte_bt[] = new byte[(int)fd.length()];
|
||||||
|
fd.read(byte_bt);
|
||||||
|
fd.seek(0);
|
||||||
|
System.out.println("Updating sha1 hash");
|
||||||
|
update_sha1(byte_bt);
|
||||||
|
System.out.println("Encrypting savedata");
|
||||||
|
encrypt_buffer(byte_bt);
|
||||||
|
fd.write(byte_bt);
|
||||||
|
fd.close();
|
||||||
|
System.out.println("Finished");
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_sha1(byte buf[]) {
|
||||||
|
int len = buf.length - 24;
|
||||||
|
len += savedata_sha1_key.length();
|
||||||
|
byte buffer[] = new byte[len];
|
||||||
|
System.arraycopy(buf, 0, buffer, 0, len-savedata_sha1_key.length());
|
||||||
|
System.arraycopy(savedata_sha1_key.getBytes(), 0, buffer, len-savedata_sha1_key.length(), savedata_sha1_key.length());
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("sha-1");
|
||||||
|
byte digest[] = md.digest(buffer);
|
||||||
|
System.arraycopy(digest, 0, buf, buf.length - 24, 0x10);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void encrypt_buffer(byte buffer[]) {
|
||||||
|
byte encrypt_table[] = new byte[256];
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
encrypt_table[decrypt_table[i] & 0xFF] = (byte) i;
|
||||||
|
}
|
||||||
|
int len = buffer.length - 4;
|
||||||
|
byte seed[] = new byte[4];
|
||||||
|
System.arraycopy(buffer, len, seed, 0, 4);
|
||||||
|
long alpha = get_table_value(seed, 0);
|
||||||
|
initSeed(alpha);
|
||||||
|
for (int i = 0; i < len; i += 4) {
|
||||||
|
long gamma = get_table_value(buffer, i);
|
||||||
|
long beta = getBeta();
|
||||||
|
alpha = beta ^ gamma;
|
||||||
|
set_table_value(buffer, i, alpha);
|
||||||
|
get_table_value(encrypt_table, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
250
src/jpcsp/crypto/AES128.java
Normal file
250
src/jpcsp/crypto/AES128.java
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
This file is part of jpcsp.
|
||||||
|
|
||||||
|
Jpcsp is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Jpcsp is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jpcsp.crypto;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.Security;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
|
||||||
|
public class AES128 {
|
||||||
|
|
||||||
|
private static byte[] const_Zero = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
private static byte[] const_Rb = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x87};
|
||||||
|
private byte[] contentKey;
|
||||||
|
private ByteArrayOutputStream barros;
|
||||||
|
|
||||||
|
public AES128() {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private encrypting method for CMAC (IV == 0).
|
||||||
|
private static byte[] encrypt(byte[] in, byte[] encKey) {
|
||||||
|
Key keySpec = new SecretKeySpec(encKey, "AES");
|
||||||
|
byte[] iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
IvParameterSpec ivec = new IvParameterSpec(iv);
|
||||||
|
try {
|
||||||
|
Cipher c = Cipher.getInstance("AES/CBC/NoPadding", "BC");
|
||||||
|
c.init(Cipher.ENCRYPT_MODE, keySpec, ivec);
|
||||||
|
ByteArrayInputStream inStream = new ByteArrayInputStream(in);
|
||||||
|
CipherInputStream cIn = new CipherInputStream(inStream, c);
|
||||||
|
DataInputStream dIn = new DataInputStream(cIn);
|
||||||
|
byte[] bytes = new byte[in.length];
|
||||||
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
|
bytes[i] = (byte) dIn.read();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public encrypting/decrypting methods (for CryptoEngine calls).
|
||||||
|
public byte[] encryptCBC(byte[] in, byte[] encKey, byte[] iv) {
|
||||||
|
Key keySpec = new SecretKeySpec(encKey, "AES");
|
||||||
|
IvParameterSpec ivec = new IvParameterSpec(iv);
|
||||||
|
try {
|
||||||
|
Cipher c = Cipher.getInstance("AES/CBC/NoPadding", "BC");
|
||||||
|
c.init(Cipher.ENCRYPT_MODE, keySpec, ivec);
|
||||||
|
ByteArrayInputStream inStream = new ByteArrayInputStream(in);
|
||||||
|
CipherInputStream cIn = new CipherInputStream(inStream, c);
|
||||||
|
DataInputStream dIn = new DataInputStream(cIn);
|
||||||
|
byte[] bytes = new byte[in.length];
|
||||||
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
|
bytes[i] = (byte) dIn.read();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] decryptCBC(byte[] in, byte[] decKey, byte[] iv) {
|
||||||
|
Key keySpec = new SecretKeySpec(decKey, "AES");
|
||||||
|
IvParameterSpec ivec = new IvParameterSpec(iv);
|
||||||
|
try {
|
||||||
|
Cipher c = Cipher.getInstance("AES/CBC/NoPadding", "BC");
|
||||||
|
c.init(Cipher.DECRYPT_MODE, keySpec, ivec);
|
||||||
|
ByteArrayInputStream inStream = new ByteArrayInputStream(in);
|
||||||
|
CipherInputStream cIn = new CipherInputStream(inStream, c);
|
||||||
|
DataInputStream dIn = new DataInputStream(cIn);
|
||||||
|
byte[] bytes = new byte[in.length];
|
||||||
|
for (int i = 0; i < in.length; i++) {
|
||||||
|
bytes[i] = (byte) dIn.read();
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doInitCMAC(byte[] contentKey) {
|
||||||
|
this.contentKey = contentKey;
|
||||||
|
barros = new ByteArrayOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doUpdateCMAC(byte[] input, int offset, int len) {
|
||||||
|
barros.write(input, offset, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doUpdateCMAC(byte[] input) {
|
||||||
|
barros.write(input, 0, input.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] doFinalCMAC() {
|
||||||
|
Object[] keys = generateSubKey(contentKey);
|
||||||
|
byte[] K1 = (byte[]) keys[0];
|
||||||
|
byte[] K2 = (byte[]) keys[1];
|
||||||
|
|
||||||
|
byte[] input = barros.toByteArray();
|
||||||
|
|
||||||
|
int numberOfRounds = (input.length + 15) / 16;
|
||||||
|
boolean lastBlockComplete;
|
||||||
|
|
||||||
|
if (numberOfRounds == 0) {
|
||||||
|
numberOfRounds = 1;
|
||||||
|
lastBlockComplete = false;
|
||||||
|
} else {
|
||||||
|
if (input.length % 16 == 0) {
|
||||||
|
lastBlockComplete = true;
|
||||||
|
} else {
|
||||||
|
lastBlockComplete = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] M_last;
|
||||||
|
int srcPos = 16 * (numberOfRounds - 1);
|
||||||
|
|
||||||
|
if (lastBlockComplete) {
|
||||||
|
byte[] partInput = new byte[16];
|
||||||
|
|
||||||
|
System.arraycopy(input, srcPos, partInput, 0, 16);
|
||||||
|
M_last = xor128(partInput, K1);
|
||||||
|
} else {
|
||||||
|
byte[] partInput = new byte[input.length % 16];
|
||||||
|
|
||||||
|
System.arraycopy(input, srcPos, partInput, 0, input.length % 16);
|
||||||
|
byte[] padded = doPaddingCMAC(partInput);
|
||||||
|
M_last = xor128(padded, K2);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] X = const_Zero.clone();
|
||||||
|
byte[] partInput = new byte[16];
|
||||||
|
byte[] Y;
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfRounds - 1; i++) {
|
||||||
|
srcPos = 16 * i;
|
||||||
|
System.arraycopy(input, srcPos, partInput, 0, 16);
|
||||||
|
|
||||||
|
Y = xor128(partInput, X); /* Y := Mi (+) X */
|
||||||
|
X = encrypt(Y, contentKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Y = xor128(X, M_last);
|
||||||
|
X = encrypt(Y, contentKey);
|
||||||
|
|
||||||
|
return X;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean doVerifyCMAC(byte[] verificationCMAC) {
|
||||||
|
byte[] cmac = doFinalCMAC();
|
||||||
|
|
||||||
|
if (verificationCMAC == null || verificationCMAC.length != cmac.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cmac.length; i++) {
|
||||||
|
if (cmac[i] != verificationCMAC[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] doPaddingCMAC(byte[] input) {
|
||||||
|
byte[] padded = new byte[16];
|
||||||
|
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
if (j < input.length) {
|
||||||
|
padded[j] = input[j];
|
||||||
|
} else if (j == input.length) {
|
||||||
|
padded[j] = (byte) 0x80;
|
||||||
|
} else {
|
||||||
|
padded[j] = (byte) 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return padded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object[] generateSubKey(byte[] key) {
|
||||||
|
byte[] L = encrypt(const_Zero, key);
|
||||||
|
|
||||||
|
byte[] K1 = null;
|
||||||
|
if ((L[0] & 0x80) == 0) { /* If MSB(L) = 0, then K1 = L << 1 */
|
||||||
|
K1 = doLeftShiftOneBit(L);
|
||||||
|
} else { /* Else K1 = ( L << 1 ) (+) Rb */
|
||||||
|
byte[] tmp = doLeftShiftOneBit(L);
|
||||||
|
K1 = xor128(tmp, const_Rb);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] K2 = null;
|
||||||
|
if ((K1[0] & 0x80) == 0) {
|
||||||
|
K2 = doLeftShiftOneBit(K1);
|
||||||
|
} else {
|
||||||
|
byte[] tmp = doLeftShiftOneBit(K1);
|
||||||
|
K2 = xor128(tmp, const_Rb);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object[] result = new Object[2];
|
||||||
|
result[0] = K1;
|
||||||
|
result[1] = K2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] xor128(byte[] input1, byte[] input2) {
|
||||||
|
byte[] output = new byte[input1.length];
|
||||||
|
for (int i = 0; i < input1.length; i++) {
|
||||||
|
output[i] = (byte) (((int) input1[i] ^ (int) input2[i]) & 0xFF);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] doLeftShiftOneBit(byte[] input) {
|
||||||
|
byte[] output = new byte[input.length];
|
||||||
|
byte overflow = 0;
|
||||||
|
|
||||||
|
for (int i = (input.length - 1); i >= 0; i--) {
|
||||||
|
output[i] = (byte) ((int) input[i] << 1 & 0xFF);
|
||||||
|
output[i] |= overflow;
|
||||||
|
overflow = ((input[i] & 0x80) != 0) ? (byte) 1 : (byte) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
1922
src/jpcsp/crypto/CryptoEngine.java
Normal file
1922
src/jpcsp/crypto/CryptoEngine.java
Normal file
File diff suppressed because it is too large
Load diff
39
src/jpcsp/crypto/SHA1.java
Normal file
39
src/jpcsp/crypto/SHA1.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
This file is part of jpcsp.
|
||||||
|
|
||||||
|
Jpcsp is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Jpcsp is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jpcsp.crypto;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
|
public class SHA1 {
|
||||||
|
|
||||||
|
public SHA1() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] doSHA1(byte[] bytes, int lenght) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||||
|
byte[] sha1Hash = new byte[40];
|
||||||
|
md.update(bytes, 0, lenght);
|
||||||
|
sha1Hash = md.digest();
|
||||||
|
return sha1Hash;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/keys/GameKeys.java
Normal file
27
src/keys/GameKeys.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/* MHTrans - MHP3 game keys
|
||||||
|
Copyright (C) 2011 Codestation
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package keys;
|
||||||
|
|
||||||
|
public interface GameKeys {
|
||||||
|
final byte gamekey[] = {
|
||||||
|
(byte)0xE3, (byte)0x05, (byte)0xCE, (byte)0xFA,
|
||||||
|
(byte)0xEB, (byte)0x46, (byte)0xB0, (byte)0x31,
|
||||||
|
(byte)0x85, (byte)0x9A, (byte)0x27, (byte)0x5B,
|
||||||
|
(byte)0xDF, (byte)0x32, (byte)0xD8, (byte)0x63
|
||||||
|
};
|
||||||
|
}
|
|
@ -75,4 +75,6 @@ public interface SavedataKeys {
|
||||||
final long seed_b = 0xDFA3;
|
final long seed_b = 0xDFA3;
|
||||||
final long mod_a = 0xFF8F;
|
final long mod_a = 0xFF8F;
|
||||||
final long mod_b = 0xFFEF;
|
final long mod_b = 0xFFEF;
|
||||||
|
|
||||||
|
final String savedata_sha1_key = "VQ(DOdIO9?X3!2GmW#XF";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue