]> jfr.im git - irc/rizon/acid.git/blob - acid/src/main/java/net/rizon/acid/util/CloakGenerator.java
fe27d35e1d33f81b372d6bd8f28547f64b2eebe6
[irc/rizon/acid.git] / acid / src / main / java / net / rizon / acid / util / CloakGenerator.java
1 package net.rizon.acid.util;
2
3 import java.net.InetAddress;
4 import java.net.Inet4Address;
5 import java.net.Inet6Address;
6 import java.net.UnknownHostException;
7 import java.security.MessageDigest;
8 import java.security.NoSuchAlgorithmException;
9
10 /**
11 * CloakGenerator that generates a cloaked host from an input IP or hostname.
12 *
13 * @author Shiz
14 */
15 public class CloakGenerator
16 {
17
18 protected String[] keys;
19 protected String hostPrefix;
20 protected MessageDigest digest;
21
22 public static boolean validateCloakKey(final String key)
23 {
24 boolean hasLowerCase = false;
25 boolean hasUpperCase = false;
26 boolean hasDigit = false;
27
28 for (int i = 0; i < key.length(); i++)
29 {
30 char c = key.charAt(i);
31 if (!hasLowerCase && Character.isLowerCase(c))
32 {
33 hasLowerCase = true;
34 }
35 if (!hasUpperCase && Character.isUpperCase(c))
36 {
37 hasUpperCase = true;
38 }
39 if (!hasDigit && Character.isDigit(c))
40 {
41 hasDigit = true;
42 }
43 if (hasLowerCase && hasUpperCase && hasDigit)
44 {
45 return true;
46 }
47 }
48
49 return false;
50 }
51
52 public CloakGenerator(String[] keys, String hostPrefix) throws NoSuchAlgorithmException
53 {
54 this.keys = keys;
55 this.hostPrefix = hostPrefix;
56 this.digest = MessageDigest.getInstance("MD5");
57 }
58
59 /**
60 * * Cloak an arbitrary host. Will do detection to figure out what kind
61 * of host.
62 */
63 public String cloak(String host)
64 {
65 /* IPv6. */
66 if (host.contains(":"))
67 {
68 /* Plexus' cloaking code has an IPv6 parsing bug where it stops scanning at the first ::. */
69 int cutoff = host.indexOf("::");
70 if (cutoff != -1)
71 {
72 host = host.substring(0, cutoff + 2);
73 }
74
75 try
76 {
77 return this.cloak(Inet6Address.getByName(host));
78 }
79 catch (UnknownHostException e)
80 {
81 return null;
82 }
83 /* IPv4 or host. */
84 }
85 else if (host.contains("."))
86 {
87 for (char c : host.toCharArray())
88 {
89 if (c != '.' && !Character.isDigit(c)) /* Host. */ {
90 return this.cloakHost(host);
91 }
92 }
93 /* IPv4. */
94 try
95 {
96 return this.cloak(Inet4Address.getByName(host));
97 }
98 catch (UnknownHostException e)
99 {
100 return null;
101 }
102 /* Host */
103 }
104 else
105 {
106 return this.cloakHost(host);
107 }
108 }
109
110 /**
111 * Cloak an internet address.
112 */
113 public String cloak(InetAddress addr)
114 {
115 if (addr instanceof Inet4Address)
116 {
117 return this.cloakIPv4((Inet4Address) addr);
118 }
119 else if (addr instanceof Inet6Address)
120 {
121 return this.cloakIPv6((Inet6Address) addr);
122 }
123 return null;
124 }
125
126 /**
127 * Cloak an IPv4 address.
128 *
129 * Cloak format: A.B.C.D -> ALPHA.BETA.GAMMA.IP
130 *
131 * ALPHA: downmix(md5(md5(keys[1]:A.B.C.D:keys[2])keys[0])) BETA:
132 * downmix(md5(md5(keys[2]:A.B.C:keys[0])keys[1])) GAMMA:
133 * downmix(md5(md5(keys[0]:A.B:keys[1])keys[2]))
134 */
135 public String cloakIPv4(Inet4Address addr)
136 {
137 /* Gather IP pieces. */
138 byte[] bytePieces = addr.getAddress();
139 int[] pieces = new int[bytePieces.length];
140 for (int i = 0; i < bytePieces.length; i++)
141 {
142 pieces[i] = bytePieces[i] & 0xFF;
143 }
144
145 /* Generate alpha. */
146 String alphaInput = String.format("%d.%d.%d.%d", pieces[0], pieces[1], pieces[2], pieces[3]);
147 String alpha = this.stage(alphaInput, this.keys[1], this.keys[2], this.keys[0]);
148
149 /* Generate beta. */
150 String betaInput = String.format("%d.%d.%d", pieces[0], pieces[1], pieces[2]);
151 String beta = this.stage(betaInput, this.keys[2], this.keys[0], this.keys[1]);
152
153 /* Generate gamma. */
154 String gammaInput = String.format("%d.%d", pieces[0], pieces[1]);
155 String gamma = this.stage(gammaInput, this.keys[0], this.keys[1], this.keys[2]);
156
157 return alpha + "." + beta + "." + gamma + ".IP";
158 }
159
160 /**
161 * Cloak an IPv6 address.
162 *
163 * Cloak format: a:b:c:d:e:f:g:h -> ALPHA:BETA:GAMMA:IP
164 *
165 * ALPHA: downmix(md5(md5(keys[1]:a:b:c:d:e:f:g:h:keys[2])keys[0]))
166 * BETA: downmix(md5(md5(keys[2]:a:b:c:d:e:f:g:keys[0])keys[1])) GAMMA:
167 * downmix(md5(md5(keys[0]:a:b:c:d:keys[1])keys[2]))
168 */
169 public String cloakIPv6(Inet6Address addr)
170 {
171 /* Gather IP pieces. */
172 byte[] bytePieces = addr.getAddress();
173 int[] pieces = new int[bytePieces.length / 2];
174 System.out.println("Byte length: " + bytePieces.length);
175 for (int i = 0; i < bytePieces.length; i += 2)
176 {
177 pieces[i / 2] = ((bytePieces[i] & 0xFF) << 8) | (bytePieces[i + 1] & 0xFF);
178 }
179
180 /* Generate alpha. */
181 String alphaInput = String.format("%x:%x:%x:%x:%x:%x:%x:%x",
182 pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7]);
183 String alpha = this.stage(alphaInput, this.keys[1], this.keys[2], this.keys[0]);
184
185 /* Generate beta. */
186 String betaInput = String.format("%x:%x:%x:%x:%x:%x:%x",
187 pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6]);
188 String beta = this.stage(betaInput, this.keys[2], this.keys[0], this.keys[1]);
189
190 /* Generate gamma. */
191 String gammaInput = String.format("%x:%x:%x:%x", pieces[0], pieces[1], pieces[2], pieces[3]);
192 String gamma = this.stage(gammaInput, this.keys[0], this.keys[1], this.keys[2]);
193
194 return alpha + ":" + beta + ":" + gamma + ":IP";
195 }
196
197 /**
198 * Cloak a reverse DNS host.
199 *
200 * Cloak format: my.host.com -> PREFIX-ALPHA.host.com localhost ->
201 * PREFIX-ALPHA
202 *
203 * ALPHA: downmix(md5(md5(keys[0]:my.host.com:keys[1])keys[2]))
204 */
205 public String cloakHost(String host)
206 {
207 int dot = host.indexOf('.');
208 String alpha = this.stage(host, this.keys[0], this.keys[1], this.keys[2]);
209
210 if (dot == -1 || dot == host.length() - 1)
211 {
212 return this.hostPrefix + alpha;
213 }
214 else
215 {
216 return this.hostPrefix + alpha + host.substring(dot);
217 }
218 }
219
220 /**
221 * Perform a downmix(md5(md5(key1:host:key2)key3)) stage.
222 */
223 protected String stage(String input, String key1, String key2, String key3)
224 {
225 String salted = String.format("%s:%s:%s", key1, input, key2);
226 byte[] firstMD5 = this.md5sum(salted);
227 byte[] secondMD5 = this.md5sum(firstMD5, key3.getBytes());
228 return this.downmix(secondMD5);
229 }
230
231 /**
232 * Mix the 128 bits of the MD5 hash into a single 32-bit value.
233 */
234 protected String downmix(byte[] input)
235 {
236 byte[] res = new byte[4];
237 res[0] = (byte) (input[0] ^ input[1] ^ input[2] ^ input[3]);
238 res[1] = (byte) (input[4] ^ input[5] ^ input[6] ^ input[7]);
239 res[2] = (byte) (input[8] ^ input[9] ^ input[10] ^ input[11]);
240 res[3] = (byte) (input[12] ^ input[13] ^ input[14] ^ input[15]);
241
242 int r = (((int) res[0] & 0xFFFFFFFF) << 24)
243 + (((int) res[1] & 0xFFFFFFFF) << 16)
244 + (((int) res[2] & 0xFFFFFFFF) << 8)
245 + ((int) res[3] & 0xFFFFFFFF);
246 return String.format("%X", r);
247 }
248
249 protected byte[] md5sum(String input)
250 {
251 return this.md5sum(input.getBytes());
252 }
253
254 protected byte[] md5sum(byte[]... input)
255 {
256 this.digest.reset();
257 for (byte[] b : input)
258 {
259 this.digest.update(b);
260 }
261 return this.digest.digest();
262 }
263 }