Home | History | Annotate | Download | only in conscrypt
      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.conscrypt;
     19 
     20 import java.security.GeneralSecurityException;
     21 import java.security.MessageDigest;
     22 import java.util.Arrays;
     23 import javax.crypto.Cipher;
     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 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  */
     36 public 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] & 0xFF;
    298             for (int i=0; i<padding_length; i++) {
    299                 if ((data[data.length-2-i] & 0xFF) != 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