| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.ssl;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.nio.ByteBuffer;  | 
 | 
import java.security.AccessController;  | 
 | 
import java.security.AlgorithmConstraints;  | 
 | 
import java.security.GeneralSecurityException;  | 
 | 
import java.security.Principal;  | 
 | 
import java.security.PrivilegedActionException;  | 
 | 
import java.security.PrivilegedExceptionAction;  | 
 | 
import java.text.MessageFormat;  | 
 | 
import java.util.Arrays;  | 
 | 
import java.util.LinkedList;  | 
 | 
import java.util.List;  | 
 | 
import java.util.Locale;  | 
 | 
import java.util.Map;  | 
 | 
import java.util.Set;  | 
 | 
import javax.crypto.SecretKey;  | 
 | 
import javax.crypto.spec.IvParameterSpec;  | 
 | 
import javax.net.ssl.SSLException;  | 
 | 
import javax.net.ssl.SSLHandshakeException;  | 
 | 
import javax.net.ssl.SSLProtocolException;  | 
 | 
import javax.security.auth.Subject;  | 
 | 
 | 
 | 
import sun.security.ssl.CipherSuite.KeyExchange;  | 
 | 
import sun.security.ssl.ClientHello.ClientHelloMessage;  | 
 | 
import sun.security.ssl.SSLCipher.SSLReadCipher;  | 
 | 
import sun.security.ssl.SSLCipher.SSLWriteCipher;  | 
 | 
import sun.security.ssl.SSLHandshake.HandshakeMessage;  | 
 | 
