package net.jselby.escapists.util;


import org.apache.commons.io.IOUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Decrypts Escapists map and resource files, using their default key.
 * <br>
 * <br>
 * Solution idea from - http://stackoverflow.com/questions/11422497/whats-the-difference-between-blowfish-and-blowfish-compat
 * <br>
 * Removing the null bytes at the end of the decrypted content is fine since we are dealing with strings
 * http://stackoverflow.com/questions/5672012/mcrypt-encrypt-adding-s-bunch-of-00-to-end-of-string
 *
 * @author Zirow (code) - Major kudos!
 **/
public final class BlowfishCompatEncryption {
    private BlowfishCompatEncryption() {}

    /**
     * The default encryption key used by the Escapists.
     *
     * This is a magic value!
     */
    private static final String ENCRYPTION_KEY = "mothking";

    /**
     * Decrypts a Blowfish encrypted file using the built-in key.
     *
     * @param t The file to read and decrypt
     * @return The byte contents of this file decrypted
     **/
    public static byte[] decrypt(File t) throws IOException {
        // get the data
        InputStream in = new FileInputStream(t);
        byte[] data = IOUtils.toByteArray(in);
        in.close();

        return decryptBytes(data, ENCRYPTION_KEY.getBytes());
    }

    /**
     * Decrypts a Blowfish encrypted array using the built-in key.
     *
     * @param t The file to read and decrypt
     * @return The byte contents of this file decrypted
     **/
    public static byte[] decrypt(byte[] t) throws IOException {
        return decryptBytes(t, ENCRYPTION_KEY.getBytes());
    }

    /**
     * Decrypts the specified byte array encrypted via Blowfish using
     * the specified key.
     *
     * @param data The data to decrypt
     * @param key The key to use for decryption
     * @return The decrypted bytes
     **/
    public static byte[] decryptBytes(byte[] data, byte[] key) throws IOException {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish");
            Cipher cipher;

            // get the cipher
            String cipherInstName = "Blowfish/ECB/NoPadding";
            cipher = Cipher.getInstance(cipherInstName);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);

            // reverse
            inplaceReverse(data);

            // decrypt
            byte[] decrypted = cipher.doFinal(data);
            inplaceReverse(decrypted);

            // Remove all null bytes at the end until a character is found
            ///*
            int end = decrypted.length - 1;

            while (decrypted[end] == 0)
                end--;

            byte[] result = new byte[end + 1];

            System.arraycopy(decrypted, 0, result, 0, end + 1);

            decrypted = result;
            //*/
            return decrypted;
        } catch (Exception e) {
            // Generic Exception handler to make sure things don't break upstream.
            throw new IOException(e);
        }
    }

    /**
     * Encrypts a Blowfish encrypted file using the built-in key.
     *
     * @param t The file to read and encrypt
     * @return The byte contents of this file encrypted
     **/
    public static byte[] encrypt(File t) throws IOException {
        // get the data
        InputStream in = new FileInputStream(t);
        byte[] data = IOUtils.toByteArray(in);
        in.close();

        return encryptBytes(data, ENCRYPTION_KEY.getBytes());
    }

    /**
     * Encrypts a Blowfish encrypted array using the built-in key.
     *
     * @param t The file to read and encrypt
     * @return The byte contents of this file encrypted
     **/
    public static byte[] encrypt(byte[] t) throws IOException {
        return encryptBytes(t, ENCRYPTION_KEY.getBytes());
    }


    /**
     * Encrypted the specified byte array via Blowfish using
     * the specified key.
     *
     * @param data The data to encrypt
     * @param key The key to use for encryption
     * @return The encrypted bytes
     **/
    public static byte[] encryptBytes(byte[] data, byte[] key) throws IOException {
        try {
            ///*
            if (data.length % 8 > 0) {
                // we need to add padding nulls
                int left = 8 - (data.length % 8);

                byte[] newData = new byte[data.length + left];

                System.arraycopy(data, 0, newData, 0, data.length);
                // no need to put 0's, it's byte's default value
                data = newData;
            }
            //*/

            SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish");
            Cipher cipher;

            // get the cipher
            // shouldn't i need to do some kind of padding here?
            String cipherInstName = "Blowfish/ECB/NoPadding";
            cipher = Cipher.getInstance(cipherInstName);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

            inplaceReverse(data);
            // decrypt
            byte[] encrypted = cipher.doFinal(data);
            inplaceReverse(encrypted);
            //inplaceReverse(data);
            return encrypted;

        } catch (Exception e) {
            // Generic Exception handler to make sure things don't break upstream.
            throw new IOException(e);
        }
    }

    /**
     * Swaps around bytes in the specified array to remain compatible with
     * Blowfish-Compat encryption.
     *
     * @param data The data to reverse.
     */
    private static void inplaceReverse(byte[] data) {
        byte a0, a1, a2, a3;
        for (int i = 0; i < data.length; i += 4) {
            a0 = data[i];
            a1 = data[i + 1];
            a2 = data[i + 2];
            a3 = data[i + 3];

            data[i] = a3;
            data[i + 1] = a2;
            data[i + 2] = a1;
            data[i + 3] = a0;
        }
    }
}

