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 org.apache.harmony.xnet.provider.jsse.AlertException;
21import org.apache.harmony.xnet.provider.jsse.SSLSessionImpl;
22import org.apache.harmony.xnet.provider.jsse.PRF;
23import org.apache.harmony.xnet.provider.jsse.ConnectionState;
24
25import java.security.GeneralSecurityException;
26import java.util.Arrays;
27import javax.crypto.Cipher;
28import javax.crypto.Mac;
29import javax.crypto.spec.IvParameterSpec;
30import javax.crypto.spec.SecretKeySpec;
31import javax.net.ssl.SSLProtocolException;
32
33/**
34 * This class encapsulates the operating environment of the TLS v1
35 * (http://www.ietf.org/rfc/rfc2246.txt) Record Protocol and provides
36 * relating encryption/decryption functionality.
37 * The work functionality is based on the security
38 * parameters negotiated during the handshake.
39 */
40public class ConnectionStateTLS extends ConnectionState {
41
42    // Pre-calculated prf label values:
43    // "key expansion".getBytes()
44    private static byte[] KEY_EXPANSION_LABEL = {
45        (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x20, (byte) 0x65,
46        (byte) 0x78, (byte) 0x70, (byte) 0x61, (byte) 0x6E, (byte) 0x73,
47        (byte) 0x69, (byte) 0x6F, (byte) 0x6E };
48
49    // "client write key".getBytes()
50    private static byte[] CLIENT_WRITE_KEY_LABEL = {
51        (byte) 0x63, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E,
52        (byte) 0x74, (byte) 0x20, (byte) 0x77, (byte) 0x72, (byte) 0x69,
53        (byte) 0x74, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65,
54        (byte) 0x79 };
55
56    // "server write key".getBytes()
57    private static byte[] SERVER_WRITE_KEY_LABEL = {
58        (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76, (byte) 0x65,
59        (byte) 0x72, (byte) 0x20, (byte) 0x77, (byte) 0x72, (byte) 0x69,
60        (byte) 0x74, (byte) 0x65, (byte) 0x20, (byte) 0x6B, (byte) 0x65,
61        (byte) 0x79 };
62
63    // "IV block".getBytes()
64    private static byte[] IV_BLOCK_LABEL = {
65        (byte) 0x49, (byte) 0x56, (byte) 0x20, (byte) 0x62, (byte) 0x6C,
66        (byte) 0x6F, (byte) 0x63, (byte) 0x6B };
67
68    // MACs to create and check the message integrity info
69    private final Mac encMac;
70    private final Mac decMac;
71
72    // Once created permanently used array:
73    // is used to create the header of the MAC material value:
74    // 5 == 1(TLSCompressed.type) + 2(TLSCompressed.version) +
75    //      2(TLSCompressed.length)
76    private final byte[] mac_material_header = new byte[] {0, 3, 1, 0, 0};
77
78    /**
79     * Creates the instance of TLS v1 Connection State. All of the
80     * security parameters are provided by session object.
81     * @param   session: the sessin object which incapsulates
82     * all of the security parameters established by handshake protocol.
83     * The key calculation for the state is done according
84     * to the TLS v 1.0 Protocol specification.
85     * (http://www.ietf.org/rfc/rfc2246.txt)
86     */
87    protected ConnectionStateTLS(SSLSessionImpl session) {
88        try {
89            CipherSuite cipherSuite = session.cipherSuite;
90
91            hash_size = cipherSuite.getMACLength();
92            boolean is_exportabe =  cipherSuite.isExportable();
93            int key_size = (is_exportabe)
94                ? cipherSuite.keyMaterial
95                : cipherSuite.expandedKeyMaterial;
96            int iv_size = cipherSuite.getBlockSize();
97
98            String algName = cipherSuite.getBulkEncryptionAlgorithm();
99            String macName = cipherSuite.getHmacName();
100            if (logger != null) {
101                logger.println("ConnectionStateTLS.create:");
102                logger.println("  cipher suite name: "
103                                            + cipherSuite.getName());
104                logger.println("  encryption alg name: " + algName);
105                logger.println("  mac alg name: " + macName);
106                logger.println("  hash size: " + hash_size);
107                logger.println("  block size: " + iv_size);
108                logger.println("  IV size (== block size):" + iv_size);
109                logger.println("  key size: " + key_size);
110            }
111
112            byte[] clientRandom = session.clientRandom;
113            byte[] serverRandom = session.serverRandom;
114            // so we need PRF value of size of
115            // 2*hash_size + 2*key_size + 2*iv_size
116            byte[] key_block = new byte[2*hash_size + 2*key_size + 2*iv_size];
117            byte[] seed = new byte[clientRandom.length + serverRandom.length];
118            System.arraycopy(serverRandom, 0, seed, 0, serverRandom.length);
119            System.arraycopy(clientRandom, 0, seed, serverRandom.length,
120                    clientRandom.length);
121
122            PRF.computePRF(key_block, session.master_secret,
123                    KEY_EXPANSION_LABEL, seed);
124
125            byte[] client_mac_secret = new byte[hash_size];
126            byte[] server_mac_secret = new byte[hash_size];
127            byte[] client_key = new byte[key_size];
128            byte[] server_key = new byte[key_size];
129
130            boolean is_client = !session.isServer;
131
132            is_block_cipher = (iv_size > 0);
133            // do not count, as block_size is always 8
134            // block_size = iv_size;
135
136            System.arraycopy(key_block, 0, client_mac_secret, 0, hash_size);
137            System.arraycopy(key_block, hash_size,
138                    server_mac_secret, 0, hash_size);
139            System.arraycopy(key_block, 2*hash_size, client_key, 0, key_size);
140            System.arraycopy(key_block, 2*hash_size+key_size,
141                    server_key, 0, key_size);
142
143            IvParameterSpec clientIV = null;
144            IvParameterSpec serverIV = null;
145
146            if (is_exportabe) {
147                System.arraycopy(clientRandom, 0,
148                        seed, 0, clientRandom.length);
149                System.arraycopy(serverRandom, 0,
150                        seed, clientRandom.length, serverRandom.length);
151                byte[] final_client_key =
152                    new byte[cipherSuite.expandedKeyMaterial];
153                byte[] final_server_key =
154                    new byte[cipherSuite.expandedKeyMaterial];
155                PRF.computePRF(final_client_key, client_key,
156                        CLIENT_WRITE_KEY_LABEL, seed);
157                PRF.computePRF(final_server_key, server_key,
158                        SERVER_WRITE_KEY_LABEL, seed);
159                client_key = final_client_key;
160                server_key = final_server_key;
161                if (is_block_cipher) {
162                    byte[] iv_block = new byte[2*iv_size];
163                    PRF.computePRF(iv_block, null, IV_BLOCK_LABEL, seed);
164                    clientIV = new IvParameterSpec(iv_block, 0, iv_size);
165                    serverIV = new IvParameterSpec(iv_block, iv_size, iv_size);
166                }
167            } else if (is_block_cipher) {
168                clientIV = new IvParameterSpec(key_block,
169                        2*(hash_size+key_size), iv_size);
170                serverIV = new IvParameterSpec(key_block,
171                        2*(hash_size+key_size)+iv_size, iv_size);
172            }
173
174            if (logger != null) {
175                logger.println("is exportable: "+is_exportabe);
176                logger.println("master_secret");
177                logger.print(session.master_secret);
178                logger.println("client_random");
179                logger.print(clientRandom);
180                logger.println("server_random");
181                logger.print(serverRandom);
182                //logger.println("key_block");
183                //logger.print(key_block);
184                logger.println("client_mac_secret");
185                logger.print(client_mac_secret);
186                logger.println("server_mac_secret");
187                logger.print(server_mac_secret);
188                logger.println("client_key");
189                logger.print(client_key);
190                logger.println("server_key");
191                logger.print(server_key);
192                if (clientIV == null) {
193                    logger.println("no IV.");
194                } else {
195                    logger.println("client_iv");
196                    logger.print(clientIV.getIV());
197                    logger.println("server_iv");
198                    logger.print(serverIV.getIV());
199                }
200            }
201
202            encCipher = Cipher.getInstance(algName);
203            decCipher = Cipher.getInstance(algName);
204            encMac = Mac.getInstance(macName);
205            decMac = Mac.getInstance(macName);
206
207            if (is_client) { // client side
208                encCipher.init(Cipher.ENCRYPT_MODE,
209                        new SecretKeySpec(client_key, algName), clientIV);
210                decCipher.init(Cipher.DECRYPT_MODE,
211                        new SecretKeySpec(server_key, algName), serverIV);
212                encMac.init(new SecretKeySpec(client_mac_secret, macName));
213                decMac.init(new SecretKeySpec(server_mac_secret, macName));
214            } else { // server side
215                encCipher.init(Cipher.ENCRYPT_MODE,
216                        new SecretKeySpec(server_key, algName), serverIV);
217                decCipher.init(Cipher.DECRYPT_MODE,
218                        new SecretKeySpec(client_key, algName), clientIV);
219                encMac.init(new SecretKeySpec(server_mac_secret, macName));
220                decMac.init(new SecretKeySpec(client_mac_secret, macName));
221            }
222        } catch (Exception e) {
223            e.printStackTrace();
224            throw new AlertException(AlertProtocol.INTERNAL_ERROR,
225                    new SSLProtocolException(
226                        "Error during computation of security parameters"));
227        }
228    }
229
230    /**
231     * Creates the GenericStreamCipher or GenericBlockCipher
232     * data structure for specified data of specified type.
233     * @throws AlertException if alert was occurred.
234     */
235    @Override
236    protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) {
237        try {
238            int content_mac_length = len + hash_size;
239            int padding_length = is_block_cipher
240                    ? ((8 - (++content_mac_length & 0x07)) & 0x07)
241                    : 0;
242            byte[] res = new byte[content_mac_length + padding_length];
243            System.arraycopy(fragment, offset, res, 0, len);
244
245            mac_material_header[0] = type;
246            mac_material_header[3] = (byte) ((0x00FF00 & len) >> 8);
247            mac_material_header[4] = (byte) (0x0000FF & len);
248
249            encMac.update(write_seq_num);
250            encMac.update(mac_material_header);
251            encMac.update(fragment, offset, len);
252            encMac.doFinal(res, len);
253
254            //if (logger != null) {
255            //    logger.println("MAC Material:");
256            //    logger.print(write_seq_num);
257            //    logger.print(mac_material_header);
258            //    logger.print(fragment, offset, len);
259            //}
260
261            if (is_block_cipher) {
262                // do padding:
263                Arrays.fill(res, content_mac_length-1,
264                        res.length, (byte) (padding_length));
265            }
266            if (logger != null) {
267                logger.println("SSLRecordProtocol.do_encryption: Generic"
268                        + (is_block_cipher
269                            ? "BlockCipher with padding["+padding_length+"]:"
270                            : "StreamCipher:"));
271                logger.print(res);
272            }
273            byte[] rez = new byte[encCipher.getOutputSize(res.length)];
274            // We should not call just doFinal because it reinitialize
275            // the cipher, but as says rfc 2246:
276            // "For stream ciphers that do not use a synchronization
277            // vector (such as RC4), the stream cipher state from the end
278            // of one record is simply used on the subsequent packet."
279            // and for block ciphers:
280            // "The IV for subsequent records is the last ciphertext block from
281            // the previous record."
282            // i.e. we should keep the cipher state.
283            encCipher.update(res, 0, res.length, rez);
284            incSequenceNumber(write_seq_num);
285            return rez;
286        } catch (GeneralSecurityException e) {
287            e.printStackTrace();
288            throw new AlertException(AlertProtocol.INTERNAL_ERROR,
289                    new SSLProtocolException("Error during the encryption"));
290        }
291    }
292
293    /**
294     * Retrieves the fragment of the Plaintext structure of
295     * the specified type from the provided data representing
296     * the Generic[Stream|Block]Cipher structure.
297     * @throws AlertException if alert was occurred.
298     */
299    @Override
300    protected byte[] decrypt(byte type, byte[] fragment,
301            int offset, int len) {
302        // plain data of the Generic[Stream|Block]Cipher structure
303        byte[] data = decCipher.update(fragment, offset, len);
304        // the 'content' part of the structure
305        byte[] content;
306        if (is_block_cipher) {
307            // check padding
308            int padding_length = data[data.length-1];
309            for (int i=0; i<padding_length; i++) {
310                if (data[data.length-2-i] != padding_length) {
311                    throw new AlertException(
312                            AlertProtocol.DECRYPTION_FAILED,
313                            new SSLProtocolException(
314                                "Received message has bad padding"));
315                }
316            }
317            content = new byte[data.length - hash_size - padding_length - 1];
318        } else {
319            content = new byte[data.length - hash_size];
320        }
321
322        mac_material_header[0] = type;
323        mac_material_header[3] = (byte) ((0x00FF00 & content.length) >> 8);
324        mac_material_header[4] = (byte) (0x0000FF & content.length);
325
326        decMac.update(read_seq_num);
327        decMac.update(mac_material_header);
328        decMac.update(data, 0, content.length); // mac.update(fragment);
329        byte[] mac_value = decMac.doFinal();
330        if (logger != null) {
331            logger.println("Decrypted:");
332            logger.print(data);
333            //logger.println("MAC Material:");
334            //logger.print(read_seq_num);
335            //logger.print(mac_material_header);
336            //logger.print(data, 0, content.length);
337            logger.println("Expected mac value:");
338            logger.print(mac_value);
339        }
340        // checking the mac value
341        for (int i=0; i<hash_size; i++) {
342            if (mac_value[i] != data[i+content.length]) {
343                throw new AlertException(AlertProtocol.BAD_RECORD_MAC,
344                        new SSLProtocolException("Bad record MAC"));
345            }
346        }
347        System.arraycopy(data, 0, content, 0, content.length);
348        incSequenceNumber(read_seq_num);
349        return content;
350    }
351}
352
353