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.security.MessageDigest;
     22 import java.util.Arrays;
     23 import javax.crypto.Cipher;
     24 import javax.crypto.spec.IvParameterSpec;
     25 import javax.crypto.spec.SecretKeySpec;
     26 import 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  */
     35 public 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