OpenJDK / jdk / jdk12
changeset 30904:ec0224270f90
8043758: Datagram Transport Layer Security (DTLS)
Reviewed-by: jnimeh, weijun, mullan, wetmore
line wrap: on
line diff
--- a/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/ExtendedSSLSession.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,7 @@ import java.util.List; /** - * Extends the <code>SSLSession</code> interface to support additional + * Extends the {@code SSLSession} interface to support additional * session attributes. * * @since 1.7 @@ -39,8 +39,8 @@ * is willing to use. * <p> * Note: this method is used to indicate to the peer which signature - * algorithms may be used for digital signatures in TLS 1.2. It is - * not meaningful for TLS versions prior to 1.2. + * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is + * not meaningful for TLS/DTLS versions prior to 1.2. * <p> * The signature algorithm name must be a standard Java Security * name (such as "SHA1withRSA", "SHA256withECDSA", and so on). @@ -52,7 +52,7 @@ * Note: the local supported signature algorithms should conform to * the algorithm constraints specified by * {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()} - * method in <code>SSLParameters</code>. + * method in {@code SSLParameters}. * * @return An array of supported signature algorithms, in descending * order of preference. The return value is an empty array if @@ -67,8 +67,8 @@ * able to use. * <p> * Note: this method is used to indicate to the local side which signature - * algorithms may be used for digital signatures in TLS 1.2. It is - * not meaningful for TLS versions prior to 1.2. + * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is + * not meaningful for TLS/DTLS versions prior to 1.2. * <p> * The signature algorithm name must be a standard Java Security * name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SNIServerName.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SNIServerName.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ * Instances of this class represent a server name in a Server Name * Indication (SNI) extension. * <P> - * The SNI extension is a feature that extends the SSL/TLS protocols to + * The SNI extension is a feature that extends the SSL/TLS/DTLS protocols to * indicate what server name the client is attempting to connect to during * handshaking. See section 3, "Server Name Indication", of <A * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>.
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLContext.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLContext.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,12 +32,12 @@ /** * Instances of this class represent a secure socket protocol * implementation which acts as a factory for secure socket - * factories or <code>SSLEngine</code>s. This class is initialized + * factories or {@code SSLEngine}s. This class is initialized * with an optional set of key and trust managers and source of * secure random bytes. * * <p> Every implementation of the Java platform is required to support the - * following standard <code>SSLContext</code> protocol: + * following standard {@code SSLContext} protocol: * <ul> * <li><tt>TLSv1</tt></li> * </ul> @@ -79,7 +79,7 @@ * <p>If a default context was set using the {@link #setDefault * SSLContext.setDefault()} method, it is returned. Otherwise, the first * call of this method triggers the call - * <code>SSLContext.getInstance("Default")</code>. + * {@code SSLContext.getInstance("Default")}. * If successful, that object is made the default SSL context and returned. * * <p>The default context is immediately @@ -106,8 +106,8 @@ * @param context the SSLContext * @throws NullPointerException if context is null * @throws SecurityException if a security manager exists and its - * <code>checkPermission</code> method does not allow - * <code>SSLPermission("setDefaultSSLContext")</code> + * {@code checkPermission} method does not allow + * {@code SSLPermission("setDefaultSSLContext")} * @since 1.6 */ public static synchronized void setDefault(SSLContext context) { @@ -122,7 +122,7 @@ } /** - * Returns a <code>SSLContext</code> object that implements the + * Returns a {@code SSLContext} object that implements the * specified secure socket protocol. * * <p> This method traverses the list of registered security Providers, @@ -141,7 +141,7 @@ * Documentation</a> * for information about standard protocol names. * - * @return the new <code>SSLContext</code> object. + * @return the new {@code SSLContext} object. * * @exception NoSuchAlgorithmException if no Provider supports a * SSLContextSpi implementation for the @@ -159,7 +159,7 @@ } /** - * Returns a <code>SSLContext</code> object that implements the + * Returns a {@code SSLContext} object that implements the * specified secure socket protocol. * * <p> A new SSLContext object encapsulating the @@ -179,7 +179,7 @@ * * @param provider the name of the provider. * - * @return the new <code>SSLContext</code> object. + * @return the new {@code SSLContext} object. * * @throws NoSuchAlgorithmException if a SSLContextSpi * implementation for the specified protocol is not @@ -202,7 +202,7 @@ } /** - * Returns a <code>SSLContext</code> object that implements the + * Returns a {@code SSLContext} object that implements the * specified secure socket protocol. * * <p> A new SSLContext object encapsulating the @@ -219,7 +219,7 @@ * * @param provider an instance of the provider. * - * @return the new <code>SSLContext</code> object. + * @return the new {@code SSLContext} object. * * @throws NoSuchAlgorithmException if a SSLContextSpi * implementation for the specified protocol is not available @@ -239,22 +239,22 @@ } /** - * Returns the protocol name of this <code>SSLContext</code> object. + * Returns the protocol name of this {@code SSLContext} object. * * <p>This is the same name that was specified in one of the - * <code>getInstance</code> calls that created this - * <code>SSLContext</code> object. + * {@code getInstance} calls that created this + * {@code SSLContext} object. * - * @return the protocol name of this <code>SSLContext</code> object. + * @return the protocol name of this {@code SSLContext} object. */ public final String getProtocol() { return this.protocol; } /** - * Returns the provider of this <code>SSLContext</code> object. + * Returns the provider of this {@code SSLContext} object. * - * @return the provider of this <code>SSLContext</code> object + * @return the provider of this {@code SSLContext} object */ public final Provider getProvider() { return this.provider; @@ -283,31 +283,35 @@ } /** - * Returns a <code>SocketFactory</code> object for this + * Returns a {@code SocketFactory} object for this * context. * - * @return the <code>SocketFactory</code> object + * @return the {@code SocketFactory} object + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>init()</code> has not been called + * initialization and the {@code init()} has not been called */ public final SSLSocketFactory getSocketFactory() { return contextSpi.engineGetSocketFactory(); } /** - * Returns a <code>ServerSocketFactory</code> object for + * Returns a {@code ServerSocketFactory} object for * this context. * - * @return the <code>ServerSocketFactory</code> object + * @return the {@code ServerSocketFactory} object + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>init()</code> has not been called + * initialization and the {@code init()} has not been called */ public final SSLServerSocketFactory getServerSocketFactory() { return contextSpi.engineGetServerSocketFactory(); } /** - * Creates a new <code>SSLEngine</code> using this context. + * Creates a new {@code SSLEngine} using this context. * <P> * Applications using this factory method are providing no hints * for an internal session reuse strategy. If hints are desired, @@ -317,11 +321,11 @@ * Some cipher suites (such as Kerberos) require remote hostname * information, in which case this factory method should not be used. * - * @return the <code>SSLEngine</code> object + * @return the {@code SSLEngine} object * @throws UnsupportedOperationException if the underlying provider * does not implement the operation. * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>init()</code> has not been called + * initialization and the {@code init()} has not been called * @since 1.5 */ public final SSLEngine createSSLEngine() { @@ -338,7 +342,7 @@ } /** - * Creates a new <code>SSLEngine</code> using this context using + * Creates a new {@code SSLEngine} using this context using * advisory peer information. * <P> * Applications using this factory method are providing hints @@ -349,11 +353,11 @@ * * @param peerHost the non-authoritative name of the host * @param peerPort the non-authoritative port - * @return the new <code>SSLEngine</code> object + * @return the new {@code SSLEngine} object * @throws UnsupportedOperationException if the underlying provider * does not implement the operation. * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>init()</code> has not been called + * initialization and the {@code init()} has not been called * @since 1.5 */ public final SSLEngine createSSLEngine(String peerHost, int peerPort) {
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLContextSpi.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,7 @@ /** * This class defines the <i>Service Provider Interface</i> (<b>SPI</b>) - * for the <code>SSLContext</code> class. + * for the {@code SSLContext} class. * * <p> All the abstract methods in this class must be implemented by each * cryptographic service provider who wishes to supply the implementation @@ -52,31 +52,35 @@ SecureRandom sr) throws KeyManagementException; /** - * Returns a <code>SocketFactory</code> object for this + * Returns a {@code SocketFactory} object for this * context. * - * @return the <code>SocketFactory</code> object + * @return the {@code SocketFactory} object + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>engineInit()</code> + * initialization and the {@code engineInit()} * has not been called * @see javax.net.ssl.SSLContext#getSocketFactory() */ protected abstract SSLSocketFactory engineGetSocketFactory(); /** - * Returns a <code>ServerSocketFactory</code> object for + * Returns a {@code ServerSocketFactory} object for * this context. * - * @return the <code>ServerSocketFactory</code> object + * @return the {@code ServerSocketFactory} object + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>engineInit()</code> + * initialization and the {@code engineInit()} * has not been called * @see javax.net.ssl.SSLContext#getServerSocketFactory() */ protected abstract SSLServerSocketFactory engineGetServerSocketFactory(); /** - * Creates a new <code>SSLEngine</code> using this context. + * Creates a new {@code SSLEngine} using this context. * <P> * Applications using this factory method are providing no hints * for an internal session reuse strategy. If hints are desired, @@ -86,9 +90,9 @@ * Some cipher suites (such as Kerberos) require remote hostname * information, in which case this factory method should not be used. * - * @return the <code>SSLEngine</code> Object + * @return the {@code SSLEngine} Object * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>engineInit()</code> + * initialization and the {@code engineInit()} * has not been called * * @see SSLContext#createSSLEngine() @@ -98,7 +102,7 @@ protected abstract SSLEngine engineCreateSSLEngine(); /** - * Creates a <code>SSLEngine</code> using this context. + * Creates a {@code SSLEngine} using this context. * <P> * Applications using this factory method are providing hints * for an internal session reuse strategy. @@ -108,9 +112,9 @@ * * @param host the non-authoritative name of the host * @param port the non-authoritative port - * @return the <code>SSLEngine</code> Object + * @return the {@code SSLEngine} Object * @throws IllegalStateException if the SSLContextImpl requires - * initialization and the <code>engineInit()</code> + * initialization and the {@code engineInit()} * has not been called * * @see SSLContext#createSSLEngine(String, int) @@ -120,19 +124,19 @@ protected abstract SSLEngine engineCreateSSLEngine(String host, int port); /** - * Returns a server <code>SSLSessionContext</code> object for + * Returns a server {@code SSLSessionContext} object for * this context. * - * @return the <code>SSLSessionContext</code> object + * @return the {@code SSLSessionContext} object * @see javax.net.ssl.SSLContext#getServerSessionContext() */ protected abstract SSLSessionContext engineGetServerSessionContext(); /** - * Returns a client <code>SSLSessionContext</code> object for + * Returns a client {@code SSLSessionContext} object for * this context. * - * @return the <code>SSLSessionContext</code> object + * @return the {@code SSLSessionContext} object * @see javax.net.ssl.SSLContext#getClientSessionContext() */ protected abstract SSLSessionContext engineGetClientSessionContext();
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngine.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,15 +37,15 @@ * <P> * The secure communications modes include: <UL> * - * <LI> <em>Integrity Protection</em>. SSL/TLS protects against + * <LI> <em>Integrity Protection</em>. SSL/TLS/DTLS protects against * modification of messages by an active wiretapper. * - * <LI> <em>Authentication</em>. In most modes, SSL/TLS provides + * <LI> <em>Authentication</em>. In most modes, SSL/TLS/DTLS provides * peer authentication. Servers are usually authenticated, and * clients may be authenticated as requested by servers. * * <LI> <em>Confidentiality (Privacy Protection)</em>. In most - * modes, SSL/TLS encrypts data being sent between client and + * modes, SSL/TLS/DTLS encrypts data being sent between client and * server. This protects the confidentiality of data, so that * passive wiretappers won't see sensitive data such as financial * information or personal information of many kinds. @@ -65,19 +65,19 @@ * handshaking has completed, you can access session attributes by * using the {@link #getSession()} method. * <P> - * The <code>SSLSocket</code> class provides much of the same security + * The {@code SSLSocket} class provides much of the same security * functionality, but all of the inbound and outbound data is * automatically transported using the underlying {@link * java.net.Socket Socket}, which by design uses a blocking model. * While this is appropriate for many applications, this model does not * provide the scalability required by large servers. * <P> - * The primary distinction of an <code>SSLEngine</code> is that it + * The primary distinction of an {@code SSLEngine} is that it * operates on inbound and outbound byte streams, independent of the * transport mechanism. It is the responsibility of the - * <code>SSLEngine</code> user to arrange for reliable I/O transport to - * the peer. By separating the SSL/TLS abstraction from the I/O - * transport mechanism, the <code>SSLEngine</code> can be used for a + * {@code SSLEngine} user to arrange for reliable I/O transport to + * the peer. By separating the SSL/TLS/DTLS abstraction from the I/O + * transport mechanism, the {@code SSLEngine} can be used for a * wide variety of I/O types, such as {@link * java.nio.channels.spi.AbstractSelectableChannel#configureBlocking(boolean) * non-blocking I/O (polling)}, {@link java.nio.channels.Selector @@ -87,7 +87,7 @@ * HREF="http://www.jcp.org/en/jsr/detail?id=203"> future asynchronous * I/O models </A>, and so on. * <P> - * At a high level, the <code>SSLEngine</code> appears thus: + * At a high level, the {@code SSLEngine} appears thus: * * <pre> * app data @@ -115,18 +115,18 @@ * mechanism. Inbound data is data which has been received from the * peer, and outbound data is destined for the peer. * <P> - * (In the context of an <code>SSLEngine</code>, the term "handshake + * (In the context of an {@code SSLEngine}, the term "handshake * data" is taken to mean any data exchanged to establish and control a - * secure connection. Handshake data includes the SSL/TLS messages + * secure connection. Handshake data includes the SSL/TLS/DTLS messages * "alert", "change_cipher_spec," and "handshake.") * <P> - * There are five distinct phases to an <code>SSLEngine</code>. + * There are five distinct phases to an {@code SSLEngine}. * * <OL> - * <li> Creation - The <code>SSLEngine</code> has been created and + * <li> Creation - The {@code SSLEngine} has been created and * initialized, but has not yet been used. During this phase, an - * application may set any <code>SSLEngine</code>-specific settings - * (enabled cipher suites, whether the <code>SSLEngine</code> should + * application may set any {@code SSLEngine}-specific settings + * (enabled cipher suites, whether the {@code SSLEngine} should * handshake in client or server mode, and so on). Once * handshaking has begun, though, any new settings (except * client/server mode, see below) will be used for @@ -139,7 +139,7 @@ * * <li> Application Data - Once the communication parameters have * been established and the handshake is complete, application data - * may flow through the <code>SSLEngine</code>. Outbound + * may flow through the {@code SSLEngine}. Outbound * application messages are encrypted and integrity protected, * and inbound messages reverse the process. * @@ -147,50 +147,50 @@ * the session at any time during the Application Data phase. New * handshaking data can be intermixed among the application data. * Before starting the rehandshake phase, the application may - * reset the SSL/TLS communication parameters such as the list of + * reset the SSL/TLS/DTLS communication parameters such as the list of * enabled ciphersuites and whether to use client authentication, * but can not change between client/server modes. As before, once - * handshaking has begun, any new <code>SSLEngine</code> + * handshaking has begun, any new {@code SSLEngine} * configuration settings will not be used until the next * handshake. * * <li> Closure - When the connection is no longer needed, the - * application should close the <code>SSLEngine</code> and should + * application should close the {@code SSLEngine} and should * send/receive any remaining messages to the peer before * closing the underlying transport mechanism. Once an engine is - * closed, it is not reusable: a new <code>SSLEngine</code> must + * closed, it is not reusable: a new {@code SSLEngine} must * be created. * </OL> - * An <code>SSLEngine</code> is created by calling {@link + * An {@code SSLEngine} is created by calling {@link * SSLContext#createSSLEngine()} from an initialized - * <code>SSLContext</code>. Any configuration + * {@code SSLContext}. Any configuration * parameters should be set before making the first call to - * <code>wrap()</code>, <code>unwrap()</code>, or - * <code>beginHandshake()</code>. These methods all trigger the + * {@code wrap()}, {@code unwrap()}, or + * {@code beginHandshake()}. These methods all trigger the * initial handshake. * <P> * Data moves through the engine by calling {@link #wrap(ByteBuffer, * ByteBuffer) wrap()} or {@link #unwrap(ByteBuffer, ByteBuffer) * unwrap()} on outbound or inbound data, respectively. Depending on - * the state of the <code>SSLEngine</code>, a <code>wrap()</code> call + * the state of the {@code SSLEngine}, a {@code wrap()} call * may consume application data from the source buffer and may produce * network data in the destination buffer. The outbound data * may contain application and/or handshake data. A call to - * <code>unwrap()</code> will examine the source buffer and may + * {@code unwrap()} will examine the source buffer and may * advance the handshake if the data is handshaking information, or * may place application data in the destination buffer if the data - * is application. The state of the underlying SSL/TLS algorithm + * is application. The state of the underlying SSL/TLS/DTLS algorithm * will determine when data is consumed and produced. * <P> - * Calls to <code>wrap()</code> and <code>unwrap()</code> return an - * <code>SSLEngineResult</code> which indicates the status of the + * Calls to {@code wrap()} and {@code unwrap()} return an + * {@code SSLEngineResult} which indicates the status of the * operation, and (optionally) how to interact with the engine to make * progress. * <P> - * The <code>SSLEngine</code> produces/consumes complete SSL/TLS + * The {@code SSLEngine} produces/consumes complete SSL/TLS/DTLS * packets only, and does not store application data internally between - * calls to <code>wrap()/unwrap()</code>. Thus input and output - * <code>ByteBuffer</code>s must be sized appropriately to hold the + * calls to {@code wrap()/unwrap()}. Thus input and output + * {@code ByteBuffer}s must be sized appropriately to hold the * maximum record that can be produced. Calls to {@link * SSLSession#getPacketBufferSize()} and {@link * SSLSession#getApplicationBufferSize()} should be used to determine @@ -200,12 +200,12 @@ * must determine (via {@link SSLEngineResult}) and correct the * problem, and then try the call again. * <P> - * For example, <code>unwrap()</code> will return a {@link + * For example, {@code unwrap()} will return a {@link * SSLEngineResult.Status#BUFFER_OVERFLOW} result if the engine * determines that there is not enough destination buffer space available. * Applications should call {@link SSLSession#getApplicationBufferSize()} * and compare that value with the space available in the destination buffer, - * enlarging the buffer if necessary. Similarly, if <code>unwrap()</code> + * enlarging the buffer if necessary. Similarly, if {@code unwrap()} * were to return a {@link SSLEngineResult.Status#BUFFER_UNDERFLOW}, the * application should call {@link SSLSession#getPacketBufferSize()} to ensure * that the source buffer has enough room to hold a record (enlarging if @@ -241,8 +241,8 @@ * }</pre> * * <P> - * Unlike <code>SSLSocket</code>, all methods of SSLEngine are - * non-blocking. <code>SSLEngine</code> implementations may + * Unlike {@code SSLSocket}, all methods of SSLEngine are + * non-blocking. {@code SSLEngine} implementations may * require the results of tasks that may take an extended period of * time to complete, or may even block. For example, a TrustManager * may need to connect to a remote certificate validation service, @@ -252,8 +252,8 @@ * seemingly blocking. * <P> * For any operation which may potentially block, the - * <code>SSLEngine</code> will create a {@link java.lang.Runnable} - * delegated task. When <code>SSLEngineResult</code> indicates that a + * {@code SSLEngine} will create a {@link java.lang.Runnable} + * delegated task. When {@code SSLEngineResult} indicates that a * delegated task result is needed, the application must call {@link * #getDelegatedTask()} to obtain an outstanding delegated task and * call its {@link java.lang.Runnable#run() run()} method (possibly using @@ -262,16 +262,16 @@ * exist, and try the original operation again. * <P> * At the end of a communication session, applications should properly - * close the SSL/TLS link. The SSL/TLS protocols have closure handshake - * messages, and these messages should be communicated to the peer - * before releasing the <code>SSLEngine</code> and closing the + * close the SSL/TLS/DTLS link. The SSL/TLS/DTLS protocols have closure + * handshake messages, and these messages should be communicated to the + * peer before releasing the {@code SSLEngine} and closing the * underlying transport mechanism. A close can be initiated by one of: * an SSLException, an inbound closure handshake message, or one of the * close methods. In all cases, closure handshake messages are - * generated by the engine, and <code>wrap()</code> should be repeatedly - * called until the resulting <code>SSLEngineResult</code>'s status + * generated by the engine, and {@code wrap()} should be repeatedly + * called until the resulting {@code SSLEngineResult}'s status * returns "CLOSED", or {@link #isOutboundDone()} returns true. All - * data obtained from the <code>wrap()</code> method should be sent to the + * data obtained from the {@code wrap()} method should be sent to the * peer. * <P> * {@link #closeOutbound()} is used to signal the engine that the @@ -279,12 +279,12 @@ * <P> * A peer will signal its intent to close by sending its own closure * handshake message. After this message has been received and - * processed by the local <code>SSLEngine</code>'s <code>unwrap()</code> + * processed by the local {@code SSLEngine}'s {@code unwrap()} * call, the application can detect the close by calling - * <code>unwrap()</code> and looking for a <code>SSLEngineResult</code> + * {@code unwrap()} and looking for a {@code SSLEngineResult} * with status "CLOSED", or if {@link #isInboundDone()} returns true. * If for some reason the peer closes the communication link without - * sending the proper SSL/TLS closure message, the application can + * sending the proper SSL/TLS/DTLS closure message, the application can * detect the end-of-stream and can signal the engine via {@link * #closeInbound()} that there will no more inbound messages to * process. Some applications might choose to require orderly shutdown @@ -315,16 +315,16 @@ * and/or non-private (unencrypted) communications will such a * cipher suite be selected. * <P> - * Each SSL/TLS connection must have one client and one server, thus + * Each SSL/TLS/DTLS connection must have one client and one server, thus * each endpoint must decide which role to assume. This choice determines * who begins the handshaking process as well as which type of messages * should be sent by each party. The method {@link * #setUseClientMode(boolean)} configures the mode. Once the initial - * handshaking has started, an <code>SSLEngine</code> can not switch + * handshaking has started, an {@code SSLEngine} can not switch * between client and server modes, even when performing renegotiations. * <P> * Applications might choose to process delegated tasks in different - * threads. When an <code>SSLEngine</code> + * threads. When an {@code SSLEngine} * is created, the current {@link java.security.AccessControlContext} * is saved. All future delegated tasks will be processed using this * context: that is, all access control decisions will be made using the @@ -336,10 +336,10 @@ * There are two concurrency issues to be aware of: * * <OL> - * <li>The <code>wrap()</code> and <code>unwrap()</code> methods + * <li>The {@code wrap()} and {@code unwrap()} methods * may execute concurrently of each other. * - * <li> The SSL/TLS protocols employ ordered packets. + * <li> The SSL/TLS/DTLS protocols employ ordered packets. * Applications must take care to ensure that generated packets * are delivered in sequence. If packets arrive * out-of-order, unexpected or fatal results may occur. @@ -354,7 +354,7 @@ * </pre> * * As a corollary, two threads must not attempt to call the same method - * (either <code>wrap()</code> or <code>unwrap()</code>) concurrently, + * (either {@code wrap()} or {@code unwrap()}) concurrently, * because there is no way to guarantee the eventual packet ordering. * </OL> * @@ -374,7 +374,7 @@ private int peerPort = -1; /** - * Constructor for an <code>SSLEngine</code> providing no hints + * Constructor for an {@code SSLEngine} providing no hints * for an internal session reuse strategy. * * @see SSLContext#createSSLEngine() @@ -384,10 +384,10 @@ } /** - * Constructor for an <code>SSLEngine</code>. + * Constructor for an {@code SSLEngine}. * <P> - * <code>SSLEngine</code> implementations may use the - * <code>peerHost</code> and <code>peerPort</code> parameters as hints + * {@code SSLEngine} implementations may use the + * {@code peerHost} and {@code peerPort} parameters as hints * for their internal session reuse strategy. * <P> * Some cipher suites (such as Kerberos) require remote hostname @@ -395,7 +395,7 @@ * constructor to use Kerberos. * <P> * The parameters are not authenticated by the - * <code>SSLEngine</code>. + * {@code SSLEngine}. * * @param peerHost the name of the peer host * @param peerPort the port number of the peer @@ -435,7 +435,7 @@ /** * Attempts to encode a buffer of plaintext application data into - * SSL/TLS network data. + * SSL/TLS/DTLS network data. * <P> * An invocation of this method behaves in exactly the same manner * as the invocation: @@ -445,20 +445,20 @@ * </pre></blockquote> * * @param src - * a <code>ByteBuffer</code> containing outbound application data + * a {@code ByteBuffer} containing outbound application data * @param dst - * a <code>ByteBuffer</code> to hold outbound network data - * @return an <code>SSLEngineResult</code> describing the result + * a {@code ByteBuffer} to hold outbound network data + * @return an {@code SSLEngineResult} describing the result * of this operation. * @throws SSLException * A problem was encountered while processing the - * data that caused the <code>SSLEngine</code> to abort. + * data that caused the {@code SSLEngine} to abort. * See the class description for more information on * engine closure. * @throws ReadOnlyBufferException - * if the <code>dst</code> buffer is read-only. + * if the {@code dst} buffer is read-only. * @throws IllegalArgumentException - * if either <code>src</code> or <code>dst</code> + * if either {@code src} or {@code dst} * is null. * @throws IllegalStateException if the client/server mode * has not yet been set. @@ -471,7 +471,7 @@ /** * Attempts to encode plaintext bytes from a sequence of data - * buffers into SSL/TLS network data. + * buffers into SSL/TLS/DTLS network data. * <P> * An invocation of this method behaves in exactly the same manner * as the invocation: @@ -481,22 +481,22 @@ * </pre></blockquote> * * @param srcs - * an array of <code>ByteBuffers</code> containing the + * an array of {@code ByteBuffers} containing the * outbound application data * @param dst - * a <code>ByteBuffer</code> to hold outbound network data - * @return an <code>SSLEngineResult</code> describing the result + * a {@code ByteBuffer} to hold outbound network data + * @return an {@code SSLEngineResult} describing the result * of this operation. * @throws SSLException * A problem was encountered while processing the - * data that caused the <code>SSLEngine</code> to abort. + * data that caused the {@code SSLEngine} to abort. * See the class description for more information on * engine closure. * @throws ReadOnlyBufferException - * if the <code>dst</code> buffer is read-only. + * if the {@code dst} buffer is read-only. * @throws IllegalArgumentException - * if either <code>srcs</code> or <code>dst</code> - * is null, or if any element in <code>srcs</code> is null. + * if either {@code srcs} or {@code dst} + * is null, or if any element in {@code srcs} is null. * @throws IllegalStateException if the client/server mode * has not yet been set. * @see #wrap(ByteBuffer [], int, int, ByteBuffer) @@ -512,7 +512,7 @@ /** * Attempts to encode plaintext bytes from a subsequence of data - * buffers into SSL/TLS network data. This <i>"gathering"</i> + * buffers into SSL/TLS/DTLS network data. This <i>"gathering"</i> * operation encodes, in a single invocation, a sequence of bytes * from one or more of a given sequence of buffers. Gathering * wraps are often useful when implementing network protocols or @@ -535,49 +535,49 @@ * it was generated. The application must properly synchronize * multiple calls to this method. * <P> - * If this <code>SSLEngine</code> has not yet started its initial + * If this {@code SSLEngine} has not yet started its initial * handshake, this method will automatically start the handshake. * <P> - * This method will attempt to produce SSL/TLS records, and will + * This method will attempt to produce SSL/TLS/DTLS records, and will * consume as much source data as possible, but will never consume * more than the sum of the bytes remaining in each buffer. Each - * <code>ByteBuffer</code>'s position is updated to reflect the + * {@code ByteBuffer}'s position is updated to reflect the * amount of data consumed or produced. The limits remain the * same. * <P> - * The underlying memory used by the <code>srcs</code> and - * <code>dst ByteBuffer</code>s must not be the same. + * The underlying memory used by the {@code srcs} and + * {@code dst ByteBuffer}s must not be the same. * <P> * See the class description for more information on engine closure. * * @param srcs - * an array of <code>ByteBuffers</code> containing the + * an array of {@code ByteBuffers} containing the * outbound application data * @param offset * The offset within the buffer array of the first buffer from * which bytes are to be retrieved; it must be non-negative - * and no larger than <code>srcs.length</code> + * and no larger than {@code srcs.length} * @param length * The maximum number of buffers to be accessed; it must be * non-negative and no larger than - * <code>srcs.length</code> - <code>offset</code> + * {@code srcs.length} - {@code offset} * @param dst - * a <code>ByteBuffer</code> to hold outbound network data - * @return an <code>SSLEngineResult</code> describing the result + * a {@code ByteBuffer} to hold outbound network data + * @return an {@code SSLEngineResult} describing the result * of this operation. * @throws SSLException * A problem was encountered while processing the - * data that caused the <code>SSLEngine</code> to abort. + * data that caused the {@code SSLEngine} to abort. * See the class description for more information on * engine closure. * @throws IndexOutOfBoundsException - * if the preconditions on the <code>offset</code> and - * <code>length</code> parameters do not hold. + * if the preconditions on the {@code offset} and + * {@code length} parameters do not hold. * @throws ReadOnlyBufferException - * if the <code>dst</code> buffer is read-only. + * if the {@code dst} buffer is read-only. * @throws IllegalArgumentException - * if either <code>srcs</code> or <code>dst</code> - * is null, or if any element in the <code>srcs</code> + * if either {@code srcs} or {@code dst} + * is null, or if any element in the {@code srcs} * subsequence specified is null. * @throws IllegalStateException if the client/server mode * has not yet been set. @@ -589,7 +589,7 @@ int length, ByteBuffer dst) throws SSLException; /** - * Attempts to decode SSL/TLS network data into a plaintext + * Attempts to decode SSL/TLS/DTLS network data into a plaintext * application data buffer. * <P> * An invocation of this method behaves in exactly the same manner @@ -600,20 +600,20 @@ * </pre></blockquote> * * @param src - * a <code>ByteBuffer</code> containing inbound network data. + * a {@code ByteBuffer} containing inbound network data. * @param dst - * a <code>ByteBuffer</code> to hold inbound application data. - * @return an <code>SSLEngineResult</code> describing the result + * a {@code ByteBuffer} to hold inbound application data. + * @return an {@code SSLEngineResult} describing the result * of this operation. * @throws SSLException * A problem was encountered while processing the - * data that caused the <code>SSLEngine</code> to abort. + * data that caused the {@code SSLEngine} to abort. * See the class description for more information on * engine closure. * @throws ReadOnlyBufferException - * if the <code>dst</code> buffer is read-only. + * if the {@code dst} buffer is read-only. * @throws IllegalArgumentException - * if either <code>src</code> or <code>dst</code> + * if either {@code src} or {@code dst} * is null. * @throws IllegalStateException if the client/server mode * has not yet been set. @@ -625,7 +625,7 @@ } /** - * Attempts to decode SSL/TLS network data into a sequence of plaintext + * Attempts to decode SSL/TLS/DTLS network data into a sequence of plaintext * application data buffers. * <P> * An invocation of this method behaves in exactly the same manner @@ -636,22 +636,22 @@ * </pre></blockquote> * * @param src - * a <code>ByteBuffer</code> containing inbound network data. + * a {@code ByteBuffer} containing inbound network data. * @param dsts - * an array of <code>ByteBuffer</code>s to hold inbound + * an array of {@code ByteBuffer}s to hold inbound * application data. - * @return an <code>SSLEngineResult</code> describing the result + * @return an {@code SSLEngineResult} describing the result * of this operation. * @throws SSLException * A problem was encountered while processing the - * data that caused the <code>SSLEngine</code> to abort. + * data that caused the {@code SSLEngine} to abort. * See the class description for more information on * engine closure. * @throws ReadOnlyBufferException - * if any of the <code>dst</code> buffers are read-only. + * if any of the {@code dst} buffers are read-only. * @throws IllegalArgumentException - * if either <code>src</code> or <code>dsts</code> - * is null, or if any element in <code>dsts</code> is null. + * if either {@code src} or {@code dsts} + * is null, or if any element in {@code dsts} is null. * @throws IllegalStateException if the client/server mode * has not yet been set. * @see #unwrap(ByteBuffer, ByteBuffer [], int, int) @@ -665,7 +665,7 @@ } /** - * Attempts to decode SSL/TLS network data into a subsequence of + * Attempts to decode SSL/TLS/DTLS network data into a subsequence of * plaintext application data buffers. This <i>"scattering"</i> * operation decodes, in a single invocation, a sequence of bytes * into one or more of a given sequence of buffers. Scattering @@ -688,55 +688,55 @@ * order it was received. The application must properly synchronize * multiple calls to this method. * <P> - * If this <code>SSLEngine</code> has not yet started its initial + * If this {@code SSLEngine} has not yet started its initial * handshake, this method will automatically start the handshake. * <P> - * This method will attempt to consume one complete SSL/TLS network + * This method will attempt to consume one complete SSL/TLS/DTLS network * packet, but will never consume more than the sum of the bytes - * remaining in the buffers. Each <code>ByteBuffer</code>'s + * remaining in the buffers. Each {@code ByteBuffer}'s * position is updated to reflect the amount of data consumed or * produced. The limits remain the same. * <P> - * The underlying memory used by the <code>src</code> and - * <code>dsts ByteBuffer</code>s must not be the same. + * The underlying memory used by the {@code src} and + * {@code dsts ByteBuffer}s must not be the same. * <P> * The inbound network buffer may be modified as a result of this * call: therefore if the network data packet is required for some * secondary purpose, the data should be duplicated before calling this * method. Note: the network data will not be useful to a second * SSLEngine, as each SSLEngine contains unique random state which - * influences the SSL/TLS messages. + * influences the SSL/TLS/DTLS messages. * <P> * See the class description for more information on engine closure. * * @param src - * a <code>ByteBuffer</code> containing inbound network data. + * a {@code ByteBuffer} containing inbound network data. * @param dsts - * an array of <code>ByteBuffer</code>s to hold inbound + * an array of {@code ByteBuffer}s to hold inbound * application data. * @param offset * The offset within the buffer array of the first buffer from * which bytes are to be transferred; it must be non-negative - * and no larger than <code>dsts.length</code>. + * and no larger than {@code dsts.length}. * @param length * The maximum number of buffers to be accessed; it must be * non-negative and no larger than - * <code>dsts.length</code> - <code>offset</code>. - * @return an <code>SSLEngineResult</code> describing the result + * {@code dsts.length} - {@code offset}. + * @return an {@code SSLEngineResult} describing the result * of this operation. * @throws SSLException * A problem was encountered while processing the - * data that caused the <code>SSLEngine</code> to abort. + * data that caused the {@code SSLEngine} to abort. * See the class description for more information on * engine closure. * @throws IndexOutOfBoundsException - * If the preconditions on the <code>offset</code> and - * <code>length</code> parameters do not hold. + * If the preconditions on the {@code offset} and + * {@code length} parameters do not hold. * @throws ReadOnlyBufferException - * if any of the <code>dst</code> buffers are read-only. + * if any of the {@code dst} buffers are read-only. * @throws IllegalArgumentException - * if either <code>src</code> or <code>dsts</code> - * is null, or if any element in the <code>dsts</code> + * if either {@code src} or {@code dsts} + * is null, or if any element in the {@code dsts} * subsequence specified is null. * @throws IllegalStateException if the client/server mode * has not yet been set. @@ -749,19 +749,19 @@ /** - * Returns a delegated <code>Runnable</code> task for - * this <code>SSLEngine</code>. + * Returns a delegated {@code Runnable} task for + * this {@code SSLEngine}. * <P> - * <code>SSLEngine</code> operations may require the results of + * {@code SSLEngine} operations may require the results of * operations that block, or may take an extended period of time to * complete. This method is used to obtain an outstanding {@link * java.lang.Runnable} operation (task). Each task must be assigned * a thread (possibly the current) to perform the {@link * java.lang.Runnable#run() run} operation. Once the - * <code>run</code> method returns, the <code>Runnable</code> object + * {@code run} method returns, the {@code Runnable} object * is no longer needed and may be discarded. * <P> - * Delegated tasks run in the <code>AccessControlContext</code> + * Delegated tasks run in the {@code AccessControlContext} * in place when this object was created. * <P> * A call to this method will return each outstanding task @@ -769,7 +769,7 @@ * <P> * Multiple delegated tasks can be run in parallel. * - * @return a delegated <code>Runnable</code> task, or null + * @return a delegated {@code Runnable} task, or null * if none are available. */ public abstract Runnable getDelegatedTask(); @@ -777,7 +777,7 @@ /** * Signals that no more inbound network data will be sent - * to this <code>SSLEngine</code>. + * to this {@code SSLEngine}. * <P> * If the application initiated the closing process by calling * {@link #closeOutbound()}, under some circumstances it is not @@ -789,9 +789,9 @@ * <P> * But if the application did not initiate the closure process, or * if the circumstances above do not apply, this method should be - * called whenever the end of the SSL/TLS data stream is reached. + * called whenever the end of the SSL/TLS/DTLS data stream is reached. * This ensures closure of the inbound side, and checks that the - * peer followed the SSL/TLS close procedure properly, thus + * peer followed the SSL/TLS/DTLS close procedure properly, thus * detecting possible truncation attacks. * <P> * This method is idempotent: if the inbound side has already @@ -801,7 +801,7 @@ * called to flush any remaining handshake data. * * @throws SSLException - * if this engine has not received the proper SSL/TLS close + * if this engine has not received the proper SSL/TLS/DTLS close * notification message from the peer. * * @see #isInboundDone() @@ -814,7 +814,7 @@ * Returns whether {@link #unwrap(ByteBuffer, ByteBuffer)} will * accept any more inbound data messages. * - * @return true if the <code>SSLEngine</code> will not + * @return true if the {@code SSLEngine} will not * consume anymore network data (and by implication, * will not produce any more application data.) * @see #closeInbound() @@ -824,7 +824,7 @@ /** * Signals that no more outbound application data will be sent - * on this <code>SSLEngine</code>. + * on this {@code SSLEngine}. * <P> * This method is idempotent: if the outbound side has already * been closed, this method does not do anything. @@ -841,12 +841,12 @@ * Returns whether {@link #wrap(ByteBuffer, ByteBuffer)} will * produce any more outbound data messages. * <P> - * Note that during the closure phase, a <code>SSLEngine</code> may + * Note that during the closure phase, a {@code SSLEngine} may * generate handshake closure data that must be sent to the peer. - * <code>wrap()</code> must be called to generate this data. When + * {@code wrap()} must be called to generate this data. When * this method returns true, no more outbound data will be created. * - * @return true if the <code>SSLEngine</code> will not produce + * @return true if the {@code SSLEngine} will not produce * any more network data * * @see #closeOutbound() @@ -890,10 +890,10 @@ /** * Sets the cipher suites enabled for use on this engine. * <P> - * Each cipher suite in the <code>suites</code> parameter must have + * Each cipher suite in the {@code suites} parameter must have * been listed by getSupportedCipherSuites(), or the method will * fail. Following a successful call to this method, only suites - * listed in the <code>suites</code> parameter are enabled for use. + * listed in the {@code suites} parameter are enabled for use. * <P> * See {@link #getEnabledCipherSuites()} for more information * on why a specific cipher suite may never be used on a engine. @@ -910,7 +910,7 @@ /** * Returns the names of the protocols which could be enabled for use - * with this <code>SSLEngine</code>. + * with this {@code SSLEngine}. * * @return an array of protocols supported */ @@ -919,7 +919,7 @@ /** * Returns the names of the protocol versions which are currently - * enabled for use with this <code>SSLEngine</code>. + * enabled for use with this {@code SSLEngine}. * * @return an array of protocols * @see #setEnabledProtocols(String []) @@ -932,7 +932,7 @@ * <P> * The protocols must have been listed by getSupportedProtocols() * as being supported. Following a successful call to this method, - * only protocols listed in the <code>protocols</code> parameter + * only protocols listed in the {@code protocols} parameter * are enabled for use. * * @param protocols Names of all the protocols to enable. @@ -945,8 +945,8 @@ /** - * Returns the <code>SSLSession</code> in use in this - * <code>SSLEngine</code>. + * Returns the {@code SSLSession} in use in this + * {@code SSLEngine}. * <P> * These can be long lived, and frequently correspond to an entire * login session for some user. The session specifies a particular @@ -961,22 +961,22 @@ * a session object which reports an invalid cipher suite of * "SSL_NULL_WITH_NULL_NULL". * - * @return the <code>SSLSession</code> for this <code>SSLEngine</code> + * @return the {@code SSLSession} for this {@code SSLEngine} * @see SSLSession */ public abstract SSLSession getSession(); /** - * Returns the {@code SSLSession} being constructed during a SSL/TLS + * Returns the {@code SSLSession} being constructed during a SSL/TLS/DTLS * handshake. * <p> - * TLS protocols may negotiate parameters that are needed when using + * TLS/DTLS protocols may negotiate parameters that are needed when using * an instance of this class, but before the {@code SSLSession} has * been completely initialized and made available via {@code getSession}. * For example, the list of valid signature algorithms may restrict * the type of certificates that can used during TrustManager - * decisions, or the maximum TLS fragment packet sizes can be + * decisions, or the maximum TLS/DTLS fragment packet sizes can be * resized to better support the network environment. * <p> * This method provides early access to the {@code SSLSession} being @@ -1012,26 +1012,26 @@ * Initiates handshaking (initial or renegotiation) on this SSLEngine. * <P> * This method is not needed for the initial handshake, as the - * <code>wrap()</code> and <code>unwrap()</code> methods will + * {@code wrap()} and {@code unwrap()} methods will * implicitly call this method if handshaking has not already begun. * <P> * Note that the peer may also request a session renegotiation with - * this <code>SSLEngine</code> by sending the appropriate + * this {@code SSLEngine} by sending the appropriate * session renegotiate handshake message. * <P> * Unlike the {@link SSLSocket#startHandshake() * SSLSocket#startHandshake()} method, this method does not block * until handshaking is completed. * <P> - * To force a complete SSL/TLS session renegotiation, the current + * To force a complete SSL/TLS/DTLS session renegotiation, the current * session should be invalidated prior to calling this method. * <P> * Some protocols may not support multiple handshakes on an existing - * engine and may throw an <code>SSLException</code>. + * engine and may throw an {@code SSLException}. * * @throws SSLException * if a problem was encountered while signaling the - * <code>SSLEngine</code> to begin a new handshake. + * {@code SSLEngine} to begin a new handshake. * See the class description for more information on * engine closure. * @throws IllegalStateException if the client/server mode @@ -1042,9 +1042,9 @@ /** - * Returns the current handshake status for this <code>SSLEngine</code>. + * Returns the current handshake status for this {@code SSLEngine}. * - * @return the current <code>SSLEngineResult.HandshakeStatus</code>. + * @return the current {@code SSLEngineResult.HandshakeStatus}. */ public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus();
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngineResult.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLEngineResult.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,13 @@ /** * An encapsulation of the result state produced by - * <code>SSLEngine</code> I/O calls. + * {@code SSLEngine} I/O calls. * - * <p> A <code>SSLEngine</code> provides a means for establishing - * secure communication sessions between two peers. <code>SSLEngine</code> + * <p> A {@code SSLEngine} provides a means for establishing + * secure communication sessions between two peers. {@code SSLEngine} * operations typically consume bytes from an input buffer and produce * bytes in an output buffer. This class provides operational result - * values describing the state of the <code>SSLEngine</code>, including + * values describing the state of the {@code SSLEngine}, including * indications of what operations are needed to finish an * ongoing handshake. Lastly, it reports the number of bytes consumed * and produced as a result of this operation. @@ -49,12 +49,12 @@ public class SSLEngineResult { /** - * An <code>SSLEngineResult</code> enum describing the overall result - * of the <code>SSLEngine</code> operation. + * An {@code SSLEngineResult} enum describing the overall result + * of the {@code SSLEngine} operation. * - * The <code>Status</code> value does not reflect the - * state of a <code>SSLEngine</code> handshake currently - * in progress. The <code>SSLEngineResult's HandshakeStatus</code> + * The {@code Status} value does not reflect the + * state of a {@code SSLEngine} handshake currently + * in progress. The {@code SSLEngineResult's HandshakeStatus} * should be consulted for that information. * * @author Brad R. Wetmore @@ -63,7 +63,7 @@ public static enum Status { /** - * The <code>SSLEngine</code> was not able to unwrap the + * The {@code SSLEngine} was not able to unwrap the * incoming data because there were not enough source bytes * available to make a complete packet. * @@ -73,7 +73,7 @@ BUFFER_UNDERFLOW, /** - * The <code>SSLEngine</code> was not able to process the + * The {@code SSLEngine} was not able to process the * operation because there are not enough bytes available in the * destination buffer to hold the result. * <P> @@ -85,22 +85,22 @@ BUFFER_OVERFLOW, /** - * The <code>SSLEngine</code> completed the operation, and + * The {@code SSLEngine} completed the operation, and * is available to process similar calls. */ OK, /** * The operation just closed this side of the - * <code>SSLEngine</code>, or the operation + * {@code SSLEngine}, or the operation * could not be completed because it was already closed. */ CLOSED; } /** - * An <code>SSLEngineResult</code> enum describing the current - * handshaking state of this <code>SSLEngine</code>. + * An {@code SSLEngineResult} enum describing the current + * handshaking state of this {@code SSLEngine}. * * @author Brad R. Wetmore * @since 1.5 @@ -108,17 +108,17 @@ public static enum HandshakeStatus { /** - * The <code>SSLEngine</code> is not currently handshaking. + * The {@code SSLEngine} is not currently handshaking. */ NOT_HANDSHAKING, /** - * The <code>SSLEngine</code> has just finished handshaking. + * The {@code SSLEngine} has just finished handshaking. * <P> * This value is only generated by a call to - * <code>SSLEngine.wrap()/unwrap()</code> when that call + * {@code SSLEngine.wrap()/unwrap()} when that call * finishes a handshake. It is never generated by - * <code>SSLEngine.getHandshakeStatus()</code>. + * {@code SSLEngine.getHandshakeStatus()}. * * @see SSLEngine#wrap(ByteBuffer, ByteBuffer) * @see SSLEngine#unwrap(ByteBuffer, ByteBuffer) @@ -127,7 +127,7 @@ FINISHED, /** - * The <code>SSLEngine</code> needs the results of one (or more) + * The {@code SSLEngine} needs the results of one (or more) * delegated tasks before handshaking can continue. * * @see SSLEngine#getDelegatedTask() @@ -135,8 +135,8 @@ NEED_TASK, /** - * The <code>SSLEngine</code> must send data to the remote side - * before handshaking can continue, so <code>SSLEngine.wrap()</code> + * The {@code SSLEngine} must send data to the remote side + * before handshaking can continue, so {@code SSLEngine.wrap()} * should be called. * * @see SSLEngine#wrap(ByteBuffer, ByteBuffer) @@ -144,10 +144,22 @@ NEED_WRAP, /** - * The <code>SSLEngine</code> needs to receive data from the + * The {@code SSLEngine} needs to receive data from the * remote side before handshaking can continue. */ - NEED_UNWRAP; + NEED_UNWRAP, + + /** + * The {@code SSLEngine} needs to unwrap before handshaking can + * can continue. + * <P> + * This value is used to indicate that not-yet-interpreted data + * has been previously received from the remote side, and does + * not need to be received again. + * + * @since 1.9 + */ + NEED_UNWRAP_AGAIN; } @@ -155,6 +167,7 @@ private final HandshakeStatus handshakeStatus; private final int bytesConsumed; private final int bytesProduced; + private final long sequenceNumber; /** * Initializes a new instance of this class. @@ -172,12 +185,44 @@ * the number of bytes placed into the destination ByteBuffer * * @throws IllegalArgumentException - * if the <code>status</code> or <code>handshakeStatus</code> - * arguments are null, or if <code>bytesConsumed</code> or - * <code>bytesProduced</code> is negative. + * if the {@code status} or {@code handshakeStatus} + * arguments are null, or if {@code bytesConsumed} or + * {@code bytesProduced} is negative. */ public SSLEngineResult(Status status, HandshakeStatus handshakeStatus, int bytesConsumed, int bytesProduced) { + this(status, handshakeStatus, bytesConsumed, bytesProduced, -1); + } + + /** + * Initializes a new instance of this class. + * + * @param status + * the return value of the operation. + * + * @param handshakeStatus + * the current handshaking status. + * + * @param bytesConsumed + * the number of bytes consumed from the source ByteBuffer + * + * @param bytesProduced + * the number of bytes placed into the destination ByteBuffer + * + * @param sequenceNumber + * the sequence number (unsigned long) of the produced or + * consumed SSL/TLS/DTLS record, or ${@code -1L} if no record + * produced or consumed + * + * @throws IllegalArgumentException + * if the {@code status} or {@code handshakeStatus} + * arguments are null, or if {@code bytesConsumed} or + * {@code bytesProduced} is negative + * + * @since 1.9 + */ + public SSLEngineResult(Status status, HandshakeStatus handshakeStatus, + int bytesConsumed, int bytesProduced, long sequenceNumber) { if ((status == null) || (handshakeStatus == null) || (bytesConsumed < 0) || (bytesProduced < 0)) { @@ -188,10 +233,11 @@ this.handshakeStatus = handshakeStatus; this.bytesConsumed = bytesConsumed; this.bytesProduced = bytesProduced; + this.sequenceNumber = sequenceNumber; } /** - * Gets the return value of this <code>SSLEngine</code> operation. + * Gets the return value of this {@code SSLEngine} operation. * * @return the return value */ @@ -200,7 +246,7 @@ } /** - * Gets the handshake status of this <code>SSLEngine</code> + * Gets the handshake status of this {@code SSLEngine} * operation. * * @return the handshake status @@ -228,6 +274,41 @@ } /** + * Returns the sequence number of the produced or consumed SSL/TLS/DTLS + * record (optional operation). + * + * @apiNote Note that sequence number is an unsigned long and cannot + * exceed {@code -1L}. It is desired to use the unsigned + * long comparing mode for comparison of unsigned long values + * (see also {@link java.lang.Long#compareUnsigned() + * Long.compareUnsigned()}). + * <P> + * For DTLS protocols, the first 16 bits of the sequence + * number is a counter value (epoch) that is incremented on + * every cipher state change. The remaining 48 bits on the + * right side of the sequence number represents the sequence + * of the record, which is maintained separately for each epoch. + * + * @implNote It is recommended that providers should never allow the + * sequence number incremented to {@code -1L}. If the sequence + * number is close to wrapping, renegotiate should be requested, + * otherwise the connection should be closed immediately. + * This should be carried on automatically by the underlying + * implementation. + * + * @return the sequence number of the produced or consumed SSL/TLS/DTLS + * record; or ${@code -1L} if no record is produced or consumed, + * or this operation is not supported by the underlying provider + * + * @see java.lang.Long#compareUnsigned(boolean, boolean) + * + * @since 1.9 + */ + final public long sequenceNumber() { + return sequenceNumber; + } + + /** * Returns a String representation of this object. */ @Override @@ -235,6 +316,8 @@ return ("Status = " + status + " HandshakeStatus = " + handshakeStatus + "\nbytesConsumed = " + bytesConsumed + - " bytesProduced = " + bytesProduced); + " bytesProduced = " + bytesProduced + + (sequenceNumber == -1 ? "" : + " sequenceNumber = " + Long.toUnsignedString(sequenceNumber))); } }
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLParameters.java Tue Jun 02 04:01:04 2015 +0000 @@ -35,22 +35,22 @@ import java.util.LinkedHashMap; /** - * Encapsulates parameters for an SSL/TLS connection. The parameters - * are the list of ciphersuites to be accepted in an SSL/TLS handshake, + * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters + * are the list of ciphersuites to be accepted in an SSL/TLS/DTLS handshake, * the list of protocols to be allowed, the endpoint identification - * algorithm during SSL/TLS handshaking, the Server Name Indication (SNI), - * the algorithm constraints and whether SSL/TLS servers should request - * or require client authentication, etc. + * algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI), + * the maximum network packet size, the algorithm constraints and whether + * SSL/TLS/DTLS servers should request or require client authentication, etc. * <p> * SSLParameters can be created via the constructors in this class. - * Objects can also be obtained using the <code>getSSLParameters()</code> + * Objects can also be obtained using the {@code getSSLParameters()} * methods in * {@link SSLSocket#getSSLParameters SSLSocket} and * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and * {@link SSLEngine#getSSLParameters SSLEngine} or the * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} - * methods in <code>SSLContext</code>. + * methods in {@code SSLContext}. * <p> * SSLParameters can be applied to a connection via the methods * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and @@ -74,14 +74,18 @@ private Map<Integer, SNIServerName> sniNames = null; private Map<Integer, SNIMatcher> sniMatchers = null; private boolean preferLocalCipherSuites; + private boolean enableRetransmissions = true; + private int maximumPacketSize = 0; /** * Constructs SSLParameters. * <p> * The values of cipherSuites, protocols, cryptographic algorithm * constraints, endpoint identification algorithm, server names and - * server name matchers are set to <code>null</code>, useCipherSuitesOrder, - * wantClientAuth and needClientAuth are set to <code>false</code>. + * server name matchers are set to {@code null}; useCipherSuitesOrder, + * wantClientAuth and needClientAuth are set to {@code false}; + * enableRetransmissions is set to {@code true}; maximum network packet + * size is set to {@code 0}. */ public SSLParameters() { // empty @@ -92,7 +96,7 @@ * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by - * <code>setCipherSuites(cipherSuites);</code>. + * {@code setCipherSuites(cipherSuites);}. * * @param cipherSuites the array of ciphersuites (or null) */ @@ -106,7 +110,7 @@ * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by - * <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>. + * {@code setCipherSuites(cipherSuites); setProtocols(protocols);}. * * @param cipherSuites the array of ciphersuites (or null) * @param protocols the array of protocols (or null) @@ -171,7 +175,7 @@ /** * Sets whether client authentication should be requested. Calling - * this method clears the <code>needClientAuth</code> flag. + * this method clears the {@code needClientAuth} flag. * * @param wantClientAuth whether client authentication should be requested */ @@ -191,7 +195,7 @@ /** * Sets whether client authentication should be required. Calling - * this method clears the <code>wantClientAuth</code> flag. + * this method clears the {@code wantClientAuth} flag. * * @param needClientAuth whether client authentication should be required */ @@ -218,9 +222,9 @@ * Sets the cryptographic algorithm constraints, which will be used * in addition to any configured by the runtime environment. * <p> - * If the <code>constraints</code> parameter is non-null, every + * If the {@code constraints} parameter is non-null, every * cryptographic algorithm, key and algorithm parameters used in the - * SSL/TLS handshake must be permitted by the constraints. + * SSL/TLS/DTLS handshake must be permitted by the constraints. * * @param constraints the algorithm constraints (or null) * @@ -249,9 +253,9 @@ /** * Sets the endpoint identification algorithm. * <p> - * If the <code>algorithm</code> parameter is non-null or non-empty, the + * If the {@code algorithm} parameter is non-null or non-empty, the * endpoint identification/verification procedures must be handled during - * SSL/TLS handshaking. This is to prevent man-in-the-middle attacks. + * SSL/TLS/DTLS handshaking. This is to prevent man-in-the-middle attacks. * * @param algorithm The standard string name of the endpoint * identification algorithm (or null). See Appendix A in the <a href= @@ -317,7 +321,7 @@ * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s * operating in client mode. * <P> - * For SSL/TLS connections, the underlying SSL/TLS provider + * For SSL/TLS/DTLS connections, the underlying SSL/TLS/DTLS provider * may specify a default value for a certain server name type. In * client mode, it is recommended that, by default, providers should * include the server name indication whenever the server can be located @@ -440,7 +444,7 @@ * * @param honorOrder whether local cipher suites order in * {@code #getCipherSuites} should be honored during - * SSL/TLS handshaking. + * SSL/TLS/DTLS handshaking. * * @see #getUseCipherSuitesOrder() * @@ -454,7 +458,7 @@ * Returns whether the local cipher suites preference should be honored. * * @return whether local cipher suites order in {@code #getCipherSuites} - * should be honored during SSL/TLS handshaking. + * should be honored during SSL/TLS/DTLS handshaking. * * @see #setUseCipherSuitesOrder(boolean) * @@ -463,5 +467,107 @@ public final boolean getUseCipherSuitesOrder() { return preferLocalCipherSuites; } + + /** + * Sets whether DTLS handshake retransmissions should be enabled. + * + * This method only applies to DTLS. + * + * @param enableRetransmissions + * {@code true} indicates that DTLS handshake retransmissions + * should be enabled; {@code false} indicates that DTLS handshake + * retransmissions should be disabled + * + * @see #getEnableRetransmissions() + * + * @since 1.9 + */ + public void setEnableRetransmissions(boolean enableRetransmissions) { + this.enableRetransmissions = enableRetransmissions; + } + + /** + * Returns whether DTLS handshake retransmissions should be enabled. + * + * This method only applies to DTLS. + * + * @return true, if DTLS handshake retransmissions should be enabled + * + * @see #setEnableRetransmissions(boolean) + * + * @since 1.9 + */ + public boolean getEnableRetransmissions() { + return enableRetransmissions; + } + + /** + * Sets the maximum expected network packet size in bytes for + * SSL/TLS/DTLS records. + * + * @apiNote It is recommended that if possible, the maximum packet size + * should not be less than 256 bytes so that small handshake + * messages, such as HelloVerifyRequests, are not fragmented. + * + * @implNote If the maximum packet size is too small to hold a minimal + * record, an implementation may attempt to generate as minimal + * records as possible. However, this may cause a generated + * packet to be larger than the maximum packet size. + * + * @param maximumPacketSize + * the maximum expected network packet size in bytes, or + * {@code 0} to use the implicit size that is automatically + * specified by the underlying implementation. + * @throws IllegalArgumentException + * if {@code maximumPacketSize} is negative. + * + * @see #getMaximumPacketSize() + * + * @since 1.9 + */ + public void setMaximumPacketSize(int maximumPacketSize) { + if (maximumPacketSize < 0) { + throw new IllegalArgumentException( + "The maximum packet size cannot be negative"); + } + + this.maximumPacketSize = maximumPacketSize; + } + + /** + * Returns the maximum expected network packet size in bytes for + * SSL/TLS/DTLS records. + * + * @apiNote The implicit size may not be a fixed value, especially + * for a DTLS protocols implementation. + * + * @implNote For SSL/TLS/DTLS connections, the underlying provider + * should calculate and specify the implicit value of the + * maximum expected network packet size if it is not + * configured explicitly. For any connection populated + * object, this method should never return {@code 0} so + * that applications can retrieve the actual implicit size + * of the underlying implementation. + * <P> + * An implementation should attempt to comply with the maximum + * packet size configuration. However, if the maximum packet + * size is too small to hold a minimal record, an implementation + * may try to generate as minimal records as possible. This + * may cause a generated packet to be larger than the maximum + * packet size. + * + * @return the maximum expected network packet size, or {@code 0} if + * use the implicit size that is automatically specified by + * the underlying implementation and this object has not been + * populated by any connection. + * + * @see #setMaximumPacketSize(int) + * + * @since 1.9 + */ + public int getMaximumPacketSize() { + return maximumPacketSize; + } + }
--- a/jdk/src/java.base/share/classes/javax/net/ssl/SSLSession.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/SSLSession.java Tue Jun 02 04:01:04 2015 +0000 @@ -35,7 +35,7 @@ * also be replaced by a different session. Sessions are created, or * rejoined, as part of the SSL handshaking protocol. Sessions may be * invalidated due to policies affecting security or resource usage, - * or by an application explicitly calling <code>invalidate</code>. + * or by an application explicitly calling {@code invalidate}. * Session management policies are typically used to tune performance. * * <P> In addition to the standard session attributes, SSL sessions expose @@ -82,8 +82,8 @@ * security manager installed, the caller may require * permission to access it or a security exception may be thrown. * In a Java environment, the security manager's - * <code>checkPermission</code> method is called with a - * <code>SSLPermission("getSSLSessionContext")</code> permission. + * {@code checkPermission} method is called with a + * {@code SSLPermission("getSSLSessionContext")} permission. * * @throws SecurityException if the calling thread does not have * permission to get SSL session context. @@ -148,14 +148,14 @@ /** * - * Binds the specified <code>value</code> object into the + * Binds the specified {@code value} object into the * session's application layer data - * with the given <code>name</code>. + * with the given {@code name}. * <P> - * Any existing binding using the same <code>name</code> is - * replaced. If the new (or existing) <code>value</code> implements the - * <code>SSLSessionBindingListener</code> interface, the object - * represented by <code>value</code> is notified appropriately. + * Any existing binding using the same {@code name} is + * replaced. If the new (or existing) {@code value} implements the + * {@code SSLSessionBindingListener} interface, the object + * represented by {@code value} is notified appropriately. * <p> * For security reasons, the same named values may not be * visible across different access control contexts. @@ -187,7 +187,7 @@ * Removes the object bound to the given name in the session's * application layer data. Does nothing if there is no object * bound to the given name. If the bound existing object - * implements the <code>SessionBindingListener</code> interface, + * implements the {@code SessionBindingListener} interface, * it is notified appropriately. * <p> * For security reasons, the same named values may not be @@ -349,7 +349,7 @@ * by this method. * <P> * This value is not authenticated and should not be relied upon. - * It is mainly used as a hint for <code>SSLSession</code> caching + * It is mainly used as a hint for {@code SSLSession} caching * strategies. * * @return the host name of the peer host, or null if no information @@ -364,7 +364,7 @@ * the client, it is the server's port number. * <P> * This value is not authenticated and should not be relied upon. - * It is mainly used as a hint for <code>SSLSession</code> caching + * It is mainly used as a hint for {@code SSLSession} caching * strategies. * * @return the port number of the peer host, or -1 if no information @@ -375,14 +375,14 @@ public int getPeerPort(); /** - * Gets the current size of the largest SSL/TLS packet that is expected - * when using this session. + * Gets the current size of the largest SSL/TLS/DTLS packet that is + * expected when using this session. * <P> - * A <code>SSLEngine</code> using this session may generate SSL/TLS + * An {@code SSLEngine} using this session may generate SSL/TLS/DTLS * packets of any size up to and including the value returned by this - * method. All <code>SSLEngine</code> network buffers should be sized + * method. All {@code SSLEngine} network buffers should be sized * at least this large to avoid insufficient space problems when - * performing <code>wrap</code> and <code>unwrap</code> calls. + * performing {@code wrap} and {@code unwrap} calls. * * @return the current maximum expected network packet size * @@ -398,7 +398,7 @@ * Gets the current size of the largest application data that is * expected when using this session. * <P> - * <code>SSLEngine</code> application data buffers must be large + * {@code SSLEngine} application data buffers must be large * enough to hold the application data from any inbound network * application data packet received. Typically, outbound * application data buffers can be of any size.
--- a/jdk/src/java.base/share/classes/javax/net/ssl/X509ExtendedTrustManager.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/javax/net/ssl/X509ExtendedTrustManager.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,16 +32,17 @@ import java.security.cert.CertificateException; /** - * Extensions to the <code>X509TrustManager</code> interface to support - * SSL/TLS connection sensitive trust management. + * Extensions to the {@code X509TrustManager} interface to support + * SSL/TLS/DTLS connection sensitive trust management. * <p> * To prevent man-in-the-middle attacks, hostname checks can be done * to verify that the hostname in an end-entity certificate matches the - * targeted hostname. TLS does not require such checks, but some protocols - * over TLS (such as HTTPS) do. In earlier versions of the JDK, the - * certificate chain checks were done at the SSL/TLS layer, and the hostname - * verification checks were done at the layer over TLS. This class allows - * for the checking to be done during a single call to this class. + * targeted hostname. TLS/DTLS does not require such checks, but some + * protocols over TLS/DTLS (such as HTTPS) do. In earlier versions of the + * JDK, the certificate chain checks were done at the SSL/TLS/DTLS layer, + * and the hostname verification checks were done at the layer over TLS/DTLS. + * This class allows for the checking to be done during a single call to + * this class. * <p> * RFC 2830 defines the server identification specification for the "LDAPS" * algorithm. RFC 2818 defines both the server identification and the @@ -62,17 +63,17 @@ * used. For instance, if RSAPublicKey is used, the authType * should be "RSA". Checking is case-sensitive. * <p> - * If the <code>socket</code> parameter is an instance of + * If the {@code socket} parameter is an instance of * {@link javax.net.ssl.SSLSocket}, and the endpoint identification - * algorithm of the <code>SSLParameters</code> is non-empty, to prevent - * man-in-the-middle attacks, the address that the <code>socket</code> + * algorithm of the {@code SSLParameters} is non-empty, to prevent + * man-in-the-middle attacks, the address that the {@code socket} * connected to should be checked against the peer's identity presented * in the end-entity X509 certificate, as specified in the endpoint * identification algorithm. * <p> - * If the <code>socket</code> parameter is an instance of + * If the {@code socket} parameter is an instance of * {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the - * <code>SSLParameters</code> is non-null, for every certificate in the + * {@code SSLParameters} is non-null, for every certificate in the * certification path, fields such as subject public key, the signature * algorithm, key usage, extended key usage, etc. need to conform to the * algorithm constraints in place on this socket. @@ -83,8 +84,8 @@ * can be null, which indicates that implementations need not check * the ssl parameters * @throws IllegalArgumentException if null or zero-length array is passed - * in for the <code>chain</code> parameter or if null or zero-length - * string is passed in for the <code>authType</code> parameter + * in for the {@code chain} parameter or if null or zero-length + * string is passed in for the {@code authType} parameter * @throws CertificateException if the certificate chain is not trusted * by this TrustManager * @@ -110,17 +111,17 @@ * used for the key exchange, and RSA when the key from the server * certificate is used. Checking is case-sensitive. * <p> - * If the <code>socket</code> parameter is an instance of + * If the {@code socket} parameter is an instance of * {@link javax.net.ssl.SSLSocket}, and the endpoint identification - * algorithm of the <code>SSLParameters</code> is non-empty, to prevent - * man-in-the-middle attacks, the address that the <code>socket</code> + * algorithm of the {@code SSLParameters} is non-empty, to prevent + * man-in-the-middle attacks, the address that the {@code socket} * connected to should be checked against the peer's identity presented * in the end-entity X509 certificate, as specified in the endpoint * identification algorithm. * <p> - * If the <code>socket</code> parameter is an instance of + * If the {@code socket} parameter is an instance of * {@link javax.net.ssl.SSLSocket}, and the algorithm constraints of the - * <code>SSLParameters</code> is non-null, for every certificate in the + * {@code SSLParameters} is non-null, for every certificate in the * certification path, fields such as subject public key, the signature * algorithm, key usage, extended key usage, etc. need to conform to the * algorithm constraints in place on this socket. @@ -131,8 +132,8 @@ * can be null, which indicates that implementations need not check * the ssl parameters * @throws IllegalArgumentException if null or zero-length array is passed - * in for the <code>chain</code> parameter or if null or zero-length - * string is passed in for the <code>authType</code> parameter + * in for the {@code chain} parameter or if null or zero-length + * string is passed in for the {@code authType} parameter * @throws CertificateException if the certificate chain is not trusted * by this TrustManager * @@ -153,15 +154,15 @@ * used. For instance, if RSAPublicKey is used, the authType * should be "RSA". Checking is case-sensitive. * <p> - * If the <code>engine</code> parameter is available, and the endpoint - * identification algorithm of the <code>SSLParameters</code> is + * If the {@code engine} parameter is available, and the endpoint + * identification algorithm of the {@code SSLParameters} is * non-empty, to prevent man-in-the-middle attacks, the address that - * the <code>engine</code> connected to should be checked against + * the {@code engine} connected to should be checked against * the peer's identity presented in the end-entity X509 certificate, * as specified in the endpoint identification algorithm. * <p> - * If the <code>engine</code> parameter is available, and the algorithm - * constraints of the <code>SSLParameters</code> is non-null, for every + * If the {@code engine} parameter is available, and the algorithm + * constraints of the {@code SSLParameters} is non-null, for every * certificate in the certification path, fields such as subject public * key, the signature algorithm, key usage, extended key usage, etc. * need to conform to the algorithm constraints in place on this engine. @@ -172,8 +173,8 @@ * can be null, which indicates that implementations need not check * the ssl parameters * @throws IllegalArgumentException if null or zero-length array is passed - * in for the <code>chain</code> parameter or if null or zero-length - * string is passed in for the <code>authType</code> parameter + * in for the {@code chain} parameter or if null or zero-length + * string is passed in for the {@code authType} parameter * @throws CertificateException if the certificate chain is not trusted * by this TrustManager * @@ -199,15 +200,15 @@ * used for the key exchange, and RSA when the key from the server * certificate is used. Checking is case-sensitive. * <p> - * If the <code>engine</code> parameter is available, and the endpoint - * identification algorithm of the <code>SSLParameters</code> is + * If the {@code engine} parameter is available, and the endpoint + * identification algorithm of the {@code SSLParameters} is * non-empty, to prevent man-in-the-middle attacks, the address that - * the <code>engine</code> connected to should be checked against + * the {@code engine} connected to should be checked against * the peer's identity presented in the end-entity X509 certificate, * as specified in the endpoint identification algorithm. * <p> - * If the <code>engine</code> parameter is available, and the algorithm - * constraints of the <code>SSLParameters</code> is non-null, for every + * If the {@code engine} parameter is available, and the algorithm + * constraints of the {@code SSLParameters} is non-null, for every * certificate in the certification path, fields such as subject public * key, the signature algorithm, key usage, extended key usage, etc. * need to conform to the algorithm constraints in place on this engine. @@ -218,8 +219,8 @@ * can be null, which indicates that implementations need not check * the ssl parameters * @throws IllegalArgumentException if null or zero-length array is passed - * in for the <code>chain</code> parameter or if null or zero-length - * string is passed in for the <code>authType</code> parameter + * in for the {@code chain} parameter or if null or zero-length + * string is passed in for the {@code authType} parameter * @throws CertificateException if the certificate chain is not trusted * by this TrustManager *
--- a/jdk/src/java.base/share/classes/sun/security/ssl/AppInputStream.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/AppInputStream.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,41 +26,54 @@ package sun.security.ssl; -import java.io.*; +import java.io.InputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import javax.net.ssl.SSLProtocolException; /** * InputStream for application data as returned by SSLSocket.getInputStream(). - * It uses an InputRecord as internal buffer that is refilled on demand - * whenever it runs out of data. * * @author David Brownell */ -class AppInputStream extends InputStream { +final class AppInputStream extends InputStream { + // the buffer size for each read of network data + private static final int READ_BUFFER_SIZE = 4096; // static dummy array we use to implement skip() - private final static byte[] SKIP_ARRAY = new byte[1024]; + private static final byte[] SKIP_ARRAY = new byte[256]; - private SSLSocketImpl c; - InputRecord r; + // the related socket of the input stream + private final SSLSocketImpl socket; + + // the temporary buffer used to read network + private ByteBuffer buffer; + + // Is application data available in the stream? + private boolean appDataIsAvailable; // One element array used to implement the single byte read() method private final byte[] oneByte = new byte[1]; AppInputStream(SSLSocketImpl conn) { - r = new InputRecord(); - c = conn; + this.buffer = ByteBuffer.allocate(READ_BUFFER_SIZE); + this.socket = conn; + this.appDataIsAvailable = false; } /** * Return the minimum number of bytes that can be read without blocking. + * * Currently not synchronized. */ @Override public int available() throws IOException { - if (c.checkEOF() || (r.isAppDataValid() == false)) { + if ((!appDataIsAvailable) || socket.checkEOF()) { return 0; } - return r.available(); + + return buffer.remaining(); } /** @@ -72,17 +85,21 @@ if (n <= 0) { // EOF return -1; } - return oneByte[0] & 0xff; + return oneByte[0] & 0xFF; } /** - * Read up to "len" bytes into this buffer, starting at "off". + * Reads up to {@code len} bytes of data from the input stream into an + * array of bytes. An attempt is made to read as many as {@code len} bytes, + * but a smaller number may be read. The number of bytes actually read + * is returned as an integer. + * * If the layer above needs more data, it asks for more, so we * are responsible only for blocking to fill at most one buffer, * and returning "-1" on non-fault EOF status. */ @Override - public synchronized int read(byte b[], int off, int len) + public synchronized int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); @@ -92,28 +109,69 @@ return 0; } - if (c.checkEOF()) { + if (socket.checkEOF()) { return -1; } + + // Read the available bytes at first. + int remains = available(); + if (remains > 0) { + int howmany = Math.min(remains, len); + buffer.get(b, off, howmany); + + return howmany; + } + + appDataIsAvailable = false; + int volume = 0; + try { /* * Read data if needed ... notice that the connection guarantees * that handshake, alert, and change cipher spec data streams are * handled as they arrive, so we never see them here. */ - while (r.available() == 0) { - c.readDataRecord(r); - if (c.checkEOF()) { + while(volume == 0) { + // Clear the buffer for a new record reading. + buffer.clear(); + + // + // grow the buffer if needed + // + + // Read the header of a record into the buffer, and return + // the packet size. + int packetLen = socket.bytesInCompletePacket(); + if (packetLen < 0) { // EOF return -1; } + + // Is this packet bigger than SSL/TLS normally allows? + if (packetLen > SSLRecord.maxLargeRecordSize) { + throw new SSLProtocolException( + "Illegal packet size: " + packetLen); + } + + if (packetLen > buffer.remaining()) { + buffer = ByteBuffer.allocate(packetLen); + } + + volume = socket.readRecord(buffer); + if (volume < 0) { // EOF + return -1; + } else if (volume > 0) { + appDataIsAvailable = true; + break; + } } - int howmany = Math.min(len, r.available()); - howmany = r.read(b, off, howmany); + int howmany = Math.min(len, volume); + buffer.get(b, off, howmany); return howmany; } catch (Exception e) { // shutdown and rethrow (wrapped) exception as appropriate - c.handleException(e); + socket.handleException(e); + // dummy for compiler return -1; } @@ -147,9 +205,8 @@ */ @Override public void close() throws IOException { - c.close(); + socket.close(); } // inherit default mark/reset behavior (throw Exceptions) from InputStream - }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/AppOutputStream.java Tue Jun 02 04:01:04 2015 +0000 @@ -23,41 +23,33 @@ * questions. */ - package sun.security.ssl; import java.io.OutputStream; import java.io.IOException; +import java.nio.ByteBuffer; /* - * Output stream for application data. This is the kind of stream - * that's handed out via SSLSocket.getOutputStream(). It's all the application - * ever sees. - * - * Once the initial handshake has completed, application data may be - * interleaved with handshake data. That is handled internally and remains - * transparent to the application. + * OutputStream for application data as returned by SSLSocket.getOutputStream(). * * @author David Brownell */ class AppOutputStream extends OutputStream { - private SSLSocketImpl c; - OutputRecord r; + private SSLSocketImpl socket; // One element array used to implement the write(byte) method private final byte[] oneByte = new byte[1]; AppOutputStream(SSLSocketImpl conn) { - r = new OutputRecord(Record.ct_application_data); - c = conn; + this.socket = conn; } /** * Write the data out, NOW. */ @Override - synchronized public void write(byte b[], int off, int len) + synchronized public void write(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); @@ -68,64 +60,15 @@ } // check if the Socket is invalid (error or closed) - c.checkWrite(); + socket.checkWrite(); - /* - * By default, we counter chosen plaintext issues on CBC mode - * ciphersuites in SSLv3/TLS1.0 by sending one byte of application - * data in the first record of every payload, and the rest in - * subsequent record(s). Note that the issues have been solved in - * TLS 1.1 or later. - * - * It is not necessary to split the very first application record of - * a freshly negotiated TLS session, as there is no previous - * application data to guess. To improve compatibility, we will not - * split such records. - * - * This avoids issues in the outbound direction. For a full fix, - * the peer must have similar protections. - */ - boolean isFirstRecordOfThePayload = true; - - // Always flush at the end of each application level record. - // This lets application synchronize read and write streams - // however they like; if we buffered here, they couldn't. + // Delegate the writing to the underlying socket. try { - do { - boolean holdRecord = false; - int howmuch; - if (isFirstRecordOfThePayload && c.needToSplitPayload()) { - howmuch = Math.min(0x01, r.availableDataBytes()); - /* - * Nagle's algorithm (TCP_NODELAY) was coming into - * play here when writing short (split) packets. - * Signal to the OutputRecord code to internally - * buffer this small packet until the next outbound - * packet (of any type) is written. - */ - if ((len != 1) && (howmuch == 1)) { - holdRecord = true; - } - } else { - howmuch = Math.min(len, r.availableDataBytes()); - } - - if (isFirstRecordOfThePayload && howmuch != 0) { - isFirstRecordOfThePayload = false; - } - - // NOTE: *must* call c.writeRecord() even for howmuch == 0 - if (howmuch > 0) { - r.write(b, off, howmuch); - off += howmuch; - len -= howmuch; - } - c.writeRecord(r, holdRecord); - c.checkWrite(); - } while (len > 0); + socket.writeRecord(b, off, len); + socket.checkWrite(); } catch (Exception e) { // shutdown and rethrow (wrapped) exception as appropriate - c.handleException(e); + socket.handleException(e); } } @@ -143,7 +86,7 @@ */ @Override public void close() throws IOException { - c.close(); + socket.close(); } // inherit no-op flush()
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Authenticator.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/Authenticator.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,19 +28,26 @@ import java.util.Arrays; /** - * This class represents an SSL/TLS message authentication token, + * This class represents an SSL/TLS/DTLS message authentication token, * which encapsulates a sequence number and ensures that attempts to * delete or reorder messages can be detected. * - * Each SSL/TLS connection state contains a sequence number, which - * is maintained separately for read and write states. The sequence - * number MUST be set to zero whenever a connection state is made the - * active state. Sequence numbers are of type uint64 and may not - * exceed 2^64-1. Sequence numbers do not wrap. If a SSL/TLS - * implementation would need to wrap a sequence number, it must - * renegotiate instead. A sequence number is incremented after each - * record: specifically, the first record transmitted under a - * particular connection state MUST use sequence number 0. + * Each connection state contains a sequence number, which is maintained + * separately for read and write states. + * + * For SSL/TLS protocols, the sequence number MUST be set to zero + * whenever a connection state is made the active state. + * + * DTLS uses an explicit sequence number, rather than an implicit one. + * Sequence numbers are maintained separately for each epoch, with + * each sequence number initially being 0 for each epoch. The sequence + * number used to compute the DTLS MAC is the 64-bit value formed by + * concatenating the epoch and the sequence number. + * + * Sequence numbers do not wrap. If an implementation would need to wrap + * a sequence number, it must renegotiate instead. A sequence number is + * incremented after each record: specifically, the first record transmitted + * under a particular connection state MUST use sequence number 0. */ class Authenticator { @@ -56,13 +63,30 @@ // sequence number + record type + protocol version + record length private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2; + // the block size of DTLS v1.0 and later: + // epoch + sequence number + record type + protocol version + record length + private static final int BLOCK_SIZE_DTLS = 2 + 6 + 1 + 2 + 2; + + private final boolean isDTLS; + /** * Default construct, no message authentication token is initialized. * * Note that this construct can only be called for null MAC */ - Authenticator() { - block = new byte[0]; + protected Authenticator(boolean isDTLS) { + if (isDTLS) { + // For DTLS protocols, plaintexts use explicit epoch and + // sequence number in each record. The first 8 byte of + // the block is initialized for null MAC so that the + // epoch and sequence number can be acquired to generate + // plaintext records. + block = new byte[8]; + } else { + block = new byte[0]; + } + + this.isDTLS = isDTLS; } /** @@ -70,12 +94,22 @@ * SSL/TLS protocol. */ Authenticator(ProtocolVersion protocolVersion) { - if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (protocolVersion.isDTLSProtocol()) { + block = new byte[BLOCK_SIZE_DTLS]; + block[9] = protocolVersion.major; + block[10] = protocolVersion.minor; + + this.isDTLS = true; + } else if (protocolVersion.v >= ProtocolVersion.TLS10.v) { block = new byte[BLOCK_SIZE_TLS]; block[9] = protocolVersion.major; block[10] = protocolVersion.minor; + + this.isDTLS = false; } else { block = new byte[BLOCK_SIZE_SSL]; + + this.isDTLS = false; } } @@ -93,11 +127,19 @@ * Conservatively, we don't allow more records to be generated * when there are only 2^8 sequence numbers left. */ - return (block.length != 0 && + if (isDTLS) { + return (block.length != 0 && + // no epoch bytes, block[0] and block[1] + block[2] == (byte)0xFF && block[3] == (byte)0xFF && + block[4] == (byte)0xFF && block[5] == (byte)0xFF && + block[6] == (byte)0xFF); + } else { + return (block.length != 0 && block[0] == (byte)0xFF && block[1] == (byte)0xFF && block[2] == (byte)0xFF && block[3] == (byte)0xFF && block[4] == (byte)0xFF && block[5] == (byte)0xFF && block[6] == (byte)0xFF); + } } /** @@ -113,14 +155,22 @@ final boolean seqNumIsHuge() { /* * Conservatively, we should ask for renegotiation when there are - * only 2^48 sequence numbers left. + * only 2^32 sequence numbers left. */ - return (block.length != 0 && - block[0] == (byte)0xFF && block[1] == (byte)0xFF); + if (isDTLS) { + return (block.length != 0 && + // no epoch bytes, block[0] and block[1] + block[2] == (byte)0xFF && block[3] == (byte)0xFF); + } else { + return (block.length != 0 && + block[0] == (byte)0xFF && block[1] == (byte)0xFF && + block[2] == (byte)0xFF && block[3] == (byte)0xFF); + } } /** - * Gets the current sequence number. + * Gets the current sequence number, including the epoch number for + * DTLS protocols. * * @return the byte array of the current sequence number */ @@ -129,33 +179,83 @@ } /** + * Sets the epoch number (only apply to DTLS protocols). + */ + final void setEpochNumber(int epoch) { + if (!isDTLS) { + throw new RuntimeException( + "Epoch numbers apply to DTLS protocols only"); + } + + block[0] = (byte)((epoch >> 8) & 0xFF); + block[1] = (byte)(epoch & 0xFF); + } + + /** + * Increase the sequence number. + */ + final void increaseSequenceNumber() { + /* + * The sequence number in the block array is a 64-bit + * number stored in big-endian format. + */ + int k = 7; + while ((k >= 0) && (++block[k] == 0)) { + k--; + } + } + + /** * Acquires the current message authentication information with the * specified record type and fragment length, and then increases the * sequence number. * * @param type the record type * @param length the fragment of the record + * @param sequence the explicit sequence number of the record + * * @return the byte array of the current message authentication information */ - final byte[] acquireAuthenticationBytes(byte type, int length) { + final byte[] acquireAuthenticationBytes( + byte type, int length, byte[] sequence) { + byte[] copy = block.clone(); + if (sequence != null) { + if (sequence.length != 8) { + throw new RuntimeException( + "Insufficient explicit sequence number bytes"); + } + + System.arraycopy(sequence, 0, copy, 0, sequence.length); + } // Otherwise, use the implicit sequence number. if (block.length != 0) { copy[8] = type; + copy[copy.length - 2] = (byte)(length >> 8); copy[copy.length - 1] = (byte)(length); - /* - * Increase the sequence number in the block array - * it is a 64-bit number stored in big-endian format - */ - int k = 7; - while ((k >= 0) && (++block[k] == 0)) { - k--; + if (sequence == null || sequence.length != 0) { + // Increase the implicit sequence number in the block array. + increaseSequenceNumber(); } } return copy; } + final static long toLong(byte[] recordEnS) { + if (recordEnS != null && recordEnS.length == 8) { + return ((recordEnS[0] & 0xFFL) << 56) | + ((recordEnS[1] & 0xFFL) << 48) | + ((recordEnS[2] & 0xFFL) << 40) | + ((recordEnS[3] & 0xFFL) << 32) | + ((recordEnS[4] & 0xFFL) << 24) | + ((recordEnS[5] & 0xFFL) << 16) | + ((recordEnS[6] & 0xFFL) << 8) | + (recordEnS[7] & 0xFFL); + } + + return -1L; + } }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java Tue Jun 02 04:01:04 2015 +0000 @@ -154,9 +154,9 @@ * NULL cipherbox. Identity operation, no encryption. */ private CipherBox() { - this.protocolVersion = ProtocolVersion.DEFAULT; + this.protocolVersion = ProtocolVersion.DEFAULT_TLS; this.cipher = null; - this.cipherType = STREAM_CIPHER; + this.cipherType = NULL_CIPHER; this.fixedIv = new byte[0]; this.key = null; this.mode = Cipher.ENCRYPT_MODE; // choose at random @@ -197,7 +197,7 @@ */ if (iv == null && bulkCipher.ivSize != 0 && mode == Cipher.DECRYPT_MODE && - protocolVersion.v >= ProtocolVersion.TLS11.v) { + protocolVersion.useTLS11PlusSpec()) { iv = getFixedMask(bulkCipher.ivSize); } @@ -491,7 +491,7 @@ newLen = removePadding( buf, offset, newLen, tagLen, blockSize, protocolVersion); - if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (protocolVersion.useTLS11PlusSpec()) { if (newLen < blockSize) { throw new BadPaddingException("invalid explicit IV"); } @@ -573,7 +573,7 @@ newLen = removePadding(bb, tagLen, blockSize, protocolVersion); // check the explicit IV of TLS v1.1 or later - if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (protocolVersion.useTLS11PlusSpec()) { if (newLen < blockSize) { throw new BadPaddingException("invalid explicit IV"); } @@ -746,7 +746,7 @@ // The padding data should be filled with the padding length value. int[] results = checkPadding(buf, offset + newLen, padLen + 1, (byte)(padLen & 0xFF)); - if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (protocolVersion.useTLS10PlusSpec()) { if (results[0] != 0) { // padding data has invalid bytes throw new BadPaddingException("Invalid TLS padding data"); } @@ -792,7 +792,7 @@ int[] results = checkPadding( bb.duplicate().position(offset + newLen), (byte)(padLen & 0xFF)); - if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (protocolVersion.useTLS10PlusSpec()) { if (results[0] != 0) { // padding data has invalid bytes throw new BadPaddingException("Invalid TLS padding data"); } @@ -873,7 +873,7 @@ // For block ciphers, the explicit IV length is of length // SecurityParameters.record_iv_length, which is equal to // the SecurityParameters.block_size. - if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (protocolVersion.useTLS11PlusSpec()) { return cipher.getBlockSize(); } break; @@ -902,7 +902,7 @@ * @return the explicit nonce size of the cipher. */ int applyExplicitNonce(Authenticator authenticator, byte contentType, - ByteBuffer bb) throws BadPaddingException { + ByteBuffer bb, byte[] sequence) throws BadPaddingException { switch (cipherType) { case BLOCK_CIPHER: // sanity check length of the ciphertext @@ -918,7 +918,7 @@ // For block ciphers, the explicit IV length is of length // SecurityParameters.record_iv_length, which is equal to // the SecurityParameters.block_size. - if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (protocolVersion.useTLS11PlusSpec()) { return cipher.getBlockSize(); } break; @@ -945,7 +945,8 @@ // update the additional authentication data byte[] aad = authenticator.acquireAuthenticationBytes( - contentType, bb.remaining() - recordIvSize - tagSize); + contentType, bb.remaining() - recordIvSize - tagSize, + sequence); cipher.updateAAD(aad); return recordIvSize; @@ -957,33 +958,6 @@ } /* - * Applies the explicit nonce/IV to this cipher. This method is used to - * decrypt an SSL/TLS input record. - * - * The returned value is the SecurityParameters.record_iv_length in - * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the - * size of explicit nonce for AEAD mode. - * - * @param authenticator the authenticator to get the additional - * authentication data - * @param contentType the content type of the input record - * @param buf the byte array to get the explicit nonce from - * @param offset the offset of the byte buffer - * @param cipheredLength the ciphered fragment length of the output - * record, it is the TLSCiphertext.length in RFC 4346/5246. - * - * @return the explicit nonce size of the cipher. - */ - int applyExplicitNonce(Authenticator authenticator, - byte contentType, byte[] buf, int offset, - int cipheredLength) throws BadPaddingException { - - ByteBuffer bb = ByteBuffer.wrap(buf, offset, cipheredLength); - - return applyExplicitNonce(authenticator, contentType, bb); - } - - /* * Creates the explicit nonce/IV to this cipher. This method is used to * encrypt an SSL/TLS output record. * @@ -1005,7 +979,7 @@ byte[] nonce = new byte[0]; switch (cipherType) { case BLOCK_CIPHER: - if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (protocolVersion.useTLS11PlusSpec()) { // For block ciphers, the explicit IV length is of length // SecurityParameters.record_iv_length, which is equal to // the SecurityParameters.block_size. @@ -1034,9 +1008,10 @@ "invalid key or spec in GCM mode", ikae); } - // update the additional authentication data + // Update the additional authentication data, using the + // implicit sequence number of the authenticator. byte[] aad = authenticator.acquireAuthenticationBytes( - contentType, fragmentLength); + contentType, fragmentLength, null); cipher.updateAAD(aad); break; } @@ -1044,6 +1019,93 @@ return nonce; } + // See also CipherSuite.calculatePacketSize(). + int calculatePacketSize(int fragmentSize, int macLen, int headerSize) { + int packetSize = fragmentSize; + if (cipher != null) { + int blockSize = cipher.getBlockSize(); + switch (cipherType) { + case BLOCK_CIPHER: + packetSize += macLen; + packetSize += 1; // 1 byte padding length field + packetSize += // use the minimal padding + (blockSize - (packetSize % blockSize)) % blockSize; + if (protocolVersion.useTLS11PlusSpec()) { + packetSize += blockSize; // explicit IV + } + + break; + case AEAD_CIPHER: + packetSize += recordIvSize; + packetSize += tagSize; + + break; + default: // NULL_CIPHER or STREAM_CIPHER + packetSize += macLen; + } + } + + return packetSize + headerSize; + } + + // See also CipherSuite.calculateFragSize(). + int calculateFragmentSize(int packetLimit, int macLen, int headerSize) { + int fragLen = packetLimit - headerSize; + if (cipher != null) { + int blockSize = cipher.getBlockSize(); + switch (cipherType) { + case BLOCK_CIPHER: + if (protocolVersion.useTLS11PlusSpec()) { + fragLen -= blockSize; // explicit IV + } + fragLen -= (fragLen % blockSize); // cannot hold a block + // No padding for a maximum fragment. + fragLen -= 1; // 1 byte padding length field: 0x00 + fragLen -= macLen; + + break; + case AEAD_CIPHER: + fragLen -= recordIvSize; + fragLen -= tagSize; + + break; + default: // NULL_CIPHER or STREAM_CIPHER + fragLen -= macLen; + } + } + + return fragLen; + } + + // Estimate the maximum fragment size of a received packet. + int estimateFragmentSize(int packetSize, int macLen, int headerSize) { + int fragLen = packetSize - headerSize; + if (cipher != null) { + int blockSize = cipher.getBlockSize(); + switch (cipherType) { + case BLOCK_CIPHER: + if (protocolVersion.useTLS11PlusSpec()) { + fragLen -= blockSize; // explicit IV + } + // No padding for a maximum fragment. + fragLen -= 1; // 1 byte padding length field: 0x00 + fragLen -= macLen; + + break; + case AEAD_CIPHER: + fragLen -= recordIvSize; + fragLen -= tagSize; + + break; + default: // NULL_CIPHER or STREAM_CIPHER + fragLen -= macLen; + } + } + + return fragLen; + } + + /* * Is this cipher available? * @@ -1100,7 +1162,7 @@ if ((fragmentLen % blockSize) == 0) { int minimal = tagLen + 1; minimal = (minimal >= blockSize) ? minimal : blockSize; - if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (protocolVersion.useTLS11PlusSpec()) { minimal += blockSize; // plus the size of the explicit IV }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Tue Jun 02 04:01:04 2015 +0000 @@ -122,9 +122,15 @@ final boolean allowed; // obsoleted since protocol version + // + // TLS version is used. If checking DTLS versions, please map to + // TLS version firstly. See ProtocolVersion.mapToTLSProtocol(). final int obsoleted; - // supported since protocol version + // supported since protocol version (TLS version is used) + // + // TLS version is used. If checking DTLS versions, please map to + // TLS version firstly. See ProtocolVersion.mapToTLSProtocol(). final int supported; /** @@ -182,6 +188,70 @@ return this != C_SCSV && isAvailable(); } + // See also CipherBox.calculatePacketSize(). + int calculatePacketSize(int fragmentSize, + ProtocolVersion protocolVersion, boolean isDTLS) { + + int packetSize = fragmentSize; + if (cipher != B_NULL) { + int blockSize = cipher.ivSize; + switch (cipher.cipherType) { + case BLOCK_CIPHER: + packetSize += macAlg.size; + packetSize += 1; // 1 byte padding length field + packetSize += // use the minimal padding + (blockSize - (packetSize % blockSize)) % blockSize; + if (protocolVersion.useTLS11PlusSpec()) { + packetSize += blockSize; // explicit IV + } + + break; + case AEAD_CIPHER: + packetSize += cipher.ivSize - cipher.fixedIvSize; // record IV + packetSize += cipher.tagSize; + + break; + default: // NULL_CIPHER or STREAM_CIPHER + packetSize += macAlg.size; + } + } + + return packetSize + + (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize); + } + + // See also CipherBox.calculateFragmentSize(). + int calculateFragSize(int packetLimit, + ProtocolVersion protocolVersion, boolean isDTLS) { + + int fragSize = packetLimit - + (isDTLS ? DTLSRecord.headerSize : SSLRecord.headerSize); + if (cipher != B_NULL) { + int blockSize = cipher.ivSize; + switch (cipher.cipherType) { + case BLOCK_CIPHER: + if (protocolVersion.useTLS11PlusSpec()) { + fragSize -= blockSize; // explicit IV + } + fragSize -= (fragSize % blockSize); // cannot hold a block + // No padding for a maximum fragment. + fragSize -= 1; // 1 byte padding length field: 0x00 + fragSize -= macAlg.size; + + break; + case AEAD_CIPHER: + fragSize -= cipher.tagSize; + fragSize -= cipher.ivSize - cipher.fixedIvSize; // record IV + + break; + default: // NULL_CIPHER or STREAM_CIPHER + fragSize -= macAlg.size; + } + } + + return fragSize; + } + /** * Compares CipherSuites based on their priority. Has the effect of * sorting CipherSuites when put in a sorted collection, which is @@ -242,7 +312,7 @@ return c; } - // for use by CipherSuiteList only + // for use by SSLContextImpl only static Collection<CipherSuite> allowedCipherSuites() { return nameMap.values(); } @@ -372,7 +442,8 @@ } static enum CipherType { - STREAM_CIPHER, // null or stream cipher + NULL_CIPHER, // null cipher + STREAM_CIPHER, // stream cipher BLOCK_CIPHER, // block cipher in CBC mode AEAD_CIPHER // AEAD cipher } @@ -387,7 +458,7 @@ static enum BulkCipher { // export strength ciphers - B_NULL("NULL", STREAM_CIPHER, 0, 0, 0, 0, true), + B_NULL("NULL", NULL_CIPHER, 0, 0, 0, 0, true), B_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true), B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false), B_DES_40(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true), @@ -568,7 +639,7 @@ iv = new IvParameterSpec(new byte[cipher.ivSize]); } temporary = cipher.newCipher( - ProtocolVersion.DEFAULT, + ProtocolVersion.DEFAULT_TLS, key, iv, secureRandom, true); b = temporary.isAvailable(); } catch (NoSuchAlgorithmException e) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/Ciphertext.java Tue Jun 02 04:01:04 2015 +0000 @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import static sun.security.ssl.HandshakeMessage.*; + +/* + * enumeration of record type + */ +final class Ciphertext { + final static Ciphertext CIPHERTEXT_NULL = new Ciphertext(); + + RecordType recordType; + long recordSN; + + HandshakeStatus handshakeStatus; // null if not used or not handshaking + + Ciphertext() { + this.recordType = null; + this.recordSN = -1L; + this.handshakeStatus = null; + } + + Ciphertext(RecordType recordType, long recordSN) { + this.recordType = recordType; + this.recordSN = recordSN; + this.handshakeStatus = null; + } + + static enum RecordType { + RECORD_CHANGE_CIPHER_SPEC ( + Record.ct_change_cipher_spec, ht_not_applicable), + RECORD_ALERT ( + Record.ct_alert, ht_not_applicable), + RECORD_HELLO_REQUEST ( + Record.ct_handshake, ht_hello_request), + RECORD_CLIENT_HELLO ( + Record.ct_handshake, ht_client_hello), + RECORD_SERVER_HELLO ( + Record.ct_handshake, ht_server_hello), + RECORD_HELLO_VERIFY_REQUEST ( + Record.ct_handshake, ht_hello_verify_request), + RECORD_NEW_SESSION_TICKET ( + Record.ct_handshake, ht_new_session_ticket), + RECORD_CERTIFICATE ( + Record.ct_handshake, ht_certificate), + RECORD_SERVER_KEY_EXCHANGE ( + Record.ct_handshake, ht_server_key_exchange), + RECORD_CERTIFICATE_REQUEST ( + Record.ct_handshake, ht_certificate_request), + RECORD_SERVER_HELLO_DONE ( + Record.ct_handshake, ht_server_hello_done), + RECORD_CERTIFICATE_VERIFY ( + Record.ct_handshake, ht_certificate_verify), + RECORD_CLIENT_KEY_EXCHANGE ( + Record.ct_handshake, ht_client_key_exchange), + RECORD_FINISHED ( + Record.ct_handshake, ht_finished), + RECORD_CERTIFICATE_URL ( + Record.ct_handshake, ht_certificate_url), + RECORD_CERTIFICATE_STATUS ( + Record.ct_handshake, ht_certificate_status), + RECORD_SUPPLIEMENTAL_DATA ( + Record.ct_handshake, ht_supplemental_data), + RECORD_APPLICATION_DATA ( + Record.ct_application_data, ht_not_applicable); + + byte contentType; + byte handshakeType; + + private RecordType(byte contentType, byte handshakeType) { + this.contentType = contentType; + this.handshakeType = handshakeType; + } + + static RecordType valueOf(byte contentType, byte handshakeType) { + if (contentType == Record.ct_change_cipher_spec) { + return RECORD_CHANGE_CIPHER_SPEC; + } else if (contentType == Record.ct_alert) { + return RECORD_ALERT; + } else if (contentType == Record.ct_application_data) { + return RECORD_APPLICATION_DATA; + } else if (handshakeType == ht_hello_request) { + return RECORD_HELLO_REQUEST; + } else if (handshakeType == ht_client_hello) { + return RECORD_CLIENT_HELLO; + } else if (handshakeType == ht_server_hello) { + return RECORD_SERVER_HELLO; + } else if (handshakeType == ht_hello_verify_request) { + return RECORD_HELLO_VERIFY_REQUEST; + } else if (handshakeType == ht_new_session_ticket) { + return RECORD_NEW_SESSION_TICKET; + } else if (handshakeType == ht_certificate) { + return RECORD_CERTIFICATE; + } else if (handshakeType == ht_server_key_exchange) { + return RECORD_SERVER_KEY_EXCHANGE; + } else if (handshakeType == ht_certificate_request) { + return RECORD_CERTIFICATE_REQUEST; + } else if (handshakeType == ht_server_hello_done) { + return RECORD_SERVER_HELLO_DONE; + } else if (handshakeType == ht_certificate_verify) { + return RECORD_CERTIFICATE_VERIFY; + } else if (handshakeType == ht_client_key_exchange) { + return RECORD_CLIENT_KEY_EXCHANGE; + } else if (handshakeType == ht_finished) { + return RECORD_FINISHED; + } else if (handshakeType == ht_certificate_url) { + return RECORD_CERTIFICATE_URL; + } else if (handshakeType == ht_certificate_status) { + return RECORD_CERTIFICATE_STATUS; + } else if (handshakeType == ht_supplemental_data) { + return RECORD_SUPPLIEMENTAL_DATA; + } + + // otherwise, invalid record type + throw new IllegalArgumentException( + "Invalid record type (ContentType:" + contentType + + ", HandshakeType:" + handshakeType + ")"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientAuthType.java Tue Jun 02 04:01:04 2015 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +/** + * Client authentication type. + */ +enum ClientAuthType { + CLIENT_AUTH_NONE, // turn off client authentication + CLIENT_AUTH_REQUESTED, // need to request client authentication + CLIENT_AUTH_REQUIRED // require client authentication +} +
--- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Tue Jun 02 04:01:04 2015 +0000 @@ -141,11 +141,20 @@ private final static boolean allowUnsafeServerCertChange = Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false); + // To switch off the max_fragment_length extension. + private final static boolean enableMFLExtension = + Debug.getBooleanProperty("jsse.enableMFLExtension", false); + private List<SNIServerName> requestedServerNames = Collections.<SNIServerName>emptyList(); + // maximum fragment length + private int requestedMFLength = -1; // -1: no fragment length limit + private boolean serverNamesAccepted = false; + private ClientHello initialClientHelloMsg = null; // DTLS only + /* * the reserved server certificate chain in previous handshaking * @@ -172,11 +181,12 @@ ProtocolList enabledProtocols, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData) { + byte[] clientVerifyData, byte[] serverVerifyData, + boolean isDTLS) { super(engine, context, enabledProtocols, true, true, activeProtocolVersion, isInitialHandshake, secureRenegotiation, - clientVerifyData, serverVerifyData); + clientVerifyData, serverVerifyData, isDTLS); } /* @@ -191,19 +201,35 @@ */ @Override void processMessage(byte type, int messageLen) throws IOException { - if (state >= type - && (type != HandshakeMessage.ht_hello_request)) { - throw new SSLProtocolException( - "Handshake message sequence violation, " + type); - } + // check the handshake state + handshakeState.check(type); switch (type) { case HandshakeMessage.ht_hello_request: - this.serverHelloRequest(new HelloRequest(input)); + HelloRequest helloRequest = new HelloRequest(input); + handshakeState.update(helloRequest, resumingSession); + this.serverHelloRequest(helloRequest); + break; + + case HandshakeMessage.ht_hello_verify_request: + if (!isDTLS) { + throw new SSLProtocolException( + "hello_verify_request is not a SSL/TLS handshake message"); + } + + HelloVerifyRequest helloVerifyRequest = + new HelloVerifyRequest(input, messageLen); + handshakeState.update(helloVerifyRequest, resumingSession); + this.helloVerifyRequest(helloVerifyRequest); break; case HandshakeMessage.ht_server_hello: - this.serverHello(new ServerHello(input, messageLen)); + ServerHello serverHello = new ServerHello(input, messageLen); + this.serverHello(serverHello); + + // This handshake state update needs the resumingSession value + // set by serverHello(). + handshakeState.update(serverHello, resumingSession); break; case HandshakeMessage.ht_certificate: @@ -213,7 +239,9 @@ "unexpected server cert chain"); // NOTREACHED } - this.serverCertificate(new CertificateMsg(input)); + CertificateMsg certificateMsg = new CertificateMsg(input); + handshakeState.update(certificateMsg, resumingSession); + this.serverCertificate(certificateMsg); serverKey = session.getPeerCertificates()[0].getPublicKey(); break; @@ -249,41 +277,52 @@ } try { - this.serverKeyExchange(new RSA_ServerKeyExchange(input)); + RSA_ServerKeyExchange rsaSrvKeyExchange = + new RSA_ServerKeyExchange(input); + handshakeState.update(rsaSrvKeyExchange, resumingSession); + this.serverKeyExchange(rsaSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_DH_ANON: try { - this.serverKeyExchange(new DH_ServerKeyExchange( - input, protocolVersion)); + DH_ServerKeyExchange dhSrvKeyExchange = + new DH_ServerKeyExchange(input, protocolVersion); + handshakeState.update(dhSrvKeyExchange, resumingSession); + this.serverKeyExchange(dhSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_DHE_DSS: case K_DHE_RSA: try { - this.serverKeyExchange(new DH_ServerKeyExchange( - input, serverKey, - clnt_random.random_bytes, svr_random.random_bytes, - messageLen, - localSupportedSignAlgs, protocolVersion)); + DH_ServerKeyExchange dhSrvKeyExchange = + new DH_ServerKeyExchange( + input, serverKey, + clnt_random.random_bytes, svr_random.random_bytes, + messageLen, + localSupportedSignAlgs, protocolVersion); + handshakeState.update(dhSrvKeyExchange, resumingSession); + this.serverKeyExchange(dhSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_ECDHE_ECDSA: case K_ECDHE_RSA: case K_ECDH_ANON: try { - this.serverKeyExchange(new ECDH_ServerKeyExchange - (input, serverKey, clnt_random.random_bytes, - svr_random.random_bytes, - localSupportedSignAlgs, protocolVersion)); + ECDH_ServerKeyExchange ecdhSrvKeyExchange = + new ECDH_ServerKeyExchange + (input, serverKey, clnt_random.random_bytes, + svr_random.random_bytes, + localSupportedSignAlgs, protocolVersion); + handshakeState.update(ecdhSrvKeyExchange, resumingSession); + this.serverKeyExchange(ecdhSrvKeyExchange); } catch (GeneralSecurityException e) { - throwSSLException("Server key", e); + throw new SSLException("Server key", e); } break; case K_RSA: @@ -320,8 +359,9 @@ if (debug != null && Debug.isOn("handshake")) { certRequest.print(System.out); } + handshakeState.update(certRequest, resumingSession); - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { Collection<SignatureAndHashAlgorithm> peerSignAlgs = certRequest.getSignAlgorithms(); if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { @@ -345,33 +385,24 @@ break; case HandshakeMessage.ht_server_hello_done: - this.serverHelloDone(new ServerHelloDone(input)); + ServerHelloDone serverHelloDone = new ServerHelloDone(input); + handshakeState.update(serverHelloDone, resumingSession); + this.serverHelloDone(serverHelloDone); + break; case HandshakeMessage.ht_finished: - // A ChangeCipherSpec record must have been received prior to - // reception of the Finished message (RFC 5246, 7.4.9). - if (!receivedChangeCipherSpec()) { - fatalSE(Alerts.alert_handshake_failure, - "Received Finished message before ChangeCipherSpec"); - } + Finished serverFinished = + new Finished(protocolVersion, input, cipherSuite); + handshakeState.update(serverFinished, resumingSession); + this.serverFinished(serverFinished); - this.serverFinished( - new Finished(protocolVersion, input, cipherSuite)); break; default: throw new SSLProtocolException( "Illegal client handshake msg, " + type); } - - // - // Move state machine forward if the message handling - // code didn't already do so - // - if (state < type) { - state = type; - } } /* @@ -389,10 +420,10 @@ // Could be (e.g. at connection setup) that we already // sent the "client hello" but the server's not seen it. // - if (state < HandshakeMessage.ht_client_hello) { + if (!clientHelloDelivered) { if (!secureRenegotiation && !allowUnsafeRenegotiation) { // renegotiation is not allowed. - if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { + if (activeProtocolVersion.useTLS10PlusSpec()) { // response with a no_renegotiation warning, warningSE(Alerts.alert_no_renegotiation); @@ -428,6 +459,29 @@ } } + private void helloVerifyRequest( + HelloVerifyRequest mesg) throws IOException { + + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + // + // Note that HelloVerifyRequest.server_version is used solely to + // indicate packet formatting, and not as part of version negotiation. + // Need not to check version values match for HelloVerifyRequest + // message. + // + initialClientHelloMsg.cookie = mesg.cookie.clone(); + + if (debug != null && Debug.isOn("handshake")) { + initialClientHelloMsg.print(System.out); + } + + // deliver the ClientHello message with cookie + initialClientHelloMsg.write(output); + handshakeState.update(initialClientHelloMsg, resumingSession); + } /* * Server chooses session parameters given options created by the @@ -441,6 +495,9 @@ * probably authentication getting done. */ private void serverHello(ServerHello mesg) throws IOException { + // Dispose the reserved ClientHello message (if exists). + initialClientHelloMsg = null; + serverKeyExchangeReceived = false; if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); @@ -536,7 +593,7 @@ } setCipherSuite(mesg.cipherSuite); - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); } @@ -611,9 +668,8 @@ } } - // looks fine; resume it, and update the state machine. + // looks fine; resume it. resumingSession = true; - state = HandshakeMessage.ht_finished - 1; calculateConnectionKeys(session.getMasterSecret()); if (debug != null && Debug.isOn("session")) { System.out.println("%% Server resumed " + session); @@ -627,6 +683,24 @@ } } + // check the "max_fragment_length" extension + MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension) + mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH); + if (maxFragLenExt != null) { + if ((requestedMFLength == -1) || + maxFragLenExt.getMaxFragLen() != requestedMFLength) { + // If the client did not request this extension, or the + // response value is different from the length it requested, + // abort the handshake with a fatal illegal_parameter alert. + fatalSE(Alerts.alert_illegal_parameter, + "Failed to negotiate the max_fragment_length"); + } + } else if (!resumingSession) { + // no "max_fragment_length" extension + requestedMFLength = -1; + } // Otherwise, using the value negotiated during the original + // session initiation + if (resumingSession && session != null) { setHandshakeSessionSE(session); // Reserve the handshake state if this is a session-resumption @@ -657,6 +731,8 @@ getLocalSupportedSignAlgs(), mesg.sessionId, getHostSE(), getPortSE()); session.setRequestedServerNames(requestedServerNames); + session.setNegotiatedMaxFragSize(requestedMFLength); + session.setMaximumPacketSize(maximumPacketSize); setHandshakeSessionSE(session); if (debug != null && Debug.isOn("handshake")) { System.out.println("** " + cipherSuite); @@ -681,7 +757,6 @@ ephemeralServerKey = mesg.getPublicKey(); } - /* * Diffie-Hellman key exchange. We save the server public key and * our own D-H algorithm object so we can defer key calculations @@ -716,13 +791,6 @@ if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } - /* - * Always make sure the input has been digested before we - * start emitting data, to ensure the hashes are correctly - * computed for the Finished and CertificateVerify messages - * which we send (here). - */ - input.digestNow(); /* * FIRST ... if requested, send an appropriate Certificate chain @@ -817,7 +885,7 @@ // server. For SSLv3, send the no_certificate alert; // TLS uses an empty cert chain instead. // - if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (protocolVersion.useTLS10PlusSpec()) { m1 = new CertificateMsg(new X509Certificate [0]); } else { warningSE(Alerts.alert_no_certificate); @@ -837,6 +905,7 @@ m1.print(System.out); } m1.write(output); + handshakeState.update(m1, resumingSession); } } @@ -1000,7 +1069,7 @@ m2.print(System.out); } m2.write(output); - + handshakeState.update(m2, resumingSession); /* * THIRD, send a "change_cipher_spec" record followed by the @@ -1010,8 +1079,6 @@ * to compute the "Finished" message, and to compute the keys used * to protect all records following the change_cipher_spec. */ - - output.doHashes(); output.flush(); /* @@ -1069,7 +1136,7 @@ CertificateVerify m3; try { SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { preferableSignatureAlgorithm = SignatureAndHashAlgorithm.getPreferableAlgorithm( peerSupportedSignAlgs, signingKey.getAlgorithm(), @@ -1103,13 +1170,17 @@ m3.print(System.out); } m3.write(output); - output.doHashes(); + handshakeState.update(m3, resumingSession); + output.flush(); } /* * OK, that's that! */ sendChangeCipherAndFinish(false); + + // expecting the final ChangeCipherSpec and Finished messages + expectingFinishFlightSE(); } @@ -1158,8 +1229,9 @@ * completed handshakes. */ if (resumingSession) { - input.digestNow(); sendChangeCipherAndFinish(true); + } else { + handshakeFinished = true; } session.setLastAccessedTime(System.currentTimeMillis()); @@ -1188,6 +1260,10 @@ */ private void sendChangeCipherAndFinish(boolean finishedTag) throws IOException { + + // Reload if this message has been reserved. + handshakeHash.reload(); + Finished mesg = new Finished(protocolVersion, handshakeHash, Finished.CLIENT, session.getMasterSecret(), cipherSuite); @@ -1205,13 +1281,6 @@ if (secureRenegotiation) { clientVerifyData = mesg.getVerifyData(); } - - /* - * Update state machine so server MUST send 'finished' next. - * (In "long" handshake case; in short case, we're responding - * to its message.) - */ - state = HandshakeMessage.ht_finished - 1; } @@ -1361,10 +1430,10 @@ // create the ClientHello message ClientHello clientHelloMessage = new ClientHello( sslContext.getSecureRandom(), maxProtocolVersion, - sessionId, cipherSuites); + sessionId, cipherSuites, isDTLS); // add signature_algorithm extension - if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) { + if (maxProtocolVersion.useTLS12PlusSpec()) { // we will always send the signature_algorithm extension Collection<SignatureAndHashAlgorithm> localSignAlgs = getLocalSupportedSignAlgs(); @@ -1389,6 +1458,37 @@ } } + // add max_fragment_length extension + if (enableMFLExtension) { + if (session != null) { + // The same extension should be sent for resumption. + requestedMFLength = session.getNegotiatedMaxFragSize(); + } else if (maximumPacketSize != 0) { + // Maybe we can calculate the fragment size more accurate + // by condering the enabled cipher suites in the future. + requestedMFLength = maximumPacketSize; + if (isDTLS) { + requestedMFLength -= DTLSRecord.maxPlaintextPlusSize; + } else { + requestedMFLength -= SSLRecord.maxPlaintextPlusSize; + } + } else { + // Need no max_fragment_length extension. + requestedMFLength = -1; + } + + if ((requestedMFLength > 0) && + MaxFragmentLengthExtension.needFragLenNego(requestedMFLength)) { + + requestedMFLength = + MaxFragmentLengthExtension.getValidMaxFragLen( + requestedMFLength); + clientHelloMessage.addMFLExtension(requestedMFLength); + } else { + requestedMFLength = -1; + } + } + // reset the client random cookie clnt_random = clientHelloMessage.clnt_random; @@ -1403,6 +1503,11 @@ clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData); } + if (isDTLS) { + // Cookie exchange need to reserve the initial ClientHello message. + initialClientHelloMsg = clientHelloMessage; + } + return clientHelloMessage; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java Tue Jun 02 04:01:04 2015 +0000 @@ -0,0 +1,1265 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.nio.*; +import java.util.*; +import javax.crypto.BadPaddingException; + +import javax.net.ssl.*; + +import sun.misc.HexDumpEncoder; +import static sun.security.ssl.HandshakeMessage.*; + +/** + * DTLS {@code InputRecord} implementation for {@code SSLEngine}. + */ +final class DTLSInputRecord extends InputRecord implements DTLSRecord { + + private DTLSReassembler reassembler = null; + + // Cache the session identifier for the detection of session-resuming + // handshake. + byte[] prevSessionID = new byte[0]; + + int readEpoch; + + int prevReadEpoch; + Authenticator prevReadAuthenticator; + CipherBox prevReadCipher; + + DTLSInputRecord() { + this.readEpoch = 0; + this.readAuthenticator = new MAC(true); + + this.prevReadEpoch = 0; + this.prevReadCipher = CipherBox.NULL; + this.prevReadAuthenticator = new MAC(true); + } + + @Override + void changeReadCiphers(Authenticator readAuthenticator, + CipherBox readCipher) { + + prevReadCipher.dispose(); + + this.prevReadAuthenticator = this.readAuthenticator; + this.prevReadCipher = this.readCipher; + this.prevReadEpoch = this.readEpoch; + + this.readAuthenticator = readAuthenticator; + this.readCipher = readCipher; + this.readEpoch++; + } + + @Override + synchronized public void close() throws IOException { + if (!isClosed) { + prevReadCipher.dispose(); + super.close(); + } + } + + @Override + boolean isEmpty() { + return ((reassembler == null) || reassembler.isEmpty()); + } + + @Override + int estimateFragmentSize(int packetSize) { + int macLen = 0; + if (readAuthenticator instanceof MAC) { + macLen = ((MAC)readAuthenticator).MAClen(); + } + + if (packetSize > 0) { + return readCipher.estimateFragmentSize( + packetSize, macLen, headerSize); + } else { + return Record.maxDataSize; + } + } + + @Override + void expectingFinishFlight() { + if (reassembler != null) { + reassembler.expectingFinishFlight(); + } + } + + @Override + Plaintext acquirePlaintext() { + if (reassembler != null) { + Plaintext plaintext = reassembler.acquirePlaintext(); + if (reassembler.finished()) { + // discard all buffered unused message. + reassembler = null; + } + + return plaintext; + } + + return null; + } + + @Override + Plaintext decode(ByteBuffer packet) { + + if (isClosed) { + return null; + } + + if (debug != null && Debug.isOn("packet")) { + Debug.printHex( + "[Raw read]: length = " + packet.remaining(), packet); + } + + // The caller should have validated the record. + int srcPos = packet.position(); + int srcLim = packet.limit(); + + byte contentType = packet.get(); // pos: 0 + byte majorVersion = packet.get(); // pos: 1 + byte minorVersion = packet.get(); // pos: 2 + byte[] recordEnS = new byte[8]; // epoch + seqence + packet.get(recordEnS); + int recordEpoch = ((recordEnS[0] & 0xFF) << 8) | + (recordEnS[1] & 0xFF); // pos: 3, 4 + long recordSeq = Authenticator.toLong(recordEnS); + int contentLen = ((packet.get() & 0xFF) << 8) | + (packet.get() & 0xFF); // pos: 11, 12 + + if (debug != null && Debug.isOn("record")) { + System.out.println(Thread.currentThread().getName() + + ", READ: " + + ProtocolVersion.valueOf(majorVersion, minorVersion) + + " " + Record.contentName(contentType) + ", length = " + + contentLen); + } + + int recLim = srcPos + DTLSRecord.headerSize + contentLen; + if (this.readEpoch > recordEpoch) { + // Discard old records delivered before this epoch. + + // Reset the position of the packet buffer. + packet.position(recLim); + return null; + } + + if (this.readEpoch < recordEpoch) { + if (contentType != Record.ct_handshake) { + // just discard it if not a handshake message + packet.position(recLim); + return null; + } + + // Not ready to decrypt this record, may be encrypted Finished + // message, need to buffer it. + if (reassembler == null) { + reassembler = new DTLSReassembler(); + } + + byte[] fragment = new byte[contentLen]; + packet.get(fragment); // copy the fragment + RecordFragment buffered = new RecordFragment(fragment, contentType, + majorVersion, minorVersion, + recordEnS, recordEpoch, recordSeq, true); + + reassembler.queueUpFragment(buffered); + + // consume the full record in the packet buffer. + packet.position(recLim); + + Plaintext plaintext = reassembler.acquirePlaintext(); + if (reassembler.finished()) { + // discard all buffered unused message. + reassembler = null; + } + + return plaintext; + } + + if (this.readEpoch == recordEpoch) { + // decrypt the fragment + packet.limit(recLim); + packet.position(srcPos + DTLSRecord.headerSize); + + ByteBuffer plaintextFragment; + try { + plaintextFragment = decrypt(readAuthenticator, + readCipher, contentType, packet, recordEnS); + } catch (BadPaddingException bpe) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + " discard invalid record: " + bpe); + } + + // invalid, discard this record [section 4.1.2.7, RFC 6347] + return null; + } finally { + // comsume a complete record + packet.limit(srcLim); + packet.position(recLim); + } + + if (contentType != Record.ct_change_cipher_spec && + contentType != Record.ct_handshake) { // app data or alert + // no retransmission + return new Plaintext(contentType, majorVersion, minorVersion, + recordEpoch, recordSeq, plaintextFragment); + } + + if (contentType == Record.ct_change_cipher_spec) { + if (reassembler == null) { + // handshake has not started, should be an + // old handshake message, discard it. + return null; + } + + reassembler.queueUpFragment( + new RecordFragment(plaintextFragment, contentType, + majorVersion, minorVersion, + recordEnS, recordEpoch, recordSeq, false)); + } else { // handshake record + // One record may contain 1+ more handshake messages. + while (plaintextFragment.remaining() > 0) { + + HandshakeFragment hsFrag = parseHandshakeMessage( + contentType, majorVersion, minorVersion, + recordEnS, recordEpoch, recordSeq, plaintextFragment); + + if (hsFrag == null) { + // invalid, discard this record + return null; + } + + if ((reassembler == null) && + isKickstart(hsFrag.handshakeType)) { + reassembler = new DTLSReassembler(); + } + + if (reassembler != null) { + reassembler.queueUpHandshake(hsFrag); + } // else, just ignore the message. + } + } + + // Completed the read of the full record. Acquire the reassembled + // messages. + if (reassembler != null) { + Plaintext plaintext = reassembler.acquirePlaintext(); + if (reassembler.finished()) { + // discard all buffered unused message. + reassembler = null; + } + + return plaintext; + } + } + + return null; // make the complier happy + } + + @Override + int bytesInCompletePacket(ByteBuffer packet) throws SSLException { + + // DTLS length field is in bytes 11/12 + if (packet.remaining() < headerSize) { + return -1; + } + + // Last sanity check that it's not a wild record + int pos = packet.position(); + + // Check the content type of the record. + byte contentType = packet.get(pos); + if (!Record.isValidContentType(contentType)) { + throw new SSLException( + "Unrecognized SSL message, plaintext connection?"); + } + + // Check the protocol version of the record. + ProtocolVersion recordVersion = + ProtocolVersion.valueOf(packet.get(pos + 1), packet.get(pos + 2)); + checkRecordVersion(recordVersion, false); + + // Get the fragment length of the record. + int fragLen = ((packet.get(pos + 11) & 0xFF) << 8) + + (packet.get(pos + 12) & 0xFF) + headerSize; + if (fragLen > Record.maxFragmentSize) { + throw new SSLException( + "Record overflow, fragment length (" + fragLen + + ") MUST not exceed " + Record.maxFragmentSize); + } + + return fragLen; + } + + @Override + void checkRecordVersion(ProtocolVersion recordVersion, + boolean allowSSL20Hello) throws SSLException { + + if (!recordVersion.maybeDTLSProtocol()) { + throw new SSLException( + "Unrecognized record version " + recordVersion + + " , plaintext connection?"); + } + } + + private static boolean isKickstart(byte handshakeType) { + return (handshakeType == HandshakeMessage.ht_client_hello) || + (handshakeType == HandshakeMessage.ht_hello_request) || + (handshakeType == HandshakeMessage.ht_hello_verify_request); + } + + private static HandshakeFragment parseHandshakeMessage( + byte contentType, byte majorVersion, byte minorVersion, + byte[] recordEnS, int recordEpoch, long recordSeq, + ByteBuffer plaintextFragment) { + + int remaining = plaintextFragment.remaining(); + if (remaining < handshakeHeaderSize) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println( + Thread.currentThread().getName() + + " discard invalid record: " + + "too small record to hold a handshake fragment"); + } + + // invalid, discard this record [section 4.1.2.7, RFC 6347] + return null; + } + + byte handshakeType = plaintextFragment.get(); // pos: 0 + int messageLength = + ((plaintextFragment.get() & 0xFF) << 16) | + ((plaintextFragment.get() & 0xFF) << 8) | + (plaintextFragment.get() & 0xFF); // pos: 1-3 + int messageSeq = + ((plaintextFragment.get() & 0xFF) << 8) | + (plaintextFragment.get() & 0xFF); // pos: 4/5 + int fragmentOffset = + ((plaintextFragment.get() & 0xFF) << 16) | + ((plaintextFragment.get() & 0xFF) << 8) | + (plaintextFragment.get() & 0xFF); // pos: 6-8 + int fragmentLength = + ((plaintextFragment.get() & 0xFF) << 16) | + ((plaintextFragment.get() & 0xFF) << 8) | + (plaintextFragment.get() & 0xFF); // pos: 9-11 + if ((remaining - handshakeHeaderSize) < fragmentLength) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println( + Thread.currentThread().getName() + + " discard invalid record: " + + "not a complete handshake fragment in the record"); + } + + // invalid, discard this record [section 4.1.2.7, RFC 6347] + return null; + } + + byte[] fragment = new byte[fragmentLength]; + plaintextFragment.get(fragment); + + return new HandshakeFragment(fragment, contentType, + majorVersion, minorVersion, + recordEnS, recordEpoch, recordSeq, + handshakeType, messageLength, + messageSeq, fragmentOffset, fragmentLength); + } + + // buffered record fragment + private static class RecordFragment implements Comparable<RecordFragment> { + boolean isCiphertext; + + byte contentType; + byte majorVersion; + byte minorVersion; + int recordEpoch; + long recordSeq; + byte[] recordEnS; + byte[] fragment; + + RecordFragment(ByteBuffer fragBuf, byte contentType, + byte majorVersion, byte minorVersion, byte[] recordEnS, + int recordEpoch, long recordSeq, boolean isCiphertext) { + this((byte[])null, contentType, majorVersion, minorVersion, + recordEnS, recordEpoch, recordSeq, isCiphertext); + + this.fragment = new byte[fragBuf.remaining()]; + fragBuf.get(this.fragment); + } + + RecordFragment(byte[] fragment, byte contentType, + byte majorVersion, byte minorVersion, byte[] recordEnS, + int recordEpoch, long recordSeq, boolean isCiphertext) { + this.isCiphertext = isCiphertext; + + this.contentType = contentType; + this.majorVersion = majorVersion; + this.minorVersion = minorVersion; + this.recordEpoch = recordEpoch; + this.recordSeq = recordSeq; + this.recordEnS = recordEnS; + this.fragment = fragment; // The caller should have cloned + // the buffer if necessary. + } + + @Override + public int compareTo(RecordFragment o) { + return Long.compareUnsigned(this.recordSeq, o.recordSeq); + } + } + + // buffered handshake message + private static final class HandshakeFragment extends RecordFragment { + + byte handshakeType; // handshake msg_type + int messageSeq; // message_seq + int messageLength; // Handshake body length + int fragmentOffset; // fragment_offset + int fragmentLength; // fragment_length + + HandshakeFragment(byte[] fragment, byte contentType, + byte majorVersion, byte minorVersion, byte[] recordEnS, + int recordEpoch, long recordSeq, + byte handshakeType, int messageLength, + int messageSeq, int fragmentOffset, int fragmentLength) { + + super(fragment, contentType, majorVersion, minorVersion, + recordEnS, recordEpoch , recordSeq, false); + + this.handshakeType = handshakeType; + this.messageSeq = messageSeq; + this.messageLength = messageLength; + this.fragmentOffset = fragmentOffset; + this.fragmentLength = fragmentLength; + } + + @Override + public int compareTo(RecordFragment o) { + if (o instanceof HandshakeFragment) { + HandshakeFragment other = (HandshakeFragment)o; + if (this.messageSeq != other.messageSeq) { + // keep the insertion order for the same message + return this.messageSeq - other.messageSeq; + } + } + + return Long.compareUnsigned(this.recordSeq, o.recordSeq); + } + } + + private static final class HoleDescriptor { + int offset; // fragment_offset + int limit; // fragment_offset + fragment_length + + HoleDescriptor(int offset, int limit) { + this.offset = offset; + this.limit = limit; + } + } + + final class DTLSReassembler { + TreeSet<RecordFragment> bufferedFragments = new TreeSet<>(); + + HashMap<Byte, List<HoleDescriptor>> holesMap = new HashMap<>(5); + + // Epoch, sequence number and handshake message sequence of the + // beginning message of a flight. + byte flightType = (byte)0xFF; + + int flightTopEpoch = 0; + long flightTopRecordSeq = -1; + int flightTopMessageSeq = 0; + + // Epoch, sequence number and handshake message sequence of the + // next message acquisition of a flight. + int nextRecordEpoch = 0; // next record epoch + long nextRecordSeq = 0; // next record sequence number + int nextMessageSeq = 0; // next handshake message number + + // Expect ChangeCipherSpec and Finished messages for the final flight. + boolean expectCCSFlight = false; + + // Ready to process this flight if received all messages of the flight. + boolean flightIsReady = false; + boolean needToCheckFlight = false; + + // Is it a session-resuming abbreviated handshake.? + boolean isAbbreviatedHandshake = false; + + // The handshke fragment with the biggest record sequence number + // in a flight, not counting the Finished message. + HandshakeFragment lastHandshakeFragment = null; + + // Is handshake (intput) finished? + boolean handshakeFinished = false; + + DTLSReassembler() { + // blank + } + + boolean finished() { + return handshakeFinished; + } + + void expectingFinishFlight() { + expectCCSFlight = true; + } + + void queueUpHandshake(HandshakeFragment hsf) { + + if ((nextRecordEpoch > hsf.recordEpoch) || + (nextRecordSeq > hsf.recordSeq) || + (nextMessageSeq > hsf.messageSeq)) { + // too old, discard this record + return; + } + + // Is it the first message of next flight? + if ((flightTopMessageSeq == hsf.messageSeq) && + (hsf.fragmentOffset == 0) && (flightTopRecordSeq == -1)) { + + flightType = hsf.handshakeType; + flightTopEpoch = hsf.recordEpoch; + flightTopRecordSeq = hsf.recordSeq; + + if (hsf.handshakeType == HandshakeMessage.ht_server_hello) { + // Is it a session-resuming handshake? + try { + isAbbreviatedHandshake = + isSessionResuming(hsf.fragment, prevSessionID); + } catch (SSLException ssle) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println( + Thread.currentThread().getName() + + " discard invalid record: " + ssle); + } + + // invalid, discard it [section 4.1.2.7, RFC 6347] + return; + } + + if (!isAbbreviatedHandshake) { + prevSessionID = getSessionID(hsf.fragment); + } + } + } + + boolean fragmented = false; + if ((hsf.fragmentOffset) != 0 || + (hsf.fragmentLength != hsf.messageLength)) { + + fragmented = true; + } + + List<HoleDescriptor> holes = holesMap.get(hsf.handshakeType); + if (holes == null) { + if (!fragmented) { + holes = Collections.emptyList(); + } else { + holes = new LinkedList<HoleDescriptor>(); + holes.add(new HoleDescriptor(0, hsf.messageLength)); + } + holesMap.put(hsf.handshakeType, holes); + } else if (holes.isEmpty()) { + // Have got the full handshake message. This record may be + // a handshake message retransmission. Discard this record. + // + // It's OK to discard retransmission as the handshake hash + // is computed as if each handshake message had been sent + // as a single fragment. + // + // Note that ClientHello messages are delivered twice in + // DTLS handshaking. + if ((hsf.handshakeType != HandshakeMessage.ht_client_hello && + hsf.handshakeType != ht_hello_verify_request) || + (nextMessageSeq != hsf.messageSeq)) { + return; + } + + if (fragmented) { + holes = new LinkedList<HoleDescriptor>(); + holes.add(new HoleDescriptor(0, hsf.messageLength)); + } + holesMap.put(hsf.handshakeType, holes); + } + + if (fragmented) { + int fragmentLimit = hsf.fragmentOffset + hsf.fragmentLength; + for (int i = 0; i < holes.size(); i++) { + + HoleDescriptor hole = holes.get(i); + if ((hole.limit <= hsf.fragmentOffset) || + (hole.offset >= fragmentLimit)) { + // Also discard overlapping handshake retransmissions. + continue; + } + + // The ranges SHOULD NOT overlap. + if (((hole.offset > hsf.fragmentOffset) && + (hole.offset < fragmentLimit)) || + ((hole.limit > hsf.fragmentOffset) && + (hole.limit < fragmentLimit))) { + + if (debug != null && Debug.isOn("ssl")) { + System.out.println( + Thread.currentThread().getName() + + " discard invalid record: " + + "handshake fragment ranges are overlapping"); + } + + // invalid, discard it [section 4.1.2.7, RFC 6347] + return; + } + + // This record interacts with this hole, fill the hole. + holes.remove(i); + // i--; + + if (hsf.fragmentOffset > hole.offset) { + holes.add(new HoleDescriptor( + hole.offset, hsf.fragmentOffset)); + // i++; + } + + if (fragmentLimit < hole.limit) { + holes.add(new HoleDescriptor( + fragmentLimit, hole.limit)); + // i++; + } + + // As no ranges overlap, no interact with other holes. + break; + } + } + + // append this fragment + bufferedFragments.add(hsf); + + if ((lastHandshakeFragment == null) || + (lastHandshakeFragment.compareTo(hsf) < 0)) { + + lastHandshakeFragment = hsf; + } + + if (flightIsReady) { + flightIsReady = false; + } + needToCheckFlight = true; + } + + // queue up change_cipher_spec or encrypted message + void queueUpFragment(RecordFragment rf) { + if ((nextRecordEpoch > rf.recordEpoch) || + (nextRecordSeq > rf.recordSeq)) { + // too old, discard this record + return; + } + + // Is it the first message of next flight? + if (expectCCSFlight && + (rf.contentType == Record.ct_change_cipher_spec)) { + + flightType = (byte)0xFE; + flightTopEpoch = rf.recordEpoch; + flightTopRecordSeq = rf.recordSeq; + } + + // append this fragment + bufferedFragments.add(rf); + + if (flightIsReady) { + flightIsReady = false; + } + needToCheckFlight = true; + } + + boolean isEmpty() { + return (bufferedFragments.isEmpty() || + (!flightIsReady && !needToCheckFlight) || + (needToCheckFlight && !flightIsReady())); + } + + Plaintext acquirePlaintext() { + if (bufferedFragments.isEmpty()) { + // reset the flight + if (flightIsReady) { + flightIsReady = false; + needToCheckFlight = false; + } + + return null; + } + + if (!flightIsReady && needToCheckFlight) { + // check the fligth status + flightIsReady = flightIsReady(); + + // set for next flight + if (flightIsReady) { + flightTopMessageSeq = lastHandshakeFragment.messageSeq + 1; + flightTopRecordSeq = -1; + } + + needToCheckFlight = false; + } + + if (!flightIsReady) { + return null; + } + + RecordFragment rFrag = bufferedFragments.first(); + if (!rFrag.isCiphertext) { + // handshake message, or ChangeCipherSpec message + return acquireHandshakeMessage(); + } else { + // a Finished message or other ciphertexts + return acquireCachedMessage(); + } + } + + private Plaintext acquireCachedMessage() { + + RecordFragment rFrag = bufferedFragments.first(); + if (readEpoch != rFrag.recordEpoch) { + if (readEpoch > rFrag.recordEpoch) { + // discard old records + bufferedFragments.remove(rFrag); // popup the fragment + } + + // reset the flight + if (flightIsReady) { + flightIsReady = false; + } + return null; + } + + bufferedFragments.remove(rFrag); // popup the fragment + + ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment); + ByteBuffer plaintextFragment = null; + try { + plaintextFragment = decrypt(readAuthenticator, readCipher, + rFrag.contentType, fragment, rFrag.recordEnS); + } catch (BadPaddingException bpe) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + " discard invalid record: " + bpe); + } + + // invalid, discard this record [section 4.1.2.7, RFC 6347] + return null; + } + + // The ciphtext handshake message can only be Finished (the + // end of this flight), ClinetHello or HelloRequest (the + // beginning of the next flight) message. Need not to check + // any ChangeCipherSpec message. + if (rFrag.contentType == Record.ct_handshake) { + HandshakeFragment finFrag = null; + while (plaintextFragment.remaining() > 0) { + HandshakeFragment hsFrag = parseHandshakeMessage( + rFrag.contentType, + rFrag.majorVersion, rFrag.minorVersion, + rFrag.recordEnS, rFrag.recordEpoch, rFrag.recordSeq, + plaintextFragment); + + if (hsFrag == null) { + // invalid, discard this record + return null; + } + + if (hsFrag.handshakeType == HandshakeMessage.ht_finished) { + finFrag = hsFrag; + + // reset for the next flight + this.flightType = (byte)0xFF; + this.flightTopEpoch = rFrag.recordEpoch; + this.flightTopMessageSeq = hsFrag.messageSeq + 1; + this.flightTopRecordSeq = -1; + } else { + // reset the flight + if (flightIsReady) { + flightIsReady = false; + } + queueUpHandshake(hsFrag); + } + } + + this.nextRecordSeq = rFrag.recordSeq + 1; + this.nextMessageSeq = 0; + + if (finFrag != null) { + this.nextRecordEpoch = finFrag.recordEpoch; + this.nextRecordSeq = finFrag.recordSeq + 1; + this.nextMessageSeq = finFrag.messageSeq + 1; + + // Finished message does not fragment. + byte[] recordFrag = new byte[finFrag.messageLength + 4]; + Plaintext plaintext = new Plaintext(finFrag.contentType, + finFrag.majorVersion, finFrag.minorVersion, + finFrag.recordEpoch, finFrag.recordSeq, + ByteBuffer.wrap(recordFrag)); + + // fill the handshake fragment of the record + recordFrag[0] = finFrag.handshakeType; + recordFrag[1] = + (byte)((finFrag.messageLength >>> 16) & 0xFF); + recordFrag[2] = + (byte)((finFrag.messageLength >>> 8) & 0xFF); + recordFrag[3] = (byte)(finFrag.messageLength & 0xFF); + + System.arraycopy(finFrag.fragment, 0, + recordFrag, 4, finFrag.fragmentLength); + + // handshake hashing + handshakeHashing(finFrag, plaintext); + + // input handshake finished + handshakeFinished = true; + + return plaintext; + } else { + return acquirePlaintext(); + } + } else { + return new Plaintext(rFrag.contentType, + rFrag.majorVersion, rFrag.minorVersion, + rFrag.recordEpoch, rFrag.recordSeq, + plaintextFragment); + } + } + + private Plaintext acquireHandshakeMessage() { + + RecordFragment rFrag = bufferedFragments.first(); + if (rFrag.contentType == Record.ct_change_cipher_spec) { + this.nextRecordEpoch = rFrag.recordEpoch + 1; + this.nextRecordSeq = 0; + // no change on next handshake message sequence number + + bufferedFragments.remove(rFrag); // popup the fragment + + // Reload if this message has been reserved for handshake hash. + handshakeHash.reload(); + + return new Plaintext(rFrag.contentType, + rFrag.majorVersion, rFrag.minorVersion, + rFrag.recordEpoch, rFrag.recordSeq, + ByteBuffer.wrap(rFrag.fragment)); + } else { // rFrag.contentType == Record.ct_handshake + HandshakeFragment hsFrag = (HandshakeFragment)rFrag; + if ((hsFrag.messageLength == hsFrag.fragmentLength) && + (hsFrag.fragmentOffset == 0)) { // no fragmentation + + bufferedFragments.remove(rFrag); // popup the fragment + + // this.nextRecordEpoch = hsFrag.recordEpoch; + this.nextRecordSeq = hsFrag.recordSeq + 1; + this.nextMessageSeq = hsFrag.messageSeq + 1; + + // Note: may try to avoid byte array copy in the future. + byte[] recordFrag = new byte[hsFrag.messageLength + 4]; + Plaintext plaintext = new Plaintext(hsFrag.contentType, + hsFrag.majorVersion, hsFrag.minorVersion, + hsFrag.recordEpoch, hsFrag.recordSeq, + ByteBuffer.wrap(recordFrag)); + + // fill the handshake fragment of the record + recordFrag[0] = hsFrag.handshakeType; + recordFrag[1] = + (byte)((hsFrag.messageLength >>> 16) & 0xFF); + recordFrag[2] = + (byte)((hsFrag.messageLength >>> 8) & 0xFF); + recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF); + + System.arraycopy(hsFrag.fragment, 0, + recordFrag, 4, hsFrag.fragmentLength); + + // handshake hashing + handshakeHashing(hsFrag, plaintext); + + return plaintext; + } else { // fragmented handshake message + // the first record + // + // Note: may try to avoid byte array copy in the future. + byte[] recordFrag = new byte[hsFrag.messageLength + 4]; + Plaintext plaintext = new Plaintext(hsFrag.contentType, + hsFrag.majorVersion, hsFrag.minorVersion, + hsFrag.recordEpoch, hsFrag.recordSeq, + ByteBuffer.wrap(recordFrag)); + + // fill the handshake fragment of the record + recordFrag[0] = hsFrag.handshakeType; + recordFrag[1] = + (byte)((hsFrag.messageLength >>> 16) & 0xFF); + recordFrag[2] = + (byte)((hsFrag.messageLength >>> 8) & 0xFF); + recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF); + + int msgSeq = hsFrag.messageSeq; + long maxRecodeSN = hsFrag.recordSeq; + HandshakeFragment hmFrag = hsFrag; + do { + System.arraycopy(hmFrag.fragment, 0, + recordFrag, hmFrag.fragmentOffset + 4, + hmFrag.fragmentLength); + // popup the fragment + bufferedFragments.remove(rFrag); + + if (maxRecodeSN < hmFrag.recordSeq) { + maxRecodeSN = hmFrag.recordSeq; + } + + // Note: may buffer retransmitted fragments in order to + // speed up the reassembly in the future. + + // read the next buffered record + if (!bufferedFragments.isEmpty()) { + rFrag = bufferedFragments.first(); + if (rFrag.contentType != Record.ct_handshake) { + break; + } else { + hmFrag = (HandshakeFragment)rFrag; + } + } + } while (!bufferedFragments.isEmpty() && + (msgSeq == hmFrag.messageSeq)); + + // handshake hashing + handshakeHashing(hsFrag, plaintext); + + this.nextRecordSeq = maxRecodeSN + 1; + this.nextMessageSeq = msgSeq + 1; + + return plaintext; + } + } + } + + boolean flightIsReady() { + + // + // the ChangeCipherSpec/Finished flight + // + if (expectCCSFlight) { + // Have the ChangeCipherSpec/Finished messages been received? + return hasFinisedMessage(bufferedFragments); + } + + if (flightType == (byte)0xFF) { + return false; + } + + if ((flightType == HandshakeMessage.ht_client_hello) || + (flightType == HandshakeMessage.ht_hello_request) || + (flightType == HandshakeMessage.ht_hello_verify_request)) { + + // single handshake message flight + return hasCompleted(holesMap.get(flightType)); + } + + // + // the ServerHello flight + // + if (flightType == HandshakeMessage.ht_server_hello) { + // Firstly, check the first flight handshake message. + if (!hasCompleted(holesMap.get(flightType))) { + return false; + } + + // + // an abbreviated handshake + // + if (isAbbreviatedHandshake) { + // Ready to use the flight if received the + // ChangeCipherSpec and Finished messages. + return hasFinisedMessage(bufferedFragments); + } + + // + // a full handshake + // + if (lastHandshakeFragment.handshakeType != + HandshakeMessage.ht_server_hello_done) { + // Not yet got the final message of the flight. + return false; + } + + // Have all handshake message been received? + return hasCompleted(bufferedFragments, + flightTopMessageSeq, lastHandshakeFragment.messageSeq); + } + + // + // the ClientKeyExchange flight + // + // Note: need to consider more messages in this flight if + // ht_supplemental_data and ht_certificate_url are + // suppported in the future. + // + if ((flightType == HandshakeMessage.ht_certificate) || + (flightType == HandshakeMessage.ht_client_key_exchange)) { + + // Firstly, check the first flight handshake message. + if (!hasCompleted(holesMap.get(flightType))) { + return false; + } + + if (!hasFinisedMessage(bufferedFragments)) { + // not yet got the ChangeCipherSpec/Finished messages + return false; + } + + if (flightType == HandshakeMessage.ht_client_key_exchange) { + // single handshake message flight + return true; + } + + // + // flightType == HandshakeMessage.ht_certificate + // + // We don't support certificates containing fixed + // Diffie-Hellman parameters. Therefore, CertificateVerify + // message is required if client Certificate message presents. + // + if (lastHandshakeFragment.handshakeType != + HandshakeMessage.ht_certificate_verify) { + // Not yet got the final message of the flight. + return false; + } + + // Have all handshake message been received? + return hasCompleted(bufferedFragments, + flightTopMessageSeq, lastHandshakeFragment.messageSeq); + } + + // + // Otherwise, need to receive more handshake messages. + // + return false; + } + + private boolean isSessionResuming( + byte[] fragment, byte[] prevSid) throws SSLException { + + // As the first fragment of ServerHello should be big enough + // to hold the session_id field, need not to worry about the + // fragmentation here. + if ((fragment == null) || (fragment.length < 38)) { + // 38: the minimal ServerHello body length + throw new SSLException( + "Invalid ServerHello message: no sufficient data"); + } + + int sidLen = fragment[34]; // 34: the length field + if (sidLen > 32) { // opaque SessionID<0..32> + throw new SSLException( + "Invalid ServerHello message: invalid session id"); + } + + if (fragment.length < 38 + sidLen) { + throw new SSLException( + "Invalid ServerHello message: no sufficient data"); + } + + if (sidLen != 0 && (prevSid.length == sidLen)) { + // may be a session-resuming handshake + for (int i = 0; i < sidLen; i++) { + if (prevSid[i] != fragment[35 + i]) { + // 35: the session identifier + return false; + } + } + + return true; + } + + return false; + } + + private byte[] getSessionID(byte[] fragment) { + // The validity has been checked in the call to isSessionResuming(). + int sidLen = fragment[34]; // 34: the sessionID length field + + byte[] temporary = new byte[sidLen]; + System.arraycopy(fragment, 35, temporary, 0, sidLen); + + return temporary; + } + + // Looking for the ChangeCipherSpec and Finished messages. + // + // As the cached Finished message should be a ciphertext, we don't + // exactly know a ciphertext is a Finished message or not. According + // to the spec of TLS/DTLS handshaking, a Finished message is always + // sent immediately after a ChangeCipherSpec message. The first + // ciphertext handshake message should be the expected Finished message. + private boolean hasFinisedMessage( + Set<RecordFragment> fragments) { + + boolean hasCCS = false; + boolean hasFin = false; + for (RecordFragment fragment : fragments) { + if (fragment.contentType == Record.ct_change_cipher_spec) { + if (hasFin) { + return true; + } + hasCCS = true; + } else if (fragment.contentType == Record.ct_handshake) { + // Finished is the first expected message of a new epoch. + if (fragment.isCiphertext) { + if (hasCCS) { + return true; + } + hasFin = true; + } + } + } + + return hasFin && hasCCS; + } + + private boolean hasCompleted(List<HoleDescriptor> holes) { + if (holes == null) { + // not yet received this kind of handshake message + return false; + } + + return holes.isEmpty(); // no fragment hole for complete message + } + + private boolean hasCompleted( + Set<RecordFragment> fragments, + int presentMsgSeq, int endMsgSeq) { + + // The caller should have checked the completion of the first + // present handshake message. Need not to check it again. + for (RecordFragment rFrag : fragments) { + if ((rFrag.contentType != Record.ct_handshake) || + rFrag.isCiphertext) { + break; + } + + HandshakeFragment hsFrag = (HandshakeFragment)rFrag; + if (hsFrag.messageSeq == presentMsgSeq) { + continue; + } else if (hsFrag.messageSeq == (presentMsgSeq + 1)) { + // check the completion of the handshake message + if (!hasCompleted(holesMap.get(hsFrag.handshakeType))) { + return false; + } + + presentMsgSeq = hsFrag.messageSeq; + } else { + // not yet got handshake message next to presentMsgSeq + break; + } + } + + return (presentMsgSeq >= endMsgSeq); + // false: if not yet got all messages of the flight. + } + + private void handshakeHashing( + HandshakeFragment hsFrag, Plaintext plaintext) { + + byte hsType = hsFrag.handshakeType; + if ((hsType == HandshakeMessage.ht_hello_request) || + (hsType == HandshakeMessage.ht_hello_verify_request)) { + + // omitted from handshake hash computation + return; + } + + if ((hsFrag.messageSeq == 0) && + (hsType == HandshakeMessage.ht_client_hello)) { + + // omit initial ClientHello message + // + // 4: handshake header + // 2: ClientHello.client_version + // 32: ClientHello.random + int sidLen = plaintext.fragment.get(38); + + if (sidLen == 0) { // empty session_id, initial handshake + return; + } + } + + // calculate the DTLS header + byte[] temporary = new byte[12]; // 12: handshake header size + + // Handshake.msg_type + temporary[0] = hsFrag.handshakeType; + + // Handshake.length + temporary[1] = (byte)((hsFrag.messageLength >> 16) & 0xFF); + temporary[2] = (byte)((hsFrag.messageLength >> 8) & 0xFF); + temporary[3] = (byte)(hsFrag.messageLength & 0xFF); + + // Handshake.message_seq + temporary[4] = (byte)((hsFrag.messageSeq >> 8) & 0xFF); + temporary[5] = (byte)(hsFrag.messageSeq & 0xFF); + + // Handshake.fragment_offset + temporary[6] = 0; + temporary[7] = 0; + temporary[8] = 0; + + // Handshake.fragment_length + temporary[9] = temporary[1]; + temporary[10] = temporary[2]; + temporary[11] = temporary[3]; + + plaintext.fragment.position(4); // ignore the TLS header + if ((hsType != HandshakeMessage.ht_finished) && + (hsType != HandshakeMessage.ht_certificate_verify)) { + + if (handshakeHash == null) { + // used for cache only + handshakeHash = new HandshakeHash(false); + } + handshakeHash.update(temporary, 0, 12); + handshakeHash.update(plaintext.fragment); + } else { + // Reserve until this handshake message has been processed. + if (handshakeHash == null) { + // used for cache only + handshakeHash = new HandshakeHash(false); + } + handshakeHash.reserve(temporary, 0, 12); + handshakeHash.reserve(plaintext.fragment); + } + plaintext.fragment.position(0); // restore the position + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java Tue Jun 02 04:01:04 2015 +0000 @@ -0,0 +1,597 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.nio.*; +import java.util.*; + +import javax.crypto.BadPaddingException; + +import javax.net.ssl.*; + +import sun.misc.HexDumpEncoder; +import static sun.security.ssl.Ciphertext.RecordType; + +/** + * DTLS {@code OutputRecord} implementation for {@code SSLEngine}. + */ +final class DTLSOutputRecord extends OutputRecord implements DTLSRecord { + + private DTLSFragmenter fragmenter = null; + + int writeEpoch; + + int prevWriteEpoch; + Authenticator prevWriteAuthenticator; + CipherBox prevWriteCipher; + + private LinkedList<RecordMemo> alertMemos = new LinkedList<>(); + + DTLSOutputRecord() { + this.writeAuthenticator = new MAC(true); + + this.writeEpoch = 0; + this.prevWriteEpoch = 0; + this.prevWriteCipher = CipherBox.NULL; + this.prevWriteAuthenticator = new MAC(true); + + this.packetSize = DTLSRecord.maxRecordSize; + this.protocolVersion = ProtocolVersion.DEFAULT_DTLS; + } + + @Override + void changeWriteCiphers(Authenticator writeAuthenticator, + CipherBox writeCipher) throws IOException { + + encodeChangeCipherSpec(); + + prevWriteCipher.dispose(); + + this.prevWriteAuthenticator = this.writeAuthenticator; + this.prevWriteCipher = this.writeCipher; + this.prevWriteEpoch = this.writeEpoch; + + this.writeAuthenticator = writeAuthenticator; + this.writeCipher = writeCipher; + this.writeEpoch++; + + this.isFirstAppOutputRecord = true; + + // set the epoch number + this.writeAuthenticator.setEpochNumber(this.writeEpoch); + } + + @Override + void encodeAlert(byte level, byte description) throws IOException { + RecordMemo memo = new RecordMemo(); + + memo.contentType = Record.ct_alert; + memo.majorVersion = protocolVersion.major; + memo.minorVersion = protocolVersion.minor; + memo.encodeEpoch = writeEpoch; + memo.encodeCipher = writeCipher; + memo.encodeAuthenticator = writeAuthenticator; + + memo.fragment = new byte[2]; + memo.fragment[0] = level; + memo.fragment[1] = description; + + alertMemos.add(memo); + } + + @Override + void encodeChangeCipherSpec() throws IOException { + if (fragmenter == null) { + fragmenter = new DTLSFragmenter(); + } + fragmenter.queueUpChangeCipherSpec(); + } + + @Override + void encodeHandshake(byte[] source, + int offset, int length) throws IOException { + + if (firstMessage) { + firstMessage = false; + } + + if (fragmenter == null) { + fragmenter = new DTLSFragmenter(); + } + + fragmenter.queueUpHandshake(source, offset, length); + } + + @Override + Ciphertext encode(ByteBuffer[] sources, int offset, int length, + ByteBuffer destination) throws IOException { + + if (writeAuthenticator.seqNumOverflow()) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", sequence number extremely close to overflow " + + "(2^64-1 packets). Closing connection."); + } + + throw new SSLHandshakeException("sequence number overflow"); + } + + // not apply to handshake message + int macLen = 0; + if (writeAuthenticator instanceof MAC) { + macLen = ((MAC)writeAuthenticator).MAClen(); + } + + int fragLen; + if (packetSize > 0) { + fragLen = Math.min(maxRecordSize, packetSize); + fragLen = writeCipher.calculateFragmentSize( + fragLen, macLen, headerSize); + + fragLen = Math.min(fragLen, Record.maxDataSize); + } else { + fragLen = Record.maxDataSize; + } + + if (fragmentSize > 0) { + fragLen = Math.min(fragLen, fragmentSize); + } + + int dstPos = destination.position(); + int dstLim = destination.limit(); + int dstContent = dstPos + headerSize + + writeCipher.getExplicitNonceSize(); + destination.position(dstContent); + + int remains = Math.min(fragLen, destination.remaining()); + fragLen = 0; + int srcsLen = offset + length; + for (int i = offset; (i < srcsLen) && (remains > 0); i++) { + int amount = Math.min(sources[i].remaining(), remains); + int srcLimit = sources[i].limit(); + sources[i].limit(sources[i].position() + amount); + destination.put(sources[i]); + sources[i].limit(srcLimit); // restore the limit + remains -= amount; + fragLen += amount; + } + + destination.limit(destination.position()); + destination.position(dstContent); + + if ((debug != null) && Debug.isOn("record")) { + System.out.println(Thread.currentThread().getName() + + ", WRITE: " + protocolVersion + " " + + Record.contentName(Record.ct_application_data) + + ", length = " + destination.remaining()); + } + + // Encrypt the fragment and wrap up a record. + long recordSN = encrypt(writeAuthenticator, writeCipher, + Record.ct_application_data, destination, + dstPos, dstLim, headerSize, + protocolVersion, true); + + if ((debug != null) && Debug.isOn("packet")) { + ByteBuffer temporary = destination.duplicate(); + temporary.limit(temporary.position()); + temporary.position(dstPos); + Debug.printHex( + "[Raw write]: length = " + temporary.remaining(), + temporary); + } + + // remain the limit unchanged + destination.limit(dstLim); + + return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN); + } + + @Override + Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException { + if (alertMemos != null && !alertMemos.isEmpty()) { + RecordMemo memo = alertMemos.pop(); + + int macLen = 0; + if (memo.encodeAuthenticator instanceof MAC) { + macLen = ((MAC)memo.encodeAuthenticator).MAClen(); + } + + int dstPos = destination.position(); + int dstLim = destination.limit(); + int dstContent = dstPos + headerSize + + writeCipher.getExplicitNonceSize(); + destination.position(dstContent); + + destination.put(memo.fragment); + + destination.limit(destination.position()); + destination.position(dstContent); + + if ((debug != null) && Debug.isOn("record")) { + System.out.println(Thread.currentThread().getName() + + ", WRITE: " + protocolVersion + " " + + Record.contentName(Record.ct_alert) + + ", length = " + destination.remaining()); + } + + // Encrypt the fragment and wrap up a record. + long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher, + Record.ct_alert, destination, dstPos, dstLim, headerSize, + ProtocolVersion.valueOf(memo.majorVersion, + memo.minorVersion), true); + + if ((debug != null) && Debug.isOn("packet")) { + ByteBuffer temporary = destination.duplicate(); + temporary.limit(temporary.position()); + temporary.position(dstPos); + Debug.printHex( + "[Raw write]: length = " + temporary.remaining(), + temporary); + } + + // remain the limit unchanged + destination.limit(dstLim); + + return new Ciphertext(RecordType.RECORD_ALERT, recordSN); + } + + if (fragmenter != null) { + return fragmenter.acquireCiphertext(destination); + } + + return null; + } + + @Override + boolean isEmpty() { + return ((fragmenter == null) || fragmenter.isEmpty()) && + ((alertMemos == null) || alertMemos.isEmpty()); + } + + @Override + void initHandshaker() { + // clean up + fragmenter = null; + } + + // buffered record fragment + private static class RecordMemo { + byte contentType; + byte majorVersion; + byte minorVersion; + int encodeEpoch; + CipherBox encodeCipher; + Authenticator encodeAuthenticator; + + byte[] fragment; + } + + private static class HandshakeMemo extends RecordMemo { + byte handshakeType; + int messageSequence; + int acquireOffset; + } + + private final class DTLSFragmenter { + private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>(); + private int acquireIndex = 0; + private int messageSequence = 0; + private boolean flightIsReady = false; + + // Per section 4.1.1, RFC 6347: + // + // If repeated retransmissions do not result in a response, and the + // PMTU is unknown, subsequent retransmissions SHOULD back off to a + // smaller record size, fragmenting the handshake message as + // appropriate. + // + // In this implementation, two times of retransmits would be attempted + // before backing off. The back off is supported only if the packet + // size is bigger than 256 bytes. + private int retransmits = 2; // attemps of retransmits + + void queueUpChangeCipherSpec() { + + // Cleanup if a new flight starts. + if (flightIsReady) { + handshakeMemos.clear(); + acquireIndex = 0; + flightIsReady = false; + } + + RecordMemo memo = new RecordMemo(); + + memo.contentType = Record.ct_change_cipher_spec; + memo.majorVersion = protocolVersion.major; + memo.minorVersion = protocolVersion.minor; + memo.encodeEpoch = writeEpoch; + memo.encodeCipher = writeCipher; + memo.encodeAuthenticator = writeAuthenticator; + + memo.fragment = new byte[1]; + memo.fragment[0] = 1; + + handshakeMemos.add(memo); + } + + void queueUpHandshake(byte[] buf, + int offset, int length) throws IOException { + + // Cleanup if a new flight starts. + if (flightIsReady) { + handshakeMemos.clear(); + acquireIndex = 0; + flightIsReady = false; + } + + HandshakeMemo memo = new HandshakeMemo(); + + memo.contentType = Record.ct_handshake; + memo.majorVersion = protocolVersion.major; + memo.minorVersion = protocolVersion.minor; + memo.encodeEpoch = writeEpoch; + memo.encodeCipher = writeCipher; + memo.encodeAuthenticator = writeAuthenticator; + + memo.handshakeType = buf[offset]; + memo.messageSequence = messageSequence++; + memo.acquireOffset = 0; + memo.fragment = new byte[length - 4]; // 4: header size + // 1: HandshakeType + // 3: message length + System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4); + + handshakeHashing(memo, memo.fragment); + handshakeMemos.add(memo); + + if ((memo.handshakeType == HandshakeMessage.ht_client_hello) || + (memo.handshakeType == HandshakeMessage.ht_hello_request) || + (memo.handshakeType == + HandshakeMessage.ht_hello_verify_request) || + (memo.handshakeType == HandshakeMessage.ht_server_hello_done) || + (memo.handshakeType == HandshakeMessage.ht_finished)) { + + flightIsReady = true; + } + } + + Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException { + if (isEmpty()) { + if (isRetransmittable()) { + setRetransmission(); // configure for retransmission + } else { + return null; + } + } + + RecordMemo memo = handshakeMemos.get(acquireIndex); + HandshakeMemo hsMemo = null; + if (memo.contentType == Record.ct_handshake) { + hsMemo = (HandshakeMemo)memo; + } + + int macLen = 0; + if (memo.encodeAuthenticator instanceof MAC) { + macLen = ((MAC)memo.encodeAuthenticator).MAClen(); + } + + // ChangeCipherSpec message is pretty small. Don't worry about + // the fragmentation of ChangeCipherSpec record. + int fragLen; + if (packetSize > 0) { + fragLen = Math.min(maxRecordSize, packetSize); + fragLen = memo.encodeCipher.calculateFragmentSize( + fragLen, macLen, 25); // 25: header size + // 13: DTLS record + // 12: DTLS handshake message + fragLen = Math.min(fragLen, Record.maxDataSize); + } else { + fragLen = Record.maxDataSize; + } + + if (fragmentSize > 0) { + fragLen = Math.min(fragLen, fragmentSize); + } + + int dstPos = dstBuf.position(); + int dstLim = dstBuf.limit(); + int dstContent = dstPos + headerSize + + memo.encodeCipher.getExplicitNonceSize(); + dstBuf.position(dstContent); + + if (hsMemo != null) { + fragLen = Math.min(fragLen, + (hsMemo.fragment.length - hsMemo.acquireOffset)); + + dstBuf.put(hsMemo.handshakeType); + dstBuf.put((byte)((hsMemo.fragment.length >> 16) & 0xFF)); + dstBuf.put((byte)((hsMemo.fragment.length >> 8) & 0xFF)); + dstBuf.put((byte)(hsMemo.fragment.length & 0xFF)); + dstBuf.put((byte)((hsMemo.messageSequence >> 8) & 0xFF)); + dstBuf.put((byte)(hsMemo.messageSequence & 0xFF)); + dstBuf.put((byte)((hsMemo.acquireOffset >> 16) & 0xFF)); + dstBuf.put((byte)((hsMemo.acquireOffset >> 8) & 0xFF)); + dstBuf.put((byte)(hsMemo.acquireOffset & 0xFF)); + dstBuf.put((byte)((fragLen >> 16) & 0xFF)); + dstBuf.put((byte)((fragLen >> 8) & 0xFF)); + dstBuf.put((byte)(fragLen & 0xFF)); + dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, fragLen); + } else { + fragLen = Math.min(fragLen, memo.fragment.length); + dstBuf.put(memo.fragment, 0, fragLen); + } + + dstBuf.limit(dstBuf.position()); + dstBuf.position(dstContent); + + if ((debug != null) && Debug.isOn("record")) { + System.out.println(Thread.currentThread().getName() + + ", WRITE: " + protocolVersion + " " + + Record.contentName(memo.contentType) + + ", length = " + dstBuf.remaining()); + } + + // Encrypt the fragment and wrap up a record. + long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher, + memo.contentType, dstBuf, + dstPos, dstLim, headerSize, + ProtocolVersion.valueOf(memo.majorVersion, + memo.minorVersion), true); + + if ((debug != null) && Debug.isOn("packet")) { + ByteBuffer temporary = dstBuf.duplicate(); + temporary.limit(temporary.position()); + temporary.position(dstPos); + Debug.printHex( + "[Raw write]: length = " + temporary.remaining(), + temporary); + } + + // remain the limit unchanged + dstBuf.limit(dstLim); + + // Reset the fragmentation offset. + if (hsMemo != null) { + hsMemo.acquireOffset += fragLen; + if (hsMemo.acquireOffset == hsMemo.fragment.length) { + acquireIndex++; + } + + return new Ciphertext(RecordType.valueOf( + hsMemo.contentType, hsMemo.handshakeType), recordSN); + } else { + acquireIndex++; + return new Ciphertext( + RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN); + } + } + + private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) { + + byte hsType = hsFrag.handshakeType; + if ((hsType == HandshakeMessage.ht_hello_request) || + (hsType == HandshakeMessage.ht_hello_verify_request)) { + + // omitted from handshake hash computation + return; + } + + if ((hsFrag.messageSequence == 0) && + (hsType == HandshakeMessage.ht_client_hello)) { + + // omit initial ClientHello message + // + // 2: ClientHello.client_version + // 32: ClientHello.random + int sidLen = hsBody[34]; + + if (sidLen == 0) { // empty session_id, initial handshake + return; + } + } + + // calculate the DTLS header + byte[] temporary = new byte[12]; // 12: handshake header size + + // Handshake.msg_type + temporary[0] = hsFrag.handshakeType; + + // Handshake.length + temporary[1] = (byte)((hsBody.length >> 16) & 0xFF); + temporary[2] = (byte)((hsBody.length >> 8) & 0xFF); + temporary[3] = (byte)(hsBody.length & 0xFF); + + // Handshake.message_seq + temporary[4] = (byte)((hsFrag.messageSequence >> 8) & 0xFF); + temporary[5] = (byte)(hsFrag.messageSequence & 0xFF); + + // Handshake.fragment_offset + temporary[6] = 0; + temporary[7] = 0; + temporary[8] = 0; + + // Handshake.fragment_length + temporary[9] = temporary[1]; + temporary[10] = temporary[2]; + temporary[11] = temporary[3]; + + if ((hsType != HandshakeMessage.ht_finished) && + (hsType != HandshakeMessage.ht_certificate_verify)) { + + handshakeHash.update(temporary, 0, 12); + handshakeHash.update(hsBody, 0, hsBody.length); + } else { + // Reserve until this handshake message has been processed. + handshakeHash.reserve(temporary, 0, 12); + handshakeHash.reserve(hsBody, 0, hsBody.length); + } + + } + + boolean isEmpty() { + if (!flightIsReady || handshakeMemos.isEmpty() || + acquireIndex >= handshakeMemos.size()) { + return true; + } + + return false; + } + + boolean isRetransmittable() { + return (flightIsReady && !handshakeMemos.isEmpty() && + (acquireIndex >= handshakeMemos.size())); + } + + private void setRetransmission() { + acquireIndex = 0; + for (RecordMemo memo : handshakeMemos) { + if (memo instanceof HandshakeMemo) { + HandshakeMemo hmemo = (HandshakeMemo)memo; + hmemo.acquireOffset = 0; + } + } + + // Shrink packet size if: + // 1. maximum fragment size is allowed, in which case the packet + // size is configured bigger than maxRecordSize; + // 2. maximum packet is bigger than 256 bytes; + // 3. two times of retransmits have been attempted. + if ((packetSize <= maxRecordSize) && + (packetSize > 256) && ((retransmits--) <= 0)) { + + // shrink packet size + shrinkPacketSize(); + retransmits = 2; // attemps of retransmits + } + } + + private void shrinkPacketSize() { + packetSize = Math.max(256, packetSize / 2); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java Tue Jun 02 04:01:04 2015 +0000 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +/** + * DTLS record + */ +interface DTLSRecord extends Record { + + static final int headerSize = 13; // DTLS record header + + static final int handshakeHeaderSize = 12; // DTLS handshake header + + /* + * The size of the header plus the max IV length + */ + static final int headerPlusMaxIVSize = + headerSize // header + + maxIVLength; // iv + + /* + * The maximum size that may be increased when translating plaintext to + * ciphertext fragment. + */ + static final int maxPlaintextPlusSize = + headerSize // header + + maxIVLength // iv + + maxMacSize // MAC or AEAD tag + + maxPadding; // block cipher padding + + /* + * the maximum record size + */ + static final int maxRecordSize = + headerPlusMaxIVSize // header + iv + + maxDataSize // data + + maxPadding // padding + + maxMacSize; // MAC or AEAD tag + + /* + * For CBC protection in SSL3/TLS1, we break some plaintext into two + * packets. Max application data size for the second packet. + */ + static final int maxDataSizeMinusOneByteRecord = + maxDataSize // max data size + - ( // max one byte record size + headerPlusMaxIVSize // header + iv + + 1 // one byte data + + maxPadding // padding + + maxMacSize // MAC + ); + + /* + * Maximum record size for alert and change cipher spec records. + * They only contain 2 and 1 bytes of data, respectively. + * Allocate a smaller array. + */ + static final int maxAlertRecordSize = + headerPlusMaxIVSize // header + iv + + 2 // alert + + maxPadding // padding + + maxMacSize; // MAC + +}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java Tue Jun 02 04:01:04 2015 +0000 @@ -29,6 +29,9 @@ import java.security.AccessController; import java.util.Locale; +import sun.misc.HexDumpEncoder; +import java.nio.ByteBuffer; + import sun.security.action.GetPropertyAction; /** @@ -198,4 +201,47 @@ static String toString(byte[] b) { return sun.security.util.Debug.toString(b); } + + static void printHex(String prefix, byte[] bytes) { + HexDumpEncoder dump = new HexDumpEncoder(); + + synchronized (System.out) { + System.out.println(prefix); + try { + dump.encodeBuffer(bytes, System.out); + } catch (Exception e) { + // ignore + } + System.out.flush(); + } + } + + static void printHex(String prefix, ByteBuffer bb) { + HexDumpEncoder dump = new HexDumpEncoder(); + + synchronized (System.out) { + System.out.println(prefix); + try { + dump.encodeBuffer(bb.slice(), System.out); + } catch (Exception e) { + // ignore + } + System.out.flush(); + } + } + + static void printHex(String prefix, byte[] bytes, int offset, int length) { + HexDumpEncoder dump = new HexDumpEncoder(); + + synchronized (System.out) { + System.out.println(prefix); + try { + ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length); + dump.encodeBuffer(bb, System.out); + } catch (Exception e) { + // ignore + } + System.out.flush(); + } + } }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineArgs.java Mon Jun 01 10:29:06 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/* - * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.security.ssl; - -import java.nio.*; - -/* - * A multi-purpose class which handles all of the SSLEngine arguments. - * It validates arguments, checks for RO conditions, does space - * calculations, performs scatter/gather, etc. - * - * @author Brad R. Wetmore - */ -class EngineArgs { - - /* - * Keep track of the input parameters. - */ - ByteBuffer netData; - ByteBuffer [] appData; - - private int offset; // offset/len for the appData array. - private int len; - - /* - * The initial pos/limit conditions. This is useful because we can - * quickly calculate the amount consumed/produced in successful - * operations, or easily return the buffers to their pre-error - * conditions. - */ - private int netPos; - private int netLim; - - private int [] appPoss; - private int [] appLims; - - /* - * Sum total of the space remaining in all of the appData buffers - */ - private int appRemaining = 0; - - private boolean wrapMethod; - - /* - * Called by the SSLEngine.wrap() method. - */ - EngineArgs(ByteBuffer [] appData, int offset, int len, - ByteBuffer netData) { - this.wrapMethod = true; - init(netData, appData, offset, len); - } - - /* - * Called by the SSLEngine.unwrap() method. - */ - EngineArgs(ByteBuffer netData, ByteBuffer [] appData, int offset, - int len) { - this.wrapMethod = false; - init(netData, appData, offset, len); - } - - /* - * The main initialization method for the arguments. Most - * of them are pretty obvious as to what they do. - * - * Since we're already iterating over appData array for validity - * checking, we also keep track of how much remainging space is - * available. Info is used in both unwrap (to see if there is - * enough space available in the destination), and in wrap (to - * determine how much more we can copy into the outgoing data - * buffer. - */ - private void init(ByteBuffer netData, ByteBuffer [] appData, - int offset, int len) { - - if ((netData == null) || (appData == null)) { - throw new IllegalArgumentException("src/dst is null"); - } - - if ((offset < 0) || (len < 0) || (offset > appData.length - len)) { - throw new IndexOutOfBoundsException(); - } - - if (wrapMethod && netData.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - - netPos = netData.position(); - netLim = netData.limit(); - - appPoss = new int [appData.length]; - appLims = new int [appData.length]; - - for (int i = offset; i < offset + len; i++) { - if (appData[i] == null) { - throw new IllegalArgumentException( - "appData[" + i + "] == null"); - } - - /* - * If we're unwrapping, then check to make sure our - * destination bufffers are writable. - */ - if (!wrapMethod && appData[i].isReadOnly()) { - throw new ReadOnlyBufferException(); - } - - appRemaining += appData[i].remaining(); - - appPoss[i] = appData[i].position(); - appLims[i] = appData[i].limit(); - } - - /* - * Ok, looks like we have a good set of args, let's - * store the rest of this stuff. - */ - this.netData = netData; - this.appData = appData; - this.offset = offset; - this.len = len; - } - - /* - * Given spaceLeft bytes to transfer, gather up that much data - * from the appData buffers (starting at offset in the array), - * and transfer it into the netData buffer. - * - * The user has already ensured there is enough room. - */ - void gather(int spaceLeft) { - for (int i = offset; (i < (offset + len)) && (spaceLeft > 0); i++) { - int amount = Math.min(appData[i].remaining(), spaceLeft); - appData[i].limit(appData[i].position() + amount); - netData.put(appData[i]); - appRemaining -= amount; - spaceLeft -= amount; - } - } - - /* - * Using the supplied buffer, scatter the data into the appData buffers - * (starting at offset in the array). - * - * The user has already ensured there is enough room. - */ - void scatter(ByteBuffer readyData) { - int amountLeft = readyData.remaining(); - - for (int i = offset; (i < (offset + len)) && (amountLeft > 0); - i++) { - int amount = Math.min(appData[i].remaining(), amountLeft); - readyData.limit(readyData.position() + amount); - appData[i].put(readyData); - amountLeft -= amount; - } - assert(readyData.remaining() == 0); - } - - int getAppRemaining() { - return appRemaining; - } - - /* - * Calculate the bytesConsumed/byteProduced. Aren't you glad - * we saved this off earlier? - */ - int deltaNet() { - return (netData.position() - netPos); - } - - /* - * Calculate the bytesConsumed/byteProduced. Aren't you glad - * we saved this off earlier? - */ - int deltaApp() { - int sum = 0; // Only calculating 2^14 here, don't need a long. - - for (int i = offset; i < offset + len; i++) { - sum += appData[i].position() - appPoss[i]; - } - - return sum; - } - - /* - * In the case of Exception, we want to reset the positions - * to appear as though no data has been consumed or produced. - * - * Currently, this method is only called as we are preparing to - * fail out, and thus we don't need to actually recalculate - * appRemaining. If that assumption changes, that variable should - * be updated here. - */ - void resetPos() { - netData.position(netPos); - for (int i = offset; i < offset + len; i++) { - // See comment above about recalculating appRemaining. - appData[i].position(appPoss[i]); - } - } - - /* - * We are doing lots of ByteBuffer manipulations, in which case - * we need to make sure that the limits get set back correctly. - * This is one of the last things to get done before returning to - * the user. - */ - void resetLim() { - netData.limit(netLim); - for (int i = offset; i < offset + len; i++) { - appData[i].limit(appLims[i]); - } - } -}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineInputRecord.java Mon Jun 01 10:29:06 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,427 +0,0 @@ -/* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -package sun.security.ssl; - -import java.io.*; -import java.nio.*; -import javax.net.ssl.*; -import javax.crypto.BadPaddingException; -import sun.misc.HexDumpEncoder; - - -/** - * Wrapper class around InputRecord. - * - * Application data is kept external to the InputRecord, - * but handshake data (alert/change_cipher_spec/handshake) will - * be kept internally in the ByteArrayInputStream. - * - * @author Brad Wetmore - */ -final class EngineInputRecord extends InputRecord { - - private SSLEngineImpl engine; - - /* - * A dummy ByteBuffer we'll pass back even when the data - * is stored internally. It'll never actually be used. - */ - static private ByteBuffer tmpBB = ByteBuffer.allocate(0); - - /* - * Flag to tell whether the last read/parsed data resides - * internal in the ByteArrayInputStream, or in the external - * buffers. - */ - private boolean internalData; - - EngineInputRecord(SSLEngineImpl engine) { - super(); - this.engine = engine; - } - - @Override - byte contentType() { - if (internalData) { - return super.contentType(); - } else { - return ct_application_data; - } - } - - /* - * Check if there is enough inbound data in the ByteBuffer - * to make a inbound packet. Look for both SSLv2 and SSLv3. - * - * @return -1 if there are not enough bytes to tell (small header), - */ - int bytesInCompletePacket(ByteBuffer buf) throws SSLException { - - /* - * SSLv2 length field is in bytes 0/1 - * SSLv3/TLS length field is in bytes 3/4 - */ - if (buf.remaining() < 5) { - return -1; - } - - int pos = buf.position(); - byte byteZero = buf.get(pos); - - int len = 0; - - /* - * If we have already verified previous packets, we can - * ignore the verifications steps, and jump right to the - * determination. Otherwise, try one last hueristic to - * see if it's SSL/TLS. - */ - if (formatVerified || - (byteZero == ct_handshake) || - (byteZero == ct_alert)) { - /* - * Last sanity check that it's not a wild record - */ - ProtocolVersion recordVersion = - ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2)); - - // check the record version - checkRecordVersion(recordVersion, false); - - /* - * Reasonably sure this is a V3, disable further checks. - * We can't do the same in the v2 check below, because - * read still needs to parse/handle the v2 clientHello. - */ - formatVerified = true; - - /* - * One of the SSLv3/TLS message types. - */ - len = ((buf.get(pos + 3) & 0xff) << 8) + - (buf.get(pos + 4) & 0xff) + headerSize; - - } else { - /* - * Must be SSLv2 or something unknown. - * Check if it's short (2 bytes) or - * long (3) header. - * - * Internals can warn about unsupported SSLv2 - */ - boolean isShort = ((byteZero & 0x80) != 0); - - if (isShort && - ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) { - - ProtocolVersion recordVersion = - ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4)); - - // check the record version - checkRecordVersion(recordVersion, true); - - /* - * Client or Server Hello - */ - int mask = (isShort ? 0x7f : 0x3f); - len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) + - (isShort ? 2 : 3); - - } else { - // Gobblygook! - throw new SSLException( - "Unrecognized SSL message, plaintext connection?"); - } - } - - return len; - } - - /* - * Pass the data down if it's internally cached, otherwise - * do it here. - * - * If internal data, data is decrypted internally. - * - * If external data(app), return a new ByteBuffer with data to - * process. - */ - ByteBuffer decrypt(Authenticator authenticator, - CipherBox box, ByteBuffer bb) throws BadPaddingException { - - if (internalData) { - decrypt(authenticator, box); // MAC is checked during decryption - return tmpBB; - } - - BadPaddingException reservedBPE = null; - int tagLen = - (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; - int cipheredLength = bb.remaining(); - - if (!box.isNullCipher()) { - try { - // apply explicit nonce for AEAD/CBC cipher suites if needed - int nonceSize = - box.applyExplicitNonce(authenticator, contentType(), bb); - - // decrypt the content - if (box.isAEADMode()) { - // DON'T encrypt the nonce_explicit for AEAD mode - bb.position(bb.position() + nonceSize); - } // The explicit IV for CBC mode can be decrypted. - - // Note that the CipherBox.decrypt() does not change - // the capacity of the buffer. - box.decrypt(bb, tagLen); - bb.position(nonceSize); // We don't actually remove the nonce. - } catch (BadPaddingException bpe) { - // RFC 2246 states that decryption_failed should be used - // for this purpose. However, that allows certain attacks, - // so we just send bad record MAC. We also need to make - // sure to always check the MAC to avoid a timing attack - // for the same issue. See paper by Vaudenay et al and the - // update in RFC 4346/5246. - // - // Failover to message authentication code checking. - reservedBPE = bpe; - } - } - - // Requires message authentication code for null, stream and block - // cipher suites. - if ((authenticator instanceof MAC) && (tagLen != 0)) { - MAC signer = (MAC)authenticator; - int macOffset = bb.limit() - tagLen; - - // Note that although it is not necessary, we run the same MAC - // computation and comparison on the payload for both stream - // cipher and CBC block cipher. - if (bb.remaining() < tagLen) { - // negative data length, something is wrong - if (reservedBPE == null) { - reservedBPE = new BadPaddingException("bad record"); - } - - // set offset of the dummy MAC - macOffset = cipheredLength - tagLen; - bb.limit(cipheredLength); - } - - // Run MAC computation and comparison on the payload. - if (checkMacTags(contentType(), bb, signer, false)) { - if (reservedBPE == null) { - reservedBPE = new BadPaddingException("bad record MAC"); - } - } - - // Run MAC computation and comparison on the remainder. - // - // It is only necessary for CBC block cipher. It is used to get a - // constant time of MAC computation and comparison on each record. - if (box.isCBCMode()) { - int remainingLen = calculateRemainingLen( - signer, cipheredLength, macOffset); - - // NOTE: here we use the InputRecord.buf because I did not find - // an effective way to work on ByteBuffer when its capacity is - // less than remainingLen. - - // NOTE: remainingLen may be bigger (less than 1 block of the - // hash algorithm of the MAC) than the cipheredLength. However, - // We won't need to worry about it because we always use a - // maximum buffer for every record. We need a change here if - // we use small buffer size in the future. - if (remainingLen > buf.length) { - // unlikely to happen, just a placehold - throw new RuntimeException( - "Internal buffer capacity error"); - } - - // Won't need to worry about the result on the remainder. And - // then we won't need to worry about what's actual data to - // check MAC tag on. We start the check from the header of the - // buffer so that we don't need to construct a new byte buffer. - checkMacTags(contentType(), buf, 0, remainingLen, signer, true); - } - - bb.limit(macOffset); - } - - // Is it a failover? - if (reservedBPE != null) { - throw reservedBPE; - } - - return bb.slice(); - } - - /* - * Run MAC computation and comparison - * - * Please DON'T change the content of the ByteBuffer parameter! - */ - private static boolean checkMacTags(byte contentType, ByteBuffer bb, - MAC signer, boolean isSimulated) { - - int position = bb.position(); - int tagLen = signer.MAClen(); - int lim = bb.limit(); - int macData = lim - tagLen; - - bb.limit(macData); - byte[] hash = signer.compute(contentType, bb, isSimulated); - if (hash == null || tagLen != hash.length) { - // Something is wrong with MAC implementation. - throw new RuntimeException("Internal MAC error"); - } - - bb.position(macData); - bb.limit(lim); - try { - int[] results = compareMacTags(bb, hash); - return (results[0] != 0); - } finally { - // reset to the data - bb.position(position); - bb.limit(macData); - } - } - - /* - * A constant-time comparison of the MAC tags. - * - * Please DON'T change the content of the ByteBuffer parameter! - */ - private static int[] compareMacTags(ByteBuffer bb, byte[] tag) { - - // An array of hits is used to prevent Hotspot optimization for - // the purpose of a constant-time check. - int[] results = {0, 0}; // {missed #, matched #} - - // The caller ensures there are enough bytes available in the buffer. - // So we won't need to check the remaining of the buffer. - for (int i = 0; i < tag.length; i++) { - if (bb.get() != tag[i]) { - results[0]++; // mismatched bytes - } else { - results[1]++; // matched bytes - } - } - - return results; - } - - /* - * Override the actual write below. We do things this way to be - * consistent with InputRecord. InputRecord may try to write out - * data to the peer, and *then* throw an Exception. This forces - * data to be generated/output before the exception is ever - * generated. - */ - @Override - void writeBuffer(OutputStream s, byte [] buf, int off, int len) - throws IOException { - /* - * Copy data out of buffer, it's ready to go. - */ - ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, 0, len).flip(); - engine.writer.putOutboundDataSync(netBB); - } - - /* - * Delineate or read a complete packet from src. - * - * If internal data (hs, alert, ccs), the data is read and - * stored internally. - * - * If external data (app), return a new ByteBuffer which points - * to the data to process. - */ - ByteBuffer read(ByteBuffer srcBB) throws IOException { - /* - * Could have a src == null/dst == null check here, - * but that was already checked by SSLEngine.unwrap before - * ever attempting to read. - */ - - /* - * If we have anything besides application data, - * or if we haven't even done the initial v2 verification, - * we send this down to be processed by the underlying - * internal cache. - */ - if (!formatVerified || - (srcBB.get(srcBB.position()) != ct_application_data)) { - internalData = true; - read(new ByteBufferInputStream(srcBB), (OutputStream) null); - return tmpBB; - } - - internalData = false; - - int srcPos = srcBB.position(); - int srcLim = srcBB.limit(); - - ProtocolVersion recordVersion = ProtocolVersion.valueOf( - srcBB.get(srcPos + 1), srcBB.get(srcPos + 2)); - - // check the record version - checkRecordVersion(recordVersion, false); - - /* - * It's really application data. How much to consume? - * Jump over the header. - */ - int len = bytesInCompletePacket(srcBB); - assert(len > 0); - - if (debug != null && Debug.isOn("packet")) { - try { - HexDumpEncoder hd = new HexDumpEncoder(); - ByteBuffer bb = srcBB.duplicate(); // Use copy of BB - bb.limit(srcPos + len); - - System.out.println("[Raw read (bb)]: length = " + len); - hd.encodeBuffer(bb, System.out); - } catch (IOException e) { } - } - - // Demarcate past header to end of packet. - srcBB.position(srcPos + headerSize); - srcBB.limit(srcPos + len); - - // Protect remainder of buffer, create slice to actually - // operate on. - ByteBuffer bb = srcBB.slice(); - - srcBB.position(srcBB.limit()); - srcBB.limit(srcLim); - - return bb; - } -}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineOutputRecord.java Mon Jun 01 10:29:06 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,329 +0,0 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - - -package sun.security.ssl; - -import java.io.*; -import java.nio.*; - -/** - * A OutputRecord class extension which uses external ByteBuffers - * or the internal ByteArrayOutputStream for data manipulations. - * <P> - * Instead of rewriting this entire class - * to use ByteBuffers, we leave things intact, so handshake, CCS, - * and alerts will continue to use the internal buffers, but application - * data will use external buffers. - * - * @author Brad Wetmore - */ -final class EngineOutputRecord extends OutputRecord { - - private SSLEngineImpl engine; - private EngineWriter writer; - - private boolean finishedMsg = false; - - /* - * All handshake hashing is done by the superclass - */ - - /* - * Default constructor makes a record supporting the maximum - * SSL record size. It allocates the header bytes directly. - * - * @param type the content type for the record - */ - EngineOutputRecord(byte type, SSLEngineImpl engine) { - super(type, recordSize(type)); - this.engine = engine; - writer = engine.writer; - } - - /** - * Get the size of the buffer we need for records of the specified - * type. - * <P> - * Application data buffers will provide their own byte buffers, - * and will not use the internal byte caching. - */ - private static int recordSize(byte type) { - switch (type) { - - case ct_change_cipher_spec: - case ct_alert: - return maxAlertRecordSize; - - case ct_handshake: - return maxRecordSize; - - case ct_application_data: - return 0; - } - - throw new RuntimeException("Unknown record type: " + type); - } - - void setFinishedMsg() { - finishedMsg = true; - } - - @Override - public void flush() throws IOException { - finishedMsg = false; - } - - boolean isFinishedMsg() { - return finishedMsg; - } - - /* - * Override the actual write below. We do things this way to be - * consistent with InputRecord. InputRecord may try to write out - * data to the peer, and *then* throw an Exception. This forces - * data to be generated/output before the exception is ever - * generated. - */ - @Override - void writeBuffer(OutputStream s, byte [] buf, int off, int len, - int debugOffset) throws IOException { - /* - * Copy data out of buffer, it's ready to go. - */ - ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, off, len).flip(); - writer.putOutboundData(netBB); - } - - /* - * Main method for writing non-application data. - * We MAC/encrypt, then send down for processing. - */ - void write(Authenticator authenticator, CipherBox writeCipher) - throws IOException { - - /* - * Sanity check. - */ - switch (contentType()) { - case ct_change_cipher_spec: - case ct_alert: - case ct_handshake: - break; - default: - throw new RuntimeException("unexpected byte buffers"); - } - - /* - * Don't bother to really write empty records. We went this - * far to drive the handshake machinery, for correctness; not - * writing empty records improves performance by cutting CPU - * time and network resource usage. Also, some protocol - * implementations are fragile and don't like to see empty - * records, so this increases robustness. - * - * (Even change cipher spec messages have a byte of data!) - */ - if (!isEmpty()) { - // compress(); // eventually - encrypt(authenticator, writeCipher); - - // send down for processing - write((OutputStream)null, false, (ByteArrayOutputStream)null); - } - return; - } - - /** - * Main wrap/write driver. - */ - void write(EngineArgs ea, Authenticator authenticator, - CipherBox writeCipher) throws IOException { - /* - * sanity check to make sure someone didn't inadvertantly - * send us an impossible combination we don't know how - * to process. - */ - assert(contentType() == ct_application_data); - - /* - * Have we set the MAC's yet? If not, we're not ready - * to process application data yet. - */ - if (authenticator == MAC.NULL) { - return; - } - - /* - * Don't bother to really write empty records. We went this - * far to drive the handshake machinery, for correctness; not - * writing empty records improves performance by cutting CPU - * time and network resource usage. Also, some protocol - * implementations are fragile and don't like to see empty - * records, so this increases robustness. - */ - if (ea.getAppRemaining() == 0) { - return; - } - - /* - * By default, we counter chosen plaintext issues on CBC mode - * ciphersuites in SSLv3/TLS1.0 by sending one byte of application - * data in the first record of every payload, and the rest in - * subsequent record(s). Note that the issues have been solved in - * TLS 1.1 or later. - * - * It is not necessary to split the very first application record of - * a freshly negotiated TLS session, as there is no previous - * application data to guess. To improve compatibility, we will not - * split such records. - * - * Because of the compatibility, we'd better produce no more than - * SSLSession.getPacketBufferSize() net data for each wrap. As we - * need a one-byte record at first, the 2nd record size should be - * equal to or less than Record.maxDataSizeMinusOneByteRecord. - * - * This avoids issues in the outbound direction. For a full fix, - * the peer must have similar protections. - */ - int length; - if (engine.needToSplitPayload(writeCipher, protocolVersion)) { - write(ea, authenticator, writeCipher, 0x01); - ea.resetLim(); // reset application data buffer limit - length = Math.min(ea.getAppRemaining(), - maxDataSizeMinusOneByteRecord); - } else { - length = Math.min(ea.getAppRemaining(), maxDataSize); - } - - // Don't bother to really write empty records. - if (length > 0) { - write(ea, authenticator, writeCipher, length); - } - - return; - } - - void write(EngineArgs ea, Authenticator authenticator, - CipherBox writeCipher, int length) throws IOException { - /* - * Copy out existing buffer values. - */ - ByteBuffer dstBB = ea.netData; - int dstPos = dstBB.position(); - int dstLim = dstBB.limit(); - - /* - * Where to put the data. Jump over the header. - * - * Don't need to worry about SSLv2 rewrites, if we're here, - * that's long since done. - */ - int dstData = dstPos + headerSize + writeCipher.getExplicitNonceSize(); - dstBB.position(dstData); - - /* - * transfer application data into the network data buffer - */ - ea.gather(length); - dstBB.limit(dstBB.position()); - dstBB.position(dstData); - - /* - * "flip" but skip over header again, add MAC & encrypt - */ - if (authenticator instanceof MAC) { - MAC signer = (MAC)authenticator; - if (signer.MAClen() != 0) { - byte[] hash = signer.compute(contentType(), dstBB, false); - - /* - * position was advanced to limit in compute above. - * - * Mark next area as writable (above layers should have - * established that we have plenty of room), then write - * out the hash. - */ - dstBB.limit(dstBB.limit() + hash.length); - dstBB.put(hash); - - // reset the position and limit - dstBB.limit(dstBB.position()); - dstBB.position(dstData); - } - } - - if (!writeCipher.isNullCipher()) { - /* - * Requires explicit IV/nonce for CBC/AEAD cipher suites for TLS 1.1 - * or later. - */ - if (protocolVersion.v >= ProtocolVersion.TLS11.v && - (writeCipher.isCBCMode() || writeCipher.isAEADMode())) { - byte[] nonce = writeCipher.createExplicitNonce( - authenticator, contentType(), dstBB.remaining()); - dstBB.position(dstPos + headerSize); - dstBB.put(nonce); - if (!writeCipher.isAEADMode()) { - // The explicit IV in TLS 1.1 and later can be encrypted. - dstBB.position(dstPos + headerSize); - } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode - } - - /* - * Encrypt may pad, so again the limit may have changed. - */ - writeCipher.encrypt(dstBB, dstLim); - - if ((debug != null) && (Debug.isOn("record") || - (Debug.isOn("handshake") && - (contentType() == ct_change_cipher_spec)))) { - System.out.println(Thread.currentThread().getName() - // v3.0/v3.1 ... - + ", WRITE: " + protocolVersion - + " " + InputRecord.contentName(contentType()) - + ", length = " + length); - } - } else { - dstBB.position(dstBB.limit()); - } - - int packetLength = dstBB.limit() - dstPos - headerSize; - - /* - * Finish out the record header. - */ - dstBB.put(dstPos, contentType()); - dstBB.put(dstPos + 1, protocolVersion.major); - dstBB.put(dstPos + 2, protocolVersion.minor); - dstBB.put(dstPos + 3, (byte)(packetLength >> 8)); - dstBB.put(dstPos + 4, (byte)packetLength); - - /* - * Position was already set by encrypt() above. - */ - dstBB.limit(dstLim); - } -}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/EngineWriter.java Mon Jun 01 10:29:06 2015 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package sun.security.ssl; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.LinkedList; -import javax.net.ssl.SSLEngineResult.HandshakeStatus; -import sun.misc.HexDumpEncoder; - -/** - * A class to help abstract away SSLEngine writing synchronization. - */ -final class EngineWriter { - - /* - * Outgoing handshake Data waiting for a ride is stored here. - * Normal application data is written directly into the outbound - * buffer, but handshake data can be written out at any time, - * so we have buffer it somewhere. - * - * When wrap is called, we first check to see if there is - * any data waiting, then if we're in a data transfer state, - * we try to write app data. - * - * This will contain either ByteBuffers, or the marker - * HandshakeStatus.FINISHED to signify that a handshake just completed. - */ - private LinkedList<Object> outboundList; - - private boolean outboundClosed = false; - - /* Class and subclass dynamic debugging support */ - private static final Debug debug = Debug.getInstance("ssl"); - - EngineWriter() { - outboundList = new LinkedList<Object>(); - } - - /* - * Upper levels assured us we had room for at least one packet of data. - * As per the SSLEngine spec, we only return one SSL packets worth of - * data. - */ - private HandshakeStatus getOutboundData(ByteBuffer dstBB) { - - Object msg = outboundList.removeFirst(); - assert(msg instanceof ByteBuffer); - - ByteBuffer bbIn = (ByteBuffer) msg; - assert(dstBB.remaining() >= bbIn.remaining()); - - dstBB.put(bbIn); - - /* - * If we have more data in the queue, it's either - * a finished message, or an indication that we need - * to call wrap again. - */ - if (hasOutboundDataInternal()) { - msg = outboundList.getFirst(); - if (msg == HandshakeStatus.FINISHED) { - outboundList.removeFirst(); // consume the message - return HandshakeStatus.FINISHED; - } else { - return HandshakeStatus.NEED_WRAP; - } - } else { - return null; - } - } - - /* - * Properly orders the output of the data written to the wrap call. - * This is only handshake data, application data goes through the - * other writeRecord. - */ - synchronized void writeRecord(EngineOutputRecord outputRecord, - Authenticator authenticator, - CipherBox writeCipher) throws IOException { - - /* - * Only output if we're still open. - */ - if (outboundClosed) { - throw new IOException("writer side was already closed."); - } - - outputRecord.write(authenticator, writeCipher); - - /* - * Did our handshakers notify that we just sent the - * Finished message? - * - * Add an "I'm finished" message to the queue. - */ - if (outputRecord.isFinishedMsg()) { - outboundList.addLast(HandshakeStatus.FINISHED); - } - } - - /* - * Output the packet info. - */ - private void dumpPacket(EngineArgs ea, boolean hsData) { - try { - HexDumpEncoder hd = new HexDumpEncoder(); - - ByteBuffer bb = ea.netData.duplicate(); - - int pos = bb.position(); - bb.position(pos - ea.deltaNet()); - bb.limit(pos); - - System.out.println("[Raw write" + - (hsData ? "" : " (bb)") + "]: length = " + - bb.remaining()); - hd.encodeBuffer(bb, System.out); - } catch (IOException e) { } - } - - /* - * Properly orders the output of the data written to the wrap call. - * Only app data goes through here, handshake data goes through - * the other writeRecord. - * - * Shouldn't expect to have an IOException here. - * - * Return any determined status. - */ - synchronized HandshakeStatus writeRecord( - EngineOutputRecord outputRecord, EngineArgs ea, - Authenticator authenticator, - CipherBox writeCipher) throws IOException { - - /* - * If we have data ready to go, output this first before - * trying to consume app data. - */ - if (hasOutboundDataInternal()) { - HandshakeStatus hss = getOutboundData(ea.netData); - - if (debug != null && Debug.isOn("packet")) { - /* - * We could have put the dump in - * OutputRecord.write(OutputStream), but let's actually - * output when it's actually output by the SSLEngine. - */ - dumpPacket(ea, true); - } - - return hss; - } - - /* - * If we are closed, no more app data can be output. - * Only existing handshake data (above) can be obtained. - */ - if (outboundClosed) { - throw new IOException("The write side was already closed"); - } - - outputRecord.write(ea, authenticator, writeCipher); - - if (debug != null && Debug.isOn("packet")) { - dumpPacket(ea, false); - } - - /* - * No way new outbound handshake data got here if we're - * locked properly. - * - * We don't have any status we can return. - */ - return null; - } - - /* - * We already hold "this" lock, this is the callback from the - * outputRecord.write() above. We already know this - * writer can accept more data (outboundClosed == false), - * and the closure is sync'd. - */ - void putOutboundData(ByteBuffer bytes) { - outboundList.addLast(bytes); - } - - /* - * This is for the really rare case that someone is writing from - * the *InputRecord* before we know what to do with it. - */ - synchronized void putOutboundDataSync(ByteBuffer bytes) - throws IOException { - - if (outboundClosed) { - throw new IOException("Write side already closed"); - } - - outboundList.addLast(bytes); - } - - /* - * Non-synch'd version of this method, called by internals - */ - private boolean hasOutboundDataInternal() { - return (outboundList.size() != 0); - } - - synchronized boolean hasOutboundData() { - return hasOutboundDataInternal(); - } - - synchronized boolean isOutboundDone() { - return outboundClosed && !hasOutboundDataInternal(); - } - - synchronized void closeOutbound() { - outboundClosed = true; - } - -}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeHash.java Tue Jun 02 04:01:04 2015 +0000 @@ -29,6 +29,7 @@ import java.io.ByteArrayOutputStream; import java.security.*; import java.util.Locale; +import java.nio.ByteBuffer; /** * Abstraction for the SSL/TLS hash of all handshake messages that is @@ -99,6 +100,9 @@ // For TLS 1.2 private MessageDigest finMD; + // Cache for input record handshake hash computation + private ByteArrayOutputStream reserve = new ByteArrayOutputStream(); + /** * Create a new HandshakeHash. needCertificateVerify indicates whether * a hash for the certificate verify message is required. @@ -107,7 +111,106 @@ clonesNeeded = needCertificateVerify ? 3 : 2; } + void reserve(ByteBuffer input) { + if (input.hasArray()) { + reserve.write(input.array(), + input.position() + input.arrayOffset(), input.remaining()); + } else { + int inPos = input.position(); + byte[] holder = new byte[input.remaining()]; + input.get(holder); + input.position(inPos); + reserve.write(holder, 0, holder.length); + } + } + + void reserve(byte[] b, int offset, int len) { + reserve.write(b, offset, len); + } + + void reload() { + if (reserve.size() != 0) { + byte[] bytes = reserve.toByteArray(); + reserve.reset(); + update(bytes, 0, bytes.length); + } + } + + void update(ByteBuffer input) { + + // reload if there are reserved messages. + reload(); + + int inPos = input.position(); + switch (version) { + case 1: + md5.update(input); + input.position(inPos); + + sha.update(input); + input.position(inPos); + + break; + default: + if (finMD != null) { + finMD.update(input); + input.position(inPos); + } + if (input.hasArray()) { + data.write(input.array(), + inPos + input.arrayOffset(), input.remaining()); + } else { + byte[] holder = new byte[input.remaining()]; + input.get(holder); + input.position(inPos); + data.write(holder, 0, holder.length); + } + break; + } + } + + void update(byte handshakeType, byte[] handshakeBody) { + + // reload if there are reserved messages. + reload(); + + switch (version) { + case 1: + md5.update(handshakeType); + sha.update(handshakeType); + + md5.update((byte)((handshakeBody.length >> 16) & 0xFF)); + sha.update((byte)((handshakeBody.length >> 16) & 0xFF)); + md5.update((byte)((handshakeBody.length >> 8) & 0xFF)); + sha.update((byte)((handshakeBody.length >> 8) & 0xFF)); + md5.update((byte)(handshakeBody.length & 0xFF)); + sha.update((byte)(handshakeBody.length & 0xFF)); + + md5.update(handshakeBody); + sha.update(handshakeBody); + break; + default: + if (finMD != null) { + finMD.update(handshakeType); + finMD.update((byte)((handshakeBody.length >> 16) & 0xFF)); + finMD.update((byte)((handshakeBody.length >> 8) & 0xFF)); + finMD.update((byte)(handshakeBody.length & 0xFF)); + finMD.update(handshakeBody); + } + data.write(handshakeType); + data.write((byte)((handshakeBody.length >> 16) & 0xFF)); + data.write((byte)((handshakeBody.length >> 8) & 0xFF)); + data.write((byte)(handshakeBody.length & 0xFF)); + data.write(handshakeBody, 0, handshakeBody.length); + break; + } + } + void update(byte[] b, int offset, int len) { + + // reload if there are reserved messages. + reload(); + switch (version) { case 1: md5.update(b, offset, len); @@ -139,9 +242,15 @@ void protocolDetermined(ProtocolVersion pv) { // Do not set again, will ignore - if (version != -1) return; + if (version != -1) { + return; + } - version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1; + if (pv.maybeDTLSProtocol()) { + version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1; + } else { + version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1; + } switch (version) { case 1: // initiate md5, sha and call update on saved array
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeInStream.java Tue Jun 02 04:01:04 2015 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,11 +23,11 @@ * questions. */ - package sun.security.ssl; -import java.io.InputStream; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.ByteBuffer; import javax.net.ssl.SSLException; @@ -38,154 +38,104 @@ * Once a new handshake record arrives, it is buffered in this class until * processed by the Handshaker. The buffer may also contain incomplete * handshake messages in case the message is split across multiple records. - * Handshaker.process_record deals with all that. It may also contain + * Handshaker.processRecord deals with all that. It may also contain * handshake messages larger than the default buffer size (e.g. large - * certificate messages). The buffer is grown dynamically to handle that - * (see InputRecord.queueHandshake()). + * certificate messages). The buffer is grown dynamically to handle that. * - * Note that the InputRecord used as a buffer here is separate from the - * AppInStream.r, which is where data from the socket is initially read - * into. This is because once the initial handshake has been completed, - * handshake and application data messages may be interleaved arbitrarily - * and must be processed independently. + * Note that this class only handles Handshake messages in TLS format. + * DTLS Handshake messages should be converted into TLS format before + * calling into this method. * * @author David Brownell */ -public class HandshakeInStream extends InputStream { - InputRecord r; +// This class is used to handle plain text handshake messages. +// +public final class HandshakeInStream extends ByteArrayInputStream { /* * Construct the stream; we'll be accumulating hashes of the * input records using two sets of digests. */ - HandshakeInStream(HandshakeHash handshakeHash) { - r = new InputRecord(); - r.setHandshakeHash(handshakeHash); + HandshakeInStream() { + super(new byte[0]); // lazy to alloacte the internal buffer } + // + // overridden ByteArrayInputStream methods + // - // overridden InputStream methods + @Override + public int read(byte[] b) throws IOException { + if (super.read(b) != b.length) { + throw new SSLException("Unexpected end of handshake data"); + } - /* - * Return the number of bytes available for read(). - * - * Note that this returns the bytes remaining in the buffer, not - * the bytes remaining in the current handshake message. - */ - @Override - public int available() { - return r.available(); + return b.length; } - /* - * Get a byte of handshake data. - */ - @Override - public int read() throws IOException { - int n = r.read(); - if (n == -1) { - throw new SSLException("Unexpected end of handshake data"); - } - return n; - } - - /* - * Get a bunch of bytes of handshake data. - */ - @Override - public int read(byte b [], int off, int len) throws IOException { - // we read from a ByteArrayInputStream, it always returns the - // data in a single read if enough is available - int n = r.read(b, off, len); - if (n != len) { - throw new SSLException("Unexpected end of handshake data"); - } - return n; - } - - /* - * Skip some handshake data. - */ - @Override - public long skip(long n) throws IOException { - return r.skip(n); - } - - /* - * Mark/ reset code, implemented using InputRecord mark/ reset. - * - * Note that it currently provides only a limited mark functionality - * and should be used with care (once a new handshake record has been - * read, data that has already been consumed is lost even if marked). - */ - - @Override - public void mark(int readlimit) { - r.mark(readlimit); - } - - @Override - public void reset() throws IOException { - r.reset(); - } - - @Override - public boolean markSupported() { - return true; - } - - - // handshake management functions + // + // handshake input stream management functions + // /* * Here's an incoming record with handshake data. Queue the contents; * it might be one or more entire messages, complete a message that's * partly queued, or both. */ - void incomingRecord(InputRecord in) throws IOException { - r.queueHandshake(in); + void incomingRecord(ByteBuffer in) throws IOException { + int len; + + // Move any unread data to the front of the buffer. + if (pos != 0) { + len = count - pos; + if (len != 0) { + System.arraycopy(buf, pos, buf, 0, len); + } + pos = 0; + count = len; + } + + // Grow buffer if needed. + len = in.remaining() + count; + if (buf.length < len) { + byte[] newbuf = new byte[len]; + if (count != 0) { + System.arraycopy(buf, 0, newbuf, 0, count); + } + buf = newbuf; + } + + // Append the incoming record to the buffer + in.get(buf, count, in.remaining()); + count = len; } - /* - * Hash any data we've consumed but not yet hashed. Useful mostly - * for processing client certificate messages (so we can check the - * immediately following cert verify message) and finished messages - * (so we can compute our own finished message). - */ - void digestNow() { - r.doHashes(); - } - - /* - * Do more than skip that handshake data ... totally ignore it. - * The difference is that the data does not get hashed. - */ - void ignore(int n) { - r.ignore(n); - } - - + // // Message parsing methods + // /* * Read 8, 16, 24, and 32 bit SSL integer data types, encoded * in standard big-endian form. */ - int getInt8() throws IOException { + verifyLength(1); return read(); } int getInt16() throws IOException { + verifyLength(2); return (getInt8() << 8) | getInt8(); } int getInt24() throws IOException { + verifyLength(3); return (getInt8() << 16) | (getInt8() << 8) | getInt8(); } int getInt32() throws IOException { + verifyLength(4); return (getInt8() << 24) | (getInt8() << 16) | (getInt8() << 8) | getInt8(); } @@ -193,13 +143,12 @@ /* * Read byte vectors with 8, 16, and 24 bit length encodings. */ - byte[] getBytes8() throws IOException { int len = getInt8(); verifyLength(len); byte b[] = new byte[len]; - read(b, 0, len); + read(b); return b; } @@ -208,7 +157,7 @@ verifyLength(len); byte b[] = new byte[len]; - read(b, 0, len); + read(b); return b; } @@ -217,16 +166,14 @@ verifyLength(len); byte b[] = new byte[len]; - read(b, 0, len); + read(b); return b; } // Is a length greater than available bytes in the record? private void verifyLength(int len) throws SSLException { if (len > available()) { - throw new SSLException( - "Not enough data to fill declared vector size"); + throw new SSLException("Unexpected end of handshake data"); } } - }
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java Tue Jun 02 04:01:04 2015 +0000 @@ -73,24 +73,43 @@ */ public abstract class HandshakeMessage { - HandshakeMessage() { } + /* Class and subclass dynamic debugging support */ + public static final Debug debug = Debug.getInstance("ssl"); // enum HandshakeType: - static final byte ht_hello_request = 0; - static final byte ht_client_hello = 1; - static final byte ht_server_hello = 2; + static final byte ht_hello_request = 0; // RFC 5246 + static final byte ht_client_hello = 1; // RFC 5246 + static final byte ht_server_hello = 2; // RFC 5246 + static final byte ht_hello_verify_request = 3; // RFC 6347 + static final byte ht_new_session_ticket = 4; // RFC 4507 - static final byte ht_certificate = 11; - static final byte ht_server_key_exchange = 12; - static final byte ht_certificate_request = 13; - static final byte ht_server_hello_done = 14; - static final byte ht_certificate_verify = 15; - static final byte ht_client_key_exchange = 16; + static final byte ht_certificate = 11; // RFC 5246 + static final byte ht_server_key_exchange = 12; // RFC 5246 + static final byte ht_certificate_request = 13; // RFC 5246 + static final byte ht_server_hello_done = 14; // RFC 5246 + static final byte ht_certificate_verify = 15; // RFC 5246 + static final byte ht_client_key_exchange = 16; // RFC 5246 - static final byte ht_finished = 20; + static final byte ht_finished = 20; // RFC 5246 + static final byte ht_certificate_url = 21; // RFC 6066 + static final byte ht_certificate_status = 22; // RFC 6066 + static final byte ht_supplemental_data = 23; // RFC 4680 - /* Class and subclass dynamic debugging support */ - public static final Debug debug = Debug.getInstance("ssl"); + static final byte ht_not_applicable = -1; // N/A + + /* + * SSL 3.0 MAC padding constants. + * Also used by CertificateVerify and Finished during the handshake. + */ + static final byte[] MD5_pad1 = genPad(0x36, 48); + static final byte[] MD5_pad2 = genPad(0x5c, 48); + + static final byte[] SHA_pad1 = genPad(0x36, 40); + static final byte[] SHA_pad2 = genPad(0x5c, 40); + + // default constructor + HandshakeMessage() { + } /** * Utility method to convert a BigInteger to a byte array in unsigned @@ -109,16 +128,6 @@ return b; } - /* - * SSL 3.0 MAC padding constants. - * Also used by CertificateVerify and Finished during the handshake. - */ - static final byte[] MD5_pad1 = genPad(0x36, 48); - static final byte[] MD5_pad2 = genPad(0x5c, 48); - - static final byte[] SHA_pad1 = genPad(0x36, 40); - static final byte[] SHA_pad2 = genPad(0x5c, 40); - private static byte[] genPad(int b, int count) { byte[] padding = new byte[count]; Arrays.fill(padding, (byte)b); @@ -141,6 +150,7 @@ s.write(messageType()); s.putInt24(len); send(s); + s.complete(); } /* @@ -199,6 +209,69 @@ } +/* + * HelloVerifyRequest ... SERVER --> CLIENT [DTLS only] + * + * The definition of HelloVerifyRequest is as follows: + * + * struct { + * ProtocolVersion server_version; + * opaque cookie<0..2^8-1>; + * } HelloVerifyRequest; + * + * For DTLS protocols, once the client has transmitted the ClientHello message, + * it expects to see a HelloVerifyRequest from the server. However, if the + * server's message is lost, the client knows that either the ClientHello or + * the HelloVerifyRequest has been lost and retransmits. [RFC 6347] + */ +static final class HelloVerifyRequest extends HandshakeMessage { + ProtocolVersion protocolVersion; + byte[] cookie; // 1 to 2^8 - 1 bytes + + HelloVerifyRequest(HelloCookieManager helloCookieManager, + ClientHello clientHelloMsg) { + + this.protocolVersion = clientHelloMsg.protocolVersion; + this.cookie = helloCookieManager.getCookie(clientHelloMsg); + } + + HelloVerifyRequest( + HandshakeInStream input, int messageLength) throws IOException { + + this.protocolVersion = + ProtocolVersion.valueOf(input.getInt8(), input.getInt8()); + this.cookie = input.getBytes8(); + + // Is it a valid cookie? + HelloCookieManager.checkCookie(protocolVersion, cookie); + } + + @Override + int messageType() { + return ht_hello_verify_request; + } + + @Override + int messageLength() { + return 2 + cookie.length; // 2: the length of protocolVersion + } + + @Override + void send(HandshakeOutStream hos) throws IOException { + hos.putInt8(protocolVersion.major); + hos.putInt8(protocolVersion.minor); + hos.putBytes8(cookie); + } + + @Override + void print(PrintStream out) throws IOException { + out.println("*** HelloVerifyRequest"); + if (debug != null && Debug.isOn("verbose")) { + out.println("server_version: " + protocolVersion); + Debug.println(out, "cookie", cookie); + } + } +} /* * ClientHello ... CLIENT --> SERVER @@ -213,22 +286,31 @@ */ static final class ClientHello extends HandshakeMessage { - ProtocolVersion protocolVersion; - RandomCookie clnt_random; - SessionId sessionId; - private CipherSuiteList cipherSuites; - byte[] compression_methods; + ProtocolVersion protocolVersion; + RandomCookie clnt_random; + SessionId sessionId; + byte[] cookie; // DTLS only + private CipherSuiteList cipherSuites; + private final boolean isDTLS; + byte[] compression_methods; HelloExtensions extensions = new HelloExtensions(); private final static byte[] NULL_COMPRESSION = new byte[] {0}; ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, - SessionId sessionId, CipherSuiteList cipherSuites) { + SessionId sessionId, CipherSuiteList cipherSuites, + boolean isDTLS) { + this.isDTLS = isDTLS; this.protocolVersion = protocolVersion; this.sessionId = sessionId; this.cipherSuites = cipherSuites; + if (isDTLS) { + this.cookie = new byte[0]; + } else { + this.cookie = null; + } if (cipherSuites.containsEC()) { extensions.add(SupportedEllipticCurvesExtension.DEFAULT); @@ -239,11 +321,21 @@ compression_methods = NULL_COMPRESSION; } - ClientHello(HandshakeInStream s, int messageLength) throws IOException { + ClientHello(HandshakeInStream s, + int messageLength, boolean isDTLS) throws IOException { + + this.isDTLS = isDTLS; + protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8()); clnt_random = new RandomCookie(s); sessionId = new SessionId(s.getBytes8()); sessionId.checkLength(protocolVersion); + if (isDTLS) { + cookie = s.getBytes8(); + } else { + cookie = null; + } + cipherSuites = new CipherSuiteList(s); compression_methods = s.getBytes8(); if (messageLength() != messageLength) { @@ -279,6 +371,28 @@ extensions.add(signatureAlgorithm); } + void addMFLExtension(int maximumPacketSize) { + HelloExtension maxFragmentLength = + new MaxFragmentLengthExtension(maximumPacketSize); + extensions.add(maxFragmentLength); + } + + void updateHelloCookie(MessageDigest cookieDigest) { + // + // Just use HandshakeOutStream to compute the hello verify cookie. + // Not actually used to output handshake message records. + // + HandshakeOutStream hos = new HandshakeOutStream(null); + + try { + send(hos, false); // Do not count hello verify cookie. + } catch (IOException ioe) { + // unlikely to happen + } + + cookieDigest.update(hos.toByteArray()); + } + @Override int messageType() { return ht_client_hello; } @@ -290,6 +404,7 @@ */ return (2 + 32 + 1 + 2 + 1 + sessionId.length() /* ... + variable parts */ + + (isDTLS ? (1 + cookie.length) : 0) + (cipherSuites.size() * 2) + compression_methods.length) + extensions.length(); @@ -297,13 +412,7 @@ @Override void send(HandshakeOutStream s) throws IOException { - s.putInt8(protocolVersion.major); - s.putInt8(protocolVersion.minor); - clnt_random.send(s); - s.putBytes8(sessionId.getId()); - cipherSuites.send(s); - s.putBytes8(compression_methods); - extensions.send(s); + send(s, true); // Count hello verify cookie. } @Override @@ -317,6 +426,10 @@ s.print("Session ID: "); s.println(sessionId); + if (isDTLS) { + Debug.println(s, "cookie", cookie); + } + s.println("Cipher Suites: " + cipherSuites); Debug.println(s, "Compression Methods", compression_methods); @@ -324,6 +437,21 @@ s.println("***"); } } + + private void send(HandshakeOutStream s, + boolean computeCookie) throws IOException { + s.putInt8(protocolVersion.major); + s.putInt8(protocolVersion.minor); + clnt_random.send(s); + s.putBytes8(sessionId.getId()); + if (isDTLS && computeCookie) { + s.putBytes8(cookie); + } + cipherSuites.send(s); + s.putBytes8(compression_methods); + extensions.send(s); + } + } /* @@ -740,7 +868,7 @@ setValues(obj); Signature sig; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { this.preferableSignatureAlgorithm = signAlgorithm; sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); } else { @@ -801,7 +929,7 @@ new BigInteger(1, dh_g))); // read the signature and hash algorithm - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { int hash = input.getInt8(); // hash algorithm int signature = input.getInt8(); // signature algorithm @@ -834,7 +962,7 @@ Signature sig; String algorithm = publicKey.getAlgorithm(); - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { sig = JsseJce.getSignature( preferableSignatureAlgorithm.getAlgorithmName()); } else { @@ -914,7 +1042,7 @@ temp += dh_Ys.length; if (signature != null) { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { temp += SignatureAndHashAlgorithm.sizeInRecord(); } @@ -934,7 +1062,7 @@ s.putBytes16(dh_Ys); if (signature != null) { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); } @@ -959,7 +1087,7 @@ if (signature == null) { s.println("Anonymous"); } else { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { s.println("Signature Algorithm " + preferableSignatureAlgorithm.getAlgorithmName()); } @@ -1021,7 +1149,7 @@ } Signature sig; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { this.preferableSignatureAlgorithm = signAlgorithm; sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); } else { @@ -1084,7 +1212,7 @@ } // read the signature and hash algorithm - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { int hash = input.getInt8(); // hash algorithm int signature = input.getInt8(); // signature algorithm @@ -1105,7 +1233,7 @@ // verify the signature Signature sig; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { sig = JsseJce.getSignature( preferableSignatureAlgorithm.getAlgorithmName()); } else { @@ -1157,7 +1285,7 @@ int sigLen = 0; if (signatureBytes != null) { sigLen = 2 + signatureBytes.length; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { sigLen += SignatureAndHashAlgorithm.sizeInRecord(); } } @@ -1172,7 +1300,7 @@ s.putBytes8(pointBytes); if (signatureBytes != null) { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); } @@ -1189,7 +1317,7 @@ if (signatureBytes == null) { s.println("Anonymous"); } else { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { s.println("Signature Algorithm " + preferableSignatureAlgorithm.getAlgorithmName()); } @@ -1315,7 +1443,7 @@ this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC; // Use supported_signature_algorithms for TLS 1.2 or later. - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { if (signAlgs == null || signAlgs.isEmpty()) { throw new SSLProtocolException( "No supported signature algorithms"); @@ -1339,7 +1467,7 @@ types = input.getBytes8(); // Read the supported_signature_algorithms for TLS 1.2 or later. - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { algorithmsLen = input.getInt16(); if (algorithmsLen < 2) { throw new SSLProtocolException( @@ -1406,7 +1534,7 @@ int messageLength() { int len = 1 + types.length + 2; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { len += algorithmsLen + 2; } @@ -1423,7 +1551,7 @@ output.putBytes8(types); // put supported_signature_algorithms - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { output.putInt16(algorithmsLen); for (SignatureAndHashAlgorithm algorithm : algorithms) { output.putInt8(algorithm.getHashValue()); // hash @@ -1478,7 +1606,7 @@ } s.println(); - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { StringBuilder sb = new StringBuilder(); boolean opened = false; for (SignatureAndHashAlgorithm signAlg : algorithms) { @@ -1576,7 +1704,7 @@ String algorithm = privateKey.getAlgorithm(); Signature sig = null; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { this.preferableSignatureAlgorithm = signAlgorithm; sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); } else { @@ -1598,7 +1726,7 @@ this.protocolVersion = protocolVersion; // read the signature and hash algorithm - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { int hashAlg = input.getInt8(); // hash algorithm int signAlg = input.getInt8(); // signature algorithm @@ -1634,7 +1762,7 @@ SecretKey masterSecret) throws GeneralSecurityException { String algorithm = publicKey.getAlgorithm(); Signature sig = null; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { sig = JsseJce.getSignature( preferableSignatureAlgorithm.getAlgorithmName()); } else { @@ -1676,11 +1804,11 @@ throws SignatureException { if (algorithm.equals("RSA")) { - if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- + if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1- MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); - if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 + if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3 updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); } @@ -1692,10 +1820,10 @@ sig.update(handshakeHash.getAllHandshakeMessages()); } } else { // DSA, ECDSA - if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- + if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1- MessageDigest shaClone = handshakeHash.getSHAClone(); - if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 + if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3 updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); } @@ -1811,7 +1939,7 @@ int messageLength() { int temp = 2; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { temp += SignatureAndHashAlgorithm.sizeInRecord(); } @@ -1820,7 +1948,7 @@ @Override void send(HandshakeOutStream s) throws IOException { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); } @@ -1833,7 +1961,7 @@ s.println("*** CertificateVerify"); if (debug != null && Debug.isOn("verbose")) { - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (protocolVersion.useTLS12PlusSpec()) { s.println("Signature Algorithm " + preferableSignatureAlgorithm.getAlgorithmName()); } @@ -1899,7 +2027,7 @@ CipherSuite cipherSuite) throws IOException { this.protocolVersion = protocolVersion; this.cipherSuite = cipherSuite; - int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36; + int msgLen = protocolVersion.useTLS10PlusSpec() ? 12 : 36; verifyData = new byte[msgLen]; input.read(verifyData); } @@ -1932,7 +2060,7 @@ throw new RuntimeException("Invalid sender: " + sender); } - if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (protocolVersion.useTLS10PlusSpec()) { // TLS 1.0+ try { byte [] seed; @@ -1940,14 +2068,14 @@ PRF prf; // Get the KeyGenerator alg and calculate the seed. - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { - // TLS 1.2 + if (protocolVersion.useTLS12PlusSpec()) { + // TLS 1.2+ or DTLS 1.2+ seed = handshakeHash.getFinishedHash(); prfAlg = "SunTls12Prf"; prf = cipherSuite.prfAlg; } else { - // TLS 1.0/1.1 + // TLS 1.0/1.1, DTLS 1.0 MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); seed = new byte[36];
--- a/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeOutStream.java Tue Jun 02 04:01:04 2015 +0000 @@ -23,10 +23,9 @@ * questions. */ - package sun.security.ssl; -import java.io.OutputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; /** @@ -40,197 +39,113 @@ * * @author David Brownell */ -public class HandshakeOutStream extends OutputStream { +public class HandshakeOutStream extends ByteArrayOutputStream { - private SSLSocketImpl socket; - private SSLEngineImpl engine; + OutputRecord outputRecord; // May be null if not actually used to + // output handshake message records. - OutputRecord r; - - HandshakeOutStream(ProtocolVersion protocolVersion, - ProtocolVersion helloVersion, HandshakeHash handshakeHash, - SSLSocketImpl socket) { - this.socket = socket; - r = new OutputRecord(Record.ct_handshake); - init(protocolVersion, helloVersion, handshakeHash); + HandshakeOutStream(OutputRecord outputRecord) { + super(); + this.outputRecord = outputRecord; } - HandshakeOutStream(ProtocolVersion protocolVersion, - ProtocolVersion helloVersion, HandshakeHash handshakeHash, - SSLEngineImpl engine) { - this.engine = engine; - r = new EngineOutputRecord(Record.ct_handshake, engine); - init(protocolVersion, helloVersion, handshakeHash); + // Complete a handshakin message writing. Called by HandshakeMessage. + void complete() throws IOException { + if (size() < 4) { // 4: handshake message header size + // internal_error alert will be triggered + throw new RuntimeException("handshake message is not available"); + } + + // outputRecord cannot be null + outputRecord.encodeHandshake(buf, 0, count); + + // reset the byte array output stream + reset(); } - private void init(ProtocolVersion protocolVersion, - ProtocolVersion helloVersion, HandshakeHash handshakeHash) { - r.setVersion(protocolVersion); - r.setHelloVersion(helloVersion); - r.setHandshakeHash(handshakeHash); - } + // + // overridden ByteArrayOutputStream methods + // - - /* - * Update the handshake data hashes ... mostly for use after a - * client cert has been sent, so the cert verify message can be - * constructed correctly yet without forcing extra I/O. In all - * other cases, automatic hash calculation suffices. - */ - void doHashes() { - r.doHashes(); - } - - /* - * Write some data out onto the stream ... buffers as much as possible. - * Hashes are updated automatically if something gets flushed to the - * network (e.g. a big cert message etc). - */ @Override - public void write(byte buf[], int off, int len) throws IOException { - while (len > 0) { - int howmuch = Math.min(len, r.availableDataBytes()); - - if (howmuch == 0) { - flush(); - } else { - r.write(buf, off, howmuch); - off += howmuch; - len -= howmuch; - } - } - } - - /* - * write-a-byte - */ - @Override - public void write(int i) throws IOException { - if (r.availableDataBytes() < 1) { - flush(); - } - r.write(i); + public void write(byte[] b, int off, int len) { + // The maximum fragment size is 24 bytes. + checkOverflow(len, Record.OVERFLOW_OF_INT24); + super.write(b, off, len); } @Override public void flush() throws IOException { - if (socket != null) { - try { - socket.writeRecord(r); - } catch (IOException e) { - // Had problems writing; check if there was an - // alert from peer. If alert received, waitForClose - // will throw an exception for the alert - socket.waitForClose(true); - - // No alert was received, just rethrow exception - throw e; - } - } else { // engine != null - /* - * Even if record might be empty, flush anyway in case - * there is a finished handshake message that we need - * to queue. - */ - engine.writeRecord((EngineOutputRecord)r); - } + outputRecord.flush(); } - /* - * Tell the OutputRecord that a finished message was - * contained either in this record or the one immeiately - * preceding it. We need to reliably pass back notifications - * that a finish message occurred. - */ - void setFinishedMsg() { - assert(socket == null); - - ((EngineOutputRecord)r).setFinishedMsg(); - } + // + // handshake output stream management functions + // /* * Put integers encoded in standard 8, 16, 24, and 32 bit * big endian formats. Note that OutputStream.write(int) only * writes the least significant 8 bits and ignores the rest. */ - void putInt8(int i) throws IOException { checkOverflow(i, Record.OVERFLOW_OF_INT08); - r.write(i); + super.write(i); } void putInt16(int i) throws IOException { checkOverflow(i, Record.OVERFLOW_OF_INT16); - if (r.availableDataBytes() < 2) { - flush(); - } - r.write(i >> 8); - r.write(i); + super.write(i >> 8); + super.write(i); } void putInt24(int i) throws IOException { checkOverflow(i, Record.OVERFLOW_OF_INT24); - if (r.availableDataBytes() < 3) { - flush(); - } - r.write(i >> 16); - r.write(i >> 8); - r.write(i); - } - - void putInt32(int i) throws IOException { - if (r.availableDataBytes() < 4) { - flush(); - } - r.write(i >> 24); - r.write(i >> 16); - r.write(i >> 8); - r.write(i); + super.write(i >> 16); + super.write(i >> 8); + super.write(i); } /* * Put byte arrays with length encoded as 8, 16, 24 bit * integers in big-endian format. */ - void putBytes8(byte b[]) throws IOException { + void putBytes8(byte[] b) throws IOException { if (b == null) { putInt8(0); - return; } else { - checkOverflow(b.length, Record.OVERFLOW_OF_INT08); + putInt8(b.length); + super.write(b, 0, b.length); } - putInt8(b.length); - write(b, 0, b.length); } public void putBytes16(byte b[]) throws IOException { if (b == null) { putInt16(0); - return; } else { - checkOverflow(b.length, Record.OVERFLOW_OF_INT16); + putInt16(b.length); + super.write(b, 0, b.length); } - putInt16(b.length); - write(b, 0, b.length); } void putBytes24(byte b[]) throws IOException { if (b == null) { putInt24(0); - return; } else { - checkOverflow(b.length, Record.OVERFLOW_OF_INT24); + putInt24(b.length); + super.write(b, 0, b.length); } - putInt24(b.length); - write(b, 0, b.length); } - private void checkOverflow(int length, int overflow) { - if (length >= overflow) { + /* + * Does the specified length overflow the limitation? + */ + private static void checkOverflow(int length, int limit) { + if (length >= limit) { // internal_error alert will be triggered throw new RuntimeException( "Field length overflow, the field length (" + - length + ") should be less than " + overflow); + length + ") should be less than " + limit); } } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HandshakeStateManager.java Tue Jun 02 04:01:04 2015 +0000 @@ -0,0 +1,925 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.LinkedList; +import java.util.HashMap; +import javax.net.ssl.SSLProtocolException; + +import sun.security.ssl.HandshakeMessage.*; + +import static sun.security.ssl.CipherSuite.KeyExchange; +import static sun.security.ssl.CipherSuite.KeyExchange.*; +import static sun.security.ssl.HandshakeStateManager.HandshakeState.*; +import static sun.security.ssl.HandshakeMessage.*; + +/* + * Handshake state manager. + * + * Messages flow for a full handshake: + * + * - - + * | HelloRequest (No.0, RFC 5246) [*] | + * | <-------------------------------------------- | + * | | + * | ClientHello (No.1, RFC 5246) | + * | --------------------------------------------> | + * | | + * | - HelloVerifyRequest (No.3, RFC 6347) - | + * | D | <-------------------------------------------- | D | + * | T | | T | + * | L | ClientHello (No.1, RFC 5246) | L | + * | S | --------------------------------------------> | S | + * | - - | + * | | + * C | ServerHello (No.2, RFC 5246) | S + * L | SupplementalData (No.23, RFC4680) [*] | E + * I | Certificate (No.11, RFC 5246) [*] | R + * E | CertificateStatus (No.22, RFC 6066) [*] | V + * N | ServerKeyExchange (No.12, RFC 5246) [*] | E + * T | CertificateRequest (No.13, RFC 5246) [*] | R + * | ServerHelloDone (No.14, RFC 5246) | + * | <-------------------------------------------- | + * | | + * | SupplementalData (No.23, RFC4680) [*] | + * | Certificate (No.11, RFC 5246) [*] Or | + * | CertificateURL (No.21, RFC6066) [*] | + * | ClientKeyExchange (No.16, RFC 5246) | + * | CertificateVerify (No.15, RFC 5246) [*] | + * | [ChangeCipherSpec] (RFC 5246) | + * | Finished (No.20, RFC 5246) | + * | --------------------------------------------> | + * | | + * | NewSessionTicket (No.4, RFC4507) [*] | + * | [ChangeCipherSpec] (RFC 5246) | + * | Finished (No.20, RFC 5246) | + * | <-------------------------------------------- | + * - - + * [*] Indicates optional or situation-dependent messages that are not + * always sent. + * + * Message flow for an abbreviated handshake: + * - - + * | ClientHello (No.1, RFC 5246) | + * | --------------------------------------------> | + * | | + * C | ServerHello (No.2, RFC 5246) | S + * L | NewSessionTicket (No.4, RFC4507) [*] | E + * I | [ChangeCipherSpec] (RFC 5246) | R + * E | Finished (No.20, RFC 5246) | V + * N | <-------------------------------------------- | E + * T | | R + * | [ChangeCipherSpec] (RFC 5246) | + * | Finished (No.20, RFC 5246) | + * | --------------------------------------------> | + * - - + * + * + * State machine of handshake states: + * + * +--------------+ + * START -----> | HelloRequest | + * | +--------------+ + * | | + * v v + * +---------------------+ --> +---------------------+ + * | ClientHello | | HelloVerifyRequest | + * +---------------------+ <-- +---------------------+ + * | + * | + * ========================================================================= + * | + * v + * +---------------------+ + * | ServerHello | ----------------------------------+------+ + * +---------------------+ --> +-------------------------+ | | + * | | Server SupplementalData | | | + * | +-------------------------+ | | + * | | | | + * v v | | + * +---------------------+ | | + * +---- | Server Certificate | | | + * | +---------------------+ | | + * | | | | + * | | +--------------------+ | | + * | +-> | CertificateStatus | | | + * | | +--------------------+ v | + * | | | | +--------------------+ | + * | v v +--> | ServerKeyExchange | | + * | +---------------------+ | +--------------------+ | + * | | CertificateRequest | | | | + * | +---------------------+ <-+---------+ | + * | | | | | + * v v | | | + * +---------------------+ <-------+ | | + * | ServerHelloDone | <-----------------+ | + * +---------------------+ | + * | | | + * | | | + * | | | + * ========================================================================= + * | | | + * | v | + * | +-------------------------+ | + * | | Client SupplementalData | --------------+ | + * | +-------------------------+ | | + * | | | | + * | v | | + * | +--------------------+ | | + * +-> | Client Certificate | ALT. | | + * | +--------------------+----------------+ | | + * | | CertificateURL | | | + * | +----------------+ | | + * v | | + * +-------------------+ <------------------------+ | + * | ClientKeyExchange | | + * +-------------------+ | + * | | | + * | v | + * | +-------------------+ | + * | | CertificateVerify | | + * | +-------------------+ | + * | | | + * v v | + * +-------------------------+ | + * | Client ChangeCipherSpec | <---------------+ | + * +-------------------------+ | | + * | | | + * v | | + * +-----------------+ (abbreviated) | | + * | Client Finished | -------------> END | | + * +-----------------+ (Abbreviated handshake) | | + * | | | + * | (full) | | + * | | | + * ================================ | | + * | | | + * | ================================ + * | | | + * v | | + * +------------------+ | (abbreviated) | + * | NewSessionTicket | <--------------------------------+ + * +------------------+ | | + * | | | + * v | | + * +-------------------------+ | (abbreviated) | + * | Server ChangeCipherSpec | <-------------------------------------+ + * +-------------------------+ | + * | | + * v | + * +-----------------+ (abbreviated) | + * | Server Finished | -------------------------+ + * +-----------------+ + * | (full) + * v + * END (Full handshake) + * + * + * The scenarios of the use of this class: + * 1. Create an instance of HandshakeStateManager during the initializtion + * handshake. + * 2. If receiving a handshake message, call HandshakeStateManager.check() + * to make sure that the message is of the expected handshake type. And + * then call HandshakeStateManager.update() in case handshake states may + * be impacted by this new incoming handshake message. + * 3. On delivering a handshake message, call HandshakeStateManager.update() + * in case handshake states may by thie new outgoing handshake message. + * 4. On receiving and delivering ChangeCipherSpec message, call + * HandshakeStateManager.changeCipherSpec() to check the present sequence + * of this message, and update the states if necessary. + */ +final class HandshakeStateManager { + // upcoming handshake states. + private LinkedList<HandshakeState> upcomingStates; + private LinkedList<HandshakeState> alternatives; + + private boolean isDTLS; + + private final static boolean debugIsOn; + + private final static HashMap<Byte, String> handshakeTypes; + + static { + debugIsOn = (Handshaker.debug != null) && + Debug.isOn("handshake") && Debug.isOn("verbose"); + handshakeTypes = new HashMap<>(15); + + handshakeTypes.put(ht_hello_request, "hello_request"); + handshakeTypes.put(ht_client_hello, "client_hello"); + handshakeTypes.put(ht_server_hello, "server_hello"); + handshakeTypes.put(ht_hello_verify_request, "hello_verify_request"); + handshakeTypes.put(ht_new_session_ticket, "session_ticket"); + handshakeTypes.put(ht_certificate, "certificate"); + handshakeTypes.put(ht_server_key_exchange, "server_key_exchange"); + handshakeTypes.put(ht_certificate_request, "certificate_request"); + handshakeTypes.put(ht_server_hello_done, "server_hello_done"); + handshakeTypes.put(ht_certificate_verify, "certificate_verify"); + handshakeTypes.put(ht_client_key_exchange, "client_key_exchange"); + handshakeTypes.put(ht_finished, "finished"); + handshakeTypes.put(ht_certificate_url, "certificate_url"); + handshakeTypes.put(ht_certificate_status, "certificate_status"); + handshakeTypes.put(ht_supplemental_data, "supplemental_data"); + } + + HandshakeStateManager(boolean isDTLS) { + this.upcomingStates = new LinkedList<>(); + this.alternatives = new LinkedList<>(); + this.isDTLS = isDTLS; + } + + // + // enumation of handshake type + // + static enum HandshakeState { + HS_HELLO_REQUEST( + "hello_request", + HandshakeMessage.ht_hello_request), + HS_CLIENT_HELLO( + "client_hello", + HandshakeMessage.ht_client_hello), + HS_HELLO_VERIFY_REQUEST( + "hello_verify_request", + HandshakeMessage.ht_hello_verify_request), + HS_SERVER_HELLO( + "server_hello", + HandshakeMessage.ht_server_hello), + HS_SERVER_SUPPLEMENTAL_DATA( + "server supplemental_data", + HandshakeMessage.ht_supplemental_data, true), + HS_SERVER_CERTIFICATE( + "server certificate", + HandshakeMessage.ht_certificate), + HS_CERTIFICATE_STATUS( + "certificate_status", + HandshakeMessage.ht_certificate_status, true), + HS_SERVER_KEY_EXCHANGE( + "server_key_exchange", + HandshakeMessage.ht_server_key_exchange, true), + HS_CERTIFICATE_REQUEST( + "certificate_request", + HandshakeMessage.ht_certificate_request, true), + HS_SERVER_HELLO_DONE( + "server_hello_done", + HandshakeMessage.ht_server_hello_done), + HS_CLIENT_SUPPLEMENTAL_DATA( + "client supplemental_data", + HandshakeMessage.ht_supplemental_data, true), + HS_CLIENT_CERTIFICATE( + "client certificate", + HandshakeMessage.ht_certificate, true), + HS_CERTIFICATE_URL( + "certificate_url", + HandshakeMessage.ht_certificate_url, true), + HS_CLIENT_KEY_EXCHANGE( + "client_key_exchange", + HandshakeMessage.ht_client_key_exchange), + HS_CERTIFICATE_VERIFY( + "certificate_verify", + HandshakeMessage.ht_certificate_verify, true), + HS_CLIENT_CHANGE_CIPHER_SPEC( + "client change_cipher_spec", + HandshakeMessage.ht_not_applicable), + HS_CLEINT_FINISHED( + "client finished", + HandshakeMessage.ht_finished), + HS_NEW_SESSION_TICKET( + "session_ticket", + HandshakeMessage.ht_new_session_ticket), + HS_SERVER_CHANGE_CIPHER_SPEC( + "server change_cipher_spec", + HandshakeMessage.ht_not_applicable), + HS_SERVER_FINISHDE( + "server finished", + HandshakeMessage.ht_finished); + + final String description; + final byte handshakeType; + final boolean isOptional; + + HandshakeState(String description, byte handshakeType) { + this.description = description; + this.handshakeType = handshakeType; + this.isOptional = false; + } + + HandshakeState(String description, + byte handshakeType, boolean isOptional) { + + this.description = description; + this.handshakeType = handshakeType; + this.isOptional = isOptional; + } + + public String toString() { + return description + "[" + handshakeType + "]" + + (isOptional ? "(optional)" : ""); + } + } + + boolean isEmpty() { + return upcomingStates.isEmpty(); + } + + void check(byte handshakeType) throws SSLProtocolException { + String exceptionMsg = + "Handshake message sequence violation, " + handshakeType; + + if (debugIsOn) { + System.out.println( + "check handshake state: " + toString(handshakeType)); + } + + if (upcomingStates.isEmpty()) { + // Is it a kickstart message? + if ((handshakeType != HandshakeMessage.ht_hello_request) && + (handshakeType != HandshakeMessage.ht_client_hello)) { + + throw new SSLProtocolException( + "Handshake message sequence violation, " + handshakeType); + } + + // It is a kickstart message. + return; + } + + // Ignore the checking for HelloRequest messages as they are + // may be sent by the server at any time. + if (handshakeType == HandshakeMessage.ht_hello_request) { + return; + } + + for (HandshakeState handshakeState : upcomingStates) { + if (handshakeState.handshakeType == handshakeType) { + // It's the expected next handshake type. + return; + } + + if (handshakeState.isOptional) { + continue; + } else { + for (HandshakeState alternative : alternatives) { + if (alternative.handshakeType == handshakeType) { + return; + } + + if (alternative.isOptional) { + continue; + } else { + throw new SSLProtocolException(exceptionMsg); + } + } + } + + throw new SSLProtocolException(exceptionMsg); + } + + // Not an expected Handshake message. + throw new SSLProtocolException( + "Handshake message sequence violation, " + handshakeType); + } + + void update(HandshakeMessage handshakeMessage, + boolean isAbbreviated) throws SSLProtocolException { + + byte handshakeType = (byte)handshakeMessage.messageType(); + String exceptionMsg = + "Handshake message sequence violation, " + handshakeType; + + if (debugIsOn) { + System.out.println( + "update handshake state: " + toString(handshakeType)); + } + + boolean hasPresentState = false; + switch (handshakeType) { + case HandshakeMessage.ht_hello_request: + // + // State machine: + // PRESENT: START + // TO : ClientHello + // + + // No old state to update. + + // Add the upcoming states. + if (!upcomingStates.isEmpty()) { + // A ClientHello message should be followed. + upcomingStates.add(HS_CLIENT_HELLO); + + } // Otherwise, ignore this HelloRequest message. + + break; + + case HandshakeMessage.ht_client_hello: + // + // State machine: + // PRESENT: START + // HS_CLIENT_HELLO + // TO : HS_HELLO_VERIFY_REQUEST (DTLS) + // HS_SERVER_HELLO + // + + // Check and update the present state. + if (!upcomingStates.isEmpty()) { + // The current state should be HS_CLIENT_HELLO. + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState != HS_CLIENT_HELLO) { + throw new SSLProtocolException(exceptionMsg); + } + } + + // Add the upcoming states. + ClientHello clientHello = (ClientHello)handshakeMessage; + if (isDTLS) { + // Is it an initial ClientHello message? + if (clientHello.cookie == null || + clientHello.cookie.length == 0) { + // Is it an abbreviated handshake? + if (clientHello.sessionId.length() != 0) { + // A HelloVerifyRequest message or a ServerHello + // message may follow the abbreviated session + // resuming handshake request. + upcomingStates.add(HS_HELLO_VERIFY_REQUEST); + alternatives.add(HS_SERVER_HELLO); + } else { + // A HelloVerifyRequest message should follow + // the initial ClientHello message. + upcomingStates.add(HS_HELLO_VERIFY_REQUEST); + } + } else { + // A HelloVerifyRequest may be followed if the cookie + // cannot be verified. + upcomingStates.add(HS_SERVER_HELLO); + alternatives.add(HS_HELLO_VERIFY_REQUEST); + } + } else { + upcomingStates.add(HS_SERVER_HELLO); + } + + break; + + case HandshakeMessage.ht_hello_verify_request: + // + // State machine: + // PRESENT: HS_HELLO_VERIFY_REQUEST + // TO : HS_CLIENT_HELLO + // + // Note that this state may have an alternative option. + + // Check and update the present state. + if (!upcomingStates.isEmpty()) { + // The current state should be HS_HELLO_VERIFY_REQUEST. + HandshakeState handshakeState = upcomingStates.pop(); + HandshakeState alternative = null; + if (!alternatives.isEmpty()) { + alternative = alternatives.pop(); + } + + if ((handshakeState != HS_HELLO_VERIFY_REQUEST) && + (alternative != HS_HELLO_VERIFY_REQUEST)) { + + throw new SSLProtocolException(exceptionMsg); + } + } else { + // No present state. + throw new SSLProtocolException(exceptionMsg); + } + + // Add the upcoming states. + upcomingStates.add(HS_CLIENT_HELLO); + + break; + + case HandshakeMessage.ht_server_hello: + // + // State machine: + // PRESENT: HS_SERVER_HELLO + // TO : + // Full handshake state stacks + // (ServerHello Flight) + // HS_SERVER_SUPPLEMENTAL_DATA [optional] + // --> HS_SERVER_CERTIFICATE [optional] + // --> HS_CERTIFICATE_STATUS [optional] + // --> HS_SERVER_KEY_EXCHANGE [optional] + // --> HS_CERTIFICATE_REQUEST [optional] + // --> HS_SERVER_HELLO_DONE + // (Client ClientKeyExchange Flight) + // --> HS_CLIENT_SUPPLEMENTAL_DATA [optional] + // --> HS_CLIENT_CERTIFICATE or + // HS_CERTIFICATE_URL + // --> HS_CLIENT_KEY_EXCHANGE + // --> HS_CERTIFICATE_VERIFY [optional] + // --> HS_CLIENT_CHANGE_CIPHER_SPEC + // --> HS_CLEINT_FINISHED + // (Server Finished Flight) + // --> HS_CLIENT_SUPPLEMENTAL_DATA [optional] + // + // Abbreviated handshake state stacks + // (Server Finished Flight) + // HS_NEW_SESSION_TICKET + // --> HS_SERVER_CHANGE_CIPHER_SPEC + // --> HS_SERVER_FINISHDE + // (Client Finished Flight) + // --> HS_CLIENT_CHANGE_CIPHER_SPEC + // --> HS_CLEINT_FINISHED + // + // Note that this state may have an alternative option. + + // Check and update the present state. + if (!upcomingStates.isEmpty()) { + // The current state should be HS_SERVER_HELLO + HandshakeState handshakeState = upcomingStates.pop(); + HandshakeState alternative = null; + if (!alternatives.isEmpty()) { + alternative = alternatives.pop(); + } + + if ((handshakeState != HS_SERVER_HELLO) && + (alternative != HS_SERVER_HELLO)) { + + throw new SSLProtocolException(exceptionMsg); + } + } else { + // No present state. + throw new SSLProtocolException(exceptionMsg); + } + + // Add the upcoming states. + ServerHello serverHello = (ServerHello)handshakeMessage; + HelloExtensions hes = serverHello.extensions; + + + // Not support SessionTicket extension yet. + // + // boolean hasSessionTicketExt = + // (hes.get(HandshakeMessage.ht_new_session_ticket) != null); + + if (isAbbreviated) { + // Not support SessionTicket extension yet. + // + // // Mandatory NewSessionTicket message + // if (hasSessionTicketExt) { + // upcomingStates.add(HS_NEW_SESSION_TICKET); + // } + + // Mandatory server ChangeCipherSpec and Finished messages + upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_SERVER_FINISHDE); + + // Mandatory client ChangeCipherSpec and Finished messages + upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_CLEINT_FINISHED); + } else { + // Not support SupplementalData extension yet. + // + // boolean hasSupplementalDataExt = + // (hes.get(HandshakeMessage.ht_supplemental_data) != null); + + // Not support CertificateStatus extension yet. + // + // boolean hasCertificateStatusExt = + // (hes.get(HandshakeMessage.ht_certificate_status) != null); + + // Not support CertificateURL extension yet. + // + // boolean hasCertificateUrlExt = + // (hes.get(HandshakeMessage.ht_certificate_url) != null); + + // Not support SupplementalData extension yet. + // + // // Optional SupplementalData message + // if (hasSupplementalDataExt) { + // upcomingStates.add(HS_SERVER_SUPPLEMENTAL_DATA); + // } + + // Need server Certificate message or not? + KeyExchange keyExchange = serverHello.cipherSuite.keyExchange; + if ((keyExchange != K_KRB5) && + (keyExchange != K_KRB5_EXPORT) && + (keyExchange != K_DH_ANON) && + (keyExchange != K_ECDH_ANON)) { + // Mandatory Certificate message + upcomingStates.add(HS_SERVER_CERTIFICATE); + } + + // Not support CertificateStatus extension yet. + // + // // Optional CertificateStatus message + // if (hasCertificateStatusExt) { + // upcomingStates.add(HS_CERTIFICATE_STATUS); + // } + + // Need ServerKeyExchange message or not? + if ((keyExchange == K_RSA_EXPORT) || + (keyExchange == K_DHE_RSA) || + (keyExchange == K_DHE_DSS) || + (keyExchange == K_DH_ANON) || + (keyExchange == K_ECDHE_RSA) || + (keyExchange == K_ECDHE_ECDSA) || + (keyExchange == K_ECDH_ANON)) { + // Optional ServerKeyExchange message + upcomingStates.add(HS_SERVER_KEY_EXCHANGE); + } + + // Optional CertificateRequest message + upcomingStates.add(HS_CERTIFICATE_REQUEST); + + // Mandatory ServerHelloDone message + upcomingStates.add(HS_SERVER_HELLO_DONE); + + // Not support SupplementalData extension yet. + // + // // Optional SupplementalData message + // if (hasSupplementalDataExt) { + // upcomingStates.add(HS_CLIENT_SUPPLEMENTAL_DATA); + // } + + // Optional client Certificate message + upcomingStates.add(HS_CLIENT_CERTIFICATE); + + // Not support CertificateURL extension yet. + // + // // Alternative CertificateURL message, optional too. + // // + // // Please put CertificateURL rather than Certificate + // // message in the alternatives list. So that we can + // // simplify the process of this alternative pair later. + // if (hasCertificateUrlExt) { + // alternatives.add(HS_CERTIFICATE_URL); + // } + + // Mandatory ClientKeyExchange message + upcomingStates.add(HS_CLIENT_KEY_EXCHANGE); + + // Optional CertificateVerify message + upcomingStates.add(HS_CERTIFICATE_VERIFY); + + // Mandatory client ChangeCipherSpec and Finished messages + upcomingStates.add(HS_CLIENT_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_CLEINT_FINISHED); + + // Not support SessionTicket extension yet. + // + // // Mandatory NewSessionTicket message + // if (hasSessionTicketExt) { + // upcomingStates.add(HS_NEW_SESSION_TICKET); + // } + + // Mandatory server ChangeCipherSpec and Finished messages + upcomingStates.add(HS_SERVER_CHANGE_CIPHER_SPEC); + upcomingStates.add(HS_SERVER_FINISHDE); + } + + break; + + case HandshakeMessage.ht_certificate: + // + // State machine: + // PRESENT: HS_CERTIFICATE_URL or + // HS_CLIENT_CERTIFICATE + // TO : HS_CLIENT_KEY_EXCHANGE + // + // Or + // + // PRESENT: HS_SERVER_CERTIFICATE + // TO : HS_CERTIFICATE_STATUS [optional] + // HS_SERVER_KEY_EXCHANGE [optional] + // HS_CERTIFICATE_REQUEST [optional] + // HS_SERVER_HELLO_DONE + // + // Note that this state may have an alternative option. + + // Check and update the present state. + while (!upcomingStates.isEmpty()) { + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState.handshakeType == handshakeType) { + hasPresentState = true; + + // The current state should be HS_CLIENT_CERTIFICATE or + // HS_SERVER_CERTIFICATE. + // + // Note that we won't put HS_CLIENT_CERTIFICATE into + // the alternative list. + if ((handshakeState != HS_CLIENT_CERTIFICATE) && + (handshakeState != HS_SERVER_CERTIFICATE)) { + throw new SSLProtocolException(exceptionMsg); + } + + // Is it an expected client Certificate message? + boolean isClientMessage = false; + if (!upcomingStates.isEmpty()) { + // If the next expected message is ClientKeyExchange, + // this one should be an expected client Certificate + // message. + HandshakeState nextState = upcomingStates.getFirst(); + if (nextState == HS_CLIENT_KEY_EXCHANGE) { + isClientMessage = true; + } + } + + if (isClientMessage) { + if (handshakeState != HS_CLIENT_CERTIFICATE) { + throw new SSLProtocolException(exceptionMsg); + } + + // Not support CertificateURL extension yet. + /******************************************* + // clear up the alternatives list + if (!alternatives.isEmpty()) { + HandshakeState alternative = alternatives.pop(); + + if (alternative != HS_CERTIFICATE_URL) { + throw new SSLProtocolException(exceptionMsg); + } + } + ********************************************/ + } else { + if ((handshakeState != HS_SERVER_CERTIFICATE)) { + throw new SSLProtocolException(exceptionMsg); + } + } + + break; + } else if (!handshakeState.isOptional) { + throw new SSLProtocolException(exceptionMsg); + } // Otherwise, looking for next state track. + } + + // No present state. + if (!hasPresentState) { + throw new SSLProtocolException(exceptionMsg); + } + + // no new upcoming states. + + break; + + // Not support CertificateURL extension yet. + /*************************************************/ + case HandshakeMessage.ht_certificate_url: + // + // State machine: + // PRESENT: HS_CERTIFICATE_URL or + // HS_CLIENT_CERTIFICATE + // TO : HS_CLIENT_KEY_EXCHANGE + // + // Note that this state may have an alternative option. + + // Check and update the present state. + while (!upcomingStates.isEmpty()) { + // The current state should be HS_CLIENT_CERTIFICATE. + // + // Note that we won't put HS_CLIENT_CERTIFICATE into + // the alternative list. + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState.handshakeType == + HS_CLIENT_CERTIFICATE.handshakeType) { + hasPresentState = true; + + // Look for HS_CERTIFICATE_URL state track. + if (!alternatives.isEmpty()) { + HandshakeState alternative = alternatives.pop(); + + if (alternative != HS_CERTIFICATE_URL) { + throw new SSLProtocolException(exceptionMsg); + } + } else { + // No alternative CertificateUR state track. + throw new SSLProtocolException(exceptionMsg); + } + + if ((handshakeState != HS_CLIENT_CERTIFICATE)) { + throw new SSLProtocolException(exceptionMsg); + } + + break; + } else if (!handshakeState.isOptional) { + throw new SSLProtocolException(exceptionMsg); + } // Otherwise, looking for next state track. + + } + + // No present state. + if (!hasPresentState) { + // No present state. + throw new SSLProtocolException(exceptionMsg); + } + + // no new upcoming states. + + break; + /*************************************************/ + + default: + // Check and update the present state. + while (!upcomingStates.isEmpty()) { + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState.handshakeType == handshakeType) { + hasPresentState = true; + break; + } else if (!handshakeState.isOptional) { + throw new SSLProtocolException(exceptionMsg); + } // Otherwise, looking for next state track. + } + + // No present state. + if (!hasPresentState) { + throw new SSLProtocolException(exceptionMsg); + } + + // no new upcoming states. + } + + if (debugIsOn) { + for (HandshakeState handshakeState : upcomingStates) { + System.out.println( + "upcoming handshake states: " + handshakeState); + } + for (HandshakeState handshakeState : alternatives) { + System.out.println( + "upcoming handshake alternative state: " + handshakeState); + } + } + } + + void changeCipherSpec(boolean isInput, + boolean isClient) throws SSLProtocolException { + + if (debugIsOn) { + System.out.println( + "update handshake state: change_cipher_spec"); + } + + String exceptionMsg = "ChangeCipherSpec message sequence violation"; + + HandshakeState expectedState; + if ((isClient && isInput) || (!isClient && !isInput)) { + expectedState = HS_SERVER_CHANGE_CIPHER_SPEC; + } else { + expectedState = HS_CLIENT_CHANGE_CIPHER_SPEC; + } + + boolean hasPresentState = false; + + // Check and update the present state. + while (!upcomingStates.isEmpty()) { + HandshakeState handshakeState = upcomingStates.pop(); + if (handshakeState == expectedState) { + hasPresentState = true; + break; + } else if (!handshakeState.isOptional) { + throw new SSLProtocolException(exceptionMsg); + } // Otherwise, looking for next state track. + } + + // No present state. + if (!hasPresentState) { + throw new SSLProtocolException(exceptionMsg); + } + + // no new upcoming states. + + if (debugIsOn) { + for (HandshakeState handshakeState : upcomingStates) { + System.out.println( + "upcoming handshake states: " + handshakeState); + } + for (HandshakeState handshakeState : alternatives) { + System.out.println( + "upcoming handshake alternative state: " + handshakeState); + } + } + } + + private static String toString(byte handshakeType) { + String s = handshakeTypes.get(handshakeType); + if (s == null) { + s = "unknown"; + } + return (s + "[" + handshakeType + "]"); + } +} +
--- a/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java Mon Jun 01 10:29:06 2015 -0400 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/Handshaker.java Tue Jun 02 04:01:04 2015 +0000 @@ -29,6 +29,7 @@ import java.io.*; import java.util.*; import java.security.*; +import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.AccessController; import java.security.AlgorithmConstraints; @@ -83,7 +84,7 @@ private CipherSuiteList enabledCipherSuites; // The endpoint identification protocol - String identificationProtocol; + String identificationProtocol; // The cryptographic algorithm constraints private AlgorithmConstraints algorithmConstraints = null; @@ -109,12 +110,15 @@ * Active cipher suites is a subset of enabled cipher suites, and will * contain only those cipher suites available for the active protocols. */ - private CipherSuiteList activeCipherSuites; + private CipherSuiteList activeCipherSuites; // The server name indication and matchers List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList(); Collection<SNIMatcher> sniMatchers = Collections.<SNIMatcher>emptyList(); + // The maximum expected network packet size for SSL/TLS/DTLS records. + int maximumPacketSize = 0; + private boolean isClient; private boolean needCertVerify; @@ -124,11 +128,16 @@ HandshakeHash handshakeHash; HandshakeInStream input; HandshakeOutStream output; - int state; SSLContextImpl sslContext; RandomCookie clnt_random, svr_random; SSLSessionImpl session; + HandshakeStateManager handshakeState; + boolean clientHelloDelivered; + boolean serverHelloRequested; + boolean handshakeActivated; + boolean handshakeFinished; + // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL CipherSuite cipherSuite; @@ -141,10 +150,6 @@ // True if it's OK to start a new SSL session boolean enableNewSession; - // True if session keys have been calculated and the caller may receive - // and process a ChangeCipherSpec message - private boolean sessKeysCalculated; - // Whether local cipher suites preference should be honored during // handshaking? // @@ -207,12 +212,18 @@ // need to dispose the object when it is invalidated boolean invalidated; + /* + * Is this an instance for Datagram Transport Layer Security (DTLS)? + */ + final boolean isDTLS; + Handshaker(SSLSocketImpl c, SSLContextImpl context, ProtocolList enabledProtocols, boolean needCertVerify, boolean isClient, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, byte[] clientVerifyData, byte[] serverVerifyData) { this.conn = c; + this.isDTLS = false; init(context, enabledProtocols, needCertVerify, isClient, activeProtocolVersion, isInitialHandshake, secureRenegotiation, clientVerifyData, serverVerifyData); @@ -222,8 +233,10 @@ ProtocolList enabledProtocols, boolean needCertVerify, boolean isClient, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, boolean secureRenegotiation, - byte[] clientVerifyData, byte[] serverVerifyData) { + byte[] clientVerifyData, byte[] serverVerifyData, + boolean isDTLS) { this.engine = engine; + this.isDTLS = isDTLS; init(context, enabledProtocols, needCertVerify, isClient, activeProtocolVersion, isInitialHandshake, secureRenegotiation, clientVerifyData, serverVerifyData); @@ -251,9 +264,13 @@ this.secureRenegotiation = secureRenegotiation; this.clientVerifyData = clientVerifyData; this.serverVerifyData = serverVerifyData; - enableNewSession = true; - invalidated = false; - sessKeysCalculated = false; + this.enableNewSession = true; + this.invalidated = false; + this.handshakeState = new HandshakeStateManager(isDTLS); + this.clientHelloDelivered = false; + this.serverHelloRequested = false; + this.handshakeActivated = false; + this.handshakeFinished = false; setCipherSuite(CipherSuite.C_NULL); setEnabledProtocols(enabledProtocols); @@ -263,22 +280,6 @@ } else { // engine != null algorithmConstraints = new SSLAlgorithmConstraints(engine, true); } - - - // - // In addition to the connection state machine, controlling - // how the connection deals with the different sorts of records - // that get sent (notably handshake transitions!), there's - // also a handshaking state machine that controls message - // sequencing. - // - // It's a convenient artifact of the protocol that this can, - // with only a couple of minor exceptions, be driven by the - // type constant for the last message seen: except for the - // client's cert verify, those constants are in a convenient - // order to drastically simplify state machine checking. - // - state = -2; // initialized but not activated } /* @@ -360,14 +361,6 @@ } } - final boolean receivedChangeCipherSpec() { - if (conn != null) { - return conn.receivedChangeCipherSpec(); - } else { - return engine.receivedChangeCipherSpec(); - } - } - String getEndpointIdentificationAlgorithmSE() { SSLParameters paras; if (conn != null) { @@ -395,8 +388,6 @@ void setVersion(ProtocolVersion protocolVersion) { this.protocolVersion = protocolVersion; setVersionSE(protocolVersion); - - output.r.setVersion(protocolVersion); } /** @@ -483,6 +474,13 @@ } /** + * Sets the maximum packet size of the handshaking. + */ + void setMaximumPacketSize(int maximumPacketSize) { + this.maximumPacketSize = maximumPacketSize; + } + + /** * Sets the cipher suites preference. */ void setUseCipherSuitesOrder(boolean on) { @@ -532,23 +530,29 @@ handshakeHash = new HandshakeHash(needCertVerify); // Generate handshake input/output stream. - input = new HandshakeInStream(handshakeHash); if (conn != null) { - output = new HandshakeOutStream(protocolVersion, helloVersion, - handshakeHash, conn); - conn.getAppInputStream().r.setHandshakeHash(handshakeHash); - conn.getAppInputStream().r.setHelloVersion(helloVersion); - conn.getAppOutputStream().r.setHelloVersion(helloVersion); - } else { - output = new HandshakeOutStream(protocolVersion, helloVersion, - handshakeHash, engine); + input = new HandshakeInStream(); + output = new HandshakeOutStream(conn.outputRecord); + + conn.inputRecord.setHandshakeHash(handshakeHash); + conn.inputRecord.setHelloVersion(helloVersion); + + conn.outputRecord.setHandshakeHash(handshakeHash); + conn.outputRecord.setHelloVersion(helloVersion); + conn.outputRecord.setVersion(protocolVersion); + } else if (engine != null) { + input = new HandshakeInStream(); + output = new HandshakeOutStream(engine.outputRecord); + engine.inputRecord.setHandshakeHash(handshakeHash); engine.inputRecord.setHelloVersion(helloVersion); + + engine.outputRecord.setHandshakeHash(handshakeHash); engine.outputRecord.setHelloVersion(helloVersion); + engine.outputRecord.setVersion(protocolVersion); } - // move state to activated - state = -1; + handshakeActivated = true; } /** @@ -637,15 +641,15 @@ if (!(activeProtocols.collection().isEmpty()) && activeProtocols.min.v != ProtocolVersion.NONE.v) { for (CipherSuite suite : enabledCipherSuites.collection()) { - if (suite.obsoleted > activeProtocols.min.v && - suite.supported <= activeProtocols.max.v) { + if (!activeProtocols.min.obsoletes(suite) && + activeProtocols.max.supports(suite)) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); } } else if (debug != null && Debug.isOn("verbose")) { - if (suite.obsoleted <= activeProtocols.min.v) { + if (activeProtocols.min.obsoletes(suite)) { System.out.println( "Ignoring obsoleted cipher suite: " + suite); } else { @@ -700,8 +704,8 @@ boolean found = false; for (CipherSuite suite : enabledCipherSuites.collection()) { - if (suite.isAvailable() && suite.obsoleted > protocol.v && - suite.supported <= protocol.v) { + if (suite.isAvailable() && (!protocol.obsoletes(suite)) && + protocol.supports(suite)) { if (algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { @@ -837,7 +841,7 @@ * this freshly created session can become the current one. */ boolean isDone() { - return state == HandshakeMessage.ht_finished; + return started() && handshakeState.isEmpty() && handshakeFinished; } @@ -861,6 +865,14 @@ } } + void expectingFinishFlightSE() { + if (conn != null) { + conn.expectingFinishFlight(); + } else { + engine.expectingFinishFlight(); + } + } + /* * Returns true if renegotiation is in use for this connection. */ @@ -886,8 +898,8 @@ * This routine is fed SSL handshake records when they become available, * and processes messages found therein. */ - void process_record(InputRecord r, boolean expectingFinished) - throws IOException { + void processRecord(ByteBuffer record, + boolean expectingFinished) throws IOException { checkThrown(); @@ -895,7 +907,7 @@ * Store the incoming handshake data, then see if we can * now process any completed handshake messages */ - input.incomingRecord(r); + input.incomingRecord(record); /* * We don't need to create a separate delegatable task @@ -946,6 +958,13 @@ return; } + // Set the flags in the message receiving side. + if (messageType == HandshakeMessage.ht_client_hello) { + clientHelloDelivered = true; + } else if (messageType == HandshakeMessage.ht_hello_request) { + serverHelloRequested = true; + } + /* * Process the message. We require * that processMessage() consumes the entire message. In @@ -961,14 +980,16 @@ * Also, note that hello request messages are never hashed; * that includes the hello request header, too. */ - if (messageType == HandshakeMessage.ht_hello_request) { - input.reset(); - processMessage(messageType, messageLen); - input.ignore(4 + messageLen); - } else { - input.mark(messageLen); - processMessage(messageType, messageLen); - input.digestNow(); + processMessage(messageType, messageLen); + + // Reload if this message has been reserved. + // + // Note: in the implementation, only certificate_verify and + // finished messages are reserved. + if ((messageType == HandshakeMessage.ht_finished) || + (messageType == HandshakeMessage.ht_certificate_verify)) { + + handshakeHash.reload(); } } } @@ -980,29 +1001,29 @@ * In activated state, the handshaker may not send any messages out. */ boolean activated() { - return state >= -1; + return handshakeActivated; } /** * Returns true iff the handshaker has sent any messages. */ boolean started() { - return state >= 0; // 0: HandshakeMessage.ht_hello_request - // 1: HandshakeMessage.ht_client_hello + return (serverHelloRequested || clientHelloDelivered); } - /* * Used to kickstart the negotiation ... either writing a * ClientHello or a HelloRequest as appropriate, whichever * the subclass returns. NOP if handshaking's already started. */ void kickstart() throws IOException { - if (state >= 0) { + if ((isClient && clientHelloDelivered) || + (!isClient && serverHelloRequested)) { return; } HandshakeMessage m = getKickstartMessage(); + handshakeState.update(m, resumingSession); if (debug != null && Debug.isOn("handshake")) { m.print(System.out); @@ -1010,7 +1031,13 @@ m.write(output); output.flush(); - state = m.messageType(); + // Set the flags in the message delivering side. + int handshakeType = m.messageType(); + if (handshakeType == HandshakeMessage.ht_hello_request) { + serverHelloRequested = true; + } else { // HandshakeMessage.ht_client_hello + clientHelloDelivered = true; + } } /** @@ -1052,24 +1079,16 @@ * We already hold SSLEngine/SSLSocket "this" by virtue * of this being called from the readRecord code. */ - OutputRecord r; - if (conn != null) { - r = new OutputRecord(Record.ct_change_cipher_spec); - } else { - r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine); - } - - r.setVersion(protocolVersion); - r.write(1); // single byte of data - if (conn != null) { conn.writeLock.lock(); try { - conn.writeRecord(r); + handshakeState.changeCipherSpec(false, isClient); conn.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } + + handshakeState.update(mesg, resumingSession); mesg.write(output); output.flush(); } finally { @@ -1077,19 +1096,25 @@ } } else { synchronized (engine.writeLock) { - engine.writeRecord((EngineOutputRecord)r); + handshakeState.changeCipherSpec(false, isClient); engine.changeWriteCiphers(); if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } + + handshakeState.update(mesg, resumingSession); mesg.write(output); - - if (lastMessage) { - output.setFinishedMsg(); - } output.flush(); } } + + if (lastMessage) { + handshakeFinished = true; + } + } + + void receiveChangeCipherSpec() throws IOException { + handshakeState.changeCipherSpec(true, isClient); } /* @@ -1131,12 +1156,31 @@ String masterAlg; PRF prf; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { - masterAlg = "SunTls12MasterSecret"; - prf = cipherSuite.prfAlg; + byte majorVersion = protocolVersion.major; + byte minorVersion = protocolVersion.minor; + if (protocolVersion.isDTLSProtocol()) { + // Use TLS version number for DTLS key calculation + if (protocolVersion.v == ProtocolVersion.DTLS10.v) { + majorVersion = ProtocolVersion.TLS11.major; + minorVersion = ProtocolVersion.TLS11.minor; + + masterAlg = "SunTlsMasterSecret"; + prf = P_NONE; + } else { // DTLS 1.2 + majorVersion = ProtocolVersion.TLS12.major; + minorVersion = ProtocolVersion.TLS12.minor; + + masterAlg = "SunTls12MasterSecret"; + prf = cipherSuite.prfAlg; + } } else { - masterAlg = "SunTlsMasterSecret"; - prf = P_NONE; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + masterAlg = "SunTls12MasterSecret"; + prf = cipherSuite.prfAlg; + } else { + masterAlg = "SunTlsMasterSecret"; + prf = P_NONE; + } } String prfHashAlg = prf.getPRFHashAlg(); @@ -1145,7 +1189,7 @@ @SuppressWarnings("deprecation") TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( - preMasterSecret, protocolVersion.major, protocolVersion.minor, + preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF), clnt_random.random_bytes, svr_random.random_bytes, prfHashAlg, prfHashLength, prfBlockSize); @@ -1196,36 +1240,55 @@ String keyMaterialAlg; PRF prf; - if (protocolVersion.v >= ProtocolVersion.TLS12.v) { - keyMaterialAlg = "SunTls12KeyMaterial"; - prf = cipherSuite.prfAlg; + byte majorVersion = protocolVersion.major; + byte minorVersion = protocolVersion.minor; + if (protocolVersion.isDTLSProtocol()) { + // Use TLS version number for DTLS key calculation + if (protocolVersion.v == ProtocolVersion.DTLS10.v) { + majorVersion = ProtocolVersion.TLS11.major; + minorVersion = ProtocolVersion.TLS11.minor; + + keyMaterialAlg = "SunTlsKeyMaterial"; + prf = P_NONE; + } else { // DTLS 1.2+ + majorVersion = ProtocolVersion.TLS12.major; + minorVersion = ProtocolVersion.TLS12.minor; + + keyMaterialAlg = "SunTls12KeyMaterial"; + prf = cipherSuite.prfAlg; + } } else { - keyMaterialAlg = "SunTlsKeyMaterial"; - prf = P_NONE; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + keyMaterialAlg = "SunTls12KeyMaterial"; + prf = cipherSuite.prfAlg; + } else { + keyMaterialAlg = "SunTlsKeyMaterial"; + prf = P_NONE; + } } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); - // TLS v1.1 or later uses an explicit IV in CBC cipher suites to + // TLS v1.1+ and DTLS use an explicit IV in CBC cipher suites to // protect against the CBC attacks. AEAD/GCM cipher suites in TLS // v1.2 or later use a fixed IV as the implicit part of the partially // implicit nonce technique described in RFC 5116. int ivSize = cipher.ivSize; if (cipher.cipherType == AEAD_CIPHER) { ivSize = cipher.fixedIvSize; - } else if (protocolVersion.v >= ProtocolVersion.TLS11.v && - cipher.cipherType == BLOCK_CIPHER) { + } else if ((cipher.cipherType == BLOCK_CIPHER) && + protocolVersion.useTLS11PlusSpec()) { ivSize = 0; } TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( - masterKey, protocolVersion.major, protocolVersion.minor, - clnt_random.random_bytes, svr_random.random_bytes, - cipher.algorithm, cipher.keySize, expandedKeySize, - ivSize, hashSize, - prfHashAlg, prfHashLength, prfBlockSize); + masterKey, (majorVersion & 0xFF), (minorVersion & 0xFF), + clnt_random.random_bytes, svr_random.random_bytes, + cipher.algorithm, cipher.keySize, expandedKeySize, + ivSize, hashSize, + prfHashAlg, prfHashLength, prfBlockSize); try { KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); @@ -1247,10 +1310,6 @@ throw new ProviderException(e); } - // Mark a flag that allows outside entities (like SSLSocket/SSLEngine) - // determine if a ChangeCipherSpec message could be processed. - sessKeysCalculated = true; - // // Dump the connection keys as they're generated. // @@ -1293,7 +1352,7 @@ System.out.println("Server write IV:"); printHex(dump, svrWriteIV.getIV()); } else { - if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (protocolVersion.useTLS11PlusSpec()) { System.out.println( "... no IV derived for this protocol"); } else { @@ -1305,15 +1364,6 @@ } } - /** - * Return whether or not the Handshaker has derived session keys for - * this handshake. This is used for determining readiness to process - * an incoming ChangeCipherSpec message. - */ - boolean sessionKeysCalculated() { - return sessKeysCalculated; - } - private static void printHex(HexDumpEncoder dump, byte[] bytes) { if (bytes == null) { System.out.println("(key bytes not available)"); @@ -1326,19 +1376,6 @@ } } - /** - * Throw an SSLException with the specified message and cause. - * Shorthand until a new SSLException constructor is added. - * This method never returns. - */ - static void throwSSLException(String msg, Throwable cause) - throws SSLException { - SSLException e = new SSLException(msg); - e.initCause(cause); - throw e; - } - - /* * Implement a simple task delegator. *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java Tue Jun 02 04:01:04 2015 +0000 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import javax.net.ssl.SSLProtocolException; +import java.security.MessageDigest; +import java.security.SecureRandom; + +import sun.security.ssl.HandshakeMessage.ClientHello; + +/* + * HelloVerifyRequest cookie manager + */ +final class HelloCookieManager { + // the cookie secret life time + private static long COOKIE_TIMING_WINDOW = 3600000; // in milliseconds + private static int COOKIE_MAX_LENGTH_DTLS10 = 32; // 32 bytes + private static int COOKIE_MAX_LENGTH_DTLS12 = 0xFF; // 2^8 -1 bytes + + private final SecureRandom secureRandom; + private final MessageDigest cookieDigest; + + private int cookieVersion; // allow to wrap + private long secretLifetime; + private byte[] cookieSecret; + + private int prevCookieVersion; + private byte[] prevCookieSecret; + + HelloCookieManager(SecureRandom secureRandom) { + this.secureRandom = secureRandom; + this.cookieDigest = JsseJce.getMessageDigest("SHA-256"); + + this.cookieVersion = secureRandom.nextInt(); + this.secretLifetime = 0; + this.cookieSecret = null; + + this.prevCookieVersion = 0; + this.prevCookieSecret = null; + } + + // Used by server side to generate cookies in HelloVerifyRequest message. + synchronized byte[] getCookie(ClientHello clientHelloMsg) { + if (secretLifetime < System.currentTimeMillis()) { + if (cookieSecret != null) { + prevCookieVersion = cookieVersion; + prevCookieSecret = cookieSecret.clone(); + } else { + cookieSecret = new byte[32]; + } + + cookieVersion++; + secureRandom.nextBytes(cookieSecret); + secretLifetime = System.currentTimeMillis() + COOKIE_TIMING_WINDOW; + } + + clientHelloMsg.updateHelloCookie(cookieDigest); + byte[]