1 /* SSLEngineImpl.java -- implementation of SSLEngine. 2 Copyright (C) 2006 Free Software Foundation, Inc. 3 4 This file is a part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or (at 9 your option) any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 19 USA 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package gnu.javax.net.ssl.provider; 40 41 import gnu.classpath.debug.Component; 42 import gnu.classpath.debug.SystemLogger; 43 44 import gnu.java.security.util.ByteBufferOutputStream; 45 import gnu.javax.net.ssl.Session; 46 import gnu.javax.net.ssl.SSLRecordHandler; 47 48 import java.nio.BufferOverflowException; 49 import java.nio.ByteBuffer; 50 import java.nio.ByteOrder; 51 52 import java.security.NoSuchAlgorithmException; 53 import java.util.ArrayList; 54 import java.util.List; 55 import java.util.zip.DataFormatException; 56 57 import javax.crypto.IllegalBlockSizeException; 58 import javax.crypto.ShortBufferException; 59 import javax.net.ssl.SSLEngine; 60 import javax.net.ssl.SSLEngineResult; 61 import javax.net.ssl.SSLException; 62 import javax.net.ssl.SSLSession; 63 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 64 import javax.net.ssl.SSLEngineResult.Status; 65 66 public final class SSLEngineImpl extends SSLEngine 67 { 68 final SSLContextImpl contextImpl; 69 private SSLRecordHandler[] handlers; 70 private static final SystemLogger logger = SystemLogger.SYSTEM; 71 private SessionImpl session; 72 private InputSecurityParameters insec; 73 private OutputSecurityParameters outsec; 74 private boolean inClosed; 75 private boolean outClosed; 76 private boolean createSessions; 77 private boolean needClientAuth; 78 private boolean wantClientAuth; 79 private boolean initialHandshakeDone; 80 private AbstractHandshake handshake; 81 private Alert lastAlert; 82 private SSLEngineResult.HandshakeStatus handshakeStatus; 83 private boolean changeCipherSpec; 84 85 private String[] enabledSuites; 86 private String[] enabledProtocols; 87 88 /** 89 * We can receive any message chunked across multiple records, 90 * including alerts, even though all alert messages are only two 91 * bytes long. Handshake messages are de-chunked in the handshake 92 * handler, change-cipher-spec messages are always empty, and we 93 * don't care about chunking of application messages. 94 * 95 * This buffer will hold the incomplete alert that we receive, if 96 * any. 97 */ 98 private final ByteBuffer alertBuffer; 99 100 private Mode mode; 101 102 private enum Mode { SERVER, CLIENT } 103 SSLEngineImpl(SSLContextImpl contextImpl, String host, int port)104 SSLEngineImpl (SSLContextImpl contextImpl, String host, int port) 105 { 106 super(host, port); 107 this.contextImpl = contextImpl; 108 handlers = new SSLRecordHandler[256]; 109 session = new SessionImpl(); 110 session.suite = CipherSuite.TLS_NULL_WITH_NULL_NULL; 111 session.version = ProtocolVersion.TLS_1_1; 112 byte[] sid = new byte[32]; 113 contextImpl.random.nextBytes(sid); 114 session.setId(new Session.ID(sid)); 115 session.setRandom(contextImpl.random); 116 117 if (Debug.DEBUG) 118 logger.logv(Component.SSL_RECORD_LAYER, "generated session ID {0} with random {1}", 119 session.id(), contextImpl.random); 120 121 // Begin with no encryption. 122 insec = new InputSecurityParameters (null, null, null, session, 123 CipherSuite.TLS_NULL_WITH_NULL_NULL); 124 outsec = new OutputSecurityParameters (null, null, null, session, 125 CipherSuite.TLS_NULL_WITH_NULL_NULL); 126 inClosed = false; 127 outClosed = false; 128 needClientAuth = false; 129 wantClientAuth = false; 130 createSessions = true; 131 initialHandshakeDone = false; 132 alertBuffer = ByteBuffer.wrap (new byte[2]); 133 mode = null; 134 lastAlert = null; 135 handshakeStatus = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; 136 changeCipherSpec = false; 137 138 // Set up default protocols and suites. 139 enabledProtocols = new String[] { 140 ProtocolVersion.TLS_1_1.toString(), 141 ProtocolVersion.TLS_1.toString(), 142 ProtocolVersion.SSL_3.toString() 143 }; 144 enabledSuites = defaultSuites(); 145 } 146 defaultSuites()147 static String[] defaultSuites() 148 { 149 return new String[] { 150 CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA.toString(), 151 CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA.toString(), 152 CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA.toString(), 153 CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA.toString(), 154 CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA.toString(), 155 CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA.toString(), 156 CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA.toString(), 157 CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA.toString(), 158 CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA.toString(), 159 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.toString(), 160 CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.toString(), 161 CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA.toString(), 162 CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA.toString(), 163 CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA.toString(), 164 CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA.toString(), 165 CipherSuite.TLS_RSA_WITH_RC4_128_MD5.toString(), 166 CipherSuite.TLS_RSA_WITH_RC4_128_SHA.toString(), 167 CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA.toString(), 168 CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA.toString(), 169 CipherSuite.TLS_DH_DSS_WITH_DES_CBC_SHA.toString(), 170 CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.toString(), 171 CipherSuite.TLS_RSA_WITH_DES_CBC_SHA.toString(), 172 CipherSuite.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(), 173 CipherSuite.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(), 174 CipherSuite.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(), 175 CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.toString(), 176 CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(), 177 CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(), 178 CipherSuite.TLS_RSA_WITH_NULL_MD5.toString(), 179 CipherSuite.TLS_RSA_WITH_NULL_SHA.toString() 180 }; 181 } 182 183 // XXX implement? 184 /*public void registerHandler (final int contentType, 185 SSLRecordHandler handler) 186 throws SSLException 187 { 188 if (type.equals (ContentType.CHANGE_CIPHER_SPEC) 189 || type.equals (ContentType.ALERT) 190 || type.equals (ContentType.HANDSHAKE) 191 || type.equals (ContentType.APPLICATION_DATA)) 192 throw new SSLException ("can't override handler for content type " + type); 193 int i = type.getValue (); 194 if (i < 0 || i > 255) 195 throw new SSLException ("illegal content type: " + type); 196 handlers[i] = handler; 197 }*/ 198 199 @Override beginHandshake()200 public void beginHandshake () throws SSLException 201 { 202 if (Debug.DEBUG) 203 logger.log(Component.SSL_HANDSHAKE, "{0} handshake begins", mode); 204 205 if (mode == null) 206 throw new IllegalStateException("setUseClientMode was never used"); 207 208 switch (mode) 209 { 210 case SERVER: 211 if (getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) 212 throw new SSLException("handshake already in progress"); 213 try 214 { 215 handshake = new ServerHandshake(initialHandshakeDone, this); 216 } 217 catch (NoSuchAlgorithmException nsae) 218 { 219 throw new SSLException(nsae); 220 } 221 break; 222 223 case CLIENT: 224 try 225 { 226 handshake = new ClientHandshake(this); 227 } 228 catch (NoSuchAlgorithmException nsae) 229 { 230 throw new SSLException(nsae); 231 } 232 break; 233 } 234 } 235 236 @Override closeInbound()237 public void closeInbound() 238 { 239 inClosed = true; 240 } 241 242 @Override closeOutbound()243 public void closeOutbound() 244 { 245 lastAlert = new Alert(Alert.Level.WARNING, Alert.Description.CLOSE_NOTIFY); 246 } 247 248 @Override getDelegatedTask()249 public Runnable getDelegatedTask() 250 { 251 if (handshake == null) 252 return null; 253 return handshake.getTask(); 254 } 255 256 @Override getEnabledCipherSuites()257 public String[] getEnabledCipherSuites() 258 { 259 return (String[]) enabledSuites.clone(); 260 } 261 262 @Override getEnabledProtocols()263 public String[] getEnabledProtocols() 264 { 265 return (String[]) enabledProtocols.clone(); 266 } 267 268 @Override getEnableSessionCreation()269 public boolean getEnableSessionCreation() 270 { 271 return createSessions; 272 } 273 274 @Override getHandshakeStatus()275 public HandshakeStatus getHandshakeStatus() 276 { 277 if (handshake == null) 278 return HandshakeStatus.NOT_HANDSHAKING; 279 return handshake.status(); 280 } 281 282 @Override getNeedClientAuth()283 public boolean getNeedClientAuth() 284 { 285 return needClientAuth; 286 } 287 288 @Override getSession()289 public SSLSession getSession() 290 { 291 return session; 292 } 293 294 @Override getUseClientMode()295 public boolean getUseClientMode () 296 { 297 return (mode == Mode.CLIENT); 298 } 299 300 @Override getWantClientAuth()301 public boolean getWantClientAuth() 302 { 303 return wantClientAuth; 304 } 305 306 @Override isInboundDone()307 public boolean isInboundDone() 308 { 309 return inClosed; 310 } 311 312 @Override isOutboundDone()313 public boolean isOutboundDone() 314 { 315 return outClosed; 316 } 317 318 @Override setEnableSessionCreation(final boolean createSessions)319 public void setEnableSessionCreation(final boolean createSessions) 320 { 321 this.createSessions = createSessions; 322 } 323 324 @Override setEnabledCipherSuites(final String[] suites)325 public void setEnabledCipherSuites(final String[] suites) 326 { 327 if (suites.length == 0) 328 throw new IllegalArgumentException("need at least one suite"); 329 enabledSuites = (String[]) suites.clone(); 330 } 331 332 @Override setEnabledProtocols(final String[] protocols)333 public void setEnabledProtocols(final String[] protocols) 334 { 335 if (protocols.length == 0) 336 throw new IllegalArgumentException("need at least one protocol"); 337 enabledProtocols = (String[]) protocols.clone(); 338 } 339 340 @Override getSupportedCipherSuites()341 public String[] getSupportedCipherSuites() 342 { 343 // XXX if we ever want to support "pluggable" cipher suites, we'll need 344 // to figure this out. 345 346 return CipherSuite.availableSuiteNames().toArray(new String[0]); 347 } 348 349 @Override getSupportedProtocols()350 public String[] getSupportedProtocols() 351 { 352 return new String[] { ProtocolVersion.SSL_3.toString(), 353 ProtocolVersion.TLS_1.toString(), 354 ProtocolVersion.TLS_1_1.toString() }; 355 } 356 357 @Override setNeedClientAuth(final boolean needClientAuth)358 public void setNeedClientAuth(final boolean needClientAuth) 359 { 360 this.needClientAuth = needClientAuth; 361 } 362 363 @Override setUseClientMode(final boolean clientMode)364 public void setUseClientMode (final boolean clientMode) 365 { 366 if (clientMode) 367 mode = Mode.CLIENT; 368 else 369 mode = Mode.SERVER; 370 } 371 setWantClientAuth(final boolean wantClientAuth)372 public @Override void setWantClientAuth(final boolean wantClientAuth) 373 { 374 this.wantClientAuth = wantClientAuth; 375 } 376 unwrap(final ByteBuffer source, final ByteBuffer[] sinks, final int offset, final int length)377 public @Override SSLEngineResult unwrap (final ByteBuffer source, 378 final ByteBuffer[] sinks, 379 final int offset, final int length) 380 throws SSLException 381 { 382 if (mode == null) 383 throw new IllegalStateException ("setUseClientMode was never called"); 384 385 if (inClosed) 386 return new SSLEngineResult(SSLEngineResult.Status.CLOSED, 387 handshakeStatus, 0, 0); 388 389 if (source.remaining() < 5) 390 { 391 return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, 392 handshakeStatus, 0, 0); 393 } 394 395 Record record = null; 396 boolean helloV2 = false; 397 398 // XXX: messages may be chunked across multiple records; does this 399 // include the SSLv2 message? I don't think it does, but we should 400 // make sure. 401 if (!getUseClientMode() && (source.get(source.position()) & 0x80) == 0x80) 402 { 403 if (handshake == null) 404 beginHandshake(); 405 int hellolen = source.getShort(source.position()) & 0x7FFF; 406 this.handshake.handleV2Hello(source.slice()); 407 if (!insec.cipherSuite().equals (CipherSuite.TLS_NULL_WITH_NULL_NULL)) 408 throw new SSLException ("received SSLv2 client hello in encrypted " 409 + "session; this is invalid."); 410 if (Debug.DEBUG) 411 logger.log (Component.SSL_RECORD_LAYER, 412 "converting SSLv2 client hello to version 3 hello"); 413 414 source.getShort(); // skip length 415 ClientHelloV2 v2 = new ClientHelloV2(source.slice()); 416 417 if (Debug.DEBUG) 418 logger.log(Component.SSL_RECORD_LAYER, "v2 hello: {0}", v2); 419 420 List<CipherSuite> suites = v2.cipherSpecs(); 421 422 ClientHelloBuilder hello = new ClientHelloBuilder(); 423 hello.setVersion(v2.version ()); 424 425 Random random = hello.random(); 426 byte[] challenge = v2.challenge(); 427 if (challenge.length < 32) 428 { 429 byte[] b = new byte[32]; 430 System.arraycopy(challenge, 0, b, b.length - challenge.length, 431 challenge.length); 432 challenge = b; 433 } 434 random.setGmtUnixTime((challenge[0] & 0xFF) << 24 435 | (challenge[1] & 0xFF) << 16 436 | (challenge[2] & 0xFF) << 8 437 | (challenge[3] & 0xFF)); 438 random.setRandomBytes(challenge, 4); 439 440 byte[] sessionId = v2.sessionId(); 441 hello.setSessionId(sessionId, 0, sessionId.length); 442 hello.setCipherSuites(suites); 443 ArrayList<CompressionMethod> comps = new ArrayList<CompressionMethod>(1); 444 comps.add(CompressionMethod.NULL); 445 hello.setCompressionMethods(comps); 446 447 record = new Record(ByteBuffer.allocate(hello.length() + 9)); 448 record.setContentType(ContentType.HANDSHAKE); 449 record.setVersion(v2.version()); 450 record.setLength(hello.length() + 4); 451 452 Handshake handshake = new Handshake(record.fragment()); 453 handshake.setLength(hello.length()); 454 handshake.setType(Handshake.Type.CLIENT_HELLO); 455 456 handshake.bodyBuffer().put(hello.buffer()); 457 source.position(source.position() + hellolen); 458 helloV2 = true; 459 } 460 else 461 record = new Record(source); 462 463 ContentType type = record.contentType (); 464 465 if (Debug.DEBUG) 466 logger.log(Component.SSL_RECORD_LAYER, "input record:\n{0}", record); 467 468 if (record.length() > session.getPacketBufferSize() - 5) 469 { 470 lastAlert = new Alert(Alert.Level.FATAL, 471 Alert.Description.RECORD_OVERFLOW); 472 throw new AlertException(lastAlert); 473 } 474 475 ByteBufferOutputStream sysMsg = null; 476 ByteBuffer msg = null; 477 478 int produced = 0; 479 try 480 { 481 // Application data will get decrypted directly into the user's 482 // output buffers. 483 if (record.contentType() == ContentType.APPLICATION_DATA) 484 produced = insec.decrypt(record, sinks, offset, length); 485 else 486 { 487 if (insec.cipherSuite() == CipherSuite.TLS_NULL_WITH_NULL_NULL) 488 msg = record.fragment(); 489 else 490 { 491 sysMsg = new ByteBufferOutputStream(); 492 insec.decrypt(record, sysMsg); 493 } 494 } 495 496 // Advance the input buffer past the record we just read. 497 if (!helloV2) 498 source.position(source.position() + record.length() + 5); 499 } 500 catch (BufferOverflowException boe) 501 { 502 // We throw this if the output buffers are not large enough; signal 503 // the caller about this. 504 logger.log(Component.SSL_RECORD_LAYER, "buffer overflow when decrypting", boe); 505 return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, 506 handshakeStatus, 0, 0); 507 } 508 catch (IllegalBlockSizeException ibse) 509 { 510 lastAlert = new Alert(Alert.Level.FATAL, 511 Alert.Description.BAD_RECORD_MAC); 512 throw new AlertException(lastAlert, ibse); 513 } 514 catch (DataFormatException dfe) 515 { 516 lastAlert = new Alert(Alert.Level.FATAL, 517 Alert.Description.DECOMPRESSION_FAILURE); 518 throw new AlertException(lastAlert, dfe); 519 } 520 catch (MacException me) 521 { 522 lastAlert = new Alert(Alert.Level.FATAL, 523 Alert.Description.BAD_RECORD_MAC); 524 throw new AlertException(lastAlert, me); 525 } 526 catch (ShortBufferException sbe) 527 { 528 // We've messed up if this happens. 529 lastAlert = new Alert(Alert.Level.FATAL, 530 Alert.Description.INTERNAL_ERROR); 531 throw new AlertException(lastAlert, sbe); 532 } 533 534 SSLEngineResult result = null; 535 536 // If we need to handle the output here, do it. Otherwise, the output 537 // has been stored in the supplied output buffers. 538 if (sysMsg != null) 539 { 540 if (Debug.DEBUG) 541 logger.logv(Component.SSL_RECORD_LAYER, "sysmessage {0}", sysMsg); 542 msg = sysMsg.buffer(); 543 } 544 545 if (type == ContentType.CHANGE_CIPHER_SPEC) 546 { 547 // We *may* get a partial message, even though the message is only 548 // one byte long. 549 if (msg.remaining() == 0) 550 { 551 result = new SSLEngineResult (SSLEngineResult.Status.OK, 552 handshakeStatus, 553 record.length() + 5, 0); 554 } 555 else 556 { 557 byte b = msg.get(); 558 if (b != 1) 559 throw new SSLException ("unknown ChangeCipherSpec value: " + (b & 0xFF)); 560 InputSecurityParameters params = handshake.getInputParams(); 561 logger.log (Component.SSL_RECORD_LAYER, 562 "switching to input security parameters {0}", 563 params.cipherSuite()); 564 insec = params; 565 result = new SSLEngineResult (SSLEngineResult.Status.OK, 566 handshakeStatus, 567 record.length() + 5, 0); 568 } 569 } 570 else if (type == ContentType.ALERT) 571 { 572 int len = 0; 573 if (alertBuffer.position() > 0) 574 { 575 alertBuffer.put(msg.get()); 576 len = 1; 577 } 578 if (Debug.DEBUG) 579 logger.logv(Component.SSL_RECORD_LAYER, "processing alerts {0}", 580 Util.wrapBuffer(msg)); 581 len += msg.remaining() / 2; 582 Alert[] alerts = new Alert[len]; 583 int i = 0; 584 if (alertBuffer.position() > 0) 585 { 586 alertBuffer.flip(); 587 alerts[0] = new Alert(alertBuffer); 588 i++; 589 } 590 while (i < alerts.length) 591 { 592 alerts[i++] = new Alert(msg.duplicate()); 593 msg.position(msg.position() + 2); 594 } 595 if (Debug.DEBUG) 596 logger.logv(Component.SSL_RECORD_LAYER, "alerts: {0}", alerts.length); 597 598 for (i = 0; i < alerts.length; i++) 599 { 600 if (alerts[i].level() == Alert.Level.FATAL) 601 throw new AlertException(alerts[i], false); 602 if (alerts[i].description() != Alert.Description.CLOSE_NOTIFY) 603 logger.log(java.util.logging.Level.WARNING, 604 "received alert: {0}", alerts[i]); 605 if (alerts[i].description() == Alert.Description.CLOSE_NOTIFY) 606 inClosed = true; 607 } 608 609 if (msg.hasRemaining()) 610 alertBuffer.position(0).limit(2); 611 612 result = new SSLEngineResult (SSLEngineResult.Status.OK, 613 handshakeStatus, 614 record.length() + 5, 0); 615 } 616 else if (type == ContentType.HANDSHAKE) 617 { 618 if (handshake == null) 619 beginHandshake(); 620 try 621 { 622 handshakeStatus = handshake.handleInput(msg); 623 } 624 catch (AlertException ae) 625 { 626 lastAlert = ae.alert(); 627 return new SSLEngineResult(SSLEngineResult.Status.OK, 628 SSLEngineResult.HandshakeStatus.NEED_WRAP, 629 0, 0); 630 } 631 if (Debug.DEBUG) 632 logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}", handshakeStatus); 633 result = new SSLEngineResult(SSLEngineResult.Status.OK, 634 handshakeStatus, 635 record.length() + 5, 636 0); 637 if (handshakeStatus == HandshakeStatus.FINISHED) 638 { 639 handshake = null; 640 handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; 641 } 642 } 643 else if (type == ContentType.APPLICATION_DATA) 644 { 645 // Do nothing more; the application data has been put into 646 // the output buffers. 647 result = new SSLEngineResult(SSLEngineResult.Status.OK, 648 handshakeStatus, 649 record.length() + 5, 650 produced); 651 } 652 else 653 { 654 SSLRecordHandler handler = handlers[type.getValue()]; 655 if (handler != null) 656 { 657 result = new SSLEngineResult(SSLEngineResult.Status.OK, 658 handshakeStatus, 659 record.length() + 5, 660 0); 661 } 662 else 663 throw new SSLException ("unknown content type: " + type); 664 } 665 666 if (Debug.DEBUG) 667 logger.logv(Component.SSL_RECORD_LAYER, "return result: {0}", result); 668 669 return result; 670 } 671 wrap(ByteBuffer[] sources, int offset, int length, ByteBuffer sink)672 public @Override SSLEngineResult wrap (ByteBuffer[] sources, int offset, int length, 673 ByteBuffer sink) 674 throws SSLException 675 { 676 if (mode == null) 677 throw new IllegalStateException ("setUseClientMode was never called"); 678 679 if (outClosed) 680 return new SSLEngineResult(SSLEngineResult.Status.CLOSED, 681 handshakeStatus, 0, 0); 682 683 ContentType type = null; 684 ByteBuffer sysMessage = null; 685 if (Debug.DEBUG) 686 logger.logv(Component.SSL_RECORD_LAYER, "wrap {0} {1} {2} {3} / {4}", 687 sources, offset, length, sink, getHandshakeStatus()); 688 if (lastAlert != null) 689 { 690 type = ContentType.ALERT; 691 sysMessage = ByteBuffer.allocate(2); 692 Alert alert = new Alert(sysMessage); 693 alert.setDescription(lastAlert.description()); 694 alert.setLevel(lastAlert.level()); 695 if (lastAlert.description() == Alert.Description.CLOSE_NOTIFY) 696 outClosed = true; 697 } 698 else if (changeCipherSpec) 699 { 700 type = ContentType.CHANGE_CIPHER_SPEC; 701 sysMessage = ByteBuffer.allocate(1); 702 sysMessage.put(0, (byte) 1); 703 } 704 else if (getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) 705 { 706 // If we are not encrypting, optimize the handshake to fill 707 // the buffer directly. 708 if (outsec.suite() == CipherSuite.TLS_NULL_WITH_NULL_NULL) 709 { 710 int orig = sink.position(); 711 sink.order(ByteOrder.BIG_ENDIAN); 712 sink.put((byte) ContentType.HANDSHAKE.getValue()); 713 sink.putShort((short) session.version.rawValue()); 714 sink.putShort((short) 0); 715 handshakeStatus = handshake.handleOutput(sink); 716 int produced = sink.position() - orig; 717 sink.putShort(orig + 3, (short) (produced - 5)); 718 if (Debug.DEBUG) 719 logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}", 720 new Record((ByteBuffer) sink.duplicate().position(orig))); 721 SSLEngineResult result = new SSLEngineResult(SSLEngineResult.Status.OK, 722 handshakeStatus, 0, produced); 723 724 // Note, this will only happen if we transition from 725 // TLS_NULL_WITH_NULL_NULL *to* TLS_NULL_WITH_NULL_NULL, which 726 // doesn't make a lot of sense, but we support it anyway. 727 if (handshakeStatus == HandshakeStatus.FINISHED) 728 { 729 handshake = null; // finished with it. 730 handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; 731 } 732 return result; 733 } 734 735 // Rough guideline; XXX. 736 sysMessage = ByteBuffer.allocate(sink.remaining() - 2048); 737 type = ContentType.HANDSHAKE; 738 try 739 { 740 handshakeStatus = handshake.handleOutput(sysMessage); 741 } 742 catch (AlertException ae) 743 { 744 lastAlert = ae.alert(); 745 return new SSLEngineResult(Status.OK, 746 HandshakeStatus.NEED_WRAP, 0, 0); 747 } 748 sysMessage.flip(); 749 if (Debug.DEBUG) 750 logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}", 751 handshakeStatus); 752 } 753 754 int produced = 0; 755 int consumed = 0; 756 757 try 758 { 759 int orig = sink.position(); 760 int[] inout = null; 761 if (sysMessage != null) 762 { 763 if (Debug.DEBUG) 764 logger.logv(Component.SSL_RECORD_LAYER, "encrypt system message {0} to {1}", sysMessage, sink); 765 inout = outsec.encrypt(new ByteBuffer[] { sysMessage }, 0, 1, 766 type, sink); 767 produced = inout[1]; 768 } 769 else 770 { 771 if (outsec.needToSplitPayload()) 772 { 773 inout = outsec.encrypt(sources, offset, 1, 774 ContentType.APPLICATION_DATA, sink); 775 consumed = inout[0]; 776 produced = inout[1]; 777 if (length > 1) 778 { 779 inout = outsec.encrypt(sources, offset+1, length-1, 780 ContentType.APPLICATION_DATA, sink); 781 consumed += inout[0]; 782 produced += inout[1]; 783 } 784 } 785 else 786 { 787 inout = outsec.encrypt(sources, offset, length, 788 ContentType.APPLICATION_DATA, sink); 789 consumed = inout[0]; 790 produced = inout[1]; 791 } 792 } 793 794 if (Debug.DEBUG) 795 logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}", 796 new Record((ByteBuffer) sink.duplicate().position(orig).limit(produced))); 797 } 798 catch (ShortBufferException sbe) 799 { 800 // We don't expect this to happen, except for bugs; signal an 801 // internal error. 802 lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR); 803 return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0); 804 } 805 catch (IllegalBlockSizeException ibse) 806 { 807 // We don't expect this to happen, except for bugs; signal an 808 // internal error. 809 lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR); 810 return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0); 811 } 812 catch (DataFormatException dfe) 813 { 814 // We don't expect this to happen; signal an internal error. 815 lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR); 816 return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0); 817 } 818 819 if (lastAlert != null && lastAlert.level() == Alert.Level.FATAL) 820 { 821 AlertException ae = new AlertException(lastAlert); 822 lastAlert = null; 823 throw ae; 824 } 825 826 if (changeCipherSpec) 827 { 828 outsec = handshake.getOutputParams(); 829 changeCipherSpec = false; 830 } 831 SSLEngineResult result 832 = new SSLEngineResult(outClosed ? SSLEngineResult.Status.CLOSED 833 : SSLEngineResult.Status.OK, 834 handshakeStatus, consumed, produced); 835 if (handshakeStatus == HandshakeStatus.FINISHED) 836 { 837 handshake = null; // done with it. 838 handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; 839 } 840 return result; 841 } 842 843 // Package-private methods. 844 session()845 SessionImpl session () 846 { 847 return session; 848 } 849 setSession(SessionImpl session)850 void setSession(SessionImpl session) 851 { 852 this.session = session; 853 } 854 changeCipherSpec()855 void changeCipherSpec() 856 { 857 changeCipherSpec = true; 858 } 859 } 860