123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860 |
- /* SSLEngineImpl.java -- implementation of SSLEngine.
- Copyright (C) 2006 Free Software Foundation, Inc.
- This file is a part of GNU Classpath.
- GNU Classpath is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at
- your option) any later version.
- GNU Classpath is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with GNU Classpath; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
- USA
- Linking this library statically or dynamically with other modules is
- making a combined work based on this library. Thus, the terms and
- conditions of the GNU General Public License cover the whole
- combination.
- As a special exception, the copyright holders of this library give you
- permission to link this library with independent modules to produce an
- executable, regardless of the license terms of these independent
- modules, and to copy and distribute the resulting executable under
- terms of your choice, provided that you also meet, for each linked
- independent module, the terms and conditions of the license of that
- module. An independent module is a module which is not derived from
- or based on this library. If you modify this library, you may extend
- this exception to your version of the library, but you are not
- obligated to do so. If you do not wish to do so, delete this
- exception statement from your version. */
- package gnu.javax.net.ssl.provider;
- import gnu.classpath.debug.Component;
- import gnu.classpath.debug.SystemLogger;
- import gnu.java.security.util.ByteBufferOutputStream;
- import gnu.javax.net.ssl.Session;
- import gnu.javax.net.ssl.SSLRecordHandler;
- import java.nio.BufferOverflowException;
- import java.nio.ByteBuffer;
- import java.nio.ByteOrder;
- import java.security.NoSuchAlgorithmException;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.zip.DataFormatException;
- import javax.crypto.IllegalBlockSizeException;
- import javax.crypto.ShortBufferException;
- import javax.net.ssl.SSLEngine;
- import javax.net.ssl.SSLEngineResult;
- import javax.net.ssl.SSLException;
- import javax.net.ssl.SSLSession;
- import javax.net.ssl.SSLEngineResult.HandshakeStatus;
- import javax.net.ssl.SSLEngineResult.Status;
- public final class SSLEngineImpl extends SSLEngine
- {
- final SSLContextImpl contextImpl;
- private SSLRecordHandler[] handlers;
- private static final SystemLogger logger = SystemLogger.SYSTEM;
- private SessionImpl session;
- private InputSecurityParameters insec;
- private OutputSecurityParameters outsec;
- private boolean inClosed;
- private boolean outClosed;
- private boolean createSessions;
- private boolean needClientAuth;
- private boolean wantClientAuth;
- private boolean initialHandshakeDone;
- private AbstractHandshake handshake;
- private Alert lastAlert;
- private SSLEngineResult.HandshakeStatus handshakeStatus;
- private boolean changeCipherSpec;
- private String[] enabledSuites;
- private String[] enabledProtocols;
- /**
- * We can receive any message chunked across multiple records,
- * including alerts, even though all alert messages are only two
- * bytes long. Handshake messages are de-chunked in the handshake
- * handler, change-cipher-spec messages are always empty, and we
- * don't care about chunking of application messages.
- *
- * This buffer will hold the incomplete alert that we receive, if
- * any.
- */
- private final ByteBuffer alertBuffer;
- private Mode mode;
- private enum Mode { SERVER, CLIENT }
- SSLEngineImpl (SSLContextImpl contextImpl, String host, int port)
- {
- super(host, port);
- this.contextImpl = contextImpl;
- handlers = new SSLRecordHandler[256];
- session = new SessionImpl();
- session.suite = CipherSuite.TLS_NULL_WITH_NULL_NULL;
- session.version = ProtocolVersion.TLS_1_1;
- byte[] sid = new byte[32];
- contextImpl.random.nextBytes(sid);
- session.setId(new Session.ID(sid));
- session.setRandom(contextImpl.random);
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "generated session ID {0} with random {1}",
- session.id(), contextImpl.random);
- // Begin with no encryption.
- insec = new InputSecurityParameters (null, null, null, session,
- CipherSuite.TLS_NULL_WITH_NULL_NULL);
- outsec = new OutputSecurityParameters (null, null, null, session,
- CipherSuite.TLS_NULL_WITH_NULL_NULL);
- inClosed = false;
- outClosed = false;
- needClientAuth = false;
- wantClientAuth = false;
- createSessions = true;
- initialHandshakeDone = false;
- alertBuffer = ByteBuffer.wrap (new byte[2]);
- mode = null;
- lastAlert = null;
- handshakeStatus = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
- changeCipherSpec = false;
- // Set up default protocols and suites.
- enabledProtocols = new String[] {
- ProtocolVersion.TLS_1_1.toString(),
- ProtocolVersion.TLS_1.toString(),
- ProtocolVersion.SSL_3.toString()
- };
- enabledSuites = defaultSuites();
- }
- static String[] defaultSuites()
- {
- return new String[] {
- CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA.toString(),
- CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA.toString(),
- CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA.toString(),
- CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA.toString(),
- CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA.toString(),
- CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA.toString(),
- CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA.toString(),
- CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.toString(),
- CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
- CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
- CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
- CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_WITH_RC4_128_MD5.toString(),
- CipherSuite.TLS_RSA_WITH_RC4_128_SHA.toString(),
- CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA.toString(),
- CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA.toString(),
- CipherSuite.TLS_DH_DSS_WITH_DES_CBC_SHA.toString(),
- CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_WITH_DES_CBC_SHA.toString(),
- CipherSuite.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
- CipherSuite.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.toString(),
- CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
- CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
- CipherSuite.TLS_RSA_WITH_NULL_MD5.toString(),
- CipherSuite.TLS_RSA_WITH_NULL_SHA.toString()
- };
- }
- // XXX implement?
- /*public void registerHandler (final int contentType,
- SSLRecordHandler handler)
- throws SSLException
- {
- if (type.equals (ContentType.CHANGE_CIPHER_SPEC)
- || type.equals (ContentType.ALERT)
- || type.equals (ContentType.HANDSHAKE)
- || type.equals (ContentType.APPLICATION_DATA))
- throw new SSLException ("can't override handler for content type " + type);
- int i = type.getValue ();
- if (i < 0 || i > 255)
- throw new SSLException ("illegal content type: " + type);
- handlers[i] = handler;
- }*/
- @Override
- public void beginHandshake () throws SSLException
- {
- if (Debug.DEBUG)
- logger.log(Component.SSL_HANDSHAKE, "{0} handshake begins", mode);
- if (mode == null)
- throw new IllegalStateException("setUseClientMode was never used");
- switch (mode)
- {
- case SERVER:
- if (getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
- throw new SSLException("handshake already in progress");
- try
- {
- handshake = new ServerHandshake(initialHandshakeDone, this);
- }
- catch (NoSuchAlgorithmException nsae)
- {
- throw new SSLException(nsae);
- }
- break;
- case CLIENT:
- try
- {
- handshake = new ClientHandshake(this);
- }
- catch (NoSuchAlgorithmException nsae)
- {
- throw new SSLException(nsae);
- }
- break;
- }
- }
- @Override
- public void closeInbound()
- {
- inClosed = true;
- }
- @Override
- public void closeOutbound()
- {
- lastAlert = new Alert(Alert.Level.WARNING, Alert.Description.CLOSE_NOTIFY);
- }
- @Override
- public Runnable getDelegatedTask()
- {
- if (handshake == null)
- return null;
- return handshake.getTask();
- }
- @Override
- public String[] getEnabledCipherSuites()
- {
- return (String[]) enabledSuites.clone();
- }
- @Override
- public String[] getEnabledProtocols()
- {
- return (String[]) enabledProtocols.clone();
- }
- @Override
- public boolean getEnableSessionCreation()
- {
- return createSessions;
- }
- @Override
- public HandshakeStatus getHandshakeStatus()
- {
- if (handshake == null)
- return HandshakeStatus.NOT_HANDSHAKING;
- return handshake.status();
- }
- @Override
- public boolean getNeedClientAuth()
- {
- return needClientAuth;
- }
- @Override
- public SSLSession getSession()
- {
- return session;
- }
- @Override
- public boolean getUseClientMode ()
- {
- return (mode == Mode.CLIENT);
- }
- @Override
- public boolean getWantClientAuth()
- {
- return wantClientAuth;
- }
- @Override
- public boolean isInboundDone()
- {
- return inClosed;
- }
- @Override
- public boolean isOutboundDone()
- {
- return outClosed;
- }
- @Override
- public void setEnableSessionCreation(final boolean createSessions)
- {
- this.createSessions = createSessions;
- }
- @Override
- public void setEnabledCipherSuites(final String[] suites)
- {
- if (suites.length == 0)
- throw new IllegalArgumentException("need at least one suite");
- enabledSuites = (String[]) suites.clone();
- }
- @Override
- public void setEnabledProtocols(final String[] protocols)
- {
- if (protocols.length == 0)
- throw new IllegalArgumentException("need at least one protocol");
- enabledProtocols = (String[]) protocols.clone();
- }
- @Override
- public String[] getSupportedCipherSuites()
- {
- // XXX if we ever want to support "pluggable" cipher suites, we'll need
- // to figure this out.
- return CipherSuite.availableSuiteNames().toArray(new String[0]);
- }
- @Override
- public String[] getSupportedProtocols()
- {
- return new String[] { ProtocolVersion.SSL_3.toString(),
- ProtocolVersion.TLS_1.toString(),
- ProtocolVersion.TLS_1_1.toString() };
- }
- @Override
- public void setNeedClientAuth(final boolean needClientAuth)
- {
- this.needClientAuth = needClientAuth;
- }
- @Override
- public void setUseClientMode (final boolean clientMode)
- {
- if (clientMode)
- mode = Mode.CLIENT;
- else
- mode = Mode.SERVER;
- }
- public @Override void setWantClientAuth(final boolean wantClientAuth)
- {
- this.wantClientAuth = wantClientAuth;
- }
- public @Override SSLEngineResult unwrap (final ByteBuffer source,
- final ByteBuffer[] sinks,
- final int offset, final int length)
- throws SSLException
- {
- if (mode == null)
- throw new IllegalStateException ("setUseClientMode was never called");
- if (inClosed)
- return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
- handshakeStatus, 0, 0);
- if (source.remaining() < 5)
- {
- return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
- handshakeStatus, 0, 0);
- }
- Record record = null;
- boolean helloV2 = false;
- // XXX: messages may be chunked across multiple records; does this
- // include the SSLv2 message? I don't think it does, but we should
- // make sure.
- if (!getUseClientMode() && (source.get(source.position()) & 0x80) == 0x80)
- {
- if (handshake == null)
- beginHandshake();
- int hellolen = source.getShort(source.position()) & 0x7FFF;
- this.handshake.handleV2Hello(source.slice());
- if (!insec.cipherSuite().equals (CipherSuite.TLS_NULL_WITH_NULL_NULL))
- throw new SSLException ("received SSLv2 client hello in encrypted "
- + "session; this is invalid.");
- if (Debug.DEBUG)
- logger.log (Component.SSL_RECORD_LAYER,
- "converting SSLv2 client hello to version 3 hello");
- source.getShort(); // skip length
- ClientHelloV2 v2 = new ClientHelloV2(source.slice());
- if (Debug.DEBUG)
- logger.log(Component.SSL_RECORD_LAYER, "v2 hello: {0}", v2);
- List<CipherSuite> suites = v2.cipherSpecs();
- ClientHelloBuilder hello = new ClientHelloBuilder();
- hello.setVersion(v2.version ());
- Random random = hello.random();
- byte[] challenge = v2.challenge();
- if (challenge.length < 32)
- {
- byte[] b = new byte[32];
- System.arraycopy(challenge, 0, b, b.length - challenge.length,
- challenge.length);
- challenge = b;
- }
- random.setGmtUnixTime((challenge[0] & 0xFF) << 24
- | (challenge[1] & 0xFF) << 16
- | (challenge[2] & 0xFF) << 8
- | (challenge[3] & 0xFF));
- random.setRandomBytes(challenge, 4);
- byte[] sessionId = v2.sessionId();
- hello.setSessionId(sessionId, 0, sessionId.length);
- hello.setCipherSuites(suites);
- ArrayList<CompressionMethod> comps = new ArrayList<CompressionMethod>(1);
- comps.add(CompressionMethod.NULL);
- hello.setCompressionMethods(comps);
- record = new Record(ByteBuffer.allocate(hello.length() + 9));
- record.setContentType(ContentType.HANDSHAKE);
- record.setVersion(v2.version());
- record.setLength(hello.length() + 4);
- Handshake handshake = new Handshake(record.fragment());
- handshake.setLength(hello.length());
- handshake.setType(Handshake.Type.CLIENT_HELLO);
- handshake.bodyBuffer().put(hello.buffer());
- source.position(source.position() + hellolen);
- helloV2 = true;
- }
- else
- record = new Record(source);
- ContentType type = record.contentType ();
- if (Debug.DEBUG)
- logger.log(Component.SSL_RECORD_LAYER, "input record:\n{0}", record);
- if (record.length() > session.getPacketBufferSize() - 5)
- {
- lastAlert = new Alert(Alert.Level.FATAL,
- Alert.Description.RECORD_OVERFLOW);
- throw new AlertException(lastAlert);
- }
- ByteBufferOutputStream sysMsg = null;
- ByteBuffer msg = null;
- int produced = 0;
- try
- {
- // Application data will get decrypted directly into the user's
- // output buffers.
- if (record.contentType() == ContentType.APPLICATION_DATA)
- produced = insec.decrypt(record, sinks, offset, length);
- else
- {
- if (insec.cipherSuite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
- msg = record.fragment();
- else
- {
- sysMsg = new ByteBufferOutputStream();
- insec.decrypt(record, sysMsg);
- }
- }
- // Advance the input buffer past the record we just read.
- if (!helloV2)
- source.position(source.position() + record.length() + 5);
- }
- catch (BufferOverflowException boe)
- {
- // We throw this if the output buffers are not large enough; signal
- // the caller about this.
- logger.log(Component.SSL_RECORD_LAYER, "buffer overflow when decrypting", boe);
- return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
- handshakeStatus, 0, 0);
- }
- catch (IllegalBlockSizeException ibse)
- {
- lastAlert = new Alert(Alert.Level.FATAL,
- Alert.Description.BAD_RECORD_MAC);
- throw new AlertException(lastAlert, ibse);
- }
- catch (DataFormatException dfe)
- {
- lastAlert = new Alert(Alert.Level.FATAL,
- Alert.Description.DECOMPRESSION_FAILURE);
- throw new AlertException(lastAlert, dfe);
- }
- catch (MacException me)
- {
- lastAlert = new Alert(Alert.Level.FATAL,
- Alert.Description.BAD_RECORD_MAC);
- throw new AlertException(lastAlert, me);
- }
- catch (ShortBufferException sbe)
- {
- // We've messed up if this happens.
- lastAlert = new Alert(Alert.Level.FATAL,
- Alert.Description.INTERNAL_ERROR);
- throw new AlertException(lastAlert, sbe);
- }
- SSLEngineResult result = null;
- // If we need to handle the output here, do it. Otherwise, the output
- // has been stored in the supplied output buffers.
- if (sysMsg != null)
- {
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "sysmessage {0}", sysMsg);
- msg = sysMsg.buffer();
- }
- if (type == ContentType.CHANGE_CIPHER_SPEC)
- {
- // We *may* get a partial message, even though the message is only
- // one byte long.
- if (msg.remaining() == 0)
- {
- result = new SSLEngineResult (SSLEngineResult.Status.OK,
- handshakeStatus,
- record.length() + 5, 0);
- }
- else
- {
- byte b = msg.get();
- if (b != 1)
- throw new SSLException ("unknown ChangeCipherSpec value: " + (b & 0xFF));
- InputSecurityParameters params = handshake.getInputParams();
- logger.log (Component.SSL_RECORD_LAYER,
- "switching to input security parameters {0}",
- params.cipherSuite());
- insec = params;
- result = new SSLEngineResult (SSLEngineResult.Status.OK,
- handshakeStatus,
- record.length() + 5, 0);
- }
- }
- else if (type == ContentType.ALERT)
- {
- int len = 0;
- if (alertBuffer.position() > 0)
- {
- alertBuffer.put(msg.get());
- len = 1;
- }
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "processing alerts {0}",
- Util.wrapBuffer(msg));
- len += msg.remaining() / 2;
- Alert[] alerts = new Alert[len];
- int i = 0;
- if (alertBuffer.position() > 0)
- {
- alertBuffer.flip();
- alerts[0] = new Alert(alertBuffer);
- i++;
- }
- while (i < alerts.length)
- {
- alerts[i++] = new Alert(msg.duplicate());
- msg.position(msg.position() + 2);
- }
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "alerts: {0}", alerts.length);
- for (i = 0; i < alerts.length; i++)
- {
- if (alerts[i].level() == Alert.Level.FATAL)
- throw new AlertException(alerts[i], false);
- if (alerts[i].description() != Alert.Description.CLOSE_NOTIFY)
- logger.log(java.util.logging.Level.WARNING,
- "received alert: {0}", alerts[i]);
- if (alerts[i].description() == Alert.Description.CLOSE_NOTIFY)
- inClosed = true;
- }
- if (msg.hasRemaining())
- alertBuffer.position(0).limit(2);
- result = new SSLEngineResult (SSLEngineResult.Status.OK,
- handshakeStatus,
- record.length() + 5, 0);
- }
- else if (type == ContentType.HANDSHAKE)
- {
- if (handshake == null)
- beginHandshake();
- try
- {
- handshakeStatus = handshake.handleInput(msg);
- }
- catch (AlertException ae)
- {
- lastAlert = ae.alert();
- return new SSLEngineResult(SSLEngineResult.Status.OK,
- SSLEngineResult.HandshakeStatus.NEED_WRAP,
- 0, 0);
- }
- if (Debug.DEBUG)
- logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}", handshakeStatus);
- result = new SSLEngineResult(SSLEngineResult.Status.OK,
- handshakeStatus,
- record.length() + 5,
- 0);
- if (handshakeStatus == HandshakeStatus.FINISHED)
- {
- handshake = null;
- handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
- }
- }
- else if (type == ContentType.APPLICATION_DATA)
- {
- // Do nothing more; the application data has been put into
- // the output buffers.
- result = new SSLEngineResult(SSLEngineResult.Status.OK,
- handshakeStatus,
- record.length() + 5,
- produced);
- }
- else
- {
- SSLRecordHandler handler = handlers[type.getValue()];
- if (handler != null)
- {
- result = new SSLEngineResult(SSLEngineResult.Status.OK,
- handshakeStatus,
- record.length() + 5,
- 0);
- }
- else
- throw new SSLException ("unknown content type: " + type);
- }
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "return result: {0}", result);
- return result;
- }
- public @Override SSLEngineResult wrap (ByteBuffer[] sources, int offset, int length,
- ByteBuffer sink)
- throws SSLException
- {
- if (mode == null)
- throw new IllegalStateException ("setUseClientMode was never called");
- if (outClosed)
- return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
- handshakeStatus, 0, 0);
- ContentType type = null;
- ByteBuffer sysMessage = null;
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "wrap {0} {1} {2} {3} / {4}",
- sources, offset, length, sink, getHandshakeStatus());
- if (lastAlert != null)
- {
- type = ContentType.ALERT;
- sysMessage = ByteBuffer.allocate(2);
- Alert alert = new Alert(sysMessage);
- alert.setDescription(lastAlert.description());
- alert.setLevel(lastAlert.level());
- if (lastAlert.description() == Alert.Description.CLOSE_NOTIFY)
- outClosed = true;
- }
- else if (changeCipherSpec)
- {
- type = ContentType.CHANGE_CIPHER_SPEC;
- sysMessage = ByteBuffer.allocate(1);
- sysMessage.put(0, (byte) 1);
- }
- else if (getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP)
- {
- // If we are not encrypting, optimize the handshake to fill
- // the buffer directly.
- if (outsec.suite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
- {
- int orig = sink.position();
- sink.order(ByteOrder.BIG_ENDIAN);
- sink.put((byte) ContentType.HANDSHAKE.getValue());
- sink.putShort((short) session.version.rawValue());
- sink.putShort((short) 0);
- handshakeStatus = handshake.handleOutput(sink);
- int produced = sink.position() - orig;
- sink.putShort(orig + 3, (short) (produced - 5));
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
- new Record((ByteBuffer) sink.duplicate().position(orig)));
- SSLEngineResult result = new SSLEngineResult(SSLEngineResult.Status.OK,
- handshakeStatus, 0, produced);
- // Note, this will only happen if we transition from
- // TLS_NULL_WITH_NULL_NULL *to* TLS_NULL_WITH_NULL_NULL, which
- // doesn't make a lot of sense, but we support it anyway.
- if (handshakeStatus == HandshakeStatus.FINISHED)
- {
- handshake = null; // finished with it.
- handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
- }
- return result;
- }
- // Rough guideline; XXX.
- sysMessage = ByteBuffer.allocate(sink.remaining() - 2048);
- type = ContentType.HANDSHAKE;
- try
- {
- handshakeStatus = handshake.handleOutput(sysMessage);
- }
- catch (AlertException ae)
- {
- lastAlert = ae.alert();
- return new SSLEngineResult(Status.OK,
- HandshakeStatus.NEED_WRAP, 0, 0);
- }
- sysMessage.flip();
- if (Debug.DEBUG)
- logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}",
- handshakeStatus);
- }
- int produced = 0;
- int consumed = 0;
- try
- {
- int orig = sink.position();
- int[] inout = null;
- if (sysMessage != null)
- {
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "encrypt system message {0} to {1}", sysMessage, sink);
- inout = outsec.encrypt(new ByteBuffer[] { sysMessage }, 0, 1,
- type, sink);
- produced = inout[1];
- }
- else
- {
- if (outsec.needToSplitPayload())
- {
- inout = outsec.encrypt(sources, offset, 1,
- ContentType.APPLICATION_DATA, sink);
- consumed = inout[0];
- produced = inout[1];
- if (length > 1)
- {
- inout = outsec.encrypt(sources, offset+1, length-1,
- ContentType.APPLICATION_DATA, sink);
- consumed += inout[0];
- produced += inout[1];
- }
- }
- else
- {
- inout = outsec.encrypt(sources, offset, length,
- ContentType.APPLICATION_DATA, sink);
- consumed = inout[0];
- produced = inout[1];
- }
- }
- if (Debug.DEBUG)
- logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
- new Record((ByteBuffer) sink.duplicate().position(orig).limit(produced)));
- }
- catch (ShortBufferException sbe)
- {
- // We don't expect this to happen, except for bugs; signal an
- // internal error.
- lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
- return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
- }
- catch (IllegalBlockSizeException ibse)
- {
- // We don't expect this to happen, except for bugs; signal an
- // internal error.
- lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
- return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
- }
- catch (DataFormatException dfe)
- {
- // We don't expect this to happen; signal an internal error.
- lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
- return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
- }
- if (lastAlert != null && lastAlert.level() == Alert.Level.FATAL)
- {
- AlertException ae = new AlertException(lastAlert);
- lastAlert = null;
- throw ae;
- }
- if (changeCipherSpec)
- {
- outsec = handshake.getOutputParams();
- changeCipherSpec = false;
- }
- SSLEngineResult result
- = new SSLEngineResult(outClosed ? SSLEngineResult.Status.CLOSED
- : SSLEngineResult.Status.OK,
- handshakeStatus, consumed, produced);
- if (handshakeStatus == HandshakeStatus.FINISHED)
- {
- handshake = null; // done with it.
- handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
- }
- return result;
- }
- // Package-private methods.
- SessionImpl session ()
- {
- return session;
- }
- void setSession(SessionImpl session)
- {
- this.session = session;
- }
- void changeCipherSpec()
- {
- changeCipherSpec = true;
- }
- }
|