001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander Y. Kleymenov
020: * @version $Revision$
021: */package org.apache.harmony.xnet.provider.jsse;
022:
023: import org.apache.harmony.xnet.provider.jsse.AlertException;
024: import org.apache.harmony.xnet.provider.jsse.SSLSessionImpl;
025: import org.apache.harmony.xnet.provider.jsse.PRF;
026: import org.apache.harmony.xnet.provider.jsse.ConnectionState;
027:
028: import java.security.GeneralSecurityException;
029: import java.util.Arrays;
030: import javax.crypto.Cipher;
031: import javax.crypto.Mac;
032: import javax.crypto.spec.IvParameterSpec;
033: import javax.crypto.spec.SecretKeySpec;
034: import javax.net.ssl.SSLProtocolException;
035:
036: /**
037: * This class incapsulates the operating environment of the TLS v1
038: * (http://www.ietf.org/rfc/rfc2246.txt) Record Protocol and provides
039: * relating encryption/decryption functionality.
040: * The work functionality is based on the security
041: * parameters negotiated during the handshake.
042: */
043: public class ConnectionStateTLS extends ConnectionState {
044:
045: // Precomputed prf label values:
046: // "key expansion".getBytes()
047: private static byte[] KEY_EXPANSION_LABEL = { (byte) 0x6B,
048: (byte) 0x65, (byte) 0x79, (byte) 0x20, (byte) 0x65,
049: (byte) 0x78, (byte) 0x70, (byte) 0x61, (byte) 0x6E,
050: (byte) 0x73, (byte) 0x69, (byte) 0x6F, (byte) 0x6E };
051:
052: // "client write key".getBytes()
053: private static byte[] CLIENT_WRITE_KEY_LABEL = { (byte) 0x63,
054: (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E,
055: (byte) 0x74, (byte) 0x20, (byte) 0x77, (byte) 0x72,
056: (byte) 0x69, (byte) 0x74, (byte) 0x65, (byte) 0x20,
057: (byte) 0x6B, (byte) 0x65, (byte) 0x79 };
058:
059: // "server write key".getBytes()
060: private static byte[] SERVER_WRITE_KEY_LABEL = { (byte) 0x73,
061: (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65,
062: (byte) 0x72, (byte) 0x20, (byte) 0x77, (byte) 0x72,
063: (byte) 0x69, (byte) 0x74, (byte) 0x65, (byte) 0x20,
064: (byte) 0x6B, (byte) 0x65, (byte) 0x79 };
065:
066: // "IV block".getBytes()
067: private static byte[] IV_BLOCK_LABEL = { (byte) 0x49, (byte) 0x56,
068: (byte) 0x20, (byte) 0x62, (byte) 0x6C, (byte) 0x6F,
069: (byte) 0x63, (byte) 0x6B };
070:
071: // MACs to create and check the message integrity info
072: private final Mac encMac;
073: private final Mac decMac;
074:
075: // Once created permanently used array:
076: // is used to create the header of the MAC material value:
077: // 5 == 1(TLSCompressed.type) + 2(TLSCompressed.version) +
078: // 2(TLSCompressed.length)
079: private final byte[] mac_material_header = new byte[] { 0, 3, 1, 0,
080: 0 };
081:
082: /**
083: * Creates the instance of TLS v1 Connection State. All of the
084: * security parameters are provided by session object.
085: * @param session: the sessin object which incapsulates
086: * all of the security parameters established by handshake protocol.
087: * The key calculation for the state is done according
088: * to the TLS v 1.0 Protocol specification.
089: * (http://www.ietf.org/rfc/rfc2246.txt)
090: */
091: protected ConnectionStateTLS(SSLSessionImpl session) {
092: try {
093: CipherSuite cipherSuite = session.cipherSuite;
094:
095: hash_size = cipherSuite.getMACLength();
096: boolean is_exportabe = cipherSuite.isExportable();
097: int key_size = (is_exportabe) ? cipherSuite.keyMaterial
098: : cipherSuite.expandedKeyMaterial;
099: int iv_size = cipherSuite.getBlockSize();
100:
101: String algName = cipherSuite.getBulkEncryptionAlgorithm();
102: String macName = cipherSuite.getHmacName();
103: if (logger != null) {
104: logger.println("ConnectionStateTLS.create:");
105: logger.println(" cipher suite name: "
106: + cipherSuite.getName());
107: logger.println(" encryption alg name: " + algName);
108: logger.println(" mac alg name: " + macName);
109: logger.println(" hash size: " + hash_size);
110: logger.println(" block size: " + iv_size);
111: logger.println(" IV size (== block size):" + iv_size);
112: logger.println(" key size: " + key_size);
113: }
114:
115: byte[] clientRandom = session.clientRandom;
116: byte[] serverRandom = session.serverRandom;
117: // so we need PRF value of size of
118: // 2*hash_size + 2*key_size + 2*iv_size
119: byte[] key_block = new byte[2 * hash_size + 2 * key_size
120: + 2 * iv_size];
121: byte[] seed = new byte[clientRandom.length
122: + serverRandom.length];
123: System.arraycopy(serverRandom, 0, seed, 0,
124: serverRandom.length);
125: System.arraycopy(clientRandom, 0, seed,
126: serverRandom.length, clientRandom.length);
127:
128: PRF.computePRF(key_block, session.master_secret,
129: KEY_EXPANSION_LABEL, seed);
130:
131: byte[] client_mac_secret = new byte[hash_size];
132: byte[] server_mac_secret = new byte[hash_size];
133: byte[] client_key = new byte[key_size];
134: byte[] server_key = new byte[key_size];
135:
136: boolean is_client = !session.isServer;
137:
138: is_block_cipher = (iv_size > 0);
139: // do not count, as block_size is always 8
140: // block_size = iv_size;
141:
142: System.arraycopy(key_block, 0, client_mac_secret, 0,
143: hash_size);
144: System.arraycopy(key_block, hash_size, server_mac_secret,
145: 0, hash_size);
146: System.arraycopy(key_block, 2 * hash_size, client_key, 0,
147: key_size);
148: System.arraycopy(key_block, 2 * hash_size + key_size,
149: server_key, 0, key_size);
150:
151: IvParameterSpec clientIV = null;
152: IvParameterSpec serverIV = null;
153:
154: if (is_exportabe) {
155: System.arraycopy(clientRandom, 0, seed, 0,
156: clientRandom.length);
157: System.arraycopy(serverRandom, 0, seed,
158: clientRandom.length, serverRandom.length);
159: byte[] final_client_key = new byte[cipherSuite.expandedKeyMaterial];
160: byte[] final_server_key = new byte[cipherSuite.expandedKeyMaterial];
161: PRF.computePRF(final_client_key, client_key,
162: CLIENT_WRITE_KEY_LABEL, seed);
163: PRF.computePRF(final_server_key, server_key,
164: SERVER_WRITE_KEY_LABEL, seed);
165: client_key = final_client_key;
166: server_key = final_server_key;
167: if (is_block_cipher) {
168: byte[] iv_block = new byte[2 * iv_size];
169: PRF
170: .computePRF(iv_block, null, IV_BLOCK_LABEL,
171: seed);
172: clientIV = new IvParameterSpec(iv_block, 0, iv_size);
173: serverIV = new IvParameterSpec(iv_block, iv_size,
174: iv_size);
175: }
176: } else if (is_block_cipher) {
177: clientIV = new IvParameterSpec(key_block,
178: 2 * (hash_size + key_size), iv_size);
179: serverIV = new IvParameterSpec(key_block, 2
180: * (hash_size + key_size) + iv_size, iv_size);
181: }
182:
183: if (logger != null) {
184: logger.println("is exportable: " + is_exportabe);
185: logger.println("master_secret");
186: logger.print(session.master_secret);
187: logger.println("client_random");
188: logger.print(clientRandom);
189: logger.println("server_random");
190: logger.print(serverRandom);
191: //logger.println("key_block");
192: //logger.print(key_block);
193: logger.println("client_mac_secret");
194: logger.print(client_mac_secret);
195: logger.println("server_mac_secret");
196: logger.print(server_mac_secret);
197: logger.println("client_key");
198: logger.print(client_key);
199: logger.println("server_key");
200: logger.print(server_key);
201: if (clientIV == null) {
202: logger.println("no IV.");
203: } else {
204: logger.println("client_iv");
205: logger.print(clientIV.getIV());
206: logger.println("server_iv");
207: logger.print(serverIV.getIV());
208: }
209: }
210:
211: encCipher = Cipher.getInstance(algName);
212: decCipher = Cipher.getInstance(algName);
213: encMac = Mac.getInstance(macName);
214: decMac = Mac.getInstance(macName);
215:
216: if (is_client) { // client side
217: encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(
218: client_key, algName), clientIV);
219: decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(
220: server_key, algName), serverIV);
221: encMac.init(new SecretKeySpec(client_mac_secret,
222: macName));
223: decMac.init(new SecretKeySpec(server_mac_secret,
224: macName));
225: } else { // server side
226: encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(
227: server_key, algName), serverIV);
228: decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(
229: client_key, algName), clientIV);
230: encMac.init(new SecretKeySpec(server_mac_secret,
231: macName));
232: decMac.init(new SecretKeySpec(client_mac_secret,
233: macName));
234: }
235: } catch (Exception e) {
236: e.printStackTrace();
237: throw new AlertException(
238: AlertProtocol.INTERNAL_ERROR,
239: new SSLProtocolException(
240: "Error during computation of security parameters"));
241: }
242: }
243:
244: /**
245: * Creates the GenericStreamCipher or GenericBlockCipher
246: * data structure for specified data of specified type.
247: * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured.
248: */
249: protected byte[] encrypt(byte type, byte[] fragment, int offset,
250: int len) {
251: try {
252: int content_mac_length = len + hash_size;
253: int padding_length = is_block_cipher ? ((8 - (++content_mac_length & 0x07)) & 0x07)
254: : 0;
255: byte[] res = new byte[content_mac_length + padding_length];
256: System.arraycopy(fragment, offset, res, 0, len);
257:
258: mac_material_header[0] = type;
259: mac_material_header[3] = (byte) ((0x00FF00 & len) >> 8);
260: mac_material_header[4] = (byte) (0x0000FF & len);
261:
262: encMac.update(write_seq_num);
263: encMac.update(mac_material_header);
264: encMac.update(fragment, offset, len);
265: encMac.doFinal(res, len);
266:
267: //if (logger != null) {
268: // logger.println("MAC Material:");
269: // logger.print(write_seq_num);
270: // logger.print(mac_material_header);
271: // logger.print(fragment, offset, len);
272: //}
273:
274: if (is_block_cipher) {
275: // do padding:
276: Arrays.fill(res, content_mac_length - 1, res.length,
277: (byte) (padding_length));
278: }
279: if (logger != null) {
280: logger
281: .println("SSLRecordProtocol.do_encryption: Generic"
282: + (is_block_cipher ? "BlockCipher with padding["
283: + padding_length + "]:"
284: : "StreamCipher:"));
285: logger.print(res);
286: }
287: byte[] rez = new byte[encCipher.getOutputSize(res.length)];
288: // We should not call just doFinal because it reinitialize
289: // the cipher, but as says rfc 2246:
290: // "For stream ciphers that do not use a synchronization
291: // vector (such as RC4), the stream cipher state from the end
292: // of one record is simply used on the subsequent packet."
293: // and for block ciphers:
294: // "The IV for subsequent records is the last ciphertext block from
295: // the previous record."
296: // i.e. we should keep the cipher state.
297: encCipher.update(res, 0, res.length, rez);
298: incSequenceNumber(write_seq_num);
299: return rez;
300: } catch (GeneralSecurityException e) {
301: e.printStackTrace();
302: throw new AlertException(AlertProtocol.INTERNAL_ERROR,
303: new SSLProtocolException(
304: "Error during the encryption"));
305: }
306: }
307:
308: /**
309: * Retrieves the fragment of the Plaintext structure of
310: * the specified type from the provided data representing
311: * the Generic[Stream|Block]Cipher structure.
312: * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured.
313: */
314: protected byte[] decrypt(byte type, byte[] fragment, int offset,
315: int len) {
316: // plain data of the Generic[Stream|Block]Cipher structure
317: byte[] data = decCipher.update(fragment, offset, len);
318: // the 'content' part of the structure
319: byte[] content;
320: if (is_block_cipher) {
321: // check padding
322: int padding_length = data[data.length - 1];
323: for (int i = 0; i < padding_length; i++) {
324: if (data[data.length - 2 - i] != padding_length) {
325: throw new AlertException(
326: AlertProtocol.DECRYPTION_FAILED,
327: new SSLProtocolException(
328: "Received message has bad padding"));
329: }
330: }
331: content = new byte[data.length - hash_size - padding_length
332: - 1];
333: } else {
334: content = new byte[data.length - hash_size];
335: }
336:
337: mac_material_header[0] = type;
338: mac_material_header[3] = (byte) ((0x00FF00 & content.length) >> 8);
339: mac_material_header[4] = (byte) (0x0000FF & content.length);
340:
341: decMac.update(read_seq_num);
342: decMac.update(mac_material_header);
343: decMac.update(data, 0, content.length); // mac.update(fragment);
344: byte[] mac_value = decMac.doFinal();
345: if (logger != null) {
346: logger.println("Decrypted:");
347: logger.print(data);
348: //logger.println("MAC Material:");
349: //logger.print(read_seq_num);
350: //logger.print(mac_material_header);
351: //logger.print(data, 0, content.length);
352: logger.println("Expected mac value:");
353: logger.print(mac_value);
354: }
355: // checking the mac value
356: for (int i = 0; i < hash_size; i++) {
357: if (mac_value[i] != data[i + content.length]) {
358: throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
359: new SSLProtocolException("Bad record MAC"));
360: }
361: }
362: System.arraycopy(data, 0, content, 0, content.length);
363: incSequenceNumber(read_seq_num);
364: return content;
365: }
366: }
|