001: /*
002: * @(#)Record.java 1.23 02/07/24 @(#)
003: *
004: * Copyright (c) 2000-2002 Sun Microsystems, Inc. All rights reserved.
005: * PROPRIETARY/CONFIDENTIAL
006: * Use is subject to license terms.
007: */
008:
009: package com.sun.portal.kssl;
010:
011: import java.io.InputStream;
012: import java.io.DataInputStream;
013: import java.io.OutputStream;
014: import java.io.IOException;
015: import com.sun.portal.ksecurity.MessageDigest;
016: import com.sun.portal.ksecurity.SecretKey;
017: import com.sun.portal.ksecurity.KeyBuilder;
018:
019: /**
020: * Implements an SSL record layer that sits atop a TCP connection
021: * and beneath the user-visible interface to an SSL socket. It
022: * maintains all the state information necessary to encode/decode
023: * application data.
024: */
025: class Record {
026: /*
027: * TODO: We should try to avoid multiple buffer copies by defining a
028: * new message class that allocates a large enough buffer to begin with
029: * so new headers can be prepended and MAC/padding can be appended by
030: * simple pointer manipulation.
031: */
032:
033: /*
034: * SSLRecord types: CCS (Change Cipher Spec), ALRT (Alert),
035: * HNDSHK (Handshake) and APP (Application Data)
036: */
037: /** Change Cipher Spec (20). */
038: static final byte CCS = 20;
039: /** Alert (21). */
040: static final byte ALRT = 21;
041: /** Handshake (22). */
042: static final byte HNDSHK = 22;
043: /** Application data (23). */
044: static final byte APP = 23;
045:
046: // There are two severity levels for alerts
047: /** Warning severity level for alerts (1). */
048: static final byte WARNING = 1;
049: /** Fatal severity level for alerts (2). */
050: static final byte FATAL = 2;
051:
052: // Next, we have various Alert types
053: /** Close notification alert type (0). */
054: static final byte CLOSE_NTFY = 0;
055: /** Unexpected message alert type (10). */
056: static final byte UNEXP_MSG = 10;
057: /** Bad MAC alert type (20). */
058: static final byte BAD_MAC = 20;
059: // static final byte DECOMP_FAIL = 30; we do not support compression
060: /** Handshake failure alert type (40). */
061: static final byte HNDSHK_FAIL = 40;
062: /** No certificate found alert type (41). */
063: static final byte NO_CERT = 41;
064: /** Bad certificate alert type (42). */
065: static final byte BAD_CERT = 42;
066: /** Unsupported certificate alert type (43). */
067: static final byte UNSUP_CERT = 43;
068: /** Certificate revoked alert type (44). */
069: static final byte CERT_REVKD = 44;
070: /** Certificate expired alaert type (45). */
071: static final byte CERT_EXPRD = 45;
072: /** Unknown certificate feature alert type (46). */
073: static final byte CERT_UNKWN = 46;
074: /** Bad parameter alert type (47). */
075: static final byte BAD_PARAM = 47;
076:
077: /*
078: * Possible roles for this SSL record layer (client or server).
079: */
080: /** Server role for SSL record layout (0). */
081: static final byte SERVER = 0;
082: /** Client role for SSL record layout (1). */
083: static final byte CLIENT = 1;
084:
085: /**
086: * PAD1 is a 48-byte array filled with 0x36. It is used in
087: * MAC computations.
088: */
089: static final byte[] PAD1 = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
090: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
091: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
092: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
093: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
094: 0x36, 0x36 };
095:
096: /**
097: * PAD2 is a 48-byte array filled with 0x5c. It is used in
098: * MAC computations.
099: */
100: static final byte[] PAD2 = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
101: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
102: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
103: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
104: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
105: 0x5c, 0x5c };
106: /** Size of record header */
107: private final int HEADER_SIZE = 5;
108: /** Underlying input stream beneath the record layer. */
109: private InputStream in;
110: /** Underlying output stream beneath the record layer. */
111: private OutputStream out;
112:
113: // Connection state information
114: /** Client MAC seceret. */
115: private byte[] cMACKey;
116: /** Server MAC secret. */
117: private byte[] sMACKey;
118: /** Client write key for bulk encryption. */
119: private byte[] cKey;
120: /** Server write key for bulk encryption. */
121: private byte[] sKey;
122: /** Client write IV for block encryption. */
123: private byte[] cIV;
124: /** Server write IV for block encryption. */
125: private byte[] sIV;
126: /** Client write sequence number. */
127: private long cSeq = (long) 0;
128: /** Server write sequence number. */
129: private long sSeq = (long) 0;
130: /** Current role (either client or server). */
131: private byte role;
132: /** Flag indicating change cipher spec received. */
133: private byte rActive = 0;
134: /** Flag indicating change cipher spec has been sent. */
135: private byte wActive = 0;
136:
137: /** Current digest. */
138: private MessageDigest d = null;
139: /** Length of the digest generated by d. */
140: private byte dLen = 0;
141: /** Length of PAD1/PAD2 used in MACs. */
142: private int padLen = 0;
143: /** Client write cipher. */
144: private Cipher cc = null;
145: /** Server write cipher/client decryptor. */
146: private Cipher sc = null;
147: /** Clients bulk encryption secret key. */
148: private SecretKey cBulkKey = null;
149: /** Servers bulk encryption secret key. */
150: private SecretKey sBulkKey = null;
151: /** The SSL version in one byte (0x30=3.0). */
152: private byte ver;
153: /**
154: * Flag indicating type of cipher (1 for a block
155: * cipher, 0 otherwise).
156: */
157: private byte isBlk = 0;
158: /** Cipher block length. */
159: private int cblk = 1;
160: /** Current input header. */
161: private byte[] inputHeader = new byte[HEADER_SIZE];
162: /** Current input data. */
163: byte[] inputData;
164: /** Offset of the plain text in the input buffer */
165: int plainTextOffset;
166: /** Length of the plain text in the input buffer */
167: int plainTextLength = -1;
168:
169: /**
170: * Creates a new SSL record layer.
171: * <P />
172: * @param who role (either CLIENT or SERVER) of this side in
173: * the SSL negotiation
174: * @param ins input stream belonging to the underlying TCP connection
175: * @param outs output stream belonging to the underlying TCP connection
176: */
177: Record(byte who, InputStream ins, OutputStream outs) {
178: in = ins;
179: out = outs;
180: ver = (byte) 0x30; // TODO: This is hardcoded for now
181: role = who;
182: }
183:
184: /**
185: * Frees record layer data structures.
186: */
187: void destroy() {
188: in = null;
189: out = null;
190: cMACKey = sMACKey = null;
191: cIV = sIV = null;
192: d = null;
193: cc = sc = null;
194: cBulkKey = sBulkKey = null;
195: }
196:
197: /**
198: * Increments the client-side or server-side write sequence
199: * number depending on the specified argument.
200: * <P />
201: * @param who one of CLIENT or SERVER
202: * @exception IOException if the sequence numbers rolls around
203: */
204: private void bump(byte who) throws IOException {
205: if (who == CLIENT) {
206: if (++cSeq == (long) 0)
207: throw new IOException("Clnt seq rolled over");
208: } else {
209: if (++sSeq == (long) 0)
210: throw new IOException("Srvr seq rolled over");
211: }
212: }
213:
214: /**
215: * Chops up a master secret into the client and server MAC secrets,
216: * bulk encryption keys and IVs. Also initializes the Cipher and
217: * MessageDigest objects used in record encoding/decoding.
218: * <P />
219: * @param crand 32-byte random value chosen by the client
220: * @param srand 32-byte random value chosen by the server
221: * @param suite negotiated cipher suite
222: * @param master master secret resulting from the key exchange
223: * @exception Exception if the negotiated cipher suite involves an
224: * unsupported hash or cipher algorithm
225: */
226: void init(byte[] crand, byte[] srand, byte suite, byte[] master)
227: throws Exception {
228: /*
229: * The following should suffice to generate a total of
230: * 16*7 = 112 bytes of key material. 3DES_SHA requires
231: * 2*(20 + 24 + 8) = 104 bytes.
232: */
233: byte[] expansion[] = { { 0x41 }, // 'A'
234: { 0x42, 0x42 }, // 'BB'
235: { 0x43, 0x43, 0x43 }, // 'CCC'
236: { 0x44, 0x44, 0x44, 0x44 }, // 'DDDD'
237: { 0x45, 0x45, 0x45, 0x45, 0x45 }, // 'EEEEE'
238: { 0x46, 0x46, 0x46, 0x46, 0x46, 0x46 }, // 'FFFFFF'
239: { 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47 }, // 'GGGGGGG'
240: };
241:
242: /*
243: * The actual size of our computed key block is the closest
244: * 16-byte multiple and depends on the choice of hashing
245: * and encryption algorithms.
246: */
247: int keyMat = 5;
248: //Support for new Ciphers
249: int ivSize = 0;
250: switch (suite) {
251: case CipherSuites.ARCFOUR_128_MD5:
252: keyMat = 16;
253: d = MessageDigest.getInstance(MessageDigest.ALG_MD5, false);
254: dLen = 16;
255: padLen = 48;
256: break;
257:
258: // fall through
259: case CipherSuites.ARCFOUR_40_MD5:
260: d = MessageDigest.getInstance(MessageDigest.ALG_MD5, false);
261: dLen = 16;
262: padLen = 48;
263: break;
264:
265: case CipherSuites.ARCFOUR_128_SHA:
266: keyMat = 16;
267: d = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
268: dLen = 20;
269: padLen = 40;
270: break;
271:
272: //Support for new Ciphers
273: case CipherSuites.DES_CBC_SHA:
274: ivSize = 8;
275: keyMat = 8;
276: d = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
277: dLen = 20;
278: padLen = 40;
279: break;
280:
281: case CipherSuites.TRIPLEDES_CBC_SHA:
282: ivSize = 8;
283: keyMat = 24;
284: d = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
285: dLen = 20;
286: padLen = 40;
287: break;
288:
289: default:
290: throw new RuntimeException("Unsupported suite");
291: }
292:
293: /*
294: * key_block =
295: * MD5(master + SHA('A' + master +
296: * ServerHello.random + ClientHello.random)) +
297: * MD5(master + SHA('BB' + master +
298: * ServerHello.random + ClientHello.random)) +
299: * MD5(master + SHA('CCC' + master +
300: * ServerHello.random + ClientHello.random)) +
301: * [..]
302: *
303: * We set
304: * tmp = master + ServerHello.random + ClientHello.random
305: */
306: byte[] tmp = new byte[master.length + srand.length
307: + crand.length];
308: System.arraycopy(master, 0, tmp, 0, master.length);
309: System.arraycopy(srand, 0, tmp, master.length, srand.length);
310: System.arraycopy(crand, 0, tmp, (master.length + srand.length),
311: crand.length);
312: /*
313: * Key block size is the closest 16-byte multiple larger than
314: * 2x(hash_size + key_material + IV_size)
315: * int keyMat = CipherSuite.cipherList[
316: * CipherSuite.suiteList[suite][2]][2];
317: * int ivSize = CipherSuite.cipherList[
318: * CipherSuite.suiteList[suite][2]][4];
319: */
320: //Support for new Ciphers
321: //int ivSize = 0; // stream ciphers do not use IVs
322: int i = (dLen + keyMat + ivSize) << 1;
323: byte[] kBlk = new byte[((i + 15) >>> 4) << 4];
324: MessageDigest md = MessageDigest.getInstance(
325: MessageDigest.ALG_MD5, false);
326: MessageDigest sd = MessageDigest.getInstance(
327: MessageDigest.ALG_SHA, false);
328:
329: for (i = 0; i < (kBlk.length >>> 4); i++) {
330: md.update(master, 0, master.length);
331: sd.update(expansion[i], 0, expansion[i].length);
332: byte[] res = new byte[20];
333: sd.doFinal(tmp, 0, tmp.length, res, 0);
334: md.doFinal(res, 0, 20, kBlk, i << 4);
335: }
336:
337: // At this point, we have a key block in kBlk. Chop it up!
338: cMACKey = new byte[dLen];
339: sMACKey = new byte[dLen];
340: System.arraycopy(kBlk, 0, cMACKey, 0, cMACKey.length);
341: System.arraycopy(kBlk, cMACKey.length, sMACKey, 0,
342: sMACKey.length);
343:
344: cKey = new byte[keyMat];
345: sKey = new byte[keyMat];
346: System
347: .arraycopy(kBlk, 2 * cMACKey.length, cKey, 0,
348: cKey.length);
349: System.arraycopy(kBlk, (2 * cMACKey.length + cKey.length),
350: sKey, 0, sKey.length);
351:
352: cIV = new byte[ivSize];
353: sIV = new byte[ivSize];
354: if (suite == CipherSuites.ARCFOUR_128_MD5
355: || suite == CipherSuites.ARCFOUR_128_SHA) {
356: /*
357: * NOTE: We know ivSze is always zero for ARCFOUR cipher suites
358: * so this is commented out. It wasn't removed in case we need
359: * to add support for DES or another block cipher.
360: * if (ivSize != 0) {
361: * // bulk encryption uses a block cipher, so initialize IVs
362: * System.arraycopy(kBlk, 2*(cMACKey.length + cKey.length),
363: * cIV, 0, ivSize);
364: * System.arraycopy(kBlk, 2*(cMACKey.length + cKey.length)
365: * + ivSize, sIV, 0, ivSize);
366: * }
367: */
368: }
369: //Support for new Ciphers
370: else if ((suite == CipherSuites.DES_CBC_SHA)
371: || (suite == CipherSuites.TRIPLEDES_CBC_SHA)) {
372: if (ivSize != 0) {
373: // bulk encryption uses a block cipher, so initialize IVs
374: System.arraycopy(kBlk,
375: 2 * (cMACKey.length + cKey.length), cIV, 0,
376: ivSize);
377: System.arraycopy(kBlk, 2
378: * (cMACKey.length + cKey.length) + ivSize, sIV,
379: 0, ivSize);
380: }
381: } else {
382: /*
383: * TODO: The only other option is ARCFOUR_40_MD5
384: *
385: * Expand the keys for exportable cipher suites
386: * final_client_write_key = MD5(client_write_key +
387: * ClientHello.random +
388: * ServerHello.random);
389: * final_server_write_key = MD5(server_write_key +
390: * ServerHello.random +
391: * ClientHello.random);
392: */
393: byte[] res = new byte[16];
394: md.update(cKey, 0, cKey.length);
395: md.update(crand, 0, crand.length);
396: md.doFinal(srand, 0, srand.length, res, 0);
397:
398: /*
399: * NOTE: For both ARCFOUR_128_MD5 and ARCFOUR_40_MD5,
400: * expanded key is 16
401: */
402: byte[] fcKey = new byte[16];
403: System.arraycopy(res, 0, fcKey, 0, fcKey.length);
404:
405: md.update(sKey, 0, sKey.length);
406: md.update(srand, 0, srand.length);
407: md.doFinal(crand, 0, crand.length, res, 0);
408: byte[] fsKey = new byte[fcKey.length];
409: System.arraycopy(res, 0, fsKey, 0, fsKey.length);
410:
411: cKey = fcKey;
412: sKey = fsKey;
413:
414: /*
415: * ... and compute IVs in a special way
416: * client_write_IV = MD5(ClientHello.random + ServerHello.random)
417: * server_write_IV = MD5(ServerHello.random + ClientHello.random)
418: *
419: * NOTE: We know for the chosen ciphersuites, ivSize is zero
420: * so this code is commented out for now
421: */
422: //Support for new Ciphers
423: if (ivSize != 0) {
424: md.update(crand, (short) 0, (short) crand.length);
425: md.doFinal(srand, (short) 0, (short) srand.length, res,
426: (short) 0);
427: System.arraycopy(res, 0, cIV, 0, ivSize);
428:
429: md.update(srand, (short) 0, (short) srand.length);
430: md.doFinal(crand, (short) 0, (short) crand.length, res,
431: (short) 0);
432: System.arraycopy(res, 0, sIV, 0, ivSize);
433: }
434:
435: }
436:
437: /*
438: * Now initialize the ciphers and keys. FOr now this is always
439: * ARCFOUR and we comment out support for other ciphers.
440: */
441: if (suite == CipherSuites.DES_CBC_SHA) {
442: cBulkKey = (SecretKey) KeyBuilder.buildKey(
443: KeyBuilder.TYPE_DES, (short) (cKey.length << 3),
444: false);
445: cBulkKey.setKey(cKey, (short) 0);
446: cc = Cipher.getInstance(Cipher.ALG_DES, false);
447: cc.init(cBulkKey, Cipher.MODE_ENCRYPT, cIV, 0, cIV.length);
448:
449: sBulkKey = (SecretKey) KeyBuilder.buildKey(
450: KeyBuilder.TYPE_DES, (short) (sKey.length << 3),
451: false);
452: sBulkKey.setKey(sKey, (short) 0);
453: sc = Cipher.getInstance(Cipher.ALG_DES, false);
454: sc.init(sBulkKey, Cipher.MODE_DECRYPT, sIV, 0, sIV.length);
455:
456: cblk = 8;
457: isBlk = 1;
458:
459: } else if (suite == CipherSuites.TRIPLEDES_CBC_SHA) {
460: cBulkKey = (SecretKey) KeyBuilder.buildKey(
461: KeyBuilder.TYPE_TRIPLEDES,
462: (short) (cKey.length << 3), false);
463: cBulkKey.setKey(cKey, (short) 0);
464: cc = Cipher.getInstance(Cipher.ALG_TRIPLEDES, false);
465: cc.init(cBulkKey, Cipher.MODE_ENCRYPT, cIV, 0, cIV.length);
466:
467: sBulkKey = (SecretKey) KeyBuilder.buildKey(
468: KeyBuilder.TYPE_TRIPLEDES,
469: (short) (sKey.length << 3), false);
470: sBulkKey.setKey(sKey, (short) 0);
471: sc = Cipher.getInstance(Cipher.ALG_TRIPLEDES, false);
472: sc.init(sBulkKey, Cipher.MODE_DECRYPT, sIV, 0, sIV.length);
473:
474: cblk = 8;
475: isBlk = 1;
476:
477: } else {
478: cBulkKey = (SecretKey) KeyBuilder.buildKey(
479: KeyBuilder.TYPE_ARCFOUR,
480: (short) (cKey.length << 3), false);
481: cBulkKey.setKey(cKey, (short) 0);
482: cc = Cipher.getInstance(Cipher.ALG_ARCFOUR, false);
483: cc.init(cBulkKey, Cipher.MODE_ENCRYPT);
484:
485: sBulkKey = (SecretKey) KeyBuilder.buildKey(
486: KeyBuilder.TYPE_ARCFOUR,
487: (short) (sKey.length << 3), false);
488: sBulkKey.setKey(sKey, (short) 0);
489: sc = Cipher.getInstance(Cipher.ALG_ARCFOUR, false);
490: sc.init(sBulkKey, Cipher.MODE_DECRYPT);
491: cblk = 1;
492: isBlk = 0;
493:
494: }
495:
496: }
497:
498: /**
499: * Computes the MAC for an SSLCompressed structure.
500: * <P />
501: * @param who one of CLIENT or SERVER to indicate whether
502: * client-side or server-side MAC secrets and sequence numbers
503: * ought to be used in the MAC computation
504: * @param type SSL record type of the SSLCompressed structure
505: * @param buf byte array containing the SSLCompressed fragment
506: * @param off starting offset of the fragment in buf
507: * @param len length of the fragment
508: * @return a byte array containing the MAC
509: */
510: private synchronized byte[] getMAC(byte who, byte type, byte[] buf,
511: int off, int len) {
512: /*
513: * MAC = hash(MAC_secret + PAD2 +
514: * hash(MAC_secret + PAD1 + seq_num + type + len +
515: * compressed_fragment));
516: */
517: // Compute the inner hash first
518: byte[] mKey; // MAC_write_secret for use in outer hash
519: byte[] res = null;
520: if (who == CLIENT) {
521: d.update(cMACKey, 0, cMACKey.length);
522: d.update(PAD1, 0, padLen);
523: res = Utils.longToBytes(cSeq);
524: d.update(res, 0, res.length);
525:
526: mKey = cMACKey; // save the right MAC_write_secret
527: } else {
528: d.update(sMACKey, 0, sMACKey.length);
529: d.update(PAD1, 0, padLen);
530: res = Utils.longToBytes(sSeq);
531: d.update(res, 0, res.length);
532: mKey = sMACKey; // save the right MAC_write_secret
533: }
534:
535: res = new byte[3];
536: res[0] = type;
537: res[1] = (byte) (len >>> 8);
538: res[2] = (byte) (len & 0xff);
539: d.update(res, 0, res.length);
540: byte[] mac = new byte[d.getLength()];
541: d.doFinal(buf, off, len, mac, 0);
542: // Now, the outer hash
543: d.update(mKey, 0, mKey.length);
544: d.update(PAD2, 0, padLen);
545: byte[] val = new byte[mac.length];
546: d.doFinal(mac, 0, mac.length, val, 0);
547: return val;
548: }
549:
550: /**
551: * Converts a byte array containing an SSLPlaintext structure
552: * to the corresponding SSLCiphertext structure and sends it on
553: * the underlying OutputStream. The process typically involves
554: * the addition of a MAC followed by encryption.
555: * <P />
556: * @param ptxt byte array containing SSLPlaintext
557: * @return the number of bytes written to the OutputStream
558: */
559: private byte[] encode(byte[] ptxt) throws IOException {
560: /*
561: * Since we only support NULL compression, SSLPlaintext
562: * the same as SSLCompressed.
563: */
564: byte[] fragNMAC = null; // fragment plus MAC
565:
566: if (d != null) {
567: fragNMAC = new byte[ptxt.length - 5 + dLen];
568: System.arraycopy(ptxt, 5, fragNMAC, 0, ptxt.length - 5);
569: System.arraycopy(getMAC(role, ptxt[0], ptxt, 5,
570: (ptxt.length - 5)), 0, fragNMAC, (ptxt.length - 5),
571: dLen);
572: } else {
573: fragNMAC = new byte[ptxt.length - 5];
574: System.arraycopy(ptxt, 5, fragNMAC, 0, ptxt.length - 5);
575: }
576:
577: // ... now we need to encrypt fragNMAC and possibly update IVs
578: byte[] efragNMAC = null;
579: if (cc != null) {
580: try {
581: /*
582: * NOTE: For now, we always have a stream cipher so this
583: * is commented out.
584: */
585: // We have a stream cipher
586: //Support for new Ciphers
587: /*efragNMAC = fragNMAC;
588: cc.update(fragNMAC, 0, fragNMAC.length, efragNMAC, 0);*/
589: if (isBlk == 1) {
590: // Compute total padding
591: int pLen = cblk - (fragNMAC.length % cblk);
592: byte[] tmp = new byte[fragNMAC.length + pLen];
593: System.arraycopy(fragNMAC, 0, tmp, 0,
594: fragNMAC.length);
595: for (int i = fragNMAC.length; i < tmp.length; i++) {
596: tmp[i] = (byte) (pLen - 1);
597: }
598:
599: efragNMAC = new byte[tmp.length];
600: cc.update(tmp, (short) 0, (short) tmp.length,
601: efragNMAC, (short) 0);
602:
603: } else {
604:
605: /*
606: * NOTE: For now, we always have a stream cipher so this
607: * is commented out.
608: */
609: // We have a stream cipher
610: efragNMAC = fragNMAC;
611: cc.update(fragNMAC, 0, fragNMAC.length, efragNMAC,
612: 0);
613: }
614: } catch (Exception e) {
615: throw new IOException("Encode caught " + e);
616: }
617: } else {
618: // Cipher algorithm is NULL
619: efragNMAC = fragNMAC;
620: }
621:
622: /*
623: * Utils.logln(Utils.LOG_DEBUG,
624: * "efragNMAC: " + Utils.hexEncode(efragNMAC));
625: */
626:
627: byte[] rec = new byte[efragNMAC.length + 5];
628: System.arraycopy(ptxt, 0, rec, 0, 3);
629: rec[3] = (byte) (efragNMAC.length >>> 8);
630: rec[4] = (byte) (efragNMAC.length & 0xff);
631: System.arraycopy(efragNMAC, 0, rec, 5, efragNMAC.length);
632: // We have encoded one more record, increment seq number
633: bump(role);
634:
635: return rec;
636: }
637:
638: /**
639: * Converts a byte array containing an SSLCiphertext structure
640: * to the corresponding SSLPlaintext structure. The process
641: * typically involves decryption followed by MAC verification
642: * and MAC stripping.
643: *
644: * @return Length of the decrypted data in the input buffer.
645: *
646: * @exception IOException if a problem is encountered during decryption
647: * or MAC verification
648: */
649: private int decode() throws IOException {
650: int padLen = 0;
651: int dataLen;
652:
653: if (sc != null) {
654: // Cipher algorithm is not NULL (ctxt needs to be decrypted)
655:
656: try {
657: /*
658: * NOTE: For now, we only have ARCFOUR, a stream cipher
659: * so there is no IV or padding that a block cipher has.
660: *
661: * Otherwise we would have to find the plaintext offset
662: * after we decrypt.
663: */
664:
665: // We have a stream cipher (NOTE: assuming CLIENT role)
666: // We can decode in place w/o using additional memory
667: //Support for new Ciphers
668: sc.update(inputData, 0, inputData.length, inputData, 0);
669: if (isBlk == 1) {
670: padLen = (inputData[inputData.length - 1] & 0xff) + 1;
671: if ((padLen < 0) || (padLen > cblk)) {
672: throw new IOException("bad padding <padLen="
673: + padLen + ">");
674: }
675: }
676:
677: } catch (Exception e) {
678: throw new IOException("Decode caught " + e);
679: }
680: }
681:
682: dataLen = inputData.length - dLen - padLen;
683:
684: if (d != null) {
685: byte[] expMAC = null; // expected MAC
686: expMAC = getMAC((byte) (1 - role), inputHeader[0], // the type
687: inputData, 0, dataLen);
688: if (!Utils.byteMatch(expMAC, 0, inputData,
689: (inputData.length - padLen - dLen), dLen)) {
690: alert(FATAL, BAD_MAC);
691: throw new IOException("Bad MAC");
692: }
693: }
694:
695: // We have received one more record, bump peer's write sequence number.
696: bump((byte) (1 - role));
697:
698: return dataLen;
699: }
700:
701: /**
702: * Reads and returns a record (including the 5-byte header) of
703: * the specified type. If the caller asks for application data
704: * and a close_notify warning alert is found as the next available
705: * record, this method returns null to signal the end of the
706: * input stream.
707: *
708: * @param type desired SSL record type
709: *
710: * @exception IOException if an unexpected record type or SSL alert is
711: * found in the underlying sockets input stream
712: */
713: void rdRec(byte type) throws IOException {
714: inputData = null;
715: plainTextOffset = 0;
716: plainTextLength = -1;
717:
718: //long startTime = System.currentTimeMillis();
719: rdRec();
720: //System.out.println("Time taken to read record -> " + (System.currentTimeMillis() - startTime));
721:
722: if ((inputData == null) || (inputHeader[0] == type)) {
723: // success
724: return;
725: }
726:
727: plainTextLength = -1;
728:
729: switch (inputHeader[0]) {
730: case CCS:
731: // Can change_cipher_spec can be passed to handshake clients?
732: // if (type == HNDSHK) return r; // fall through otherwise
733: case HNDSHK:
734: case APP:
735: default:
736: alert(FATAL, UNEXP_MSG);
737: throw new IOException("Unexpected SSL record, type: "
738: + inputHeader[0]);
739:
740: case ALRT:
741: // An Alert record needs to be atleast 2 bytes of data
742: if (inputData.length < 2) {
743: throw new IOException("Bad alert length");
744: }
745:
746: if ((inputData[0] == WARNING)
747: && (inputData[1] == CLOSE_NTFY) && (type == APP)) {
748: /*
749: * We got a close_notify warning
750: * We do not send a close notify here, but in
751: * closing the stream we perform
752: * alert(WARNING, CLOSE_NTFY).
753: */
754: return; // signal end of InputStream
755: }
756:
757: if ((inputData[0] < WARNING) || (inputData[0] > FATAL)) {
758: throw new IOException("Bad alert level");
759: }
760:
761: throw new IOException("Alert (" + inputData[0] + ","
762: + inputData[1] + ")");
763: }
764: }
765:
766: /**
767: * Returns the next record read from the record layer (the 5-byte
768: * SSL record header is included).
769: *
770: * @exception IOException if an I/O error occurs
771: */
772: private void rdRec() throws IOException {
773: int hlen;
774: int b;
775: int rlen;
776: int cnt;
777:
778: b = in.read(inputHeader, 0, 1);
779: if (b == -1) {
780: /*
781: * Peer closed SSL connection without close_notify
782: */
783: return;
784: }
785: hlen = inputHeader.length;
786: cnt = 1;
787:
788: while (cnt < hlen) {
789: // long startTime = System.currentTimeMillis();
790: b = in.read(inputHeader, cnt, hlen - cnt);
791: // System.out.println("Inside while. Time taken -> " + (System.currentTimeMillis() - startTime));
792: if (b == -1) {
793: throw new IOException(
794: "SSL connection ended abnormally "
795: + "while reading record header");
796: }
797: cnt += b;
798: }
799:
800: /* System.out.println("\nHeader sent from JSS Server: ");
801: for(int i=0; i < 5; ++i) {
802: System.out.println("Header[" + i + "] -> " + inputHeader[i]);
803: }
804: */
805: // Check record type and version
806: if ((inputHeader[0] < CCS) || (inputHeader[0] > APP)
807: || (inputHeader[1] != (byte) (ver >>> 4))
808: || (inputHeader[2] != (byte) (ver & 0x0f))) {
809: alert(FATAL, UNEXP_MSG);
810: throw new IOException("Bad record type (" + inputHeader[0]
811: + ") or version (" + inputHeader[1] + "."
812: + inputHeader[2] + ")");
813: }
814:
815: rlen = ((inputHeader[3] & 0xff) << 8) + (inputHeader[4] & 0xff);
816: inputData = new byte[rlen];
817: cnt = 0;
818: b = 0;
819: while (cnt < rlen) {
820: //long startTime = System.currentTimeMillis();
821: b = in.read(inputData, cnt, rlen - cnt);
822: //System.out.println("Time taken to read data -> " + (System.currentTimeMillis() - startTime));
823: if (b == -1) {
824: throw new IOException(
825: "SSL connection ended abnormally "
826: + "after reading record byte "
827: + (cnt + inputHeader.length));
828: }
829:
830: cnt += b;
831: }
832:
833: if (rActive == 1) {
834: //long startTime = System.currentTimeMillis();
835: plainTextLength = decode();
836: // System.out.println("Time taken to decode data -> " + (System.currentTimeMillis() - startTime));
837: } else {
838: plainTextLength = rlen;
839: }
840:
841: if (inputHeader[0] == CCS) {
842: rActive = 1;
843: }
844: }
845:
846: /**
847: * Writes an SSL record to the underlying socket's output stream.
848: * <P />
849: * @param type record type (one of CCS, ALRT, HNDSHK or APP)
850: * @param buf byte array containing the record body (i.e. everything
851: * but the 5-byte header)
852: * @param off starting offset of the record body inside buf
853: * @param len length of the record body, the maximum is 2^14 +2048 as
854: * defined by RFC 2246
855: * @exception IOException if an I/O error occurs.
856: */
857: /*
858: * REVISIT: We currently do not handle fragmentation and only
859: * increment sequence numbers when encoding/decoding are
860: * turned on. Is it necessary to maintain these counts for
861: * handshake messages as well???
862: */
863: void wrRec(byte type, byte[] buf, int off, int len)
864: throws IOException {
865: // Create a new byte array with room for the header
866: byte[] rec = new byte[len + 5];
867: // Fill the record header with type, version and length
868: rec[0] = type;
869: rec[1] = (byte) (ver >>> 4);
870: rec[2] = (byte) (ver & 0x0f);
871: rec[3] = (byte) (len >>> 8);
872: rec[4] = (byte) (len & 0xff);
873:
874: /* System.out.println("\nHeader sent from KSSL client: ");
875: for(int i=0; i < 5; ++i) {
876: System.out.println("Header[" + i + "] -> " + rec[i]);
877: }
878: */
879: // Fill the rest of the record
880: System.arraycopy(buf, off, rec, 5, len);
881: if (wActive == 1) {
882: out.write(encode(rec));
883: } else {
884: out.write(rec);
885: }
886: if (type == CCS)
887: wActive = 1;
888: out.flush();
889: }
890:
891: /**
892: * Sends an alert message of the specified level and type to the SSL peer.
893: * <p />
894: * @param level one of WARNING or FATAL)
895: * @param type one of CLOSE_NTFY, UNEXP_MSG, BAD_MAC, DECOMP_FAIL,
896: * HNDSHK_FAIL, NO_CERT, BAD_CERT, UNSUP_CERT, CERT_REVKD,
897: * CERT_EXPRD, CERT_UNKWN, BAD_PARAM
898: */
899: public void alert(byte level, byte type) {
900: byte[] tmp = new byte[2];
901: tmp[0] = level;
902: tmp[1] = type;
903:
904: try {
905: wrRec(ALRT, tmp, 0, 2);
906: } catch (IOException e) {
907: // ignore, we do not want to step on the real error
908: }
909: }
910: }
|