import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

public class OnstarAES {

	public static void main(String[] args) throws Exception {
		String resourceDir = args[0];
		String payload = new OnstarAES(Files.newInputStream(new File(resourceDir, "ic_onstar").toPath()))
				.decrypt(Files.newInputStream(new File(resourceDir, "connect").toPath()));
		System.out.println(payload);
	}

	private final Cipher cipher;

	public OnstarAES(InputStream keyInput) throws Exception {
		this.cipher = initCipher(initKey(keyInput));
	}

	public String decrypt(InputStream input) throws Exception {
		StringBuilder decrypted = new StringBuilder();
		DataInputStream encrypted;
		encrypted = new DataInputStream(input);
		encrypted.skip(8);

		int n;
		byte[] encryptedBuf = new byte[64];
		byte[] decryptedBuf;
		while ((n = encrypted.read(encryptedBuf)) != -1) {
			decryptedBuf = this.cipher.update(encryptedBuf, 0, n);
			if (decryptedBuf == null) {
				continue;
			}
			decrypted.append(new String(decryptedBuf));
		}
		decryptedBuf = this.cipher.doFinal();
		decrypted.append(new String(decryptedBuf));
		return decrypted.toString();
	}

	private static Key initKey(InputStream input) throws IOException {
		DataInputStream keyInput = new DataInputStream(input);
		byte[] keyBytes = new byte[16];
		try {
			keyInput.skip(111);
		} catch (IOException exception) {
			return new SecretKeySpec(keyBytes, "AES");
		}
		for (int i = 0; i < 16; ++i) {
			keyBytes[i] = (byte) Math.abs((int) Math.floor(Math.sin((double) Math.abs(keyInput.readByte()) / 2.0) * 255.0));
		}
		return new SecretKeySpec(keyBytes, "AES");
	}

	private static Cipher initCipher(Key key) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
		Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
		cipher.init(2, key, new GCMParameterSpec(128, new byte[16]));
		return cipher;
	}

}