package org.araqne.log.api;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptoPAn {
public static class Mask {
public long mask;
public long pad;
public Mask(long mask, long pad) {
this.mask = mask;
this.pad = pad;
}
}
private Cipher cipher;
private Mask[] masks;
private String aesKey;
private byte[] pad;
public CryptoPAn(String key) {
if (key.length() != 32) {
throw new IllegalArgumentException("key must me a 32 byte long string");
}
this.aesKey = key.substring(0, 16);
SecretKeySpec keyspec = new SecretKeySpec(aesKey.getBytes(), "AES");
try {
cipher = javax.crypto.Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keyspec, new IvParameterSpec(new byte[16]));
this.pad = cipher.doFinal(key.substring(16).getBytes());
byte[] f4 = Arrays.copyOf(this.pad, 4);
long f4bp = toInt(f4);
this.masks = new Mask[32];
for (int p = 0; p < 32; ++p) {
long mask = 0xFFFFFFFFL >> (32 - p) << (32 - p);
masks[p] = new Mask(mask, f4bp & (~mask));
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
}
private long toInt(byte[] arr) {
long l0 = ((long) arr[0] & 0xffL) << (8 * (3 - 0));
long l1 = ((long) arr[1] & 0xffL) << (8 * (3 - 1));
long l2 = ((long) arr[2] & 0xffL) << (8 * (3 - 2));
long l3 = ((long) arr[3] & 0xffL) << (8 * (3 - 3));
return l0 | l1 | l2 | l3;
}
public int[] anonymizei(String ip) {
long result = 0;
byte[] addria = new byte[4];
{
int i = 0;
for (String a : ip.split("\\.")) {
addria[i++] = (byte) Integer.parseInt(a.trim());
}
if (i != 4)
throw new IllegalArgumentException("Invalid IPv4 address");
}
long addri = toInt(addria);
long[] addresses = new long[32];
for (int i = 0; i < this.masks.length; ++i) {
addresses[i] = (addri & masks[i].mask) | masks[i].pad;
}
int[] calcResult = new int[32];
try {
for (int i = 0; i < 32; ++i) {
calcResult[i] = calc(addresses[i]);
}
result = 0;
for (int i = 0; i < 32; ++i) {
result = (result << 1) | calcResult[i];
}
byte[] rarr = toArray(result ^ addri);
int[] iresult = new int[4];
iresult[0] = ((int) rarr[0]) & 0xff;
iresult[1] = ((int) rarr[1]) & 0xff;
iresult[2] = ((int) rarr[2]) & 0xff;
iresult[3] = ((int) rarr[3]) & 0xff;
return iresult;
} catch (IllegalBlockSizeException e) {
throw new IllegalStateException(e);
} catch (BadPaddingException e) {
throw new IllegalStateException(e);
}
}
public String anonymize(String ip) {
int[] result = anonymizei(ip);
StringBuilder sb = new StringBuilder();
sb.append(result[0]);
sb.append(".");
sb.append(result[1]);
sb.append(".");
sb.append(result[2]);
sb.append(".");
sb.append(result[3]);
return sb.toString();
}
private byte[] toArray(long n) {
byte[] result = new byte[4];
for (int i = 3; i > -1; --i) {
result[3 - i] = (byte) (n >> (i * 8) & 0xFF);
}
return result;
}
// calculate the first bit for Crypto-PAN
private int calc(long a) throws IllegalBlockSizeException, BadPaddingException {
pad[3] = (byte) (a & 0xFF);
a >>= 8;
pad[2] = (byte) (a & 0xFF);
a >>= 8;
pad[1] = (byte) (a & 0xFF);
a >>= 8;
pad[0] = (byte) (a & 0xFF);
byte[] doFinal = cipher.doFinal(pad);
return doFinal[0] < 0 ? 1 : 0;
}
public static void main(String[] args) throws InterruptedException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 32; ++i) {
sb.append((char) i);
}
CryptoPAn c = new CryptoPAn(sb.toString());
System.out.println(c.anonymize("192.0.2.2"));
System.out.println(c.anonymize("192.0.2.1"));
System.out.println(c.anonymize("192.0.3.1"));
System.out.println(c.anonymize("192.1.2.1"));
System.out.println(new CryptoPAn(sb.toString()).anonymize("192.0.2.1"));
System.out.println("2.90.93.17".equals(c.anonymize("192.0.2.1")));
long started = System.currentTimeMillis();
for (int i = 0; i < 50000; ++i) {
c.anonymize("192.0.2.1");
}
System.out.println("elapsed: " + (System.currentTimeMillis() - started));
}
}