1 /* ClientHandshake.java --
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 static gnu.javax.net.ssl.provider.ClientHandshake.State.*;
42 import static gnu.javax.net.ssl.provider.KeyExchangeAlgorithm.*;
43 
44 import gnu.classpath.debug.Component;
45 import gnu.java.security.action.GetSecurityPropertyAction;
46 import gnu.javax.crypto.key.dh.GnuDHPublicKey;
47 import gnu.javax.net.ssl.AbstractSessionContext;
48 import gnu.javax.net.ssl.Session;
49 import gnu.javax.net.ssl.provider.Alert.Description;
50 import gnu.javax.net.ssl.provider.Alert.Level;
51 import gnu.javax.net.ssl.provider.CertificateRequest.ClientCertificateType;
52 import gnu.javax.net.ssl.provider.ServerNameList.NameType;
53 import gnu.javax.net.ssl.provider.ServerNameList.ServerName;
54 
55 import java.nio.ByteBuffer;
56 import java.security.AccessController;
57 import java.security.InvalidAlgorithmParameterException;
58 import java.security.InvalidKeyException;
59 import java.security.KeyPair;
60 import java.security.KeyPairGenerator;
61 import java.security.MessageDigest;
62 import java.security.NoSuchAlgorithmException;
63 import java.security.PrivateKey;
64 import java.security.SignatureException;
65 import java.security.cert.CertificateException;
66 import java.security.cert.X509Certificate;
67 import java.util.Arrays;
68 import java.util.Collections;
69 import java.util.LinkedList;
70 import java.util.List;
71 import java.util.zip.Deflater;
72 import java.util.zip.Inflater;
73 
74 import javax.crypto.BadPaddingException;
75 import javax.crypto.Cipher;
76 import javax.crypto.IllegalBlockSizeException;
77 import javax.crypto.NoSuchPaddingException;
78 import javax.crypto.interfaces.DHPrivateKey;
79 import javax.crypto.interfaces.DHPublicKey;
80 import javax.crypto.spec.DHParameterSpec;
81 import javax.net.ssl.SSLException;
82 import javax.net.ssl.SSLPeerUnverifiedException;
83 import javax.net.ssl.X509ExtendedKeyManager;
84 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
85 import javax.security.auth.x500.X500Principal;
86 
87 /**
88  * @author Casey Marshall (csm@gnu.org)
89  */
90 public class ClientHandshake extends AbstractHandshake
91 {
92   static enum State
93   {
94     WRITE_CLIENT_HELLO (false, true),
95     READ_SERVER_HELLO (true, false),
96     READ_CERTIFICATE (true, false),
97     READ_SERVER_KEY_EXCHANGE (true, false),
98     READ_CERTIFICATE_REQUEST (true, false),
99     READ_SERVER_HELLO_DONE (true, false),
100     WRITE_CERTIFICATE (false, true),
101     WRITE_CLIENT_KEY_EXCHANGE (false, true),
102     WRITE_CERTIFICATE_VERIFY (false, true),
103     WRITE_FINISHED (false, true),
104     READ_FINISHED (true, false),
105     DONE (false, false);
106 
107     private final boolean isWriteState;
108     private final boolean isReadState;
109 
State(boolean isReadState, boolean isWriteState)110     private State(boolean isReadState, boolean isWriteState)
111     {
112       this.isReadState = isReadState;
113       this.isWriteState = isWriteState;
114     }
115 
isReadState()116     boolean isReadState()
117     {
118       return isReadState;
119     }
120 
isWriteState()121     boolean isWriteState()
122     {
123       return isWriteState;
124     }
125   }
126 
127   private State state;
128   private ByteBuffer outBuffer;
129   private boolean continuedSession;
130   private SessionImpl continued;
131   private KeyPair dhPair;
132   private String keyAlias;
133   private PrivateKey privateKey;
134   private MaxFragmentLength maxFragmentLengthSent;
135   private boolean truncatedHMacSent;
136   private ProtocolVersion sentVersion;
137 
138   // Delegated tasks.
139   private CertVerifier certVerifier;
140   private ParamsVerifier paramsVerifier;
141   private DelegatedTask keyExchange;
142   private CertLoader certLoader;
143   private GenCertVerify genCertVerify;
144 
ClientHandshake(SSLEngineImpl engine)145   public ClientHandshake(SSLEngineImpl engine) throws NoSuchAlgorithmException
146   {
147     super(engine);
148     state = WRITE_CLIENT_HELLO;
149     continuedSession = false;
150   }
151 
152   /* (non-Javadoc)
153    * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleInput()
154    */
implHandleInput()155   @Override protected HandshakeStatus implHandleInput() throws SSLException
156   {
157     if (state == DONE)
158       return HandshakeStatus.FINISHED;
159 
160     if (state.isWriteState()
161         || (outBuffer != null && outBuffer.hasRemaining()))
162       return HandshakeStatus.NEED_WRAP;
163 
164     // Copy the current buffer, and prepare it for reading.
165     ByteBuffer buffer = handshakeBuffer.duplicate ();
166     buffer.flip();
167     buffer.position(handshakeOffset);
168 
169     Handshake handshake = new Handshake(buffer.slice(),
170                                         engine.session().suite,
171                                         engine.session().version);
172 
173     if (Debug.DEBUG)
174       logger.logv(Component.SSL_HANDSHAKE, "processing in state {0}:\n{1}",
175                   state, handshake);
176 
177     switch (state)
178       {
179         // Server Hello.
180         case READ_SERVER_HELLO:
181         {
182           if (handshake.type() != Handshake.Type.SERVER_HELLO)
183             throw new AlertException(new Alert(Alert.Level.FATAL,
184                                                Alert.Description.UNEXPECTED_MESSAGE));
185           ServerHello hello = (ServerHello) handshake.body();
186           serverRandom = hello.random().copy();
187           engine.session().suite = hello.cipherSuite();
188           engine.session().version = hello.version();
189           compression = hello.compressionMethod();
190           Session.ID serverId = new Session.ID(hello.sessionId());
191           if (continued != null
192               && continued.id().equals(serverId))
193             {
194               continuedSession = true;
195               engine.setSession(continued);
196             }
197           else if (engine.getEnableSessionCreation())
198             {
199               ((AbstractSessionContext) engine.contextImpl
200                   .engineGetClientSessionContext()).put(engine.session());
201             }
202           ExtensionList extensions = hello.extensions();
203           if (extensions != null)
204             {
205               for (Extension extension : extensions)
206                 {
207                   Extension.Type type = extension.type();
208                   if (type == null)
209                     continue;
210                   switch (type)
211                     {
212                       case MAX_FRAGMENT_LENGTH:
213                         MaxFragmentLength mfl
214                           = (MaxFragmentLength) extension.value();
215                         if (maxFragmentLengthSent == mfl)
216                           engine.session().setApplicationBufferSize(mfl.maxLength());
217                         break;
218 
219                       case TRUNCATED_HMAC:
220                         if (truncatedHMacSent)
221                           engine.session().setTruncatedMac(true);
222                         break;
223                     }
224                 }
225             }
226 
227           KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm();
228           if (continuedSession)
229             {
230               byte[][] keys = generateKeys(clientRandom, serverRandom,
231                                            engine.session());
232               setupSecurityParameters(keys, true, engine, compression);
233               state = READ_FINISHED;
234             }
235           else if (kex == RSA || kex == DH_DSS || kex == DH_RSA
236                    || kex == DHE_DSS || kex == DHE_RSA || kex == RSA_PSK)
237             state = READ_CERTIFICATE;
238           else if (kex == DH_anon || kex == PSK || kex == DHE_PSK)
239             state = READ_SERVER_KEY_EXCHANGE;
240           else
241             state = READ_CERTIFICATE_REQUEST;
242         }
243         break;
244 
245         // Server Certificate.
246         case READ_CERTIFICATE:
247         {
248           if (handshake.type() != Handshake.Type.CERTIFICATE)
249             {
250               // We need a certificate for non-anonymous suites.
251               if (engine.session().suite.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS)
252                 throw new AlertException(new Alert(Level.FATAL,
253                                                    Description.UNEXPECTED_MESSAGE));
254               state = READ_SERVER_KEY_EXCHANGE;
255             }
256           Certificate cert = (Certificate) handshake.body();
257           X509Certificate[] chain = null;
258           try
259             {
260               chain = cert.certificates().toArray(new X509Certificate[0]);
261             }
262           catch (CertificateException ce)
263             {
264               throw new AlertException(new Alert(Level.FATAL,
265                                                  Description.BAD_CERTIFICATE),
266                                        ce);
267             }
268           catch (NoSuchAlgorithmException nsae)
269             {
270               throw new AlertException(new Alert(Level.FATAL,
271                                                  Description.UNSUPPORTED_CERTIFICATE),
272                                        nsae);
273             }
274           engine.session().setPeerCertificates(chain);
275           certVerifier = new CertVerifier(true, chain);
276           tasks.add(certVerifier);
277 
278           // If we are doing an RSA key exchange, generate our parameters.
279           KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm();
280           if (kea == RSA || kea == RSA_PSK)
281             {
282               keyExchange = new RSAGen(kea == RSA);
283               tasks.add(keyExchange);
284               if (kea == RSA)
285                 state = READ_CERTIFICATE_REQUEST;
286               else
287                 state = READ_SERVER_KEY_EXCHANGE;
288             }
289           else
290             state = READ_SERVER_KEY_EXCHANGE;
291         }
292         break;
293 
294         // Server Key Exchange.
295         case READ_SERVER_KEY_EXCHANGE:
296         {
297           CipherSuite s = engine.session().suite;
298           KeyExchangeAlgorithm kexalg = s.keyExchangeAlgorithm();
299           // XXX also SRP.
300           if (kexalg != DHE_DSS && kexalg != DHE_RSA && kexalg != DH_anon
301               && kexalg != DHE_PSK && kexalg != PSK && kexalg != RSA_PSK)
302             throw new AlertException(new Alert(Level.FATAL,
303                                                Description.UNEXPECTED_MESSAGE));
304 
305           if (handshake.type() != Handshake.Type.SERVER_KEY_EXCHANGE)
306             {
307               if (kexalg != RSA_PSK && kexalg != PSK)
308                 throw new AlertException(new Alert(Level.FATAL,
309                                                    Description.UNEXPECTED_MESSAGE));
310               state = READ_CERTIFICATE_REQUEST;
311               return HandshakeStatus.NEED_UNWRAP;
312             }
313 
314           ServerKeyExchange skex = (ServerKeyExchange) handshake.body();
315           ByteBuffer paramsBuffer = null;
316           if (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon)
317             {
318               ServerDHParams dhParams = (ServerDHParams) skex.params();
319               ByteBuffer b = dhParams.buffer();
320               paramsBuffer = ByteBuffer.allocate(b.remaining());
321               paramsBuffer.put(b);
322             }
323 
324           if (s.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS)
325             {
326               byte[] signature = skex.signature().signature();
327               paramsVerifier = new ParamsVerifier(paramsBuffer, signature);
328               tasks.add(paramsVerifier);
329             }
330 
331           if (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon)
332             {
333               ServerDHParams dhParams = (ServerDHParams) skex.params();
334               DHPublicKey serverKey = new GnuDHPublicKey(null,
335                                                          dhParams.p(),
336                                                          dhParams.g(),
337                                                          dhParams.y());
338               DHParameterSpec params = new DHParameterSpec(dhParams.p(),
339                                                            dhParams.g());
340               keyExchange = new ClientDHGen(serverKey, params, true);
341               tasks.add(keyExchange);
342             }
343           if (kexalg == DHE_PSK)
344             {
345               ServerDHE_PSKParameters pskParams = (ServerDHE_PSKParameters)
346                 skex.params();
347               ServerDHParams dhParams = pskParams.params();
348               DHPublicKey serverKey = new GnuDHPublicKey(null,
349                                                          dhParams.p(),
350                                                          dhParams.g(),
351                                                          dhParams.y());
352               DHParameterSpec params = new DHParameterSpec(dhParams.p(),
353                                                            dhParams.g());
354               keyExchange = new ClientDHGen(serverKey, params, false);
355               tasks.add(keyExchange);
356             }
357           state = READ_CERTIFICATE_REQUEST;
358         }
359         break;
360 
361         // Certificate Request.
362         case READ_CERTIFICATE_REQUEST:
363         {
364           if (handshake.type() != Handshake.Type.CERTIFICATE_REQUEST)
365             {
366               state = READ_SERVER_HELLO_DONE;
367               return HandshakeStatus.NEED_UNWRAP;
368             }
369 
370           CertificateRequest req = (CertificateRequest) handshake.body();
371           ClientCertificateTypeList types = req.types();
372           LinkedList<String> typeList = new LinkedList<String>();
373           for (ClientCertificateType t : types)
374             typeList.add(t.name());
375 
376           X500PrincipalList issuers = req.authorities();
377           LinkedList<X500Principal> issuerList = new LinkedList<X500Principal>();
378           for (X500Principal p : issuers)
379             issuerList.add(p);
380 
381           certLoader = new CertLoader(typeList, issuerList);
382           tasks.add(certLoader);
383         }
384         break;
385 
386         // Server Hello Done.
387         case READ_SERVER_HELLO_DONE:
388         {
389           if (handshake.type() != Handshake.Type.SERVER_HELLO_DONE)
390             throw new AlertException(new Alert(Level.FATAL,
391                                                Description.UNEXPECTED_MESSAGE));
392           state = WRITE_CERTIFICATE;
393         }
394         break;
395 
396         // Finished.
397         case READ_FINISHED:
398         {
399           if (handshake.type() != Handshake.Type.FINISHED)
400             throw new AlertException(new Alert(Level.FATAL,
401                                                Description.UNEXPECTED_MESSAGE));
402 
403           Finished serverFinished = (Finished) handshake.body();
404           MessageDigest md5copy = null;
405           MessageDigest shacopy = null;
406           try
407             {
408               md5copy = (MessageDigest) md5.clone();
409               shacopy = (MessageDigest) sha.clone();
410             }
411           catch (CloneNotSupportedException cnse)
412             {
413               // We're improperly configured to use a non-cloneable
414               // md5/sha-1, OR there's a runtime bug.
415               throw new SSLException(cnse);
416             }
417           Finished clientFinished =
418             new Finished(generateFinished(md5copy, shacopy,
419                                           false, engine.session()),
420                                           engine.session().version);
421 
422           if (Debug.DEBUG)
423             logger.logv(Component.SSL_HANDSHAKE, "clientFinished: {0}",
424                         clientFinished);
425 
426           if (engine.session().version == ProtocolVersion.SSL_3)
427             {
428               if (!Arrays.equals(clientFinished.md5Hash(),
429                                  serverFinished.md5Hash())
430                   || !Arrays.equals(clientFinished.shaHash(),
431                                     serverFinished.shaHash()))
432                 {
433                   engine.session().invalidate();
434                   throw new SSLException("session verify failed");
435                 }
436             }
437           else
438             {
439               if (!Arrays.equals(clientFinished.verifyData(),
440                                  serverFinished.verifyData()))
441                 {
442                   engine.session().invalidate();
443                   throw new SSLException("session verify failed");
444                 }
445             }
446 
447           if (continuedSession)
448             {
449               engine.changeCipherSpec();
450               state = WRITE_FINISHED;
451             }
452           else
453             state = DONE;
454         }
455         break;
456 
457         default:
458           throw new IllegalStateException("invalid state: " + state);
459       }
460 
461     handshakeOffset += handshake.length() + 4;
462 
463     if (!tasks.isEmpty())
464       return HandshakeStatus.NEED_TASK;
465     if (state.isWriteState()
466         || (outBuffer != null && outBuffer.hasRemaining()))
467       return HandshakeStatus.NEED_WRAP;
468     if (state.isReadState())
469       return HandshakeStatus.NEED_UNWRAP;
470 
471     return HandshakeStatus.FINISHED;
472   }
473 
474   /* (non-Javadoc)
475    * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleOutput(java.nio.ByteBuffer)
476    */
implHandleOutput(ByteBuffer fragment)477   @Override protected HandshakeStatus implHandleOutput(ByteBuffer fragment)
478     throws SSLException
479   {
480     if (Debug.DEBUG)
481       logger.logv(Component.SSL_HANDSHAKE, "output to {0}; state:{1}; outBuffer:{2}",
482                   fragment, state, outBuffer);
483 
484     // Drain the output buffer, if it needs it.
485     if (outBuffer != null && outBuffer.hasRemaining())
486       {
487         int l = Math.min(fragment.remaining(), outBuffer.remaining());
488         fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
489         outBuffer.position(outBuffer.position() + l);
490       }
491 
492     if (!fragment.hasRemaining())
493       {
494         if (state.isWriteState() || outBuffer.hasRemaining())
495           return HandshakeStatus.NEED_WRAP;
496         else
497           return HandshakeStatus.NEED_UNWRAP;
498       }
499 
500 outer_loop:
501     while (fragment.remaining() >= 4 && state.isWriteState())
502       {
503         if (Debug.DEBUG)
504           logger.logv(Component.SSL_HANDSHAKE, "loop state={0}", state);
505 
506         switch (state)
507           {
508             case WRITE_CLIENT_HELLO:
509             {
510               ClientHelloBuilder hello = new ClientHelloBuilder();
511               AbstractSessionContext ctx = (AbstractSessionContext)
512                 engine.contextImpl.engineGetClientSessionContext();
513               continued = (SessionImpl) ctx.getSession(engine.getPeerHost(),
514                                                        engine.getPeerPort());
515               engine.session().setId(new Session.ID(new byte[0]));
516               Session.ID sid = engine.session().id();
517               // If we have a session that we may want to continue, send
518               // that ID.
519               if (continued != null)
520                 sid = continued.id();
521 
522               hello.setSessionId(sid.id());
523               sentVersion = chooseVersion();
524               hello.setVersion(sentVersion);
525               hello.setCipherSuites(getSuites());
526               hello.setCompressionMethods(getCompressionMethods());
527               Random r = hello.random();
528               r.setGmtUnixTime(Util.unixTime());
529               byte[] nonce = new byte[28];
530               engine.session().random().nextBytes(nonce);
531               r.setRandomBytes(nonce);
532               clientRandom = r.copy();
533               if (enableExtensions())
534                 {
535                   List<Extension> extensions = new LinkedList<Extension>();
536                   MaxFragmentLength fraglen = maxFragmentLength();
537                   if (fraglen != null)
538                     {
539                       extensions.add(new Extension(Extension.Type.MAX_FRAGMENT_LENGTH,
540                                                    fraglen));
541                       maxFragmentLengthSent = fraglen;
542                     }
543 
544                   String host = engine.getPeerHost();
545                   if (host != null)
546                     {
547                       ServerName name
548                         = new ServerName(NameType.HOST_NAME, host);
549                       ServerNameList names
550                         = new ServerNameList(Collections.singletonList(name));
551                       extensions.add(new Extension(Extension.Type.SERVER_NAME,
552                                                    names));
553                     }
554 
555                   if (truncatedHMac())
556                     {
557                       extensions.add(new Extension(Extension.Type.TRUNCATED_HMAC,
558                                                    new TruncatedHMAC()));
559                       truncatedHMacSent = true;
560                     }
561 
562                   ExtensionList elist = new ExtensionList(extensions);
563                   hello.setExtensions(elist.buffer());
564                 }
565               else
566                 hello.setDisableExtensions(true);
567 
568               if (Debug.DEBUG)
569                 logger.logv(Component.SSL_HANDSHAKE, "{0}", hello);
570 
571               fragment.putInt((Handshake.Type.CLIENT_HELLO.getValue() << 24)
572                               | (hello.length() & 0xFFFFFF));
573               outBuffer = hello.buffer();
574               int l = Math.min(fragment.remaining(), outBuffer.remaining());
575               fragment.put((ByteBuffer) outBuffer.duplicate()
576                            .limit(outBuffer.position() + l));
577               outBuffer.position(outBuffer.position() + l);
578 
579               state = READ_SERVER_HELLO;
580             }
581             break;
582 
583             case WRITE_CERTIFICATE:
584             {
585               java.security.cert.Certificate[] chain
586                 = engine.session().getLocalCertificates();
587               if (chain != null)
588                 {
589                   CertificateBuilder cert
590                     = new CertificateBuilder(CertificateType.X509);
591                   try
592                     {
593                       cert.setCertificates(Arrays.asList(chain));
594                     }
595                   catch (CertificateException ce)
596                     {
597                       throw new AlertException(new Alert(Level.FATAL,
598                                                          Description.INTERNAL_ERROR),
599                                                ce);
600                     }
601 
602                   outBuffer = cert.buffer();
603 
604                   fragment.putInt((Handshake.Type.CERTIFICATE.getValue() << 24)
605                                   | (cert.length() & 0xFFFFFF));
606 
607                   int l = Math.min(fragment.remaining(), outBuffer.remaining());
608                   fragment.put((ByteBuffer) outBuffer.duplicate()
609                                .limit(outBuffer.position() + l));
610                   outBuffer.position(outBuffer.position() + l);
611                 }
612               state = WRITE_CLIENT_KEY_EXCHANGE;
613             }
614             break;
615 
616             case WRITE_CLIENT_KEY_EXCHANGE:
617             {
618               KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm();
619               ClientKeyExchangeBuilder ckex
620                 = new ClientKeyExchangeBuilder(engine.session().suite,
621                                                engine.session().version);
622               if (kea == DHE_DSS || kea == DHE_RSA || kea == DH_anon
623                   || kea == DH_DSS || kea == DH_RSA)
624                 {
625                   assert(dhPair != null);
626                   DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic();
627                   ClientDiffieHellmanPublic pub
628                     = new ClientDiffieHellmanPublic(pubkey.getY());
629                   ckex.setExchangeKeys(pub.buffer());
630                 }
631               if (kea == RSA || kea == RSA_PSK)
632                 {
633                   assert(keyExchange instanceof RSAGen);
634                   assert(keyExchange.hasRun());
635                   if (keyExchange.thrown() != null)
636                     throw new AlertException(new Alert(Level.FATAL,
637                                                        Description.HANDSHAKE_FAILURE),
638                                              keyExchange.thrown());
639                   EncryptedPreMasterSecret epms
640                     = new EncryptedPreMasterSecret(((RSAGen) keyExchange).encryptedSecret(),
641                                                    engine.session().version);
642                   if (kea == RSA)
643                     ckex.setExchangeKeys(epms.buffer());
644                   else
645                     {
646                       String identity = getPSKIdentity();
647                       if (identity == null)
648                         throw new SSLException("no pre-shared-key identity;"
649                                                + " set the security property"
650                                                + " \"jessie.client.psk.identity\"");
651                       ClientRSA_PSKParameters params =
652                         new ClientRSA_PSKParameters(identity, epms.buffer());
653                       ckex.setExchangeKeys(params.buffer());
654                       generatePSKSecret(identity, preMasterSecret, true);
655                     }
656                 }
657               if (kea == DHE_PSK)
658                 {
659                   assert(keyExchange instanceof ClientDHGen);
660                   assert(dhPair != null);
661                   String identity = getPSKIdentity();
662                   if (identity == null)
663                     throw new SSLException("no pre-shared key identity; set"
664                                            + " the security property"
665                                            + " \"jessie.client.psk.identity\"");
666                   DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic();
667                   ClientDHE_PSKParameters params =
668                     new ClientDHE_PSKParameters(identity,
669                                                 new ClientDiffieHellmanPublic(pubkey.getY()));
670                   ckex.setExchangeKeys(params.buffer());
671                   generatePSKSecret(identity, preMasterSecret, true);
672                 }
673               if (kea == PSK)
674                 {
675                   String identity = getPSKIdentity();
676                   if (identity == null)
677                     throw new SSLException("no pre-shared key identity; set"
678                                            + " the security property"
679                                            + " \"jessie.client.psk.identity\"");
680                   generatePSKSecret(identity, null, true);
681                   ClientPSKParameters params = new ClientPSKParameters(identity);
682                   ckex.setExchangeKeys(params.buffer());
683                 }
684               if (kea == NONE)
685                 {
686                   Inflater inflater = null;
687                   Deflater deflater = null;
688                   if (compression == CompressionMethod.ZLIB)
689                     {
690                       inflater = new Inflater();
691                       deflater = new Deflater();
692                     }
693                   inParams = new InputSecurityParameters(null, null, inflater,
694                                                          engine.session(),
695                                                          engine.session().suite);
696                   outParams = new OutputSecurityParameters(null, null, deflater,
697                                                            engine.session(),
698                                                            engine.session().suite);
699                   engine.session().privateData.masterSecret = new byte[0];
700                 }
701 
702               if (Debug.DEBUG)
703                 logger.logv(Component.SSL_HANDSHAKE, "{0}", ckex);
704 
705               outBuffer = ckex.buffer();
706               if (Debug.DEBUG)
707                 logger.logv(Component.SSL_HANDSHAKE, "client kex buffer {0}", outBuffer);
708               fragment.putInt((Handshake.Type.CLIENT_KEY_EXCHANGE.getValue() << 24)
709                               | (ckex.length() & 0xFFFFFF));
710               int l = Math.min(fragment.remaining(), outBuffer.remaining());
711               fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
712               outBuffer.position(outBuffer.position() + l);
713 
714               if (privateKey != null)
715                 {
716                   genCertVerify = new GenCertVerify(md5, sha);
717                   tasks.add(genCertVerify);
718                   state = WRITE_CERTIFICATE_VERIFY;
719                 }
720               else
721                 {
722                   engine.changeCipherSpec();
723                   state = WRITE_FINISHED;
724                 }
725             }
726             // Both states terminate in a NEED_TASK, or a need to change cipher
727             // specs; so we can't write any more messages here.
728             break outer_loop;
729 
730             case WRITE_CERTIFICATE_VERIFY:
731             {
732               assert(genCertVerify != null);
733               assert(genCertVerify.hasRun());
734               CertificateVerify verify = new CertificateVerify(genCertVerify.signed(),
735                                                                engine.session().suite.signatureAlgorithm());
736 
737               outBuffer = verify.buffer();
738               fragment.putInt((Handshake.Type.CERTIFICATE_VERIFY.getValue() << 24)
739                               | (verify.length() & 0xFFFFFF));
740               int l = Math.min(fragment.remaining(), outBuffer.remaining());
741               fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
742               outBuffer.position(outBuffer.position() + l);
743 
744               // XXX This is a potential problem: we may not have drained
745               // outBuffer, but set the changeCipherSpec toggle.
746               engine.changeCipherSpec();
747               state = WRITE_FINISHED;
748             }
749             break outer_loop;
750 
751             case WRITE_FINISHED:
752             {
753               MessageDigest md5copy = null;
754               MessageDigest shacopy = null;
755               try
756                 {
757                   md5copy = (MessageDigest) md5.clone();
758                   shacopy = (MessageDigest) sha.clone();
759                 }
760               catch (CloneNotSupportedException cnse)
761                 {
762                   // We're improperly configured to use a non-cloneable
763                   // md5/sha-1, OR there's a runtime bug.
764                   throw new SSLException(cnse);
765                 }
766               outBuffer
767                 = generateFinished(md5copy, shacopy, true,
768                                    engine.session());
769 
770               fragment.putInt((Handshake.Type.FINISHED.getValue() << 24)
771                               | outBuffer.remaining() & 0xFFFFFF);
772 
773               int l = Math.min(outBuffer.remaining(), fragment.remaining());
774               fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
775               outBuffer.position(outBuffer.position() + l);
776 
777               if (continuedSession)
778                 state = DONE;
779               else
780                 state = READ_FINISHED;
781             }
782             break;
783 
784             default:
785               throw new IllegalStateException("invalid state: " + state);
786           }
787       }
788 
789     if (!tasks.isEmpty())
790       return HandshakeStatus.NEED_TASK;
791     if (state.isWriteState() ||
792         (outBuffer != null && outBuffer.hasRemaining()))
793       return HandshakeStatus.NEED_WRAP;
794     if (state.isReadState())
795       return HandshakeStatus.NEED_UNWRAP;
796 
797     return HandshakeStatus.FINISHED;
798   }
799 
800   /* (non-Javadoc)
801    * @see gnu.javax.net.ssl.provider.AbstractHandshake#status()
802    */
status()803   @Override HandshakeStatus status()
804   {
805     if (state.isReadState())
806       return HandshakeStatus.NEED_UNWRAP;
807     if (state.isWriteState())
808       return HandshakeStatus.NEED_WRAP;
809     return HandshakeStatus.FINISHED;
810   }
811 
checkKeyExchange()812   @Override void checkKeyExchange() throws SSLException
813   {
814     // XXX implement.
815   }
816 
817   /* (non-Javadoc)
818    * @see gnu.javax.net.ssl.provider.AbstractHandshake#handleV2Hello(java.nio.ByteBuffer)
819    */
handleV2Hello(ByteBuffer hello)820   @Override void handleV2Hello(ByteBuffer hello) throws SSLException
821   {
822     throw new SSLException("this should be impossible");
823   }
824 
chooseVersion()825   private ProtocolVersion chooseVersion() throws SSLException
826   {
827     // Select the highest enabled version, for our initial key exchange.
828     ProtocolVersion version = null;
829     for (String ver : engine.getEnabledProtocols())
830       {
831         try
832           {
833             ProtocolVersion v = ProtocolVersion.forName(ver);
834             if (version == null || version.compareTo(v) < 0)
835               version = v;
836           }
837         catch (Exception x)
838           {
839             continue;
840           }
841       }
842 
843     if (version == null)
844       throw new SSLException("no suitable enabled versions");
845 
846     return version;
847   }
848 
getSuites()849   private List<CipherSuite> getSuites() throws SSLException
850   {
851     List<CipherSuite> suites = new LinkedList<CipherSuite>();
852     for (String s : engine.getEnabledCipherSuites())
853       {
854         CipherSuite suite = CipherSuite.forName(s);
855         if (suite != null)
856           suites.add(suite);
857       }
858     if (suites.isEmpty())
859       throw new SSLException("no cipher suites enabled");
860     return suites;
861   }
862 
getCompressionMethods()863   private List<CompressionMethod> getCompressionMethods()
864   {
865     List<CompressionMethod> methods = new LinkedList<CompressionMethod>();
866     GetSecurityPropertyAction gspa = new GetSecurityPropertyAction("jessie.enable.compression");
867     if (Boolean.valueOf(AccessController.doPrivileged(gspa)))
868       methods.add(CompressionMethod.ZLIB);
869     methods.add(CompressionMethod.NULL);
870     return methods;
871   }
872 
enableExtensions()873   private boolean enableExtensions()
874   {
875     GetSecurityPropertyAction action
876       = new GetSecurityPropertyAction("jessie.client.enable.extensions");
877     return Boolean.valueOf(AccessController.doPrivileged(action));
878   }
879 
maxFragmentLength()880   private MaxFragmentLength maxFragmentLength()
881   {
882     GetSecurityPropertyAction action
883       = new GetSecurityPropertyAction("jessie.client.maxFragmentLength");
884     String s = AccessController.doPrivileged(action);
885     if (s != null)
886       {
887         try
888           {
889             int len = Integer.parseInt(s);
890             switch (len)
891               {
892                 case 9:
893                 case (1 <<  9): return MaxFragmentLength.LEN_2_9;
894                 case 10:
895                 case (1 << 10): return MaxFragmentLength.LEN_2_10;
896                 case 11:
897                 case (1 << 11): return MaxFragmentLength.LEN_2_11;
898                 case 12:
899                 case (1 << 12): return MaxFragmentLength.LEN_2_12;
900               }
901           }
902         catch (NumberFormatException nfe)
903           {
904           }
905       }
906     return null;
907   }
908 
truncatedHMac()909   private boolean truncatedHMac()
910   {
911     GetSecurityPropertyAction action
912       = new GetSecurityPropertyAction("jessie.client.truncatedHMac");
913     return Boolean.valueOf(AccessController.doPrivileged(action));
914   }
915 
getPSKIdentity()916   private String getPSKIdentity()
917   {
918     GetSecurityPropertyAction action
919       = new GetSecurityPropertyAction("jessie.client.psk.identity");
920     return AccessController.doPrivileged(action);
921   }
922 
923   // Delegated tasks.
924 
925   class ParamsVerifier extends DelegatedTask
926   {
927     private final ByteBuffer paramsBuffer;
928     private final byte[] signature;
929     private boolean verified;
930 
ParamsVerifier(ByteBuffer paramsBuffer, byte[] signature)931     ParamsVerifier(ByteBuffer paramsBuffer, byte[] signature)
932     {
933       this.paramsBuffer = paramsBuffer;
934       this.signature = signature;
935     }
936 
implRun()937     public void implRun()
938       throws InvalidKeyException, NoSuchAlgorithmException,
939              SSLPeerUnverifiedException, SignatureException
940     {
941       java.security.Signature s
942         = java.security.Signature.getInstance(engine.session().suite
943                                               .signatureAlgorithm().algorithm());
944       s.initVerify(engine.session().getPeerCertificates()[0]);
945       s.update(paramsBuffer);
946       verified = s.verify(signature);
947       synchronized (this)
948         {
949           notifyAll();
950         }
951     }
952 
verified()953     boolean verified()
954     {
955       return verified;
956     }
957   }
958 
959   class ClientDHGen extends DelegatedTask
960   {
961     private final DHPublicKey serverKey;
962     private final DHParameterSpec params;
963     private final boolean full;
964 
ClientDHGen(DHPublicKey serverKey, DHParameterSpec params, boolean full)965     ClientDHGen(DHPublicKey serverKey, DHParameterSpec params, boolean full)
966     {
967       this.serverKey = serverKey;
968       this.params = params;
969       this.full = full;
970     }
971 
implRun()972     public void implRun()
973       throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
974              SSLException
975     {
976       if (Debug.DEBUG)
977         logger.log(Component.SSL_DELEGATED_TASK, "running client DH phase");
978       if (paramsVerifier != null)
979         {
980           synchronized (paramsVerifier)
981             {
982               try
983                 {
984                   while (!paramsVerifier.hasRun())
985                     paramsVerifier.wait(500);
986                 }
987               catch (InterruptedException ie)
988                 {
989                   // Ignore.
990                 }
991             }
992         }
993       KeyPairGenerator gen = KeyPairGenerator.getInstance("DH");
994       gen.initialize(params, engine.session().random());
995       dhPair = gen.generateKeyPair();
996       if (Debug.DEBUG_KEY_EXCHANGE)
997         logger.logv(Component.SSL_KEY_EXCHANGE,
998                     "client keys public:{0} private:{1}", dhPair.getPublic(),
999                     dhPair.getPrivate());
1000 
1001       initDiffieHellman((DHPrivateKey) dhPair.getPrivate(), engine.session().random());
1002 
1003       // We have enough info to do the full key exchange; so let's do it.
1004       DHPhase phase = new DHPhase(serverKey, full);
1005       phase.run();
1006       if (phase.thrown() != null)
1007         throw new SSLException(phase.thrown());
1008     }
1009 
serverKey()1010     DHPublicKey serverKey()
1011     {
1012       return serverKey;
1013     }
1014   }
1015 
1016   class CertLoader extends DelegatedTask
1017   {
1018     private final List<String> keyTypes;
1019     private final List<X500Principal> issuers;
1020 
CertLoader(List<String> keyTypes, List<X500Principal> issuers)1021     CertLoader(List<String> keyTypes, List<X500Principal> issuers)
1022     {
1023       this.keyTypes = keyTypes;
1024       this.issuers = issuers;
1025     }
1026 
implRun()1027     public void implRun()
1028     {
1029       X509ExtendedKeyManager km = engine.contextImpl.keyManager;
1030       if (km == null)
1031         return;
1032       keyAlias = km.chooseEngineClientAlias(keyTypes.toArray(new String[keyTypes.size()]),
1033                                             issuers.toArray(new X500Principal[issuers.size()]),
1034                                             engine);
1035       engine.session().setLocalCertificates(km.getCertificateChain(keyAlias));
1036       privateKey = km.getPrivateKey(keyAlias);
1037     }
1038   }
1039 
1040   class RSAGen extends DelegatedTask
1041   {
1042     private byte[] encryptedPreMasterSecret;
1043     private final boolean full;
1044 
RSAGen()1045     RSAGen()
1046     {
1047       this(true);
1048     }
1049 
RSAGen(boolean full)1050     RSAGen(boolean full)
1051     {
1052       this.full = full;
1053     }
1054 
implRun()1055     public void implRun()
1056       throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException,
1057              NoSuchAlgorithmException, NoSuchPaddingException,
1058              SSLException
1059     {
1060       if (certVerifier != null)
1061         {
1062           synchronized (certVerifier)
1063             {
1064               try
1065                 {
1066                   while (!certVerifier.hasRun())
1067                     certVerifier.wait(500);
1068                 }
1069               catch (InterruptedException ie)
1070                 {
1071                   // Ignore.
1072                 }
1073             }
1074         }
1075       preMasterSecret = new byte[48];
1076       engine.session().random().nextBytes(preMasterSecret);
1077       preMasterSecret[0] = (byte) sentVersion.major();
1078       preMasterSecret[1] = (byte) sentVersion.minor();
1079       Cipher rsa = Cipher.getInstance("RSA");
1080       java.security.cert.Certificate cert
1081         = engine.session().getPeerCertificates()[0];
1082       if (cert instanceof X509Certificate)
1083         {
1084           boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage();
1085           if (keyUsage != null && !keyUsage[2])
1086             throw new InvalidKeyException("certificate's keyUsage does not permit keyEncipherment");
1087         }
1088       rsa.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
1089       encryptedPreMasterSecret = rsa.doFinal(preMasterSecret);
1090 
1091       // Generate our session keys, because we can.
1092       if (full)
1093         {
1094           generateMasterSecret(clientRandom, serverRandom, engine.session());
1095           byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session());
1096           setupSecurityParameters(keys, true, engine, compression);
1097         }
1098     }
1099 
encryptedSecret()1100     byte[] encryptedSecret()
1101     {
1102       return encryptedPreMasterSecret;
1103     }
1104   }
1105 
1106   class GenCertVerify extends DelegatedTask
1107   {
1108     private final MessageDigest md5, sha;
1109     private byte[] signed;
1110 
GenCertVerify(MessageDigest md5, MessageDigest sha)1111     GenCertVerify(MessageDigest md5, MessageDigest sha)
1112     {
1113       try
1114         {
1115           this.md5 = (MessageDigest) md5.clone();
1116           this.sha = (MessageDigest) sha.clone();
1117         }
1118       catch (CloneNotSupportedException cnse)
1119         {
1120           // Our message digests *should* be cloneable.
1121           throw new Error(cnse);
1122         }
1123     }
1124 
implRun()1125     public void implRun()
1126       throws InvalidKeyException, NoSuchAlgorithmException, SignatureException
1127     {
1128       byte[] toSign;
1129       if (engine.session().version == ProtocolVersion.SSL_3)
1130         {
1131           toSign = genV3CertificateVerify(md5, sha, engine.session());
1132         }
1133       else
1134         {
1135           if (engine.session().suite.signatureAlgorithm() == SignatureAlgorithm.RSA)
1136             toSign = Util.concat(md5.digest(), sha.digest());
1137           else
1138             toSign = sha.digest();
1139         }
1140 
1141       java.security.Signature sig =
1142         java.security.Signature.getInstance(engine.session().suite.signatureAlgorithm().name());
1143       sig.initSign(privateKey);
1144       sig.update(toSign);
1145       signed = sig.sign();
1146     }
1147 
signed()1148     byte[] signed()
1149     {
1150       return signed;
1151     }
1152   }
1153 }
1154