author | chegar |
Mon, 14 Dec 2015 19:24:33 +0000 | |
changeset 34687 | d302ed125dc9 |
parent 27957 | 24b4e6082f19 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
5506 | 2 |
* Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
*/ |
4 |
||
5 |
/* |
|
6 |
* Copyright (C) 1998 by the FundsXpress, INC. |
|
7 |
* |
|
8 |
* All rights reserved. |
|
9 |
* |
|
10 |
* Export of this software from the United States of America may require |
|
11 |
* a specific license from the United States Government. It is the |
|
12 |
* responsibility of any person or organization contemplating export to |
|
13 |
* obtain such a license before exporting. |
|
14 |
* |
|
15 |
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and |
|
16 |
* distribute this software and its documentation for any purpose and |
|
17 |
* without fee is hereby granted, provided that the above copyright |
|
18 |
* notice appear in all copies and that both that copyright notice and |
|
19 |
* this permission notice appear in supporting documentation, and that |
|
20 |
* the name of FundsXpress. not be used in advertising or publicity pertaining |
|
21 |
* to distribution of the software without specific, written prior |
|
22 |
* permission. FundsXpress makes no representations about the suitability of |
|
23 |
* this software for any purpose. It is provided "as is" without express |
|
24 |
* or implied warranty. |
|
25 |
* |
|
26 |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
|
27 |
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
|
28 |
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
|
29 |
*/ |
|
30 |
||
31 |
package sun.security.krb5.internal.crypto.dk; |
|
32 |
||
33 |
import javax.crypto.Cipher; |
|
34 |
import javax.crypto.Mac; |
|
35 |
import java.security.GeneralSecurityException; |
|
36 |
import java.io.UnsupportedEncodingException; |
|
37 |
import java.util.Arrays; |
|
38 |
import java.io.ByteArrayInputStream; |
|
39 |
import java.io.ByteArrayOutputStream; |
|
40 |
import java.nio.charset.Charset; |
|
41 |
import java.nio.CharBuffer; |
|
42 |
import java.nio.ByteBuffer; |
|
34687
d302ed125dc9
8144995: Move sun.misc.HexDumpEncoder to sun.security.util
chegar
parents:
27957
diff
changeset
|
43 |
import sun.security.util.HexDumpEncoder; |
2 | 44 |
import sun.security.krb5.Confounder; |
45 |
import sun.security.krb5.internal.crypto.KeyUsage; |
|
46 |
import sun.security.krb5.KrbCryptoException; |
|
47 |
||
48 |
/** |
|
49 |
* Implements Derive Key cryptography functionality as defined in RFC 3961. |
|
50 |
* http://www.ietf.org/rfc/rfc3961.txt |
|
51 |
* |
|
52 |
* This is an abstract class. Concrete subclasses need to implement |
|
53 |
* the abstract methods. |
|
54 |
*/ |
|
55 |
||
56 |
public abstract class DkCrypto { |
|
57 |
||
58 |
protected static final boolean debug = false; |
|
59 |
||
60 |
// These values correspond to the ASCII encoding for the string "kerberos" |
|
61 |
static final byte[] KERBEROS_CONSTANT = |
|
62 |
{0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73}; |
|
63 |
||
64 |
protected abstract int getKeySeedLength(); // in bits |
|
65 |
||
66 |
protected abstract byte[] randomToKey(byte[] in); |
|
67 |
||
68 |
protected abstract Cipher getCipher(byte[] key, byte[] ivec, int mode) |
|
69 |
throws GeneralSecurityException; |
|
70 |
||
71 |
public abstract int getChecksumLength(); // in bytes |
|
72 |
||
73 |
protected abstract byte[] getHmac(byte[] key, byte[] plaintext) |
|
74 |
throws GeneralSecurityException; |
|
75 |
||
76 |
/** |
|
77 |
* From RFC 3961. |
|
78 |
* |
|
79 |
* encryption function conf = random string of length c |
|
80 |
* pad = shortest string to bring confounder |
|
81 |
* and plaintext to a length that's a |
|
82 |
* multiple of m |
|
83 |
* (C1, newIV) = E(Ke, conf | plaintext | pad, |
|
84 |
* oldstate.ivec) |
|
85 |
* H1 = HMAC(Ki, conf | plaintext | pad) |
|
86 |
* ciphertext = C1 | H1[1..h] |
|
87 |
* newstate.ivec = newIV |
|
88 |
* |
|
89 |
* @param ivec initial vector to use when initializing the cipher; if null, |
|
90 |
* then blocksize number of zeros are used, |
|
91 |
* @param new_ivec if non-null, it is updated upon return to be the |
|
92 |
* new ivec to use when calling encrypt next time |
|
93 |
*/ |
|
94 |
public byte[] encrypt(byte[] baseKey, int usage, |
|
95 |
byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len) |
|
96 |
throws GeneralSecurityException, KrbCryptoException { |
|
97 |
||
98 |
if (!KeyUsage.isValid(usage)) { |
|
99 |
throw new GeneralSecurityException("Invalid key usage number: " |
|
100 |
+ usage); |
|
101 |
} |
|
102 |
||
103 |
byte[] Ke = null; |
|
104 |
byte[] Ki = null; |
|
105 |
||
106 |
try { |
|
107 |
// Derive encryption key |
|
108 |
||
109 |
byte[] constant = new byte[5]; |
|
110 |
constant[0] = (byte) ((usage>>24)&0xff); |
|
111 |
constant[1] = (byte) ((usage>>16)&0xff); |
|
112 |
constant[2] = (byte) ((usage>>8)&0xff); |
|
113 |
constant[3] = (byte) (usage&0xff); |
|
114 |
||
115 |
constant[4] = (byte) 0xaa; |
|
116 |
||
117 |
Ke = dk(baseKey, constant); |
|
118 |
if (debug) { |
|
119 |
System.err.println("usage: " + usage); |
|
120 |
if (ivec != null) { |
|
121 |
traceOutput("old_state.ivec", ivec, 0, ivec.length); |
|
122 |
} |
|
123 |
traceOutput("plaintext", plaintext, start, Math.min(len, 32)); |
|
124 |
traceOutput("constant", constant, 0, constant.length); |
|
125 |
traceOutput("baseKey", baseKey, 0, baseKey.length); |
|
126 |
traceOutput("Ke", Ke, 0, Ke.length); |
|
127 |
} |
|
128 |
||
129 |
// Encrypt |
|
130 |
// C1 = E(Ke, conf | plaintext | pad, oldivec) |
|
131 |
Cipher encCipher = getCipher(Ke, ivec, Cipher.ENCRYPT_MODE); |
|
132 |
int blockSize = encCipher.getBlockSize(); |
|
133 |
byte[] confounder = Confounder.bytes(blockSize); |
|
134 |
||
135 |
int plainSize = roundup(confounder.length + len, blockSize); |
|
136 |
if (debug) { |
|
137 |
System.err.println("confounder = " + confounder.length + |
|
138 |
"; plaintext = " + len + "; padding = " + |
|
139 |
(plainSize - confounder.length - len) + "; total = " + |
|
140 |
plainSize); |
|
141 |
traceOutput("confounder", confounder, 0, confounder.length); |
|
142 |
} |
|
143 |
||
144 |
byte[] toBeEncrypted = new byte[plainSize]; |
|
145 |
System.arraycopy(confounder, 0, toBeEncrypted, |
|
146 |
0, confounder.length); |
|
147 |
System.arraycopy(plaintext, start, toBeEncrypted, |
|
148 |
confounder.length, len); |
|
149 |
||
150 |
// Set padding bytes to zero |
|
151 |
Arrays.fill(toBeEncrypted, confounder.length + len, plainSize, |
|
152 |
(byte)0); |
|
153 |
||
154 |
int cipherSize = encCipher.getOutputSize(plainSize); |
|
155 |
int ccSize = cipherSize + getChecksumLength(); // cipher | hmac |
|
156 |
||
157 |
byte[] ciphertext = new byte[ccSize]; |
|
158 |
||
159 |
encCipher.doFinal(toBeEncrypted, 0, plainSize, ciphertext, 0); |
|
160 |
||
161 |
// Update ivec for next operation |
|
162 |
// (last blockSize bytes of ciphertext) |
|
163 |
// newstate.ivec = newIV |
|
164 |
if (new_ivec != null && new_ivec.length == blockSize) { |
|
165 |
System.arraycopy(ciphertext, cipherSize - blockSize, |
|
166 |
new_ivec, 0, blockSize); |
|
167 |
if (debug) { |
|
168 |
traceOutput("new_ivec", new_ivec, 0, new_ivec.length); |
|
169 |
} |
|
170 |
} |
|
171 |
||
172 |
// Derive integrity key |
|
173 |
constant[4] = (byte) 0x55; |
|
174 |
Ki = dk(baseKey, constant); |
|
175 |
if (debug) { |
|
176 |
traceOutput("constant", constant, 0, constant.length); |
|
177 |
traceOutput("Ki", Ki, 0, Ke.length); |
|
178 |
} |
|
179 |
||
180 |
// Generate checksum |
|
181 |
// H1 = HMAC(Ki, conf | plaintext | pad) |
|
182 |
byte[] hmac = getHmac(Ki, toBeEncrypted); |
|
183 |
||
184 |
if (debug) { |
|
185 |
traceOutput("hmac", hmac, 0, hmac.length); |
|
186 |
traceOutput("ciphertext", ciphertext, 0, |
|
187 |
Math.min(ciphertext.length, 32)); |
|
188 |
} |
|
189 |
||
190 |
// C1 | H1[1..h] |
|
191 |
System.arraycopy(hmac, 0, ciphertext, cipherSize, |
|
192 |
getChecksumLength()); |
|
193 |
return ciphertext; |
|
194 |
} finally { |
|
195 |
if (Ke != null) { |
|
196 |
Arrays.fill(Ke, 0, Ke.length, (byte) 0); |
|
197 |
} |
|
198 |
if (Ki != null) { |
|
199 |
Arrays.fill(Ki, 0, Ki.length, (byte) 0); |
|
200 |
} |
|
201 |
} |
|
202 |
} |
|
203 |
||
204 |
/** |
|
205 |
* Performs encryption using given key only; does not add |
|
206 |
* confounder, padding, or checksum. Incoming data to be encrypted |
|
207 |
* assumed to have the correct blocksize. |
|
208 |
* Ignore key usage. |
|
209 |
*/ |
|
210 |
public byte[] encryptRaw(byte[] baseKey, int usage, |
|
211 |
byte[] ivec, byte[] plaintext, int start, int len) |
|
212 |
throws GeneralSecurityException, KrbCryptoException { |
|
213 |
||
214 |
if (debug) { |
|
215 |
System.err.println("usage: " + usage); |
|
216 |
if (ivec != null) { |
|
217 |
traceOutput("old_state.ivec", ivec, 0, ivec.length); |
|
218 |
} |
|
219 |
traceOutput("plaintext", plaintext, start, Math.min(len, 32)); |
|
220 |
traceOutput("baseKey", baseKey, 0, baseKey.length); |
|
221 |
} |
|
222 |
||
223 |
// Encrypt |
|
224 |
Cipher encCipher = getCipher(baseKey, ivec, Cipher.ENCRYPT_MODE); |
|
225 |
int blockSize = encCipher.getBlockSize(); |
|
226 |
||
227 |
if ((len % blockSize) != 0) { |
|
228 |
throw new GeneralSecurityException( |
|
229 |
"length of data to be encrypted (" + len + |
|
230 |
") is not a multiple of the blocksize (" + blockSize + ")"); |
|
231 |
} |
|
232 |
||
233 |
int cipherSize = encCipher.getOutputSize(len); |
|
234 |
byte[] ciphertext = new byte[cipherSize]; |
|
235 |
||
236 |
encCipher.doFinal(plaintext, 0, len, ciphertext, 0); |
|
237 |
return ciphertext; |
|
238 |
} |
|
239 |
||
240 |
/** |
|
241 |
* Decrypts data using specified key and initial vector. |
|
242 |
* @param baseKey encryption key to use |
|
243 |
* @param ciphertext encrypted data to be decrypted |
|
244 |
* @param usage ignored |
|
245 |
*/ |
|
246 |
public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, |
|
247 |
byte[] ciphertext, int start, int len) |
|
248 |
throws GeneralSecurityException { |
|
249 |
||
250 |
if (debug) { |
|
251 |
System.err.println("usage: " + usage); |
|
252 |
if (ivec != null) { |
|
253 |
traceOutput("old_state.ivec", ivec, 0, ivec.length); |
|
254 |
} |
|
255 |
traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); |
|
256 |
traceOutput("baseKey", baseKey, 0, baseKey.length); |
|
257 |
} |
|
258 |
||
259 |
Cipher decCipher = getCipher(baseKey, ivec, Cipher.DECRYPT_MODE); |
|
260 |
||
261 |
int blockSize = decCipher.getBlockSize(); |
|
262 |
||
263 |
if ((len % blockSize) != 0) { |
|
264 |
throw new GeneralSecurityException( |
|
265 |
"length of data to be decrypted (" + len + |
|
266 |
") is not a multiple of the blocksize (" + blockSize + ")"); |
|
267 |
} |
|
268 |
||
269 |
byte[] decrypted = decCipher.doFinal(ciphertext, start, len); |
|
270 |
||
271 |
if (debug) { |
|
272 |
traceOutput("decrypted", decrypted, 0, |
|
273 |
Math.min(decrypted.length, 32)); |
|
274 |
} |
|
275 |
||
276 |
return decrypted; |
|
277 |
} |
|
278 |
||
279 |
/** |
|
280 |
* @param baseKey key from which keys are to be derived using usage |
|
281 |
* @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h] |
|
282 |
*/ |
|
283 |
public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, |
|
284 |
byte[] ciphertext, int start, int len) throws GeneralSecurityException { |
|
285 |
||
286 |
if (!KeyUsage.isValid(usage)) { |
|
287 |
throw new GeneralSecurityException("Invalid key usage number: " |
|
288 |
+ usage); |
|
289 |
} |
|
290 |
||
291 |
byte[] Ke = null; |
|
292 |
byte[] Ki = null; |
|
293 |
||
294 |
try { |
|
295 |
// Derive encryption key |
|
296 |
byte[] constant = new byte[5]; |
|
297 |
constant[0] = (byte) ((usage>>24)&0xff); |
|
298 |
constant[1] = (byte) ((usage>>16)&0xff); |
|
299 |
constant[2] = (byte) ((usage>>8)&0xff); |
|
300 |
constant[3] = (byte) (usage&0xff); |
|
301 |
||
302 |
constant[4] = (byte) 0xaa; |
|
303 |
||
304 |
Ke = dk(baseKey, constant); // Encryption key |
|
305 |
||
306 |
if (debug) { |
|
307 |
System.err.println("usage: " + usage); |
|
308 |
if (ivec != null) { |
|
309 |
traceOutput("old_state.ivec", ivec, 0, ivec.length); |
|
310 |
} |
|
311 |
traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); |
|
312 |
traceOutput("constant", constant, 0, constant.length); |
|
313 |
traceOutput("baseKey", baseKey, 0, baseKey.length); |
|
314 |
traceOutput("Ke", Ke, 0, Ke.length); |
|
315 |
} |
|
316 |
||
317 |
Cipher decCipher = getCipher(Ke, ivec, Cipher.DECRYPT_MODE); |
|
318 |
int blockSize = decCipher.getBlockSize(); |
|
319 |
||
320 |
// Decrypt [confounder | plaintext | padding] (without checksum) |
|
321 |
int cksumSize = getChecksumLength(); |
|
322 |
int cipherSize = len - cksumSize; |
|
323 |
byte[] decrypted = decCipher.doFinal(ciphertext, start, cipherSize); |
|
324 |
||
325 |
if (debug) { |
|
326 |
traceOutput("decrypted", decrypted, 0, |
|
327 |
Math.min(decrypted.length, 32)); |
|
328 |
} |
|
329 |
||
330 |
// decrypted = [confounder | plaintext | padding] |
|
331 |
||
332 |
// Derive integrity key |
|
333 |
constant[4] = (byte) 0x55; |
|
334 |
Ki = dk(baseKey, constant); // Integrity key |
|
335 |
if (debug) { |
|
336 |
traceOutput("constant", constant, 0, constant.length); |
|
337 |
traceOutput("Ki", Ki, 0, Ke.length); |
|
338 |
} |
|
339 |
||
340 |
// Verify checksum |
|
341 |
// H1 = HMAC(Ki, conf | plaintext | pad) |
|
342 |
byte[] calculatedHmac = getHmac(Ki, decrypted); |
|
343 |
||
344 |
if (debug) { |
|
345 |
traceOutput("calculated Hmac", calculatedHmac, 0, |
|
346 |
calculatedHmac.length); |
|
347 |
traceOutput("message Hmac", ciphertext, cipherSize, |
|
348 |
cksumSize); |
|
349 |
} |
|
350 |
||
351 |
boolean cksumFailed = false; |
|
352 |
if (calculatedHmac.length >= cksumSize) { |
|
353 |
for (int i = 0; i < cksumSize; i++) { |
|
354 |
if (calculatedHmac[i] != ciphertext[cipherSize+i]) { |
|
355 |
cksumFailed = true; |
|
356 |
break; |
|
357 |
} |
|
358 |
} |
|
359 |
} |
|
360 |
||
361 |
if (cksumFailed) { |
|
362 |
throw new GeneralSecurityException("Checksum failed"); |
|
363 |
} |
|
364 |
||
365 |
// Prepare decrypted msg and ivec to be returned |
|
366 |
// Last blockSize bytes of ciphertext without checksum |
|
367 |
if (ivec != null && ivec.length == blockSize) { |
|
368 |
System.arraycopy(ciphertext, start + cipherSize - blockSize, |
|
369 |
ivec, 0, blockSize); |
|
370 |
if (debug) { |
|
371 |
traceOutput("new_state.ivec", ivec, 0, ivec.length); |
|
372 |
} |
|
373 |
} |
|
374 |
||
375 |
// Get rid of confounder |
|
376 |
// [plaintext | padding] |
|
377 |
byte[] plaintext = new byte[decrypted.length - blockSize]; |
|
378 |
System.arraycopy(decrypted, blockSize, plaintext, |
|
379 |
0, plaintext.length); |
|
380 |
return plaintext; // padding still there |
|
381 |
} finally { |
|
382 |
if (Ke != null) { |
|
383 |
Arrays.fill(Ke, 0, Ke.length, (byte) 0); |
|
384 |
} |
|
385 |
if (Ki != null) { |
|
386 |
Arrays.fill(Ki, 0, Ki.length, (byte) 0); |
|
387 |
} |
|
388 |
} |
|
389 |
} |
|
390 |
||
391 |
// Round up to the next blocksize |
|
392 |
int roundup(int n, int blocksize) { |
|
393 |
return (((n + blocksize - 1) / blocksize) * blocksize); |
|
394 |
} |
|
395 |
||
396 |
public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input, |
|
397 |
int start, int len) throws GeneralSecurityException { |
|
398 |
||
399 |
if (!KeyUsage.isValid(usage)) { |
|
400 |
throw new GeneralSecurityException("Invalid key usage number: " |
|
401 |
+ usage); |
|
402 |
} |
|
403 |
||
404 |
// Derive keys |
|
405 |
byte[] constant = new byte[5]; |
|
406 |
constant[0] = (byte) ((usage>>24)&0xff); |
|
407 |
constant[1] = (byte) ((usage>>16)&0xff); |
|
408 |
constant[2] = (byte) ((usage>>8)&0xff); |
|
409 |
constant[3] = (byte) (usage&0xff); |
|
410 |
||
411 |
constant[4] = (byte) 0x99; |
|
412 |
||
413 |
byte[] Kc = dk(baseKey, constant); // Checksum key |
|
414 |
if (debug) { |
|
415 |
System.err.println("usage: " + usage); |
|
416 |
traceOutput("input", input, start, Math.min(len, 32)); |
|
417 |
traceOutput("constant", constant, 0, constant.length); |
|
418 |
traceOutput("baseKey", baseKey, 0, baseKey.length); |
|
419 |
traceOutput("Kc", Kc, 0, Kc.length); |
|
420 |
} |
|
421 |
||
422 |
try { |
|
423 |
// Generate checksum |
|
424 |
// H1 = HMAC(Kc, input) |
|
425 |
byte[] hmac = getHmac(Kc, input); |
|
426 |
if (debug) { |
|
427 |
traceOutput("hmac", hmac, 0, hmac.length); |
|
428 |
} |
|
429 |
if (hmac.length == getChecksumLength()) { |
|
430 |
return hmac; |
|
431 |
} else if (hmac.length > getChecksumLength()) { |
|
432 |
byte[] buf = new byte[getChecksumLength()]; |
|
433 |
System.arraycopy(hmac, 0, buf, 0, buf.length); |
|
434 |
return buf; |
|
435 |
} else { |
|
436 |
throw new GeneralSecurityException("checksum size too short: " + |
|
437 |
hmac.length + "; expecting : " + getChecksumLength()); |
|
438 |
} |
|
439 |
} finally { |
|
440 |
Arrays.fill(Kc, 0, Kc.length, (byte)0); |
|
441 |
} |
|
442 |
} |
|
443 |
||
444 |
// DK(Key, Constant) = random-to-key(DR(Key, Constant)) |
|
445 |
byte[] dk(byte[] key, byte[] constant) |
|
446 |
throws GeneralSecurityException { |
|
447 |
return randomToKey(dr(key, constant)); |
|
448 |
} |
|
449 |
||
450 |
/* |
|
451 |
* From RFC 3961. |
|
452 |
* |
|
453 |
* DR(Key, Constant) = k-truncate(E(Key, Constant, |
|
454 |
* initial-cipher-state)) |
|
455 |
* |
|
456 |
* Here DR is the random-octet generation function described below, and |
|
457 |
* DK is the key-derivation function produced from it. In this |
|
458 |
* construction, E(Key, Plaintext, CipherState) is a cipher, Constant is |
|
459 |
* a well-known constant determined by the specific usage of this |
|
460 |
* function, and k-truncate truncates its argument by taking the first k |
|
461 |
* bits. Here, k is the key generation seed length needed for the |
|
462 |
* encryption system. |
|
463 |
* |
|
464 |
* The output of the DR function is a string of bits; the actual key is |
|
465 |
* produced by applying the cryptosystem's random-to-key operation on |
|
466 |
* this bitstring. |
|
467 |
* |
|
468 |
* If the Constant is smaller than the cipher block size of E, then it |
|
469 |
* must be expanded with n-fold() so it can be encrypted. If the output |
|
470 |
* of E is shorter than k bits it is fed back into the encryption as |
|
471 |
* many times as necessary. The construct is as follows (where | |
|
472 |
* indicates concatentation): |
|
473 |
* |
|
474 |
* K1 = E(Key, n-fold(Constant), initial-cipher-state) |
|
475 |
* K2 = E(Key, K1, initial-cipher-state) |
|
476 |
* K3 = E(Key, K2, initial-cipher-state) |
|
477 |
* K4 = ... |
|
478 |
* |
|
479 |
* DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...) |
|
480 |
*/ |
|
481 |
private byte[] dr(byte[] key, byte[] constant) |
|
482 |
throws GeneralSecurityException { |
|
483 |
||
484 |
Cipher encCipher = getCipher(key, null, Cipher.ENCRYPT_MODE); |
|
485 |
int blocksize = encCipher.getBlockSize(); |
|
486 |
||
487 |
if (constant.length != blocksize) { |
|
488 |
constant = nfold(constant, blocksize * 8); |
|
489 |
} |
|
490 |
byte[] toBeEncrypted = constant; |
|
491 |
||
492 |
int keybytes = (getKeySeedLength()>>3); // from bits to bytes |
|
493 |
byte[] rawkey = new byte[keybytes]; |
|
494 |
int posn = 0; |
|
495 |
||
496 |
/* loop encrypting the blocks until enough key bytes are generated */ |
|
497 |
int n = 0, len; |
|
498 |
while (n < keybytes) { |
|
499 |
if (debug) { |
|
500 |
System.err.println("Encrypting: " + |
|
501 |
bytesToString(toBeEncrypted)); |
|
502 |
} |
|
503 |
||
504 |
byte[] cipherBlock = encCipher.doFinal(toBeEncrypted); |
|
505 |
if (debug) { |
|
506 |
System.err.println("K: " + ++posn + " = " + |
|
507 |
bytesToString(cipherBlock)); |
|
508 |
} |
|
509 |
||
510 |
len = (keybytes - n <= cipherBlock.length ? (keybytes - n) : |
|
511 |
cipherBlock.length); |
|
512 |
if (debug) { |
|
513 |
System.err.println("copying " + len + " key bytes"); |
|
514 |
} |
|
515 |
System.arraycopy(cipherBlock, 0, rawkey, n, len); |
|
516 |
n += len; |
|
517 |
toBeEncrypted = cipherBlock; |
|
518 |
} |
|
519 |
return rawkey; |
|
520 |
} |
|
521 |
||
522 |
// --------------------------------- |
|
523 |
||
524 |
// From MIT-1.3.1 distribution |
|
525 |
/* |
|
526 |
* n-fold(k-bits): |
|
527 |
* l = lcm(n,k) |
|
528 |
* r = l/k |
|
529 |
* s = k-bits | k-bits rot 13 | k-bits rot 13*2 | ... | k-bits rot 13*(r-1) |
|
530 |
* compute the 1's complement sum: |
|
531 |
* n-fold = s[0..n-1]+s[n..2n-1]+s[2n..3n-1]+..+s[(k-1)*n..k*n-1] |
|
532 |
*/ |
|
533 |
||
534 |
/* |
|
535 |
* representation: msb first, assume n and k are multiples of 8, and |
|
536 |
* that k>=16. this is the case of all the cryptosystems which are |
|
537 |
* likely to be used. this function can be replaced if that |
|
538 |
* assumption ever fails. |
|
539 |
*/ |
|
540 |
||
541 |
/* input length is in bits */ |
|
542 |
static byte[] nfold(byte[] in, int outbits) { |
|
543 |
||
544 |
int inbits = in.length; |
|
545 |
outbits >>= 3; // count in bytes |
|
546 |
||
547 |
/* first compute lcm(n,k) */ |
|
548 |
int a, b, c, lcm; |
|
549 |
a = outbits; // n |
|
550 |
b = inbits; // k |
|
551 |
||
552 |
while (b != 0) { |
|
553 |
c = b; |
|
554 |
b = a % b; |
|
555 |
a = c; |
|
556 |
} |
|
557 |
lcm = outbits*inbits/a; |
|
558 |
||
559 |
if (debug) { |
|
560 |
System.err.println("k: " + inbits); |
|
561 |
System.err.println("n: " + outbits); |
|
562 |
System.err.println("lcm: " + lcm); |
|
563 |
} |
|
564 |
||
565 |
/* now do the real work */ |
|
566 |
byte[] out = new byte[outbits]; |
|
567 |
Arrays.fill(out, (byte)0); |
|
568 |
||
569 |
int thisbyte = 0; |
|
570 |
int msbit, i, bval, oval; |
|
571 |
||
572 |
// this will end up cycling through k lcm(k,n)/k times, which |
|
573 |
// is correct |
|
574 |
for (i = lcm-1; i >= 0; i--) { |
|
575 |
/* compute the msbit in k which gets added into this byte */ |
|
576 |
msbit = (/* first, start with msbit in the first, unrotated byte */ |
|
577 |
((inbits<<3)-1) |
|
578 |
/* then, for each byte, shift to right for each repetition */ |
|
579 |
+ (((inbits<<3)+13)*(i/inbits)) |
|
580 |
/* last, pick out correct byte within that shifted repetition */ |
|
581 |
+ ((inbits-(i%inbits)) << 3)) % (inbits << 3); |
|
582 |
||
583 |
/* pull out the byte value itself */ |
|
584 |
// Mask off values using &0xff to get only the lower byte |
|
585 |
// Use >>> to avoid sign extension |
|
586 |
bval = ((((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8)| |
|
587 |
(in[((inbits)-(msbit>>>3))%inbits]&0xff)) |
|
588 |
>>>((msbit&7)+1))&0xff; |
|
589 |
||
590 |
/* |
|
591 |
System.err.println("((" + |
|
592 |
((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8) |
|
593 |
+ "|" + (in[((inbits)-(msbit>>>3))%inbits]&0xff) + ")" |
|
594 |
+ ">>>" + ((msbit&7)+1) + ")&0xff = " + bval); |
|
595 |
*/ |
|
596 |
||
597 |
thisbyte += bval; |
|
598 |
||
599 |
/* do the addition */ |
|
600 |
// Mask off values using &0xff to get only the lower byte |
|
601 |
oval = (out[i%outbits]&0xff); |
|
602 |
thisbyte += oval; |
|
603 |
out[i%outbits] = (byte) (thisbyte&0xff); |
|
604 |
||
605 |
if (debug) { |
|
606 |
System.err.println("msbit[" + i + "] = " + msbit + "\tbval=" + |
|
607 |
Integer.toHexString(bval) + "\toval=" + |
|
608 |
Integer.toHexString(oval) |
|
609 |
+ "\tsum = " + Integer.toHexString(thisbyte)); |
|
610 |
} |
|
611 |
||
612 |
||
613 |
/* keep around the carry bit, if any */ |
|
614 |
thisbyte >>>= 8; |
|
615 |
||
616 |
if (debug) { |
|
617 |
System.err.println("carry=" + thisbyte); |
|
618 |
} |
|
619 |
} |
|
620 |
||
621 |
/* if there's a carry bit left over, add it back in */ |
|
622 |
if (thisbyte != 0) { |
|
623 |
for (i = outbits-1; i >= 0; i--) { |
|
624 |
/* do the addition */ |
|
625 |
thisbyte += (out[i]&0xff); |
|
626 |
out[i] = (byte) (thisbyte&0xff); |
|
627 |
||
628 |
/* keep around the carry bit, if any */ |
|
629 |
thisbyte >>>= 8; |
|
630 |
} |
|
631 |
} |
|
632 |
||
633 |
return out; |
|
634 |
} |
|
635 |
||
636 |
// Routines used for debugging |
|
637 |
static String bytesToString(byte[] digest) { |
|
638 |
// Get character representation of digest |
|
24969
afa6934dd8e8
8041679: Replace uses of StringBuffer with StringBuilder within core library classes
psandoz
parents:
5506
diff
changeset
|
639 |
StringBuilder digestString = new StringBuilder(); |
2 | 640 |
|
641 |
for (int i = 0; i < digest.length; i++) { |
|
642 |
if ((digest[i] & 0x000000ff) < 0x10) { |
|
27957
24b4e6082f19
8055723: Replace concat String to append in StringBuilder parameters (dev)
weijun
parents:
25859
diff
changeset
|
643 |
digestString.append('0').append(Integer.toHexString(digest[i] & 0x000000ff)); |
2 | 644 |
} else { |
645 |
digestString.append( |
|
646 |
Integer.toHexString(digest[i] & 0x000000ff)); |
|
647 |
} |
|
648 |
} |
|
649 |
return digestString.toString(); |
|
650 |
} |
|
651 |
||
652 |
private static byte[] binaryStringToBytes(String str) { |
|
653 |
char[] usageStr = str.toCharArray(); |
|
654 |
byte[] usage = new byte[usageStr.length/2]; |
|
655 |
for (int i = 0; i < usage.length; i++) { |
|
656 |
byte a = Byte.parseByte(new String(usageStr, i*2, 1), 16); |
|
657 |
byte b = Byte.parseByte(new String(usageStr, i*2 + 1, 1), 16); |
|
658 |
usage[i] = (byte) ((a<<4)|b); |
|
659 |
} |
|
660 |
return usage; |
|
661 |
} |
|
662 |
||
663 |
static void traceOutput(String traceTag, byte[] output, int offset, |
|
664 |
int len) { |
|
665 |
try { |
|
666 |
ByteArrayOutputStream out = new ByteArrayOutputStream(len); |
|
667 |
new HexDumpEncoder().encodeBuffer( |
|
668 |
new ByteArrayInputStream(output, offset, len), out); |
|
669 |
||
670 |
System.err.println(traceTag + ":" + out.toString()); |
|
671 |
} catch (Exception e) { |
|
672 |
} |
|
673 |
} |
|
674 |
||
675 |
// String.getBytes("UTF-8"); |
|
676 |
// Do this instead of using String to avoid making password immutable |
|
677 |
static byte[] charToUtf8(char[] chars) { |
|
678 |
Charset utf8 = Charset.forName("UTF-8"); |
|
679 |
||
680 |
CharBuffer cb = CharBuffer.wrap(chars); |
|
681 |
ByteBuffer bb = utf8.encode(cb); |
|
682 |
int len = bb.limit(); |
|
683 |
byte[] answer = new byte[len]; |
|
684 |
bb.get(answer, 0, len); |
|
685 |
return answer; |
|
686 |
} |
|
687 |
||
688 |
static byte[] charToUtf16(char[] chars) { |
|
689 |
Charset utf8 = Charset.forName("UTF-16LE"); |
|
690 |
||
691 |
CharBuffer cb = CharBuffer.wrap(chars); |
|
692 |
ByteBuffer bb = utf8.encode(cb); |
|
693 |
int len = bb.limit(); |
|
694 |
byte[] answer = new byte[len]; |
|
695 |
bb.get(answer, 0, len); |
|
696 |
return answer; |
|
697 |
} |
|
698 |
} |