ConnectionStateSSLv3.java revision 6882e31b7ce2d04ebbc91c7a55d7840e8fdce8a5
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.NullCipher;
25import javax.crypto.spec.IvParameterSpec;
26import javax.crypto.spec.SecretKeySpec;
27import javax.net.ssl.SSLProtocolException;
28
29/**
30 * This class encapsulates the operating environment of the SSL v3
31 * (http://wp.netscape.com/eng/ssl3) Record Protocol and provides
32 * relating encryption/decryption functionality.
33 * The work functionality is based on the security
34 * parameters negotiated during the handshake.
35 */
36public class ConnectionStateSSLv3 extends ConnectionState {
37
38    // digest to create and check the message integrity info
39    private final MessageDigest messageDigest;
40    private final byte[] mac_write_secret;
41    private final byte[] mac_read_secret;
42
43    // paddings
44    private final byte[] pad_1;
45    private final byte[] pad_2;
46    // array will hold the part of the MAC material:
47    // length of 3 == 1(SSLCompressed.type) + 2(SSLCompressed.length)
48    // (more on SSLv3 MAC computation and payload protection see
49    // SSL v3 specification, p. 5.2.3)
50    private final byte[] mac_material_part = new byte[3];
51
52    /**
53     * Creates the instance of SSL v3 Connection State. All of the
54     * security parameters are provided by session object.
55     * @param   session: the sessin object which incapsulates
56     * all of the security parameters established by handshake protocol.
57     * The key calculation for the state is done according
58     * to the SSL v3 Protocol specification.
59     * (http://www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt)
60     */
61    protected ConnectionStateSSLv3(SSLSessionImpl session) {
62        try {
63            CipherSuite cipherSuite = session.cipherSuite;
64
65            boolean is_exportabe =  cipherSuite.isExportable();
66            hash_size = cipherSuite.getMACLength();
67            int key_size = (is_exportabe)
68                ? cipherSuite.keyMaterial
69                : cipherSuite.expandedKeyMaterial;
70            int iv_size = cipherSuite.ivSize;
71            block_size = cipherSuite.getBlockSize();
72
73            String algName = cipherSuite.getBulkEncryptionAlgorithm();
74            String hashName = cipherSuite.getHashName();
75            if (logger != null) {
76                logger.println("ConnectionStateSSLv3.create:");
77                logger.println("  cipher suite name: "
78                                        + session.getCipherSuite());
79                logger.println("  encryption alg name: " + algName);
80                logger.println("  hash alg name: " + hashName);
81                logger.println("  hash size: " + hash_size);
82                logger.println("  block size: " + block_size);
83                logger.println("  IV size:" + iv_size);
84                logger.println("  key size: " + key_size);
85            }
86
87            byte[] clientRandom = session.clientRandom;
88            byte[] serverRandom = session.serverRandom;
89            // so we need PRF value of size of
90            // 2*hash_size + 2*key_size + 2*iv_size
91            byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
92            byte[] seed = new byte[clientRandom.length + serverRandom.length];
93            System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
94            System.arraycopy(clientRandom, 0, seed, serverRandom.length,
95                    clientRandom.length);
96
97            PRF.computePRF_SSLv3(key_block, session.master_secret, seed);
98
99            byte[] client_mac_secret = new byte[hash_size];
100            byte[] server_mac_secret = new byte[hash_size];
101            byte[] client_key = new byte[key_size];
102            byte[] server_key = new byte[key_size];
103
104            boolean is_client = !session.isServer;
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 (block_size != 0) {
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 (block_size != 0) {
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
177            if (algName == null) {
178                encCipher = new NullCipher();
179                decCipher = new NullCipher();
180            } else {
181                encCipher = Cipher.getInstance(algName);
182                decCipher = Cipher.getInstance(algName);
183                if (is_client) { // client side
184                    encCipher.init(Cipher.ENCRYPT_MODE,
185                                   new SecretKeySpec(client_key, 0, key_size, algName),
186                                   clientIV);
187                    decCipher.init(Cipher.DECRYPT_MODE,
188                                   new SecretKeySpec(server_key, 0, key_size, algName),
189                                   serverIV);
190                } else { // server side
191                    encCipher.init(Cipher.ENCRYPT_MODE,
192                                   new SecretKeySpec(server_key, 0, key_size, algName),
193                                   serverIV);
194                    decCipher.init(Cipher.DECRYPT_MODE,
195                                   new SecretKeySpec(client_key, 0, key_size, algName),
196                                   clientIV);
197                }
198            }
199
200            messageDigest = MessageDigest.getInstance(hashName);
201            if (is_client) { // client side
202                mac_write_secret = client_mac_secret;
203                mac_read_secret = server_mac_secret;
204            } else { // server side
205                mac_write_secret = server_mac_secret;
206                mac_read_secret = client_mac_secret;
207            }
208            if (hashName.equals("MD5")) {
209                pad_1 = SSLv3Constants.MD5pad1;
210                pad_2 = SSLv3Constants.MD5pad2;
211            } else {
212                pad_1 = SSLv3Constants.SHApad1;
213                pad_2 = SSLv3Constants.SHApad2;
214            }
215        } catch (Exception e) {
216            e.printStackTrace();
217            throw new AlertException(AlertProtocol.INTERNAL_ERROR,
218                    new SSLProtocolException(
219                        "Error during computation of security parameters"));
220        }
221    }
222
223    /**
224     * Creates the GenericStreamCipher or GenericBlockCipher
225     * data structure for specified data of specified type.
226     * @throws AlertException if alert was occurred.
227     */
228    @Override
229    protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) {
230        try {
231            int content_mac_length = len + hash_size;
232            int padding_length = (block_size == 0) ? 0 : getPaddingSize(++content_mac_length);
233            byte[] res = new byte[content_mac_length + padding_length];
234            System.arraycopy(fragment, offset, res, 0, len);
235
236            mac_material_part[0] = type;
237            mac_material_part[1] = (byte) ((0x00FF00 & len) >> 8);
238            mac_material_part[2] = (byte) (0x0000FF & len);
239
240            messageDigest.update(mac_write_secret);
241            messageDigest.update(pad_1);
242            messageDigest.update(write_seq_num);
243            messageDigest.update(mac_material_part);
244            messageDigest.update(fragment, offset, len);
245            byte[] digest = messageDigest.digest();
246            messageDigest.update(mac_write_secret);
247            messageDigest.update(pad_2);
248            messageDigest.update(digest);
249            digest = messageDigest.digest();
250            System.arraycopy(digest, 0, res, len, hash_size);
251
252            //if (logger != null) {
253            //    logger.println("MAC Material:");
254            //    logger.print(write_seq_num);
255            //    logger.print(mac_material_header);
256            //    logger.print(fragment, offset, len);
257            //}
258
259            if (block_size != 0) {
260                // do padding:
261                Arrays.fill(res, content_mac_length-1,
262                        res.length, (byte) (padding_length));
263            }
264            if (logger != null) {
265                logger.println("SSLRecordProtocol.encrypt: "
266                        + (block_size != 0
267                            ? "GenericBlockCipher with padding["
268                                +padding_length+"]:"
269                            : "GenericStreamCipher:"));
270                logger.print(res);
271            }
272            byte[] rez = new byte[encCipher.getOutputSize(res.length)];
273            encCipher.update(res, 0, res.length, rez);
274            incSequenceNumber(write_seq_num);
275            return rez;
276        } catch (GeneralSecurityException e) {
277            e.printStackTrace();
278            throw new AlertException(AlertProtocol.INTERNAL_ERROR,
279                    new SSLProtocolException("Error during the encryption"));
280        }
281    }
282
283    /**
284     * Retrieves the fragment of the Plaintext structure of
285     * the specified type from the provided data.
286     * @throws AlertException if alert was occured.
287     */
288    @Override
289    protected byte[] decrypt(byte type, byte[] fragment,
290            int offset, int len) {
291        // plain data of the Generic[Stream|Block]Cipher structure
292        byte[] data = decCipher.update(fragment, offset, len);
293        // the 'content' part of the structure
294        byte[] content;
295        if (block_size != 0) {
296            // check padding
297            int padding_length = data[data.length-1];
298            for (int i=0; i<padding_length; i++) {
299                if (data[data.length-2-i] != padding_length) {
300                    throw new AlertException(
301                            AlertProtocol.DECRYPTION_FAILED,
302                            new SSLProtocolException(
303                                "Received message has bad padding"));
304                }
305            }
306            content = new byte[data.length - hash_size - padding_length - 1];
307        } else {
308            content = new byte[data.length - hash_size];
309        }
310
311        byte[] mac_value;
312
313        mac_material_part[0] = type;
314        mac_material_part[1] = (byte) ((0x00FF00 & content.length) >> 8);
315        mac_material_part[2] = (byte) (0x0000FF & content.length);
316
317        messageDigest.update(mac_read_secret);
318        messageDigest.update(pad_1);
319        messageDigest.update(read_seq_num);
320        messageDigest.update(mac_material_part);
321        messageDigest.update(data, 0, content.length);
322        mac_value = messageDigest.digest();
323        messageDigest.update(mac_read_secret);
324        messageDigest.update(pad_2);
325        messageDigest.update(mac_value);
326        mac_value = messageDigest.digest();
327
328        if (logger != null) {
329            logger.println("Decrypted:");
330            logger.print(data);
331            //logger.println("MAC Material:");
332            //logger.print(read_seq_num);
333            //logger.print(mac_material_header);
334            //logger.print(data, 0, content.length);
335            logger.println("Expected mac value:");
336            logger.print(mac_value);
337        }
338        // checking the mac value
339        for (int i=0; i<hash_size; i++) {
340            if (mac_value[i] != data[i+content.length]) {
341                throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
342                        new SSLProtocolException("Bad record MAC"));
343            }
344        }
345        System.arraycopy(data, 0, content, 0, content.length);
346        incSequenceNumber(read_seq_num);
347        return content;
348    }
349
350    /**
351     * Shutdown the protocol. It will be impossible to use the instance
352     * after the calling of this method.
353     */
354    @Override
355    protected void shutdown() {
356        Arrays.fill(mac_write_secret, (byte) 0);
357        Arrays.fill(mac_read_secret, (byte) 0);
358        super.shutdown();
359    }
360}
361
362