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]; 308 for (int i=0; i<padding_length; i++) { 309 if (data[data.length-2-i] != 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