ConnectionStateSSLv3.java revision f33eae7e84eb6d3b0f4e86b59605bb3de73009f3
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package org.apache.harmony.xnet.provider.jsse;
19
20import java.security.GeneralSecurityException;
21import java.security.MessageDigest;
22import java.util.Arrays;
23import javax.crypto.Cipher;
24import javax.crypto.spec.IvParameterSpec;
25import javax.crypto.spec.SecretKeySpec;
26import javax.net.ssl.SSLProtocolException;
27
28/**
29 * This class encapsulates the operating environment of the SSL v3
30 * (http://wp.netscape.com/eng/ssl3) Record Protocol and provides
31 * relating encryption/decryption functionality.
32 * The work functionality is based on the security
33 * parameters negotiated during the handshake.
34 */
35public class ConnectionStateSSLv3 extends ConnectionState {
36
37    // digest to create and check the message integrity info
38    private final MessageDigest messageDigest;
39    private final byte[] mac_write_secret;
40    private final byte[] mac_read_secret;
41
42    // paddings
43    private final byte[] pad_1;
44    private final byte[] pad_2;
45    // array will hold the part of the MAC material:
46    // length of 3 == 1(SSLCompressed.type) + 2(SSLCompressed.length)
47    // (more on SSLv3 MAC computation and payload protection see
48    // SSL v3 specification, p. 5.2.3)
49    private final byte[] mac_material_part = new byte[3];
50
51    /**
52     * Creates the instance of SSL v3 Connection State. All of the
53     * security parameters are provided by session object.
54     * @param   session: the sessin object which incapsulates
55     * all of the security parameters established by handshake protocol.
56     * The key calculation for the state is done according
57     * to the SSL v3 Protocol specification.
58     * (http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt)
59     */
60    protected ConnectionStateSSLv3(SSLSessionImpl session) {
61        try {
62            CipherSuite cipherSuite = session.cipherSuite;
63
64            boolean is_exportabe =  cipherSuite.isExportable();
65            hash_size = cipherSuite.getMACLength();
66            int key_size = (is_exportabe)
67                ? cipherSuite.keyMaterial
68                : cipherSuite.expandedKeyMaterial;
69            int iv_size = cipherSuite.getBlockSize();
70
71            String algName = cipherSuite.getBulkEncryptionAlgorithm();
72            String hashName = cipherSuite.getHashName();
73            if (logger != null) {
74                logger.println("ConnectionStateSSLv3.create:");
75                logger.println("  cipher suite name: "
76                                        + session.getCipherSuite());
77                logger.println("  encryption alg name: " + algName);
78                logger.println("  hash alg name: " + hashName);
79                logger.println("  hash size: " + hash_size);
80                logger.println("  block size: " + iv_size);
81                logger.println("  IV size (== block size):" + iv_size);
82                logger.println("  key size: " + key_size);
83            }
84
85            byte[] clientRandom = session.clientRandom;
86            byte[] serverRandom = session.serverRandom;
87            // so we need PRF value of size of
88            // 2*hash_size + 2*key_size + 2*iv_size
89            byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
90            byte[] seed = new byte[clientRandom.length + serverRandom.length];
91            System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
92            System.arraycopy(clientRandom, 0, seed, serverRandom.length,
93                    clientRandom.length);
94
95            PRF.computePRF_SSLv3(key_block, session.master_secret, seed);
96
97            byte[] client_mac_secret = new byte[hash_size];
98            byte[] server_mac_secret = new byte[hash_size];
99            byte[] client_key = new byte[key_size];
100            byte[] server_key = new byte[key_size];
101
102            boolean is_client = !session.isServer;
103
104            is_block_cipher = (iv_size > 0);
105
106            System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size);
107            System.arraycopy(key_block, hash_size,
108                    server_mac_secret, 0, hash_size);
109            System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size);
110            System.arraycopy(key_block, 2*hash_size+key_size,
111                    server_key, 0, key_size);
112
113            IvParameterSpec clientIV = null;
114            IvParameterSpec serverIV = null;
115
116            if (is_exportabe) {
117                if (logger != null) {
118                    logger.println("ConnectionStateSSLv3: is_exportable");
119                }
120
121                MessageDigest md5 = MessageDigest.getInstance("MD5");
122                md5.update(client_key);
123                md5.update(clientRandom);
124                md5.update(serverRandom);
125                client_key = md5.digest();
126
127                md5.update(server_key);
128                md5.update(serverRandom);
129                md5.update(clientRandom);
130                server_key = md5.digest();
131
132                key_size = cipherSuite.expandedKeyMaterial;
133
134                if (is_block_cipher) {
135                    md5.update(clientRandom);
136                    md5.update(serverRandom);
137                    clientIV = new IvParameterSpec(md5.digest(), 0, iv_size);
138                    md5.update(serverRandom);
139                    md5.update(clientRandom);
140                    serverIV = new IvParameterSpec(md5.digest(), 0, iv_size);
141                }
142            } else if (is_block_cipher) {
143                clientIV = new IvParameterSpec(key_block,
144                        2*hash_size+2*key_size, iv_size);
145                serverIV = new IvParameterSpec(key_block,
146                        2*hash_size+2*key_size+iv_size, iv_size);
147            }
148
149            if (logger != null) {
150                logger.println("is exportable: "+is_exportabe);
151                logger.println("master_secret");
152                logger.print(session.master_secret);
153                logger.println("client_random");
154                logger.print(clientRandom);
155                logger.println("server_random");
156                logger.print(serverRandom);
157                //logger.println("key_block");
158                //logger.print(key_block);
159                logger.println("client_mac_secret");
160                logger.print(client_mac_secret);
161                logger.println("server_mac_secret");
162                logger.print(server_mac_secret);
163                logger.println("client_key");
164                logger.print(client_key, 0, key_size);
165                logger.println("server_key");
166                logger.print(server_key, 0, key_size);
167                if (clientIV != null) {
168                    logger.println("client_iv");
169                    logger.print(clientIV.getIV());
170                    logger.println("server_iv");
171                    logger.print(serverIV.getIV());
172                } else {
173                    logger.println("no IV.");
174                }
175            }
176            encCipher = Cipher.getInstance(algName);
177            decCipher = Cipher.getInstance(algName);
178            messageDigest = MessageDigest.getInstance(hashName);
179            if (is_client) { // client side
180                encCipher.init(Cipher.ENCRYPT_MODE,
181                        new SecretKeySpec(client_key, 0, key_size, algName),
182                        clientIV);
183                decCipher.init(Cipher.DECRYPT_MODE,
184                        new SecretKeySpec(server_key, 0, key_size, algName),
185                        serverIV);
186                mac_write_secret = client_mac_secret;
187                mac_read_secret = server_mac_secret;
188            } else { // server side
189                encCipher.init(Cipher.ENCRYPT_MODE,
190                        new SecretKeySpec(server_key, 0, key_size, algName),
191                        serverIV);
192                decCipher.init(Cipher.DECRYPT_MODE,
193                        new SecretKeySpec(client_key, 0, key_size, algName),
194                        clientIV);
195                mac_write_secret = server_mac_secret;
196                mac_read_secret = client_mac_secret;
197            }
198            if (hashName.equals("MD5")) {
199                pad_1 = SSLv3Constants.MD5pad1;
200                pad_2 = SSLv3Constants.MD5pad2;
201            } else {
202                pad_1 = SSLv3Constants.SHApad1;
203                pad_2 = SSLv3Constants.SHApad2;
204            }
205        } catch (Exception e) {
206            e.printStackTrace();
207            throw new AlertException(AlertProtocol.INTERNAL_ERROR,
208                    new SSLProtocolException(
209                        "Error during computation of security parameters"));
210        }
211    }
212
213    /**
214     * Creates the GenericStreamCipher or GenericBlockCipher
215     * data structure for specified data of specified type.
216     * @throws AlertException if alert was occurred.
217     */
218    @Override
219    protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) {
220        try {
221            int content_mac_length = len + hash_size;
222            int padding_length = is_block_cipher
223                    ? padding_length =
224                        ((8 - (++content_mac_length & 0x07)) & 0x07)
225                    : 0;
226            byte[] res = new byte[content_mac_length + padding_length];
227            System.arraycopy(fragment, offset, res, 0, len);
228
229            mac_material_part[0] = type;
230            mac_material_part[1] = (byte) ((0x00FF00 & len) >> 8);
231            mac_material_part[2] = (byte) (0x0000FF & len);
232
233            messageDigest.update(mac_write_secret);
234            messageDigest.update(pad_1);
235            messageDigest.update(write_seq_num);
236            messageDigest.update(mac_material_part);
237            messageDigest.update(fragment, offset, len);
238            byte[] digest = messageDigest.digest();
239            messageDigest.update(mac_write_secret);
240            messageDigest.update(pad_2);
241            messageDigest.update(digest);
242            digest = messageDigest.digest();
243            System.arraycopy(digest, 0, res, len, hash_size);
244
245            //if (logger != null) {
246            //    logger.println("MAC Material:");
247            //    logger.print(write_seq_num);
248            //    logger.print(mac_material_header);
249            //    logger.print(fragment, offset, len);
250            //}
251
252            if (is_block_cipher) {
253                // do padding:
254                Arrays.fill(res, content_mac_length-1,
255                        res.length, (byte) (padding_length));
256            }
257            if (logger != null) {
258                logger.println("SSLRecordProtocol.encrypt: "
259                        + (is_block_cipher
260                            ? "GenericBlockCipher with padding["
261                                +padding_length+"]:"
262                            : "GenericStreamCipher:"));
263                logger.print(res);
264            }
265            byte[] rez = new byte[encCipher.getOutputSize(res.length)];
266            encCipher.update(res, 0, res.length, rez);
267            incSequenceNumber(write_seq_num);
268            return rez;
269        } catch (GeneralSecurityException e) {
270            e.printStackTrace();
271            throw new AlertException(AlertProtocol.INTERNAL_ERROR,
272                    new SSLProtocolException("Error during the encryption"));
273        }
274    }
275
276    /**
277     * Retrieves the fragment of the Plaintext structure of
278     * the specified type from the provided data.
279     * @throws AlertException if alert was occured.
280     */
281    @Override
282    protected byte[] decrypt(byte type, byte[] fragment,
283            int offset, int len) {
284        // plain data of the Generic[Stream|Block]Cipher structure
285        byte[] data = decCipher.update(fragment, offset, len);
286        // the 'content' part of the structure
287        byte[] content;
288        if (is_block_cipher) {
289            // check padding
290            int padding_length = data[data.length-1];
291            for (int i=0; i<padding_length; i++) {
292                if (data[data.length-2-i] != padding_length) {
293                    throw new AlertException(
294                            AlertProtocol.DECRYPTION_FAILED,
295                            new SSLProtocolException(
296                                "Received message has bad padding"));
297                }
298            }
299            content = new byte[data.length - hash_size - padding_length - 1];
300        } else {
301            content = new byte[data.length - hash_size];
302        }
303
304        byte[] mac_value;
305
306        mac_material_part[0] = type;
307        mac_material_part[1] = (byte) ((0x00FF00 & content.length) >> 8);
308        mac_material_part[2] = (byte) (0x0000FF & content.length);
309
310        messageDigest.update(mac_read_secret);
311        messageDigest.update(pad_1);
312        messageDigest.update(read_seq_num);
313        messageDigest.update(mac_material_part);
314        messageDigest.update(data, 0, content.length);
315        mac_value = messageDigest.digest();
316        messageDigest.update(mac_read_secret);
317        messageDigest.update(pad_2);
318        messageDigest.update(mac_value);
319        mac_value = messageDigest.digest();
320
321        if (logger != null) {
322            logger.println("Decrypted:");
323            logger.print(data);
324            //logger.println("MAC Material:");
325            //logger.print(read_seq_num);
326            //logger.print(mac_material_header);
327            //logger.print(data, 0, content.length);
328            logger.println("Expected mac value:");
329            logger.print(mac_value);
330        }
331        // checking the mac value
332        for (int i=0; i<hash_size; i++) {
333            if (mac_value[i] != data[i+content.length]) {
334                throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
335                        new SSLProtocolException("Bad record MAC"));
336            }
337        }
338        System.arraycopy(data, 0, content, 0, content.length);
339        incSequenceNumber(read_seq_num);
340        return content;
341    }
342
343    /**
344     * Shutdown the protocol. It will be impossible to use the instance
345     * after the calling of this method.
346     */
347    @Override
348    protected void shutdown() {
349        Arrays.fill(mac_write_secret, (byte) 0);
350        Arrays.fill(mac_read_secret, (byte) 0);
351        super.shutdown();
352    }
353}
354
355