@@ -33,48 +33,8 @@
 l;%0A%0A
-%0Aimport org.apache.commons.io.IOUtils;%0A%0A
 impo
@@ -116,100 +116,19 @@
 .io.
-File;%0Aimport java.io.FileInputStream;%0Aimport java.io.IOException;%0Aimport java.io.InputStream
+IOException
 ;%0A%0A/
@@ -695,965 +695,8 @@
 %7B%7D%0A%0A
-    /**%0A     * The default encryption key used by the Escapists.%0A     *%0A     * This is a magic value!%0A     */%0A    private static final String ENCRYPTION_KEY = %22mothking%22;%0A%0A    /**%0A     * Decrypts a Blowfish encrypted file using the built-in key.%0A     *%0A     * @param t The file to read and decrypt%0A     * @return The byte contents of this file decrypted%0A     **/%0A    public static byte%5B%5D decrypt(File t) throws IOException %7B%0A        // get the data%0A        InputStream in = new FileInputStream(t);%0A        byte%5B%5D data = IOUtils.toByteArray(in);%0A        in.close();%0A%0A        return decryptBytes(data, ENCRYPTION_KEY.getBytes());%0A    %7D%0A%0A    /**%0A     * Decrypts a Blowfish encrypted array using the built-in key.%0A     *%0A     * @param t The file to read and decrypt%0A     * @return The byte contents of this file decrypted%0A     **/%0A    public static byte%5B%5D decrypt(byte%5B%5D t) throws IOException %7B%0A        return decryptBytes(t, ENCRYPTION_KEY.getBytes());%0A    %7D%0A%0A
     
@@ -2079,794 +2079,8 @@
  %7D%0A%0A
-    /**%0A     * Encrypts a Blowfish encrypted file using the built-in key.%0A     *%0A     * @param t The file to read and encrypt%0A     * @return The byte contents of this file encrypted%0A     **/%0A    public static byte%5B%5D encrypt(File t) throws IOException %7B%0A        // get the data%0A        InputStream in = new FileInputStream(t);%0A        byte%5B%5D data = IOUtils.toByteArray(in);%0A        in.close();%0A%0A        return encryptBytes(data, ENCRYPTION_KEY.getBytes());%0A    %7D%0A%0A    /**%0A     * Encrypts a Blowfish encrypted array using the built-in key.%0A     *%0A     * @param t The file to read and encrypt%0A     * @return The byte contents of this file encrypted%0A     **/%0A    public static byte%5B%5D encrypt(byte%5B%5D t) throws IOException %7B%0A        return encryptBytes(t, ENCRYPTION_KEY.getBytes());%0A    %7D%0A%0A%0A
     

Remove redundant functions in Blowfish encryption