blob: 57a0c091721199eb5718eeb0fc8050442b2eed2c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.krb5.internal.crypto.dk;
27
28import java.security.*;
29import javax.crypto.*;
30import javax.crypto.spec.*;
31import java.util.*;
32import sun.security.krb5.EncryptedData;
33import sun.security.krb5.KrbCryptoException;
34import sun.security.krb5.Confounder;
35import sun.security.krb5.internal.crypto.KeyUsage;
36
37/**
38 * Support for ArcFour in Kerberos
39 * as defined in RFC 4757.
40 * http://www.ietf.org/rfc/rfc4757.txt
41 *
42 * @author Seema Malkani
43 */
44
45public class ArcFourCrypto extends DkCrypto {
46
47 private static final boolean debug = false;
48
49 private static final int confounderSize = 8;
50 private static final byte[] ZERO_IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0};
51 private static final int hashSize = 16;
52 private final int keyLength;
53
54 public ArcFourCrypto(int length) {
55 keyLength = length;
56 }
57
58 protected int getKeySeedLength() {
59 return keyLength; // bits; RC4 key material
60 }
61
62 protected byte[] randomToKey(byte[] in) {
63 // simple identity operation
64 return in;
65 }
66
67 public byte[] stringToKey(char[] passwd)
68 throws GeneralSecurityException {
69 return stringToKey(passwd, null);
70 }
71
72 /*
73 * String2Key(Password)
74 * K = MD4(UNICODE(password))
75 */
76 private byte[] stringToKey(char[] secret, byte[] opaque)
77 throws GeneralSecurityException {
78
79 if (opaque != null && opaque.length > 0) {
80 throw new RuntimeException("Invalid parameter to stringToKey");
81 }
82
83 byte[] passwd = null;
84 byte[] digest = null;
85 try {
86 // convert ascii to unicode
87 passwd = charToUtf16(secret);
88
89 // provider for MD4
90 MessageDigest md = sun.security.provider.MD4.getInstance();
91 md.update(passwd);
92 digest = md.digest();
93 } catch (Exception e) {
94 return null;
95 } finally {
96 if (passwd != null) {
97 Arrays.fill(passwd, (byte)0);
98 }
99 }
100
101 return digest;
102 }
103
104 protected Cipher getCipher(byte[] key, byte[] ivec, int mode)
105 throws GeneralSecurityException {
106
107 // IV
108 if (ivec == null) {
109 ivec = ZERO_IV;
110 }
111 SecretKeySpec secretKey = new SecretKeySpec(key, "ARCFOUR");
112 Cipher cipher = Cipher.getInstance("ARCFOUR");
113 IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
114 cipher.init(mode, secretKey, encIv);
115 return cipher;
116 }
117
118 public int getChecksumLength() {
119 return hashSize; // bytes
120 }
121
122 /**
123 * Get the HMAC-MD5
124 */
125 protected byte[] getHmac(byte[] key, byte[] msg)
126 throws GeneralSecurityException {
127
128 SecretKey keyKi = new SecretKeySpec(key, "HmacMD5");
129 Mac m = Mac.getInstance("HmacMD5");
130 m.init(keyKi);
131
132 // generate hash
133 byte[] hash = m.doFinal(msg);
134 return hash;
135 }
136
137 /**
138 * Calculate the checksum
139 */
140 public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input,
141 int start, int len) throws GeneralSecurityException {
142
143 if (debug) {
144 System.out.println("ARCFOUR: calculateChecksum with usage = " +
145 usage);
146 }
147
148 if (!KeyUsage.isValid(usage)) {
149 throw new GeneralSecurityException("Invalid key usage number: "
150 + usage);
151 }
152
153 byte[] Ksign = null;
154 // Derive signing key from session key
155 try {
156 byte[] ss = "signaturekey".getBytes();
157 // need to append end-of-string 00
158 byte[] new_ss = new byte[ss.length+1];
159 System.arraycopy(ss, 0, new_ss, 0, ss.length);
160 Ksign = getHmac(baseKey, new_ss);
161 } catch (Exception e) {
162 GeneralSecurityException gse =
163 new GeneralSecurityException("Calculate Checkum Failed!");
164 gse.initCause(e);
165 throw gse;
166 }
167
168 // get the salt using key usage
169 byte[] salt = getSalt(usage);
170
171 // Generate checksum of message
172 MessageDigest messageDigest = null;
173 try {
174 messageDigest = MessageDigest.getInstance("MD5");
175 } catch (NoSuchAlgorithmException e) {
176 GeneralSecurityException gse =
177 new GeneralSecurityException("Calculate Checkum Failed!");
178 gse.initCause(e);
179 throw gse;
180 }
181 messageDigest.update(salt);
182 messageDigest.update(input, start, len);
183 byte[] md5tmp = messageDigest.digest();
184
185 // Generate checksum
186 byte[] hmac = getHmac(Ksign, md5tmp);
187 if (debug) {
188 traceOutput("hmac", hmac, 0, hmac.length);
189 }
190 if (hmac.length == getChecksumLength()) {
191 return hmac;
192 } else if (hmac.length > getChecksumLength()) {
193 byte[] buf = new byte[getChecksumLength()];
194 System.arraycopy(hmac, 0, buf, 0, buf.length);
195 return buf;
196 } else {
197 throw new GeneralSecurityException("checksum size too short: " +
198 hmac.length + "; expecting : " + getChecksumLength());
199 }
200 }
201
202 /**
203 * Performs encryption of Sequence Number using derived key.
204 */
205 public byte[] encryptSeq(byte[] baseKey, int usage,
206 byte[] checksum, byte[] plaintext, int start, int len)
207 throws GeneralSecurityException, KrbCryptoException {
208
209 if (!KeyUsage.isValid(usage)) {
210 throw new GeneralSecurityException("Invalid key usage number: "
211 + usage);
212 }
213 // derive encryption for sequence number
214 byte[] salt = new byte[4];
215 byte[] kSeq = getHmac(baseKey, salt);
216
217 // derive new encryption key salted with sequence number
218 kSeq = getHmac(kSeq, checksum);
219
220 Cipher cipher = Cipher.getInstance("ARCFOUR");
221 SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR");
222 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
223 byte[] output = cipher.doFinal(plaintext, start, len);
224
225 return output;
226 }
227
228 /**
229 * Performs decryption of Sequence Number using derived key.
230 */
231 public byte[] decryptSeq(byte[] baseKey, int usage,
232 byte[] checksum, byte[] ciphertext, int start, int len)
233 throws GeneralSecurityException, KrbCryptoException {
234
235 if (!KeyUsage.isValid(usage)) {
236 throw new GeneralSecurityException("Invalid key usage number: "
237 + usage);
238 }
239
240 // derive decryption for sequence number
241 byte[] salt = new byte[4];
242 byte[] kSeq = getHmac(baseKey, salt);
243
244 // derive new encryption key salted with sequence number
245 kSeq = getHmac(kSeq, checksum);
246
247 Cipher cipher = Cipher.getInstance("ARCFOUR");
248 SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR");
249 cipher.init(Cipher.DECRYPT_MODE, secretKey);
250 byte[] output = cipher.doFinal(ciphertext, start, len);
251
252 return output;
253 }
254
255 /**
256 * Performs encryption using derived key; adds confounder.
257 */
258 public byte[] encrypt(byte[] baseKey, int usage,
259 byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)
260 throws GeneralSecurityException, KrbCryptoException {
261
262 if (!KeyUsage.isValid(usage)) {
263 throw new GeneralSecurityException("Invalid key usage number: "
264 + usage);
265 }
266
267 if (debug) {
268 System.out.println("ArcFour: ENCRYPT with key usage = " + usage);
269 }
270
271 // get the confounder
272 byte[] confounder = Confounder.bytes(confounderSize);
273
274 // add confounder to the plaintext for encryption
275 int plainSize = roundup(confounder.length + len, 1);
276 byte[] toBeEncrypted = new byte[plainSize];
277 System.arraycopy(confounder, 0, toBeEncrypted, 0, confounder.length);
278 System.arraycopy(plaintext, start, toBeEncrypted,
279 confounder.length, len);
280
281 /* begin the encryption, compute K1 */
282 byte[] k1 = new byte[baseKey.length];
283 System.arraycopy(baseKey, 0, k1, 0, baseKey.length);
284
285 // get the salt using key usage
286 byte[] salt = getSalt(usage);
287
288 // compute K2 using K1
289 byte[] k2 = getHmac(k1, salt);
290
291 // generate checksum using K2
292 byte[] checksum = getHmac(k2, toBeEncrypted);
293
294 // compute K3 using K2 and checksum
295 byte[] k3 = getHmac(k2, checksum);
296
297 Cipher cipher = Cipher.getInstance("ARCFOUR");
298 SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR");
299 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
300 byte[] output = cipher.doFinal(toBeEncrypted, 0, toBeEncrypted.length);
301
302 // encryptedData + HMAC
303 byte[] result = new byte[hashSize + output.length];
304 System.arraycopy(checksum, 0, result, 0, hashSize);
305 System.arraycopy(output, 0, result, hashSize, output.length);
306
307 return result;
308 }
309
310 /**
311 * Performs encryption using derived key; does not add confounder.
312 */
313 public byte[] encryptRaw(byte[] baseKey, int usage,
314 byte[] seqNum, byte[] plaintext, int start, int len)
315 throws GeneralSecurityException, KrbCryptoException {
316
317 if (!KeyUsage.isValid(usage)) {
318 throw new GeneralSecurityException("Invalid key usage number: "
319 + usage);
320 }
321
322 if (debug) {
323 System.out.println("\nARCFOUR: encryptRaw with usage = " + usage);
324 }
325
326 // Derive encryption key for data
327 // Key derivation salt = 0
328 byte[] klocal = new byte[baseKey.length];
329 for (int i = 0; i <= 15; i++) {
330 klocal[i] = (byte) (baseKey[i] ^ 0xF0);
331 }
332 byte[] salt = new byte[4];
333 byte[] kcrypt = getHmac(klocal, salt);
334
335 // Note: When using this RC4 based encryption type, the sequence number
336 // is always sent in big-endian rather than little-endian order.
337
338 // new encryption key salted with sequence number
339 kcrypt = getHmac(kcrypt, seqNum);
340
341 Cipher cipher = Cipher.getInstance("ARCFOUR");
342 SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR");
343 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
344 byte[] output = cipher.doFinal(plaintext, start, len);
345
346 return output;
347 }
348
349 /**
350 * @param baseKey key from which keys are to be derived using usage
351 * @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h]
352 */
353 public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec,
354 byte[] ciphertext, int start, int len)
355 throws GeneralSecurityException {
356
357 if (!KeyUsage.isValid(usage)) {
358 throw new GeneralSecurityException("Invalid key usage number: "
359 + usage);
360 }
361 if (debug) {
362 System.out.println("\nARCFOUR: DECRYPT using key usage = " + usage);
363 }
364
365 // compute K1
366 byte[] k1 = new byte[baseKey.length];
367 System.arraycopy(baseKey, 0, k1, 0, baseKey.length);
368
369 // get the salt using key usage
370 byte[] salt = getSalt(usage);
371
372 // compute K2 using K1
373 byte[] k2 = getHmac(k1, salt);
374
375 // compute K3 using K2 and checksum
376 byte[] checksum = new byte[hashSize];
377 System.arraycopy(ciphertext, start, checksum, 0, hashSize);
378 byte[] k3 = getHmac(k2, checksum);
379
380 // Decrypt [confounder | plaintext ] (without checksum)
381 Cipher cipher = Cipher.getInstance("ARCFOUR");
382 SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR");
383 cipher.init(Cipher.DECRYPT_MODE, secretKey);
384 byte[] plaintext = cipher.doFinal(ciphertext, start+hashSize,
385 len-hashSize);
386
387 // Verify checksum
388 byte[] calculatedHmac = getHmac(k2, plaintext);
389 if (debug) {
390 traceOutput("calculated Hmac", calculatedHmac, 0,
391 calculatedHmac.length);
392 traceOutput("message Hmac", ciphertext, 0,
393 hashSize);
394 }
395 boolean cksumFailed = false;
396 if (calculatedHmac.length >= hashSize) {
397 for (int i = 0; i < hashSize; i++) {
398 if (calculatedHmac[i] != ciphertext[i]) {
399 cksumFailed = true;
400 System.err.println("Checksum failed !");
401 break;
402 }
403 }
404 }
405 if (cksumFailed) {
406 throw new GeneralSecurityException("Checksum failed");
407 }
408
409 // Get rid of confounder
410 // [ confounder | plaintext ]
411 byte[] output = new byte[plaintext.length - confounderSize];
412 System.arraycopy(plaintext, confounderSize, output, 0, output.length);
413
414 return output;
415 }
416
417 /**
418 * Decrypts data using specified key and initial vector.
419 * @param baseKey encryption key to use
420 * @param ciphertext encrypted data to be decrypted
421 * @param usage ignored
422 */
423 public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec,
424 byte[] ciphertext, int start, int len, byte[] seqNum)
425 throws GeneralSecurityException {
426
427 if (!KeyUsage.isValid(usage)) {
428 throw new GeneralSecurityException("Invalid key usage number: "
429 + usage);
430 }
431 if (debug) {
432 System.out.println("\nARCFOUR: decryptRaw with usage = " + usage);
433 }
434
435 // Derive encryption key for data
436 // Key derivation salt = 0
437 byte[] klocal = new byte[baseKey.length];
438 for (int i = 0; i <= 15; i++) {
439 klocal[i] = (byte) (baseKey[i] ^ 0xF0);
440 }
441 byte[] salt = new byte[4];
442 byte[] kcrypt = getHmac(klocal, salt);
443
444 // need only first 4 bytes of sequence number
445 byte[] sequenceNum = new byte[4];
446 System.arraycopy(seqNum, 0, sequenceNum, 0, sequenceNum.length);
447
448 // new encryption key salted with sequence number
449 kcrypt = getHmac(kcrypt, sequenceNum);
450
451 Cipher cipher = Cipher.getInstance("ARCFOUR");
452 SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR");
453 cipher.init(Cipher.DECRYPT_MODE, secretKey);
454 byte[] output = cipher.doFinal(ciphertext, start, len);
455
456 return output;
457 }
458
459 // get the salt using key usage
460 private byte[] getSalt(int usage) {
461 int ms_usage = arcfour_translate_usage(usage);
462 byte[] salt = new byte[4];
463 salt[0] = (byte)(ms_usage & 0xff);
464 salt[1] = (byte)((ms_usage >> 8) & 0xff);
465 salt[2] = (byte)((ms_usage >> 16) & 0xff);
466 salt[3] = (byte)((ms_usage >> 24) & 0xff);
467 return salt;
468 }
469
470 // Key usage translation for MS
471 private int arcfour_translate_usage(int usage) {
472 switch (usage) {
473 case 3: return 8;
474 case 9: return 8;
475 case 23: return 13;
476 default: return usage;
477 }
478 }
479
480}