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