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