1 /* 2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 package sun.security.ssl; 28 29 import java.io.*; 30 import java.nio.*; 31 import javax.net.ssl.*; 32 import javax.crypto.BadPaddingException; 33 import sun.misc.HexDumpEncoder; 34 35 36 /** 37 * Wrapper class around InputRecord. 38 * 39 * Application data is kept external to the InputRecord, 40 * but handshake data (alert/change_cipher_spec/handshake) will 41 * be kept internally in the ByteArrayInputStream. 42 * 43 * @author Brad Wetmore 44 */ 45 final class EngineInputRecord extends InputRecord { 46 47 private SSLEngineImpl engine; 48 49 /* 50 * A dummy ByteBuffer we'll pass back even when the data 51 * is stored internally. It'll never actually be used. 52 */ 53 static private ByteBuffer tmpBB = ByteBuffer.allocate(0); 54 55 /* 56 * Flag to tell whether the last read/parsed data resides 57 * internal in the ByteArrayInputStream, or in the external 58 * buffers. 59 */ 60 private boolean internalData; 61 62 EngineInputRecord(SSLEngineImpl engine) { 63 super(); 64 this.engine = engine; 65 } 66 67 @Override 68 byte contentType() { 69 if (internalData) { 70 return super.contentType(); 71 } else { 72 return ct_application_data; 73 } 74 } 75 76 /* 77 * Check if there is enough inbound data in the ByteBuffer 78 * to make a inbound packet. Look for both SSLv2 and SSLv3. 79 * 80 * @return -1 if there are not enough bytes to tell (small header), 81 */ 82 int bytesInCompletePacket(ByteBuffer buf) throws SSLException { 83 84 /* 85 * SSLv2 length field is in bytes 0/1 86 * SSLv3/TLS length field is in bytes 3/4 87 */ 88 if (buf.remaining() < 5) { 89 return -1; 90 } 91 92 int pos = buf.position(); 93 byte byteZero = buf.get(pos); 94 95 int len = 0; 96 97 /* 98 * If we have already verified previous packets, we can 99 * ignore the verifications steps, and jump right to the 100 * determination. Otherwise, try one last hueristic to 101 * see if it's SSL/TLS. 102 */ 103 if (formatVerified || 104 (byteZero == ct_handshake) || 105 (byteZero == ct_alert)) { 106 /* 107 * Last sanity check that it's not a wild record 108 */ 109 ProtocolVersion recordVersion = 110 ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2)); 111 112 // Check if too old (currently not possible) 113 // or if the major version does not match. 114 // The actual version negotiation is in the handshaker classes 115 if ((recordVersion.v < ProtocolVersion.MIN.v) 116 || (recordVersion.major > ProtocolVersion.MAX.major)) { 117 throw new SSLException( 118 "Unsupported record version " + recordVersion); 119 } 120 121 /* 122 * Reasonably sure this is a V3, disable further checks. 123 * We can't do the same in the v2 check below, because 124 * read still needs to parse/handle the v2 clientHello. 125 */ 126 formatVerified = true; 127 128 /* 129 * One of the SSLv3/TLS message types. 130 */ 131 len = ((buf.get(pos + 3) & 0xff) << 8) + 132 (buf.get(pos + 4) & 0xff) + headerSize; 133 134 } else { 135 /* 136 * Must be SSLv2 or something unknown. 137 * Check if it's short (2 bytes) or 138 * long (3) header. 139 * 140 * Internals can warn about unsupported SSLv2 141 */ 142 boolean isShort = ((byteZero & 0x80) != 0); 143 144 if (isShort && 145 ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) { 146 147 ProtocolVersion recordVersion = 148 ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4)); 149 150 // Check if too old (currently not possible) 151 // or if the major version does not match. 152 // The actual version negotiation is in the handshaker classes 153 if ((recordVersion.v < ProtocolVersion.MIN.v) 154 || (recordVersion.major > ProtocolVersion.MAX.major)) { 155 156 // if it's not SSLv2, we're out of here. 157 if (recordVersion.v != ProtocolVersion.SSL20Hello.v) { 158 throw new SSLException( 159 "Unsupported record version " + recordVersion); 160 } 161 } 162 163 /* 164 * Client or Server Hello 165 */ 166 int mask = (isShort ? 0x7f : 0x3f); 167 len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) + 168 (isShort ? 2 : 3); 169 170 } else { 171 // Gobblygook! 172 throw new SSLException( 173 "Unrecognized SSL message, plaintext connection?"); 174 } 175 } 176 177 return len; 178 } 179 180 /* 181 * Pass the data down if it's internally cached, otherwise 182 * do it here. 183 * 184 * If internal data, data is decrypted internally. 185 * 186 * If external data(app), return a new ByteBuffer with data to 187 * process. 188 */ 189 ByteBuffer decrypt(Authenticator authenticator, 190 CipherBox box, ByteBuffer bb) throws BadPaddingException { 191 192 if (internalData) { 193 decrypt(authenticator, box); // MAC is checked during decryption 194 return tmpBB; 195 } 196 197 BadPaddingException reservedBPE = null; 198 int tagLen = 199 (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; 200 int cipheredLength = bb.remaining(); 201 202 if (!box.isNullCipher()) { 203 try { 204 // apply explicit nonce for AEAD/CBC cipher suites if needed 205 int nonceSize = 206 box.applyExplicitNonce(authenticator, contentType(), bb); 207 208 // decrypt the content 209 if (box.isAEADMode()) { 210 // DON'T encrypt the nonce_explicit for AEAD mode 211 bb.position(bb.position() + nonceSize); 212 } // The explicit IV for CBC mode can be decrypted. 213 214 // Note that the CipherBox.decrypt() does not change 215 // the capacity of the buffer. 216 box.decrypt(bb, tagLen); 217 bb.position(nonceSize); // We don't actually remove the nonce. 218 } catch (BadPaddingException bpe) { 219 // RFC 2246 states that decryption_failed should be used 220 // for this purpose. However, that allows certain attacks, 221 // so we just send bad record MAC. We also need to make 222 // sure to always check the MAC to avoid a timing attack 223 // for the same issue. See paper by Vaudenay et al and the 224 // update in RFC 4346/5246. 225 // 226 // Failover to message authentication code checking. 227 reservedBPE = bpe; 228 } 229 } 230 231 // Requires message authentication code for null, stream and block 232 // cipher suites. 233 if ((authenticator instanceof MAC) && (tagLen != 0)) { 234 MAC signer = (MAC)authenticator; 235 int macOffset = bb.limit() - tagLen; 236 237 // Note that although it is not necessary, we run the same MAC 238 // computation and comparison on the payload for both stream 239 // cipher and CBC block cipher. 240 if (bb.remaining() < tagLen) { 241 // negative data length, something is wrong 242 if (reservedBPE == null) { 243 reservedBPE = new BadPaddingException("bad record"); 244 } 245 246 // set offset of the dummy MAC 247 macOffset = cipheredLength - tagLen; 248 bb.limit(cipheredLength); 249 } 250 251 // Run MAC computation and comparison on the payload. 252 if (checkMacTags(contentType(), bb, signer, false)) { 253 if (reservedBPE == null) { 254 reservedBPE = new BadPaddingException("bad record MAC"); 255 } 256 } 257 258 // Run MAC computation and comparison on the remainder. 259 // 260 // It is only necessary for CBC block cipher. It is used to get a 261 // constant time of MAC computation and comparison on each record. 262 if (box.isCBCMode()) { 263 int remainingLen = calculateRemainingLen( 264 signer, cipheredLength, macOffset); 265 266 // NOTE: here we use the InputRecord.buf because I did not find 267 // an effective way to work on ByteBuffer when its capacity is 268 // less than remainingLen. 269 270 // NOTE: remainingLen may be bigger (less than 1 block of the 271 // hash algorithm of the MAC) than the cipheredLength. However, 272 // We won't need to worry about it because we always use a 273 // maximum buffer for every record. We need a change here if 274 // we use small buffer size in the future. 275 if (remainingLen > buf.length) { 276 // unlikely to happen, just a placehold 277 throw new RuntimeException( 278 "Internal buffer capacity error"); 279 } 280 281 // Won't need to worry about the result on the remainder. And 282 // then we won't need to worry about what's actual data to 283 // check MAC tag on. We start the check from the header of the 284 // buffer so that we don't need to construct a new byte buffer. 285 checkMacTags(contentType(), buf, 0, remainingLen, signer, true); 286 } 287 288 bb.limit(macOffset); 289 } 290 291 // Is it a failover? 292 if (reservedBPE != null) { 293 throw reservedBPE; 294 } 295 296 return bb.slice(); 297 } 298 299 /* 300 * Run MAC computation and comparison 301 * 302 * Please DON'T change the content of the ByteBuffer parameter! 303 */ 304 private static boolean checkMacTags(byte contentType, ByteBuffer bb, 305 MAC signer, boolean isSimulated) { 306 307 int position = bb.position(); 308 int tagLen = signer.MAClen(); 309 int lim = bb.limit(); 310 int macData = lim - tagLen; 311 312 bb.limit(macData); 313 byte[] hash = signer.compute(contentType, bb, isSimulated); 314 if (hash == null || tagLen != hash.length) { 315 // Something is wrong with MAC implementation. 316 throw new RuntimeException("Internal MAC error"); 317 } 318 319 bb.position(macData); 320 bb.limit(lim); 321 try { 322 int[] results = compareMacTags(bb, hash); 323 return (results[0] != 0); 324 } finally { 325 // reset to the data 326 bb.position(position); 327 bb.limit(macData); 328 } 329 } 330 331 /* 332 * A constant-time comparison of the MAC tags. 333 * 334 * Please DON'T change the content of the ByteBuffer parameter! 335 */ 336 private static int[] compareMacTags(ByteBuffer bb, byte[] tag) { 337 338 // An array of hits is used to prevent Hotspot optimization for 339 // the purpose of a constant-time check. 340 int[] results = {0, 0}; // {missed #, matched #} 341 342 // The caller ensures there are enough bytes available in the buffer. 343 // So we won't need to check the remaining of the buffer. 344 for (int i = 0; i < tag.length; i++) { 345 if (bb.get() != tag[i]) { 346 results[0]++; // mismatched bytes 347 } else { 348 results[1]++; // matched bytes 349 } 350 } 351 352 return results; 353 } 354 355 /* 356 * Override the actual write below. We do things this way to be 357 * consistent with InputRecord. InputRecord may try to write out 358 * data to the peer, and *then* throw an Exception. This forces 359 * data to be generated/output before the exception is ever 360 * generated. 361 */ 362 @Override 363 void writeBuffer(OutputStream s, byte [] buf, int off, int len) 364 throws IOException { 365 /* 366 * Copy data out of buffer, it's ready to go. 367 */ 368 ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, 0, len).flip(); 369 engine.writer.putOutboundDataSync(netBB); 370 } 371 372 /* 373 * Delineate or read a complete packet from src. 374 * 375 * If internal data (hs, alert, ccs), the data is read and 376 * stored internally. 377 * 378 * If external data (app), return a new ByteBuffer which points 379 * to the data to process. 380 */ 381 ByteBuffer read(ByteBuffer srcBB) throws IOException { 382 /* 383 * Could have a src == null/dst == null check here, 384 * but that was already checked by SSLEngine.unwrap before 385 * ever attempting to read. 386 */ 387 388 /* 389 * If we have anything besides application data, 390 * or if we haven't even done the initial v2 verification, 391 * we send this down to be processed by the underlying 392 * internal cache. 393 */ 394 if (!formatVerified || 395 (srcBB.get(srcBB.position()) != ct_application_data)) { 396 internalData = true; 397 read(new ByteBufferInputStream(srcBB), (OutputStream) null); 398 return tmpBB; 399 } 400 401 internalData = false; 402 403 int srcPos = srcBB.position(); 404 int srcLim = srcBB.limit(); 405 406 ProtocolVersion recordVersion = ProtocolVersion.valueOf( 407 srcBB.get(srcPos + 1), srcBB.get(srcPos + 2)); 408 // Check if too old (currently not possible) 409 // or if the major version does not match. 410 // The actual version negotiation is in the handshaker classes 411 if ((recordVersion.v < ProtocolVersion.MIN.v) 412 || (recordVersion.major > ProtocolVersion.MAX.major)) { 413 throw new SSLException( 414 "Unsupported record version " + recordVersion); 415 } 416 417 /* 418 * It's really application data. How much to consume? 419 * Jump over the header. 420 */ 421 int len = bytesInCompletePacket(srcBB); 422 assert(len > 0); 423 424 if (debug != null && Debug.isOn("packet")) { 425 try { 426 HexDumpEncoder hd = new HexDumpEncoder(); 427 ByteBuffer bb = srcBB.duplicate(); // Use copy of BB 428 bb.limit(srcPos + len); 429 430 System.out.println("[Raw read (bb)]: length = " + len); 431 hd.encodeBuffer(bb, System.out); 432 } catch (IOException e) { } 433 } 434 435 // Demarcate past header to end of packet. 436 srcBB.position(srcPos + headerSize); 437 srcBB.limit(srcPos + len); 438 439 // Protect remainder of buffer, create slice to actually 440 // operate on. 441 ByteBuffer bb = srcBB.slice(); 442 443 srcBB.position(srcBB.limit()); 444 srcBB.limit(srcLim); 445 446 return bb; 447 } 448 }