OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / javax / net / ssl / provider / SSLEngineImpl.java
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   
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   
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
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
237   public void closeInbound()
238   {
239     inClosed = true;
240   }
241
242   @Override
243   public void closeOutbound()
244   {
245     lastAlert = new Alert(Alert.Level.WARNING, Alert.Description.CLOSE_NOTIFY);
246   }
247   
248   @Override
249   public Runnable getDelegatedTask()
250   {
251     if (handshake == null)
252       return null;
253     return handshake.getTask();
254   }
255   
256   @Override
257   public String[] getEnabledCipherSuites()
258   {
259     return (String[]) enabledSuites.clone();
260   }
261   
262   @Override
263   public String[] getEnabledProtocols()
264   {
265     return (String[]) enabledProtocols.clone();
266   }
267
268   @Override
269   public boolean getEnableSessionCreation()
270   {
271     return createSessions;
272   }
273   
274   @Override
275   public HandshakeStatus getHandshakeStatus()
276   {
277     if (handshake == null)
278       return HandshakeStatus.NOT_HANDSHAKING;
279     return handshake.status();
280   }
281   
282   @Override
283   public boolean getNeedClientAuth()
284   {
285     return needClientAuth;
286   }
287   
288   @Override
289   public SSLSession getSession()
290   {
291     return session;
292   }
293   
294   @Override
295   public boolean getUseClientMode ()
296   {
297     return (mode == Mode.CLIENT);
298   }
299   
300   @Override
301   public boolean getWantClientAuth()
302   {
303     return wantClientAuth;
304   }
305   
306   @Override
307   public boolean isInboundDone()
308   {
309     return inClosed;
310   }
311   
312   @Override
313   public boolean isOutboundDone()
314   {
315     return outClosed;
316   }
317   
318   @Override
319   public void setEnableSessionCreation(final boolean createSessions)
320   {
321     this.createSessions = createSessions;
322   }
323
324   @Override
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
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
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
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
358   public void setNeedClientAuth(final boolean needClientAuth)
359   {
360     this.needClientAuth = needClientAuth;
361   }
362   
363   @Override
364   public void setUseClientMode (final boolean clientMode)
365   {
366     if (clientMode)
367       mode = Mode.CLIENT;
368     else
369       mode = Mode.SERVER;
370   }
371   
372   public @Override void setWantClientAuth(final boolean wantClientAuth)
373   {
374     this.wantClientAuth = wantClientAuth;
375   }
376
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
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             inout = outsec.encrypt(sources, offset, length,
772                                    ContentType.APPLICATION_DATA, sink);
773             consumed = inout[0];
774             produced = inout[1];
775           }
776         
777         if (Debug.DEBUG)
778           logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
779                       new Record((ByteBuffer) sink.duplicate().position(orig).limit(produced)));
780       }
781     catch (ShortBufferException sbe)
782       {
783         // We don't expect this to happen, except for bugs; signal an
784         // internal error.
785         lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
786         return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
787       }
788     catch (IllegalBlockSizeException ibse)
789       {
790         // We don't expect this to happen, except for bugs; signal an
791         // internal error.
792         lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
793         return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
794       }
795     catch (DataFormatException dfe)
796       {
797         // We don't expect this to happen; signal an internal error.
798         lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
799         return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
800       }
801     
802     if (lastAlert != null && lastAlert.level() == Alert.Level.FATAL)
803       {
804         AlertException ae = new AlertException(lastAlert);
805         lastAlert = null;
806         throw ae;
807       }
808     
809     if (changeCipherSpec)
810       {
811         outsec = handshake.getOutputParams();
812         changeCipherSpec = false;
813       }
814     SSLEngineResult result
815       = new SSLEngineResult(outClosed ? SSLEngineResult.Status.CLOSED
816                                       : SSLEngineResult.Status.OK,
817                             handshakeStatus, consumed, produced);
818     if (handshakeStatus == HandshakeStatus.FINISHED)
819       {
820         handshake = null; // done with it.
821         handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
822       }
823     return result;
824   }
825
826   // Package-private methods.
827
828   SessionImpl session ()
829   {
830     return session;
831   }
832   
833   void setSession(SessionImpl session)
834   {
835     this.session = session;
836   }
837   
838   void changeCipherSpec()
839   {
840     changeCipherSpec = true;
841   }
842 }