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