import sun.security.ssl.SupportedVersionsExtension.SHSupportedVersionsSpec;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
final class ServerHello { | 
 | 
    static final SSLConsumer handshakeConsumer =  | 
 | 
        new ServerHelloConsumer();  | 
 | 
    static final HandshakeProducer t12HandshakeProducer =  | 
 | 
        new T12ServerHelloProducer();  | 
 | 
    static final HandshakeProducer t13HandshakeProducer =  | 
 | 
        new T13ServerHelloProducer();  | 
 | 
    static final HandshakeProducer hrrHandshakeProducer =  | 
 | 
        new T13HelloRetryRequestProducer();  | 
 | 
 | 
 | 
    static final HandshakeProducer hrrReproducer =  | 
 | 
        new T13HelloRetryRequestReproducer();  | 
 | 
 | 
 | 
    private static final HandshakeConsumer t12HandshakeConsumer =  | 
 | 
        new T12ServerHelloConsumer();  | 
 | 
    private static final HandshakeConsumer t13HandshakeConsumer =  | 
 | 
        new T13ServerHelloConsumer();  | 
 | 
 | 
 | 
    private static final HandshakeConsumer t13HrrHandshakeConsumer =  | 
 | 
        new T13HelloRetryRequestConsumer();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    static final class ServerHelloMessage extends HandshakeMessage { | 
 | 
        final ProtocolVersion           serverVersion;        | 
 | 
        final RandomCookie              serverRandom;  | 
 | 
        final SessionId                 sessionId;            | 
 | 
        final CipherSuite               cipherSuite;  | 
 | 
        final byte                      compressionMethod;    | 
 | 
        final SSLExtensions             extensions;  | 
 | 
 | 
 | 
        // The HelloRetryRequest producer needs to use the ClientHello message  | 
 | 
        // for cookie generation.  Please don't use this field for other  | 
 | 
          | 
 | 
        final ClientHelloMessage        clientHello;  | 
 | 
 | 
 | 
        // Reserved for HelloRetryRequest consumer.  Please don't use this  | 
 | 
          | 
 | 
        final ByteBuffer                handshakeRecord;  | 
 | 
 | 
 | 
        ServerHelloMessage(HandshakeContext context,  | 
 | 
                ProtocolVersion serverVersion, SessionId sessionId,  | 
 | 
                CipherSuite cipherSuite, RandomCookie serverRandom,  | 
 | 
                ClientHelloMessage clientHello) { | 
 | 
            super(context);  | 
 | 
 | 
 | 
            this.serverVersion = serverVersion;  | 
 | 
            this.serverRandom = serverRandom;  | 
 | 
            this.sessionId = sessionId;  | 
 | 
            this.cipherSuite = cipherSuite;  | 
 | 
            this.compressionMethod = 0x00;        | 
 | 
            this.extensions = new SSLExtensions(this);  | 
 | 
 | 
 | 
              | 
 | 
            this.clientHello = clientHello;  | 
 | 
 | 
 | 
            // The handshakeRecord field is used for HelloRetryRequest consumer  | 
 | 
            // only.  It's fine to set it to null for generating side of the  | 
 | 
              | 
 | 
            this.handshakeRecord = null;  | 
 | 
        }  | 
 | 
 | 
 | 
        ServerHelloMessage(HandshakeContext context,  | 
 | 
                ByteBuffer m) throws IOException { | 
 | 
            super(context);  | 
 | 
 | 
 | 
              | 
 | 
            this.handshakeRecord = m.duplicate();  | 
 | 
 | 
 | 
            byte major = m.get();  | 
 | 
            byte minor = m.get();  | 
 | 
            this.serverVersion = ProtocolVersion.valueOf(major, minor);  | 
 | 
            if (this.serverVersion == null) { | 
 | 
                  | 
 | 
                throw context.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "Unsupported protocol version: " +  | 
 | 
                    ProtocolVersion.nameOf(major, minor));  | 
 | 
            }  | 
 | 
 | 
 | 
            this.serverRandom = new RandomCookie(m);  | 
 | 
            this.sessionId = new SessionId(Record.getBytes8(m));  | 
 | 
            try { | 
 | 
                sessionId.checkLength(serverVersion.id);  | 
 | 
            } catch (SSLProtocolException ex) { | 
 | 
                throw handshakeContext.conContext.fatal(  | 
 | 
                        Alert.ILLEGAL_PARAMETER, ex);  | 
 | 
            }  | 
 | 
 | 
 | 
            int cipherSuiteId = Record.getInt16(m);  | 
 | 
            this.cipherSuite = CipherSuite.valueOf(cipherSuiteId);  | 
 | 
            if (cipherSuite == null || !context.isNegotiable(cipherSuite)) { | 
 | 
                throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "Server selected improper ciphersuite " +  | 
 | 
                    CipherSuite.nameOf(cipherSuiteId));  | 
 | 
            }  | 
 | 
 | 
 | 
            this.compressionMethod = m.get();  | 
 | 
            if (compressionMethod != 0) { | 
 | 
                throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "compression type not supported, " + compressionMethod);  | 
 | 
            }  | 
 | 
 | 
 | 
            SSLExtension[] supportedExtensions;  | 
 | 
            if (serverRandom.isHelloRetryRequest()) { | 
 | 
                supportedExtensions = context.sslConfig.getEnabledExtensions(  | 
 | 
                            SSLHandshake.HELLO_RETRY_REQUEST);  | 
 | 
            } else { | 
 | 
                supportedExtensions = context.sslConfig.getEnabledExtensions(  | 
 | 
                            SSLHandshake.SERVER_HELLO);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (m.hasRemaining()) { | 
 | 
                this.extensions =  | 
 | 
                    new SSLExtensions(this, m, supportedExtensions);  | 
 | 
            } else { | 
 | 
                this.extensions = new SSLExtensions(this);  | 
 | 
            }  | 
 | 
 | 
 | 
            // The clientHello field is used for HelloRetryRequest producer  | 
 | 
            // only.  It's fine to set it to null for receiving side of  | 
 | 
            // ServerHello/HelloRetryRequest message.  | 
 | 
            this.clientHello = null;          | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public SSLHandshake handshakeType() { | 
 | 
            return serverRandom.isHelloRetryRequest() ?  | 
 | 
                SSLHandshake.HELLO_RETRY_REQUEST : SSLHandshake.SERVER_HELLO;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public int messageLength() { | 
 | 
            // almost fixed header size, except session ID and extensions:  | 
 | 
            //      major + minor = 2  | 
 | 
            //      random = 32  | 
 | 
            //      session ID len field = 1  | 
 | 
            //      cipher suite = 2  | 
 | 
            //      compression = 1  | 
 | 
            //      extensions: if present, 2 + length of extensions  | 
 | 
              | 
 | 
            return 38 + sessionId.length() + extensions.length();  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void send(HandshakeOutStream hos) throws IOException { | 
 | 
            hos.putInt8(serverVersion.major);  | 
 | 
            hos.putInt8(serverVersion.minor);  | 
 | 
            hos.write(serverRandom.randomBytes);  | 
 | 
            hos.putBytes8(sessionId.getId());  | 
 | 
            hos.putInt8((cipherSuite.id >> 8) & 0xFF);  | 
 | 
            hos.putInt8(cipherSuite.id & 0xff);  | 
 | 
            hos.putInt8(compressionMethod);  | 
 | 
 | 
 | 
            extensions.send(hos);             | 
 | 
                                            // extensions is mandatory.  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            MessageFormat messageFormat = new MessageFormat(  | 
 | 
                "\"{0}\": '{'\n" + | 
 | 
                "  \"server version\"      : \"{1}\",\n" + | 
 | 
                "  \"random\"              : \"{2}\",\n" + | 
 | 
                "  \"session id\"          : \"{3}\",\n" + | 
 | 
                "  \"cipher suite\"        : \"{4}\",\n" + | 
 | 
                "  \"compression methods\" : \"{5}\",\n" + | 
 | 
                "  \"extensions\"          : [\n" +  | 
 | 
                "{6}\n" + | 
 | 
                "  ]\n" +  | 
 | 
                "'}'",  | 
 | 
                Locale.ENGLISH);  | 
 | 
            Object[] messageFields = { | 
 | 
                serverRandom.isHelloRetryRequest() ?  | 
 | 
                    "HelloRetryRequest" : "ServerHello",  | 
 | 
                serverVersion.name,  | 
 | 
                Utilities.toHexString(serverRandom.randomBytes),  | 
 | 
                sessionId.toString(),  | 
 | 
                cipherSuite.name + "(" + | 
 | 
                        Utilities.byte16HexString(cipherSuite.id) + ")",  | 
 | 
                Utilities.toHexString(compressionMethod),  | 
 | 
                Utilities.indent(extensions.toString(), "    ")  | 
 | 
            };  | 
 | 
 | 
 | 
            return messageFormat.format(messageFields);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final class T12ServerHelloProducer  | 
 | 
            implements HandshakeProducer { | 
 | 
 | 
 | 
          | 
 | 
        private T12ServerHelloProducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            ServerHandshakeContext shc = (ServerHandshakeContext)context;  | 
 | 
            ClientHelloMessage clientHello = (ClientHelloMessage)message;  | 
 | 
 | 
 | 
            // If client hasn't specified a session we can resume, start a  | 
 | 
            // new one and choose its cipher suite and compression options,  | 
 | 
              | 
 | 
            if (!shc.isResumption || shc.resumingSession == null) { | 
 | 
                if (!shc.sslConfig.enableSessionCreation) { | 
 | 
                    throw new SSLException(  | 
 | 
                        "Not resumption, and no new session is allowed");  | 
 | 
                }  | 
 | 
 | 
 | 
                if (shc.localSupportedSignAlgs == null) { | 
 | 
                    shc.localSupportedSignAlgs =  | 
 | 
                        SignatureScheme.getSupportedAlgorithms(  | 
 | 
                                shc.sslConfig,  | 
 | 
                                shc.algorithmConstraints, shc.activeProtocols);  | 
 | 
                }  | 
 | 
 | 
 | 
                SSLSessionImpl session =  | 
 | 
                        new SSLSessionImpl(shc, CipherSuite.C_NULL);  | 
 | 
                session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize);  | 
 | 
                shc.handshakeSession = session;  | 
 | 
 | 
 | 
                  | 
 | 
                SSLExtension[] enabledExtensions =  | 
 | 
                        shc.sslConfig.getEnabledExtensions(  | 
 | 
                            SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol);  | 
 | 
                clientHello.extensions.consumeOnTrade(shc, enabledExtensions);  | 
 | 
 | 
 | 
                  | 
 | 
                KeyExchangeProperties credentials =  | 
 | 
                        chooseCipherSuite(shc, clientHello);  | 
 | 
                if (credentials == null) { | 
 | 
                    throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                            "no cipher suites in common");  | 
 | 
                }  | 
 | 
                shc.negotiatedCipherSuite = credentials.cipherSuite;  | 
 | 
                shc.handshakeKeyExchange = credentials.keyExchange;  | 
 | 
                shc.handshakeSession.setSuite(credentials.cipherSuite);  | 
 | 
                shc.handshakePossessions.addAll(  | 
 | 
                        Arrays.asList(credentials.possessions));  | 
 | 
                shc.handshakeHash.determine(  | 
 | 
                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);  | 
 | 
 | 
 | 
                // Check the incoming OCSP stapling extensions and attempt  | 
 | 
                // to get responses.  If the resulting stapleParams is non  | 
 | 
                  | 
 | 
                shc.stapleParams = StatusResponseManager.processStapling(shc);  | 
 | 
                shc.staplingActive = (shc.stapleParams != null);  | 
 | 
 | 
 | 
                  | 
 | 
                SSLKeyExchange ke = credentials.keyExchange;  | 
 | 
                if (ke != null) { | 
 | 
                    for (Map.Entry<Byte, HandshakeProducer> me :  | 
 | 
                            ke.getHandshakeProducers(shc)) { | 
 | 
                        shc.handshakeProducers.put(  | 
 | 
                                me.getKey(), me.getValue());  | 
 | 
                    }  | 
 | 
                }  | 
 | 
 | 
 | 
                if ((ke != null) &&  | 
 | 
                        (shc.sslConfig.clientAuthType !=  | 
 | 
                                ClientAuthType.CLIENT_AUTH_NONE) &&  | 
 | 
                        !shc.negotiatedCipherSuite.isAnonymous()) { | 
 | 
                    for (SSLHandshake hs :  | 
 | 
                            ke.getRelatedHandshakers(shc)) { | 
 | 
                        if (hs == SSLHandshake.CERTIFICATE) { | 
 | 
                            shc.handshakeProducers.put(  | 
 | 
                                    SSLHandshake.CERTIFICATE_REQUEST.id,  | 
 | 
                                    SSLHandshake.CERTIFICATE_REQUEST);  | 
 | 
                            break;  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO_DONE.id,  | 
 | 
                        SSLHandshake.SERVER_HELLO_DONE);  | 
 | 
            } else { | 
 | 
                shc.handshakeSession = shc.resumingSession;  | 
 | 
                shc.negotiatedProtocol =  | 
 | 
                        shc.resumingSession.getProtocolVersion();  | 
 | 
                shc.negotiatedCipherSuite = shc.resumingSession.getSuite();  | 
 | 
                shc.handshakeHash.determine(  | 
 | 
                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            ServerHelloMessage shm = new ServerHelloMessage(shc,  | 
 | 
                    shc.negotiatedProtocol,  | 
 | 
                    shc.handshakeSession.getSessionId(),  | 
 | 
                    shc.negotiatedCipherSuite,  | 
 | 
                    new RandomCookie(shc),  | 
 | 
                    clientHello);  | 
 | 
            shc.serverHelloRandom = shm.serverRandom;  | 
 | 
 | 
 | 
              | 
 | 
            SSLExtension[] serverHelloExtensions =  | 
 | 
                shc.sslConfig.getEnabledExtensions(  | 
 | 
                        SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol);  | 
 | 
            shm.extensions.produce(shc, serverHelloExtensions);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine("Produced ServerHello handshake message", shm); | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            shm.write(shc.handshakeOutput);  | 
 | 
            shc.handshakeOutput.flush();  | 
 | 
 | 
 | 
            if (shc.isResumption && shc.resumingSession != null) { | 
 | 
                SSLTrafficKeyDerivation kdg =  | 
 | 
                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);  | 
 | 
                if (kdg == null) { | 
 | 
                      | 
 | 
                    throw shc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                            "Not supported key derivation: " +  | 
 | 
                            shc.negotiatedProtocol);  | 
 | 
                } else { | 
 | 
                    shc.handshakeKeyDerivation = kdg.createKeyDerivation(  | 
 | 
                            shc, shc.resumingSession.getMasterSecret());  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
                shc.handshakeProducers.put(SSLHandshake.FINISHED.id,  | 
 | 
                        SSLHandshake.FINISHED);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
 | 
 | 
        private static KeyExchangeProperties chooseCipherSuite(  | 
 | 
                ServerHandshakeContext shc,  | 
 | 
                ClientHelloMessage clientHello) throws IOException { | 
 | 
            List<CipherSuite> preferred;  | 
 | 
            List<CipherSuite> proposed;  | 
 | 
            if (shc.sslConfig.preferLocalCipherSuites) { | 
 | 
                preferred = shc.activeCipherSuites;  | 
 | 
                proposed = clientHello.cipherSuites;  | 
 | 
            } else { | 
 | 
                preferred = clientHello.cipherSuites;  | 
 | 
                proposed = shc.activeCipherSuites;  | 
 | 
            }  | 
 | 
 | 
 | 
            List<CipherSuite> legacySuites = new LinkedList<>();  | 
 | 
            for (CipherSuite cs : preferred) { | 
 | 
                if (!HandshakeContext.isNegotiable(  | 
 | 
                        proposed, shc.negotiatedProtocol, cs)) { | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
 | 
 | 
                if (shc.sslConfig.clientAuthType ==  | 
 | 
                        ClientAuthType.CLIENT_AUTH_REQUIRED) { | 
 | 
                    if ((cs.keyExchange == KeyExchange.K_DH_ANON) ||  | 
 | 
                        (cs.keyExchange == KeyExchange.K_ECDH_ANON)) { | 
 | 
                        continue;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
 | 
 | 
                SSLKeyExchange ke = SSLKeyExchange.valueOf(  | 
 | 
                        cs.keyExchange, shc.negotiatedProtocol);  | 
 | 
                if (ke == null) { | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
                if (!ServerHandshakeContext.legacyAlgorithmConstraints.permits(  | 
 | 
                        null, cs.name, null)) { | 
 | 
                    legacySuites.add(cs);  | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
 | 
 | 
                SSLPossession[] hcds = ke.createPossessions(shc);  | 
 | 
                if ((hcds == null) || (hcds.length == 0)) { | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine("use cipher suite " + cs.name); | 
 | 
                }  | 
 | 
 | 
 | 
                return new KeyExchangeProperties(cs, ke, hcds);  | 
 | 
            }  | 
 | 
 | 
 | 
            for (CipherSuite cs : legacySuites) { | 
 | 
                SSLKeyExchange ke = SSLKeyExchange.valueOf(  | 
 | 
                        cs.keyExchange,  shc.negotiatedProtocol);  | 
 | 
                if (ke != null) { | 
 | 
                    SSLPossession[] hcds = ke.createPossessions(shc);  | 
 | 
                    if ((hcds != null) && (hcds.length != 0)) { | 
 | 
                        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                            SSLLogger.warning(  | 
 | 
                                "use legacy cipher suite " + cs.name);  | 
 | 
                        }  | 
 | 
                        return new KeyExchangeProperties(cs, ke, hcds);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                    "no cipher suites in common");  | 
 | 
        }  | 
 | 
 | 
 | 
        private static final class KeyExchangeProperties { | 
 | 
            final CipherSuite cipherSuite;  | 
 | 
            final SSLKeyExchange keyExchange;  | 
 | 
            final SSLPossession[] possessions;  | 
 | 
 | 
 | 
            private KeyExchangeProperties(CipherSuite cipherSuite,  | 
 | 
                    SSLKeyExchange keyExchange, SSLPossession[] possessions) { | 
 | 
                this.cipherSuite = cipherSuite;  | 
 | 
                this.keyExchange = keyExchange;  | 
 | 
                this.possessions = possessions;  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class T13ServerHelloProducer implements HandshakeProducer { | 
 | 
          | 
 | 
        private T13ServerHelloProducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            ServerHandshakeContext shc = (ServerHandshakeContext)context;  | 
 | 
            ClientHelloMessage clientHello = (ClientHelloMessage)message;  | 
 | 
 | 
 | 
            // If client hasn't specified a session we can resume, start a  | 
 | 
            // new one and choose its cipher suite and compression options,  | 
 | 
              | 
 | 
            if (!shc.isResumption || shc.resumingSession == null) { | 
 | 
                if (!shc.sslConfig.enableSessionCreation) { | 
 | 
                    throw new SSLException(  | 
 | 
                        "Not resumption, and no new session is allowed");  | 
 | 
                }  | 
 | 
 | 
 | 
                if (shc.localSupportedSignAlgs == null) { | 
 | 
                    shc.localSupportedSignAlgs =  | 
 | 
                        SignatureScheme.getSupportedAlgorithms(  | 
 | 
                                shc.sslConfig,  | 
 | 
                                shc.algorithmConstraints, shc.activeProtocols);  | 
 | 
                }  | 
 | 
 | 
 | 
                SSLSessionImpl session =  | 
 | 
                        new SSLSessionImpl(shc, CipherSuite.C_NULL);  | 
 | 
                session.setMaximumPacketSize(shc.sslConfig.maximumPacketSize);  | 
 | 
                shc.handshakeSession = session;  | 
 | 
 | 
 | 
                  | 
 | 
                SSLExtension[] enabledExtensions =  | 
 | 
                        shc.sslConfig.getEnabledExtensions(  | 
 | 
                            SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol);  | 
 | 
                clientHello.extensions.consumeOnTrade(shc, enabledExtensions);  | 
 | 
 | 
 | 
                  | 
 | 
                CipherSuite cipherSuite = chooseCipherSuite(shc, clientHello);  | 
 | 
                if (cipherSuite == null) { | 
 | 
                    throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                            "no cipher suites in common");  | 
 | 
                }  | 
 | 
                shc.negotiatedCipherSuite = cipherSuite;  | 
 | 
                shc.handshakeSession.setSuite(cipherSuite);  | 
 | 
                shc.handshakeHash.determine(  | 
 | 
                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);  | 
 | 
            } else { | 
 | 
                shc.handshakeSession = shc.resumingSession;  | 
 | 
 | 
 | 
                  | 
 | 
                SSLExtension[] enabledExtensions =  | 
 | 
                shc.sslConfig.getEnabledExtensions(  | 
 | 
                SSLHandshake.CLIENT_HELLO, shc.negotiatedProtocol);  | 
 | 
                clientHello.extensions.consumeOnTrade(shc, enabledExtensions);  | 
 | 
 | 
 | 
                shc.negotiatedProtocol =  | 
 | 
                        shc.resumingSession.getProtocolVersion();  | 
 | 
                shc.negotiatedCipherSuite = shc.resumingSession.getSuite();  | 
 | 
                shc.handshakeHash.determine(  | 
 | 
                        shc.negotiatedProtocol, shc.negotiatedCipherSuite);  | 
 | 
 | 
 | 
                setUpPskKD(shc,  | 
 | 
                        shc.resumingSession.consumePreSharedKey());  | 
 | 
 | 
 | 
                  | 
 | 
                SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)  | 
 | 
                    shc.sslContext.engineGetServerSessionContext();  | 
 | 
                sessionCache.remove(shc.resumingSession.getSessionId());  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            shc.handshakeProducers.put(SSLHandshake.ENCRYPTED_EXTENSIONS.id,  | 
 | 
                    SSLHandshake.ENCRYPTED_EXTENSIONS);  | 
 | 
            shc.handshakeProducers.put(SSLHandshake.FINISHED.id,  | 
 | 
                    SSLHandshake.FINISHED);  | 
 | 
 | 
 | 
              | 
 | 
            ServerHelloMessage shm = new ServerHelloMessage(shc,  | 
 | 
                    ProtocolVersion.TLS12,        | 
 | 
                    clientHello.sessionId,        | 
 | 
                    shc.negotiatedCipherSuite,  | 
 | 
                    new RandomCookie(shc),  | 
 | 
                    clientHello);  | 
 | 
            shc.serverHelloRandom = shm.serverRandom;  | 
 | 
 | 
 | 
              | 
 | 
            SSLExtension[] serverHelloExtensions =  | 
 | 
                    shc.sslConfig.getEnabledExtensions(  | 
 | 
                        SSLHandshake.SERVER_HELLO, shc.negotiatedProtocol);  | 
 | 
            shm.extensions.produce(shc, serverHelloExtensions);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine("Produced ServerHello handshake message", shm); | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            shm.write(shc.handshakeOutput);  | 
 | 
            shc.handshakeOutput.flush();  | 
 | 
 | 
 | 
            // Change client/server handshake traffic secrets.  | 
 | 
              | 
 | 
            shc.handshakeHash.update();  | 
 | 
 | 
 | 
              | 
 | 
            SSLKeyExchange ke = shc.handshakeKeyExchange;  | 
 | 
            if (ke == null) { | 
 | 
                  | 
 | 
                throw shc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                        "Not negotiated key shares");  | 
 | 
            }  | 
 | 
 | 
 | 
            SSLKeyDerivation handshakeKD = ke.createKeyDerivation(shc);  | 
 | 
            SecretKey handshakeSecret = handshakeKD.deriveKey(  | 
 | 
                    "TlsHandshakeSecret", null);  | 
 | 
 | 
 | 
            SSLTrafficKeyDerivation kdg =  | 
 | 
                SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);  | 
 | 
            if (kdg == null) { | 
 | 
                  | 
 | 
                throw shc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                        "Not supported key derivation: " +  | 
 | 
                        shc.negotiatedProtocol);  | 
 | 
            }  | 
 | 
 | 
 | 
            SSLKeyDerivation kd =  | 
 | 
                    new SSLSecretDerivation(shc, handshakeSecret);  | 
 | 
 | 
 | 
              | 
 | 
            SecretKey readSecret = kd.deriveKey(  | 
 | 
                    "TlsClientHandshakeTrafficSecret", null);  | 
 | 
            SSLKeyDerivation readKD =  | 
 | 
                    kdg.createKeyDerivation(shc, readSecret);  | 
 | 
            SecretKey readKey = readKD.deriveKey(  | 
 | 
                    "TlsKey", null);  | 
 | 
            SecretKey readIvSecret = readKD.deriveKey(  | 
 | 
                    "TlsIv", null);  | 
 | 
            IvParameterSpec readIv =  | 
 | 
                    new IvParameterSpec(readIvSecret.getEncoded());  | 
 | 
            SSLReadCipher readCipher;  | 
 | 
            try { | 
 | 
                readCipher =  | 
 | 
                    shc.negotiatedCipherSuite.bulkCipher.createReadCipher(  | 
 | 
                        Authenticator.valueOf(shc.negotiatedProtocol),  | 
 | 
                        shc.negotiatedProtocol, readKey, readIv,  | 
 | 
                        shc.sslContext.getSecureRandom());  | 
 | 
            } catch (GeneralSecurityException gse) { | 
 | 
                  | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "Missing cipher algorithm", gse);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (readCipher == null) { | 
 | 
                throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "Illegal cipher suite (" + shc.negotiatedCipherSuite + | 
 | 
                    ") and protocol version (" + shc.negotiatedProtocol + | 
 | 
                    ")");  | 
 | 
            }  | 
 | 
 | 
 | 
            shc.baseReadSecret = readSecret;  | 
 | 
            shc.conContext.inputRecord.changeReadCiphers(readCipher);  | 
 | 
 | 
 | 
              | 
 | 
            SecretKey writeSecret = kd.deriveKey(  | 
 | 
                    "TlsServerHandshakeTrafficSecret", null);  | 
 | 
            SSLKeyDerivation writeKD =  | 
 | 
                    kdg.createKeyDerivation(shc, writeSecret);  | 
 | 
            SecretKey writeKey = writeKD.deriveKey(  | 
 | 
                    "TlsKey", null);  | 
 | 
            SecretKey writeIvSecret = writeKD.deriveKey(  | 
 | 
                    "TlsIv", null);  | 
 | 
            IvParameterSpec writeIv =  | 
 | 
                    new IvParameterSpec(writeIvSecret.getEncoded());  | 
 | 
            SSLWriteCipher writeCipher;  | 
 | 
            try { | 
 | 
                writeCipher =  | 
 | 
                    shc.negotiatedCipherSuite.bulkCipher.createWriteCipher(  | 
 | 
                        Authenticator.valueOf(shc.negotiatedProtocol),  | 
 | 
                        shc.negotiatedProtocol, writeKey, writeIv,  | 
 | 
                        shc.sslContext.getSecureRandom());  | 
 | 
            } catch (GeneralSecurityException gse) { | 
 | 
                  | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "Missing cipher algorithm", gse);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (writeCipher == null) { | 
 | 
                throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "Illegal cipher suite (" + shc.negotiatedCipherSuite + | 
 | 
                    ") and protocol version (" + shc.negotiatedProtocol + | 
 | 
                    ")");  | 
 | 
            }  | 
 | 
 | 
 | 
            shc.baseWriteSecret = writeSecret;  | 
 | 
            shc.conContext.outputRecord.changeWriteCiphers(  | 
 | 
                    writeCipher, (clientHello.sessionId.length() != 0));  | 
 | 
 | 
 | 
              | 
 | 
            shc.handshakeKeyDerivation = kd;  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
 | 
 | 
        private static CipherSuite chooseCipherSuite(  | 
 | 
                ServerHandshakeContext shc,  | 
 | 
                ClientHelloMessage clientHello) throws IOException { | 
 | 
            List<CipherSuite> preferred;  | 
 | 
            List<CipherSuite> proposed;  | 
 | 
            if (shc.sslConfig.preferLocalCipherSuites) { | 
 | 
                preferred = shc.activeCipherSuites;  | 
 | 
                proposed = clientHello.cipherSuites;  | 
 | 
            } else { | 
 | 
                preferred = clientHello.cipherSuites;  | 
 | 
                proposed = shc.activeCipherSuites;  | 
 | 
            }  | 
 | 
 | 
 | 
            CipherSuite legacySuite = null;  | 
 | 
            AlgorithmConstraints legacyConstraints =  | 
 | 
                    ServerHandshakeContext.legacyAlgorithmConstraints;  | 
 | 
            for (CipherSuite cs : preferred) { | 
 | 
                if (!HandshakeContext.isNegotiable(  | 
 | 
                        proposed, shc.negotiatedProtocol, cs)) { | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
 | 
 | 
                if ((legacySuite == null) &&  | 
 | 
                        !legacyConstraints.permits(null, cs.name, null)) { | 
 | 
                    legacySuite = cs;  | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
 | 
 | 
                  | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine("use cipher suite " + cs.name); | 
 | 
                }  | 
 | 
                return cs;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (legacySuite != null) { | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.warning(  | 
 | 
                            "use legacy cipher suite " + legacySuite.name);  | 
 | 
                }  | 
 | 
                return legacySuite;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class T13HelloRetryRequestProducer implements HandshakeProducer { | 
 | 
          | 
 | 
        private T13HelloRetryRequestProducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
            ServerHandshakeContext shc = (ServerHandshakeContext) context;  | 
 | 
            ClientHelloMessage clientHello = (ClientHelloMessage) message;  | 
 | 
 | 
 | 
              | 
 | 
            CipherSuite cipherSuite =  | 
 | 
                    T13ServerHelloProducer.chooseCipherSuite(shc, clientHello);  | 
 | 
            if (cipherSuite == null) { | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "no cipher suites in common for hello retry request");  | 
 | 
            }  | 
 | 
 | 
 | 
            ServerHelloMessage hhrm = new ServerHelloMessage(shc,  | 
 | 
                    ProtocolVersion.TLS12,        | 
 | 
                    clientHello.sessionId,        | 
 | 
                    cipherSuite,  | 
 | 
                    RandomCookie.hrrRandom,  | 
 | 
                    clientHello  | 
 | 
            );  | 
 | 
 | 
 | 
            shc.negotiatedCipherSuite = cipherSuite;  | 
 | 
            shc.handshakeHash.determine(  | 
 | 
                    shc.negotiatedProtocol, shc.negotiatedCipherSuite);  | 
 | 
 | 
 | 
              | 
 | 
            SSLExtension[] serverHelloExtensions =  | 
 | 
                shc.sslConfig.getEnabledExtensions(  | 
 | 
                    SSLHandshake.HELLO_RETRY_REQUEST, shc.negotiatedProtocol);  | 
 | 
            hhrm.extensions.produce(shc, serverHelloExtensions);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                        "Produced HelloRetryRequest handshake message", hhrm);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            hhrm.write(shc.handshakeOutput);  | 
 | 
            shc.handshakeOutput.flush();  | 
 | 
 | 
 | 
            // Stateless, shall we clean up the handshake context as well?  | 
 | 
            shc.handshakeHash.finish();       | 
 | 
            shc.handshakeExtensions.clear();  | 
 | 
 | 
 | 
              | 
 | 
            shc.handshakeConsumers.put(  | 
 | 
                    SSLHandshake.CLIENT_HELLO.id, SSLHandshake.CLIENT_HELLO);  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class T13HelloRetryRequestReproducer implements HandshakeProducer { | 
 | 
          | 
 | 
        private T13HelloRetryRequestReproducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
            ServerHandshakeContext shc = (ServerHandshakeContext) context;  | 
 | 
            ClientHelloMessage clientHello = (ClientHelloMessage) message;  | 
 | 
 | 
 | 
              | 
 | 
            CipherSuite cipherSuite = shc.negotiatedCipherSuite;  | 
 | 
            ServerHelloMessage hhrm = new ServerHelloMessage(shc,  | 
 | 
                    ProtocolVersion.TLS12,        | 
 | 
                    clientHello.sessionId,        | 
 | 
                    cipherSuite,  | 
 | 
                    RandomCookie.hrrRandom,  | 
 | 
                    clientHello  | 
 | 
            );  | 
 | 
 | 
 | 
              | 
 | 
            SSLExtension[] serverHelloExtensions =  | 
 | 
                shc.sslConfig.getEnabledExtensions(  | 
 | 
                    SSLHandshake.MESSAGE_HASH, shc.negotiatedProtocol);  | 
 | 
            hhrm.extensions.produce(shc, serverHelloExtensions);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                        "Reproduced HelloRetryRequest handshake message", hhrm);  | 
 | 
            }  | 
 | 
 | 
 | 
            HandshakeOutStream hos = new HandshakeOutStream(null);  | 
 | 
            hhrm.write(hos);  | 
 | 
 | 
 | 
            return hos.toByteArray();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class ServerHelloConsumer implements SSLConsumer { | 
 | 
          | 
 | 
        private ServerHelloConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                ByteBuffer message) throws IOException { | 
 | 
              | 
 | 
            ClientHandshakeContext chc = (ClientHandshakeContext)context;  | 
 | 
 | 
 | 
              | 
 | 
            chc.handshakeConsumers.remove(SSLHandshake.SERVER_HELLO.id);  | 
 | 
            if (!chc.handshakeConsumers.isEmpty()) { | 
 | 
                throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,  | 
 | 
                    "No more message expected before ServerHello is processed");  | 
 | 
            }  | 
 | 
 | 
 | 
            ServerHelloMessage shm = new ServerHelloMessage(chc, message);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine("Consuming ServerHello handshake message", shm); | 
 | 
            }  | 
 | 
 | 
 | 
            if (shm.serverRandom.isHelloRetryRequest()) { | 
 | 
                onHelloRetryRequest(chc, shm);  | 
 | 
            } else { | 
 | 
                onServerHello(chc, shm);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        private void onHelloRetryRequest(ClientHandshakeContext chc,  | 
 | 
                ServerHelloMessage helloRetryRequest) throws IOException { | 
 | 
            // Negotiate protocol version.  | 
 | 
            //  | 
 | 
              | 
 | 
            SSLExtension[] extTypes = new SSLExtension[] { | 
 | 
                    SSLExtension.HRR_SUPPORTED_VERSIONS  | 
 | 
                };  | 
 | 
            helloRetryRequest.extensions.consumeOnLoad(chc, extTypes);  | 
 | 
 | 
 | 
            ProtocolVersion serverVersion;  | 
 | 
            SHSupportedVersionsSpec svs =  | 
 | 
                    (SHSupportedVersionsSpec)chc.handshakeExtensions.get(  | 
 | 
                            SSLExtension.HRR_SUPPORTED_VERSIONS);  | 
 | 
            if (svs != null) { | 
 | 
                serverVersion =              | 
 | 
                        ProtocolVersion.valueOf(svs.selectedVersion);  | 
 | 
            } else { | 
 | 
                serverVersion = helloRetryRequest.serverVersion;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (!chc.activeProtocols.contains(serverVersion)) { | 
 | 
                throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "The server selected protocol version " + serverVersion +  | 
 | 
                    " is not accepted by client preferences " +  | 
 | 
                    chc.activeProtocols);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (!serverVersion.useTLS13PlusSpec()) { | 
 | 
                throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "Unexpected HelloRetryRequest for " + serverVersion.name);  | 
 | 
            }  | 
 | 
 | 
 | 
            chc.negotiatedProtocol = serverVersion;  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                    "Negotiated protocol version: " + serverVersion.name);  | 
 | 
            }  | 
 | 
 | 
 | 
            // TLS 1.3 key share extension may have produced client  | 
 | 
            // possessions for TLS 1.3 key exchanges.  | 
 | 
            //  | 
 | 
              | 
 | 
            chc.handshakePossessions.clear();  | 
 | 
 | 
 | 
            t13HrrHandshakeConsumer.consume(chc, helloRetryRequest);  | 
 | 
        }  | 
 | 
 | 
 | 
        private void onServerHello(ClientHandshakeContext chc,  | 
 | 
                ServerHelloMessage serverHello) throws IOException { | 
 | 
            // Negotiate protocol version.  | 
 | 
            //  | 
 | 
              | 
 | 
            SSLExtension[] extTypes = new SSLExtension[] { | 
 | 
                    SSLExtension.SH_SUPPORTED_VERSIONS  | 
 | 
                };  | 
 | 
            serverHello.extensions.consumeOnLoad(chc, extTypes);  | 
 | 
 | 
 | 
            ProtocolVersion serverVersion;  | 
 | 
            SHSupportedVersionsSpec svs =  | 
 | 
                    (SHSupportedVersionsSpec)chc.handshakeExtensions.get(  | 
 | 
                            SSLExtension.SH_SUPPORTED_VERSIONS);  | 
 | 
            if (svs != null) { | 
 | 
                serverVersion =              | 
 | 
                        ProtocolVersion.valueOf(svs.selectedVersion);  | 
 | 
            } else { | 
 | 
                serverVersion = serverHello.serverVersion;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (!chc.activeProtocols.contains(serverVersion)) { | 
 | 
                throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "The server selected protocol version " + serverVersion +  | 
 | 
                    " is not accepted by client preferences " +  | 
 | 
                    chc.activeProtocols);  | 
 | 
            }  | 
 | 
 | 
 | 
            chc.negotiatedProtocol = serverVersion;  | 
 | 
            if (!chc.conContext.isNegotiated) { | 
 | 
                chc.conContext.protocolVersion = chc.negotiatedProtocol;  | 
 | 
                chc.conContext.outputRecord.setVersion(chc.negotiatedProtocol);  | 
 | 
            }  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                    "Negotiated protocol version: " + serverVersion.name);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (serverHello.serverRandom.isVersionDowngrade(chc)) { | 
 | 
                throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "A potential protocol version downgrade attack");  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (serverVersion.useTLS13PlusSpec()) { | 
 | 
                t13HandshakeConsumer.consume(chc, serverHello);  | 
 | 
            } else { | 
 | 
                // TLS 1.3 key share extension may have produced client  | 
 | 
                  | 
 | 
                chc.handshakePossessions.clear();  | 
 | 
 | 
 | 
                t12HandshakeConsumer.consume(chc, serverHello);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final  | 
 | 
            class T12ServerHelloConsumer implements HandshakeConsumer { | 
 | 
          | 
 | 
        private T12ServerHelloConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            ClientHandshakeContext chc = (ClientHandshakeContext)context;  | 
 | 
            ServerHelloMessage serverHello = (ServerHelloMessage)message;  | 
 | 
            if (!chc.isNegotiable(serverHello.serverVersion)) { | 
 | 
                throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "Server chose " + serverHello.serverVersion +  | 
 | 
                    ", but that protocol version is not enabled or " +  | 
 | 
                    "not supported by the client.");  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            chc.negotiatedCipherSuite = serverHello.cipherSuite;  | 
 | 
            chc.handshakeHash.determine(  | 
 | 
                    chc.negotiatedProtocol, chc.negotiatedCipherSuite);  | 
 | 
            chc.serverHelloRandom = serverHello.serverRandom;  | 
 | 
            if (chc.negotiatedCipherSuite.keyExchange == null) { | 
 | 
                throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "TLS 1.2 or prior version does not support the " +  | 
 | 
                    "server cipher suite: " + chc.negotiatedCipherSuite.name);  | 
 | 
            }  | 
 | 
 | 
 | 
            //  | 
 | 
            // validate  | 
 | 
            //  | 
 | 
 | 
 | 
              | 
 | 
            SSLExtension[] extTypes = new SSLExtension[] { | 
 | 
                    SSLExtension.SH_RENEGOTIATION_INFO  | 
 | 
                };  | 
 | 
            serverHello.extensions.consumeOnLoad(chc, extTypes);  | 
 | 
 | 
 | 
              | 
 | 
            if (chc.resumingSession != null) { | 
 | 
                  | 
 | 
                if (serverHello.sessionId.equals(  | 
 | 
                        chc.resumingSession.getSessionId())) { | 
 | 
                    // server resumed the session, let's make sure everything  | 
 | 
                    // checks out  | 
 | 
 | 
 | 
                      | 
 | 
                    CipherSuite sessionSuite = chc.resumingSession.getSuite();  | 
 | 
                    if (chc.negotiatedCipherSuite != sessionSuite) { | 
 | 
                        throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                            "Server returned wrong cipher suite for session");  | 
 | 
                    }  | 
 | 
 | 
 | 
                      | 
 | 
                    ProtocolVersion sessionVersion =  | 
 | 
                            chc.resumingSession.getProtocolVersion();  | 
 | 
                    if (chc.negotiatedProtocol != sessionVersion) { | 
 | 
                        throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                            "Server resumed with wrong protocol version");  | 
 | 
                    }  | 
 | 
 | 
 | 
                      | 
 | 
                    if (sessionSuite.keyExchange == KeyExchange.K_KRB5 ||  | 
 | 
                            sessionSuite.keyExchange == KeyExchange.K_KRB5_EXPORT) { | 
 | 
                        Principal localPrincipal = chc.resumingSession.getLocalPrincipal();  | 
 | 
 | 
 | 
                        Subject subject = null;  | 
 | 
                        try { | 
 | 
                            subject = AccessController.doPrivileged(  | 
 | 
                                    new PrivilegedExceptionAction<Subject>() { | 
 | 
                                        @Override  | 
 | 
                                        public Subject run() throws Exception { | 
 | 
                                            return Krb5Helper.getClientSubject(  | 
 | 
                                                    chc.conContext.acc);  | 
 | 
                                        }});  | 
 | 
                        } catch (PrivilegedActionException e) { | 
 | 
                            subject = null;  | 
 | 
                            if (SSLLogger.isOn &&  | 
 | 
                                    SSLLogger.isOn("ssl,handshake,verbose")) { | 
 | 
                                SSLLogger.fine("Attempt to obtain" + | 
 | 
                                        " subject failed!");  | 
 | 
                            }  | 
 | 
                        }  | 
 | 
 | 
 | 
                        if (subject != null) { | 
 | 
                              | 
 | 
                            Set<Principal> principals =  | 
 | 
                                    subject.getPrincipals(Principal.class);  | 
 | 
                            if (!principals.contains(localPrincipal)) { | 
 | 
                                throw new SSLProtocolException("Server resumed" + | 
 | 
                                        " session with wrong subject identity");  | 
 | 
                            } else { | 
 | 
                                if (SSLLogger.isOn &&  | 
 | 
                                        SSLLogger.isOn("ssl,handshake,verbose")) | 
 | 
                                    SSLLogger.fine("Subject identity is same"); | 
 | 
                            }  | 
 | 
                        } else { | 
 | 
                            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) | 
 | 
                                SSLLogger.fine("Kerberos credentials are not" + | 
 | 
                                        " present in the current Subject; check if" +  | 
 | 
                                        " javax.security.auth.useSubjectCredsOnly" +  | 
 | 
                                        " system property has been set to false");  | 
 | 
                            throw new SSLProtocolException  | 
 | 
                                    ("Server resumed session with no subject"); | 
 | 
                        }  | 
 | 
                    }  | 
 | 
 | 
 | 
                      | 
 | 
                    chc.isResumption = true;  | 
 | 
                    chc.resumingSession.setAsSessionResumption(true);  | 
 | 
                    chc.handshakeSession = chc.resumingSession;  | 
 | 
                } else { | 
 | 
                    // we wanted to resume, but the server refused  | 
 | 
                    //  | 
 | 
                    // Invalidate the session for initial handshake in case  | 
 | 
                      | 
 | 
                    if (chc.resumingSession != null) { | 
 | 
                        chc.resumingSession.invalidate();  | 
 | 
                        chc.resumingSession = null;  | 
 | 
                    }  | 
 | 
                    chc.isResumption = false;  | 
 | 
                    if (!chc.sslConfig.enableSessionCreation) { | 
 | 
                        throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                            "New session creation is disabled");  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            extTypes = chc.sslConfig.getEnabledExtensions(  | 
 | 
                    SSLHandshake.SERVER_HELLO);  | 
 | 
            serverHello.extensions.consumeOnLoad(chc, extTypes);  | 
 | 
 | 
 | 
            if (!chc.isResumption) { | 
 | 
                if (chc.resumingSession != null) { | 
 | 
                      | 
 | 
                    chc.resumingSession.invalidate();  | 
 | 
                    chc.resumingSession = null;  | 
 | 
                }  | 
 | 
 | 
 | 
                if (!chc.sslConfig.enableSessionCreation) { | 
 | 
                    throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                        "New session creation is disabled");  | 
 | 
                }  | 
 | 
                chc.handshakeSession = new SSLSessionImpl(chc,  | 
 | 
                        chc.negotiatedCipherSuite,  | 
 | 
                        serverHello.sessionId);  | 
 | 
                chc.handshakeSession.setMaximumPacketSize(  | 
 | 
                        chc.sslConfig.maximumPacketSize);  | 
 | 
            }  | 
 | 
 | 
 | 
            //  | 
 | 
            // update  | 
 | 
              | 
 | 
            serverHello.extensions.consumeOnTrade(chc, extTypes);  | 
 | 
 | 
 | 
              | 
 | 
            if (chc.isResumption) { | 
 | 
                SSLTrafficKeyDerivation kdg =  | 
 | 
                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);  | 
 | 
                if (kdg == null) { | 
 | 
                      | 
 | 
                    throw chc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                            "Not supported key derivation: " +  | 
 | 
                            chc.negotiatedProtocol);  | 
 | 
                } else { | 
 | 
                    chc.handshakeKeyDerivation = kdg.createKeyDerivation(  | 
 | 
                            chc, chc.resumingSession.getMasterSecret());  | 
 | 
                }  | 
 | 
 | 
 | 
                chc.conContext.consumers.putIfAbsent(  | 
 | 
                        ContentType.CHANGE_CIPHER_SPEC.id,  | 
 | 
                        ChangeCipherSpec.t10Consumer);  | 
 | 
                chc.handshakeConsumers.put(  | 
 | 
                        SSLHandshake.FINISHED.id,  | 
 | 
                        SSLHandshake.FINISHED);  | 
 | 
            } else { | 
 | 
                SSLKeyExchange ke = SSLKeyExchange.valueOf(  | 
 | 
                        chc.negotiatedCipherSuite.keyExchange,  | 
 | 
                        chc.negotiatedProtocol);  | 
 | 
                chc.handshakeKeyExchange = ke;  | 
 | 
                if (ke != null) { | 
 | 
                    for (SSLHandshake handshake :  | 
 | 
                            ke.getRelatedHandshakers(chc)) { | 
 | 
                        chc.handshakeConsumers.put(handshake.id, handshake);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
 | 
 | 
                chc.handshakeConsumers.put(SSLHandshake.SERVER_HELLO_DONE.id,  | 
 | 
                        SSLHandshake.SERVER_HELLO_DONE);  | 
 | 
            }  | 
 | 
 | 
 | 
            //  | 
 | 
            // produce  | 
 | 
            //  | 
 | 
            // Need no new handshake message producers here.  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static void setUpPskKD(HandshakeContext hc,  | 
 | 
            SecretKey psk) throws SSLHandshakeException { | 
 | 
 | 
 | 
        if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
            SSLLogger.fine("Using PSK to derive early secret"); | 
 | 
        }  | 
 | 
 | 
 | 
        try { | 
 | 
            CipherSuite.HashAlg hashAlg = hc.negotiatedCipherSuite.hashAlg;  | 
 | 
            HKDF hkdf = new HKDF(hashAlg.name);  | 
 | 
            byte[] zeros = new byte[hashAlg.hashLength];  | 
 | 
            SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");  | 
 | 
            hc.handshakeKeyDerivation =  | 
 | 
                    new SSLSecretDerivation(hc, earlySecret);  | 
 | 
        } catch  (GeneralSecurityException gse) { | 
 | 
            throw (SSLHandshakeException) new SSLHandshakeException(  | 
 | 
                "Could not generate secret").initCause(gse);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final  | 
 | 
            class T13ServerHelloConsumer implements HandshakeConsumer { | 
 | 
          | 
 | 
        private T13ServerHelloConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            ClientHandshakeContext chc = (ClientHandshakeContext)context;  | 
 | 
            ServerHelloMessage serverHello = (ServerHelloMessage)message;  | 
 | 
            if (serverHello.serverVersion != ProtocolVersion.TLS12) { | 
 | 
                throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "The ServerHello.legacy_version field is not TLS 1.2");  | 
 | 
            }  | 
 | 
 | 
 | 
            chc.negotiatedCipherSuite = serverHello.cipherSuite;  | 
 | 
            chc.handshakeHash.determine(  | 
 | 
                    chc.negotiatedProtocol, chc.negotiatedCipherSuite);  | 
 | 
            chc.serverHelloRandom = serverHello.serverRandom;  | 
 | 
 | 
 | 
            //  | 
 | 
            // validate  | 
 | 
            //  | 
 | 
 | 
 | 
              | 
 | 
            SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(  | 
 | 
                    SSLHandshake.SERVER_HELLO);  | 
 | 
            serverHello.extensions.consumeOnLoad(chc, extTypes);  | 
 | 
            if (!chc.isResumption) { | 
 | 
                if (chc.resumingSession != null) { | 
 | 
                      | 
 | 
                    chc.resumingSession.invalidate();  | 
 | 
                    chc.resumingSession = null;  | 
 | 
                }  | 
 | 
 | 
 | 
                if (!chc.sslConfig.enableSessionCreation) { | 
 | 
                    throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                        "New session creation is disabled");  | 
 | 
                }  | 
 | 
                chc.handshakeSession = new SSLSessionImpl(chc,  | 
 | 
                        chc.negotiatedCipherSuite,  | 
 | 
                        serverHello.sessionId);  | 
 | 
                chc.handshakeSession.setMaximumPacketSize(  | 
 | 
                        chc.sslConfig.maximumPacketSize);  | 
 | 
            } else { | 
 | 
                  | 
 | 
                SecretKey psk =  | 
 | 
                        chc.resumingSession.consumePreSharedKey();  | 
 | 
                if(psk == null) { | 
 | 
                    throw chc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                    "No PSK available. Unable to resume.");  | 
 | 
                }  | 
 | 
 | 
 | 
                chc.handshakeSession = chc.resumingSession;  | 
 | 
 | 
 | 
                setUpPskKD(chc, psk);  | 
 | 
            }  | 
 | 
 | 
 | 
            //  | 
 | 
            // update  | 
 | 
              | 
 | 
            serverHello.extensions.consumeOnTrade(chc, extTypes);  | 
 | 
 | 
 | 
            // Change client/server handshake traffic secrets.  | 
 | 
              | 
 | 
            chc.handshakeHash.update();  | 
 | 
 | 
 | 
            SSLKeyExchange ke = chc.handshakeKeyExchange;  | 
 | 
            if (ke == null) { | 
 | 
                  | 
 | 
                throw chc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                        "Not negotiated key shares");  | 
 | 
            }  | 
 | 
 | 
 | 
            SSLKeyDerivation handshakeKD = ke.createKeyDerivation(chc);  | 
 | 
            SecretKey handshakeSecret = handshakeKD.deriveKey(  | 
 | 
                    "TlsHandshakeSecret", null);  | 
 | 
            SSLTrafficKeyDerivation kdg =  | 
 | 
                SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);  | 
 | 
            if (kdg == null) { | 
 | 
                  | 
 | 
                throw chc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                        "Not supported key derivation: " +  | 
 | 
                        chc.negotiatedProtocol);  | 
 | 
            }  | 
 | 
 | 
 | 
            SSLKeyDerivation secretKD =  | 
 | 
                    new SSLSecretDerivation(chc, handshakeSecret);  | 
 | 
 | 
 | 
              | 
 | 
            SecretKey readSecret = secretKD.deriveKey(  | 
 | 
                    "TlsServerHandshakeTrafficSecret", null);  | 
 | 
 | 
 | 
            SSLKeyDerivation readKD =  | 
 | 
                    kdg.createKeyDerivation(chc, readSecret);  | 
 | 
            SecretKey readKey = readKD.deriveKey(  | 
 | 
                    "TlsKey", null);  | 
 | 
            SecretKey readIvSecret = readKD.deriveKey(  | 
 | 
                    "TlsIv", null);  | 
 | 
            IvParameterSpec readIv =  | 
 | 
                    new IvParameterSpec(readIvSecret.getEncoded());  | 
 | 
            SSLReadCipher readCipher;  | 
 | 
            try { | 
 | 
                readCipher =  | 
 | 
                    chc.negotiatedCipherSuite.bulkCipher.createReadCipher(  | 
 | 
                        Authenticator.valueOf(chc.negotiatedProtocol),  | 
 | 
                        chc.negotiatedProtocol, readKey, readIv,  | 
 | 
                        chc.sslContext.getSecureRandom());  | 
 | 
            } catch (GeneralSecurityException gse) { | 
 | 
                  | 
 | 
                throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "Missing cipher algorithm", gse);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (readCipher == null) { | 
 | 
                throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "Illegal cipher suite (" + chc.negotiatedCipherSuite + | 
 | 
                    ") and protocol version (" + chc.negotiatedProtocol + | 
 | 
                    ")");  | 
 | 
            }  | 
 | 
 | 
 | 
            chc.baseReadSecret = readSecret;  | 
 | 
            chc.conContext.inputRecord.changeReadCiphers(readCipher);  | 
 | 
 | 
 | 
              | 
 | 
            SecretKey writeSecret = secretKD.deriveKey(  | 
 | 
                    "TlsClientHandshakeTrafficSecret", null);  | 
 | 
            SSLKeyDerivation writeKD =  | 
 | 
                    kdg.createKeyDerivation(chc, writeSecret);  | 
 | 
            SecretKey writeKey = writeKD.deriveKey(  | 
 | 
                    "TlsKey", null);  | 
 | 
            SecretKey writeIvSecret = writeKD.deriveKey(  | 
 | 
                    "TlsIv", null);  | 
 | 
            IvParameterSpec writeIv =  | 
 | 
                    new IvParameterSpec(writeIvSecret.getEncoded());  | 
 | 
            SSLWriteCipher writeCipher;  | 
 | 
            try { | 
 | 
                writeCipher =  | 
 | 
                    chc.negotiatedCipherSuite.bulkCipher.createWriteCipher(  | 
 | 
                        Authenticator.valueOf(chc.negotiatedProtocol),  | 
 | 
                        chc.negotiatedProtocol, writeKey, writeIv,  | 
 | 
                        chc.sslContext.getSecureRandom());  | 
 | 
            } catch (GeneralSecurityException gse) { | 
 | 
                  | 
 | 
                throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "Missing cipher algorithm", gse);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (writeCipher == null) { | 
 | 
                throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "Illegal cipher suite (" + chc.negotiatedCipherSuite + | 
 | 
                    ") and protocol version (" + chc.negotiatedProtocol + | 
 | 
                    ")");  | 
 | 
            }  | 
 | 
 | 
 | 
            chc.baseWriteSecret = writeSecret;  | 
 | 
            chc.conContext.outputRecord.changeWriteCiphers(  | 
 | 
                    writeCipher, (serverHello.sessionId.length() != 0));  | 
 | 
 | 
 | 
            // Should use resumption_master_secret for TLS 1.3.  | 
 | 
            // chc.handshakeSession.setMasterSecret(masterSecret);  | 
 | 
 | 
 | 
              | 
 | 
            chc.handshakeKeyDerivation = secretKD;  | 
 | 
 | 
 | 
            // update the consumers and producers  | 
 | 
            //  | 
 | 
            // The server sends a dummy change_cipher_spec record immediately  | 
 | 
            // after its first handshake message.  This may either be after a  | 
 | 
              | 
 | 
            chc.conContext.consumers.putIfAbsent(  | 
 | 
                    ContentType.CHANGE_CIPHER_SPEC.id,  | 
 | 
                    ChangeCipherSpec.t13Consumer);  | 
 | 
 | 
 | 
            chc.handshakeConsumers.put(  | 
 | 
                    SSLHandshake.ENCRYPTED_EXTENSIONS.id,  | 
 | 
                    SSLHandshake.ENCRYPTED_EXTENSIONS);  | 
 | 
 | 
 | 
              | 
 | 
            chc.handshakeConsumers.put(  | 
 | 
                    SSLHandshake.CERTIFICATE_REQUEST.id,  | 
 | 
                    SSLHandshake.CERTIFICATE_REQUEST);  | 
 | 
            chc.handshakeConsumers.put(  | 
 | 
                    SSLHandshake.CERTIFICATE.id,  | 
 | 
                    SSLHandshake.CERTIFICATE);  | 
 | 
            chc.handshakeConsumers.put(  | 
 | 
                    SSLHandshake.CERTIFICATE_VERIFY.id,  | 
 | 
                    SSLHandshake.CERTIFICATE_VERIFY);  | 
 | 
 | 
 | 
            chc.handshakeConsumers.put(  | 
 | 
                    SSLHandshake.FINISHED.id,  | 
 | 
                    SSLHandshake.FINISHED);  | 
 | 
 | 
 | 
            //  | 
 | 
            // produce  | 
 | 
            //  | 
 | 
            // Need no new handshake message producers here.  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final  | 
 | 
            class T13HelloRetryRequestConsumer implements HandshakeConsumer { | 
 | 
          | 
 | 
        private T13HelloRetryRequestConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            ClientHandshakeContext chc = (ClientHandshakeContext)context;  | 
 | 
            ServerHelloMessage helloRetryRequest = (ServerHelloMessage)message;  | 
 | 
            if (helloRetryRequest.serverVersion != ProtocolVersion.TLS12) { | 
 | 
                throw chc.conContext.fatal(Alert.PROTOCOL_VERSION,  | 
 | 
                    "The HelloRetryRequest.legacy_version is not TLS 1.2");  | 
 | 
            }  | 
 | 
 | 
 | 
            chc.negotiatedCipherSuite = helloRetryRequest.cipherSuite;  | 
 | 
 | 
 | 
            //  | 
 | 
            // validate  | 
 | 
            //  | 
 | 
 | 
 | 
              | 
 | 
            SSLExtension[] extTypes = chc.sslConfig.getEnabledExtensions(  | 
 | 
                    SSLHandshake.HELLO_RETRY_REQUEST);  | 
 | 
            helloRetryRequest.extensions.consumeOnLoad(chc, extTypes);  | 
 | 
 | 
 | 
            //  | 
 | 
            // update  | 
 | 
              | 
 | 
            helloRetryRequest.extensions.consumeOnTrade(chc, extTypes);  | 
 | 
 | 
 | 
            // Change client/server handshake traffic secrets.  | 
 | 
            // Refresh handshake hash  | 
 | 
            chc.handshakeHash.finish();       | 
 | 
 | 
 | 
              | 
 | 
            HandshakeOutStream hos = new HandshakeOutStream(null);  | 
 | 
            try { | 
 | 
                chc.initialClientHelloMsg.write(hos);  | 
 | 
            } catch (IOException ioe) { | 
 | 
                  | 
 | 
                throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                    "Failed to construct message hash", ioe);  | 
 | 
            }  | 
 | 
            chc.handshakeHash.deliver(hos.toByteArray());  | 
 | 
            chc.handshakeHash.determine(  | 
 | 
                    chc.negotiatedProtocol, chc.negotiatedCipherSuite);  | 
 | 
            byte[] clientHelloHash = chc.handshakeHash.digest();  | 
 | 
 | 
 | 
            // calculate the message_hash  | 
 | 
            //  | 
 | 
            // Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =  | 
 | 
            //   Hash(message_hash ||    /* Handshake type */  | 
 | 
            //     00 00 Hash.length ||  /* Handshake message length (bytes) */  | 
 | 
            //     Hash(ClientHello1) || /* Hash of ClientHello1 */  | 
 | 
              | 
 | 
            int hashLen = chc.negotiatedCipherSuite.hashAlg.hashLength;  | 
 | 
            byte[] hashedClientHello = new byte[4 + hashLen];  | 
 | 
            hashedClientHello[0] = SSLHandshake.MESSAGE_HASH.id;  | 
 | 
            hashedClientHello[1] = (byte)0x00;  | 
 | 
            hashedClientHello[2] = (byte)0x00;  | 
 | 
            hashedClientHello[3] = (byte)(hashLen & 0xFF);  | 
 | 
            System.arraycopy(clientHelloHash, 0,  | 
 | 
                    hashedClientHello, 4, hashLen);  | 
 | 
 | 
 | 
            chc.handshakeHash.finish();       | 
 | 
            chc.handshakeHash.deliver(hashedClientHello);  | 
 | 
 | 
 | 
            int hrrBodyLen = helloRetryRequest.handshakeRecord.remaining();  | 
 | 
            byte[] hrrMessage = new byte[4 + hrrBodyLen];  | 
 | 
            hrrMessage[0] = SSLHandshake.HELLO_RETRY_REQUEST.id;  | 
 | 
            hrrMessage[1] = (byte)((hrrBodyLen >> 16) & 0xFF);  | 
 | 
            hrrMessage[2] = (byte)((hrrBodyLen >> 8) & 0xFF);  | 
 | 
            hrrMessage[3] = (byte)(hrrBodyLen & 0xFF);  | 
 | 
 | 
 | 
            ByteBuffer hrrBody = helloRetryRequest.handshakeRecord.duplicate();  | 
 | 
            hrrBody.get(hrrMessage, 4, hrrBodyLen);  | 
 | 
 | 
 | 
            chc.handshakeHash.receive(hrrMessage);  | 
 | 
 | 
 | 
              | 
 | 
            chc.initialClientHelloMsg.extensions.reproduce(chc,  | 
 | 
                    new SSLExtension[] { | 
 | 
                        SSLExtension.CH_COOKIE,  | 
 | 
                        SSLExtension.CH_KEY_SHARE,  | 
 | 
                        SSLExtension.CH_PRE_SHARED_KEY  | 
 | 
                    });  | 
 | 
 | 
 | 
            //  | 
 | 
            // produce response handshake message  | 
 | 
              | 
 | 
            SSLHandshake.CLIENT_HELLO.produce(context, helloRetryRequest);  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |