0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package com.sun.midp.ssl;
0028:
0029: import java.io.InputStream;
0030: import java.io.OutputStream;
0031: import java.io.IOException;
0032: import java.io.InterruptedIOException;
0033:
0034: import com.sun.midp.log.Logging;
0035: import com.sun.midp.log.LogChannels;
0036:
0037: import com.sun.midp.crypto.*;
0038: import com.sun.midp.pki.*;
0039:
0040: /**
0041: * Implements an SSL record layer that sits atop a TCP connection
0042: * and beneath the user-visible interface to an SSL socket. It
0043: * maintains all the state information necessary to encode/decode
0044: * application data.
0045: */
0046: class Record {
0047: /*
0048: * IMPL_NOTE: We should try to avoid multiple buffer copies by defining a
0049: * new message class that allocates a large enough buffer to begin with
0050: * so new headers can be prepended and MAC/padding can be appended by
0051: * simple pointer manipulation.
0052: */
0053:
0054: /*
0055: * SSLRecord types: CCS (Change Cipher Spec), ALRT (Alert),
0056: * HNDSHK (Handshake) and APP (Application Data)
0057: */
0058: /** Change Cipher Spec (20). */
0059: static final byte CCS = 20;
0060: /** Alert (21). */
0061: static final byte ALRT = 21;
0062: /** Handshake (22). */
0063: static final byte HNDSHK = 22;
0064: /** Application data (23). */
0065: static final byte APP = 23;
0066:
0067: // There are two severity levels for alerts
0068: /** Warning severity level for alerts (1). */
0069: static final byte WARNING = 1;
0070: /** Fatal severity level for alerts (2). */
0071: static final byte FATAL = 2;
0072:
0073: // Next, we have various Alert types
0074: /** Close notification alert type (0). */
0075: static final byte CLOSE_NTFY = 0;
0076: /** Unexpected message alert type (10). */
0077: static final byte UNEXP_MSG = 10;
0078: /** Bad MAC alert type (20). */
0079: static final byte BAD_MAC = 20;
0080: // static final byte DECOMP_FAIL = 30; we do not support compression
0081: /** Handshake failure alert type (40). */
0082: static final byte HNDSHK_FAIL = 40;
0083: /** No certificate found alert type (41). */
0084: static final byte NO_CERT = 41;
0085: /** Bad certificate alert type (42). */
0086: static final byte BAD_CERT = 42;
0087: /** Unsupported certificate alert type (43). */
0088: static final byte UNSUP_CERT = 43;
0089: /** Certificate revoked alert type (44). */
0090: static final byte CERT_REVKD = 44;
0091: /** Certificate expired alaert type (45). */
0092: static final byte CERT_EXPRD = 45;
0093: /** Unknown certificate feature alert type (46). */
0094: static final byte CERT_UNKWN = 46;
0095: /** Bad parameter alert type (47). */
0096: static final byte BAD_PARAM = 47;
0097:
0098: /*
0099: * Possible roles for this SSL record layer (client or server).
0100: */
0101: /** Server role for SSL record layout (0). */
0102: static final byte SERVER = 0;
0103: /** Client role for SSL record layout (1). */
0104: static final byte CLIENT = 1;
0105:
0106: /** Size of record header */
0107: private final int HEADER_SIZE = 5;
0108: /** Underlying input stream beneath the record layer. */
0109: private InputStream in;
0110: /** Underlying output stream beneath the record layer. */
0111: private OutputStream out;
0112:
0113: // Connection state information
0114: /** Flag indicating change cipher spec received. */
0115: private byte rActive = 0;
0116: /** Flag indicating change cipher spec has been sent. */
0117: private byte wActive = 0;
0118:
0119: /** The SSL version in one byte (0x30=3.0). */
0120: private byte ver;
0121: /** Current input record header. */
0122: private byte[] inputHeader = new byte[HEADER_SIZE];
0123: /** How many bytes of the record header have been read. */
0124: private int headerBytesRead;
0125: /** How many bytes of the data in the record. */
0126: private int dataLength;
0127: /** How many bytes of the data in the record have been read. */
0128: private int dataBytesRead;
0129: /** Shutdown flag, true if connection has been shutdown. */
0130: private boolean shutdown;
0131:
0132: /** Current input record data. */
0133: byte[] inputData;
0134: /** Length of the plain text in the input buffer */
0135: int plainTextLength;
0136:
0137: /** Records encoder */
0138: private RecordEncoder encoder = null;
0139: /** Records decoder */
0140: private RecordDecoder decoder = null;
0141:
0142: /**
0143: * Creates a new SSL record layer.
0144: *
0145: * @param ins input stream belonging to the underlying TCP connection
0146: * @param outs output stream belonging to the underlying TCP connection
0147: */
0148: Record(InputStream ins, OutputStream outs) {
0149: in = ins;
0150: out = outs;
0151: ver = (byte) 0x30; // IMPL_NOTE: This is hardcoded for now
0152: }
0153:
0154: /**
0155: * Chops up a master secret into the client and server MAC secrets,
0156: * bulk encryption keys and IVs. Also initializes the Cipher and
0157: * MessageDigest objects used in record encoding/decoding.
0158: *
0159: * @param role role (either CLIENT or SERVER) of this side in the SSL
0160: * negotiation
0161: * @param clientRand 32-byte random value chosen by the client
0162: * @param serverRand 32-byte random value chosen by the server
0163: * @param suite negotiated cipher suite
0164: * @param masterSecret master secret resulting from the key exchange
0165: *
0166: * @exception Exception if the negotiated cipher suite involves an
0167: * unsupported hash or cipher algorithm
0168: */
0169: void init(byte role, byte[] clientRand, byte[] serverRand,
0170: byte suite, byte[] masterSecret) throws Exception {
0171: CipherSuiteData data = new CipherSuiteData(suite);
0172: data.generateKeys(clientRand, serverRand, masterSecret);
0173:
0174: // depending on role, we choose corresponding MAC secrets
0175: // and cipher bulk keys for encoder and decoder
0176: byte[] encodeSecret;
0177: byte[] decodeSecret;
0178: SecretKey decodeCipherKey;
0179: SecretKey encodeCipherKey;
0180: if (role == CLIENT) {
0181: encodeSecret = data.getClientMACSecret();
0182: decodeSecret = data.getServerMACSecret();
0183: encodeCipherKey = data.getClientBulkKey();
0184: decodeCipherKey = data.getServerBulkKey();
0185: } else {
0186: encodeSecret = data.getServerMACSecret();
0187: decodeSecret = data.getClientMACSecret();
0188: encodeCipherKey = data.getServerBulkKey();
0189: decodeCipherKey = data.getClientBulkKey();
0190: }
0191:
0192: Cipher encodeCipher = data.getEncodeCipher();
0193: encodeCipher.init(Cipher.ENCRYPT_MODE, encodeCipherKey);
0194: Cipher decodeCipher = data.getDecodeCipher();
0195: decodeCipher.init(Cipher.DECRYPT_MODE, decodeCipherKey);
0196:
0197: encoder = new RecordEncoder(data.getEncodeDigest(),
0198: encodeSecret, data.getPadLength(), encodeCipher);
0199: decoder = new RecordDecoder(data.getDecodeDigest(),
0200: decodeSecret, data.getPadLength(), decodeCipher);
0201: }
0202:
0203: /**
0204: * Reads and returns a record (including the 5-byte header) of
0205: * the specified type. If the caller asks for application data
0206: * and a close_notify warning alert is found as the next available
0207: * record, this method sets plainTextLength to -1 to signal the end of the
0208: * input stream.
0209: *
0210: * @param block if true the method will not return until data is available,
0211: * or end of stream
0212: * @param type desired SSL record type
0213: *
0214: * @exception IOException if an unexpected record type or SSL alert is
0215: * found in the underlying sockets input stream
0216: */
0217: void rdRec(boolean block, byte type) throws IOException {
0218: if (!rdRec(block)) {
0219: return;
0220: }
0221:
0222: if (inputHeader[0] == type) {
0223: // success
0224: return;
0225: }
0226:
0227: // Signal end of stream.
0228: plainTextLength = -1;
0229:
0230: switch (inputHeader[0]) {
0231: case CCS:
0232: // Can change_cipher_spec can be passed to handshake clients?
0233: // if (type == HNDSHK) return r; // fall through otherwise
0234: case HNDSHK:
0235: case APP:
0236: default:
0237: alert(FATAL, UNEXP_MSG);
0238: throw new IOException("Unexpected SSL record, type: "
0239: + inputHeader[0]);
0240:
0241: case ALRT:
0242: // An Alert record needs to be atleast 2 bytes of data
0243: if (inputData.length < 2) {
0244: throw new IOException("Bad alert length");
0245: }
0246:
0247: if ((inputData[0] == WARNING)
0248: && (inputData[1] == CLOSE_NTFY) && (type == APP)) {
0249: /*
0250: * We got a close_notify warning
0251: * Shutdown the connection.
0252: */
0253: shutdownConnection();
0254: return; // signal end of InputStream
0255: }
0256:
0257: if ((inputData[0] < WARNING) || (inputData[0] > FATAL)) {
0258: throw new IOException("Bad alert level");
0259: }
0260:
0261: throw new IOException("Alert (" + inputData[0] + ","
0262: + inputData[1] + ")");
0263: }
0264: }
0265:
0266: /**
0267: * Returns the next record read from the record layer (the 5-byte
0268: * SSL record header is included). Set plainTextLength to length of the
0269: * record or -1 for end of stream.
0270: *
0271: * @param block if true the method will not return until data is available,
0272: * or end of stream
0273: *
0274: * @return true if a record has been read
0275: *
0276: * @exception IOException if an I/O error occurs
0277: */
0278: private boolean rdRec(boolean block) throws IOException {
0279: int b;
0280:
0281: plainTextLength = 0;
0282:
0283: if (!block && in.available() == 0) {
0284: return false;
0285: }
0286:
0287: /*
0288: * This method could have returned last time after reading the
0289: * part of the record, in that case headerBytesRead will not be 0.
0290: */
0291: if (headerBytesRead == 0) {
0292: b = in.read(inputHeader, 0, 1);
0293: if (b == -1) {
0294: /*
0295: * Peer closed SSL connection without close_notify
0296: */
0297: plainTextLength = -1;
0298: return false;
0299: }
0300:
0301: headerBytesRead = 1;
0302: dataBytesRead = 0;
0303: dataLength = 0;
0304: }
0305:
0306: while (headerBytesRead < inputHeader.length) {
0307: if (!block && in.available() == 0) {
0308: return false;
0309: }
0310:
0311: b = in.read(inputHeader, headerBytesRead,
0312: inputHeader.length - headerBytesRead);
0313: if (b == -1) {
0314: throw new IOException(
0315: "SSL connection ended abnormally "
0316: + "while reading record header");
0317: }
0318:
0319: headerBytesRead += b;
0320: }
0321:
0322: /*
0323: * This method could have returned last time after reading the
0324: * header but not all of the data, in that case dataLength
0325: * will not be 0.
0326: */
0327: if (dataLength == 0) {
0328: // Check record type and version
0329: if ((inputHeader[0] < CCS) || (inputHeader[0] > APP)
0330: || (inputHeader[1] != (byte) (ver >>> 4))
0331: || (inputHeader[2] != (byte) (ver & 0x0f))) {
0332: alert(FATAL, UNEXP_MSG);
0333: throw new IOException("Bad record type ("
0334: + inputHeader[0] + ") or version ("
0335: + inputHeader[1] + "." + inputHeader[2] + ")");
0336: }
0337:
0338: dataLength = ((inputHeader[3] & 0xff) << 8)
0339: + (inputHeader[4] & 0xff);
0340: inputData = new byte[dataLength];
0341: }
0342:
0343: while (dataBytesRead < dataLength) {
0344: if (!block && in.available() == 0) {
0345: return false;
0346: }
0347:
0348: b = in.read(inputData, dataBytesRead, dataLength
0349: - dataBytesRead);
0350: if (b == -1) {
0351: throw new IOException(
0352: "SSL connection ended abnormally "
0353: + "after reading record byte "
0354: + (dataBytesRead + headerBytesRead));
0355: }
0356:
0357: dataBytesRead += b;
0358: }
0359:
0360: if (rActive == 1) {
0361: try {
0362: plainTextLength = decoder
0363: .decode(inputHeader, inputData);
0364: } catch (IOException e) {
0365: if (e.getMessage().compareTo("Bad MAC") == 0) {
0366: alert(FATAL, BAD_MAC);
0367: } else {
0368: throw e;
0369: }
0370: }
0371: } else {
0372: plainTextLength = dataBytesRead;
0373: }
0374:
0375: if (inputHeader[0] == CCS) {
0376: rActive = 1;
0377: }
0378:
0379: // start with a new record header next time
0380: headerBytesRead = 0;
0381:
0382: return true;
0383: }
0384:
0385: /**
0386: * Writes an SSL record to the underlying socket's output stream.
0387: *
0388: * @param type record type (one of CCS, ALRT, HNDSHK or APP)
0389: * @param buf byte array containing the record body (i.e. everything
0390: * but the 5-byte header)
0391: * @param off starting offset of the record body inside buf
0392: * @param len length of the record body, the maximum is 2^14 +2048 as
0393: * defined by RFC 2246
0394: *
0395: * @exception IOException if an I/O error occurs.
0396: */
0397: /*
0398: * REVISIT: We currently do not handle fragmentation and only
0399: * increment sequence numbers when encoding/decoding are
0400: * turned on. Is it necessary to maintain these counts for
0401: * handshake messages as well???
0402: */
0403: void wrRec(byte type, byte[] buf, int off, int len)
0404: throws IOException {
0405: byte[] rec;
0406:
0407: if (shutdown) {
0408: throw new IOException("Server has shutdown the connection");
0409: }
0410:
0411: /*
0412: * Create a new byte array with room for the header and
0413: * fill the record header with type, version and length
0414: */
0415: rec = new byte[len + 5];
0416: rec[0] = type;
0417: rec[1] = (byte) (ver >>> 4);
0418: rec[2] = (byte) (ver & 0x0f);
0419: rec[3] = (byte) (len >>> 8);
0420: rec[4] = (byte) (len & 0xff);
0421: // Fill the rest of the record
0422: System.arraycopy(buf, off, rec, 5, len);
0423: if (wActive == 1) {
0424: out.write(encoder.encode(rec));
0425: } else {
0426: out.write(rec);
0427: }
0428: if (type == CCS)
0429: wActive = 1;
0430: }
0431:
0432: /**
0433: * Sends an alert message of the specified level and type to the SSL peer.
0434: *
0435: * @param level one of WARNING or FATAL)
0436: * @param type one of CLOSE_NTFY, UNEXP_MSG, BAD_MAC, DECOMP_FAIL,
0437: * HNDSHK_FAIL, NO_CERT, BAD_CERT, UNSUP_CERT, CERT_REVKD,
0438: * CERT_EXPRD, CERT_UNKWN, BAD_PARAM
0439: */
0440: public void alert(byte level, byte type) {
0441: byte[] tmp = new byte[2];
0442: tmp[0] = level;
0443: tmp[1] = type;
0444:
0445: try {
0446: wrRec(ALRT, tmp, 0, 2);
0447: } catch (IOException e) {
0448: // ignore, we do not want to step on the real error
0449: }
0450: }
0451:
0452: /** Close input stream */
0453: void closeInputStream() {
0454: try {
0455: in.close();
0456: } catch (IOException e) {
0457: // ignore
0458: }
0459: }
0460:
0461: /** Close output stream */
0462: void closeOutputStream() {
0463: try {
0464: out.close();
0465: } catch (IOException e) {
0466: // ignore
0467: }
0468: }
0469:
0470: /**
0471: * Send a close notify and shutdown the TCP connection if needed.
0472: */
0473: public void shutdownConnection() {
0474: if (shutdown) {
0475: return;
0476: }
0477:
0478: alert(Record.WARNING, Record.CLOSE_NTFY);
0479: shutdown = true;
0480: closeOutputStream();
0481: closeInputStream();
0482: }
0483: }
0484:
0485: /**
0486: * All kinds of data related to cipher suite like keys,
0487: * digestes, etc.
0488: */
0489: class CipherSuiteData {
0490: /** Client MAC secret. */
0491: private byte[] clientMACSecret = null;
0492: /** Server MAC secret. */
0493: private byte[] serverMACSecret = null;
0494: /** Client write key for bulk encryption. */
0495: private byte[] clientKey = null;
0496: /** Server write key for bulk encryption. */
0497: private byte[] serverKey = null;
0498: /** Client write IV for block encryption. */
0499: private byte[] clientIV = null;
0500: /** Server write IV for block encryption. */
0501: private byte[] serverIV = null;
0502: /** Clients bulk encryption secret key. */
0503: private SecretKey clientBulkKey = null;
0504: /** Servers bulk encryption secret key. */
0505: private SecretKey serverBulkKey = null;
0506: /** Length of the digest */
0507: private int digestLength = 0;
0508: /** Digest for encoding */
0509: private MessageDigest encodeDigest = null;
0510: /** Digest for decoding */
0511: private MessageDigest decodeDigest = null;
0512: /** Encode cipher */
0513: private Cipher encodeCipher = null;
0514: /** Decode cipher */
0515: private Cipher decodeCipher = null;
0516: /** Length of PAD1/PAD2 used in MACs. */
0517: private int padLength = 0;
0518: /** Cipher suite type */
0519: private byte suiteType = 0;
0520: /** Digest used for keys generation */
0521: private MessageDigest md = null;
0522: /** Digest used for keys generation */
0523: private MessageDigest sd = null;
0524: /** Block of generated keys */
0525: private byte[] keyBlock = null;
0526:
0527: /**
0528: * Constructs CipherSuiteData object
0529: *
0530: * @param suite negotiated cipher suite
0531: *
0532: * @exception Exception if the negotiated cipher suite involves an
0533: * unsupported hash or cipher algorithm
0534: */
0535: CipherSuiteData(byte suite) throws Exception {
0536: suiteType = suite;
0537:
0538: /*
0539: * The actual size of our computed key block is the closest
0540: * 16-byte multiple and depends on the choice of hashing
0541: * and encryption algorithms.
0542: */
0543: int keyMaterial = 5;
0544:
0545: switch (suite) {
0546: case Handshake.ARCFOUR_128_MD5:
0547: keyMaterial = 16;
0548: // fall through
0549: case Handshake.ARCFOUR_40_MD5:
0550: padLength = 48;
0551: encodeDigest = MessageDigest.getInstance("MD5");
0552: break;
0553:
0554: case Handshake.ARCFOUR_128_SHA:
0555: keyMaterial = 16;
0556: padLength = 40;
0557: encodeDigest = MessageDigest.getInstance("SHA-1");
0558: break;
0559:
0560: default:
0561: throw new Exception("Unsupported suite");
0562: }
0563:
0564: decodeDigest = (MessageDigest) encodeDigest.clone();
0565: digestLength = encodeDigest.getDigestLength();
0566:
0567: encodeCipher = Cipher.getInstance("ARC4");
0568: decodeCipher = Cipher.getInstance("ARC4");
0569:
0570: /*
0571: * Key block size is the closest 16-byte multiple larger than
0572: * 2x(hash_size + key_material + IV_size)
0573: * int keyMaterial = CipherSuite.cipherList[
0574: * CipherSuite.suiteList[suite][2]][2];
0575: * int ivSize = CipherSuite.cipherList[
0576: * CipherSuite.suiteList[suite][2]][4];
0577: */
0578:
0579: int ivSize = 0; // stream ciphers do not use IVs
0580:
0581: int blockSize = (digestLength + keyMaterial + ivSize) << 1;
0582: blockSize = ((blockSize + 15) >>> 4) << 4;
0583: keyBlock = new byte[blockSize];
0584:
0585: clientMACSecret = new byte[digestLength];
0586: serverMACSecret = new byte[digestLength];
0587: clientKey = new byte[keyMaterial];
0588: serverKey = new byte[keyMaterial];
0589: clientIV = new byte[ivSize];
0590: serverIV = new byte[ivSize];
0591:
0592: md = MessageDigest.getInstance("MD5");
0593: sd = MessageDigest.getInstance("SHA-1");
0594: }
0595:
0596: /**
0597: * Chops up a master secret into the client and server MAC secrets,
0598: * bulk encryption keys and IVs.
0599: *
0600: * @param clientRand 32-byte random value chosen by the client
0601: * @param serverRand 32-byte random value chosen by the server
0602: * @param masterSecret master secret resulting from the key exchange
0603: *
0604: * @exception GeneralSecurityException thrown in case of failure
0605: */
0606: void generateKeys(byte[] clientRand, byte[] serverRand,
0607: byte[] masterSecret) throws GeneralSecurityException {
0608: generateKeysBlock(clientRand, serverRand, masterSecret);
0609: chopKeysBlock(clientRand, serverRand, masterSecret);
0610: keyBlock = null;
0611: md = null;
0612: sd = null;
0613: }
0614:
0615: /**
0616: * Get client MAC secret
0617: * @return client MAC secret
0618: */
0619: byte[] getClientMACSecret() {
0620: return clientMACSecret;
0621: }
0622:
0623: /**
0624: * Get server MAC secret
0625: * @return server MAC secret
0626: */
0627: byte[] getServerMACSecret() {
0628: return serverMACSecret;
0629: }
0630:
0631: /**
0632: * Get client bulk key
0633: * @return client bulk key
0634: */
0635: SecretKey getClientBulkKey() {
0636: return clientBulkKey;
0637: }
0638:
0639: /**
0640: * Get server bulk key
0641: * @return server bulk key
0642: */
0643: SecretKey getServerBulkKey() {
0644: return serverBulkKey;
0645: }
0646:
0647: /**
0648: * Get digest used for encoding
0649: * @return encode digest
0650: */
0651: MessageDigest getEncodeDigest() {
0652: return encodeDigest;
0653: }
0654:
0655: /**
0656: * Get digest used for decoding
0657: * @return decode digest
0658: */
0659: MessageDigest getDecodeDigest() {
0660: return decodeDigest;
0661: }
0662:
0663: /**
0664: * Get cipher used for encoding
0665: * @return encode cipher
0666: */
0667: Cipher getEncodeCipher() {
0668: return encodeCipher;
0669: }
0670:
0671: /**
0672: * Get cipher used for decoding
0673: * @return decode cipher
0674: */
0675: Cipher getDecodeCipher() {
0676: return decodeCipher;
0677: }
0678:
0679: /**
0680: * Get pad length used for MAC computation
0681: * @return pad length
0682: */
0683: int getPadLength() {
0684: return padLength;
0685: }
0686:
0687: /**
0688: * Generates keys block
0689: *
0690: * @param clientRand 32-byte random value chosen by the client
0691: * @param serverRand 32-byte random value chosen by the server
0692: * @param masterSecret master secret resulting from the key exchange
0693: *
0694: * @exception GeneralSecurityException thrown in case of failure
0695: */
0696: private void generateKeysBlock(byte[] clientRand,
0697: byte[] serverRand, byte[] masterSecret)
0698: throws GeneralSecurityException {
0699: /*
0700: * The following should suffice to generate a total of
0701: * 16*7 = 112 bytes of key material. 3DES_SHA requires
0702: * 2*(20 + 24 + 8) = 104 bytes.
0703: */
0704: byte[] expansion[] = { { 0x41 }, // 'A'
0705: { 0x42, 0x42 }, // 'BB'
0706: { 0x43, 0x43, 0x43 }, // 'CCC'
0707: { 0x44, 0x44, 0x44, 0x44 }, // 'DDDD'
0708: { 0x45, 0x45, 0x45, 0x45, 0x45 }, // 'EEEEE'
0709: { 0x46, 0x46, 0x46, 0x46, 0x46, 0x46 }, // 'FFFFFF'
0710: { 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47 }, // 'GGGGGGG'
0711: };
0712:
0713: /*
0714: * key_block =
0715: * MD5(master + SHA('A' + master +
0716: * ServerHello.random + ClientHello.random)) +
0717: * MD5(master + SHA('BB' + master +
0718: * ServerHello.random + ClientHello.random)) +
0719: * MD5(master + SHA('CCC' + master +
0720: * ServerHello.random + ClientHello.random)) +
0721: * [..]
0722: *
0723: * We set
0724: * subExp = master + ServerHello.random + ClientHello.random
0725: */
0726: byte[] blockSubExp = new byte[masterSecret.length
0727: + serverRand.length + clientRand.length];
0728: int offset = 0;
0729: System.arraycopy(masterSecret, 0, blockSubExp, offset,
0730: masterSecret.length);
0731: offset += masterSecret.length;
0732: System.arraycopy(serverRand, 0, blockSubExp, offset,
0733: serverRand.length);
0734: offset += serverRand.length;
0735: System.arraycopy(clientRand, 0, blockSubExp, offset,
0736: clientRand.length);
0737:
0738: for (int i = 0; i < (keyBlock.length >>> 4); i++) {
0739: md.update(masterSecret, 0, masterSecret.length);
0740: sd.update(expansion[i], 0, expansion[i].length);
0741: byte[] res = new byte[20];
0742: sd.update(blockSubExp, 0, blockSubExp.length);
0743: sd.digest(res, 0, res.length);
0744: md.update(res, 0, 20);
0745: md.digest(keyBlock, i << 4, md.getDigestLength());
0746: }
0747: }
0748:
0749: /**
0750: * Extract keys form keys block
0751: *
0752: * @param clientRand 32-byte random value chosen by the client
0753: * @param serverRand 32-byte random value chosen by the server
0754: * @param masterSecret master secret resulting from the key exchange
0755: *
0756: * @exception GeneralSecurityException thrown in case of failure
0757: */
0758: private void chopKeysBlock(byte[] clientRand, byte[] serverRand,
0759: byte[] masterSecret) throws GeneralSecurityException {
0760: int offset = 0;
0761:
0762: System.arraycopy(keyBlock, 0, clientMACSecret, 0,
0763: clientMACSecret.length);
0764: offset += clientMACSecret.length;
0765:
0766: System.arraycopy(keyBlock, offset, serverMACSecret, 0,
0767: serverMACSecret.length);
0768: offset += serverMACSecret.length;
0769:
0770: System.arraycopy(keyBlock, offset, clientKey, 0,
0771: clientKey.length);
0772: offset += clientKey.length;
0773:
0774: System.arraycopy(keyBlock, offset, serverKey, 0,
0775: serverKey.length);
0776: offset += serverKey.length;
0777:
0778: if (suiteType == Handshake.ARCFOUR_128_MD5
0779: || suiteType == Handshake.ARCFOUR_128_SHA) {
0780: /*
0781: * NOTE: We know ivSze is always zero for ARCFOUR cipher suites
0782: * so this is commented out. It wasn't removed in case we need
0783: * to add support for DES or another block cipher.
0784: * if (ivSize != 0) {
0785: * // bulk encryption uses a block cipher, so initialize IVs
0786: * System.arraycopy(keyBlock,
0787: * 2*(clientMACSecret.length + clientKey.length),
0788: * clientIV, 0, ivSize);
0789: * System.arraycopy(keyBlock,
0790: * 2*(clientMACSecret.length + clientKey.length)
0791: * + ivSize, serverIV, 0, ivSize);
0792: * }
0793: */
0794: } else {
0795: /*
0796: * IMPL_NOTE: The only other option is ARCFOUR_40_MD5
0797: *
0798: * Expand the keys for exportable cipher suites
0799: * final_client_write_key = MD5(client_write_key +
0800: * ClientHello.random +
0801: * ServerHello.random);
0802: * final_server_write_key = MD5(server_write_key +
0803: * ServerHello.random +
0804: * ClientHello.random);
0805: */
0806: byte[] res = new byte[16];
0807: md.update(clientKey, 0, clientKey.length);
0808: md.update(clientRand, 0, clientRand.length);
0809: md.update(serverRand, 0, serverRand.length);
0810: md.digest(res, 0, res.length);
0811:
0812: /*
0813: * NOTE: For both ARCFOUR_128_MD5 and ARCFOUR_40_MD5,
0814: * expanded key is 16
0815: */
0816: byte[] fcKey = new byte[16];
0817: System.arraycopy(res, 0, fcKey, 0, fcKey.length);
0818:
0819: md.update(serverKey, 0, serverKey.length);
0820: md.update(serverRand, 0, serverRand.length);
0821: md.update(clientRand, 0, clientRand.length);
0822: md.digest(res, 0, res.length);
0823: byte[] fserverKey = new byte[fcKey.length];
0824: System.arraycopy(res, 0, fserverKey, 0, fserverKey.length);
0825:
0826: clientKey = fcKey;
0827: serverKey = fserverKey;
0828:
0829: /*
0830: * ... and compute IVs in a special way
0831: * client_write_IV = MD5(ClientHello.random + ServerHello.random)
0832: * server_write_IV = MD5(ServerHello.random + ClientHello.random)
0833: *
0834: * NOTE: We know for the chosen ciphersuites, ivSize is zero
0835: * so this code is commented out for now
0836: if (ivSize != 0) {
0837: md.update(clientRand, (short) 0,
0838: (short) clientRand.length);
0839: md.doFinal(serverRand, (short) 0,
0840: (short) serverRand.length, res, (short) 0);
0841: System.arraycopy(res, 0, clientIV, 0, ivSize);
0842:
0843: md.update(serverRand, (short) 0,
0844: (short) serverRand.length);
0845: md.doFinal(clientRand, (short) 0,
0846: (short) clientRand.length, res, (short) 0);
0847: System.arraycopy(res, 0, serverIV, 0, ivSize);
0848: }
0849: */
0850: }
0851:
0852: /*
0853: * Now initialize the ciphers and keys. FOr now this is always
0854: * ARCFOUR and we comment out support for other ciphers.
0855: */
0856: clientBulkKey = new SecretKey(clientKey, 0, clientKey.length,
0857: "ARC4");
0858: serverBulkKey = new SecretKey(serverKey, 0, serverKey.length,
0859: "ARC4");
0860: }
0861: }
0862:
0863: /**
0864: * Implements MAC computation
0865: */
0866: class MAC {
0867: /**
0868: * PAD1 is a 48-byte array filled with 0x36
0869: */
0870: static final byte[] PAD1 = { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0871: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0872: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0873: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0874: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0875: 0x36, 0x36 };
0876:
0877: /**
0878: * PAD2 is a 48-byte array filled with 0x5c
0879: */
0880: static final byte[] PAD2 = { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0881: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0882: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0883: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0884: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0885: 0x5c, 0x5c };
0886:
0887: /**
0888: * Data used for MAC computation
0889: */
0890: /** MAC secret */
0891: protected byte[] macSecret = null;
0892: /** Message digest */
0893: protected MessageDigest digest = null;
0894: /** Message digest length */
0895: protected int digestLength = 0;
0896: /** pad length */
0897: protected int padLength = 0;
0898: /** Write sequence number */
0899: private long sequenceNumber = 0;
0900:
0901: /**
0902: * Computes the MAC for an SSLCompressed structure.
0903: *
0904: * @param type SSL record type of the SSLCompressed structure
0905: * @param buf byte array containing the SSLCompressed fragment
0906: * @param offset starting offset of the fragment in buf
0907: * @param length length of the fragment
0908: *
0909: * @return a byte array containing the MAC
0910: */
0911: byte[] getMAC(byte type, byte[] buf, int offset, int length) {
0912: /*
0913: * MAC = hash(MAC_secret + PAD2 +
0914: * hash(MAC_secret + PAD1 + seq_num + type + len +
0915: * compressed_fragment));
0916: */
0917:
0918: // Compute the inner hash first
0919: byte[] byteArray = null;
0920: digest.update(macSecret, 0, macSecret.length);
0921: digest.update(PAD1, 0, padLength);
0922: byteArray = Utils.longToBytes(sequenceNumber);
0923: digest.update(byteArray, 0, byteArray.length);
0924:
0925: byteArray = new byte[3];
0926: byteArray[0] = type;
0927: byteArray[1] = (byte) (length >>> 8);
0928: byteArray[2] = (byte) (length & 0xff);
0929: digest.update(byteArray, 0, byteArray.length);
0930: byte[] innerHash = new byte[digest.getDigestLength()];
0931: digest.update(buf, offset, length);
0932: try {
0933: digest.digest(innerHash, 0, innerHash.length);
0934: } catch (DigestException e) {
0935: // Ignore this exception, it should never happen
0936: }
0937:
0938: // Now, the outer hash
0939: digest.update(macSecret, 0, macSecret.length);
0940: digest.update(PAD2, 0, padLength);
0941: byte[] mac = new byte[innerHash.length];
0942: digest.update(innerHash, 0, innerHash.length);
0943: try {
0944: digest.digest(mac, 0, mac.length);
0945: } catch (DigestException e) {
0946: // Ignore this exception, it should never happen
0947: }
0948:
0949: return mac;
0950: }
0951:
0952: /**
0953: * Increments write sequence number
0954: * @exception IOException if the sequence numbers rolls around
0955: */
0956: void incrementSequenceNumber() throws IOException {
0957: if (++sequenceNumber == (long) 0)
0958: throw new IOException("Sequence number rolled over");
0959: }
0960: }
0961:
0962: /**
0963: * Implements record's encoding
0964: */
0965: class RecordEncoder extends MAC {
0966: /** Cipher used for encryption */
0967: private Cipher cipher;
0968:
0969: /**
0970: * Constructs RecordEncoder object
0971: *
0972: * @param dgst digest for MAC computation
0973: * @param secret MAC secret
0974: * @param padLen padding length
0975: * @param cphr cipher used for encoding
0976: */
0977: RecordEncoder(MessageDigest dgst, byte[] secret, int padLen,
0978: Cipher cphr) {
0979: macSecret = secret;
0980: digest = dgst;
0981: digestLength = digest.getDigestLength();
0982: padLength = padLen;
0983: cipher = cphr;
0984: }
0985:
0986: /**
0987: * Converts a byte array containing an SSLPlaintext structure
0988: * to the corresponding SSLCiphertext structure. The process
0989: * typically involves the addition of a MAC followed by
0990: * encryption.
0991: *
0992: * @param plainText byte array containing SSLPlaintext
0993: * @return the number of bytes written to the OutputStream
0994: *
0995: * @exception IOException if a problem is encountered during
0996: * encryption
0997: */
0998: byte[] encode(byte[] plainText) throws IOException {
0999: /*
1000: * Since we only support NULL compression, SSLPlaintext
1001: * the same as SSLCompressed.
1002: */
1003: byte[] fragAndMAC = null; // fragment plus MAC
1004:
1005: if (digest != null) {
1006: fragAndMAC = new byte[plainText.length - 5 + digestLength];
1007: System.arraycopy(plainText, 5, fragAndMAC, 0,
1008: plainText.length - 5);
1009: byte[] mac = getMAC(plainText[0], plainText, 5,
1010: plainText.length - 5);
1011: System.arraycopy(mac, 0, fragAndMAC,
1012: (plainText.length - 5), digestLength);
1013: } else {
1014: fragAndMAC = new byte[plainText.length - 5];
1015: System.arraycopy(plainText, 5, fragAndMAC, 0,
1016: plainText.length - 5);
1017: }
1018:
1019: // ... now we need to encrypt fragAndMAC and possibly update IVs
1020: byte[] efragAndMAC = null;
1021: if (cipher != null) {
1022: try {
1023: /*
1024: * NOTE: For now, we always have a stream cipher so this
1025: * is commented out.
1026: */
1027: // We have a stream cipher
1028: efragAndMAC = fragAndMAC;
1029: cipher.update(fragAndMAC, 0, fragAndMAC.length,
1030: efragAndMAC, 0);
1031: } catch (Exception e) {
1032: throw new IOException("Encode caught " + e);
1033: }
1034: } else {
1035: // Cipher algorithm is NULL
1036: efragAndMAC = fragAndMAC;
1037: }
1038:
1039: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1040: Logging.report(Logging.INFORMATION,
1041: LogChannels.LC_SECURITY, "efragAndMAC: "
1042: + Utils.hexEncode(efragAndMAC));
1043: }
1044:
1045: byte[] record = new byte[efragAndMAC.length + 5];
1046: System.arraycopy(plainText, 0, record, 0, 3);
1047: record[3] = (byte) (efragAndMAC.length >>> 8);
1048: record[4] = (byte) (efragAndMAC.length & 0xff);
1049: System.arraycopy(efragAndMAC, 0, record, 5, efragAndMAC.length);
1050:
1051: // We have encoded one more record, increment seq number
1052: incrementSequenceNumber();
1053:
1054: return record;
1055: }
1056: }
1057:
1058: /**
1059: * Implements record's decoding
1060: */
1061: class RecordDecoder extends MAC {
1062: /** Cipher used for decryption */
1063: private Cipher cipher;
1064:
1065: /**
1066: * Constructs RecordDecoder object
1067: *
1068: * @param dgst digest for MAC computation
1069: * @param secret MAC secret
1070: * @param padLen padding length
1071: * @param cphr cipher used for decoding
1072: */
1073: RecordDecoder(MessageDigest dgst, byte[] secret, int padLen,
1074: Cipher cphr) {
1075: macSecret = secret;
1076: digest = dgst;
1077: digestLength = digest.getDigestLength();
1078: padLength = padLen;
1079: cipher = cphr;
1080: }
1081:
1082: /**
1083: * Converts a byte array containing an SSLCiphertext structure
1084: * to the corresponding SSLPlaintext structure. The process
1085: * typically involves decryption followed by MAC verification
1086: * and MAC stripping.
1087: * @param recordHeader record header
1088: * @param recordData record data
1089: * @return Length of the decrypted data in the input buffer.
1090: *
1091: * @exception IOException if a problem is encountered during decryption
1092: * or MAC verification
1093: */
1094: int decode(byte[] recordHeader, byte[] recordData)
1095: throws IOException {
1096: if (cipher != null) {
1097: // Cipher algorithm is not NULL (ctxt needs to be decrypted)
1098:
1099: try {
1100: /*
1101: * NOTE: For now, we only have ARCFOUR, a stream cipher
1102: * so there is no IV or padding that a block cipher has.
1103: *
1104: * Otherwise we would have to find the plaintext offset
1105: * after we decrypt.
1106: */
1107:
1108: // We have a stream cipher (NOTE: assuming CLIENT role)
1109: // We can decode in place w/o using additional memory
1110: cipher.update(recordData, 0, recordData.length,
1111: recordData, 0);
1112: } catch (Exception e) {
1113: throw new IOException("Decode caught " + e);
1114: }
1115: }
1116:
1117: int length = recordData.length - digestLength;
1118: if (digest != null) {
1119: byte[] expMAC = null; // expected MAC
1120: expMAC = getMAC(recordHeader[0], recordData, 0, length);
1121: if (!Utils.byteMatch(expMAC, 0, recordData, length,
1122: digestLength)) {
1123: throw new IOException("Bad MAC");
1124: }
1125: }
1126:
1127: incrementSequenceNumber();
1128:
1129: return length;
1130: }
1131: }
|