1 /* 2 * Copyright (c) 2003, 2014, 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 the record version 113 checkRecordVersion(recordVersion, false); 114 115 /* 116 * Reasonably sure this is a V3, disable further checks. 117 * We can't do the same in the v2 check below, because 118 * read still needs to parse/handle the v2 clientHello. 119 */ 120 formatVerified = true; 121 122 /* 123 * One of the SSLv3/TLS message types. 124 */ 125 len = ((buf.get(pos + 3) & 0xff) << 8) + 126 (buf.get(pos + 4) & 0xff) + headerSize; 127 128 } else { 129 /* 130 * Must be SSLv2 or something unknown. 131 * Check if it's short (2 bytes) or 132 * long (3) header. 133 * 134 * Internals can warn about unsupported SSLv2 135 */ 136 boolean isShort = ((byteZero & 0x80) != 0); 137 138 if (isShort && 139 ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) { 140 141 ProtocolVersion recordVersion = 142 ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4)); 143 144 // check the record version 145 checkRecordVersion(recordVersion, true); 146 147 /* 148 * Client or Server Hello 149 */ 150 int mask = (isShort ? 0x7f : 0x3f); 151 len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) + 152 (isShort ? 2 : 3); 153 154 } else { 155 // Gobblygook! 156 throw new SSLException( 157 "Unrecognized SSL message, plaintext connection?"); 158 } 159 } 160 161 return len; 162 } 163 164 /* 165 * Pass the data down if it's internally cached, otherwise 166 * do it here. 167 * 168 * If internal data, data is decrypted internally. 169 * 170 * If external data(app), return a new ByteBuffer with data to 171 * process. 172 */ 173 ByteBuffer decrypt(Authenticator authenticator, 174 CipherBox box, ByteBuffer bb) throws BadPaddingException { 175 176 if (internalData) { 177 decrypt(authenticator, box); // MAC is checked during decryption 178 return tmpBB; 179 } 180 181 BadPaddingException reservedBPE = null; 182 int tagLen = 183 (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; 184 int cipheredLength = bb.remaining(); 185 186 if (!box.isNullCipher()) { 187 try { 188 // apply explicit nonce for AEAD/CBC cipher suites if needed 189 int nonceSize = 190 box.applyExplicitNonce(authenticator, contentType(), bb); 191 192 // decrypt the content 193 if (box.isAEADMode()) { 194 // DON'T encrypt the nonce_explicit for AEAD mode 195 bb.position(bb.position() + nonceSize); 196 } // The explicit IV for CBC mode can be decrypted. 197 198 // Note that the CipherBox.decrypt() does not change 199 // the capacity of the buffer. 200 box.decrypt(bb, tagLen); 201 bb.position(nonceSize); // We don't actually remove the nonce. 202 } catch (BadPaddingException bpe) { 203 // RFC 2246 states that decryption_failed should be used 204 // for this purpose. However, that allows certain attacks, 205 // so we just send bad record MAC. We also need to make 206 // sure to always check the MAC to avoid a timing attack 207 // for the same issue. See paper by Vaudenay et al and the 208 // update in RFC 4346/5246. 209 // 210 // Failover to message authentication code checking. 211 reservedBPE = bpe; 212 } 213 } 214 215 // Requires message authentication code for null, stream and block 216 // cipher suites. 217 if ((authenticator instanceof MAC) && (tagLen != 0)) { 218 MAC signer = (MAC)authenticator; 219 int macOffset = bb.limit() - tagLen; 220 221 // Note that although it is not necessary, we run the same MAC 222 // computation and comparison on the payload for both stream 223 // cipher and CBC block cipher. 224 if (bb.remaining() < tagLen) { 225 // negative data length, something is wrong 226 if (reservedBPE == null) { 227 reservedBPE = new BadPaddingException("bad record"); 228 } 229 230 // set offset of the dummy MAC 231 macOffset = cipheredLength - tagLen; 232 bb.limit(cipheredLength); 233 } 234 235 // Run MAC computation and comparison on the payload. 236 if (checkMacTags(contentType(), bb, signer, false)) { 237 if (reservedBPE == null) { 238 reservedBPE = new BadPaddingException("bad record MAC"); 239 } 240 } 241 242 // Run MAC computation and comparison on the remainder. 243 // 244 // It is only necessary for CBC block cipher. It is used to get a 245 // constant time of MAC computation and comparison on each record. 246 if (box.isCBCMode()) { 247 int remainingLen = calculateRemainingLen( 248 signer, cipheredLength, macOffset); 249 250 // NOTE: here we use the InputRecord.buf because I did not find 251 // an effective way to work on ByteBuffer when its capacity is 252 // less than remainingLen. 253 254 // NOTE: remainingLen may be bigger (less than 1 block of the 255 // hash algorithm of the MAC) than the cipheredLength. However, 256 // We won't need to worry about it because we always use a 257 // maximum buffer for every record. We need a change here if 258 // we use small buffer size in the future. 259 if (remainingLen > buf.length) { 260 // unlikely to happen, just a placehold 261 throw new RuntimeException( 262 "Internal buffer capacity error"); 263 } 264 265 // Won't need to worry about the result on the remainder. And 266 // then we won't need to worry about what's actual data to 267 // check MAC tag on. We start the check from the header of the 268 // buffer so that we don't need to construct a new byte buffer. 269 checkMacTags(contentType(), buf, 0, remainingLen, signer, true); 270 } 271 272 bb.limit(macOffset); 273 } 274 275 // Is it a failover? 276 if (reservedBPE != null) { 277 throw reservedBPE; 278 } 279 280 return bb.slice(); 281 } 282 283 /* 284 * Run MAC computation and comparison 285 * 286 * Please DON'T change the content of the ByteBuffer parameter! 287 */ 288 private static boolean checkMacTags(byte contentType, ByteBuffer bb, 289 MAC signer, boolean isSimulated) { 290 291 int position = bb.position(); 292 int tagLen = signer.MAClen(); 293 int lim = bb.limit(); 294 int macData = lim - tagLen; 295 296 bb.limit(macData); 297 byte[] hash = signer.compute(contentType, bb, isSimulated); 298 if (hash == null || tagLen != hash.length) { 299 // Something is wrong with MAC implementation. 300 throw new RuntimeException("Internal MAC error"); 301 } 302 303 bb.position(macData); 304 bb.limit(lim); 305 try { 306 int[] results = compareMacTags(bb, hash); 307 return (results[0] != 0); 308 } finally { 309 // reset to the data 310 bb.position(position); 311 bb.limit(macData); 312 } 313 } 314 315 /* 316 * A constant-time comparison of the MAC tags. 317 * 318 * Please DON'T change the content of the ByteBuffer parameter! 319 */ 320 private static int[] compareMacTags(ByteBuffer bb, byte[] tag) { 321 322 // An array of hits is used to prevent Hotspot optimization for 323 // the purpose of a constant-time check. 324 int[] results = {0, 0}; // {missed #, matched #} 325 326 // The caller ensures there are enough bytes available in the buffer. 327 // So we won't need to check the remaining of the buffer. 328 for (int i = 0; i < tag.length; i++) { 329 if (bb.get() != tag[i]) { 330 results[0]++; // mismatched bytes 331 } else { 332 results[1]++; // matched bytes 333 } 334 } 335 336 return results; 337 } 338 339 /* 340 * Override the actual write below. We do things this way to be 341 * consistent with InputRecord. InputRecord may try to write out 342 * data to the peer, and *then* throw an Exception. This forces 343 * data to be generated/output before the exception is ever 344 * generated. 345 */ 346 @Override 347 void writeBuffer(OutputStream s, byte [] buf, int off, int len) 348 throws IOException { 349 /* 350 * Copy data out of buffer, it's ready to go. 351 */ 352 ByteBuffer netBB = (ByteBuffer) 353 (ByteBuffer.allocate(len).put(buf, 0, len).flip()); 354 engine.writer.putOutboundDataSync(netBB); 355 } 356 357 /* 358 * Delineate or read a complete packet from src. 359 * 360 * If internal data (hs, alert, ccs), the data is read and 361 * stored internally. 362 * 363 * If external data (app), return a new ByteBuffer which points 364 * to the data to process. 365 */ 366 ByteBuffer read(ByteBuffer srcBB) throws IOException { 367 /* 368 * Could have a src == null/dst == null check here, 369 * but that was already checked by SSLEngine.unwrap before 370 * ever attempting to read. 371 */ 372 373 /* 374 * If we have anything besides application data, 375 * or if we haven't even done the initial v2 verification, 376 * we send this down to be processed by the underlying 377 * internal cache. 378 */ 379 if (!formatVerified || 380 (srcBB.get(srcBB.position()) != ct_application_data)) { 381 internalData = true; 382 read(new ByteBufferInputStream(srcBB), (OutputStream) null); 383 return tmpBB; 384 } 385 386 internalData = false; 387 388 int srcPos = srcBB.position(); 389 int srcLim = srcBB.limit(); 390 391 ProtocolVersion recordVersion = ProtocolVersion.valueOf( 392 srcBB.get(srcPos + 1), srcBB.get(srcPos + 2)); 393 394 // check the record version 395 checkRecordVersion(recordVersion, false); 396 397 /* 398 * It's really application data. How much to consume? 399 * Jump over the header. 400 */ 401 int len = bytesInCompletePacket(srcBB); 402 assert(len > 0); 403 404 if (debug != null && Debug.isOn("packet")) { 405 try { 406 HexDumpEncoder hd = new HexDumpEncoder(); 407 ByteBuffer bb = srcBB.duplicate(); // Use copy of BB 408 bb.limit(srcPos + len); 409 410 System.out.println("[Raw read (bb)]: length = " + len); 411 hd.encodeBuffer(bb, System.out); 412 } catch (IOException e) { } 413 } 414 415 // Demarcate past header to end of packet. 416 srcBB.position(srcPos + headerSize); 417 srcBB.limit(srcPos + len); 418 419 // Protect remainder of buffer, create slice to actually 420 // operate on. 421 ByteBuffer bb = srcBB.slice(); 422 423 srcBB.position(srcBB.limit()); 424 srcBB.limit(srcLim); 425 426 return bb; 427 } 428 }