8043758: Datagram Transport Layer Security (DTLS)

Reviewed-by: jnimeh, weijun, mullan, wetmore
This commit is contained in:
Xue-Lei Andrew Fan 2015-06-02 04:01:04 +00:00
parent ea68abc64e
commit 0f1698f906
76 changed files with 11196 additions and 4956 deletions

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,7 +28,7 @@ package javax.net.ssl;
import java.util.List; import java.util.List;
/** /**
* Extends the <code>SSLSession</code> interface to support additional * Extends the {@code SSLSession} interface to support additional
* session attributes. * session attributes.
* *
* @since 1.7 * @since 1.7
@ -39,8 +39,8 @@ public abstract class ExtendedSSLSession implements SSLSession {
* is willing to use. * is willing to use.
* <p> * <p>
* Note: this method is used to indicate to the peer which signature * 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 * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
* not meaningful for TLS versions prior to 1.2. * not meaningful for TLS/DTLS versions prior to 1.2.
* <p> * <p>
* The signature algorithm name must be a standard Java Security * The signature algorithm name must be a standard Java Security
* name (such as "SHA1withRSA", "SHA256withECDSA", and so on). * name (such as "SHA1withRSA", "SHA256withECDSA", and so on).
@ -52,7 +52,7 @@ public abstract class ExtendedSSLSession implements SSLSession {
* Note: the local supported signature algorithms should conform to * Note: the local supported signature algorithms should conform to
* the algorithm constraints specified by * the algorithm constraints specified by
* {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()} * {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()}
* method in <code>SSLParameters</code>. * method in {@code SSLParameters}.
* *
* @return An array of supported signature algorithms, in descending * @return An array of supported signature algorithms, in descending
* order of preference. The return value is an empty array if * order of preference. The return value is an empty array if
@ -67,8 +67,8 @@ public abstract class ExtendedSSLSession implements SSLSession {
* able to use. * able to use.
* <p> * <p>
* Note: this method is used to indicate to the local side which signature * 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 * algorithms may be used for digital signatures in TLS/DTLS 1.2. It is
* not meaningful for TLS versions prior to 1.2. * not meaningful for TLS/DTLS versions prior to 1.2.
* <p> * <p>
* The signature algorithm name must be a standard Java Security * The signature algorithm name must be a standard Java Security
* name (such as "SHA1withRSA", "SHA256withECDSA", and so on). * name (such as "SHA1withRSA", "SHA256withECDSA", and so on).

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -31,7 +31,7 @@ import java.util.Arrays;
* Instances of this class represent a server name in a Server Name * Instances of this class represent a server name in a Server Name
* Indication (SNI) extension. * Indication (SNI) extension.
* <P> * <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 * indicate what server name the client is attempting to connect to during
* handshaking. See section 3, "Server Name Indication", of <A * handshaking. See section 3, "Server Name Indication", of <A
* HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>. * HREF="http://www.ietf.org/rfc/rfc6066.txt">TLS Extensions (RFC 6066)</A>.

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -32,12 +32,12 @@ import sun.security.jca.GetInstance;
/** /**
* Instances of this class represent a secure socket protocol * Instances of this class represent a secure socket protocol
* implementation which acts as a factory for secure socket * 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 * with an optional set of key and trust managers and source of
* secure random bytes. * secure random bytes.
* *
* <p> Every implementation of the Java platform is required to support the * <p> Every implementation of the Java platform is required to support the
* following standard <code>SSLContext</code> protocol: * following standard {@code SSLContext} protocol:
* <ul> * <ul>
* <li><tt>TLSv1</tt></li> * <li><tt>TLSv1</tt></li>
* </ul> * </ul>
@ -79,7 +79,7 @@ public class SSLContext {
* <p>If a default context was set using the {@link #setDefault * <p>If a default context was set using the {@link #setDefault
* SSLContext.setDefault()} method, it is returned. Otherwise, the first * SSLContext.setDefault()} method, it is returned. Otherwise, the first
* call of this method triggers the call * 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. * If successful, that object is made the default SSL context and returned.
* *
* <p>The default context is immediately * <p>The default context is immediately
@ -106,8 +106,8 @@ public class SSLContext {
* @param context the SSLContext * @param context the SSLContext
* @throws NullPointerException if context is null * @throws NullPointerException if context is null
* @throws SecurityException if a security manager exists and its * @throws SecurityException if a security manager exists and its
* <code>checkPermission</code> method does not allow * {@code checkPermission} method does not allow
* <code>SSLPermission("setDefaultSSLContext")</code> * {@code SSLPermission("setDefaultSSLContext")}
* @since 1.6 * @since 1.6
*/ */
public static synchronized void setDefault(SSLContext context) { public static synchronized void setDefault(SSLContext context) {
@ -122,7 +122,7 @@ public class SSLContext {
} }
/** /**
* Returns a <code>SSLContext</code> object that implements the * Returns a {@code SSLContext} object that implements the
* specified secure socket protocol. * specified secure socket protocol.
* *
* <p> This method traverses the list of registered security Providers, * <p> This method traverses the list of registered security Providers,
@ -141,7 +141,7 @@ public class SSLContext {
* Documentation</a> * Documentation</a>
* for information about standard protocol names. * 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 * @exception NoSuchAlgorithmException if no Provider supports a
* SSLContextSpi implementation for the * SSLContextSpi implementation for the
@ -159,7 +159,7 @@ public class SSLContext {
} }
/** /**
* Returns a <code>SSLContext</code> object that implements the * Returns a {@code SSLContext} object that implements the
* specified secure socket protocol. * specified secure socket protocol.
* *
* <p> A new SSLContext object encapsulating the * <p> A new SSLContext object encapsulating the
@ -179,7 +179,7 @@ public class SSLContext {
* *
* @param provider the name of the provider. * @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 * @throws NoSuchAlgorithmException if a SSLContextSpi
* implementation for the specified protocol is not * implementation for the specified protocol is not
@ -202,7 +202,7 @@ public class SSLContext {
} }
/** /**
* Returns a <code>SSLContext</code> object that implements the * Returns a {@code SSLContext} object that implements the
* specified secure socket protocol. * specified secure socket protocol.
* *
* <p> A new SSLContext object encapsulating the * <p> A new SSLContext object encapsulating the
@ -219,7 +219,7 @@ public class SSLContext {
* *
* @param provider an instance of the provider. * @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 * @throws NoSuchAlgorithmException if a SSLContextSpi
* implementation for the specified protocol is not available * implementation for the specified protocol is not available
@ -239,22 +239,22 @@ public class SSLContext {
} }
/** /**
* 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 * <p>This is the same name that was specified in one of the
* <code>getInstance</code> calls that created this * {@code getInstance} calls that created this
* <code>SSLContext</code> object. * {@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() { public final String getProtocol() {
return this.protocol; 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() { public final Provider getProvider() {
return this.provider; return this.provider;
@ -283,31 +283,35 @@ public class SSLContext {
} }
/** /**
* Returns a <code>SocketFactory</code> object for this * Returns a {@code SocketFactory} object for this
* context. * 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 * @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() { public final SSLSocketFactory getSocketFactory() {
return contextSpi.engineGetSocketFactory(); return contextSpi.engineGetSocketFactory();
} }
/** /**
* Returns a <code>ServerSocketFactory</code> object for * Returns a {@code ServerSocketFactory} object for
* this context. * 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 * @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() { public final SSLServerSocketFactory getServerSocketFactory() {
return contextSpi.engineGetServerSocketFactory(); return contextSpi.engineGetServerSocketFactory();
} }
/** /**
* Creates a new <code>SSLEngine</code> using this context. * Creates a new {@code SSLEngine} using this context.
* <P> * <P>
* Applications using this factory method are providing no hints * Applications using this factory method are providing no hints
* for an internal session reuse strategy. If hints are desired, * for an internal session reuse strategy. If hints are desired,
@ -317,11 +321,11 @@ public class SSLContext {
* Some cipher suites (such as Kerberos) require remote hostname * Some cipher suites (such as Kerberos) require remote hostname
* information, in which case this factory method should not be used. * 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 * @throws UnsupportedOperationException if the underlying provider
* does not implement the operation. * does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires * @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 * @since 1.5
*/ */
public final SSLEngine createSSLEngine() { public final SSLEngine createSSLEngine() {
@ -338,7 +342,7 @@ public class SSLContext {
} }
/** /**
* Creates a new <code>SSLEngine</code> using this context using * Creates a new {@code SSLEngine} using this context using
* advisory peer information. * advisory peer information.
* <P> * <P>
* Applications using this factory method are providing hints * Applications using this factory method are providing hints
@ -349,11 +353,11 @@ public class SSLContext {
* *
* @param peerHost the non-authoritative name of the host * @param peerHost the non-authoritative name of the host
* @param peerPort the non-authoritative port * @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 * @throws UnsupportedOperationException if the underlying provider
* does not implement the operation. * does not implement the operation.
* @throws IllegalStateException if the SSLContextImpl requires * @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 * @since 1.5
*/ */
public final SSLEngine createSSLEngine(String peerHost, int peerPort) { public final SSLEngine createSSLEngine(String peerHost, int peerPort) {

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -29,7 +29,7 @@ import java.security.*;
/** /**
* This class defines the <i>Service Provider Interface</i> (<b>SPI</b>) * 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 * <p> All the abstract methods in this class must be implemented by each
* cryptographic service provider who wishes to supply the implementation * cryptographic service provider who wishes to supply the implementation
@ -52,31 +52,35 @@ public abstract class SSLContextSpi {
SecureRandom sr) throws KeyManagementException; SecureRandom sr) throws KeyManagementException;
/** /**
* Returns a <code>SocketFactory</code> object for this * Returns a {@code SocketFactory} object for this
* context. * 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 * @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code> * initialization and the {@code engineInit()}
* has not been called * has not been called
* @see javax.net.ssl.SSLContext#getSocketFactory() * @see javax.net.ssl.SSLContext#getSocketFactory()
*/ */
protected abstract SSLSocketFactory engineGetSocketFactory(); protected abstract SSLSocketFactory engineGetSocketFactory();
/** /**
* Returns a <code>ServerSocketFactory</code> object for * Returns a {@code ServerSocketFactory} object for
* this context. * 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 * @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code> * initialization and the {@code engineInit()}
* has not been called * has not been called
* @see javax.net.ssl.SSLContext#getServerSocketFactory() * @see javax.net.ssl.SSLContext#getServerSocketFactory()
*/ */
protected abstract SSLServerSocketFactory engineGetServerSocketFactory(); protected abstract SSLServerSocketFactory engineGetServerSocketFactory();
/** /**
* Creates a new <code>SSLEngine</code> using this context. * Creates a new {@code SSLEngine} using this context.
* <P> * <P>
* Applications using this factory method are providing no hints * Applications using this factory method are providing no hints
* for an internal session reuse strategy. If hints are desired, * for an internal session reuse strategy. If hints are desired,
@ -86,9 +90,9 @@ public abstract class SSLContextSpi {
* Some cipher suites (such as Kerberos) require remote hostname * Some cipher suites (such as Kerberos) require remote hostname
* information, in which case this factory method should not be used. * 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 * @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code> * initialization and the {@code engineInit()}
* has not been called * has not been called
* *
* @see SSLContext#createSSLEngine() * @see SSLContext#createSSLEngine()
@ -98,7 +102,7 @@ public abstract class SSLContextSpi {
protected abstract SSLEngine engineCreateSSLEngine(); protected abstract SSLEngine engineCreateSSLEngine();
/** /**
* Creates a <code>SSLEngine</code> using this context. * Creates a {@code SSLEngine} using this context.
* <P> * <P>
* Applications using this factory method are providing hints * Applications using this factory method are providing hints
* for an internal session reuse strategy. * for an internal session reuse strategy.
@ -108,9 +112,9 @@ public abstract class SSLContextSpi {
* *
* @param host the non-authoritative name of the host * @param host the non-authoritative name of the host
* @param port the non-authoritative port * @param port the non-authoritative port
* @return the <code>SSLEngine</code> Object * @return the {@code SSLEngine} Object
* @throws IllegalStateException if the SSLContextImpl requires * @throws IllegalStateException if the SSLContextImpl requires
* initialization and the <code>engineInit()</code> * initialization and the {@code engineInit()}
* has not been called * has not been called
* *
* @see SSLContext#createSSLEngine(String, int) * @see SSLContext#createSSLEngine(String, int)
@ -120,19 +124,19 @@ public abstract class SSLContextSpi {
protected abstract SSLEngine engineCreateSSLEngine(String host, int port); 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. * this context.
* *
* @return the <code>SSLSessionContext</code> object * @return the {@code SSLSessionContext} object
* @see javax.net.ssl.SSLContext#getServerSessionContext() * @see javax.net.ssl.SSLContext#getServerSessionContext()
*/ */
protected abstract SSLSessionContext engineGetServerSessionContext(); protected abstract SSLSessionContext engineGetServerSessionContext();
/** /**
* Returns a client <code>SSLSessionContext</code> object for * Returns a client {@code SSLSessionContext} object for
* this context. * this context.
* *
* @return the <code>SSLSessionContext</code> object * @return the {@code SSLSessionContext} object
* @see javax.net.ssl.SSLContext#getClientSessionContext() * @see javax.net.ssl.SSLContext#getClientSessionContext()
*/ */
protected abstract SSLSessionContext engineGetClientSessionContext(); protected abstract SSLSessionContext engineGetClientSessionContext();

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -37,15 +37,15 @@ import java.nio.ReadOnlyBufferException;
* <P> * <P>
* The secure communications modes include: <UL> * 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. * 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 * peer authentication. Servers are usually authenticated, and
* clients may be authenticated as requested by servers. * clients may be authenticated as requested by servers.
* *
* <LI> <em>Confidentiality (Privacy Protection)</em>. In most * <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 * server. This protects the confidentiality of data, so that
* passive wiretappers won't see sensitive data such as financial * passive wiretappers won't see sensitive data such as financial
* information or personal information of many kinds. * information or personal information of many kinds.
@ -65,19 +65,19 @@ import java.nio.ReadOnlyBufferException;
* handshaking has completed, you can access session attributes by * handshaking has completed, you can access session attributes by
* using the {@link #getSession()} method. * using the {@link #getSession()} method.
* <P> * <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 * functionality, but all of the inbound and outbound data is
* automatically transported using the underlying {@link * automatically transported using the underlying {@link
* java.net.Socket Socket}, which by design uses a blocking model. * java.net.Socket Socket}, which by design uses a blocking model.
* While this is appropriate for many applications, this model does not * While this is appropriate for many applications, this model does not
* provide the scalability required by large servers. * provide the scalability required by large servers.
* <P> * <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 * operates on inbound and outbound byte streams, independent of the
* transport mechanism. It is the responsibility of the * transport mechanism. It is the responsibility of the
* <code>SSLEngine</code> user to arrange for reliable I/O transport to * {@code SSLEngine} user to arrange for reliable I/O transport to
* the peer. By separating the SSL/TLS abstraction from the I/O * the peer. By separating the SSL/TLS/DTLS abstraction from the I/O
* transport mechanism, the <code>SSLEngine</code> can be used for a * transport mechanism, the {@code SSLEngine} can be used for a
* wide variety of I/O types, such as {@link * wide variety of I/O types, such as {@link
* java.nio.channels.spi.AbstractSelectableChannel#configureBlocking(boolean) * java.nio.channels.spi.AbstractSelectableChannel#configureBlocking(boolean)
* non-blocking I/O (polling)}, {@link java.nio.channels.Selector * non-blocking I/O (polling)}, {@link java.nio.channels.Selector
@ -87,7 +87,7 @@ import java.nio.ReadOnlyBufferException;
* HREF="http://www.jcp.org/en/jsr/detail?id=203"> future asynchronous * HREF="http://www.jcp.org/en/jsr/detail?id=203"> future asynchronous
* I/O models </A>, and so on. * I/O models </A>, and so on.
* <P> * <P>
* At a high level, the <code>SSLEngine</code> appears thus: * At a high level, the {@code SSLEngine} appears thus:
* *
* <pre> * <pre>
* app data * app data
@ -115,18 +115,18 @@ import java.nio.ReadOnlyBufferException;
* mechanism. Inbound data is data which has been received from the * mechanism. Inbound data is data which has been received from the
* peer, and outbound data is destined for the peer. * peer, and outbound data is destined for the peer.
* <P> * <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 * 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.") * "alert", "change_cipher_spec," and "handshake.")
* <P> * <P>
* There are five distinct phases to an <code>SSLEngine</code>. * There are five distinct phases to an {@code SSLEngine}.
* *
* <OL> * <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 * initialized, but has not yet been used. During this phase, an
* application may set any <code>SSLEngine</code>-specific settings * application may set any {@code SSLEngine}-specific settings
* (enabled cipher suites, whether the <code>SSLEngine</code> should * (enabled cipher suites, whether the {@code SSLEngine} should
* handshake in client or server mode, and so on). Once * handshake in client or server mode, and so on). Once
* handshaking has begun, though, any new settings (except * handshaking has begun, though, any new settings (except
* client/server mode, see below) will be used for * client/server mode, see below) will be used for
@ -139,7 +139,7 @@ import java.nio.ReadOnlyBufferException;
* *
* <li> Application Data - Once the communication parameters have * <li> Application Data - Once the communication parameters have
* been established and the handshake is complete, application data * 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, * application messages are encrypted and integrity protected,
* and inbound messages reverse the process. * and inbound messages reverse the process.
* *
@ -147,50 +147,50 @@ import java.nio.ReadOnlyBufferException;
* the session at any time during the Application Data phase. New * the session at any time during the Application Data phase. New
* handshaking data can be intermixed among the application data. * handshaking data can be intermixed among the application data.
* Before starting the rehandshake phase, the application may * 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, * enabled ciphersuites and whether to use client authentication,
* but can not change between client/server modes. As before, once * 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 * configuration settings will not be used until the next
* handshake. * handshake.
* *
* <li> Closure - When the connection is no longer needed, the * <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 * send/receive any remaining messages to the peer before
* closing the underlying transport mechanism. Once an engine is * 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. * be created.
* </OL> * </OL>
* An <code>SSLEngine</code> is created by calling {@link * An {@code SSLEngine} is created by calling {@link
* SSLContext#createSSLEngine()} from an initialized * SSLContext#createSSLEngine()} from an initialized
* <code>SSLContext</code>. Any configuration * {@code SSLContext}. Any configuration
* parameters should be set before making the first call to * parameters should be set before making the first call to
* <code>wrap()</code>, <code>unwrap()</code>, or * {@code wrap()}, {@code unwrap()}, or
* <code>beginHandshake()</code>. These methods all trigger the * {@code beginHandshake()}. These methods all trigger the
* initial handshake. * initial handshake.
* <P> * <P>
* Data moves through the engine by calling {@link #wrap(ByteBuffer, * Data moves through the engine by calling {@link #wrap(ByteBuffer,
* ByteBuffer) wrap()} or {@link #unwrap(ByteBuffer, ByteBuffer) * ByteBuffer) wrap()} or {@link #unwrap(ByteBuffer, ByteBuffer)
* unwrap()} on outbound or inbound data, respectively. Depending on * 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 * may consume application data from the source buffer and may produce
* network data in the destination buffer. The outbound data * network data in the destination buffer. The outbound data
* may contain application and/or handshake data. A call to * 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 * advance the handshake if the data is handshaking information, or
* may place application data in the destination buffer if the data * 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. * will determine when data is consumed and produced.
* <P> * <P>
* Calls to <code>wrap()</code> and <code>unwrap()</code> return an * Calls to {@code wrap()} and {@code unwrap()} return an
* <code>SSLEngineResult</code> which indicates the status of the * {@code SSLEngineResult} which indicates the status of the
* operation, and (optionally) how to interact with the engine to make * operation, and (optionally) how to interact with the engine to make
* progress. * progress.
* <P> * <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 * packets only, and does not store application data internally between
* calls to <code>wrap()/unwrap()</code>. Thus input and output * calls to {@code wrap()/unwrap()}. Thus input and output
* <code>ByteBuffer</code>s must be sized appropriately to hold the * {@code ByteBuffer}s must be sized appropriately to hold the
* maximum record that can be produced. Calls to {@link * maximum record that can be produced. Calls to {@link
* SSLSession#getPacketBufferSize()} and {@link * SSLSession#getPacketBufferSize()} and {@link
* SSLSession#getApplicationBufferSize()} should be used to determine * SSLSession#getApplicationBufferSize()} should be used to determine
@ -200,12 +200,12 @@ import java.nio.ReadOnlyBufferException;
* must determine (via {@link SSLEngineResult}) and correct the * must determine (via {@link SSLEngineResult}) and correct the
* problem, and then try the call again. * problem, and then try the call again.
* <P> * <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 * SSLEngineResult.Status#BUFFER_OVERFLOW} result if the engine
* determines that there is not enough destination buffer space available. * determines that there is not enough destination buffer space available.
* Applications should call {@link SSLSession#getApplicationBufferSize()} * Applications should call {@link SSLSession#getApplicationBufferSize()}
* and compare that value with the space available in the destination buffer, * 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 * were to return a {@link SSLEngineResult.Status#BUFFER_UNDERFLOW}, the
* application should call {@link SSLSession#getPacketBufferSize()} to ensure * application should call {@link SSLSession#getPacketBufferSize()} to ensure
* that the source buffer has enough room to hold a record (enlarging if * that the source buffer has enough room to hold a record (enlarging if
@ -241,8 +241,8 @@ import java.nio.ReadOnlyBufferException;
* }</pre> * }</pre>
* *
* <P> * <P>
* Unlike <code>SSLSocket</code>, all methods of SSLEngine are * Unlike {@code SSLSocket}, all methods of SSLEngine are
* non-blocking. <code>SSLEngine</code> implementations may * non-blocking. {@code SSLEngine} implementations may
* require the results of tasks that may take an extended period of * require the results of tasks that may take an extended period of
* time to complete, or may even block. For example, a TrustManager * time to complete, or may even block. For example, a TrustManager
* may need to connect to a remote certificate validation service, * may need to connect to a remote certificate validation service,
@ -252,8 +252,8 @@ import java.nio.ReadOnlyBufferException;
* seemingly blocking. * seemingly blocking.
* <P> * <P>
* For any operation which may potentially block, the * For any operation which may potentially block, the
* <code>SSLEngine</code> will create a {@link java.lang.Runnable} * {@code SSLEngine} will create a {@link java.lang.Runnable}
* delegated task. When <code>SSLEngineResult</code> indicates that a * delegated task. When {@code SSLEngineResult} indicates that a
* delegated task result is needed, the application must call {@link * delegated task result is needed, the application must call {@link
* #getDelegatedTask()} to obtain an outstanding delegated task and * #getDelegatedTask()} to obtain an outstanding delegated task and
* call its {@link java.lang.Runnable#run() run()} method (possibly using * call its {@link java.lang.Runnable#run() run()} method (possibly using
@ -262,16 +262,16 @@ import java.nio.ReadOnlyBufferException;
* exist, and try the original operation again. * exist, and try the original operation again.
* <P> * <P>
* At the end of a communication session, applications should properly * At the end of a communication session, applications should properly
* close the SSL/TLS link. The SSL/TLS protocols have closure handshake * close the SSL/TLS/DTLS link. The SSL/TLS/DTLS protocols have closure
* messages, and these messages should be communicated to the peer * handshake messages, and these messages should be communicated to the
* before releasing the <code>SSLEngine</code> and closing the * peer before releasing the {@code SSLEngine} and closing the
* underlying transport mechanism. A close can be initiated by one of: * underlying transport mechanism. A close can be initiated by one of:
* an SSLException, an inbound closure handshake message, or one of the * an SSLException, an inbound closure handshake message, or one of the
* close methods. In all cases, closure handshake messages are * close methods. In all cases, closure handshake messages are
* generated by the engine, and <code>wrap()</code> should be repeatedly * generated by the engine, and {@code wrap()} should be repeatedly
* called until the resulting <code>SSLEngineResult</code>'s status * called until the resulting {@code SSLEngineResult}'s status
* returns "CLOSED", or {@link #isOutboundDone()} returns true. All * 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. * peer.
* <P> * <P>
* {@link #closeOutbound()} is used to signal the engine that the * {@link #closeOutbound()} is used to signal the engine that the
@ -279,12 +279,12 @@ import java.nio.ReadOnlyBufferException;
* <P> * <P>
* A peer will signal its intent to close by sending its own closure * A peer will signal its intent to close by sending its own closure
* handshake message. After this message has been received and * 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 * 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. * with status "CLOSED", or if {@link #isInboundDone()} returns true.
* If for some reason the peer closes the communication link without * 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 * detect the end-of-stream and can signal the engine via {@link
* #closeInbound()} that there will no more inbound messages to * #closeInbound()} that there will no more inbound messages to
* process. Some applications might choose to require orderly shutdown * process. Some applications might choose to require orderly shutdown
@ -315,16 +315,16 @@ import java.nio.ReadOnlyBufferException;
* and/or non-private (unencrypted) communications will such a * and/or non-private (unencrypted) communications will such a
* cipher suite be selected. * cipher suite be selected.
* <P> * <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 * each endpoint must decide which role to assume. This choice determines
* who begins the handshaking process as well as which type of messages * who begins the handshaking process as well as which type of messages
* should be sent by each party. The method {@link * should be sent by each party. The method {@link
* #setUseClientMode(boolean)} configures the mode. Once the initial * #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. * between client and server modes, even when performing renegotiations.
* <P> * <P>
* Applications might choose to process delegated tasks in different * 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 created, the current {@link java.security.AccessControlContext}
* is saved. All future delegated tasks will be processed using this * is saved. All future delegated tasks will be processed using this
* context: that is, all access control decisions will be made using the * context: that is, all access control decisions will be made using the
@ -336,10 +336,10 @@ import java.nio.ReadOnlyBufferException;
* There are two concurrency issues to be aware of: * There are two concurrency issues to be aware of:
* *
* <OL> * <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. * 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 * Applications must take care to ensure that generated packets
* are delivered in sequence. If packets arrive * are delivered in sequence. If packets arrive
* out-of-order, unexpected or fatal results may occur. * out-of-order, unexpected or fatal results may occur.
@ -354,7 +354,7 @@ import java.nio.ReadOnlyBufferException;
* </pre> * </pre>
* *
* As a corollary, two threads must not attempt to call the same method * 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. * because there is no way to guarantee the eventual packet ordering.
* </OL> * </OL>
* *
@ -374,7 +374,7 @@ public abstract class SSLEngine {
private int peerPort = -1; 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. * for an internal session reuse strategy.
* *
* @see SSLContext#createSSLEngine() * @see SSLContext#createSSLEngine()
@ -384,10 +384,10 @@ public abstract class SSLEngine {
} }
/** /**
* Constructor for an <code>SSLEngine</code>. * Constructor for an {@code SSLEngine}.
* <P> * <P>
* <code>SSLEngine</code> implementations may use the * {@code SSLEngine} implementations may use the
* <code>peerHost</code> and <code>peerPort</code> parameters as hints * {@code peerHost} and {@code peerPort} parameters as hints
* for their internal session reuse strategy. * for their internal session reuse strategy.
* <P> * <P>
* Some cipher suites (such as Kerberos) require remote hostname * Some cipher suites (such as Kerberos) require remote hostname
@ -395,7 +395,7 @@ public abstract class SSLEngine {
* constructor to use Kerberos. * constructor to use Kerberos.
* <P> * <P>
* The parameters are not authenticated by the * The parameters are not authenticated by the
* <code>SSLEngine</code>. * {@code SSLEngine}.
* *
* @param peerHost the name of the peer host * @param peerHost the name of the peer host
* @param peerPort the port number of the peer * @param peerPort the port number of the peer
@ -435,7 +435,7 @@ public abstract class SSLEngine {
/** /**
* Attempts to encode a buffer of plaintext application data into * Attempts to encode a buffer of plaintext application data into
* SSL/TLS network data. * SSL/TLS/DTLS network data.
* <P> * <P>
* An invocation of this method behaves in exactly the same manner * An invocation of this method behaves in exactly the same manner
* as the invocation: * as the invocation:
@ -445,20 +445,20 @@ public abstract class SSLEngine {
* </pre></blockquote> * </pre></blockquote>
* *
* @param src * @param src
* a <code>ByteBuffer</code> containing outbound application data * a {@code ByteBuffer} containing outbound application data
* @param dst * @param dst
* a <code>ByteBuffer</code> to hold outbound network data * a {@code ByteBuffer} to hold outbound network data
* @return an <code>SSLEngineResult</code> describing the result * @return an {@code SSLEngineResult} describing the result
* of this operation. * of this operation.
* @throws SSLException * @throws SSLException
* A problem was encountered while processing the * 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 * See the class description for more information on
* engine closure. * engine closure.
* @throws ReadOnlyBufferException * @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only. * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if either <code>src</code> or <code>dst</code> * if either {@code src} or {@code dst}
* is null. * is null.
* @throws IllegalStateException if the client/server mode * @throws IllegalStateException if the client/server mode
* has not yet been set. * has not yet been set.
@ -471,7 +471,7 @@ public abstract class SSLEngine {
/** /**
* Attempts to encode plaintext bytes from a sequence of data * Attempts to encode plaintext bytes from a sequence of data
* buffers into SSL/TLS network data. * buffers into SSL/TLS/DTLS network data.
* <P> * <P>
* An invocation of this method behaves in exactly the same manner * An invocation of this method behaves in exactly the same manner
* as the invocation: * as the invocation:
@ -481,22 +481,22 @@ public abstract class SSLEngine {
* </pre></blockquote> * </pre></blockquote>
* *
* @param srcs * @param srcs
* an array of <code>ByteBuffers</code> containing the * an array of {@code ByteBuffers} containing the
* outbound application data * outbound application data
* @param dst * @param dst
* a <code>ByteBuffer</code> to hold outbound network data * a {@code ByteBuffer} to hold outbound network data
* @return an <code>SSLEngineResult</code> describing the result * @return an {@code SSLEngineResult} describing the result
* of this operation. * of this operation.
* @throws SSLException * @throws SSLException
* A problem was encountered while processing the * 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 * See the class description for more information on
* engine closure. * engine closure.
* @throws ReadOnlyBufferException * @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only. * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if either <code>srcs</code> or <code>dst</code> * if either {@code srcs} or {@code dst}
* is null, or if any element in <code>srcs</code> is null. * is null, or if any element in {@code srcs} is null.
* @throws IllegalStateException if the client/server mode * @throws IllegalStateException if the client/server mode
* has not yet been set. * has not yet been set.
* @see #wrap(ByteBuffer [], int, int, ByteBuffer) * @see #wrap(ByteBuffer [], int, int, ByteBuffer)
@ -512,7 +512,7 @@ public abstract class SSLEngine {
/** /**
* Attempts to encode plaintext bytes from a subsequence of data * 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 * operation encodes, in a single invocation, a sequence of bytes
* from one or more of a given sequence of buffers. Gathering * from one or more of a given sequence of buffers. Gathering
* wraps are often useful when implementing network protocols or * wraps are often useful when implementing network protocols or
@ -535,49 +535,49 @@ public abstract class SSLEngine {
* it was generated. The application must properly synchronize * it was generated. The application must properly synchronize
* multiple calls to this method. * multiple calls to this method.
* <P> * <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. * handshake, this method will automatically start the handshake.
* <P> * <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 * consume as much source data as possible, but will never consume
* more than the sum of the bytes remaining in each buffer. Each * 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 * amount of data consumed or produced. The limits remain the
* same. * same.
* <P> * <P>
* The underlying memory used by the <code>srcs</code> and * The underlying memory used by the {@code srcs} and
* <code>dst ByteBuffer</code>s must not be the same. * {@code dst ByteBuffer}s must not be the same.
* <P> * <P>
* See the class description for more information on engine closure. * See the class description for more information on engine closure.
* *
* @param srcs * @param srcs
* an array of <code>ByteBuffers</code> containing the * an array of {@code ByteBuffers} containing the
* outbound application data * outbound application data
* @param offset * @param offset
* The offset within the buffer array of the first buffer from * The offset within the buffer array of the first buffer from
* which bytes are to be retrieved; it must be non-negative * 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 * @param length
* The maximum number of buffers to be accessed; it must be * The maximum number of buffers to be accessed; it must be
* non-negative and no larger than * non-negative and no larger than
* <code>srcs.length</code>&nbsp;-&nbsp;<code>offset</code> * {@code srcs.length}&nbsp;-&nbsp;{@code offset}
* @param dst * @param dst
* a <code>ByteBuffer</code> to hold outbound network data * a {@code ByteBuffer} to hold outbound network data
* @return an <code>SSLEngineResult</code> describing the result * @return an {@code SSLEngineResult} describing the result
* of this operation. * of this operation.
* @throws SSLException * @throws SSLException
* A problem was encountered while processing the * 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 * See the class description for more information on
* engine closure. * engine closure.
* @throws IndexOutOfBoundsException * @throws IndexOutOfBoundsException
* if the preconditions on the <code>offset</code> and * if the preconditions on the {@code offset} and
* <code>length</code> parameters do not hold. * {@code length} parameters do not hold.
* @throws ReadOnlyBufferException * @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only. * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if either <code>srcs</code> or <code>dst</code> * if either {@code srcs} or {@code dst}
* is null, or if any element in the <code>srcs</code> * is null, or if any element in the {@code srcs}
* subsequence specified is null. * subsequence specified is null.
* @throws IllegalStateException if the client/server mode * @throws IllegalStateException if the client/server mode
* has not yet been set. * has not yet been set.
@ -589,7 +589,7 @@ public abstract class SSLEngine {
int length, ByteBuffer dst) throws SSLException; 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. * application data buffer.
* <P> * <P>
* An invocation of this method behaves in exactly the same manner * An invocation of this method behaves in exactly the same manner
@ -600,20 +600,20 @@ public abstract class SSLEngine {
* </pre></blockquote> * </pre></blockquote>
* *
* @param src * @param src
* a <code>ByteBuffer</code> containing inbound network data. * a {@code ByteBuffer} containing inbound network data.
* @param dst * @param dst
* a <code>ByteBuffer</code> to hold inbound application data. * a {@code ByteBuffer} to hold inbound application data.
* @return an <code>SSLEngineResult</code> describing the result * @return an {@code SSLEngineResult} describing the result
* of this operation. * of this operation.
* @throws SSLException * @throws SSLException
* A problem was encountered while processing the * 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 * See the class description for more information on
* engine closure. * engine closure.
* @throws ReadOnlyBufferException * @throws ReadOnlyBufferException
* if the <code>dst</code> buffer is read-only. * if the {@code dst} buffer is read-only.
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if either <code>src</code> or <code>dst</code> * if either {@code src} or {@code dst}
* is null. * is null.
* @throws IllegalStateException if the client/server mode * @throws IllegalStateException if the client/server mode
* has not yet been set. * has not yet been set.
@ -625,7 +625,7 @@ public abstract class SSLEngine {
} }
/** /**
* 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. * application data buffers.
* <P> * <P>
* An invocation of this method behaves in exactly the same manner * An invocation of this method behaves in exactly the same manner
@ -636,22 +636,22 @@ public abstract class SSLEngine {
* </pre></blockquote> * </pre></blockquote>
* *
* @param src * @param src
* a <code>ByteBuffer</code> containing inbound network data. * a {@code ByteBuffer} containing inbound network data.
* @param dsts * @param dsts
* an array of <code>ByteBuffer</code>s to hold inbound * an array of {@code ByteBuffer}s to hold inbound
* application data. * application data.
* @return an <code>SSLEngineResult</code> describing the result * @return an {@code SSLEngineResult} describing the result
* of this operation. * of this operation.
* @throws SSLException * @throws SSLException
* A problem was encountered while processing the * 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 * See the class description for more information on
* engine closure. * engine closure.
* @throws ReadOnlyBufferException * @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 * @throws IllegalArgumentException
* if either <code>src</code> or <code>dsts</code> * if either {@code src} or {@code dsts}
* is null, or if any element in <code>dsts</code> is null. * is null, or if any element in {@code dsts} is null.
* @throws IllegalStateException if the client/server mode * @throws IllegalStateException if the client/server mode
* has not yet been set. * has not yet been set.
* @see #unwrap(ByteBuffer, ByteBuffer [], int, int) * @see #unwrap(ByteBuffer, ByteBuffer [], int, int)
@ -665,7 +665,7 @@ public abstract class SSLEngine {
} }
/** /**
* 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> * plaintext application data buffers. This <i>"scattering"</i>
* operation decodes, in a single invocation, a sequence of bytes * operation decodes, in a single invocation, a sequence of bytes
* into one or more of a given sequence of buffers. Scattering * into one or more of a given sequence of buffers. Scattering
@ -688,55 +688,55 @@ public abstract class SSLEngine {
* order it was received. The application must properly synchronize * order it was received. The application must properly synchronize
* multiple calls to this method. * multiple calls to this method.
* <P> * <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. * handshake, this method will automatically start the handshake.
* <P> * <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 * 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 * position is updated to reflect the amount of data consumed or
* produced. The limits remain the same. * produced. The limits remain the same.
* <P> * <P>
* The underlying memory used by the <code>src</code> and * The underlying memory used by the {@code src} and
* <code>dsts ByteBuffer</code>s must not be the same. * {@code dsts ByteBuffer}s must not be the same.
* <P> * <P>
* The inbound network buffer may be modified as a result of this * The inbound network buffer may be modified as a result of this
* call: therefore if the network data packet is required for some * call: therefore if the network data packet is required for some
* secondary purpose, the data should be duplicated before calling this * secondary purpose, the data should be duplicated before calling this
* method. Note: the network data will not be useful to a second * method. Note: the network data will not be useful to a second
* SSLEngine, as each SSLEngine contains unique random state which * SSLEngine, as each SSLEngine contains unique random state which
* influences the SSL/TLS messages. * influences the SSL/TLS/DTLS messages.
* <P> * <P>
* See the class description for more information on engine closure. * See the class description for more information on engine closure.
* *
* @param src * @param src
* a <code>ByteBuffer</code> containing inbound network data. * a {@code ByteBuffer} containing inbound network data.
* @param dsts * @param dsts
* an array of <code>ByteBuffer</code>s to hold inbound * an array of {@code ByteBuffer}s to hold inbound
* application data. * application data.
* @param offset * @param offset
* The offset within the buffer array of the first buffer from * The offset within the buffer array of the first buffer from
* which bytes are to be transferred; it must be non-negative * 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 * @param length
* The maximum number of buffers to be accessed; it must be * The maximum number of buffers to be accessed; it must be
* non-negative and no larger than * non-negative and no larger than
* <code>dsts.length</code>&nbsp;-&nbsp;<code>offset</code>. * {@code dsts.length}&nbsp;-&nbsp;{@code offset}.
* @return an <code>SSLEngineResult</code> describing the result * @return an {@code SSLEngineResult} describing the result
* of this operation. * of this operation.
* @throws SSLException * @throws SSLException
* A problem was encountered while processing the * 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 * See the class description for more information on
* engine closure. * engine closure.
* @throws IndexOutOfBoundsException * @throws IndexOutOfBoundsException
* If the preconditions on the <code>offset</code> and * If the preconditions on the {@code offset} and
* <code>length</code> parameters do not hold. * {@code length} parameters do not hold.
* @throws ReadOnlyBufferException * @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 * @throws IllegalArgumentException
* if either <code>src</code> or <code>dsts</code> * if either {@code src} or {@code dsts}
* is null, or if any element in the <code>dsts</code> * is null, or if any element in the {@code dsts}
* subsequence specified is null. * subsequence specified is null.
* @throws IllegalStateException if the client/server mode * @throws IllegalStateException if the client/server mode
* has not yet been set. * has not yet been set.
@ -749,19 +749,19 @@ public abstract class SSLEngine {
/** /**
* Returns a delegated <code>Runnable</code> task for * Returns a delegated {@code Runnable} task for
* this <code>SSLEngine</code>. * this {@code SSLEngine}.
* <P> * <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 * operations that block, or may take an extended period of time to
* complete. This method is used to obtain an outstanding {@link * complete. This method is used to obtain an outstanding {@link
* java.lang.Runnable} operation (task). Each task must be assigned * java.lang.Runnable} operation (task). Each task must be assigned
* a thread (possibly the current) to perform the {@link * a thread (possibly the current) to perform the {@link
* java.lang.Runnable#run() run} operation. Once the * 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. * is no longer needed and may be discarded.
* <P> * <P>
* Delegated tasks run in the <code>AccessControlContext</code> * Delegated tasks run in the {@code AccessControlContext}
* in place when this object was created. * in place when this object was created.
* <P> * <P>
* A call to this method will return each outstanding task * A call to this method will return each outstanding task
@ -769,7 +769,7 @@ public abstract class SSLEngine {
* <P> * <P>
* Multiple delegated tasks can be run in parallel. * 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. * if none are available.
*/ */
public abstract Runnable getDelegatedTask(); public abstract Runnable getDelegatedTask();
@ -777,7 +777,7 @@ public abstract class SSLEngine {
/** /**
* Signals that no more inbound network data will be sent * Signals that no more inbound network data will be sent
* to this <code>SSLEngine</code>. * to this {@code SSLEngine}.
* <P> * <P>
* If the application initiated the closing process by calling * If the application initiated the closing process by calling
* {@link #closeOutbound()}, under some circumstances it is not * {@link #closeOutbound()}, under some circumstances it is not
@ -789,9 +789,9 @@ public abstract class SSLEngine {
* <P> * <P>
* But if the application did not initiate the closure process, or * But if the application did not initiate the closure process, or
* if the circumstances above do not apply, this method should be * 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 * 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. * detecting possible truncation attacks.
* <P> * <P>
* This method is idempotent: if the inbound side has already * This method is idempotent: if the inbound side has already
@ -801,7 +801,7 @@ public abstract class SSLEngine {
* called to flush any remaining handshake data. * called to flush any remaining handshake data.
* *
* @throws SSLException * @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. * notification message from the peer.
* *
* @see #isInboundDone() * @see #isInboundDone()
@ -814,7 +814,7 @@ public abstract class SSLEngine {
* Returns whether {@link #unwrap(ByteBuffer, ByteBuffer)} will * Returns whether {@link #unwrap(ByteBuffer, ByteBuffer)} will
* accept any more inbound data messages. * 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, * consume anymore network data (and by implication,
* will not produce any more application data.) * will not produce any more application data.)
* @see #closeInbound() * @see #closeInbound()
@ -824,7 +824,7 @@ public abstract class SSLEngine {
/** /**
* Signals that no more outbound application data will be sent * Signals that no more outbound application data will be sent
* on this <code>SSLEngine</code>. * on this {@code SSLEngine}.
* <P> * <P>
* This method is idempotent: if the outbound side has already * This method is idempotent: if the outbound side has already
* been closed, this method does not do anything. * been closed, this method does not do anything.
@ -841,12 +841,12 @@ public abstract class SSLEngine {
* Returns whether {@link #wrap(ByteBuffer, ByteBuffer)} will * Returns whether {@link #wrap(ByteBuffer, ByteBuffer)} will
* produce any more outbound data messages. * produce any more outbound data messages.
* <P> * <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. * 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. * 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 * any more network data
* *
* @see #closeOutbound() * @see #closeOutbound()
@ -890,10 +890,10 @@ public abstract class SSLEngine {
/** /**
* Sets the cipher suites enabled for use on this engine. * Sets the cipher suites enabled for use on this engine.
* <P> * <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 * been listed by getSupportedCipherSuites(), or the method will
* fail. Following a successful call to this method, only suites * 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> * <P>
* See {@link #getEnabledCipherSuites()} for more information * See {@link #getEnabledCipherSuites()} for more information
* on why a specific cipher suite may never be used on a engine. * on why a specific cipher suite may never be used on a engine.
@ -910,7 +910,7 @@ public abstract class SSLEngine {
/** /**
* Returns the names of the protocols which could be enabled for use * 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 * @return an array of protocols supported
*/ */
@ -919,7 +919,7 @@ public abstract class SSLEngine {
/** /**
* Returns the names of the protocol versions which are currently * 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 * @return an array of protocols
* @see #setEnabledProtocols(String []) * @see #setEnabledProtocols(String [])
@ -932,7 +932,7 @@ public abstract class SSLEngine {
* <P> * <P>
* The protocols must have been listed by getSupportedProtocols() * The protocols must have been listed by getSupportedProtocols()
* as being supported. Following a successful call to this method, * 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. * are enabled for use.
* *
* @param protocols Names of all the protocols to enable. * @param protocols Names of all the protocols to enable.
@ -945,8 +945,8 @@ public abstract class SSLEngine {
/** /**
* Returns the <code>SSLSession</code> in use in this * Returns the {@code SSLSession} in use in this
* <code>SSLEngine</code>. * {@code SSLEngine}.
* <P> * <P>
* These can be long lived, and frequently correspond to an entire * These can be long lived, and frequently correspond to an entire
* login session for some user. The session specifies a particular * login session for some user. The session specifies a particular
@ -961,22 +961,22 @@ public abstract class SSLEngine {
* a session object which reports an invalid cipher suite of * a session object which reports an invalid cipher suite of
* "SSL_NULL_WITH_NULL_NULL". * "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 * @see SSLSession
*/ */
public abstract SSLSession getSession(); 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. * handshake.
* <p> * <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 * an instance of this class, but before the {@code SSLSession} has
* been completely initialized and made available via {@code getSession}. * been completely initialized and made available via {@code getSession}.
* For example, the list of valid signature algorithms may restrict * For example, the list of valid signature algorithms may restrict
* the type of certificates that can used during TrustManager * 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. * resized to better support the network environment.
* <p> * <p>
* This method provides early access to the {@code SSLSession} being * This method provides early access to the {@code SSLSession} being
@ -1012,26 +1012,26 @@ public abstract class SSLEngine {
* Initiates handshaking (initial or renegotiation) on this SSLEngine. * Initiates handshaking (initial or renegotiation) on this SSLEngine.
* <P> * <P>
* This method is not needed for the initial handshake, as the * 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. * implicitly call this method if handshaking has not already begun.
* <P> * <P>
* Note that the peer may also request a session renegotiation with * 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. * session renegotiate handshake message.
* <P> * <P>
* Unlike the {@link SSLSocket#startHandshake() * Unlike the {@link SSLSocket#startHandshake()
* SSLSocket#startHandshake()} method, this method does not block * SSLSocket#startHandshake()} method, this method does not block
* until handshaking is completed. * until handshaking is completed.
* <P> * <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. * session should be invalidated prior to calling this method.
* <P> * <P>
* Some protocols may not support multiple handshakes on an existing * 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 * @throws SSLException
* if a problem was encountered while signaling the * 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 * See the class description for more information on
* engine closure. * engine closure.
* @throws IllegalStateException if the client/server mode * @throws IllegalStateException if the client/server mode
@ -1042,9 +1042,9 @@ public abstract class SSLEngine {
/** /**
* 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(); public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus();

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,13 +27,13 @@ package javax.net.ssl;
/** /**
* An encapsulation of the result state produced by * 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 * <p> A {@code SSLEngine} provides a means for establishing
* secure communication sessions between two peers. <code>SSLEngine</code> * secure communication sessions between two peers. {@code SSLEngine}
* operations typically consume bytes from an input buffer and produce * operations typically consume bytes from an input buffer and produce
* bytes in an output buffer. This class provides operational result * 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 * indications of what operations are needed to finish an
* ongoing handshake. Lastly, it reports the number of bytes consumed * ongoing handshake. Lastly, it reports the number of bytes consumed
* and produced as a result of this operation. * and produced as a result of this operation.
@ -49,12 +49,12 @@ package javax.net.ssl;
public class SSLEngineResult { public class SSLEngineResult {
/** /**
* An <code>SSLEngineResult</code> enum describing the overall result * An {@code SSLEngineResult} enum describing the overall result
* of the <code>SSLEngine</code> operation. * of the {@code SSLEngine} operation.
* *
* The <code>Status</code> value does not reflect the * The {@code Status} value does not reflect the
* state of a <code>SSLEngine</code> handshake currently * state of a {@code SSLEngine} handshake currently
* in progress. The <code>SSLEngineResult's HandshakeStatus</code> * in progress. The {@code SSLEngineResult's HandshakeStatus}
* should be consulted for that information. * should be consulted for that information.
* *
* @author Brad R. Wetmore * @author Brad R. Wetmore
@ -63,7 +63,7 @@ public class SSLEngineResult {
public static enum Status { 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 * incoming data because there were not enough source bytes
* available to make a complete packet. * available to make a complete packet.
* *
@ -73,7 +73,7 @@ public class SSLEngineResult {
BUFFER_UNDERFLOW, 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 * operation because there are not enough bytes available in the
* destination buffer to hold the result. * destination buffer to hold the result.
* <P> * <P>
@ -85,22 +85,22 @@ public class SSLEngineResult {
BUFFER_OVERFLOW, BUFFER_OVERFLOW,
/** /**
* The <code>SSLEngine</code> completed the operation, and * The {@code SSLEngine} completed the operation, and
* is available to process similar calls. * is available to process similar calls.
*/ */
OK, OK,
/** /**
* The operation just closed this side of the * 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. * could not be completed because it was already closed.
*/ */
CLOSED; CLOSED;
} }
/** /**
* An <code>SSLEngineResult</code> enum describing the current * An {@code SSLEngineResult} enum describing the current
* handshaking state of this <code>SSLEngine</code>. * handshaking state of this {@code SSLEngine}.
* *
* @author Brad R. Wetmore * @author Brad R. Wetmore
* @since 1.5 * @since 1.5
@ -108,17 +108,17 @@ public class SSLEngineResult {
public static enum HandshakeStatus { public static enum HandshakeStatus {
/** /**
* The <code>SSLEngine</code> is not currently handshaking. * The {@code SSLEngine} is not currently handshaking.
*/ */
NOT_HANDSHAKING, NOT_HANDSHAKING,
/** /**
* The <code>SSLEngine</code> has just finished handshaking. * The {@code SSLEngine} has just finished handshaking.
* <P> * <P>
* This value is only generated by a call to * 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 * finishes a handshake. It is never generated by
* <code>SSLEngine.getHandshakeStatus()</code>. * {@code SSLEngine.getHandshakeStatus()}.
* *
* @see SSLEngine#wrap(ByteBuffer, ByteBuffer) * @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
* @see SSLEngine#unwrap(ByteBuffer, ByteBuffer) * @see SSLEngine#unwrap(ByteBuffer, ByteBuffer)
@ -127,7 +127,7 @@ public class SSLEngineResult {
FINISHED, 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. * delegated tasks before handshaking can continue.
* *
* @see SSLEngine#getDelegatedTask() * @see SSLEngine#getDelegatedTask()
@ -135,8 +135,8 @@ public class SSLEngineResult {
NEED_TASK, NEED_TASK,
/** /**
* The <code>SSLEngine</code> must send data to the remote side * The {@code SSLEngine} must send data to the remote side
* before handshaking can continue, so <code>SSLEngine.wrap()</code> * before handshaking can continue, so {@code SSLEngine.wrap()}
* should be called. * should be called.
* *
* @see SSLEngine#wrap(ByteBuffer, ByteBuffer) * @see SSLEngine#wrap(ByteBuffer, ByteBuffer)
@ -144,10 +144,22 @@ public class SSLEngineResult {
NEED_WRAP, 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. * 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 @@ public class SSLEngineResult {
private final HandshakeStatus handshakeStatus; private final HandshakeStatus handshakeStatus;
private final int bytesConsumed; private final int bytesConsumed;
private final int bytesProduced; private final int bytesProduced;
private final long sequenceNumber;
/** /**
* Initializes a new instance of this class. * Initializes a new instance of this class.
@ -172,12 +185,44 @@ public class SSLEngineResult {
* the number of bytes placed into the destination ByteBuffer * the number of bytes placed into the destination ByteBuffer
* *
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if the <code>status</code> or <code>handshakeStatus</code> * if the {@code status} or {@code handshakeStatus}
* arguments are null, or if <code>bytesConsumed</code> or * arguments are null, or if {@code bytesConsumed} or
* <code>bytesProduced</code> is negative. * {@code bytesProduced} is negative.
*/ */
public SSLEngineResult(Status status, HandshakeStatus handshakeStatus, public SSLEngineResult(Status status, HandshakeStatus handshakeStatus,
int bytesConsumed, int bytesProduced) { 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) || if ((status == null) || (handshakeStatus == null) ||
(bytesConsumed < 0) || (bytesProduced < 0)) { (bytesConsumed < 0) || (bytesProduced < 0)) {
@ -188,10 +233,11 @@ public class SSLEngineResult {
this.handshakeStatus = handshakeStatus; this.handshakeStatus = handshakeStatus;
this.bytesConsumed = bytesConsumed; this.bytesConsumed = bytesConsumed;
this.bytesProduced = bytesProduced; 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 * @return the return value
*/ */
@ -200,7 +246,7 @@ public class SSLEngineResult {
} }
/** /**
* Gets the handshake status of this <code>SSLEngine</code> * Gets the handshake status of this {@code SSLEngine}
* operation. * operation.
* *
* @return the handshake status * @return the handshake status
@ -227,6 +273,41 @@ public class SSLEngineResult {
return bytesProduced; return bytesProduced;
} }
/**
* 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. * Returns a String representation of this object.
*/ */
@ -235,6 +316,8 @@ public class SSLEngineResult {
return ("Status = " + status + return ("Status = " + status +
" HandshakeStatus = " + handshakeStatus + " HandshakeStatus = " + handshakeStatus +
"\nbytesConsumed = " + bytesConsumed + "\nbytesConsumed = " + bytesConsumed +
" bytesProduced = " + bytesProduced); " bytesProduced = " + bytesProduced +
(sequenceNumber == -1 ? "" :
" sequenceNumber = " + Long.toUnsignedString(sequenceNumber)));
} }
} }

View file

@ -35,22 +35,22 @@ import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
/** /**
* Encapsulates parameters for an SSL/TLS connection. The parameters * Encapsulates parameters for an SSL/TLS/DTLS connection. The parameters
* are the list of ciphersuites to be accepted in an SSL/TLS handshake, * are the list of ciphersuites to be accepted in an SSL/TLS/DTLS handshake,
* the list of protocols to be allowed, the endpoint identification * the list of protocols to be allowed, the endpoint identification
* algorithm during SSL/TLS handshaking, the Server Name Indication (SNI), * algorithm during SSL/TLS/DTLS handshaking, the Server Name Indication (SNI),
* the algorithm constraints and whether SSL/TLS servers should request * the maximum network packet size, the algorithm constraints and whether
* or require client authentication, etc. * SSL/TLS/DTLS servers should request or require client authentication, etc.
* <p> * <p>
* SSLParameters can be created via the constructors in this class. * 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 * methods in
* {@link SSLSocket#getSSLParameters SSLSocket} and * {@link SSLSocket#getSSLParameters SSLSocket} and
* {@link SSLServerSocket#getSSLParameters SSLServerSocket} and * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and
* {@link SSLEngine#getSSLParameters SSLEngine} or the * {@link SSLEngine#getSSLParameters SSLEngine} or the
* {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and
* {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()}
* methods in <code>SSLContext</code>. * methods in {@code SSLContext}.
* <p> * <p>
* SSLParameters can be applied to a connection via the methods * SSLParameters can be applied to a connection via the methods
* {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and
@ -74,14 +74,18 @@ public class SSLParameters {
private Map<Integer, SNIServerName> sniNames = null; private Map<Integer, SNIServerName> sniNames = null;
private Map<Integer, SNIMatcher> sniMatchers = null; private Map<Integer, SNIMatcher> sniMatchers = null;
private boolean preferLocalCipherSuites; private boolean preferLocalCipherSuites;
private boolean enableRetransmissions = true;
private int maximumPacketSize = 0;
/** /**
* Constructs SSLParameters. * Constructs SSLParameters.
* <p> * <p>
* The values of cipherSuites, protocols, cryptographic algorithm * The values of cipherSuites, protocols, cryptographic algorithm
* constraints, endpoint identification algorithm, server names and * constraints, endpoint identification algorithm, server names and
* server name matchers are set to <code>null</code>, useCipherSuitesOrder, * server name matchers are set to {@code null}; useCipherSuitesOrder,
* wantClientAuth and needClientAuth are set to <code>false</code>. * 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() { public SSLParameters() {
// empty // empty
@ -92,7 +96,7 @@ public class SSLParameters {
* <p> * <p>
* Calling this constructor is equivalent to calling the no-args * Calling this constructor is equivalent to calling the no-args
* constructor followed by * constructor followed by
* <code>setCipherSuites(cipherSuites);</code>. * {@code setCipherSuites(cipherSuites);}.
* *
* @param cipherSuites the array of ciphersuites (or null) * @param cipherSuites the array of ciphersuites (or null)
*/ */
@ -106,7 +110,7 @@ public class SSLParameters {
* <p> * <p>
* Calling this constructor is equivalent to calling the no-args * Calling this constructor is equivalent to calling the no-args
* constructor followed by * constructor followed by
* <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>. * {@code setCipherSuites(cipherSuites); setProtocols(protocols);}.
* *
* @param cipherSuites the array of ciphersuites (or null) * @param cipherSuites the array of ciphersuites (or null)
* @param protocols the array of protocols (or null) * @param protocols the array of protocols (or null)
@ -171,7 +175,7 @@ public class SSLParameters {
/** /**
* Sets whether client authentication should be requested. Calling * 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 * @param wantClientAuth whether client authentication should be requested
*/ */
@ -191,7 +195,7 @@ public class SSLParameters {
/** /**
* Sets whether client authentication should be required. Calling * 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 * @param needClientAuth whether client authentication should be required
*/ */
@ -218,9 +222,9 @@ public class SSLParameters {
* Sets the cryptographic algorithm constraints, which will be used * Sets the cryptographic algorithm constraints, which will be used
* in addition to any configured by the runtime environment. * in addition to any configured by the runtime environment.
* <p> * <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 * 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) * @param constraints the algorithm constraints (or null)
* *
@ -249,9 +253,9 @@ public class SSLParameters {
/** /**
* Sets the endpoint identification algorithm. * Sets the endpoint identification algorithm.
* <p> * <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 * 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 * @param algorithm The standard string name of the endpoint
* identification algorithm (or null). See Appendix A in the <a href= * identification algorithm (or null). See Appendix A in the <a href=
@ -317,7 +321,7 @@ public class SSLParameters {
* This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s * This method is only useful to {@link SSLSocket}s or {@link SSLEngine}s
* operating in client mode. * operating in client mode.
* <P> * <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 * may specify a default value for a certain server name type. In
* client mode, it is recommended that, by default, providers should * client mode, it is recommended that, by default, providers should
* include the server name indication whenever the server can be located * include the server name indication whenever the server can be located
@ -440,7 +444,7 @@ public class SSLParameters {
* *
* @param honorOrder whether local cipher suites order in * @param honorOrder whether local cipher suites order in
* {@code #getCipherSuites} should be honored during * {@code #getCipherSuites} should be honored during
* SSL/TLS handshaking. * SSL/TLS/DTLS handshaking.
* *
* @see #getUseCipherSuitesOrder() * @see #getUseCipherSuitesOrder()
* *
@ -454,7 +458,7 @@ public class SSLParameters {
* Returns whether the local cipher suites preference should be honored. * Returns whether the local cipher suites preference should be honored.
* *
* @return whether local cipher suites order in {@code #getCipherSuites} * @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) * @see #setUseCipherSuitesOrder(boolean)
* *
@ -463,5 +467,107 @@ public class SSLParameters {
public final boolean getUseCipherSuitesOrder() { public final boolean getUseCipherSuitesOrder() {
return preferLocalCipherSuites; 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;
}
} }

View file

@ -35,7 +35,7 @@ import java.security.Principal;
* also be replaced by a different session. Sessions are created, or * also be replaced by a different session. Sessions are created, or
* rejoined, as part of the SSL handshaking protocol. Sessions may be * rejoined, as part of the SSL handshaking protocol. Sessions may be
* invalidated due to policies affecting security or resource usage, * 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. * Session management policies are typically used to tune performance.
* *
* <P> In addition to the standard session attributes, SSL sessions expose * <P> In addition to the standard session attributes, SSL sessions expose
@ -82,8 +82,8 @@ public interface SSLSession {
* security manager installed, the caller may require * security manager installed, the caller may require
* permission to access it or a security exception may be thrown. * permission to access it or a security exception may be thrown.
* In a Java environment, the security manager's * In a Java environment, the security manager's
* <code>checkPermission</code> method is called with a * {@code checkPermission} method is called with a
* <code>SSLPermission("getSSLSessionContext")</code> permission. * {@code SSLPermission("getSSLSessionContext")} permission.
* *
* @throws SecurityException if the calling thread does not have * @throws SecurityException if the calling thread does not have
* permission to get SSL session context. * permission to get SSL session context.
@ -148,14 +148,14 @@ public interface SSLSession {
/** /**
* *
* Binds the specified <code>value</code> object into the * Binds the specified {@code value} object into the
* session's application layer data * session's application layer data
* with the given <code>name</code>. * with the given {@code name}.
* <P> * <P>
* Any existing binding using the same <code>name</code> is * Any existing binding using the same {@code name} is
* replaced. If the new (or existing) <code>value</code> implements the * replaced. If the new (or existing) {@code value} implements the
* <code>SSLSessionBindingListener</code> interface, the object * {@code SSLSessionBindingListener} interface, the object
* represented by <code>value</code> is notified appropriately. * represented by {@code value} is notified appropriately.
* <p> * <p>
* For security reasons, the same named values may not be * For security reasons, the same named values may not be
* visible across different access control contexts. * visible across different access control contexts.
@ -187,7 +187,7 @@ public interface SSLSession {
* Removes the object bound to the given name in the session's * Removes the object bound to the given name in the session's
* application layer data. Does nothing if there is no object * application layer data. Does nothing if there is no object
* bound to the given name. If the bound existing 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. * it is notified appropriately.
* <p> * <p>
* For security reasons, the same named values may not be * For security reasons, the same named values may not be
@ -349,7 +349,7 @@ public interface SSLSession {
* by this method. * by this method.
* <P> * <P>
* This value is not authenticated and should not be relied upon. * 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. * strategies.
* *
* @return the host name of the peer host, or null if no information * @return the host name of the peer host, or null if no information
@ -364,7 +364,7 @@ public interface SSLSession {
* the client, it is the server's port number. * the client, it is the server's port number.
* <P> * <P>
* This value is not authenticated and should not be relied upon. * 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. * strategies.
* *
* @return the port number of the peer host, or -1 if no information * @return the port number of the peer host, or -1 if no information
@ -375,14 +375,14 @@ public interface SSLSession {
public int getPeerPort(); public int getPeerPort();
/** /**
* Gets the current size of the largest SSL/TLS packet that is expected * Gets the current size of the largest SSL/TLS/DTLS packet that is
* when using this session. * expected when using this session.
* <P> * <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 * 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 * 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 * @return the current maximum expected network packet size
* *
@ -398,7 +398,7 @@ public interface SSLSession {
* Gets the current size of the largest application data that is * Gets the current size of the largest application data that is
* expected when using this session. * expected when using this session.
* <P> * <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 * enough to hold the application data from any inbound network
* application data packet received. Typically, outbound * application data packet received. Typically, outbound
* application data buffers can be of any size. * application data buffers can be of any size.

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -32,16 +32,17 @@ import java.security.cert.X509Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
/** /**
* Extensions to the <code>X509TrustManager</code> interface to support * Extensions to the {@code X509TrustManager} interface to support
* SSL/TLS connection sensitive trust management. * SSL/TLS/DTLS connection sensitive trust management.
* <p> * <p>
* To prevent man-in-the-middle attacks, hostname checks can be done * To prevent man-in-the-middle attacks, hostname checks can be done
* to verify that the hostname in an end-entity certificate matches the * to verify that the hostname in an end-entity certificate matches the
* targeted hostname. TLS does not require such checks, but some protocols * targeted hostname. TLS/DTLS does not require such checks, but some
* over TLS (such as HTTPS) do. In earlier versions of the JDK, the * protocols over TLS/DTLS (such as HTTPS) do. In earlier versions of the
* certificate chain checks were done at the SSL/TLS layer, and the hostname * JDK, the certificate chain checks were done at the SSL/TLS/DTLS layer,
* verification checks were done at the layer over TLS. This class allows * and the hostname verification checks were done at the layer over TLS/DTLS.
* for the checking to be done during a single call to this class. * This class allows for the checking to be done during a single call to
* this class.
* <p> * <p>
* RFC 2830 defines the server identification specification for the "LDAPS" * RFC 2830 defines the server identification specification for the "LDAPS"
* algorithm. RFC 2818 defines both the server identification and the * algorithm. RFC 2818 defines both the server identification and the
@ -62,17 +63,17 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used. For instance, if RSAPublicKey is used, the authType * used. For instance, if RSAPublicKey is used, the authType
* should be "RSA". Checking is case-sensitive. * should be "RSA". Checking is case-sensitive.
* <p> * <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 * {@link javax.net.ssl.SSLSocket}, and the endpoint identification
* algorithm of the <code>SSLParameters</code> is non-empty, to prevent * algorithm of the {@code SSLParameters} is non-empty, to prevent
* man-in-the-middle attacks, the address that the <code>socket</code> * man-in-the-middle attacks, the address that the {@code socket}
* connected to should be checked against the peer's identity presented * connected to should be checked against the peer's identity presented
* in the end-entity X509 certificate, as specified in the endpoint * in the end-entity X509 certificate, as specified in the endpoint
* identification algorithm. * identification algorithm.
* <p> * <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 * {@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 * certification path, fields such as subject public key, the signature
* algorithm, key usage, extended key usage, etc. need to conform to the * algorithm, key usage, extended key usage, etc. need to conform to the
* algorithm constraints in place on this socket. * algorithm constraints in place on this socket.
@ -83,8 +84,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check * can be null, which indicates that implementations need not check
* the ssl parameters * the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed * @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length * in for the {@code chain} parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted * @throws CertificateException if the certificate chain is not trusted
* by this TrustManager * by this TrustManager
* *
@ -110,17 +111,17 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used for the key exchange, and RSA when the key from the server * used for the key exchange, and RSA when the key from the server
* certificate is used. Checking is case-sensitive. * certificate is used. Checking is case-sensitive.
* <p> * <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 * {@link javax.net.ssl.SSLSocket}, and the endpoint identification
* algorithm of the <code>SSLParameters</code> is non-empty, to prevent * algorithm of the {@code SSLParameters} is non-empty, to prevent
* man-in-the-middle attacks, the address that the <code>socket</code> * man-in-the-middle attacks, the address that the {@code socket}
* connected to should be checked against the peer's identity presented * connected to should be checked against the peer's identity presented
* in the end-entity X509 certificate, as specified in the endpoint * in the end-entity X509 certificate, as specified in the endpoint
* identification algorithm. * identification algorithm.
* <p> * <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 * {@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 * certification path, fields such as subject public key, the signature
* algorithm, key usage, extended key usage, etc. need to conform to the * algorithm, key usage, extended key usage, etc. need to conform to the
* algorithm constraints in place on this socket. * algorithm constraints in place on this socket.
@ -131,8 +132,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check * can be null, which indicates that implementations need not check
* the ssl parameters * the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed * @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length * in for the {@code chain} parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted * @throws CertificateException if the certificate chain is not trusted
* by this TrustManager * by this TrustManager
* *
@ -153,15 +154,15 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used. For instance, if RSAPublicKey is used, the authType * used. For instance, if RSAPublicKey is used, the authType
* should be "RSA". Checking is case-sensitive. * should be "RSA". Checking is case-sensitive.
* <p> * <p>
* If the <code>engine</code> parameter is available, and the endpoint * If the {@code engine} parameter is available, and the endpoint
* identification algorithm of the <code>SSLParameters</code> is * identification algorithm of the {@code SSLParameters} is
* non-empty, to prevent man-in-the-middle attacks, the address that * 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, * the peer's identity presented in the end-entity X509 certificate,
* as specified in the endpoint identification algorithm. * as specified in the endpoint identification algorithm.
* <p> * <p>
* If the <code>engine</code> parameter is available, and the algorithm * If the {@code engine} parameter is available, and the algorithm
* constraints of the <code>SSLParameters</code> is non-null, for every * constraints of the {@code SSLParameters} is non-null, for every
* certificate in the certification path, fields such as subject public * certificate in the certification path, fields such as subject public
* key, the signature algorithm, key usage, extended key usage, etc. * key, the signature algorithm, key usage, extended key usage, etc.
* need to conform to the algorithm constraints in place on this engine. * need to conform to the algorithm constraints in place on this engine.
@ -172,8 +173,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check * can be null, which indicates that implementations need not check
* the ssl parameters * the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed * @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length * in for the {@code chain} parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted * @throws CertificateException if the certificate chain is not trusted
* by this TrustManager * by this TrustManager
* *
@ -199,15 +200,15 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* used for the key exchange, and RSA when the key from the server * used for the key exchange, and RSA when the key from the server
* certificate is used. Checking is case-sensitive. * certificate is used. Checking is case-sensitive.
* <p> * <p>
* If the <code>engine</code> parameter is available, and the endpoint * If the {@code engine} parameter is available, and the endpoint
* identification algorithm of the <code>SSLParameters</code> is * identification algorithm of the {@code SSLParameters} is
* non-empty, to prevent man-in-the-middle attacks, the address that * 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, * the peer's identity presented in the end-entity X509 certificate,
* as specified in the endpoint identification algorithm. * as specified in the endpoint identification algorithm.
* <p> * <p>
* If the <code>engine</code> parameter is available, and the algorithm * If the {@code engine} parameter is available, and the algorithm
* constraints of the <code>SSLParameters</code> is non-null, for every * constraints of the {@code SSLParameters} is non-null, for every
* certificate in the certification path, fields such as subject public * certificate in the certification path, fields such as subject public
* key, the signature algorithm, key usage, extended key usage, etc. * key, the signature algorithm, key usage, extended key usage, etc.
* need to conform to the algorithm constraints in place on this engine. * need to conform to the algorithm constraints in place on this engine.
@ -218,8 +219,8 @@ public abstract class X509ExtendedTrustManager implements X509TrustManager {
* can be null, which indicates that implementations need not check * can be null, which indicates that implementations need not check
* the ssl parameters * the ssl parameters
* @throws IllegalArgumentException if null or zero-length array is passed * @throws IllegalArgumentException if null or zero-length array is passed
* in for the <code>chain</code> parameter or if null or zero-length * in for the {@code chain} parameter or if null or zero-length
* string is passed in for the <code>authType</code> parameter * string is passed in for the {@code authType} parameter
* @throws CertificateException if the certificate chain is not trusted * @throws CertificateException if the certificate chain is not trusted
* by this TrustManager * by this TrustManager
* *

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,41 +26,54 @@
package sun.security.ssl; 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(). * 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 * @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() // 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; // the related socket of the input stream
InputRecord r; 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 // One element array used to implement the single byte read() method
private final byte[] oneByte = new byte[1]; private final byte[] oneByte = new byte[1];
AppInputStream(SSLSocketImpl conn) { AppInputStream(SSLSocketImpl conn) {
r = new InputRecord(); this.buffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
c = conn; this.socket = conn;
this.appDataIsAvailable = false;
} }
/** /**
* Return the minimum number of bytes that can be read without blocking. * Return the minimum number of bytes that can be read without blocking.
*
* Currently not synchronized. * Currently not synchronized.
*/ */
@Override @Override
public int available() throws IOException { public int available() throws IOException {
if (c.checkEOF() || (r.isAppDataValid() == false)) { if ((!appDataIsAvailable) || socket.checkEOF()) {
return 0; return 0;
} }
return r.available();
return buffer.remaining();
} }
/** /**
@ -72,17 +85,21 @@ class AppInputStream extends InputStream {
if (n <= 0) { // EOF if (n <= 0) { // EOF
return -1; 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 * If the layer above needs more data, it asks for more, so we
* are responsible only for blocking to fill at most one buffer, * are responsible only for blocking to fill at most one buffer,
* and returning "-1" on non-fault EOF status. * and returning "-1" on non-fault EOF status.
*/ */
@Override @Override
public synchronized int read(byte b[], int off, int len) public synchronized int read(byte[] b, int off, int len)
throws IOException { throws IOException {
if (b == null) { if (b == null) {
throw new NullPointerException(); throw new NullPointerException();
@ -92,28 +109,69 @@ class AppInputStream extends InputStream {
return 0; return 0;
} }
if (c.checkEOF()) { if (socket.checkEOF()) {
return -1; 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 { try {
/* /*
* Read data if needed ... notice that the connection guarantees * Read data if needed ... notice that the connection guarantees
* that handshake, alert, and change cipher spec data streams are * that handshake, alert, and change cipher spec data streams are
* handled as they arrive, so we never see them here. * handled as they arrive, so we never see them here.
*/ */
while (r.available() == 0) { while(volume == 0) {
c.readDataRecord(r); // Clear the buffer for a new record reading.
if (c.checkEOF()) { 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; 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()); int howmany = Math.min(len, volume);
howmany = r.read(b, off, howmany); buffer.get(b, off, howmany);
return howmany; return howmany;
} catch (Exception e) { } catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate // shutdown and rethrow (wrapped) exception as appropriate
c.handleException(e); socket.handleException(e);
// dummy for compiler // dummy for compiler
return -1; return -1;
} }
@ -147,9 +205,8 @@ class AppInputStream extends InputStream {
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
c.close(); socket.close();
} }
// inherit default mark/reset behavior (throw Exceptions) from InputStream // inherit default mark/reset behavior (throw Exceptions) from InputStream
} }

View file

@ -23,41 +23,33 @@
* questions. * questions.
*/ */
package sun.security.ssl; package sun.security.ssl;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
/* /*
* Output stream for application data. This is the kind of stream * OutputStream for application data as returned by SSLSocket.getOutputStream().
* 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.
* *
* @author David Brownell * @author David Brownell
*/ */
class AppOutputStream extends OutputStream { class AppOutputStream extends OutputStream {
private SSLSocketImpl c; private SSLSocketImpl socket;
OutputRecord r;
// One element array used to implement the write(byte) method // One element array used to implement the write(byte) method
private final byte[] oneByte = new byte[1]; private final byte[] oneByte = new byte[1];
AppOutputStream(SSLSocketImpl conn) { AppOutputStream(SSLSocketImpl conn) {
r = new OutputRecord(Record.ct_application_data); this.socket = conn;
c = conn;
} }
/** /**
* Write the data out, NOW. * Write the data out, NOW.
*/ */
@Override @Override
synchronized public void write(byte b[], int off, int len) synchronized public void write(byte[] b, int off, int len)
throws IOException { throws IOException {
if (b == null) { if (b == null) {
throw new NullPointerException(); throw new NullPointerException();
@ -68,64 +60,15 @@ class AppOutputStream extends OutputStream {
} }
// check if the Socket is invalid (error or closed) // check if the Socket is invalid (error or closed)
c.checkWrite(); socket.checkWrite();
/* // Delegate the writing to the underlying socket.
* 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.
try { try {
do { socket.writeRecord(b, off, len);
boolean holdRecord = false; socket.checkWrite();
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);
} catch (Exception e) { } catch (Exception e) {
// shutdown and rethrow (wrapped) exception as appropriate // shutdown and rethrow (wrapped) exception as appropriate
c.handleException(e); socket.handleException(e);
} }
} }
@ -143,7 +86,7 @@ class AppOutputStream extends OutputStream {
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
c.close(); socket.close();
} }
// inherit no-op flush() // inherit no-op flush()

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,19 +28,26 @@ package sun.security.ssl;
import java.util.Arrays; 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 * which encapsulates a sequence number and ensures that attempts to
* delete or reorder messages can be detected. * delete or reorder messages can be detected.
* *
* Each SSL/TLS connection state contains a sequence number, which * Each connection state contains a sequence number, which is maintained
* is maintained separately for read and write states. The sequence * separately for read and write states.
* number MUST be set to zero whenever a connection state is made the *
* active state. Sequence numbers are of type uint64 and may not * For SSL/TLS protocols, the sequence number MUST be set to zero
* exceed 2^64-1. Sequence numbers do not wrap. If a SSL/TLS * whenever a connection state is made the active state.
* implementation would need to wrap a sequence number, it must *
* renegotiate instead. A sequence number is incremented after each * DTLS uses an explicit sequence number, rather than an implicit one.
* record: specifically, the first record transmitted under a * Sequence numbers are maintained separately for each epoch, with
* particular connection state MUST use sequence number 0. * 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 { class Authenticator {
@ -56,26 +63,53 @@ class Authenticator {
// sequence number + record type + protocol version + record length // sequence number + record type + protocol version + record length
private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2; 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. * Default construct, no message authentication token is initialized.
* *
* Note that this construct can only be called for null MAC * Note that this construct can only be called for null MAC
*/ */
Authenticator() { 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]; block = new byte[0];
} }
this.isDTLS = isDTLS;
}
/** /**
* Constructs the message authentication token for the specified * Constructs the message authentication token for the specified
* SSL/TLS protocol. * SSL/TLS protocol.
*/ */
Authenticator(ProtocolVersion protocolVersion) { 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 = new byte[BLOCK_SIZE_TLS];
block[9] = protocolVersion.major; block[9] = protocolVersion.major;
block[10] = protocolVersion.minor; block[10] = protocolVersion.minor;
this.isDTLS = false;
} else { } else {
block = new byte[BLOCK_SIZE_SSL]; block = new byte[BLOCK_SIZE_SSL];
this.isDTLS = false;
} }
} }
@ -93,12 +127,20 @@ class Authenticator {
* Conservatively, we don't allow more records to be generated * Conservatively, we don't allow more records to be generated
* when there are only 2^8 sequence numbers left. * when there are only 2^8 sequence numbers left.
*/ */
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 && return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF && block[0] == (byte)0xFF && block[1] == (byte)0xFF &&
block[2] == (byte)0xFF && block[3] == (byte)0xFF && block[2] == (byte)0xFF && block[3] == (byte)0xFF &&
block[4] == (byte)0xFF && block[5] == (byte)0xFF && block[4] == (byte)0xFF && block[5] == (byte)0xFF &&
block[6] == (byte)0xFF); block[6] == (byte)0xFF);
} }
}
/** /**
* Checks whether the sequence number close to renew. * Checks whether the sequence number close to renew.
@ -113,14 +155,22 @@ class Authenticator {
final boolean seqNumIsHuge() { final boolean seqNumIsHuge() {
/* /*
* Conservatively, we should ask for renegotiation when there are * Conservatively, we should ask for renegotiation when there are
* only 2^48 sequence numbers left. * only 2^32 sequence numbers left.
*/ */
if (isDTLS) {
return (block.length != 0 && return (block.length != 0 &&
block[0] == (byte)0xFF && block[1] == (byte)0xFF); // 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 * @return the byte array of the current sequence number
*/ */
@ -128,6 +178,33 @@ class Authenticator {
return Arrays.copyOf(block, 8); return Arrays.copyOf(block, 8);
} }
/**
* 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 * Acquires the current message authentication information with the
* specified record type and fragment length, and then increases the * specified record type and fragment length, and then increases the
@ -135,27 +212,50 @@ class Authenticator {
* *
* @param type the record type * @param type the record type
* @param length the fragment of the record * @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 * @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(); 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) { if (block.length != 0) {
copy[8] = type; copy[8] = type;
copy[copy.length - 2] = (byte)(length >> 8); copy[copy.length - 2] = (byte)(length >> 8);
copy[copy.length - 1] = (byte)(length); copy[copy.length - 1] = (byte)(length);
/* if (sequence == null || sequence.length != 0) {
* Increase the sequence number in the block array // Increase the implicit sequence number in the block array.
* it is a 64-bit number stored in big-endian format increaseSequenceNumber();
*/
int k = 7;
while ((k >= 0) && (++block[k] == 0)) {
k--;
} }
} }
return copy; 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;
}
} }

View file

@ -154,9 +154,9 @@ final class CipherBox {
* NULL cipherbox. Identity operation, no encryption. * NULL cipherbox. Identity operation, no encryption.
*/ */
private CipherBox() { private CipherBox() {
this.protocolVersion = ProtocolVersion.DEFAULT; this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
this.cipher = null; this.cipher = null;
this.cipherType = STREAM_CIPHER; this.cipherType = NULL_CIPHER;
this.fixedIv = new byte[0]; this.fixedIv = new byte[0];
this.key = null; this.key = null;
this.mode = Cipher.ENCRYPT_MODE; // choose at random this.mode = Cipher.ENCRYPT_MODE; // choose at random
@ -197,7 +197,7 @@ final class CipherBox {
*/ */
if (iv == null && bulkCipher.ivSize != 0 && if (iv == null && bulkCipher.ivSize != 0 &&
mode == Cipher.DECRYPT_MODE && mode == Cipher.DECRYPT_MODE &&
protocolVersion.v >= ProtocolVersion.TLS11.v) { protocolVersion.useTLS11PlusSpec()) {
iv = getFixedMask(bulkCipher.ivSize); iv = getFixedMask(bulkCipher.ivSize);
} }
@ -491,7 +491,7 @@ final class CipherBox {
newLen = removePadding( newLen = removePadding(
buf, offset, newLen, tagLen, blockSize, protocolVersion); buf, offset, newLen, tagLen, blockSize, protocolVersion);
if (protocolVersion.v >= ProtocolVersion.TLS11.v) { if (protocolVersion.useTLS11PlusSpec()) {
if (newLen < blockSize) { if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV"); throw new BadPaddingException("invalid explicit IV");
} }
@ -573,7 +573,7 @@ final class CipherBox {
newLen = removePadding(bb, tagLen, blockSize, protocolVersion); newLen = removePadding(bb, tagLen, blockSize, protocolVersion);
// check the explicit IV of TLS v1.1 or later // check the explicit IV of TLS v1.1 or later
if (protocolVersion.v >= ProtocolVersion.TLS11.v) { if (protocolVersion.useTLS11PlusSpec()) {
if (newLen < blockSize) { if (newLen < blockSize) {
throw new BadPaddingException("invalid explicit IV"); throw new BadPaddingException("invalid explicit IV");
} }
@ -746,7 +746,7 @@ final class CipherBox {
// The padding data should be filled with the padding length value. // The padding data should be filled with the padding length value.
int[] results = checkPadding(buf, offset + newLen, int[] results = checkPadding(buf, offset + newLen,
padLen + 1, (byte)(padLen & 0xFF)); padLen + 1, (byte)(padLen & 0xFF));
if (protocolVersion.v >= ProtocolVersion.TLS10.v) { if (protocolVersion.useTLS10PlusSpec()) {
if (results[0] != 0) { // padding data has invalid bytes if (results[0] != 0) { // padding data has invalid bytes
throw new BadPaddingException("Invalid TLS padding data"); throw new BadPaddingException("Invalid TLS padding data");
} }
@ -792,7 +792,7 @@ final class CipherBox {
int[] results = checkPadding( int[] results = checkPadding(
bb.duplicate().position(offset + newLen), bb.duplicate().position(offset + newLen),
(byte)(padLen & 0xFF)); (byte)(padLen & 0xFF));
if (protocolVersion.v >= ProtocolVersion.TLS10.v) { if (protocolVersion.useTLS10PlusSpec()) {
if (results[0] != 0) { // padding data has invalid bytes if (results[0] != 0) { // padding data has invalid bytes
throw new BadPaddingException("Invalid TLS padding data"); throw new BadPaddingException("Invalid TLS padding data");
} }
@ -873,7 +873,7 @@ final class CipherBox {
// For block ciphers, the explicit IV length is of length // For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to // SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size. // the SecurityParameters.block_size.
if (protocolVersion.v >= ProtocolVersion.TLS11.v) { if (protocolVersion.useTLS11PlusSpec()) {
return cipher.getBlockSize(); return cipher.getBlockSize();
} }
break; break;
@ -902,7 +902,7 @@ final class CipherBox {
* @return the explicit nonce size of the cipher. * @return the explicit nonce size of the cipher.
*/ */
int applyExplicitNonce(Authenticator authenticator, byte contentType, int applyExplicitNonce(Authenticator authenticator, byte contentType,
ByteBuffer bb) throws BadPaddingException { ByteBuffer bb, byte[] sequence) throws BadPaddingException {
switch (cipherType) { switch (cipherType) {
case BLOCK_CIPHER: case BLOCK_CIPHER:
// sanity check length of the ciphertext // sanity check length of the ciphertext
@ -918,7 +918,7 @@ final class CipherBox {
// For block ciphers, the explicit IV length is of length // For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to // SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size. // the SecurityParameters.block_size.
if (protocolVersion.v >= ProtocolVersion.TLS11.v) { if (protocolVersion.useTLS11PlusSpec()) {
return cipher.getBlockSize(); return cipher.getBlockSize();
} }
break; break;
@ -945,7 +945,8 @@ final class CipherBox {
// update the additional authentication data // update the additional authentication data
byte[] aad = authenticator.acquireAuthenticationBytes( byte[] aad = authenticator.acquireAuthenticationBytes(
contentType, bb.remaining() - recordIvSize - tagSize); contentType, bb.remaining() - recordIvSize - tagSize,
sequence);
cipher.updateAAD(aad); cipher.updateAAD(aad);
return recordIvSize; return recordIvSize;
@ -956,33 +957,6 @@ final class CipherBox {
return 0; return 0;
} }
/*
* 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 * Creates the explicit nonce/IV to this cipher. This method is used to
* encrypt an SSL/TLS output record. * encrypt an SSL/TLS output record.
@ -1005,7 +979,7 @@ final class CipherBox {
byte[] nonce = new byte[0]; byte[] nonce = new byte[0];
switch (cipherType) { switch (cipherType) {
case BLOCK_CIPHER: case BLOCK_CIPHER:
if (protocolVersion.v >= ProtocolVersion.TLS11.v) { if (protocolVersion.useTLS11PlusSpec()) {
// For block ciphers, the explicit IV length is of length // For block ciphers, the explicit IV length is of length
// SecurityParameters.record_iv_length, which is equal to // SecurityParameters.record_iv_length, which is equal to
// the SecurityParameters.block_size. // the SecurityParameters.block_size.
@ -1034,9 +1008,10 @@ final class CipherBox {
"invalid key or spec in GCM mode", ikae); "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( byte[] aad = authenticator.acquireAuthenticationBytes(
contentType, fragmentLength); contentType, fragmentLength, null);
cipher.updateAAD(aad); cipher.updateAAD(aad);
break; break;
} }
@ -1044,6 +1019,93 @@ final class CipherBox {
return nonce; 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? * Is this cipher available?
* *
@ -1100,7 +1162,7 @@ final class CipherBox {
if ((fragmentLen % blockSize) == 0) { if ((fragmentLen % blockSize) == 0) {
int minimal = tagLen + 1; int minimal = tagLen + 1;
minimal = (minimal >= blockSize) ? minimal : blockSize; minimal = (minimal >= blockSize) ? minimal : blockSize;
if (protocolVersion.v >= ProtocolVersion.TLS11.v) { if (protocolVersion.useTLS11PlusSpec()) {
minimal += blockSize; // plus the size of the explicit IV minimal += blockSize; // plus the size of the explicit IV
} }

View file

@ -122,9 +122,15 @@ final class CipherSuite implements Comparable<CipherSuite> {
final boolean allowed; final boolean allowed;
// obsoleted since protocol version // obsoleted since protocol version
//
// TLS version is used. If checking DTLS versions, please map to
// TLS version firstly. See ProtocolVersion.mapToTLSProtocol().
final int obsoleted; 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; final int supported;
/** /**
@ -182,6 +188,70 @@ final class CipherSuite implements Comparable<CipherSuite> {
return this != C_SCSV && isAvailable(); 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 * Compares CipherSuites based on their priority. Has the effect of
* sorting CipherSuites when put in a sorted collection, which is * sorting CipherSuites when put in a sorted collection, which is
@ -242,7 +312,7 @@ final class CipherSuite implements Comparable<CipherSuite> {
return c; return c;
} }
// for use by CipherSuiteList only // for use by SSLContextImpl only
static Collection<CipherSuite> allowedCipherSuites() { static Collection<CipherSuite> allowedCipherSuites() {
return nameMap.values(); return nameMap.values();
} }
@ -372,7 +442,8 @@ final class CipherSuite implements Comparable<CipherSuite> {
} }
static enum CipherType { static enum CipherType {
STREAM_CIPHER, // null or stream cipher NULL_CIPHER, // null cipher
STREAM_CIPHER, // stream cipher
BLOCK_CIPHER, // block cipher in CBC mode BLOCK_CIPHER, // block cipher in CBC mode
AEAD_CIPHER // AEAD cipher AEAD_CIPHER // AEAD cipher
} }
@ -387,7 +458,7 @@ final class CipherSuite implements Comparable<CipherSuite> {
static enum BulkCipher { static enum BulkCipher {
// export strength ciphers // 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_RC4_40(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true),
B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false), B_RC2_40("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false),
B_DES_40(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true), B_DES_40(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true),
@ -568,7 +639,7 @@ final class CipherSuite implements Comparable<CipherSuite> {
iv = new IvParameterSpec(new byte[cipher.ivSize]); iv = new IvParameterSpec(new byte[cipher.ivSize]);
} }
temporary = cipher.newCipher( temporary = cipher.newCipher(
ProtocolVersion.DEFAULT, ProtocolVersion.DEFAULT_TLS,
key, iv, secureRandom, true); key, iv, secureRandom, true);
b = temporary.isAvailable(); b = temporary.isAvailable();
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {

View file

@ -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 + ")");
}
}
}

View file

@ -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
}

View file

@ -141,11 +141,20 @@ final class ClientHandshaker extends Handshaker {
private final static boolean allowUnsafeServerCertChange = private final static boolean allowUnsafeServerCertChange =
Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false); 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 = private List<SNIServerName> requestedServerNames =
Collections.<SNIServerName>emptyList(); Collections.<SNIServerName>emptyList();
// maximum fragment length
private int requestedMFLength = -1; // -1: no fragment length limit
private boolean serverNamesAccepted = false; private boolean serverNamesAccepted = false;
private ClientHello initialClientHelloMsg = null; // DTLS only
/* /*
* the reserved server certificate chain in previous handshaking * the reserved server certificate chain in previous handshaking
* *
@ -172,11 +181,12 @@ final class ClientHandshaker extends Handshaker {
ProtocolList enabledProtocols, ProtocolList enabledProtocols,
ProtocolVersion activeProtocolVersion, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation, boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) { byte[] clientVerifyData, byte[] serverVerifyData,
boolean isDTLS) {
super(engine, context, enabledProtocols, true, true, super(engine, context, enabledProtocols, true, true,
activeProtocolVersion, isInitialHandshake, secureRenegotiation, activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData); clientVerifyData, serverVerifyData, isDTLS);
} }
/* /*
@ -191,19 +201,35 @@ final class ClientHandshaker extends Handshaker {
*/ */
@Override @Override
void processMessage(byte type, int messageLen) throws IOException { void processMessage(byte type, int messageLen) throws IOException {
if (state >= type // check the handshake state
&& (type != HandshakeMessage.ht_hello_request)) { handshakeState.check(type);
throw new SSLProtocolException(
"Handshake message sequence violation, " + type);
}
switch (type) { switch (type) {
case HandshakeMessage.ht_hello_request: 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; break;
case HandshakeMessage.ht_server_hello: 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; break;
case HandshakeMessage.ht_certificate: case HandshakeMessage.ht_certificate:
@ -213,7 +239,9 @@ final class ClientHandshaker extends Handshaker {
"unexpected server cert chain"); "unexpected server cert chain");
// NOTREACHED // NOTREACHED
} }
this.serverCertificate(new CertificateMsg(input)); CertificateMsg certificateMsg = new CertificateMsg(input);
handshakeState.update(certificateMsg, resumingSession);
this.serverCertificate(certificateMsg);
serverKey = serverKey =
session.getPeerCertificates()[0].getPublicKey(); session.getPeerCertificates()[0].getPublicKey();
break; break;
@ -249,41 +277,52 @@ final class ClientHandshaker extends Handshaker {
} }
try { try {
this.serverKeyExchange(new RSA_ServerKeyExchange(input)); RSA_ServerKeyExchange rsaSrvKeyExchange =
new RSA_ServerKeyExchange(input);
handshakeState.update(rsaSrvKeyExchange, resumingSession);
this.serverKeyExchange(rsaSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throw new SSLException("Server key", e);
} }
break; break;
case K_DH_ANON: case K_DH_ANON:
try { try {
this.serverKeyExchange(new DH_ServerKeyExchange( DH_ServerKeyExchange dhSrvKeyExchange =
input, protocolVersion)); new DH_ServerKeyExchange(input, protocolVersion);
handshakeState.update(dhSrvKeyExchange, resumingSession);
this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throw new SSLException("Server key", e);
} }
break; break;
case K_DHE_DSS: case K_DHE_DSS:
case K_DHE_RSA: case K_DHE_RSA:
try { try {
this.serverKeyExchange(new DH_ServerKeyExchange( DH_ServerKeyExchange dhSrvKeyExchange =
new DH_ServerKeyExchange(
input, serverKey, input, serverKey,
clnt_random.random_bytes, svr_random.random_bytes, clnt_random.random_bytes, svr_random.random_bytes,
messageLen, messageLen,
localSupportedSignAlgs, protocolVersion)); localSupportedSignAlgs, protocolVersion);
handshakeState.update(dhSrvKeyExchange, resumingSession);
this.serverKeyExchange(dhSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throw new SSLException("Server key", e);
} }
break; break;
case K_ECDHE_ECDSA: case K_ECDHE_ECDSA:
case K_ECDHE_RSA: case K_ECDHE_RSA:
case K_ECDH_ANON: case K_ECDH_ANON:
try { try {
this.serverKeyExchange(new ECDH_ServerKeyExchange ECDH_ServerKeyExchange ecdhSrvKeyExchange =
new ECDH_ServerKeyExchange
(input, serverKey, clnt_random.random_bytes, (input, serverKey, clnt_random.random_bytes,
svr_random.random_bytes, svr_random.random_bytes,
localSupportedSignAlgs, protocolVersion)); localSupportedSignAlgs, protocolVersion);
handshakeState.update(ecdhSrvKeyExchange, resumingSession);
this.serverKeyExchange(ecdhSrvKeyExchange);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Server key", e); throw new SSLException("Server key", e);
} }
break; break;
case K_RSA: case K_RSA:
@ -320,8 +359,9 @@ final class ClientHandshaker extends Handshaker {
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
certRequest.print(System.out); certRequest.print(System.out);
} }
handshakeState.update(certRequest, resumingSession);
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
Collection<SignatureAndHashAlgorithm> peerSignAlgs = Collection<SignatureAndHashAlgorithm> peerSignAlgs =
certRequest.getSignAlgorithms(); certRequest.getSignAlgorithms();
if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { if (peerSignAlgs == null || peerSignAlgs.isEmpty()) {
@ -345,33 +385,24 @@ final class ClientHandshaker extends Handshaker {
break; break;
case HandshakeMessage.ht_server_hello_done: case HandshakeMessage.ht_server_hello_done:
this.serverHelloDone(new ServerHelloDone(input)); ServerHelloDone serverHelloDone = new ServerHelloDone(input);
handshakeState.update(serverHelloDone, resumingSession);
this.serverHelloDone(serverHelloDone);
break; break;
case HandshakeMessage.ht_finished: case HandshakeMessage.ht_finished:
// A ChangeCipherSpec record must have been received prior to Finished serverFinished =
// reception of the Finished message (RFC 5246, 7.4.9). new Finished(protocolVersion, input, cipherSuite);
if (!receivedChangeCipherSpec()) { handshakeState.update(serverFinished, resumingSession);
fatalSE(Alerts.alert_handshake_failure, this.serverFinished(serverFinished);
"Received Finished message before ChangeCipherSpec");
}
this.serverFinished(
new Finished(protocolVersion, input, cipherSuite));
break; break;
default: default:
throw new SSLProtocolException( throw new SSLProtocolException(
"Illegal client handshake msg, " + type); "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 @@ final class ClientHandshaker extends Handshaker {
// Could be (e.g. at connection setup) that we already // Could be (e.g. at connection setup) that we already
// sent the "client hello" but the server's not seen it. // sent the "client hello" but the server's not seen it.
// //
if (state < HandshakeMessage.ht_client_hello) { if (!clientHelloDelivered) {
if (!secureRenegotiation && !allowUnsafeRenegotiation) { if (!secureRenegotiation && !allowUnsafeRenegotiation) {
// renegotiation is not allowed. // renegotiation is not allowed.
if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { if (activeProtocolVersion.useTLS10PlusSpec()) {
// response with a no_renegotiation warning, // response with a no_renegotiation warning,
warningSE(Alerts.alert_no_renegotiation); warningSE(Alerts.alert_no_renegotiation);
@ -428,6 +459,29 @@ final class ClientHandshaker extends Handshaker {
} }
} }
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 * Server chooses session parameters given options created by the
@ -441,6 +495,9 @@ final class ClientHandshaker extends Handshaker {
* probably authentication getting done. * probably authentication getting done.
*/ */
private void serverHello(ServerHello mesg) throws IOException { private void serverHello(ServerHello mesg) throws IOException {
// Dispose the reserved ClientHello message (if exists).
initialClientHelloMsg = null;
serverKeyExchangeReceived = false; serverKeyExchangeReceived = false;
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out); mesg.print(System.out);
@ -536,7 +593,7 @@ final class ClientHandshaker extends Handshaker {
} }
setCipherSuite(mesg.cipherSuite); setCipherSuite(mesg.cipherSuite);
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
} }
@ -611,9 +668,8 @@ final class ClientHandshaker extends Handshaker {
} }
} }
// looks fine; resume it, and update the state machine. // looks fine; resume it.
resumingSession = true; resumingSession = true;
state = HandshakeMessage.ht_finished - 1;
calculateConnectionKeys(session.getMasterSecret()); calculateConnectionKeys(session.getMasterSecret());
if (debug != null && Debug.isOn("session")) { if (debug != null && Debug.isOn("session")) {
System.out.println("%% Server resumed " + session); System.out.println("%% Server resumed " + session);
@ -627,6 +683,24 @@ final class ClientHandshaker extends Handshaker {
} }
} }
// 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) { if (resumingSession && session != null) {
setHandshakeSessionSE(session); setHandshakeSessionSE(session);
// Reserve the handshake state if this is a session-resumption // Reserve the handshake state if this is a session-resumption
@ -657,6 +731,8 @@ final class ClientHandshaker extends Handshaker {
getLocalSupportedSignAlgs(), getLocalSupportedSignAlgs(),
mesg.sessionId, getHostSE(), getPortSE()); mesg.sessionId, getHostSE(), getPortSE());
session.setRequestedServerNames(requestedServerNames); session.setRequestedServerNames(requestedServerNames);
session.setNegotiatedMaxFragSize(requestedMFLength);
session.setMaximumPacketSize(maximumPacketSize);
setHandshakeSessionSE(session); setHandshakeSessionSE(session);
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
System.out.println("** " + cipherSuite); System.out.println("** " + cipherSuite);
@ -681,7 +757,6 @@ final class ClientHandshaker extends Handshaker {
ephemeralServerKey = mesg.getPublicKey(); ephemeralServerKey = mesg.getPublicKey();
} }
/* /*
* Diffie-Hellman key exchange. We save the server public key and * Diffie-Hellman key exchange. We save the server public key and
* our own D-H algorithm object so we can defer key calculations * our own D-H algorithm object so we can defer key calculations
@ -716,13 +791,6 @@ final class ClientHandshaker extends Handshaker {
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out); 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 * FIRST ... if requested, send an appropriate Certificate chain
@ -817,7 +885,7 @@ final class ClientHandshaker extends Handshaker {
// server. For SSLv3, send the no_certificate alert; // server. For SSLv3, send the no_certificate alert;
// TLS uses an empty cert chain instead. // TLS uses an empty cert chain instead.
// //
if (protocolVersion.v >= ProtocolVersion.TLS10.v) { if (protocolVersion.useTLS10PlusSpec()) {
m1 = new CertificateMsg(new X509Certificate [0]); m1 = new CertificateMsg(new X509Certificate [0]);
} else { } else {
warningSE(Alerts.alert_no_certificate); warningSE(Alerts.alert_no_certificate);
@ -837,6 +905,7 @@ final class ClientHandshaker extends Handshaker {
m1.print(System.out); m1.print(System.out);
} }
m1.write(output); m1.write(output);
handshakeState.update(m1, resumingSession);
} }
} }
@ -1000,7 +1069,7 @@ final class ClientHandshaker extends Handshaker {
m2.print(System.out); m2.print(System.out);
} }
m2.write(output); m2.write(output);
handshakeState.update(m2, resumingSession);
/* /*
* THIRD, send a "change_cipher_spec" record followed by the * THIRD, send a "change_cipher_spec" record followed by the
@ -1010,8 +1079,6 @@ final class ClientHandshaker extends Handshaker {
* to compute the "Finished" message, and to compute the keys used * to compute the "Finished" message, and to compute the keys used
* to protect all records following the change_cipher_spec. * to protect all records following the change_cipher_spec.
*/ */
output.doHashes();
output.flush(); output.flush();
/* /*
@ -1069,7 +1136,7 @@ final class ClientHandshaker extends Handshaker {
CertificateVerify m3; CertificateVerify m3;
try { try {
SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; SignatureAndHashAlgorithm preferableSignatureAlgorithm = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm = preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm( SignatureAndHashAlgorithm.getPreferableAlgorithm(
peerSupportedSignAlgs, signingKey.getAlgorithm(), peerSupportedSignAlgs, signingKey.getAlgorithm(),
@ -1103,13 +1170,17 @@ final class ClientHandshaker extends Handshaker {
m3.print(System.out); m3.print(System.out);
} }
m3.write(output); m3.write(output);
output.doHashes(); handshakeState.update(m3, resumingSession);
output.flush();
} }
/* /*
* OK, that's that! * OK, that's that!
*/ */
sendChangeCipherAndFinish(false); sendChangeCipherAndFinish(false);
// expecting the final ChangeCipherSpec and Finished messages
expectingFinishFlightSE();
} }
@ -1158,8 +1229,9 @@ final class ClientHandshaker extends Handshaker {
* completed handshakes. * completed handshakes.
*/ */
if (resumingSession) { if (resumingSession) {
input.digestNow();
sendChangeCipherAndFinish(true); sendChangeCipherAndFinish(true);
} else {
handshakeFinished = true;
} }
session.setLastAccessedTime(System.currentTimeMillis()); session.setLastAccessedTime(System.currentTimeMillis());
@ -1188,6 +1260,10 @@ final class ClientHandshaker extends Handshaker {
*/ */
private void sendChangeCipherAndFinish(boolean finishedTag) private void sendChangeCipherAndFinish(boolean finishedTag)
throws IOException { throws IOException {
// Reload if this message has been reserved.
handshakeHash.reload();
Finished mesg = new Finished(protocolVersion, handshakeHash, Finished mesg = new Finished(protocolVersion, handshakeHash,
Finished.CLIENT, session.getMasterSecret(), cipherSuite); Finished.CLIENT, session.getMasterSecret(), cipherSuite);
@ -1205,13 +1281,6 @@ final class ClientHandshaker extends Handshaker {
if (secureRenegotiation) { if (secureRenegotiation) {
clientVerifyData = mesg.getVerifyData(); 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 @@ final class ClientHandshaker extends Handshaker {
// create the ClientHello message // create the ClientHello message
ClientHello clientHelloMessage = new ClientHello( ClientHello clientHelloMessage = new ClientHello(
sslContext.getSecureRandom(), maxProtocolVersion, sslContext.getSecureRandom(), maxProtocolVersion,
sessionId, cipherSuites); sessionId, cipherSuites, isDTLS);
// add signature_algorithm extension // add signature_algorithm extension
if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) { if (maxProtocolVersion.useTLS12PlusSpec()) {
// we will always send the signature_algorithm extension // we will always send the signature_algorithm extension
Collection<SignatureAndHashAlgorithm> localSignAlgs = Collection<SignatureAndHashAlgorithm> localSignAlgs =
getLocalSupportedSignAlgs(); getLocalSupportedSignAlgs();
@ -1389,6 +1458,37 @@ final class ClientHandshaker extends Handshaker {
} }
} }
// 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 // reset the client random cookie
clnt_random = clientHelloMessage.clnt_random; clnt_random = clientHelloMessage.clnt_random;
@ -1403,6 +1503,11 @@ final class ClientHandshaker extends Handshaker {
clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData); clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData);
} }
if (isDTLS) {
// Cookie exchange need to reserve the initial ClientHello message.
initialClientHelloMsg = clientHelloMessage;
}
return clientHelloMessage; return clientHelloMessage;
} }

File diff suppressed because it is too large Load diff

View file

@ -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);
}
}
}

View file

@ -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
}

View file

@ -29,6 +29,9 @@ import java.io.PrintStream;
import java.security.AccessController; import java.security.AccessController;
import java.util.Locale; import java.util.Locale;
import sun.misc.HexDumpEncoder;
import java.nio.ByteBuffer;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
/** /**
@ -198,4 +201,47 @@ public class Debug {
static String toString(byte[] b) { static String toString(byte[] b) {
return sun.security.util.Debug.toString(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();
}
}
} }

View file

@ -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]);
}
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -29,6 +29,7 @@ package sun.security.ssl;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.security.*; import java.security.*;
import java.util.Locale; import java.util.Locale;
import java.nio.ByteBuffer;
/** /**
* Abstraction for the SSL/TLS hash of all handshake messages that is * Abstraction for the SSL/TLS hash of all handshake messages that is
@ -99,6 +100,9 @@ final class HandshakeHash {
// For TLS 1.2 // For TLS 1.2
private MessageDigest finMD; private MessageDigest finMD;
// Cache for input record handshake hash computation
private ByteArrayOutputStream reserve = new ByteArrayOutputStream();
/** /**
* Create a new HandshakeHash. needCertificateVerify indicates whether * Create a new HandshakeHash. needCertificateVerify indicates whether
* a hash for the certificate verify message is required. * a hash for the certificate verify message is required.
@ -107,7 +111,106 @@ final class HandshakeHash {
clonesNeeded = needCertificateVerify ? 3 : 2; 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) { void update(byte[] b, int offset, int len) {
// reload if there are reserved messages.
reload();
switch (version) { switch (version) {
case 1: case 1:
md5.update(b, offset, len); md5.update(b, offset, len);
@ -139,9 +242,15 @@ final class HandshakeHash {
void protocolDetermined(ProtocolVersion pv) { void protocolDetermined(ProtocolVersion pv) {
// Do not set again, will ignore // Do not set again, will ignore
if (version != -1) return; if (version != -1) {
return;
}
if (pv.maybeDTLSProtocol()) {
version = pv.compareTo(ProtocolVersion.DTLS12) >= 0 ? 2 : 1;
} else {
version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1; version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
}
switch (version) { switch (version) {
case 1: case 1:
// initiate md5, sha and call update on saved array // initiate md5, sha and call update on saved array

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,11 +23,11 @@
* questions. * questions.
*/ */
package sun.security.ssl; package sun.security.ssl;
import java.io.InputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
@ -38,154 +38,104 @@ import javax.net.ssl.SSLException;
* Once a new handshake record arrives, it is buffered in this class until * Once a new handshake record arrives, it is buffered in this class until
* processed by the Handshaker. The buffer may also contain incomplete * processed by the Handshaker. The buffer may also contain incomplete
* handshake messages in case the message is split across multiple records. * 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 * handshake messages larger than the default buffer size (e.g. large
* certificate messages). The buffer is grown dynamically to handle that * certificate messages). The buffer is grown dynamically to handle that.
* (see InputRecord.queueHandshake()).
* *
* Note that the InputRecord used as a buffer here is separate from the * Note that this class only handles Handshake messages in TLS format.
* AppInStream.r, which is where data from the socket is initially read * DTLS Handshake messages should be converted into TLS format before
* into. This is because once the initial handshake has been completed, * calling into this method.
* handshake and application data messages may be interleaved arbitrarily
* and must be processed independently.
* *
* @author David Brownell * @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 * Construct the stream; we'll be accumulating hashes of the
* input records using two sets of digests. * input records using two sets of digests.
*/ */
HandshakeInStream(HandshakeHash handshakeHash) { HandshakeInStream() {
r = new InputRecord(); super(new byte[0]); // lazy to alloacte the internal buffer
r.setHandshakeHash(handshakeHash);
} }
//
// overridden ByteArrayInputStream methods
//
// overridden InputStream methods
/*
* 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 @Override
public int available() { public int read(byte[] b) throws IOException {
return r.available(); if (super.read(b) != 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"); throw new SSLException("Unexpected end of handshake data");
} }
return n;
return b.length;
} }
/* //
* Get a bunch of bytes of handshake data. // handshake input stream management functions
*/ //
@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
/* /*
* Here's an incoming record with handshake data. Queue the contents; * Here's an incoming record with handshake data. Queue the contents;
* it might be one or more entire messages, complete a message that's * it might be one or more entire messages, complete a message that's
* partly queued, or both. * partly queued, or both.
*/ */
void incomingRecord(InputRecord in) throws IOException { void incomingRecord(ByteBuffer in) throws IOException {
r.queueHandshake(in); 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.
* Hash any data we've consumed but not yet hashed. Useful mostly len = in.remaining() + count;
* for processing client certificate messages (so we can check the if (buf.length < len) {
* immediately following cert verify message) and finished messages byte[] newbuf = new byte[len];
* (so we can compute our own finished message). if (count != 0) {
*/ System.arraycopy(buf, 0, newbuf, 0, count);
void digestNow() { }
r.doHashes(); buf = newbuf;
} }
/* // Append the incoming record to the buffer
* Do more than skip that handshake data ... totally ignore it. in.get(buf, count, in.remaining());
* The difference is that the data does not get hashed. count = len;
*/
void ignore(int n) {
r.ignore(n);
} }
//
// Message parsing methods // Message parsing methods
//
/* /*
* Read 8, 16, 24, and 32 bit SSL integer data types, encoded * Read 8, 16, 24, and 32 bit SSL integer data types, encoded
* in standard big-endian form. * in standard big-endian form.
*/ */
int getInt8() throws IOException { int getInt8() throws IOException {
verifyLength(1);
return read(); return read();
} }
int getInt16() throws IOException { int getInt16() throws IOException {
verifyLength(2);
return (getInt8() << 8) | getInt8(); return (getInt8() << 8) | getInt8();
} }
int getInt24() throws IOException { int getInt24() throws IOException {
verifyLength(3);
return (getInt8() << 16) | (getInt8() << 8) | getInt8(); return (getInt8() << 16) | (getInt8() << 8) | getInt8();
} }
int getInt32() throws IOException { int getInt32() throws IOException {
verifyLength(4);
return (getInt8() << 24) | (getInt8() << 16) return (getInt8() << 24) | (getInt8() << 16)
| (getInt8() << 8) | getInt8(); | (getInt8() << 8) | getInt8();
} }
@ -193,13 +143,12 @@ public class HandshakeInStream extends InputStream {
/* /*
* Read byte vectors with 8, 16, and 24 bit length encodings. * Read byte vectors with 8, 16, and 24 bit length encodings.
*/ */
byte[] getBytes8() throws IOException { byte[] getBytes8() throws IOException {
int len = getInt8(); int len = getInt8();
verifyLength(len); verifyLength(len);
byte b[] = new byte[len]; byte b[] = new byte[len];
read(b, 0, len); read(b);
return b; return b;
} }
@ -208,7 +157,7 @@ public class HandshakeInStream extends InputStream {
verifyLength(len); verifyLength(len);
byte b[] = new byte[len]; byte b[] = new byte[len];
read(b, 0, len); read(b);
return b; return b;
} }
@ -217,16 +166,14 @@ public class HandshakeInStream extends InputStream {
verifyLength(len); verifyLength(len);
byte b[] = new byte[len]; byte b[] = new byte[len];
read(b, 0, len); read(b);
return b; return b;
} }
// Is a length greater than available bytes in the record? // Is a length greater than available bytes in the record?
private void verifyLength(int len) throws SSLException { private void verifyLength(int len) throws SSLException {
if (len > available()) { if (len > available()) {
throw new SSLException( throw new SSLException("Unexpected end of handshake data");
"Not enough data to fill declared vector size");
} }
} }
} }

View file

@ -73,25 +73,44 @@ import sun.security.util.KeyUtil;
*/ */
public abstract class HandshakeMessage { public abstract class HandshakeMessage {
HandshakeMessage() { }
// 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_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_finished = 20;
/* Class and subclass dynamic debugging support */ /* Class and subclass dynamic debugging support */
public static final Debug debug = Debug.getInstance("ssl"); public static final Debug debug = Debug.getInstance("ssl");
// enum HandshakeType:
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; // 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; // 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
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 * Utility method to convert a BigInteger to a byte array in unsigned
* format as needed in the handshake messages. BigInteger uses * format as needed in the handshake messages. BigInteger uses
@ -109,16 +128,6 @@ public abstract class HandshakeMessage {
return b; 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) { private static byte[] genPad(int b, int count) {
byte[] padding = new byte[count]; byte[] padding = new byte[count];
Arrays.fill(padding, (byte)b); Arrays.fill(padding, (byte)b);
@ -141,6 +150,7 @@ public abstract class HandshakeMessage {
s.write(messageType()); s.write(messageType());
s.putInt24(len); s.putInt24(len);
send(s); send(s);
s.complete();
} }
/* /*
@ -199,6 +209,69 @@ static final class HelloRequest extends HandshakeMessage {
} }
/*
* 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 * ClientHello ... CLIENT --> SERVER
@ -216,7 +289,9 @@ static final class ClientHello extends HandshakeMessage {
ProtocolVersion protocolVersion; ProtocolVersion protocolVersion;
RandomCookie clnt_random; RandomCookie clnt_random;
SessionId sessionId; SessionId sessionId;
byte[] cookie; // DTLS only
private CipherSuiteList cipherSuites; private CipherSuiteList cipherSuites;
private final boolean isDTLS;
byte[] compression_methods; byte[] compression_methods;
HelloExtensions extensions = new HelloExtensions(); HelloExtensions extensions = new HelloExtensions();
@ -224,11 +299,18 @@ static final class ClientHello extends HandshakeMessage {
private final static byte[] NULL_COMPRESSION = new byte[] {0}; private final static byte[] NULL_COMPRESSION = new byte[] {0};
ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, ClientHello(SecureRandom generator, ProtocolVersion protocolVersion,
SessionId sessionId, CipherSuiteList cipherSuites) { SessionId sessionId, CipherSuiteList cipherSuites,
boolean isDTLS) {
this.isDTLS = isDTLS;
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
this.sessionId = sessionId; this.sessionId = sessionId;
this.cipherSuites = cipherSuites; this.cipherSuites = cipherSuites;
if (isDTLS) {
this.cookie = new byte[0];
} else {
this.cookie = null;
}
if (cipherSuites.containsEC()) { if (cipherSuites.containsEC()) {
extensions.add(SupportedEllipticCurvesExtension.DEFAULT); extensions.add(SupportedEllipticCurvesExtension.DEFAULT);
@ -239,11 +321,21 @@ static final class ClientHello extends HandshakeMessage {
compression_methods = NULL_COMPRESSION; 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()); protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8());
clnt_random = new RandomCookie(s); clnt_random = new RandomCookie(s);
sessionId = new SessionId(s.getBytes8()); sessionId = new SessionId(s.getBytes8());
sessionId.checkLength(protocolVersion); sessionId.checkLength(protocolVersion);
if (isDTLS) {
cookie = s.getBytes8();
} else {
cookie = null;
}
cipherSuites = new CipherSuiteList(s); cipherSuites = new CipherSuiteList(s);
compression_methods = s.getBytes8(); compression_methods = s.getBytes8();
if (messageLength() != messageLength) { if (messageLength() != messageLength) {
@ -279,6 +371,28 @@ static final class ClientHello extends HandshakeMessage {
extensions.add(signatureAlgorithm); 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 @Override
int messageType() { return ht_client_hello; } int messageType() { return ht_client_hello; }
@ -290,6 +404,7 @@ static final class ClientHello extends HandshakeMessage {
*/ */
return (2 + 32 + 1 + 2 + 1 return (2 + 32 + 1 + 2 + 1
+ sessionId.length() /* ... + variable parts */ + sessionId.length() /* ... + variable parts */
+ (isDTLS ? (1 + cookie.length) : 0)
+ (cipherSuites.size() * 2) + (cipherSuites.size() * 2)
+ compression_methods.length) + compression_methods.length)
+ extensions.length(); + extensions.length();
@ -297,13 +412,7 @@ static final class ClientHello extends HandshakeMessage {
@Override @Override
void send(HandshakeOutStream s) throws IOException { void send(HandshakeOutStream s) throws IOException {
s.putInt8(protocolVersion.major); send(s, true); // Count hello verify cookie.
s.putInt8(protocolVersion.minor);
clnt_random.send(s);
s.putBytes8(sessionId.getId());
cipherSuites.send(s);
s.putBytes8(compression_methods);
extensions.send(s);
} }
@Override @Override
@ -317,6 +426,10 @@ static final class ClientHello extends HandshakeMessage {
s.print("Session ID: "); s.print("Session ID: ");
s.println(sessionId); s.println(sessionId);
if (isDTLS) {
Debug.println(s, "cookie", cookie);
}
s.println("Cipher Suites: " + cipherSuites); s.println("Cipher Suites: " + cipherSuites);
Debug.println(s, "Compression Methods", compression_methods); Debug.println(s, "Compression Methods", compression_methods);
@ -324,6 +437,21 @@ static final class ClientHello extends HandshakeMessage {
s.println("***"); 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 @@ class DH_ServerKeyExchange extends ServerKeyExchange
setValues(obj); setValues(obj);
Signature sig; Signature sig;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm; this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else { } else {
@ -801,7 +929,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
new BigInteger(1, dh_g))); new BigInteger(1, dh_g)));
// read the signature and hash algorithm // read the signature and hash algorithm
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm int signature = input.getInt8(); // signature algorithm
@ -834,7 +962,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
Signature sig; Signature sig;
String algorithm = publicKey.getAlgorithm(); String algorithm = publicKey.getAlgorithm();
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature( sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName()); preferableSignatureAlgorithm.getAlgorithmName());
} else { } else {
@ -914,7 +1042,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
temp += dh_Ys.length; temp += dh_Ys.length;
if (signature != null) { if (signature != null) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord(); temp += SignatureAndHashAlgorithm.sizeInRecord();
} }
@ -934,7 +1062,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
s.putBytes16(dh_Ys); s.putBytes16(dh_Ys);
if (signature != null) { if (signature != null) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
} }
@ -959,7 +1087,7 @@ class DH_ServerKeyExchange extends ServerKeyExchange
if (signature == null) { if (signature == null) {
s.println("Anonymous"); s.println("Anonymous");
} else { } else {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " + s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName()); preferableSignatureAlgorithm.getAlgorithmName());
} }
@ -1021,7 +1149,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
} }
Signature sig; Signature sig;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm; this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else { } else {
@ -1084,7 +1212,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
} }
// read the signature and hash algorithm // read the signature and hash algorithm
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
int hash = input.getInt8(); // hash algorithm int hash = input.getInt8(); // hash algorithm
int signature = input.getInt8(); // signature algorithm int signature = input.getInt8(); // signature algorithm
@ -1105,7 +1233,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
// verify the signature // verify the signature
Signature sig; Signature sig;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature( sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName()); preferableSignatureAlgorithm.getAlgorithmName());
} else { } else {
@ -1157,7 +1285,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
int sigLen = 0; int sigLen = 0;
if (signatureBytes != null) { if (signatureBytes != null) {
sigLen = 2 + signatureBytes.length; sigLen = 2 + signatureBytes.length;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
sigLen += SignatureAndHashAlgorithm.sizeInRecord(); sigLen += SignatureAndHashAlgorithm.sizeInRecord();
} }
} }
@ -1172,7 +1300,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
s.putBytes8(pointBytes); s.putBytes8(pointBytes);
if (signatureBytes != null) { if (signatureBytes != null) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
} }
@ -1189,7 +1317,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
if (signatureBytes == null) { if (signatureBytes == null) {
s.println("Anonymous"); s.println("Anonymous");
} else { } else {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " + s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName()); preferableSignatureAlgorithm.getAlgorithmName());
} }
@ -1315,7 +1443,7 @@ class CertificateRequest extends HandshakeMessage
this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC; this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC;
// Use supported_signature_algorithms for TLS 1.2 or later. // Use supported_signature_algorithms for TLS 1.2 or later.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (signAlgs == null || signAlgs.isEmpty()) { if (signAlgs == null || signAlgs.isEmpty()) {
throw new SSLProtocolException( throw new SSLProtocolException(
"No supported signature algorithms"); "No supported signature algorithms");
@ -1339,7 +1467,7 @@ class CertificateRequest extends HandshakeMessage
types = input.getBytes8(); types = input.getBytes8();
// Read the supported_signature_algorithms for TLS 1.2 or later. // Read the supported_signature_algorithms for TLS 1.2 or later.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
algorithmsLen = input.getInt16(); algorithmsLen = input.getInt16();
if (algorithmsLen < 2) { if (algorithmsLen < 2) {
throw new SSLProtocolException( throw new SSLProtocolException(
@ -1406,7 +1534,7 @@ class CertificateRequest extends HandshakeMessage
int messageLength() { int messageLength() {
int len = 1 + types.length + 2; int len = 1 + types.length + 2;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
len += algorithmsLen + 2; len += algorithmsLen + 2;
} }
@ -1423,7 +1551,7 @@ class CertificateRequest extends HandshakeMessage
output.putBytes8(types); output.putBytes8(types);
// put supported_signature_algorithms // put supported_signature_algorithms
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
output.putInt16(algorithmsLen); output.putInt16(algorithmsLen);
for (SignatureAndHashAlgorithm algorithm : algorithms) { for (SignatureAndHashAlgorithm algorithm : algorithms) {
output.putInt8(algorithm.getHashValue()); // hash output.putInt8(algorithm.getHashValue()); // hash
@ -1478,7 +1606,7 @@ class CertificateRequest extends HandshakeMessage
} }
s.println(); s.println();
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
boolean opened = false; boolean opened = false;
for (SignatureAndHashAlgorithm signAlg : algorithms) { for (SignatureAndHashAlgorithm signAlg : algorithms) {
@ -1576,7 +1704,7 @@ static final class CertificateVerify extends HandshakeMessage {
String algorithm = privateKey.getAlgorithm(); String algorithm = privateKey.getAlgorithm();
Signature sig = null; Signature sig = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
this.preferableSignatureAlgorithm = signAlgorithm; this.preferableSignatureAlgorithm = signAlgorithm;
sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName());
} else { } else {
@ -1598,7 +1726,7 @@ static final class CertificateVerify extends HandshakeMessage {
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
// read the signature and hash algorithm // read the signature and hash algorithm
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
int hashAlg = input.getInt8(); // hash algorithm int hashAlg = input.getInt8(); // hash algorithm
int signAlg = input.getInt8(); // signature algorithm int signAlg = input.getInt8(); // signature algorithm
@ -1634,7 +1762,7 @@ static final class CertificateVerify extends HandshakeMessage {
SecretKey masterSecret) throws GeneralSecurityException { SecretKey masterSecret) throws GeneralSecurityException {
String algorithm = publicKey.getAlgorithm(); String algorithm = publicKey.getAlgorithm();
Signature sig = null; Signature sig = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
sig = JsseJce.getSignature( sig = JsseJce.getSignature(
preferableSignatureAlgorithm.getAlgorithmName()); preferableSignatureAlgorithm.getAlgorithmName());
} else { } else {
@ -1676,11 +1804,11 @@ static final class CertificateVerify extends HandshakeMessage {
throws SignatureException { throws SignatureException {
if (algorithm.equals("RSA")) { if (algorithm.equals("RSA")) {
if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone(); MessageDigest shaClone = handshakeHash.getSHAClone();
if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3
updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey);
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
} }
@ -1692,10 +1820,10 @@ static final class CertificateVerify extends HandshakeMessage {
sig.update(handshakeHash.getAllHandshakeMessages()); sig.update(handshakeHash.getAllHandshakeMessages());
} }
} else { // DSA, ECDSA } else { // DSA, ECDSA
if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- if (!protocolVersion.useTLS12PlusSpec()) { // TLS1.1-
MessageDigest shaClone = handshakeHash.getSHAClone(); MessageDigest shaClone = handshakeHash.getSHAClone();
if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 if (!protocolVersion.useTLS10PlusSpec()) { // SSLv3
updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey);
} }
@ -1811,7 +1939,7 @@ static final class CertificateVerify extends HandshakeMessage {
int messageLength() { int messageLength() {
int temp = 2; int temp = 2;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
temp += SignatureAndHashAlgorithm.sizeInRecord(); temp += SignatureAndHashAlgorithm.sizeInRecord();
} }
@ -1820,7 +1948,7 @@ static final class CertificateVerify extends HandshakeMessage {
@Override @Override
void send(HandshakeOutStream s) throws IOException { void send(HandshakeOutStream s) throws IOException {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getHashValue());
s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue());
} }
@ -1833,7 +1961,7 @@ static final class CertificateVerify extends HandshakeMessage {
s.println("*** CertificateVerify"); s.println("*** CertificateVerify");
if (debug != null && Debug.isOn("verbose")) { if (debug != null && Debug.isOn("verbose")) {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
s.println("Signature Algorithm " + s.println("Signature Algorithm " +
preferableSignatureAlgorithm.getAlgorithmName()); preferableSignatureAlgorithm.getAlgorithmName());
} }
@ -1899,7 +2027,7 @@ static final class Finished extends HandshakeMessage {
CipherSuite cipherSuite) throws IOException { CipherSuite cipherSuite) throws IOException {
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
this.cipherSuite = cipherSuite; this.cipherSuite = cipherSuite;
int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36; int msgLen = protocolVersion.useTLS10PlusSpec() ? 12 : 36;
verifyData = new byte[msgLen]; verifyData = new byte[msgLen];
input.read(verifyData); input.read(verifyData);
} }
@ -1932,7 +2060,7 @@ static final class Finished extends HandshakeMessage {
throw new RuntimeException("Invalid sender: " + sender); throw new RuntimeException("Invalid sender: " + sender);
} }
if (protocolVersion.v >= ProtocolVersion.TLS10.v) { if (protocolVersion.useTLS10PlusSpec()) {
// TLS 1.0+ // TLS 1.0+
try { try {
byte [] seed; byte [] seed;
@ -1940,14 +2068,14 @@ static final class Finished extends HandshakeMessage {
PRF prf; PRF prf;
// Get the KeyGenerator alg and calculate the seed. // Get the KeyGenerator alg and calculate the seed.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
// TLS 1.2 // TLS 1.2+ or DTLS 1.2+
seed = handshakeHash.getFinishedHash(); seed = handshakeHash.getFinishedHash();
prfAlg = "SunTls12Prf"; prfAlg = "SunTls12Prf";
prf = cipherSuite.prfAlg; prf = cipherSuite.prfAlg;
} else { } else {
// TLS 1.0/1.1 // TLS 1.0/1.1, DTLS 1.0
MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest md5Clone = handshakeHash.getMD5Clone();
MessageDigest shaClone = handshakeHash.getSHAClone(); MessageDigest shaClone = handshakeHash.getSHAClone();
seed = new byte[36]; seed = new byte[36];

View file

@ -23,10 +23,9 @@
* questions. * questions.
*/ */
package sun.security.ssl; package sun.security.ssl;
import java.io.OutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
/** /**
@ -40,197 +39,113 @@ import java.io.IOException;
* *
* @author David Brownell * @author David Brownell
*/ */
public class HandshakeOutStream extends OutputStream { public class HandshakeOutStream extends ByteArrayOutputStream {
private SSLSocketImpl socket; OutputRecord outputRecord; // May be null if not actually used to
private SSLEngineImpl engine; // output handshake message records.
OutputRecord r; HandshakeOutStream(OutputRecord outputRecord) {
super();
HandshakeOutStream(ProtocolVersion protocolVersion, this.outputRecord = outputRecord;
ProtocolVersion helloVersion, HandshakeHash handshakeHash,
SSLSocketImpl socket) {
this.socket = socket;
r = new OutputRecord(Record.ct_handshake);
init(protocolVersion, helloVersion, handshakeHash);
} }
HandshakeOutStream(ProtocolVersion protocolVersion, // Complete a handshakin message writing. Called by HandshakeMessage.
ProtocolVersion helloVersion, HandshakeHash handshakeHash, void complete() throws IOException {
SSLEngineImpl engine) { if (size() < 4) { // 4: handshake message header size
this.engine = engine; // internal_error alert will be triggered
r = new EngineOutputRecord(Record.ct_handshake, engine); throw new RuntimeException("handshake message is not available");
init(protocolVersion, helloVersion, handshakeHash);
} }
private void init(ProtocolVersion protocolVersion, // outputRecord cannot be null
ProtocolVersion helloVersion, HandshakeHash handshakeHash) { outputRecord.encodeHandshake(buf, 0, count);
r.setVersion(protocolVersion);
r.setHelloVersion(helloVersion); // reset the byte array output stream
r.setHandshakeHash(handshakeHash); reset();
} }
//
// 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 @Override
public void write(byte buf[], int off, int len) throws IOException { public void write(byte[] b, int off, int len) {
while (len > 0) { // The maximum fragment size is 24 bytes.
int howmuch = Math.min(len, r.availableDataBytes()); checkOverflow(len, Record.OVERFLOW_OF_INT24);
super.write(b, off, len);
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);
} }
@Override @Override
public void flush() throws IOException { public void flush() throws IOException {
if (socket != null) { outputRecord.flush();
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);
}
} }
/* //
* Tell the OutputRecord that a finished message was // handshake output stream management functions
* 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();
}
/* /*
* Put integers encoded in standard 8, 16, 24, and 32 bit * Put integers encoded in standard 8, 16, 24, and 32 bit
* big endian formats. Note that OutputStream.write(int) only * big endian formats. Note that OutputStream.write(int) only
* writes the least significant 8 bits and ignores the rest. * writes the least significant 8 bits and ignores the rest.
*/ */
void putInt8(int i) throws IOException { void putInt8(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT08); checkOverflow(i, Record.OVERFLOW_OF_INT08);
r.write(i); super.write(i);
} }
void putInt16(int i) throws IOException { void putInt16(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT16); checkOverflow(i, Record.OVERFLOW_OF_INT16);
if (r.availableDataBytes() < 2) { super.write(i >> 8);
flush(); super.write(i);
}
r.write(i >> 8);
r.write(i);
} }
void putInt24(int i) throws IOException { void putInt24(int i) throws IOException {
checkOverflow(i, Record.OVERFLOW_OF_INT24); checkOverflow(i, Record.OVERFLOW_OF_INT24);
if (r.availableDataBytes() < 3) { super.write(i >> 16);
flush(); super.write(i >> 8);
} super.write(i);
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);
} }
/* /*
* Put byte arrays with length encoded as 8, 16, 24 bit * Put byte arrays with length encoded as 8, 16, 24 bit
* integers in big-endian format. * integers in big-endian format.
*/ */
void putBytes8(byte b[]) throws IOException { void putBytes8(byte[] b) throws IOException {
if (b == null) { if (b == null) {
putInt8(0); putInt8(0);
return;
} else { } else {
checkOverflow(b.length, Record.OVERFLOW_OF_INT08);
}
putInt8(b.length); putInt8(b.length);
write(b, 0, b.length); super.write(b, 0, b.length);
}
} }
public void putBytes16(byte b[]) throws IOException { public void putBytes16(byte b[]) throws IOException {
if (b == null) { if (b == null) {
putInt16(0); putInt16(0);
return;
} else { } else {
checkOverflow(b.length, Record.OVERFLOW_OF_INT16);
}
putInt16(b.length); putInt16(b.length);
write(b, 0, b.length); super.write(b, 0, b.length);
}
} }
void putBytes24(byte b[]) throws IOException { void putBytes24(byte b[]) throws IOException {
if (b == null) { if (b == null) {
putInt24(0); putInt24(0);
return;
} else { } else {
checkOverflow(b.length, Record.OVERFLOW_OF_INT24);
}
putInt24(b.length); putInt24(b.length);
write(b, 0, b.length); super.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 // internal_error alert will be triggered
throw new RuntimeException( throw new RuntimeException(
"Field length overflow, the field length (" + "Field length overflow, the field length (" +
length + ") should be less than " + overflow); length + ") should be less than " + limit);
} }
} }
} }

View file

@ -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 + "]");
}
}

View file

@ -29,6 +29,7 @@ package sun.security.ssl;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.security.*; import java.security.*;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.AccessController; import java.security.AccessController;
import java.security.AlgorithmConstraints; import java.security.AlgorithmConstraints;
@ -115,6 +116,9 @@ abstract class Handshaker {
List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList(); List<SNIServerName> serverNames = Collections.<SNIServerName>emptyList();
Collection<SNIMatcher> sniMatchers = Collections.<SNIMatcher>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 isClient;
private boolean needCertVerify; private boolean needCertVerify;
@ -124,11 +128,16 @@ abstract class Handshaker {
HandshakeHash handshakeHash; HandshakeHash handshakeHash;
HandshakeInStream input; HandshakeInStream input;
HandshakeOutStream output; HandshakeOutStream output;
int state;
SSLContextImpl sslContext; SSLContextImpl sslContext;
RandomCookie clnt_random, svr_random; RandomCookie clnt_random, svr_random;
SSLSessionImpl session; SSLSessionImpl session;
HandshakeStateManager handshakeState;
boolean clientHelloDelivered;
boolean serverHelloRequested;
boolean handshakeActivated;
boolean handshakeFinished;
// current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
CipherSuite cipherSuite; CipherSuite cipherSuite;
@ -141,10 +150,6 @@ abstract class Handshaker {
// True if it's OK to start a new SSL session // True if it's OK to start a new SSL session
boolean enableNewSession; 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 // Whether local cipher suites preference should be honored during
// handshaking? // handshaking?
// //
@ -207,12 +212,18 @@ abstract class Handshaker {
// need to dispose the object when it is invalidated // need to dispose the object when it is invalidated
boolean invalidated; boolean invalidated;
/*
* Is this an instance for Datagram Transport Layer Security (DTLS)?
*/
final boolean isDTLS;
Handshaker(SSLSocketImpl c, SSLContextImpl context, Handshaker(SSLSocketImpl c, SSLContextImpl context,
ProtocolList enabledProtocols, boolean needCertVerify, ProtocolList enabledProtocols, boolean needCertVerify,
boolean isClient, ProtocolVersion activeProtocolVersion, boolean isClient, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation, boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) { byte[] clientVerifyData, byte[] serverVerifyData) {
this.conn = c; this.conn = c;
this.isDTLS = false;
init(context, enabledProtocols, needCertVerify, isClient, init(context, enabledProtocols, needCertVerify, isClient,
activeProtocolVersion, isInitialHandshake, secureRenegotiation, activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData); clientVerifyData, serverVerifyData);
@ -222,8 +233,10 @@ abstract class Handshaker {
ProtocolList enabledProtocols, boolean needCertVerify, ProtocolList enabledProtocols, boolean needCertVerify,
boolean isClient, ProtocolVersion activeProtocolVersion, boolean isClient, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation, boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) { byte[] clientVerifyData, byte[] serverVerifyData,
boolean isDTLS) {
this.engine = engine; this.engine = engine;
this.isDTLS = isDTLS;
init(context, enabledProtocols, needCertVerify, isClient, init(context, enabledProtocols, needCertVerify, isClient,
activeProtocolVersion, isInitialHandshake, secureRenegotiation, activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData); clientVerifyData, serverVerifyData);
@ -251,9 +264,13 @@ abstract class Handshaker {
this.secureRenegotiation = secureRenegotiation; this.secureRenegotiation = secureRenegotiation;
this.clientVerifyData = clientVerifyData; this.clientVerifyData = clientVerifyData;
this.serverVerifyData = serverVerifyData; this.serverVerifyData = serverVerifyData;
enableNewSession = true; this.enableNewSession = true;
invalidated = false; this.invalidated = false;
sessKeysCalculated = false; this.handshakeState = new HandshakeStateManager(isDTLS);
this.clientHelloDelivered = false;
this.serverHelloRequested = false;
this.handshakeActivated = false;
this.handshakeFinished = false;
setCipherSuite(CipherSuite.C_NULL); setCipherSuite(CipherSuite.C_NULL);
setEnabledProtocols(enabledProtocols); setEnabledProtocols(enabledProtocols);
@ -263,22 +280,6 @@ abstract class Handshaker {
} else { // engine != null } else { // engine != null
algorithmConstraints = new SSLAlgorithmConstraints(engine, true); 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 @@ abstract class Handshaker {
} }
} }
final boolean receivedChangeCipherSpec() {
if (conn != null) {
return conn.receivedChangeCipherSpec();
} else {
return engine.receivedChangeCipherSpec();
}
}
String getEndpointIdentificationAlgorithmSE() { String getEndpointIdentificationAlgorithmSE() {
SSLParameters paras; SSLParameters paras;
if (conn != null) { if (conn != null) {
@ -395,8 +388,6 @@ abstract class Handshaker {
void setVersion(ProtocolVersion protocolVersion) { void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
setVersionSE(protocolVersion); setVersionSE(protocolVersion);
output.r.setVersion(protocolVersion);
} }
/** /**
@ -482,6 +473,13 @@ abstract class Handshaker {
this.sniMatchers = sniMatchers; this.sniMatchers = sniMatchers;
} }
/**
* Sets the maximum packet size of the handshaking.
*/
void setMaximumPacketSize(int maximumPacketSize) {
this.maximumPacketSize = maximumPacketSize;
}
/** /**
* Sets the cipher suites preference. * Sets the cipher suites preference.
*/ */
@ -532,23 +530,29 @@ abstract class Handshaker {
handshakeHash = new HandshakeHash(needCertVerify); handshakeHash = new HandshakeHash(needCertVerify);
// Generate handshake input/output stream. // Generate handshake input/output stream.
input = new HandshakeInStream(handshakeHash);
if (conn != null) { if (conn != null) {
output = new HandshakeOutStream(protocolVersion, helloVersion, input = new HandshakeInStream();
handshakeHash, conn); output = new HandshakeOutStream(conn.outputRecord);
conn.getAppInputStream().r.setHandshakeHash(handshakeHash);
conn.getAppInputStream().r.setHelloVersion(helloVersion); conn.inputRecord.setHandshakeHash(handshakeHash);
conn.getAppOutputStream().r.setHelloVersion(helloVersion); conn.inputRecord.setHelloVersion(helloVersion);
} else {
output = new HandshakeOutStream(protocolVersion, helloVersion, conn.outputRecord.setHandshakeHash(handshakeHash);
handshakeHash, engine); 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.setHandshakeHash(handshakeHash);
engine.inputRecord.setHelloVersion(helloVersion); engine.inputRecord.setHelloVersion(helloVersion);
engine.outputRecord.setHandshakeHash(handshakeHash);
engine.outputRecord.setHelloVersion(helloVersion); engine.outputRecord.setHelloVersion(helloVersion);
engine.outputRecord.setVersion(protocolVersion);
} }
// move state to activated handshakeActivated = true;
state = -1;
} }
/** /**
@ -637,15 +641,15 @@ abstract class Handshaker {
if (!(activeProtocols.collection().isEmpty()) && if (!(activeProtocols.collection().isEmpty()) &&
activeProtocols.min.v != ProtocolVersion.NONE.v) { activeProtocols.min.v != ProtocolVersion.NONE.v) {
for (CipherSuite suite : enabledCipherSuites.collection()) { for (CipherSuite suite : enabledCipherSuites.collection()) {
if (suite.obsoleted > activeProtocols.min.v && if (!activeProtocols.min.obsoletes(suite) &&
suite.supported <= activeProtocols.max.v) { activeProtocols.max.supports(suite)) {
if (algorithmConstraints.permits( if (algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) { suite.name, null)) {
suites.add(suite); suites.add(suite);
} }
} else if (debug != null && Debug.isOn("verbose")) { } else if (debug != null && Debug.isOn("verbose")) {
if (suite.obsoleted <= activeProtocols.min.v) { if (activeProtocols.min.obsoletes(suite)) {
System.out.println( System.out.println(
"Ignoring obsoleted cipher suite: " + suite); "Ignoring obsoleted cipher suite: " + suite);
} else { } else {
@ -700,8 +704,8 @@ abstract class Handshaker {
boolean found = false; boolean found = false;
for (CipherSuite suite : enabledCipherSuites.collection()) { for (CipherSuite suite : enabledCipherSuites.collection()) {
if (suite.isAvailable() && suite.obsoleted > protocol.v && if (suite.isAvailable() && (!protocol.obsoletes(suite)) &&
suite.supported <= protocol.v) { protocol.supports(suite)) {
if (algorithmConstraints.permits( if (algorithmConstraints.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) { suite.name, null)) {
@ -837,7 +841,7 @@ abstract class Handshaker {
* this freshly created session can become the current one. * this freshly created session can become the current one.
*/ */
boolean isDone() { boolean isDone() {
return state == HandshakeMessage.ht_finished; return started() && handshakeState.isEmpty() && handshakeFinished;
} }
@ -861,6 +865,14 @@ abstract class Handshaker {
} }
} }
void expectingFinishFlightSE() {
if (conn != null) {
conn.expectingFinishFlight();
} else {
engine.expectingFinishFlight();
}
}
/* /*
* Returns true if renegotiation is in use for this connection. * Returns true if renegotiation is in use for this connection.
*/ */
@ -886,8 +898,8 @@ abstract class Handshaker {
* This routine is fed SSL handshake records when they become available, * This routine is fed SSL handshake records when they become available,
* and processes messages found therein. * and processes messages found therein.
*/ */
void process_record(InputRecord r, boolean expectingFinished) void processRecord(ByteBuffer record,
throws IOException { boolean expectingFinished) throws IOException {
checkThrown(); checkThrown();
@ -895,7 +907,7 @@ abstract class Handshaker {
* Store the incoming handshake data, then see if we can * Store the incoming handshake data, then see if we can
* now process any completed handshake messages * now process any completed handshake messages
*/ */
input.incomingRecord(r); input.incomingRecord(record);
/* /*
* We don't need to create a separate delegatable task * We don't need to create a separate delegatable task
@ -946,6 +958,13 @@ abstract class Handshaker {
return; 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 * Process the message. We require
* that processMessage() consumes the entire message. In * that processMessage() consumes the entire message. In
@ -961,14 +980,16 @@ abstract class Handshaker {
* Also, note that hello request messages are never hashed; * Also, note that hello request messages are never hashed;
* that includes the hello request header, too. * that includes the hello request header, too.
*/ */
if (messageType == HandshakeMessage.ht_hello_request) {
input.reset();
processMessage(messageType, messageLen); processMessage(messageType, messageLen);
input.ignore(4 + messageLen);
} else { // Reload if this message has been reserved.
input.mark(messageLen); //
processMessage(messageType, messageLen); // Note: in the implementation, only certificate_verify and
input.digestNow(); // finished messages are reserved.
if ((messageType == HandshakeMessage.ht_finished) ||
(messageType == HandshakeMessage.ht_certificate_verify)) {
handshakeHash.reload();
} }
} }
} }
@ -980,29 +1001,29 @@ abstract class Handshaker {
* In activated state, the handshaker may not send any messages out. * In activated state, the handshaker may not send any messages out.
*/ */
boolean activated() { boolean activated() {
return state >= -1; return handshakeActivated;
} }
/** /**
* Returns true iff the handshaker has sent any messages. * Returns true iff the handshaker has sent any messages.
*/ */
boolean started() { boolean started() {
return state >= 0; // 0: HandshakeMessage.ht_hello_request return (serverHelloRequested || clientHelloDelivered);
// 1: HandshakeMessage.ht_client_hello
} }
/* /*
* Used to kickstart the negotiation ... either writing a * Used to kickstart the negotiation ... either writing a
* ClientHello or a HelloRequest as appropriate, whichever * ClientHello or a HelloRequest as appropriate, whichever
* the subclass returns. NOP if handshaking's already started. * the subclass returns. NOP if handshaking's already started.
*/ */
void kickstart() throws IOException { void kickstart() throws IOException {
if (state >= 0) { if ((isClient && clientHelloDelivered) ||
(!isClient && serverHelloRequested)) {
return; return;
} }
HandshakeMessage m = getKickstartMessage(); HandshakeMessage m = getKickstartMessage();
handshakeState.update(m, resumingSession);
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
m.print(System.out); m.print(System.out);
@ -1010,7 +1031,13 @@ abstract class Handshaker {
m.write(output); m.write(output);
output.flush(); 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 @@ abstract class Handshaker {
* We already hold SSLEngine/SSLSocket "this" by virtue * We already hold SSLEngine/SSLSocket "this" by virtue
* of this being called from the readRecord code. * 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) { if (conn != null) {
conn.writeLock.lock(); conn.writeLock.lock();
try { try {
conn.writeRecord(r); handshakeState.changeCipherSpec(false, isClient);
conn.changeWriteCiphers(); conn.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out); mesg.print(System.out);
} }
handshakeState.update(mesg, resumingSession);
mesg.write(output); mesg.write(output);
output.flush(); output.flush();
} finally { } finally {
@ -1077,19 +1096,25 @@ abstract class Handshaker {
} }
} else { } else {
synchronized (engine.writeLock) { synchronized (engine.writeLock) {
engine.writeRecord((EngineOutputRecord)r); handshakeState.changeCipherSpec(false, isClient);
engine.changeWriteCiphers(); engine.changeWriteCiphers();
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
mesg.print(System.out); mesg.print(System.out);
} }
mesg.write(output);
if (lastMessage) { handshakeState.update(mesg, resumingSession);
output.setFinishedMsg(); mesg.write(output);
}
output.flush(); output.flush();
} }
} }
if (lastMessage) {
handshakeFinished = true;
}
}
void receiveChangeCipherSpec() throws IOException {
handshakeState.changeCipherSpec(true, isClient);
} }
/* /*
@ -1131,6 +1156,24 @@ abstract class Handshaker {
String masterAlg; String masterAlg;
PRF prf; PRF prf;
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 {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
masterAlg = "SunTls12MasterSecret"; masterAlg = "SunTls12MasterSecret";
prf = cipherSuite.prfAlg; prf = cipherSuite.prfAlg;
@ -1138,6 +1181,7 @@ abstract class Handshaker {
masterAlg = "SunTlsMasterSecret"; masterAlg = "SunTlsMasterSecret";
prf = P_NONE; prf = P_NONE;
} }
}
String prfHashAlg = prf.getPRFHashAlg(); String prfHashAlg = prf.getPRFHashAlg();
int prfHashLength = prf.getPRFHashLength(); int prfHashLength = prf.getPRFHashLength();
@ -1145,7 +1189,7 @@ abstract class Handshaker {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec(
preMasterSecret, protocolVersion.major, protocolVersion.minor, preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF),
clnt_random.random_bytes, svr_random.random_bytes, clnt_random.random_bytes, svr_random.random_bytes,
prfHashAlg, prfHashLength, prfBlockSize); prfHashAlg, prfHashLength, prfBlockSize);
@ -1196,6 +1240,24 @@ abstract class Handshaker {
String keyMaterialAlg; String keyMaterialAlg;
PRF prf; PRF prf;
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 {
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
keyMaterialAlg = "SunTls12KeyMaterial"; keyMaterialAlg = "SunTls12KeyMaterial";
prf = cipherSuite.prfAlg; prf = cipherSuite.prfAlg;
@ -1203,25 +1265,26 @@ abstract class Handshaker {
keyMaterialAlg = "SunTlsKeyMaterial"; keyMaterialAlg = "SunTlsKeyMaterial";
prf = P_NONE; prf = P_NONE;
} }
}
String prfHashAlg = prf.getPRFHashAlg(); String prfHashAlg = prf.getPRFHashAlg();
int prfHashLength = prf.getPRFHashLength(); int prfHashLength = prf.getPRFHashLength();
int prfBlockSize = prf.getPRFBlockSize(); 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 // 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 // v1.2 or later use a fixed IV as the implicit part of the partially
// implicit nonce technique described in RFC 5116. // implicit nonce technique described in RFC 5116.
int ivSize = cipher.ivSize; int ivSize = cipher.ivSize;
if (cipher.cipherType == AEAD_CIPHER) { if (cipher.cipherType == AEAD_CIPHER) {
ivSize = cipher.fixedIvSize; ivSize = cipher.fixedIvSize;
} else if (protocolVersion.v >= ProtocolVersion.TLS11.v && } else if ((cipher.cipherType == BLOCK_CIPHER) &&
cipher.cipherType == BLOCK_CIPHER) { protocolVersion.useTLS11PlusSpec()) {
ivSize = 0; ivSize = 0;
} }
TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec(
masterKey, protocolVersion.major, protocolVersion.minor, masterKey, (majorVersion & 0xFF), (minorVersion & 0xFF),
clnt_random.random_bytes, svr_random.random_bytes, clnt_random.random_bytes, svr_random.random_bytes,
cipher.algorithm, cipher.keySize, expandedKeySize, cipher.algorithm, cipher.keySize, expandedKeySize,
ivSize, hashSize, ivSize, hashSize,
@ -1247,10 +1310,6 @@ abstract class Handshaker {
throw new ProviderException(e); 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. // Dump the connection keys as they're generated.
// //
@ -1293,7 +1352,7 @@ abstract class Handshaker {
System.out.println("Server write IV:"); System.out.println("Server write IV:");
printHex(dump, svrWriteIV.getIV()); printHex(dump, svrWriteIV.getIV());
} else { } else {
if (protocolVersion.v >= ProtocolVersion.TLS11.v) { if (protocolVersion.useTLS11PlusSpec()) {
System.out.println( System.out.println(
"... no IV derived for this protocol"); "... no IV derived for this protocol");
} else { } else {
@ -1305,15 +1364,6 @@ abstract class Handshaker {
} }
} }
/**
* 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) { private static void printHex(HexDumpEncoder dump, byte[] bytes) {
if (bytes == null) { if (bytes == null) {
System.out.println("(key bytes not available)"); System.out.println("(key bytes not available)");
@ -1326,19 +1376,6 @@ abstract class Handshaker {
} }
} }
/**
* 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. * Implement a simple task delegator.
* *

View file

@ -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[] cookie = cookieDigest.digest(cookieSecret); // 32 bytes
cookie[0] = (byte)((cookieVersion >> 24) & 0xFF);
cookie[1] = (byte)((cookieVersion >> 16) & 0xFF);
cookie[2] = (byte)((cookieVersion >> 8) & 0xFF);
cookie[3] = (byte)(cookieVersion & 0xFF);
return cookie;
}
// Used by server side to check the cookie in ClientHello message.
synchronized boolean isValid(ClientHello clientHelloMsg) {
byte[] cookie = clientHelloMsg.cookie;
// no cookie exchange or not a valid cookie length
if ((cookie == null) || (cookie.length != 32)) {
return false;
}
int version = ((cookie[0] & 0xFF) << 24) |
((cookie[1] & 0xFF) << 16) |
((cookie[2] & 0xFF) << 8) |
(cookie[3] & 0xFF);
byte[] secret;
if (version == cookieVersion) {
secret = cookieSecret;
} else if (version == prevCookieVersion) {
secret = prevCookieSecret;
} else {
return false; // may be out of the timing window
}
clientHelloMsg.updateHelloCookie(cookieDigest);
byte[] target = cookieDigest.digest(secret); // 32 bytes
for (int i = 4; i < 32; i++) {
if (cookie[i] != target[i]) {
return false;
}
}
return true;
}
// Used by client side to check the cookie in HelloVerifyRequest message.
static void checkCookie(ProtocolVersion protocolVersion,
byte[] cookie) throws IOException {
if (cookie != null && cookie.length != 0) {
int limit = COOKIE_MAX_LENGTH_DTLS12;
if (protocolVersion.v == ProtocolVersion.DTLS10.v) {
limit = COOKIE_MAX_LENGTH_DTLS10;
}
if (cookie.length > COOKIE_MAX_LENGTH_DTLS10) {
throw new SSLProtocolException(
"Invalid HelloVerifyRequest.cookie (length = " +
cookie.length + " bytes)");
}
}
// Otherwise, no cookie exchange.
}
}

View file

@ -85,6 +85,8 @@ final class HelloExtensions {
new SupportedEllipticPointFormatsExtension(s, extlen); new SupportedEllipticPointFormatsExtension(s, extlen);
} else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) { } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
extension = new RenegotiationInfoExtension(s, extlen); extension = new RenegotiationInfoExtension(s, extlen);
} else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
extension = new MaxFragmentLengthExtension(s, extlen);
} else { } else {
extension = new UnknownExtension(s, extlen, extType); extension = new UnknownExtension(s, extlen, extType);
} }

View file

@ -23,7 +23,6 @@
* questions. * questions.
*/ */
package sun.security.ssl; package sun.security.ssl;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
@ -50,7 +49,7 @@ import static sun.security.ssl.CipherSuite.MacAlg.*;
*/ */
final class MAC extends Authenticator { final class MAC extends Authenticator {
final static MAC NULL = new MAC(); final static MAC TLS_NULL = new MAC(false);
// Value of the null MAC is fixed // Value of the null MAC is fixed
private static final byte nullMAC[] = new byte[0]; private static final byte nullMAC[] = new byte[0];
@ -61,13 +60,15 @@ final class MAC extends Authenticator {
// JCE Mac object // JCE Mac object
private final Mac mac; private final Mac mac;
private MAC() { MAC(boolean isDTLS) {
super(isDTLS);
macAlg = M_NULL; macAlg = M_NULL;
mac = null; mac = null;
} }
/** /**
* Set up, configured for the given SSL/TLS MAC type and version. * Set up, configured for the given MAC type and version.
*/ */
MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key) MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key)
throws NoSuchAlgorithmException, InvalidKeyException { throws NoSuchAlgorithmException, InvalidKeyException {
@ -75,12 +76,14 @@ final class MAC extends Authenticator {
this.macAlg = macAlg; this.macAlg = macAlg;
String algorithm; String algorithm;
boolean tls = (protocolVersion.v >= ProtocolVersion.TLS10.v);
// using SSL MAC computation?
boolean useSSLMac = (protocolVersion.v < ProtocolVersion.TLS10.v);
if (macAlg == M_MD5) { if (macAlg == M_MD5) {
algorithm = tls ? "HmacMD5" : "SslMacMD5"; algorithm = useSSLMac ? "SslMacMD5" : "HmacMD5";
} else if (macAlg == M_SHA) { } else if (macAlg == M_SHA) {
algorithm = tls ? "HmacSHA1" : "SslMacSHA1"; algorithm = useSSLMac ? "SslMacSHA1" : "HmacSHA1";
} else if (macAlg == M_SHA256) { } else if (macAlg == M_SHA256) {
algorithm = "HmacSHA256"; // TLS 1.2+ algorithm = "HmacSHA256"; // TLS 1.2+
} else if (macAlg == M_SHA384) { } else if (macAlg == M_SHA384) {
@ -122,6 +125,8 @@ final class MAC extends Authenticator {
* @param offset start of compressed record data * @param offset start of compressed record data
* @param len the size of the compressed record * @param len the size of the compressed record
* @param isSimulated if true, simulate the MAC computation * @param isSimulated if true, simulate the MAC computation
*
* @return the MAC result
*/ */
final byte[] compute(byte type, byte buf[], final byte[] compute(byte type, byte buf[],
int offset, int len, boolean isSimulated) { int offset, int len, boolean isSimulated) {
@ -130,7 +135,8 @@ final class MAC extends Authenticator {
} }
if (!isSimulated) { if (!isSimulated) {
byte[] additional = acquireAuthenticationBytes(type, len); // Uses the implicit sequence number for the computation.
byte[] additional = acquireAuthenticationBytes(type, len, null);
mac.update(additional); mac.update(additional);
} }
mac.update(buf, offset, len); mac.update(buf, offset, len);
@ -149,15 +155,22 @@ final class MAC extends Authenticator {
* @param bb a ByteBuffer in which the position and limit * @param bb a ByteBuffer in which the position and limit
* demarcate the data to be MAC'd. * demarcate the data to be MAC'd.
* @param isSimulated if true, simulate the MAC computation * @param isSimulated if true, simulate the MAC computation
* @param sequence the explicit sequence number, or null if using
* the implicit sequence number for the computation
*
* @return the MAC result
*/ */
final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) { final byte[] compute(byte type, ByteBuffer bb,
byte[] sequence, boolean isSimulated) {
if (macAlg.size == 0) { if (macAlg.size == 0) {
return nullMAC; return nullMAC;
} }
if (!isSimulated) { if (!isSimulated) {
// Uses the explicit sequence number for the computation.
byte[] additional = byte[] additional =
acquireAuthenticationBytes(type, bb.remaining()); acquireAuthenticationBytes(type, bb.remaining(), sequence);
mac.update(additional); mac.update(additional);
} }
mac.update(bb); mac.update(bb);
@ -165,5 +178,22 @@ final class MAC extends Authenticator {
return mac.doFinal(); return mac.doFinal();
} }
/**
* Compute and returns the MAC for the remaining data
* in this ByteBuffer.
*
* On return, the bb position == limit, and limit will
* have not changed.
*
* @param type record type
* @param bb a ByteBuffer in which the position and limit
* demarcate the data to be MAC'd.
* @param isSimulated if true, simulate the the MAC computation
*
* @return the MAC result
*/
final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) {
// Uses the implicit sequence number for the computation.
return compute(type, bb, null, isSimulated);
}
} }

View file

@ -0,0 +1,139 @@
/*
* 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;
/*
* [RFC6066] TLS specifies a fixed maximum plaintext fragment length of
* 2^14 bytes. It may be desirable for constrained clients to negotiate
* a smaller maximum fragment length due to memory limitations or bandwidth
* limitations.
*
* In order to negotiate smaller maximum fragment lengths, clients MAY
* include an extension of type "max_fragment_length" in the (extended)
* client hello. The "extension_data" field of this extension SHALL
* contain:
*
* enum{
* 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
* } MaxFragmentLength;
*
* whose value is the desired maximum fragment length.
*/
final class MaxFragmentLengthExtension extends HelloExtension {
private static final int MAX_FRAGMENT_LENGTH_512 = 1; // 2^9
private static final int MAX_FRAGMENT_LENGTH_1024 = 2; // 2^10
private static final int MAX_FRAGMENT_LENGTH_2048 = 3; // 2^11
private static final int MAX_FRAGMENT_LENGTH_4096 = 4; // 2^12
final int maxFragmentLength;
MaxFragmentLengthExtension(int fragmentSize) {
super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
if (fragmentSize < 1024) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_512;
} else if (fragmentSize < 2048) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_1024;
} else if (fragmentSize < 4096) {
maxFragmentLength = MAX_FRAGMENT_LENGTH_2048;
} else {
maxFragmentLength = MAX_FRAGMENT_LENGTH_4096;
}
}
MaxFragmentLengthExtension(HandshakeInStream s, int len)
throws IOException {
super(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
// check the extension length
if (len != 1) {
throw new SSLProtocolException("Invalid " + type + " extension");
}
maxFragmentLength = s.getInt8();
if ((maxFragmentLength > 4) || (maxFragmentLength < 1)) {
throw new SSLProtocolException("Invalid " + type + " extension");
}
}
// Length of the encoded extension, including the type and length fields
@Override
int length() {
return 5; // 4: extension type and length fields
// 1: MaxFragmentLength field
}
@Override
void send(HandshakeOutStream s) throws IOException {
s.putInt16(type.id);
s.putInt16(1);
s.putInt8(maxFragmentLength);
}
int getMaxFragLen() {
switch (maxFragmentLength) {
case MAX_FRAGMENT_LENGTH_512:
return 512;
case MAX_FRAGMENT_LENGTH_1024:
return 1024;
case MAX_FRAGMENT_LENGTH_2048:
return 2048;
case MAX_FRAGMENT_LENGTH_4096:
return 4096;
}
// unlikely to happen
return -1;
}
static boolean needFragLenNego(int fragmentSize) {
return (fragmentSize > 0) && (fragmentSize <= 4096);
}
static int getValidMaxFragLen(int fragmentSize) {
if (fragmentSize < 1024) {
return 512;
} else if (fragmentSize < 2048) {
return 1024;
} else if (fragmentSize < 4096) {
return 2048;
} else if (fragmentSize == 4096) {
return 4096;
} else {
return 16384;
}
}
@Override
public String toString() {
return "Extension " + type + ", max_fragment_length: " +
"(2^" + (maxFragmentLength + 8) + ")";
}
}

View file

@ -23,7 +23,6 @@
* questions. * questions.
*/ */
package sun.security.ssl; package sun.security.ssl;
import java.io.*; import java.io.*;
@ -35,26 +34,22 @@ import sun.misc.HexDumpEncoder;
/** /**
* SSL 3.0 records, as written to a TCP stream. * {@code OutputRecord} takes care of the management of SSL/TLS/DTLS output
* * records, including buffering, encryption, handshake messages marshal, etc.
* Each record has a message area that starts out with data supplied by the
* application. It may grow/shrink due to compression and will be modified
* in place for mac-ing and encryption.
*
* Handshake records have additional needs, notably accumulation of a set
* of hashes which are used to establish that handshaking was done right.
* Handshake records usually have several handshake messages each, and we
* need message-level control over what's hashed.
* *
* @author David Brownell * @author David Brownell
*/ */
class OutputRecord extends ByteArrayOutputStream implements Record { abstract class OutputRecord extends ByteArrayOutputStream
implements Record, Closeable {
private HandshakeHash handshakeHash; /* Class and subclass dynamic debugging support */
private int lastHashed; static final Debug debug = Debug.getInstance("ssl");
private boolean firstMessage;
final private byte contentType; Authenticator writeAuthenticator;
private int headerOffset; CipherBox writeCipher;
HandshakeHash handshakeHash;
boolean firstMessage;
// current protocol version, sent as record version // current protocol version, sent as record version
ProtocolVersion protocolVersion; ProtocolVersion protocolVersion;
@ -62,65 +57,38 @@ class OutputRecord extends ByteArrayOutputStream implements Record {
// version for the ClientHello message. Only relevant if this is a // version for the ClientHello message. Only relevant if this is a
// client handshake record. If set to ProtocolVersion.SSL20Hello, // client handshake record. If set to ProtocolVersion.SSL20Hello,
// the V3 client hello is converted to V2 format. // the V3 client hello is converted to V2 format.
private ProtocolVersion helloVersion; ProtocolVersion helloVersion;
/* Class and subclass dynamic debugging support */ // Is it the first application record to write?
static final Debug debug = Debug.getInstance("ssl"); boolean isFirstAppOutputRecord = true;
// packet size
int packetSize;
// fragment size
int fragmentSize;
// closed or not?
boolean isClosed;
/* /*
* Default constructor makes a record supporting the maximum * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
* SSL record size. It allocates the header bytes directly. * This is taken from the SSL V3 specification, Appendix E.
*
* The structure of the byte buffer looks like:
*
* |---------+--------+-------+---------------------------------|
* | unused | header | IV | content, MAC/TAG, padding, etc. |
* | headerPlusMaxIVSize |
*
* unused: unused part of the buffer of size
*
* headerPlusMaxIVSize - header size - IV size
*
* When this object is created, we don't know the protocol
* version number, IV length, etc., so reserve space in front
* to avoid extra data movement (copies).
* header: the header of an SSL record
* IV: the optional IV/nonce field, it is only required for block
* (TLS 1.1 or later) and AEAD cipher suites.
*
* @param type the content type for the record
*/ */
OutputRecord(byte type, int size) { private static int[] V3toV2CipherMap1 =
super(size); {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
this.protocolVersion = ProtocolVersion.DEFAULT; private static int[] V3toV2CipherMap3 =
this.helloVersion = ProtocolVersion.DEFAULT_HELLO; {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
firstMessage = true;
count = headerPlusMaxIVSize; OutputRecord() {
contentType = type; this.writeCipher = CipherBox.NULL;
lastHashed = count; this.firstMessage = true;
headerOffset = headerPlusMaxIVSize - headerSize; this.fragmentSize = Record.maxDataSize;
// Please set packetSize and protocolVersion in the implementation.
} }
OutputRecord(byte type) { void setVersion(ProtocolVersion protocolVersion) {
this(type, recordSize(type));
}
/**
* Get the size of the buffer we need for records of the specified
* type.
*/
private static int recordSize(byte type) {
if ((type == ct_change_cipher_spec) || (type == ct_alert)) {
return maxAlertRecordSize;
} else {
return maxRecordSize;
}
}
/*
* Updates the SSL version of this record.
*/
synchronized void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
} }
@ -131,369 +99,313 @@ class OutputRecord extends ByteArrayOutputStream implements Record {
this.helloVersion = helloVersion; this.helloVersion = helloVersion;
} }
/*
* Reset the record so that it can be refilled, starting
* immediately after the header.
*/
@Override
public synchronized void reset() {
super.reset();
count = headerPlusMaxIVSize;
lastHashed = count;
headerOffset = headerPlusMaxIVSize - headerSize;
}
/* /*
* For handshaking, we need to be able to hash every byte above the * For handshaking, we need to be able to hash every byte above the
* record marking layer. This is where we're guaranteed to see those * record marking layer. This is where we're guaranteed to see those
* bytes, so this is where we can hash them. * bytes, so this is where we can hash them.
*/ */
void setHandshakeHash(HandshakeHash handshakeHash) { void setHandshakeHash(HandshakeHash handshakeHash) {
assert(contentType == ct_handshake);
this.handshakeHash = handshakeHash; this.handshakeHash = handshakeHash;
} }
/*
* We hash (the plaintext) on demand. There is one place where
* we want to access the hash in the middle of a record: client
* cert message gets hashed, and part of the same record is the
* client cert verify message which uses that hash. So we track
* how much of each record we've hashed so far.
*/
void doHashes() {
int len = count - lastHashed;
if (len > 0) {
hashInternal(buf, lastHashed, len);
lastHashed = count;
}
}
/*
* Need a helper function so we can hash the V2 hello correctly
*/
private void hashInternal(byte buf [], int offset, int len) {
if (debug != null && Debug.isOn("data")) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
System.out.println("[write] MD5 and SHA1 hashes: len = "
+ len);
hd.encodeBuffer(new ByteArrayInputStream(buf,
lastHashed, len), System.out);
} catch (IOException e) { }
}
handshakeHash.update(buf, lastHashed, len);
lastHashed = count;
}
/* /*
* Return true iff the record is empty -- to avoid doing the work * Return true iff the record is empty -- to avoid doing the work
* of sending empty records over the network. * of sending empty records over the network.
*/ */
boolean isEmpty() { boolean isEmpty() {
return count == headerPlusMaxIVSize;
}
/*
* Return true if the record is of an alert of the given description.
*
* Per SSL/TLS specifications, alert messages convey the severity of the
* message (warning or fatal) and a description of the alert. An alert
* is defined with a two bytes struct, {byte level, byte description},
* following after the header bytes.
*/
boolean isAlert(byte description) {
if ((count > (headerPlusMaxIVSize + 1)) && (contentType == ct_alert)) {
return buf[headerPlusMaxIVSize + 1] == description;
}
return false; return false;
} }
/* boolean seqNumIsHuge() {
* Encrypt ... length may grow due to block cipher padding, or return (writeAuthenticator != null) &&
* message authentication code or tag. writeAuthenticator.seqNumIsHuge();
*/
void encrypt(Authenticator authenticator, CipherBox box)
throws IOException {
// In case we are automatically flushing a handshake stream, make
// sure we have hashed the message first.
//
// when we support compression, hashing can't go here
// since it'll need to be done on the uncompressed data,
// and the MAC applies to the compressed data.
if (contentType == ct_handshake) {
doHashes();
} }
// Requires message authentication code for stream and block // SSLEngine and SSLSocket
// cipher suites. abstract void encodeAlert(byte level, byte description) throws IOException;
// SSLEngine and SSLSocket
abstract void encodeHandshake(byte[] buffer,
int offset, int length) throws IOException;
// SSLEngine and SSLSocket
abstract void encodeChangeCipherSpec() throws IOException;
// apply to SSLEngine only
Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLEngine only
void encodeV2NoCipher() throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
void deliver(byte[] source, int offset, int length) throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
void setDeliverStream(OutputStream outputStream) {
throw new UnsupportedOperationException();
}
// apply to SSLEngine only
Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
throw new UnsupportedOperationException();
}
void changeWriteCiphers(Authenticator writeAuthenticator,
CipherBox writeCipher) throws IOException {
encodeChangeCipherSpec();
/*
* Dispose of any intermediate state in the underlying cipher.
* For PKCS11 ciphers, this will release any attached sessions,
* and thus make finalization faster.
*
* Since MAC's doFinal() is called for every SSL/TLS packet, it's
* not necessary to do the same with MAC's.
*/
writeCipher.dispose();
this.writeAuthenticator = writeAuthenticator;
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
}
void changePacketSize(int packetSize) {
this.packetSize = packetSize;
}
void changeFragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize;
}
int getMaxPacketSize() {
return packetSize;
}
// apply to DTLS SSLEngine
void initHandshaker() {
// blank
}
@Override
synchronized public void close() throws IOException {
if (!isClosed) {
isClosed = true;
writeCipher.dispose();
}
}
//
// shared helpers
//
// Encrypt a fragment and wrap up a record.
//
// To be consistent with the spec of SSLEngine.wrap() methods, the
// destination ByteBuffer's position is updated to reflect the amount
// of data produced. The limit remains the same.
static long encrypt(Authenticator authenticator,
CipherBox encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion, boolean isDTLS) {
byte[] sequenceNumber = null;
int dstContent = destination.position();
// Acquire the current sequence number before using.
if (isDTLS) {
sequenceNumber = authenticator.sequenceNumber();
}
// "flip" but skip over header again, add MAC & encrypt
if (authenticator instanceof MAC) { if (authenticator instanceof MAC) {
MAC signer = (MAC)authenticator; MAC signer = (MAC)authenticator;
if (signer.MAClen() != 0) { if (signer.MAClen() != 0) {
byte[] hash = signer.compute(contentType, buf, byte[] hash = signer.compute(contentType, destination, false);
headerPlusMaxIVSize, count - headerPlusMaxIVSize, false);
write(hash); /*
* position was advanced to limit in MAC compute above.
*
* Mark next area as writable (above layers should have
* established that we have plenty of room), then write
* out the hash.
*/
destination.limit(destination.limit() + hash.length);
destination.put(hash);
// reset the position and limit
destination.limit(destination.position());
destination.position(dstContent);
} }
} }
if (!box.isNullCipher()) { if (!encCipher.isNullCipher()) {
// Requires explicit IV/nonce for CBC/AEAD cipher suites for if (protocolVersion.useTLS11PlusSpec() &&
// TLS 1.1 or later. (encCipher.isCBCMode() || encCipher.isAEADMode())) {
if ((protocolVersion.v >= ProtocolVersion.TLS11.v) && byte[] nonce = encCipher.createExplicitNonce(
(box.isCBCMode() || box.isAEADMode())) { authenticator, contentType, destination.remaining());
byte[] nonce = box.createExplicitNonce(authenticator, destination.position(headerOffset + headerSize);
contentType, count - headerPlusMaxIVSize); destination.put(nonce);
int offset = headerPlusMaxIVSize - nonce.length;
System.arraycopy(nonce, 0, buf, offset, nonce.length);
headerOffset = offset - headerSize;
} else {
headerOffset = headerPlusMaxIVSize - headerSize;
} }
if (!encCipher.isAEADMode()) {
// encrypt the content // The explicit IV in TLS 1.1 and later can be encrypted.
int offset = headerPlusMaxIVSize; destination.position(headerOffset + headerSize);
if (!box.isAEADMode()) {
// The explicit IV can be encrypted.
offset = headerOffset + headerSize;
} // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
count = offset + box.encrypt(buf, offset, count - offset); // Encrypt may pad, so again the limit may be changed.
} encCipher.encrypt(destination, dstLim);
}
/*
* Tell how full the buffer is ... for filling it with application or
* handshake data.
*/
final int availableDataBytes() {
int dataSize = count - headerPlusMaxIVSize;
return maxDataSize - dataSize;
}
/*
* Increases the capacity if necessary to ensure that it can hold
* at least the number of elements specified by the minimum
* capacity argument.
*
* Note that the increased capacity is only can be used for held
* record buffer. Please DO NOT update the availableDataBytes()
* according to the expended buffer capacity.
*
* @see availableDataBytes()
*/
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
if (minCapacity > buf.length) {
buf = Arrays.copyOf(buf, minCapacity);
}
}
/*
* Return the type of SSL record that's buffered here.
*/
final byte contentType() {
return contentType;
}
/*
* Write the record out on the stream. Note that you must have (in
* order) compressed the data, appended the MAC, and encrypted it in
* order for the record to be understood by the other end. (Some of
* those steps will be null early in handshaking.)
*
* Note that this does no locking for the connection, it's required
* that synchronization be done elsewhere. Also, this does its work
* in a single low level write, for efficiency.
*/
void write(OutputStream s, boolean holdRecord,
ByteArrayOutputStream heldRecordBuffer) throws IOException {
/*
* Don't emit content-free records. (Even change cipher spec
* messages have a byte of data!)
*/
if (count == headerPlusMaxIVSize) {
return;
}
int length = count - headerOffset - headerSize;
// "should" really never write more than about 14 Kb...
if (length < 0) {
throw new SSLException("output record size too small: "
+ length);
}
if (debug != null
&& (Debug.isOn("record") || Debug.isOn("handshake"))) {
if ((debug != null && Debug.isOn("record"))
|| contentType() == ct_change_cipher_spec)
System.out.println(Thread.currentThread().getName()
// v3.0/v3.1 ...
+ ", WRITE: " + protocolVersion
+ " " + InputRecord.contentName(contentType())
+ ", length = " + length);
}
/*
* If this is the initial ClientHello on this connection and
* we're not trying to resume a (V3) session then send a V2
* ClientHello instead so we can detect V2 servers cleanly.
*/
if (firstMessage && useV2Hello()) {
byte[] v3Msg = new byte[length - 4];
System.arraycopy(buf, headerPlusMaxIVSize + 4,
v3Msg, 0, v3Msg.length);
headerOffset = 0; // reset the header offset
V3toV2ClientHello(v3Msg);
handshakeHash.reset();
lastHashed = 2;
doHashes();
if (debug != null && Debug.isOn("record")) {
System.out.println(
Thread.currentThread().getName()
+ ", WRITE: SSLv2 client hello message"
+ ", length = " + (count - 2)); // 2 byte SSLv2 header
}
} else { } else {
/* destination.position(destination.limit());
* Fill out the header, write it and the message.
*/
buf[headerOffset + 0] = contentType;
buf[headerOffset + 1] = protocolVersion.major;
buf[headerOffset + 2] = protocolVersion.minor;
buf[headerOffset + 3] = (byte)(length >> 8);
buf[headerOffset + 4] = (byte)(length);
} }
firstMessage = false;
/* // Finish out the record header.
* The upper levels may want us to delay sending this packet so int fragLen = destination.limit() - headerOffset - headerSize;
* multiple TLS Records can be sent in one (or more) TCP packets.
* If so, add this packet to the heldRecordBuffer. destination.put(headerOffset, contentType); // content type
* destination.put(headerOffset + 1, protocolVersion.major);
* NOTE: all writes have been synchronized by upper levels. destination.put(headerOffset + 2, protocolVersion.minor);
*/ if (!isDTLS) {
int debugOffset = 0; // fragment length
if (holdRecord) { destination.put(headerOffset + 3, (byte)(fragLen >> 8));
/* destination.put(headerOffset + 4, (byte)fragLen);
* If holdRecord is true, we must have a heldRecordBuffer.
*
* Don't worry about the override of writeBuffer(), because
* when holdRecord is true, the implementation in this class
* will be used.
*/
writeBuffer(heldRecordBuffer,
buf, headerOffset, count - headerOffset, debugOffset);
} else { } else {
// It's time to send, do we have buffered data? // epoch and sequence_number
// May or may not have a heldRecordBuffer. destination.put(headerOffset + 3, sequenceNumber[0]);
if (heldRecordBuffer != null && heldRecordBuffer.size() > 0) { destination.put(headerOffset + 4, sequenceNumber[1]);
int heldLen = heldRecordBuffer.size(); destination.put(headerOffset + 5, sequenceNumber[2]);
destination.put(headerOffset + 6, sequenceNumber[3]);
destination.put(headerOffset + 7, sequenceNumber[4]);
destination.put(headerOffset + 8, sequenceNumber[5]);
destination.put(headerOffset + 9, sequenceNumber[6]);
destination.put(headerOffset + 10, sequenceNumber[7]);
// Ensure the capacity of this buffer. // fragment length
int newCount = count + heldLen - headerOffset; destination.put(headerOffset + 11, (byte)(fragLen >> 8));
ensureCapacity(newCount); destination.put(headerOffset + 12, (byte)fragLen);
// Slide everything in the buffer to the right. // Increase the sequence number for next use.
System.arraycopy(buf, headerOffset, authenticator.increaseSequenceNumber();
buf, heldLen, count - headerOffset);
// Prepend the held record to the buffer.
System.arraycopy(
heldRecordBuffer.toByteArray(), 0, buf, 0, heldLen);
count = newCount;
headerOffset = 0;
// Clear the held buffer.
heldRecordBuffer.reset();
// The held buffer has been dumped, set the debug dump offset.
debugOffset = heldLen;
}
writeBuffer(s, buf, headerOffset,
count - headerOffset, debugOffset);
} }
reset(); // Update destination position to reflect the amount of data produced.
destination.position(destination.limit());
return Authenticator.toLong(sequenceNumber);
} }
// Encrypt a fragment and wrap up a record.
//
// Uses the internal expandable buf variable and the current
// protocolVersion variable.
void encrypt(Authenticator authenticator,
CipherBox encCipher, byte contentType, int headerSize) {
int position = headerSize + writeCipher.getExplicitNonceSize();
// "flip" but skip over header again, add MAC & encrypt
int macLen = 0;
if (authenticator instanceof MAC) {
MAC signer = (MAC)authenticator;
macLen = signer.MAClen();
if (macLen != 0) {
byte[] hash = signer.compute(contentType,
buf, position, (count - position), false);
write(hash, 0, hash.length);
}
}
if (!encCipher.isNullCipher()) {
// Requires explicit IV/nonce for CBC/AEAD cipher suites for
// TLS 1.1 or later.
if (protocolVersion.useTLS11PlusSpec() &&
(encCipher.isCBCMode() || encCipher.isAEADMode())) {
byte[] nonce = encCipher.createExplicitNonce(
authenticator, contentType, (count - position));
int noncePos = position - nonce.length;
System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
}
if (!encCipher.isAEADMode()) {
// The explicit IV in TLS 1.1 and later can be encrypted.
position = headerSize;
} // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
// increase buf capacity if necessary
int fragSize = count - position;
int packetSize =
encCipher.calculatePacketSize(fragSize, macLen, headerSize);
if (packetSize > (buf.length - position)) {
byte[] newBuf = new byte[position + packetSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
// Encrypt may pad, so again the count may be changed.
count = position +
encCipher.encrypt(buf, position, (count - position));
}
// Fill out the header, write it and the message.
int fragLen = count - headerSize;
buf[0] = contentType;
buf[1] = protocolVersion.major;
buf[2] = protocolVersion.minor;
buf[3] = (byte)((fragLen >> 8) & 0xFF);
buf[4] = (byte)(fragLen & 0xFF);
}
static ByteBuffer encodeV2ClientHello(
byte[] fragment, int offset, int length) throws IOException {
int v3SessIdLenOffset = offset + 34; // 2: client_version
// 32: random
int v3SessIdLen = fragment[v3SessIdLenOffset];
int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
(fragment[v3CSLenOffset + 1] & 0xff);
int cipherSpecs = v3CSLen / 2; // 2: cipher spec size
// Estimate the max V2ClientHello message length
//
// 11: header size
// (cipherSpecs * 6): cipher_specs
// 6: one cipher suite may need 6 bytes, see V3toV2CipherSuite.
// 3: placeholder for the TLS_EMPTY_RENEGOTIATION_INFO_SCSV
// signaling cipher suite
// 32: challenge size
int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
// Create a ByteBuffer backed by an accessible byte array.
byte[] dstBytes = new byte[v2MaxMsgLen];
ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
/* /*
* Actually do the write here. For SSLEngine's HS data, * Copy over the cipher specs. We don't care about actually
* we'll override this method and let it take the appropriate * translating them for use with an actual V2 server since
* action. * we only talk V3. Therefore, just copy over the V3 cipher
* spec values with a leading 0.
*/ */
void writeBuffer(OutputStream s, byte [] buf, int off, int len, int v3CSOffset = v3CSLenOffset + 2; // skip length field
int debugOffset) throws IOException { int v2CSLen = 0;
s.write(buf, off, len);
s.flush();
// Output only the record from the specified debug offset. dstBuf.position(11);
if (debug != null && Debug.isOn("packet")) {
try {
HexDumpEncoder hd = new HexDumpEncoder();
System.out.println("[Raw write]: length = " +
(len - debugOffset));
hd.encodeBuffer(new ByteArrayInputStream(buf,
off + debugOffset, len - debugOffset), System.out);
} catch (IOException e) { }
}
}
/*
* Return whether the buffer contains a ClientHello message that should
* be converted to V2 format.
*/
private boolean useV2Hello() {
return firstMessage
&& (helloVersion == ProtocolVersion.SSL20Hello)
&& (contentType == ct_handshake)
&& (buf[headerOffset + 5] == HandshakeMessage.ht_client_hello)
// 5: recode header size
&& (buf[headerPlusMaxIVSize + 4 + 2 + 32] == 0);
// V3 session ID is empty
// 4: handshake header size
// 2: client_version in ClientHello
// 32: random in ClientHello
}
/*
* Detect "old" servers which are capable of SSL V2.0 protocol ... for
* example, Netscape Commerce 1.0 servers. The V3 message is in the
* header and the bytes passed as parameter. This routine translates
* the V3 message into an equivalent V2 one.
*
* Note that the translation will strip off all hello extensions as
* SSL V2.0 does not support hello extension.
*/
private void V3toV2ClientHello(byte v3Msg []) throws SSLException {
int v3SessionIdLenOffset = 2 + 32; // version + nonce
int v3SessionIdLen = v3Msg[v3SessionIdLenOffset];
int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen;
int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) +
(v3Msg[v3CipherSpecLenOffset + 1] & 0xff);
int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3
/*
* Copy over the cipher specs. We don't care about actually translating
* them for use with an actual V2 server since we only talk V3.
* Therefore, just copy over the V3 cipher spec values with a leading
* 0.
*/
int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length
int v2CipherSpecLen = 0;
count = 11;
boolean containsRenegoInfoSCSV = false; boolean containsRenegoInfoSCSV = false;
for (int i = 0; i < cipherSpecs; i++) { for (int i = 0; i < cipherSpecs; i++) {
byte byte1, byte2; byte byte1, byte2;
byte1 = v3Msg[v3CipherSpecOffset++]; byte1 = fragment[v3CSOffset++];
byte2 = v3Msg[v3CipherSpecOffset++]; byte2 = fragment[v3CSOffset++];
v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2); v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
if (!containsRenegoInfoSCSV && if (!containsRenegoInfoSCSV &&
byte1 == (byte)0x00 && byte2 == (byte)0xFF) { byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
containsRenegoInfoSCSV = true; containsRenegoInfoSCSV = true;
@ -501,72 +413,51 @@ class OutputRecord extends ByteArrayOutputStream implements Record {
} }
if (!containsRenegoInfoSCSV) { if (!containsRenegoInfoSCSV) {
v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF); v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
} }
/*
* Copy in the nonce.
*/
dstBuf.put(fragment, (offset + 2), 32);
/* /*
* Build the first part of the V3 record header from the V2 one * Build the first part of the V3 record header from the V2 one
* that's now buffered up. (Lengths are fixed up later). * that's now buffered up. (Lengths are fixed up later).
*/ */
buf[2] = HandshakeMessage.ht_client_hello; int msgLen = dstBuf.position() - 2; // Exclude the legth field itself
buf[3] = v3Msg[0]; // major version dstBuf.position(0);
buf[4] = v3Msg[1]; // minor version dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF))); // pos: 0
buf[5] = (byte)(v2CipherSpecLen >>> 8); dstBuf.put((byte)(msgLen & 0xFF)); // pos: 1
buf[6] = (byte)v2CipherSpecLen; dstBuf.put(HandshakeMessage.ht_client_hello); // pos: 2
buf[7] = 0; dstBuf.put(fragment[offset]); // major version, pos: 3
buf[8] = 0; // always no session dstBuf.put(fragment[offset + 1]); // minor version, pos: 4
buf[9] = 0; dstBuf.put((byte)(v2CSLen >>> 8)); // pos: 5
buf[10] = 32; // nonce length (always 32 in V3) dstBuf.put((byte)(v2CSLen & 0xFF)); // pos: 6
dstBuf.put((byte)0x00); // session_id_length, pos: 7
dstBuf.put((byte)0x00); // pos: 8
dstBuf.put((byte)0x00); // challenge_length, pos: 9
dstBuf.put((byte)32); // pos: 10
/* dstBuf.position(0);
* Copy in the nonce. dstBuf.limit(msgLen + 2);
*/
System.arraycopy(v3Msg, 2, buf, count, 32);
count += 32;
/* return dstBuf;
* Set the length of the message.
*/
count -= 2; // don't include length field itself
buf[0] = (byte)(count >>> 8);
buf[0] |= 0x80;
buf[1] = (byte)(count);
count += 2;
} }
/* private static int V3toV2CipherSuite(ByteBuffer dstBuf,
* Mappings from V3 cipher suite encodings to their pure V2 equivalents. byte byte1, byte byte2) {
* This is taken from the SSL V3 specification, Appendix E. dstBuf.put((byte)0);
*/ dstBuf.put(byte1);
private static int[] V3toV2CipherMap1 = dstBuf.put(byte2);
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
private static int[] V3toV2CipherMap3 =
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
/* if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
* See which matching pure-V2 cipher specs we need to include.
* We are including these not because we are actually prepared
* to talk V2 but because the Oracle Web Server insists on receiving
* at least 1 "pure V2" cipher suite that it supports and returns an
* illegal_parameter alert unless one is present. Rather than mindlessly
* claiming to implement all documented pure V2 cipher suites the code below
* just claims to implement the V2 cipher suite that is "equivalent"
* in terms of cipher algorithm & exportability with the actual V3 cipher
* suite that we do support.
*/
private int V3toV2CipherSuite(byte byte1, byte byte2) {
buf[count++] = 0;
buf[count++] = byte1;
buf[count++] = byte2;
if (((byte2 & 0xff) > 0xA) ||
(V3toV2CipherMap1[byte2] == -1)) {
return 3; return 3;
} }
buf[count++] = (byte)V3toV2CipherMap1[byte2]; dstBuf.put((byte)V3toV2CipherMap1[byte2]);
buf[count++] = 0; dstBuf.put((byte)0);
buf[count++] = (byte)V3toV2CipherMap3[byte2]; dstBuf.put((byte)V3toV2CipherMap3[byte2]);
return 6; return 6;
} }

View file

@ -0,0 +1,77 @@
/*
* 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.nio.ByteBuffer;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
/*
* Plaintext
*/
final class Plaintext {
final static Plaintext PLAINTEXT_NULL = new Plaintext();
byte contentType;
byte majorVersion;
byte minorVersion;
int recordEpoch; // incremented on every cipher state change
long recordSN;
ByteBuffer fragment; // null if need to be reassembled
HandshakeStatus handshakeStatus; // null if not used or not handshaking
Plaintext() {
this.contentType = 0;
this.majorVersion = 0;
this.minorVersion = 0;
this.recordEpoch = -1;
this.recordSN = -1;
this.fragment = null;
this.handshakeStatus = null;
}
Plaintext(byte contentType, byte majorVersion, byte minorVersion,
int recordEpoch, long recordSN, ByteBuffer fragment) {
this.contentType = contentType;
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.recordEpoch = recordEpoch;
this.recordSN = recordSN;
this.fragment = fragment;
this.handshakeStatus = null;
}
public String toString() {
return "contentType: " + contentType + "/" +
"majorVersion: " + majorVersion + "/" +
"minorVersion: " + minorVersion + "/" +
"recordEpoch: " + recordEpoch + "/" +
"recordSN: 0x" + Long.toHexString(recordSN) + "/" +
"fragment: " + fragment;
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -124,8 +124,9 @@ final class ProtocolList {
ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) { ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
ProtocolVersion selectedVersion = null; ProtocolVersion selectedVersion = null;
for (ProtocolVersion pv : protocols) { for (ProtocolVersion pv : protocols) {
if (pv.v > protocolVersion.v) { if (pv.compareTo(protocolVersion) > 0) {
break; // Safe to break here as this.protocols is sorted break; // Safe to break here as this.protocols is sorted,
// and DTLS and SSL/TLS protocols are not mixed.
} }
selectedVersion = pv; selectedVersion = pv;
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,6 +27,7 @@ package sun.security.ssl;
import java.util.*; import java.util.*;
import java.security.CryptoPrimitive; import java.security.CryptoPrimitive;
import sun.security.ssl.CipherSuite.*;
/** /**
* Type safe enum for an SSL/TLS protocol version. Instances are obtained * Type safe enum for an SSL/TLS protocol version. Instances are obtained
@ -61,9 +62,9 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// Dummy protocol version value for invalid SSLSession // Dummy protocol version value for invalid SSLSession
final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE"); final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE");
// If enabled, send/ accept SSLv2 hello messages // If enabled, send/accept SSLv2 hello messages
final static ProtocolVersion SSL20Hello = new ProtocolVersion(0x0002, final static ProtocolVersion SSL20Hello =
"SSLv2Hello"); new ProtocolVersion(0x0002, "SSLv2Hello");
// SSL 3.0 // SSL 3.0
final static ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3"); final static ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3");
@ -77,6 +78,19 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// TLS 1.2 // TLS 1.2
final static ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2"); final static ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2");
// DTLS 1.0
// {254, 255}, the version value of DTLS 1.0.
final static ProtocolVersion DTLS10 =
new ProtocolVersion(0xFEFF, "DTLSv1.0");
// No DTLS 1.1, that version number was skipped in order to harmonize
// version numbers with TLS.
// DTLS 1.2
// {254, 253}, the version value of DTLS 1.2.
final static ProtocolVersion DTLS12 =
new ProtocolVersion(0xFEFD, "DTLSv1.2");
private static final boolean FIPS = SunJSSE.isFIPS(); private static final boolean FIPS = SunJSSE.isFIPS();
// minimum version we implement (SSL 3.0) // minimum version we implement (SSL 3.0)
@ -85,8 +99,11 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// maximum version we implement (TLS 1.2) // maximum version we implement (TLS 1.2)
final static ProtocolVersion MAX = TLS12; final static ProtocolVersion MAX = TLS12;
// ProtocolVersion to use by default (TLS 1.2) // SSL/TLS ProtocolVersion to use by default (TLS 1.2)
final static ProtocolVersion DEFAULT = TLS12; final static ProtocolVersion DEFAULT_TLS = TLS12;
// DTLS ProtocolVersion to use by default (TLS 1.2)
final static ProtocolVersion DEFAULT_DTLS = DTLS12;
// Default version for hello messages (SSLv2Hello) // Default version for hello messages (SSLv2Hello)
final static ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30; final static ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30;
@ -108,10 +125,10 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
// Initialize the available protocols. // Initialize the available protocols.
static { static {
Set<ProtocolVersion> protocols = new HashSet<>(5); Set<ProtocolVersion> protocols = new HashSet<>(7);
ProtocolVersion[] pvs = new ProtocolVersion[] { ProtocolVersion[] pvs = new ProtocolVersion[] {
SSL20Hello, SSL30, TLS10, TLS11, TLS12}; SSL20Hello, SSL30, TLS10, TLS11, TLS12, DTLS10, DTLS12};
EnumSet<CryptoPrimitive> cryptoPrimitives = EnumSet<CryptoPrimitive> cryptoPrimitives =
EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT); EnumSet.<CryptoPrimitive>of(CryptoPrimitive.KEY_AGREEMENT);
for (ProtocolVersion p : pvs) { for (ProtocolVersion p : pvs) {
@ -145,6 +162,10 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
return TLS12; return TLS12;
} else if (v == SSL20Hello.v) { } else if (v == SSL20Hello.v) {
return SSL20Hello; return SSL20Hello;
} else if (v == DTLS10.v) {
return DTLS10;
} else if (v == DTLS12.v) {
return DTLS12;
} else { } else {
int major = (v >>> 8) & 0xFF; int major = (v >>> 8) & 0xFF;
int minor = v & 0xFF; int minor = v & 0xFF;
@ -172,8 +193,8 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
} }
if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) { if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) {
throw new IllegalArgumentException throw new IllegalArgumentException(
("Only TLS 1.0 or later allowed in FIPS mode"); "Only TLS 1.0 or later allowed in FIPS mode");
} }
if (name.equals(SSL30.name)) { if (name.equals(SSL30.name)) {
@ -186,6 +207,10 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
return TLS12; return TLS12;
} else if (name.equals(SSL20Hello.name)) { } else if (name.equals(SSL20Hello.name)) {
return SSL20Hello; return SSL20Hello;
} else if (name.equals(DTLS10.name)) {
return DTLS10;
} else if (name.equals(DTLS12.name)) {
return DTLS12;
} else { } else {
throw new IllegalArgumentException(name); throw new IllegalArgumentException(name);
} }
@ -201,6 +226,90 @@ public final class ProtocolVersion implements Comparable<ProtocolVersion> {
*/ */
@Override @Override
public int compareTo(ProtocolVersion protocolVersion) { public int compareTo(ProtocolVersion protocolVersion) {
if (maybeDTLSProtocol()) {
if (!protocolVersion.maybeDTLSProtocol()) {
throw new IllegalArgumentException("Not DTLS protocol");
}
return protocolVersion.v - this.v;
} else {
if (protocolVersion.maybeDTLSProtocol()) {
throw new IllegalArgumentException("Not TLS protocol");
}
return this.v - protocolVersion.v; return this.v - protocolVersion.v;
} }
}
/**
* Returns true if a ProtocolVersion represents a DTLS protocol.
*/
boolean isDTLSProtocol() {
return this.v == DTLS12.v || this.v == DTLS10.v;
}
/**
* Returns true if a ProtocolVersion may represent a DTLS protocol.
*/
boolean maybeDTLSProtocol() {
return (this.major & 0x80) != 0;
}
boolean useTLS12PlusSpec() {
return maybeDTLSProtocol() ? (this.v <= DTLS12.v) : (this.v >= TLS12.v);
}
boolean useTLS11PlusSpec() {
return maybeDTLSProtocol() ? true : (this.v >= TLS11.v);
}
boolean useTLS10PlusSpec() {
return maybeDTLSProtocol() ? true : (this.v >= TLS10.v);
}
boolean obsoletes(CipherSuite suite) {
ProtocolVersion proto = this;
if (proto.isDTLSProtocol()) {
// DTLS bans stream ciphers.
if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
return true;
}
proto = mapToTLSProtocol(this);
}
return (proto.v >= suite.obsoleted);
}
boolean supports(CipherSuite suite) {
ProtocolVersion proto = this;
if (proto.isDTLSProtocol()) {
// DTLS bans stream ciphers.
if (suite.cipher.cipherType == CipherType.STREAM_CIPHER) {
return false;
}
proto = mapToTLSProtocol(this);
}
return (proto.v >= suite.supported);
}
// Map a specified protocol to the corresponding TLS version.
//
// DTLS 1.2 -> TLS 1.2
// DTLS 1.0 -> TLS 1.1
private static ProtocolVersion mapToTLSProtocol(
ProtocolVersion protocolVersion) {
if (protocolVersion.isDTLSProtocol()) {
if (protocolVersion.v == DTLS10.v) {
protocolVersion = TLS11;
} else { // DTLS12
protocolVersion = TLS12;
}
}
return protocolVersion;
}
} }

View file

@ -73,8 +73,8 @@ final class RSAClientKeyExchange extends HandshakeMessage {
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
try { try {
String s = ((protocolVersion.v >= ProtocolVersion.TLS12.v) ? String s = protocolVersion.useTLS12PlusSpec() ?
"SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret"); "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret";
KeyGenerator kg = JsseJce.getKeyGenerator(s); KeyGenerator kg = JsseJce.getKeyGenerator(s);
kg.init(new TlsRsaPremasterSecretParameterSpec( kg.init(new TlsRsaPremasterSecretParameterSpec(
maxVersion.v, protocolVersion.v), generator); maxVersion.v, protocolVersion.v), generator);
@ -103,7 +103,7 @@ final class RSAClientKeyExchange extends HandshakeMessage {
throw new SSLKeyException("Private key not of type RSA"); throw new SSLKeyException("Private key not of type RSA");
} }
if (currentVersion.v >= ProtocolVersion.TLS10.v) { if (currentVersion.useTLS10PlusSpec()) {
encrypted = input.getBytes16(); encrypted = input.getBytes16();
} else { } else {
encrypted = new byte [messageSize]; encrypted = new byte [messageSize];
@ -142,7 +142,7 @@ final class RSAClientKeyExchange extends HandshakeMessage {
@Override @Override
int messageLength() { int messageLength() {
if (protocolVersion.v >= ProtocolVersion.TLS10.v) { if (protocolVersion.useTLS10PlusSpec()) {
return encrypted.length + 2; return encrypted.length + 2;
} else { } else {
return encrypted.length; return encrypted.length;
@ -151,7 +151,7 @@ final class RSAClientKeyExchange extends HandshakeMessage {
@Override @Override
void send(HandshakeOutStream s) throws IOException { void send(HandshakeOutStream s) throws IOException {
if (protocolVersion.v >= ProtocolVersion.TLS10.v) { if (protocolVersion.useTLS10PlusSpec()) {
s.putBytes16(encrypted); s.putBytes16(encrypted);
} else { } else {
s.write(encrypted); s.write(encrypted);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2007, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -70,10 +70,10 @@ final class RandomCookie {
void print(PrintStream s) { void print(PrintStream s) {
int i, gmt_unix_time; int i, gmt_unix_time;
gmt_unix_time = random_bytes[0] << 24; gmt_unix_time = ((random_bytes[0] & 0xFF) << 24) |
gmt_unix_time += random_bytes[1] << 16; ((random_bytes[1] & 0xFF) << 16) |
gmt_unix_time += random_bytes[2] << 8; ((random_bytes[2] & 0xFF) << 8) |
gmt_unix_time += random_bytes[3]; (random_bytes[3] & 0xFF);
s.print("GMT: " + gmt_unix_time + " "); s.print("GMT: " + gmt_unix_time + " ");
s.print("bytes = { "); s.print("bytes = { ");

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2013, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,21 +23,20 @@
* questions. * questions.
*/ */
package sun.security.ssl; package sun.security.ssl;
/** /**
* SSL/TLS records, as pulled off (and put onto) a TCP stream. This is * SSL/TLS/DTLS records, as pulled off (and put onto) a TCP stream. This is
* the base interface, which defines common information and interfaces * the base interface, which defines common information and interfaces
* used by both Input and Output records. * used by both Input and Output records.
* *
* @author David Brownell * @author David Brownell
*/ */
interface Record { interface Record {
/* /*
* There are four SSL record types, which are part of the interface * There are four record types, which are part of the interface
* to this level (along with the maximum record size) * to this level (along with the maximum record size).
* *
* enum { change_cipher_spec(20), alert(21), handshake(22), * enum { change_cipher_spec(20), alert(21), handshake(22),
* application_data(23), (255) } ContentType; * application_data(23), (255) } ContentType;
@ -47,80 +46,48 @@ interface Record {
static final byte ct_handshake = 22; static final byte ct_handshake = 22;
static final byte ct_application_data = 23; static final byte ct_application_data = 23;
static final int headerSize = 5; // SSLv3 record header static final int maxMacSize = 48; // the max supported MAC or
static final int maxExpansion = 1024; // for bad compression // AEAD tag size
static final int trailerSize = 20; // SHA1 hash size
static final int maxDataSize = 16384; // 2^14 bytes of data static final int maxDataSize = 16384; // 2^14 bytes of data
static final int maxPadding = 256; // block cipher padding static final int maxPadding = 256; // block cipher padding
static final int maxIVLength = 256; // IV length static final int maxIVLength = 16; // the max supported IV length
static final int maxFragmentSize = 18432; // the max fragment size
// 2^14 + 2048
/* /*
* The size of the header plus the max IV length * System property to enable/disable CBC protection in SSL3/TLS1.
*/ */
static final int headerPlusMaxIVSize =
headerSize // header
+ maxIVLength; // iv
/*
* SSL has a maximum record size. It's header, (compressed) data,
* padding, and a trailer for the message authentication information (MAC
* for block and stream ciphers, and message authentication tag for AEAD
* ciphers).
*
* Some compression algorithms have rare cases where they expand the data.
* As we don't support compression at this time, leave that out.
*/
static final int maxRecordSize =
headerPlusMaxIVSize // header + iv
+ maxDataSize // data
+ maxPadding // padding
+ trailerSize; // MAC or AEAD tag
static final boolean enableCBCProtection = static final boolean enableCBCProtection =
Debug.getBooleanProperty("jsse.enableCBCProtection", true); Debug.getBooleanProperty("jsse.enableCBCProtection", true);
/*
* 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
+ trailerSize // MAC
);
/*
* The maximum large record size.
*
* Some SSL/TLS implementations support large fragment upto 2^15 bytes,
* such as Microsoft. We support large incoming fragments.
*
* The maximum large record size is defined as maxRecordSize plus 2^14,
* this is the amount OpenSSL is using.
*/
static final int maxLargeRecordSize =
maxRecordSize // Max size with a conforming implementation
+ maxDataSize; // extra 2^14 bytes for large data packets.
/*
* 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
+ trailerSize; // MAC
/* /*
* The overflow values of integers of 8, 16 and 24 bits. * The overflow values of integers of 8, 16 and 24 bits.
*/ */
static final int OVERFLOW_OF_INT08 = (1 << 8); static final int OVERFLOW_OF_INT08 = (1 << 8);
static final int OVERFLOW_OF_INT16 = (1 << 16); static final int OVERFLOW_OF_INT16 = (1 << 16);
static final int OVERFLOW_OF_INT24 = (1 << 24); static final int OVERFLOW_OF_INT24 = (1 << 24);
/**
* Return a description for the given content type.
*/
static String contentName(byte contentType) {
switch (contentType) {
case ct_change_cipher_spec:
return "Change Cipher Spec";
case ct_alert:
return "Alert";
case ct_handshake:
return "Handshake";
case ct_application_data:
return "Application Data";
default:
return "contentType = " + contentType;
}
}
static boolean isValidContentType(byte contentType) {
return (contentType == 20) || (contentType == 21) ||
(contentType == 22) || (contentType == 23);
}
} }

View file

@ -0,0 +1,122 @@
/*
* 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;
/*
* enumation of record type
*/
enum RecordType {
RECORD_CHANGE_CIPHER_SPEC (Record.ct_change_cipher_spec,
HandshakeMessage.ht_not_applicable),
RECORD_ALERT (Record.ct_alert,
HandshakeMessage.ht_not_applicable),
RECORD_HELLO_REQUEST (Record.ct_handshake,
HandshakeMessage.ht_hello_request),
RECORD_CLIENT_HELLO (Record.ct_handshake,
HandshakeMessage.ht_client_hello),
RECORD_SERVER_HELLO (Record.ct_handshake,
HandshakeMessage.ht_server_hello),
RECORD_HELLO_VERIFY_REQUEST (Record.ct_handshake,
HandshakeMessage.ht_hello_verify_request),
RECORD_NEW_SESSION_TICKET (Record.ct_handshake,
HandshakeMessage.ht_new_session_ticket),
RECORD_CERTIFICATE (Record.ct_handshake,
HandshakeMessage.ht_certificate),
RECORD_SERVER_KEY_EXCHANGE (Record.ct_handshake,
HandshakeMessage.ht_server_key_exchange),
RECORD_CERTIFICATE_REQUEST (Record.ct_handshake,
HandshakeMessage.ht_certificate_request),
RECORD_SERVER_HELLO_DONE (Record.ct_handshake,
HandshakeMessage.ht_server_hello_done),
RECORD_CERTIFICATE_VERIFY (Record.ct_handshake,
HandshakeMessage.ht_certificate_verify),
RECORD_CLIENT_KEY_EXCHANGE (Record.ct_handshake,
HandshakeMessage.ht_client_key_exchange),
RECORD_FINISHED (Record.ct_handshake,
HandshakeMessage.ht_finished),
RECORD_CERTIFICATE_URL (Record.ct_handshake,
HandshakeMessage.ht_certificate_url),
RECORD_CERTIFICATE_STATUS (Record.ct_handshake,
HandshakeMessage.ht_certificate_status),
RECORD_SUPPLIEMENTAL_DATA (Record.ct_handshake,
HandshakeMessage.ht_supplemental_data),
RECORD_APPLICATION_DATA (Record.ct_application_data,
HandshakeMessage.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 == HandshakeMessage.ht_hello_request) {
return RECORD_HELLO_REQUEST;
} else if (handshakeType == HandshakeMessage.ht_client_hello) {
return RECORD_CLIENT_HELLO;
} else if (handshakeType == HandshakeMessage.ht_server_hello) {
return RECORD_SERVER_HELLO;
} else if (handshakeType == HandshakeMessage.ht_hello_verify_request) {
return RECORD_HELLO_VERIFY_REQUEST;
} else if (handshakeType == HandshakeMessage.ht_new_session_ticket) {
return RECORD_NEW_SESSION_TICKET;
} else if (handshakeType == HandshakeMessage.ht_certificate) {
return RECORD_CERTIFICATE;
} else if (handshakeType == HandshakeMessage.ht_server_key_exchange) {
return RECORD_SERVER_KEY_EXCHANGE;
} else if (handshakeType == HandshakeMessage.ht_certificate_request) {
return RECORD_CERTIFICATE_REQUEST;
} else if (handshakeType == HandshakeMessage.ht_server_hello_done) {
return RECORD_SERVER_HELLO_DONE;
} else if (handshakeType == HandshakeMessage.ht_certificate_verify) {
return RECORD_CERTIFICATE_VERIFY;
} else if (handshakeType == HandshakeMessage.ht_client_key_exchange) {
return RECORD_CLIENT_KEY_EXCHANGE;
} else if (handshakeType == HandshakeMessage.ht_finished) {
return RECORD_FINISHED;
} else if (handshakeType == HandshakeMessage.ht_certificate_url) {
return RECORD_CERTIFICATE_URL;
} else if (handshakeType == HandshakeMessage.ht_certificate_status) {
return RECORD_CERTIFICATE_STATUS;
} else if (handshakeType == HandshakeMessage.ht_supplemental_data) {
return RECORD_SUPPLIEMENTAL_DATA;
}
// otherwise, invalid record type
throw new IllegalArgumentException(
"Invalid record type (ContentType:" + contentType +
", HandshakeType:" + handshakeType + ")");
}
}

View file

@ -62,6 +62,9 @@ public abstract class SSLContextImpl extends SSLContextSpi {
private CipherSuiteList defaultClientCipherSuiteList; private CipherSuiteList defaultClientCipherSuiteList;
private CipherSuiteList supportedCipherSuiteList; private CipherSuiteList supportedCipherSuiteList;
// DTLS cookie exchange manager
private HelloCookieManager helloCookieManager;
SSLContextImpl() { SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager(); ephemeralKeyManager = new EphemeralKeyManager();
clientCache = new SSLSessionContextImpl(); clientCache = new SSLSessionContextImpl();
@ -175,11 +178,29 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return DummyX509KeyManager.INSTANCE; return DummyX509KeyManager.INSTANCE;
} }
abstract SSLEngine createSSLEngineImpl();
abstract SSLEngine createSSLEngineImpl(String host, int port);
@Override
protected SSLEngine engineCreateSSLEngine() {
if (!isInitialized) {
throw new IllegalStateException("SSLContext is not initialized");
}
return createSSLEngineImpl();
}
@Override
protected SSLEngine engineCreateSSLEngine(String host, int port) {
if (!isInitialized) {
throw new IllegalStateException("SSLContext is not initialized");
}
return createSSLEngineImpl(host, port);
}
@Override @Override
protected SSLSocketFactory engineGetSocketFactory() { protected SSLSocketFactory engineGetSocketFactory() {
if (!isInitialized) { if (!isInitialized) {
throw new IllegalStateException( throw new IllegalStateException("SSLContext is not initialized");
"SSLContextImpl is not initialized");
} }
return new SSLSocketFactoryImpl(this); return new SSLSocketFactoryImpl(this);
} }
@ -192,24 +213,6 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return new SSLServerSocketFactoryImpl(this); return new SSLServerSocketFactoryImpl(this);
} }
@Override
protected SSLEngine engineCreateSSLEngine() {
if (!isInitialized) {
throw new IllegalStateException(
"SSLContextImpl is not initialized");
}
return new SSLEngineImpl(this);
}
@Override
protected SSLEngine engineCreateSSLEngine(String host, int port) {
if (!isInitialized) {
throw new IllegalStateException(
"SSLContextImpl is not initialized");
}
return new SSLEngineImpl(this, host, port);
}
@Override @Override
protected SSLSessionContext engineGetClientSessionContext() { protected SSLSessionContext engineGetClientSessionContext() {
return clientCache; return clientCache;
@ -236,6 +239,23 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return ephemeralKeyManager; return ephemeralKeyManager;
} }
HelloCookieManager getHelloCookieManager() {
if (!isInitialized) {
throw new IllegalStateException("SSLContext is not initialized");
}
if (helloCookieManager == null) {
helloCookieManager = getHelloCookieManager(secureRandom);
}
return helloCookieManager;
}
HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
throw new UnsupportedOperationException(
"Cookie exchange applies to DTLS only");
}
abstract SSLParameters getDefaultServerSSLParams(); abstract SSLParameters getDefaultServerSSLParams();
abstract SSLParameters getDefaultClientSSLParams(); abstract SSLParameters getDefaultClientSSLParams();
abstract SSLParameters getSupportedSSLParams(); abstract SSLParameters getSupportedSSLParams();
@ -319,12 +339,20 @@ public abstract class SSLContextImpl extends SSLContextSpi {
(protocols == defaultClientProtocolList); (protocols == defaultClientProtocolList);
} }
/**
* Return whether a protocol list is the original default enabled
* protocols. See: SSLSocket/SSLEngine.setEnabledProtocols()
*/
boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
return (cipherSuites == defaultClientCipherSuiteList) ||
(cipherSuites == defaultServerCipherSuiteList);
}
/* /*
* Return the list of all available CipherSuites with a priority of * Return the list of all available CipherSuites with a priority of
* minPriority or above. * minPriority or above.
*/ */
private CipherSuiteList getApplicableCipherSuiteList( private static CipherSuiteList getApplicableCipherSuiteList(
ProtocolList protocols, boolean onlyEnabled) { ProtocolList protocols, boolean onlyEnabled) {
int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY; int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY;
@ -344,8 +372,8 @@ public abstract class SSLContextImpl extends SSLContextSpi {
} }
if (suite.isAvailable() && if (suite.isAvailable() &&
suite.obsoleted > protocols.min.v && !protocols.min.obsoletes(suite) &&
suite.supported <= protocols.max.v) { protocols.max.supports(suite)) {
if (SSLAlgorithmConstraints.DEFAULT.permits( if (SSLAlgorithmConstraints.DEFAULT.permits(
EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
suite.name, null)) { suite.name, null)) {
@ -353,10 +381,10 @@ public abstract class SSLContextImpl extends SSLContextSpi {
} }
} else if (debug != null && } else if (debug != null &&
Debug.isOn("sslctx") && Debug.isOn("verbose")) { Debug.isOn("sslctx") && Debug.isOn("verbose")) {
if (suite.obsoleted <= protocols.min.v) { if (protocols.min.obsoletes(suite)) {
System.out.println( System.out.println(
"Ignoring obsoleted cipher suite: " + suite); "Ignoring obsoleted cipher suite: " + suite);
} else if (suite.supported > protocols.max.v) { } else if (!protocols.max.supports(suite)) {
System.out.println( System.out.println(
"Ignoring unsupported cipher suite: " + suite); "Ignoring unsupported cipher suite: " + suite);
} else { } else {
@ -388,8 +416,25 @@ public abstract class SSLContextImpl extends SSLContextSpi {
} }
} }
private static String[] getAvailableProtocols(
ProtocolVersion[] protocolCandidates) {
List<String> availableProtocols = Collections.<String>emptyList();
if (protocolCandidates != null && protocolCandidates.length != 0) {
availableProtocols = new ArrayList<>(protocolCandidates.length);
for (ProtocolVersion p : protocolCandidates) {
if (ProtocolVersion.availableProtocols.contains(p)) {
availableProtocols.add(p.name);
}
}
}
return availableProtocols.toArray(new String[0]);
}
/* /*
* The SSLContext implementation for TLS/SSL algorithm * The SSLContext implementation for SSL/(D)TLS algorithm
* *
* SSL/TLS protocols specify the forward compatibility and version * SSL/TLS protocols specify the forward compatibility and version
* roll-back attack protections, however, a number of SSL/TLS server * roll-back attack protections, however, a number of SSL/TLS server
@ -418,14 +463,15 @@ public abstract class SSLContextImpl extends SSLContextSpi {
*/ */
/* /*
* The base abstract SSLContext implementation. * The base abstract SSLContext implementation for the Transport Layer
* Security (TLS) protocols.
* *
* This abstract class encapsulates supported and the default server * This abstract class encapsulates supported and the default server
* SSL parameters. * SSL/TLS parameters.
* *
* @see SSLContext * @see SSLContext
*/ */
private abstract static class AbstractSSLContext extends SSLContextImpl { private abstract static class AbstractTLSContext extends SSLContextImpl {
// parameters // parameters
private static final SSLParameters defaultServerSSLParams; private static final SSLParameters defaultServerSSLParams;
private static final SSLParameters supportedSSLParams; private static final SSLParameters supportedSSLParams;
@ -482,20 +528,14 @@ public abstract class SSLContextImpl extends SSLContextSpi {
return supportedSSLParams; return supportedSSLParams;
} }
static String[] getAvailableProtocols( @Override
ProtocolVersion[] protocolCandidates) { SSLEngine createSSLEngineImpl() {
return new SSLEngineImpl(this, false);
List<String> availableProtocols = Collections.<String>emptyList();
if (protocolCandidates != null && protocolCandidates.length != 0) {
availableProtocols = new ArrayList<>(protocolCandidates.length);
for (ProtocolVersion p : protocolCandidates) {
if (ProtocolVersion.availableProtocols.contains(p)) {
availableProtocols.add(p.name);
}
}
} }
return availableProtocols.toArray(new String[0]); @Override
SSLEngine createSSLEngineImpl(String host, int port) {
return new SSLEngineImpl(this, host, port, false);
} }
} }
@ -504,7 +544,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
* *
* @see SSLContext * @see SSLContext
*/ */
public static final class TLS10Context extends AbstractSSLContext { public static final class TLS10Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams; private static final SSLParameters defaultClientSSLParams;
static { static {
@ -537,7 +577,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
* *
* @see SSLContext * @see SSLContext
*/ */
public static final class TLS11Context extends AbstractSSLContext { public static final class TLS11Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams; private static final SSLParameters defaultClientSSLParams;
static { static {
@ -572,7 +612,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
* *
* @see SSLContext * @see SSLContext
*/ */
public static final class TLS12Context extends AbstractSSLContext { public static final class TLS12Context extends AbstractTLSContext {
private static final SSLParameters defaultClientSSLParams; private static final SSLParameters defaultClientSSLParams;
static { static {
@ -604,13 +644,74 @@ public abstract class SSLContextImpl extends SSLContextSpi {
} }
} }
/*
* The interface for the customized SSL/(D)TLS SSLContext.
*
* @see SSLContext
*/
private static class CustomizedSSLProtocols {
private final static String PROPERTY_NAME = "jdk.tls.client.protocols";
static IllegalArgumentException reservedException = null;
static ArrayList<ProtocolVersion>
customizedProtocols = new ArrayList<>();
// Don't want a java.lang.LinkageError for illegal system property.
//
// Please don't throw exception in this static block. Otherwise,
// java.lang.LinkageError may be thrown during the instantiation of
// the provider service. Instead, please handle the initialization
// exception in the caller's constructor.
static {
String property = AccessController.doPrivileged(
new GetPropertyAction(PROPERTY_NAME));
if (property != null && property.length() != 0) {
// remove double quote marks from beginning/end of the property
if (property.length() > 1 && property.charAt(0) == '"' &&
property.charAt(property.length() - 1) == '"') {
property = property.substring(1, property.length() - 1);
}
}
if (property != null && property.length() != 0) {
String[] protocols = property.split(",");
for (int i = 0; i < protocols.length; i++) {
protocols[i] = protocols[i].trim();
// Is it a supported protocol name?
try {
ProtocolVersion pro =
ProtocolVersion.valueOf(protocols[i]);
if (SunJSSE.isFIPS() &&
((pro.v == ProtocolVersion.SSL30.v) ||
(pro.v == ProtocolVersion.SSL20Hello.v))) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + pro +
" is not FIPS compliant");
break;
}
// ignore duplicated protocols
if (!customizedProtocols.contains(pro)) {
customizedProtocols.add(pro);
}
} catch (IllegalArgumentException iae) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + protocols[i] +
" is not a standard SSL protocol name", iae);
}
}
}
}
}
/* /*
* The SSLContext implementation for customized TLS protocols * The SSLContext implementation for customized TLS protocols
* *
* @see SSLContext * @see SSLContext
*/ */
private static class CustomizedSSLContext extends AbstractSSLContext { private static class CustomizedTLSContext extends AbstractTLSContext {
private static final String PROPERTY_NAME = "jdk.tls.client.protocols";
private static final SSLParameters defaultClientSSLParams; private static final SSLParameters defaultClientSSLParams;
private static IllegalArgumentException reservedException = null; private static IllegalArgumentException reservedException = null;
@ -621,13 +722,22 @@ public abstract class SSLContextImpl extends SSLContextSpi {
// the provider service. Instead, let's handle the initialization // the provider service. Instead, let's handle the initialization
// exception in constructor. // exception in constructor.
static { static {
reservedException = CustomizedSSLProtocols.reservedException;
if (reservedException == null) {
ArrayList<ProtocolVersion>
customizedTLSProtocols = new ArrayList<>();
for (ProtocolVersion protocol :
CustomizedSSLProtocols.customizedProtocols) {
if (!protocol.isDTLSProtocol()) {
customizedTLSProtocols.add(protocol);
}
}
// candidates for available protocols // candidates for available protocols
ProtocolVersion[] candidates; ProtocolVersion[] candidates;
if (customizedTLSProtocols.isEmpty()) {
String property = AccessController.doPrivileged( // Use the default enabled client protocols if no
new GetPropertyAction(PROPERTY_NAME)); // customized TLS protocols.
if (property == null || property.length() == 0) {
// the default enabled client TLS protocols
if (SunJSSE.isFIPS()) { if (SunJSSE.isFIPS()) {
candidates = new ProtocolVersion[] { candidates = new ProtocolVersion[] {
ProtocolVersion.TLS10, ProtocolVersion.TLS10,
@ -643,56 +753,21 @@ public abstract class SSLContextImpl extends SSLContextSpi {
}; };
} }
} else { } else {
// remove double quote marks from beginning/end of the property // Use the customized TLS protocols.
if (property.length() > 1 && property.charAt(0) == '"' && candidates =
property.charAt(property.length() - 1) == '"') { new ProtocolVersion[customizedTLSProtocols.size()];
property = property.substring(1, property.length() - 1); candidates = customizedTLSProtocols.toArray(candidates);
}
String[] protocols = null;
if (property != null && property.length() != 0) {
protocols = property.split(",");
} else {
reservedException = new IllegalArgumentException(
"No protocol specified in " +
PROPERTY_NAME + " system property");
protocols = new String[0];
}
candidates = new ProtocolVersion[protocols.length];
for (int i = 0; i < protocols.length; i++) {
protocols[i] = protocols[i].trim();
// Is it a supported protocol name?
try {
candidates[i] = ProtocolVersion.valueOf(protocols[i]);
} catch (IllegalArgumentException iae) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + protocols[i] +
" is not a standard SSL/TLS protocol name", iae);
break;
}
}
if ((reservedException == null) && SunJSSE.isFIPS()) {
for (ProtocolVersion protocolVersion : candidates) {
if (ProtocolVersion.SSL20Hello.v == protocolVersion.v ||
ProtocolVersion.SSL30.v == protocolVersion.v) {
reservedException = new IllegalArgumentException(
PROPERTY_NAME + ": " + protocolVersion +
" is not FIPS compliant");
}
}
}
} }
defaultClientSSLParams = new SSLParameters(); defaultClientSSLParams = new SSLParameters();
if (reservedException == null) {
defaultClientSSLParams.setProtocols( defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates)); getAvailableProtocols(candidates));
} else {
defaultClientSSLParams = null; // unlikely to be used
} }
} }
protected CustomizedSSLContext() { protected CustomizedTLSContext() {
if (reservedException != null) { if (reservedException != null) {
throw reservedException; throw reservedException;
} }
@ -709,7 +784,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
* *
* @see SSLContext * @see SSLContext
*/ */
public static final class TLSContext extends CustomizedSSLContext { public static final class TLSContext extends CustomizedTLSContext {
// use the default constructor and methods // use the default constructor and methods
} }
@ -718,7 +793,7 @@ public abstract class SSLContextImpl extends SSLContextSpi {
* *
* @see SSLContext * @see SSLContext
*/ */
public static final class DefaultSSLContext extends CustomizedSSLContext { public static final class DefaultSSLContext extends CustomizedTLSContext {
private static final String NONE = "NONE"; private static final String NONE = "NONE";
private static final String P11KEYSTORE = "PKCS11"; private static final String P11KEYSTORE = "PKCS11";
@ -879,6 +954,193 @@ public abstract class SSLContextImpl extends SSLContextSpi {
} }
} }
/*
* The base abstract SSLContext implementation for the Datagram Transport
* Layer Security (DTLS) protocols.
*
* This abstract class encapsulates supported and the default server DTLS
* parameters.
*
* @see SSLContext
*/
private abstract static class AbstractDTLSContext extends SSLContextImpl {
// parameters
private static final SSLParameters defaultServerSSLParams;
private static final SSLParameters supportedSSLParams;
static {
// supported SSL parameters
supportedSSLParams = new SSLParameters();
// Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
supportedSSLParams.setProtocols(new String[] {
ProtocolVersion.DTLS10.name,
ProtocolVersion.DTLS12.name
});
// candidates for available protocols
ProtocolVersion[] candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
};
defaultServerSSLParams = new SSLParameters();
defaultServerSSLParams.setProtocols(
getAvailableProtocols(candidates));
}
@Override
SSLParameters getDefaultServerSSLParams() {
return defaultServerSSLParams;
}
@Override
SSLParameters getSupportedSSLParams() {
return supportedSSLParams;
}
@Override
SSLEngine createSSLEngineImpl() {
return new SSLEngineImpl(this, true);
}
@Override
SSLEngine createSSLEngineImpl(String host, int port) {
return new SSLEngineImpl(this, host, port, true);
}
@Override
HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) {
return new HelloCookieManager(secureRandom);
}
}
/*
* The SSLContext implementation for DTLSv1.0 algorithm.
*
* @see SSLContext
*/
public static final class DTLS10Context extends AbstractDTLSContext {
private final static SSLParameters defaultClientSSLParams;
static {
// candidates for available protocols
ProtocolVersion[] candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10
};
defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
}
@Override
SSLParameters getDefaultClientSSLParams() {
return defaultClientSSLParams;
}
}
/*
* The SSLContext implementation for DTLSv1.2 algorithm.
*
* @see SSLContext
*/
public static final class DTLS12Context extends AbstractDTLSContext {
private final static SSLParameters defaultClientSSLParams;
static {
// candidates for available protocols
ProtocolVersion[] candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
};
defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
}
@Override
SSLParameters getDefaultClientSSLParams() {
return defaultClientSSLParams;
}
}
/*
* The SSLContext implementation for customized TLS protocols
*
* @see SSLContext
*/
private static class CustomizedDTLSContext extends AbstractDTLSContext {
private final static SSLParameters defaultClientSSLParams;
private static IllegalArgumentException reservedException = null;
// Don't want a java.lang.LinkageError for illegal system property.
//
// Please don't throw exception in this static block. Otherwise,
// java.lang.LinkageError may be thrown during the instantiation of
// the provider service. Instead, let's handle the initialization
// exception in constructor.
static {
reservedException = CustomizedSSLProtocols.reservedException;
if (reservedException == null) {
ArrayList<ProtocolVersion>
customizedDTLSProtocols = new ArrayList<>();
for (ProtocolVersion protocol :
CustomizedSSLProtocols.customizedProtocols) {
if (protocol.isDTLSProtocol()) {
customizedDTLSProtocols.add(protocol);
}
}
// candidates for available protocols
ProtocolVersion[] candidates;
if (customizedDTLSProtocols.isEmpty()) {
// Use the default enabled client protocols if no
// customized TLS protocols.
//
// Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
candidates = new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
};
} else {
// Use the customized TLS protocols.
candidates =
new ProtocolVersion[customizedDTLSProtocols.size()];
candidates = customizedDTLSProtocols.toArray(candidates);
}
defaultClientSSLParams = new SSLParameters();
defaultClientSSLParams.setProtocols(
getAvailableProtocols(candidates));
} else {
defaultClientSSLParams = null; // unlikely to be used
}
}
protected CustomizedDTLSContext() {
if (reservedException != null) {
throw reservedException;
}
}
@Override
SSLParameters getDefaultClientSSLParams() {
return defaultClientSSLParams;
}
}
/*
* The SSLContext implementation for default "DTLS" algorithm
*
* @see SSLContext
*/
public static final class DTLSContext extends CustomizedDTLSContext {
// use the default constructor and methods
}
} }
@ -961,7 +1223,7 @@ final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
AlgorithmConstraints constraints = null; AlgorithmConstraints constraints = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) { if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession = ExtendedSSLSession extSession =
(ExtendedSSLSession)session; (ExtendedSSLSession)session;
@ -1003,7 +1265,7 @@ final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
AlgorithmConstraints constraints = null; AlgorithmConstraints constraints = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) { if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession = ExtendedSSLSession extSession =
(ExtendedSSLSession)session; (ExtendedSSLSession)session;

View file

@ -0,0 +1,409 @@
/*
* 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 javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import sun.misc.HexDumpEncoder;
/**
* {@code InputRecord} implementation for {@code SSLEngine}.
*/
final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
// used by handshake hash computation for handshake fragment
private byte prevType = -1;
private int hsMsgOff = 0;
private int hsMsgLen = 0;
private boolean formatVerified = false; // SSLv2 ruled out?
SSLEngineInputRecord() {
this.readAuthenticator = MAC.TLS_NULL;
}
@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
int bytesInCompletePacket(ByteBuffer packet) throws SSLException {
/*
* SSLv2 length field is in bytes 0/1
* SSLv3/TLS length field is in bytes 3/4
*/
if (packet.remaining() < 5) {
return -1;
}
int pos = packet.position();
byte byteZero = packet.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(
packet.get(pos + 1), packet.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 = ((packet.get(pos + 3) & 0xFF) << 8) +
(packet.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 &&
((packet.get(pos + 2) == 1) || packet.get(pos + 2) == 4)) {
ProtocolVersion recordVersion = ProtocolVersion.valueOf(
packet.get(pos + 3), packet.get(pos + 4));
// check the record version
checkRecordVersion(recordVersion, true);
/*
* Client or Server Hello
*/
int mask = (isShort ? 0x7F : 0x3F);
len = ((byteZero & mask) << 8) +
(packet.get(pos + 1) & 0xFF) + (isShort ? 2 : 3);
} else {
// Gobblygook!
throw new SSLException(
"Unrecognized SSL message, plaintext connection?");
}
}
return len;
}
@Override
void checkRecordVersion(ProtocolVersion recordVersion,
boolean allowSSL20Hello) throws SSLException {
if (recordVersion.maybeDTLSProtocol()) {
throw new SSLException(
"Unrecognized record version " + recordVersion +
" , DTLS packet?");
}
// Check if the record version is too old.
if ((recordVersion.v < ProtocolVersion.MIN.v)) {
// if it's not SSLv2, we're out of here.
if (!allowSSL20Hello ||
(recordVersion.v != ProtocolVersion.SSL20Hello.v)) {
throw new SSLException(
"Unsupported record version " + recordVersion);
}
}
}
@Override
Plaintext decode(ByteBuffer packet)
throws IOException, BadPaddingException {
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.
if (!formatVerified) {
formatVerified = true;
/*
* The first record must either be a handshake record or an
* alert message. If it's not, it is either invalid or an
* SSLv2 message.
*/
int pos = packet.position();
byte byteZero = packet.get(pos);
if (byteZero != ct_handshake && byteZero != ct_alert) {
return handleUnknownRecord(packet);
}
}
return decodeInputRecord(packet);
}
private Plaintext decodeInputRecord(ByteBuffer packet)
throws IOException, BadPaddingException {
//
// The packet should be a complete record, or more.
//
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
int contentLen = ((packet.get() & 0xFF) << 8) +
(packet.get() & 0xFF); // pos: 3, 4
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", READ: " +
ProtocolVersion.valueOf(majorVersion, minorVersion) +
" " + Record.contentName(contentType) + ", length = " +
contentLen);
}
//
// Check for upper bound.
//
// Note: May check packetSize limit in the future.
if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
throw new SSLProtocolException(
"Bad input record size, TLSCiphertext.length = " + contentLen);
}
//
// check for handshake fragment
//
if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
throw new SSLProtocolException(
"Expected to get a handshake fragment");
}
//
// Decrypt the fragment
//
int recLim = srcPos + SSLRecord.headerSize + contentLen;
packet.limit(recLim);
packet.position(srcPos + SSLRecord.headerSize);
ByteBuffer plaintext;
try {
plaintext =
decrypt(readAuthenticator, readCipher, contentType, packet);
} finally {
// comsume a complete record
packet.limit(srcLim);
packet.position(recLim);
}
//
// handshake hashing
//
if (contentType == ct_handshake) {
int pltPos = plaintext.position();
int pltLim = plaintext.limit();
int frgPos = pltPos;
for (int remains = plaintext.remaining(); remains > 0;) {
int howmuch;
byte handshakeType;
if (hsMsgOff < hsMsgLen) {
// a fragment of the handshake message
howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
handshakeType = prevType;
hsMsgOff += howmuch;
if (hsMsgOff == hsMsgLen) {
// Now is a complete handshake message.
hsMsgOff = 0;
hsMsgLen = 0;
}
} else { // hsMsgOff == hsMsgLen, a new handshake message
handshakeType = plaintext.get();
int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
((plaintext.get() & 0xFF) << 8) |
(plaintext.get() & 0xFF);
plaintext.position(frgPos);
if (remains < (handshakeLen + 1)) { // 1: handshake type
// This handshake message is fragmented.
prevType = handshakeType;
hsMsgOff = remains - 4; // 4: handshake header
hsMsgLen = handshakeLen;
}
howmuch = Math.min(handshakeLen + 4, remains);
}
plaintext.limit(frgPos + howmuch);
if (handshakeType == HandshakeMessage.ht_hello_request) {
// omitted from handshake hash computation
} else if ((handshakeType != HandshakeMessage.ht_finished) &&
(handshakeType != HandshakeMessage.ht_certificate_verify)) {
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(plaintext);
} else {
// Reserve until this handshake message has been processed.
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.reserve(plaintext);
}
plaintext.position(frgPos + howmuch);
plaintext.limit(pltLim);
frgPos += howmuch;
remains -= howmuch;
}
plaintext.position(pltPos);
}
return new Plaintext(contentType,
majorVersion, minorVersion, -1, -1L, plaintext);
// recordEpoch, recordSeq, plaintext);
}
private Plaintext handleUnknownRecord(ByteBuffer packet)
throws IOException, BadPaddingException {
//
// The packet should be a complete record.
//
int srcPos = packet.position();
int srcLim = packet.limit();
byte firstByte = packet.get(srcPos);
byte thirdByte = packet.get(srcPos + 2);
// Does it look like a Version 2 client hello (V2ClientHello)?
if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
/*
* If SSLv2Hello is not enabled, throw an exception.
*/
if (helloVersion != ProtocolVersion.SSL20Hello) {
throw new SSLHandshakeException("SSLv2Hello is not enabled");
}
byte majorVersion = packet.get(srcPos + 3);
byte minorVersion = packet.get(srcPos + 4);
if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
(minorVersion == ProtocolVersion.SSL20Hello.minor)) {
/*
* Looks like a V2 client hello, but not one saying
* "let's talk SSLv3". So we need to send an SSLv2
* error message, one that's treated as fatal by
* clients (Otherwise we'll hang.)
*/
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
"Requested to negotiate unsupported SSLv2!");
}
// hack code, the exception is caught in SSLEngineImpl
// so that SSLv2 error message can be delivered properly.
throw new UnsupportedOperationException( // SSLv2Hello
"Unsupported SSL v2.0 ClientHello");
}
/*
* If we can map this into a V3 ClientHello, read and
* hash the rest of the V2 handshake, turn it into a
* V3 ClientHello message, and pass it up.
*/
packet.position(srcPos + 2); // exclude the header
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(packet);
packet.position(srcPos);
ByteBuffer converted = convertToClientHello(packet);
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Converted] ClientHello", converted);
}
return new Plaintext(ct_handshake,
majorVersion, minorVersion, -1, -1L, converted);
} else {
if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
throw new SSLException("SSL V2.0 servers are not supported.");
}
throw new SSLException("Unsupported or unrecognized SSL message");
}
}
}

View file

@ -0,0 +1,570 @@
/*
* 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;
import java.io.*;
import java.nio.*;
import java.util.*;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import sun.misc.HexDumpEncoder;
import static sun.security.ssl.Ciphertext.RecordType;
/**
* {@code OutputRecord} implementation for {@code SSLEngine}.
*/
final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
private HandshakeFragment fragmenter = null;
private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
private boolean isTalkingToV2 = false; // SSLv2Hello
private ByteBuffer v2ClientHello = null; // SSLv2Hello
private boolean isCloseWaiting = false;
SSLEngineOutputRecord() {
this.writeAuthenticator = MAC.TLS_NULL;
this.packetSize = SSLRecord.maxRecordSize;
this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
}
@Override
synchronized public void close() throws IOException {
if (!isClosed) {
if (alertMemos != null && !alertMemos.isEmpty()) {
isCloseWaiting = true;
} else {
super.close();
}
}
}
@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.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[2];
memo.fragment[0] = level;
memo.fragment[1] = description;
alertMemos.add(memo);
}
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
if (fragmenter == null) {
fragmenter = new HandshakeFragment();
}
if (firstMessage) {
firstMessage = false;
if ((helloVersion == ProtocolVersion.SSL20Hello) &&
(source[offset] == HandshakeMessage.ht_client_hello) &&
// 5: recode header size
(source[offset + 4 + 2 + 32] == 0)) {
// V3 session ID is empty
// 4: handshake header size
// 2: client_version in ClientHello
// 32: random in ClientHello
// Double space should be big enough for the converted message.
v2ClientHello = encodeV2ClientHello(
source, (offset + 4), (length - 4));
v2ClientHello.position(2); // exclude the header
handshakeHash.update(v2ClientHello);
v2ClientHello.position(0);
return;
}
}
byte handshakeType = source[offset];
if (handshakeType != HandshakeMessage.ht_hello_request) {
handshakeHash.update(source, offset, length);
}
fragmenter.queueUpFragment(source, offset, length);
}
@Override
void encodeChangeCipherSpec() throws IOException {
if (fragmenter == null) {
fragmenter = new HandshakeFragment();
}
fragmenter.queueUpChangeCipherSpec();
}
@Override
void encodeV2NoCipher() throws IOException {
isTalkingToV2 = true;
}
@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");
}
int macLen = 0;
if (writeAuthenticator instanceof MAC) {
macLen = ((MAC)writeAuthenticator).MAClen();
}
int dstLim = destination.limit();
boolean isFirstRecordOfThePayload = true;
int packetLeftSize = Math.min(maxRecordSize, packetSize);
boolean needMorePayload = true;
long recordSN = 0L;
while (needMorePayload) {
int fragLen;
if (isFirstRecordOfThePayload && needToSplitPayload()) {
needMorePayload = true;
fragLen = 1;
isFirstRecordOfThePayload = false;
} else {
needMorePayload = false;
if (packetLeftSize > 0) {
fragLen = writeCipher.calculateFragmentSize(
packetLeftSize, 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 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;
if (remains > 0) {
offset++;
length--;
}
}
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.
recordSN = encrypt(writeAuthenticator, writeCipher,
Record.ct_application_data, destination,
dstPos, dstLim, headerSize,
protocolVersion, false);
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);
}
packetLeftSize -= destination.position() - dstPos;
// remain the limit unchanged
destination.limit(dstLim);
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
}
return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
}
@Override
Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
if (isTalkingToV2) { // SSLv2Hello
// We don't support SSLv2. Send an SSLv2 error message
// so that the connection can be closed gracefully.
//
// Please don't change the limit of the destination buffer.
destination.put(SSLRecord.v2NoCipher);
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + SSLRecord.v2NoCipher.length,
SSLRecord.v2NoCipher);
}
isTalkingToV2 = false;
return new Ciphertext(RecordType.RECORD_ALERT, -1L);
}
if (v2ClientHello != null) {
// deliver the SSLv2 format ClientHello message
//
// Please don't change the limit of the destination buffer.
if (debug != null) {
if (Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: SSLv2 ClientHello message" +
", length = " + v2ClientHello.remaining());
}
if (Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + v2ClientHello.remaining(),
v2ClientHello);
}
}
destination.put(v2ClientHello);
v2ClientHello = null;
return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L);
}
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), false);
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);
if (isCloseWaiting && (memo.contentType == Record.ct_alert)) {
isCloseWaiting = true;
close();
}
return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
}
if (fragmenter != null) {
return fragmenter.acquireCiphertext(destination);
}
return null;
}
@Override
boolean isEmpty() {
return (!isTalkingToV2) && (v2ClientHello == null) &&
((fragmenter == null) || fragmenter.isEmpty()) &&
((alertMemos == null) || alertMemos.isEmpty());
}
// buffered record fragment
private static class RecordMemo {
byte contentType;
byte majorVersion;
byte minorVersion;
CipherBox encodeCipher;
Authenticator encodeAuthenticator;
byte[] fragment;
}
private static class HandshakeMemo extends RecordMemo {
byte handshakeType;
int acquireOffset;
}
final class HandshakeFragment {
private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
void queueUpFragment(byte[] source,
int offset, int length) throws IOException {
HandshakeMemo memo = new HandshakeMemo();
memo.contentType = Record.ct_handshake;
memo.majorVersion = protocolVersion.major; // kick start version?
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.handshakeType = source[offset];
memo.acquireOffset = 0;
memo.fragment = new byte[length - 4]; // 4: header size
// 1: HandshakeType
// 3: message length
System.arraycopy(source, offset + 4, memo.fragment, 0, length - 4);
handshakeMemos.add(memo);
}
void queueUpChangeCipherSpec() {
RecordMemo memo = new RecordMemo();
memo.contentType = Record.ct_change_cipher_spec;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
handshakeMemos.add(memo);
}
Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
if (isEmpty()) {
return null;
}
RecordMemo memo = handshakeMemos.getFirst();
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, headerSize);
} 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) {
int remainingFragLen = fragLen;
while ((remainingFragLen > 0) && !handshakeMemos.isEmpty()) {
int memoFragLen = hsMemo.fragment.length;
if (hsMemo.acquireOffset == 0) {
// Don't fragment handshake message header
if (remainingFragLen <= 4) {
break;
}
dstBuf.put(hsMemo.handshakeType);
dstBuf.put((byte)((memoFragLen >> 16) & 0xFF));
dstBuf.put((byte)((memoFragLen >> 8) & 0xFF));
dstBuf.put((byte)(memoFragLen & 0xFF));
remainingFragLen -= 4;
} // Otherwise, handshake message is fragmented.
int chipLen = Math.min(remainingFragLen,
(memoFragLen - hsMemo.acquireOffset));
dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, chipLen);
hsMemo.acquireOffset += chipLen;
if (hsMemo.acquireOffset == memoFragLen) {
handshakeMemos.removeFirst();
// still have space for more records?
if ((remainingFragLen > chipLen) &&
!handshakeMemos.isEmpty()) {
// look for the next buffered record fragment
RecordMemo reMemo = handshakeMemos.getFirst();
if (reMemo.contentType == Record.ct_handshake) {
hsMemo = (HandshakeMemo)reMemo;
} else {
// not handshake message, break the loop
break;
}
}
}
remainingFragLen -= chipLen;
}
fragLen -= remainingFragLen;
} else {
fragLen = Math.min(fragLen, memo.fragment.length);
dstBuf.put(memo.fragment, 0, fragLen);
handshakeMemos.removeFirst();
}
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), false);
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) {
return new Ciphertext(RecordType.valueOf(
hsMemo.contentType, hsMemo.handshakeType), recordSN);
} else {
return new Ciphertext(
RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
}
}
boolean isEmpty() {
return handshakeMemos.isEmpty();
}
}
/*
* Need to split the payload except the following cases:
*
* 1. protocol version is TLS 1.1 or later;
* 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
* 3. the payload is the first application record of a freshly
* negotiated TLS session.
* 4. the CBC protection is disabled;
*
* 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 needToSplitPayload() {
return (!protocolVersion.useTLS11PlusSpec()) &&
writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
Record.enableCBCProtection;
}
}

View file

@ -0,0 +1,117 @@
/*
* 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;
/**
* SSL/TLS record
*
* @author David Brownell
*/
interface SSLRecord extends Record {
static final int headerSize = 5; // SSLv3 record 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
/*
* SSL has a maximum record size. It's header, (compressed) data,
* padding, and a trailer for the message authentication information (MAC
* for block and stream ciphers, and message authentication tag for AEAD
* ciphers).
*
* Some compression algorithms have rare cases where they expand the data.
* As we don't support compression at this time, leave that out.
*/
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
);
/*
* The maximum large record size.
*
* Some SSL/TLS implementations support large fragment upto 2^15 bytes,
* such as Microsoft. We support large incoming fragments.
*
* The maximum large record size is defined as maxRecordSize plus 2^14,
* this is the amount OpenSSL is using.
*/
static final int maxLargeRecordSize =
maxRecordSize // Max size with a conforming implementation
+ maxDataSize; // extra 2^14 bytes for large data packets.
/*
* 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
/*
* We may need to send this SSL v2 "No Cipher" message back, if we
* are faced with an SSLv2 "hello" that's not saying "I talk v3".
* It's the only one documented in the V2 spec as a fatal error.
*/
static final byte[] v2NoCipher = {
(byte)0x80, (byte)0x03, // unpadded 3 byte record
(byte)0x00, // ... error message
(byte)0x00, (byte)0x01 // ... NO_CIPHER error
};
}

View file

@ -68,7 +68,7 @@ class SSLServerSocketImpl extends SSLServerSocket
private SSLContextImpl sslContext; private SSLContextImpl sslContext;
/* Do newly accepted connections require clients to authenticate? */ /* Do newly accepted connections require clients to authenticate? */
private byte doClientAuth = SSLEngineImpl.clauth_none; private ClientAuthType clientAuthType = ClientAuthType.CLIENT_AUTH_NONE;
/* Do new connections created here use the "server" mode of SSL? */ /* Do new connections created here use the "server" mode of SSL? */
private boolean useServerMode = true; private boolean useServerMode = true;
@ -230,13 +230,13 @@ class SSLServerSocketImpl extends SSLServerSocket
*/ */
@Override @Override
public void setNeedClientAuth(boolean flag) { public void setNeedClientAuth(boolean flag) {
doClientAuth = (flag ? clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUIRED :
SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none); ClientAuthType.CLIENT_AUTH_NONE);
} }
@Override @Override
public boolean getNeedClientAuth() { public boolean getNeedClientAuth() {
return (doClientAuth == SSLEngineImpl.clauth_required); return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUIRED);
} }
/** /**
@ -245,13 +245,13 @@ class SSLServerSocketImpl extends SSLServerSocket
*/ */
@Override @Override
public void setWantClientAuth(boolean flag) { public void setWantClientAuth(boolean flag) {
doClientAuth = (flag ? clientAuthType = (flag ? ClientAuthType.CLIENT_AUTH_REQUESTED :
SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none); ClientAuthType.CLIENT_AUTH_NONE);
} }
@Override @Override
public boolean getWantClientAuth() { public boolean getWantClientAuth() {
return (doClientAuth == SSLEngineImpl.clauth_requested); return (clientAuthType == ClientAuthType.CLIENT_AUTH_REQUESTED);
} }
/** /**
@ -341,7 +341,7 @@ class SSLServerSocketImpl extends SSLServerSocket
@Override @Override
public Socket accept() throws IOException { public Socket accept() throws IOException {
SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode, SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode,
enabledCipherSuites, doClientAuth, enableSessionCreation, enabledCipherSuites, clientAuthType, enableSessionCreation,
enabledProtocols, identificationProtocol, algorithmConstraints, enabledProtocols, identificationProtocol, algorithmConstraints,
sniMatchers, preferLocalCipherSuites); sniMatchers, preferLocalCipherSuites);

View file

@ -109,6 +109,8 @@ final class SSLSessionImpl extends ExtendedSSLSession {
private String[] peerSupportedSignAlgs; private String[] peerSupportedSignAlgs;
private List<SNIServerName> requestedServerNames; private List<SNIServerName> requestedServerNames;
private int negotiatedMaxFragLen;
private int maximumPacketSize;
// Principals for non-certificate based cipher suites // Principals for non-certificate based cipher suites
private Principal peerPrincipal; private Principal peerPrincipal;
@ -177,6 +179,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
sessionCount = ++counter; sessionCount = ++counter;
localSupportedSignAlgs = localSupportedSignAlgs =
SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
negotiatedMaxFragLen = -1;
if (debug != null && Debug.isOn("session")) { if (debug != null && Debug.isOn("session")) {
System.out.println("%% Initialized: " + this); System.out.println("%% Initialized: " + this);
@ -785,8 +788,30 @@ final class SSLSessionImpl extends ExtendedSSLSession {
*/ */
@Override @Override
public synchronized int getPacketBufferSize() { public synchronized int getPacketBufferSize() {
// Use the bigger packet size calculated from maximumPacketSize
// and negotiatedMaxFragLen.
int packetSize = 0;
if (negotiatedMaxFragLen > 0) {
packetSize = cipherSuite.calculatePacketSize(
negotiatedMaxFragLen, protocolVersion,
protocolVersion.isDTLSProtocol());
}
if (maximumPacketSize > 0) {
return (maximumPacketSize > packetSize) ?
maximumPacketSize : packetSize;
}
if (packetSize != 0) {
return packetSize;
}
if (protocolVersion.isDTLSProtocol()) {
return DTLSRecord.maxRecordSize;
} else {
return acceptLargeFragments ? return acceptLargeFragments ?
Record.maxLargeRecordSize : Record.maxRecordSize; SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
}
} }
/** /**
@ -795,7 +820,64 @@ final class SSLSessionImpl extends ExtendedSSLSession {
*/ */
@Override @Override
public synchronized int getApplicationBufferSize() { public synchronized int getApplicationBufferSize() {
return getPacketBufferSize() - Record.headerSize; // Use the bigger fragment size calculated from maximumPacketSize
// and negotiatedMaxFragLen.
int fragmentSize = 0;
if (maximumPacketSize > 0) {
fragmentSize = cipherSuite.calculateFragSize(
maximumPacketSize, protocolVersion,
protocolVersion.isDTLSProtocol());
}
if (negotiatedMaxFragLen > 0) {
return (negotiatedMaxFragLen > fragmentSize) ?
negotiatedMaxFragLen : fragmentSize;
}
if (fragmentSize != 0) {
return fragmentSize;
}
if (protocolVersion.isDTLSProtocol()) {
return Record.maxDataSize;
} else {
int maxPacketSize = acceptLargeFragments ?
SSLRecord.maxLargeRecordSize : SSLRecord.maxRecordSize;
return (maxPacketSize - SSLRecord.headerSize);
}
}
/**
* Sets the negotiated maximum fragment length, as specified by the
* max_fragment_length ClientHello extension in RFC 6066.
*
* @param negotiatedMaxFragLen
* the negotiated maximum fragment length, or {@code -1} if
* no such length has been negotiated.
*/
synchronized void setNegotiatedMaxFragSize(
int negotiatedMaxFragLen) {
this.negotiatedMaxFragLen = negotiatedMaxFragLen;
}
/**
* Get the negotiated maximum fragment length, as specified by the
* max_fragment_length ClientHello extension in RFC 6066.
*
* @return the negotiated maximum fragment length, or {@code -1} if
* no such length has been negotiated.
*/
synchronized int getNegotiatedMaxFragSize() {
return negotiatedMaxFragLen;
}
synchronized void setMaximumPacketSize(int maximumPacketSize) {
this.maximumPacketSize = maximumPacketSize;
}
synchronized int getMaximumPacketSize() {
return maximumPacketSize;
} }
/** /**

View file

@ -0,0 +1,441 @@
/*
* 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 javax.crypto.BadPaddingException;
import javax.net.ssl.*;
import sun.misc.HexDumpEncoder;
/**
* {@code InputRecord} implementation for {@code SSLSocket}.
*
* @author David Brownell
*/
final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
private OutputStream deliverStream = null;
private byte[] temporary = new byte[1024];
// used by handshake hash computation for handshake fragment
private byte prevType = -1;
private int hsMsgOff = 0;
private int hsMsgLen = 0;
private boolean formatVerified = false; // SSLv2 ruled out?
private boolean hasHeader = false; // Had read the record header
SSLSocketInputRecord() {
this.readAuthenticator = MAC.TLS_NULL;
}
@Override
int bytesInCompletePacket(InputStream is) throws IOException {
if (!hasHeader) {
// read exactly one record
int really = read(is, temporary, 0, headerSize);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
hasHeader = true;
}
byte byteZero = temporary[0];
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(temporary[1], temporary[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 = ((temporary[3] & 0xFF) << 8) +
(temporary[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 && ((temporary[2] == 1) || (temporary[2] == 4))) {
ProtocolVersion recordVersion =
ProtocolVersion.valueOf(temporary[3], temporary[4]);
// check the record version
checkRecordVersion(recordVersion, true);
/*
* Client or Server Hello
*/
//
// Short header is using here. We reverse the code here
// in case it it used in the future.
//
// int mask = (isShort ? 0x7F : 0x3F);
// len = ((byteZero & mask) << 8) +
// (temporary[1] & 0xFF) + (isShort ? 2 : 3);
//
len = ((byteZero & 0x7F) << 8) + (temporary[1] & 0xFF) + 2;
} else {
// Gobblygook!
throw new SSLException(
"Unrecognized SSL message, plaintext connection?");
}
}
return len;
}
// destination.position() is zero.
@Override
Plaintext decode(InputStream is, ByteBuffer destination)
throws IOException, BadPaddingException {
if (isClosed) {
return null;
}
if (!hasHeader) {
// read exactly one record
int really = read(is, temporary, 0, headerSize);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
hasHeader = true;
}
Plaintext plaintext = null;
if (!formatVerified) {
formatVerified = true;
/*
* The first record must either be a handshake record or an
* alert message. If it's not, it is either invalid or an
* SSLv2 message.
*/
if ((temporary[0] != ct_handshake) &&
(temporary[0] != ct_alert)) {
plaintext = handleUnknownRecord(is, temporary, destination);
}
}
if (plaintext == null) {
plaintext = decodeInputRecord(is, temporary, destination);
}
// The record header should has comsumed.
hasHeader = false;
return plaintext;
}
@Override
void setDeliverStream(OutputStream outputStream) {
this.deliverStream = outputStream;
}
// Note that destination may be null
private Plaintext decodeInputRecord(InputStream is, byte[] header,
ByteBuffer destination) throws IOException, BadPaddingException {
byte contentType = header[0];
byte majorVersion = header[1];
byte minorVersion = header[2];
int contentLen = ((header[3] & 0xFF) << 8) + (header[4] & 0xFF);
//
// Check for upper bound.
//
// Note: May check packetSize limit in the future.
if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) {
throw new SSLProtocolException(
"Bad input record size, TLSCiphertext.length = " + contentLen);
}
//
// Read a complete record.
//
if (destination == null) {
destination = ByteBuffer.allocate(headerSize + contentLen);
} // Otherwise, the destination buffer should have enough room.
int dstPos = destination.position();
destination.put(temporary, 0, headerSize);
while (contentLen > 0) {
int howmuch = Math.min(temporary.length, contentLen);
int really = read(is, temporary, 0, howmuch);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
destination.put(temporary, 0, howmuch);
contentLen -= howmuch;
}
destination.flip();
destination.position(dstPos + headerSize);
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", READ: " +
ProtocolVersion.valueOf(majorVersion, minorVersion) +
" " + Record.contentName(contentType) + ", length = " +
destination.remaining());
}
//
// Decrypt the fragment
//
ByteBuffer plaintext =
decrypt(readAuthenticator, readCipher, contentType, destination);
if ((contentType != ct_handshake) && (hsMsgOff != hsMsgLen)) {
throw new SSLProtocolException(
"Expected to get a handshake fragment");
}
//
// handshake hashing
//
if (contentType == ct_handshake) {
int pltPos = plaintext.position();
int pltLim = plaintext.limit();
int frgPos = pltPos;
for (int remains = plaintext.remaining(); remains > 0;) {
int howmuch;
byte handshakeType;
if (hsMsgOff < hsMsgLen) {
// a fragment of the handshake message
howmuch = Math.min((hsMsgLen - hsMsgOff), remains);
handshakeType = prevType;
hsMsgOff += howmuch;
if (hsMsgOff == hsMsgLen) {
// Now is a complete handshake message.
hsMsgOff = 0;
hsMsgLen = 0;
}
} else { // hsMsgOff == hsMsgLen, a new handshake message
handshakeType = plaintext.get();
int handshakeLen = ((plaintext.get() & 0xFF) << 16) |
((plaintext.get() & 0xFF) << 8) |
(plaintext.get() & 0xFF);
plaintext.position(frgPos);
if (remains < (handshakeLen + 1)) { // 1: handshake type
// This handshake message is fragmented.
prevType = handshakeType;
hsMsgOff = remains - 4; // 4: handshake header
hsMsgLen = handshakeLen;
}
howmuch = Math.min(handshakeLen + 4, remains);
}
plaintext.limit(frgPos + howmuch);
if (handshakeType == HandshakeMessage.ht_hello_request) {
// omitted from handshake hash computation
} else if ((handshakeType != HandshakeMessage.ht_finished) &&
(handshakeType != HandshakeMessage.ht_certificate_verify)) {
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(plaintext);
} else {
// Reserve until this handshake message has been processed.
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.reserve(plaintext);
}
plaintext.position(frgPos + howmuch);
plaintext.limit(pltLim);
frgPos += howmuch;
remains -= howmuch;
}
plaintext.position(pltPos);
}
return new Plaintext(contentType,
majorVersion, minorVersion, -1, -1L, plaintext);
// recordEpoch, recordSeq, plaintext);
}
private Plaintext handleUnknownRecord(InputStream is, byte[] header,
ByteBuffer destination) throws IOException, BadPaddingException {
byte firstByte = header[0];
byte thirdByte = header[2];
// Does it look like a Version 2 client hello (V2ClientHello)?
if (((firstByte & 0x80) != 0) && (thirdByte == 1)) {
/*
* If SSLv2Hello is not enabled, throw an exception.
*/
if (helloVersion != ProtocolVersion.SSL20Hello) {
throw new SSLHandshakeException("SSLv2Hello is not enabled");
}
byte majorVersion = header[3];
byte minorVersion = header[4];
if ((majorVersion == ProtocolVersion.SSL20Hello.major) &&
(minorVersion == ProtocolVersion.SSL20Hello.minor)) {
/*
* Looks like a V2 client hello, but not one saying
* "let's talk SSLv3". So we need to send an SSLv2
* error message, one that's treated as fatal by
* clients (Otherwise we'll hang.)
*/
deliverStream.write(SSLRecord.v2NoCipher); // SSLv2Hello
if (debug != null) {
if (Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
"Requested to negotiate unsupported SSLv2!");
}
if (Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " +
SSLRecord.v2NoCipher.length,
SSLRecord.v2NoCipher);
}
}
throw new SSLException("Unsupported SSL v2.0 ClientHello");
}
int msgLen = ((header[0] & 0x7F) << 8) | (header[1] & 0xFF);
if (destination == null) {
destination = ByteBuffer.allocate(headerSize + msgLen);
}
destination.put(temporary, 0, headerSize);
msgLen -= 3; // had read 3 bytes of content as header
while (msgLen > 0) {
int howmuch = Math.min(temporary.length, msgLen);
int really = read(is, temporary, 0, howmuch);
if (really < 0) {
throw new EOFException("SSL peer shut down incorrectly");
}
destination.put(temporary, 0, howmuch);
msgLen -= howmuch;
}
destination.flip();
/*
* If we can map this into a V3 ClientHello, read and
* hash the rest of the V2 handshake, turn it into a
* V3 ClientHello message, and pass it up.
*/
destination.position(2); // exclude the header
if (handshakeHash == null) {
// used for cache only
handshakeHash = new HandshakeHash(false);
}
handshakeHash.update(destination);
destination.position(0);
ByteBuffer converted = convertToClientHello(destination);
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Converted] ClientHello", converted);
}
return new Plaintext(ct_handshake,
majorVersion, minorVersion, -1, -1L, converted);
} else {
if (((firstByte & 0x80) != 0) && (thirdByte == 4)) {
throw new SSLException("SSL V2.0 servers are not supported.");
}
throw new SSLException("Unsupported or unrecognized SSL message");
}
}
// Read the exact bytes of data, otherwise, return -1.
private static int read(InputStream is,
byte[] buffer, int offset, int len) throws IOException {
int n = 0;
while (n < len) {
int readLen = is.read(buffer, offset + n, len - n);
if (readLen < 0) {
return -1;
}
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw read]: length = " + readLen,
buffer, offset + n, readLen);
}
n += readLen;
}
return n;
}
}

View file

@ -0,0 +1,383 @@
/*
* Copyright (c) 1996, 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.*;
import java.util.Arrays;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import sun.misc.HexDumpEncoder;
/**
* {@code OutputRecord} implementation for {@code SSLSocket}.
*/
final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord {
private OutputStream deliverStream = null;
SSLSocketOutputRecord() {
this.writeAuthenticator = MAC.TLS_NULL;
this.packetSize = SSLRecord.maxRecordSize;
this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(level);
write(description);
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_alert) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_alert, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0;
}
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
if (firstMessage) {
firstMessage = false;
if ((helloVersion == ProtocolVersion.SSL20Hello) &&
(source[offset] == HandshakeMessage.ht_client_hello) &&
// 5: recode header size
(source[offset + 4 + 2 + 32] == 0)) {
// V3 session ID is empty
// 4: handshake header size
// 2: client_version in ClientHello
// 32: random in ClientHello
ByteBuffer v2ClientHello = encodeV2ClientHello(
source, (offset + 4), (length - 4));
byte[] record = v2ClientHello.array(); // array offset is zero
int limit = v2ClientHello.limit();
handshakeHash.update(record, 2, (limit - 2));
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: SSLv2 ClientHello message" +
", length = " + limit);
}
// deliver this message
//
// Version 2 ClientHello message should be plaintext.
//
// No max fragment length negotiation.
deliverStream.write(record, 0, limit);
deliverStream.flush();
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, record, 0, limit);
}
return;
}
}
byte handshakeType = source[0];
if (handshakeType != HandshakeMessage.ht_hello_request) {
handshakeHash.update(source, offset, length);
}
int fragLimit = getFragLimit();
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count == 0) {
count = position;
}
if ((count - position) < (fragLimit - length)) {
write(source, offset, length);
return;
}
for (int limit = (offset + length); offset < limit;) {
int remains = (limit - offset) + (count - position);
int fragLen = Math.min(fragLimit, remains);
// use the buf of ByteArrayOutputStream
write(source, offset, fragLen);
if (remains < fragLimit) {
return;
}
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_handshake) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_handshake, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the offset
offset += fragLen;
// reset the internal buffer
count = position;
}
}
@Override
void encodeChangeCipherSpec() throws IOException {
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write((byte)1); // byte 1: change_cipher_spec(
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_change_cipher_spec) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_change_cipher_spec, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
// deliverStream.flush(); // flush in Finished
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0;
}
@Override
public void flush() throws IOException {
int position = headerSize + writeCipher.getExplicitNonceSize();
if (count <= position) {
return;
}
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_handshake) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_handshake, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0; // DON'T use position
}
@Override
void deliver(byte[] source, int offset, int length) 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");
}
boolean isFirstRecordOfThePayload = true;
for (int limit = (offset + length); offset < limit;) {
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);
}
if (isFirstRecordOfThePayload && needToSplitPayload()) {
fragLen = 1;
isFirstRecordOfThePayload = false;
} else {
fragLen = Math.min(fragLen, (limit - offset));
}
// use the buf of ByteArrayOutputStream
int position = headerSize + writeCipher.getExplicitNonceSize();
count = position;
write(source, offset, fragLen);
if (debug != null && Debug.isOn("record")) {
System.out.println(Thread.currentThread().getName() +
", WRITE: " + protocolVersion +
" " + Record.contentName(Record.ct_application_data) +
", length = " + (count - headerSize));
}
// Encrypt the fragment and wrap up a record.
encrypt(writeAuthenticator, writeCipher,
Record.ct_application_data, headerSize);
// deliver this message
deliverStream.write(buf, 0, count); // may throw IOException
deliverStream.flush(); // may throw IOException
if (debug != null && Debug.isOn("packet")) {
Debug.printHex(
"[Raw write]: length = " + count, buf, 0, count);
}
// reset the internal buffer
count = 0;
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
offset += fragLen;
}
}
@Override
void setDeliverStream(OutputStream outputStream) {
this.deliverStream = outputStream;
}
/*
* Need to split the payload except the following cases:
*
* 1. protocol version is TLS 1.1 or later;
* 2. bulk cipher does not use CBC mode, including null bulk cipher suites.
* 3. the payload is the first application record of a freshly
* negotiated TLS session.
* 4. the CBC protection is disabled;
*
* 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 needToSplitPayload() {
return (!protocolVersion.useTLS11PlusSpec()) &&
writeCipher.isCBCMode() && !isFirstAppOutputRecord &&
Record.enableCBCProtection;
}
private int getFragLimit() {
int macLen = 0;
if (writeAuthenticator instanceof MAC) {
macLen = ((MAC)writeAuthenticator).MAClen();
}
int fragLimit;
if (packetSize > 0) {
fragLimit = Math.min(maxRecordSize, packetSize);
fragLimit = writeCipher.calculateFragmentSize(
fragLimit, macLen, headerSize);
fragLimit = Math.min(fragLimit, Record.maxDataSize);
} else {
fragLimit = Record.maxDataSize;
}
if (fragmentSize > 0) {
fragLimit = Math.min(fragLimit, fragmentSize);
}
return fragLimit;
}
}

View file

@ -58,7 +58,7 @@ import static sun.security.ssl.CipherSuite.KeyExchange.*;
final class ServerHandshaker extends Handshaker { final class ServerHandshaker extends Handshaker {
// is the server going to require the client to authenticate? // is the server going to require the client to authenticate?
private byte doClientAuth; private ClientAuthType doClientAuth;
// our authentication info // our authentication info
private X509Certificate[] certs; private X509Certificate[] certs;
@ -143,13 +143,13 @@ final class ServerHandshaker extends Handshaker {
* Constructor ... use the keys found in the auth context. * Constructor ... use the keys found in the auth context.
*/ */
ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context, ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context,
ProtocolList enabledProtocols, byte clientAuth, ProtocolList enabledProtocols, ClientAuthType clientAuth,
ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, ProtocolVersion activeProtocolVersion, boolean isInitialHandshake,
boolean secureRenegotiation, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) { byte[] clientVerifyData, byte[] serverVerifyData) {
super(socket, context, enabledProtocols, super(socket, context, enabledProtocols,
(clientAuth != SSLEngineImpl.clauth_none), false, (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
activeProtocolVersion, isInitialHandshake, secureRenegotiation, activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData); clientVerifyData, serverVerifyData);
doClientAuth = clientAuth; doClientAuth = clientAuth;
@ -159,15 +159,16 @@ final class ServerHandshaker extends Handshaker {
* Constructor ... use the keys found in the auth context. * Constructor ... use the keys found in the auth context.
*/ */
ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context, ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context,
ProtocolList enabledProtocols, byte clientAuth, ProtocolList enabledProtocols, ClientAuthType clientAuth,
ProtocolVersion activeProtocolVersion, ProtocolVersion activeProtocolVersion,
boolean isInitialHandshake, boolean secureRenegotiation, boolean isInitialHandshake, boolean secureRenegotiation,
byte[] clientVerifyData, byte[] serverVerifyData) { byte[] clientVerifyData, byte[] serverVerifyData,
boolean isDTLS) {
super(engine, context, enabledProtocols, super(engine, context, enabledProtocols,
(clientAuth != SSLEngineImpl.clauth_none), false, (clientAuth != ClientAuthType.CLIENT_AUTH_NONE), false,
activeProtocolVersion, isInitialHandshake, secureRenegotiation, activeProtocolVersion, isInitialHandshake, secureRenegotiation,
clientVerifyData, serverVerifyData); clientVerifyData, serverVerifyData, isDTLS);
doClientAuth = clientAuth; doClientAuth = clientAuth;
} }
@ -176,7 +177,7 @@ final class ServerHandshaker extends Handshaker {
* whether client authentication is required. Otherwise, * whether client authentication is required. Otherwise,
* we will need to wait for the next handshake. * we will need to wait for the next handshake.
*/ */
void setClientAuth(byte clientAuth) { void setClientAuth(ClientAuthType clientAuth) {
doClientAuth = clientAuth; doClientAuth = clientAuth;
} }
@ -192,21 +193,15 @@ final class ServerHandshaker extends Handshaker {
@Override @Override
void processMessage(byte type, int message_len) void processMessage(byte type, int message_len)
throws IOException { throws IOException {
//
// In SSLv3 and TLS, messages follow strictly increasing // check the handshake state
// numerical order _except_ for one annoying special case. handshakeState.check(type);
//
if ((state >= type)
&& (state != HandshakeMessage.ht_client_key_exchange
&& type != HandshakeMessage.ht_certificate_verify)) {
throw new SSLProtocolException(
"Handshake message sequence violation, state = " + state
+ ", type = " + type);
}
switch (type) { switch (type) {
case HandshakeMessage.ht_client_hello: case HandshakeMessage.ht_client_hello:
ClientHello ch = new ClientHello(input, message_len); ClientHello ch = new ClientHello(input, message_len, isDTLS);
handshakeState.update(ch, resumingSession);
/* /*
* send it off for processing. * send it off for processing.
*/ */
@ -214,12 +209,14 @@ final class ServerHandshaker extends Handshaker {
break; break;
case HandshakeMessage.ht_certificate: case HandshakeMessage.ht_certificate:
if (doClientAuth == SSLEngineImpl.clauth_none) { if (doClientAuth == ClientAuthType.CLIENT_AUTH_NONE) {
fatalSE(Alerts.alert_unexpected_message, fatalSE(Alerts.alert_unexpected_message,
"client sent unsolicited cert chain"); "client sent unsolicited cert chain");
// NOTREACHED // NOTREACHED
} }
this.clientCertificate(new CertificateMsg(input)); CertificateMsg certificateMsg = new CertificateMsg(input);
handshakeState.update(certificateMsg, resumingSession);
this.clientCertificate(certificateMsg);
break; break;
case HandshakeMessage.ht_client_key_exchange: case HandshakeMessage.ht_client_key_exchange:
@ -237,17 +234,20 @@ final class ServerHandshaker extends Handshaker {
protocolVersion, clientRequestedVersion, protocolVersion, clientRequestedVersion,
sslContext.getSecureRandom(), input, sslContext.getSecureRandom(), input,
message_len, privateKey); message_len, privateKey);
handshakeState.update(pms, resumingSession);
preMasterSecret = this.clientKeyExchange(pms); preMasterSecret = this.clientKeyExchange(pms);
break; break;
case K_KRB5: case K_KRB5:
case K_KRB5_EXPORT: case K_KRB5_EXPORT:
preMasterSecret = this.clientKeyExchange( KerberosClientKeyExchange kke =
new KerberosClientKeyExchange(protocolVersion, new KerberosClientKeyExchange(protocolVersion,
clientRequestedVersion, clientRequestedVersion,
sslContext.getSecureRandom(), sslContext.getSecureRandom(),
input, input,
this.getAccSE(), this.getAccSE(),
serviceCreds)); serviceCreds);
handshakeState.update(kke, resumingSession);
preMasterSecret = this.clientKeyExchange(kke);
break; break;
case K_DHE_RSA: case K_DHE_RSA:
case K_DHE_DSS: case K_DHE_DSS:
@ -258,16 +258,19 @@ final class ServerHandshaker extends Handshaker {
* protocol difference in these five flavors is in how * protocol difference in these five flavors is in how
* the ServerKeyExchange message was constructed! * the ServerKeyExchange message was constructed!
*/ */
preMasterSecret = this.clientKeyExchange( DHClientKeyExchange dhcke = new DHClientKeyExchange(input);
new DHClientKeyExchange(input)); handshakeState.update(dhcke, resumingSession);
preMasterSecret = this.clientKeyExchange(dhcke);
break; break;
case K_ECDH_RSA: case K_ECDH_RSA:
case K_ECDH_ECDSA: case K_ECDH_ECDSA:
case K_ECDHE_RSA: case K_ECDHE_RSA:
case K_ECDHE_ECDSA: case K_ECDHE_ECDSA:
case K_ECDH_ANON: case K_ECDH_ANON:
preMasterSecret = this.clientKeyExchange ECDHClientKeyExchange ecdhcke =
(new ECDHClientKeyExchange(input)); new ECDHClientKeyExchange(input);
handshakeState.update(ecdhcke, resumingSession);
preMasterSecret = this.clientKeyExchange(ecdhcke);
break; break;
default: default:
throw new SSLProtocolException throw new SSLProtocolException
@ -282,20 +285,20 @@ final class ServerHandshaker extends Handshaker {
break; break;
case HandshakeMessage.ht_certificate_verify: case HandshakeMessage.ht_certificate_verify:
this.clientCertificateVerify(new CertificateVerify(input, CertificateVerify cvm =
localSupportedSignAlgs, protocolVersion)); new CertificateVerify(input,
localSupportedSignAlgs, protocolVersion);
handshakeState.update(cvm, resumingSession);
this.clientCertificateVerify(cvm);
break; break;
case HandshakeMessage.ht_finished: case HandshakeMessage.ht_finished:
// A ChangeCipherSpec record must have been received prior to Finished cfm =
// reception of the Finished message (RFC 5246, 7.4.9). new Finished(protocolVersion, input, cipherSuite);
if (!receivedChangeCipherSpec()) { handshakeState.update(cfm, resumingSession);
fatalSE(Alerts.alert_handshake_failure, this.clientFinished(cfm);
"Received Finished message before ChangeCipherSpec");
}
this.clientFinished(
new Finished(protocolVersion, input, cipherSuite));
break; break;
default: default:
@ -303,17 +306,6 @@ final class ServerHandshaker extends Handshaker {
"Illegal server handshake msg, " + type); "Illegal server handshake msg, " + type);
} }
//
// Move state machine forward if the message handling
// code didn't already do so
//
if (state < type) {
if(type == HandshakeMessage.ht_certificate_verify) {
state = type + 2; // an annoying special case
} else {
state = type;
}
}
} }
@ -344,7 +336,7 @@ final class ServerHandshaker extends Handshaker {
// //
// This will not have any impact on server initiated renegotiation. // This will not have any impact on server initiated renegotiation.
if (rejectClientInitiatedRenego && !isInitialHandshake && if (rejectClientInitiatedRenego && !isInitialHandshake &&
state != HandshakeMessage.ht_hello_request) { !serverHelloRequested) {
fatalSE(Alerts.alert_handshake_failure, fatalSE(Alerts.alert_handshake_failure,
"Client initiated renegotiation is not allowed"); "Client initiated renegotiation is not allowed");
} }
@ -438,7 +430,7 @@ final class ServerHandshaker extends Handshaker {
} }
} else if (!allowUnsafeRenegotiation) { } else if (!allowUnsafeRenegotiation) {
// abort the handshake // abort the handshake
if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { if (activeProtocolVersion.useTLS10PlusSpec()) {
// respond with a no_renegotiation warning // respond with a no_renegotiation warning
warningSE(Alerts.alert_no_renegotiation); warningSE(Alerts.alert_no_renegotiation);
@ -480,11 +472,52 @@ final class ServerHandshaker extends Handshaker {
} }
} }
/* // check the "max_fragment_length" extension
* Always make sure this entire record has been digested before we MaxFragmentLengthExtension maxFragLenExt = (MaxFragmentLengthExtension)
* start emitting output, to ensure correct digesting order. mesg.extensions.get(ExtensionType.EXT_MAX_FRAGMENT_LENGTH);
*/ if ((maxFragLenExt != null) && (maximumPacketSize != 0)) {
input.digestNow(); // Not yet consider the impact of IV/MAC/padding.
int estimatedMaxFragSize = maximumPacketSize;
if (isDTLS) {
estimatedMaxFragSize -= DTLSRecord.headerSize;
} else {
estimatedMaxFragSize -= SSLRecord.headerSize;
}
if (maxFragLenExt.getMaxFragLen() > estimatedMaxFragSize) {
// For better interoperability, abort the maximum fragment
// length negotiation, rather than terminate the connection
// with a fatal alert.
maxFragLenExt = null;
// fatalSE(Alerts.alert_illegal_parameter,
// "Not an allowed max_fragment_length value");
}
}
// cookie exchange
if (isDTLS) {
HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
(!hcMgr.isValid(mesg))) {
//
// Perform cookie exchange for DTLS handshaking if no cookie
// or the cookie is invalid in the ClientHello message.
//
HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
if (debug != null && Debug.isOn("handshake")) {
m0.print(System.out);
}
m0.write(output);
handshakeState.update(m0, resumingSession);
output.flush();
return;
}
}
/* /*
* FIRST, construct the ServerHello using the options and priorities * FIRST, construct the ServerHello using the options and priorities
@ -580,7 +613,7 @@ final class ServerHandshaker extends Handshaker {
} }
if (resumingSession && if (resumingSession &&
(doClientAuth == SSLEngineImpl.clauth_required)) { (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED)) {
try { try {
previous.getPeerPrincipal(); previous.getPeerPrincipal();
} catch (SSLPeerUnverifiedException e) { } catch (SSLPeerUnverifiedException e) {
@ -677,7 +710,7 @@ final class ServerHandshaker extends Handshaker {
// We only need to handle the "signature_algorithm" extension // We only need to handle the "signature_algorithm" extension
// for full handshakes and TLS 1.2 or later. // for full handshakes and TLS 1.2 or later.
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
SignatureAlgorithmsExtension signAlgs = SignatureAlgorithmsExtension signAlgs =
(SignatureAlgorithmsExtension)mesg.extensions.get( (SignatureAlgorithmsExtension)mesg.extensions.get(
ExtensionType.EXT_SIGNATURE_ALGORITHMS); ExtensionType.EXT_SIGNATURE_ALGORITHMS);
@ -708,7 +741,7 @@ final class ServerHandshaker extends Handshaker {
sslContext.getSecureRandom(), sslContext.getSecureRandom(),
getHostAddressSE(), getPortSE()); getHostAddressSE(), getPortSE());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs != null) { if (peerSupportedSignAlgs != null) {
session.setPeerSupportedSignatureAlgorithms( session.setPeerSupportedSignatureAlgorithms(
peerSupportedSignAlgs); peerSupportedSignAlgs);
@ -734,12 +767,41 @@ final class ServerHandshaker extends Handshaker {
session.setLocalPrivateKey(privateKey); session.setLocalPrivateKey(privateKey);
// chooseCompression(mesg); // chooseCompression(mesg);
// set the negotiated maximum fragment in the session
//
// The protocol version and cipher suite have been negotiated
// in previous processes.
if (maxFragLenExt != null) {
int maxFragLen = maxFragLenExt.getMaxFragLen();
// More check of the requested "max_fragment_length" extension.
if (maximumPacketSize != 0) {
int estimatedMaxFragSize = cipherSuite.calculatePacketSize(
maxFragLen, protocolVersion, isDTLS);
if (estimatedMaxFragSize > maximumPacketSize) {
// For better interoperability, abort the maximum
// fragment length negotiation, rather than terminate
// the connection with a fatal alert.
maxFragLenExt = null;
// fatalSE(Alerts.alert_illegal_parameter,
// "Not an allowed max_fragment_length value");
}
}
if (maxFragLenExt != null) {
session.setNegotiatedMaxFragSize(maxFragLen);
}
}
session.setMaximumPacketSize(maximumPacketSize);
} else { } else {
// set the handshake session // set the handshake session
setHandshakeSessionSE(session); setHandshakeSessionSE(session);
} }
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg());
} }
@ -771,11 +833,20 @@ final class ServerHandshaker extends Handshaker {
} }
} }
if ((maxFragLenExt != null) && !resumingSession) {
// When resuming a session, the server MUST NOT include a
// max_fragment_length extension in the server hello.
//
// Otherwise, use the same value as the requested extension.
m1.extensions.add(maxFragLenExt);
}
if (debug != null && Debug.isOn("handshake")) { if (debug != null && Debug.isOn("handshake")) {
m1.print(System.out); m1.print(System.out);
System.out.println("Cipher suite: " + session.getSuite()); System.out.println("Cipher suite: " + session.getSuite());
} }
m1.write(output); m1.write(output);
handshakeState.update(m1, resumingSession);
// //
// If we are resuming a session, we finish writing handshake // If we are resuming a session, we finish writing handshake
@ -784,6 +855,10 @@ final class ServerHandshaker extends Handshaker {
if (resumingSession) { if (resumingSession) {
calculateConnectionKeys(session.getMasterSecret()); calculateConnectionKeys(session.getMasterSecret());
sendChangeCipherAndFinish(false); sendChangeCipherAndFinish(false);
// expecting the final ChangeCipherSpec and Finished messages
expectingFinishFlightSE();
return; return;
} }
@ -815,6 +890,7 @@ final class ServerHandshaker extends Handshaker {
m2.print(System.out); m2.print(System.out);
} }
m2.write(output); m2.write(output);
handshakeState.update(m2, resumingSession);
// XXX has some side effects with OS TCP buffering, // XXX has some side effects with OS TCP buffering,
// leave it out for now // leave it out for now
@ -853,9 +929,9 @@ final class ServerHandshaker extends Handshaker {
sslContext.getSecureRandom()); sslContext.getSecureRandom());
privateKey = tempPrivateKey; privateKey = tempPrivateKey;
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException
("Error generating RSA server key exchange", e);
m3 = null; // make compiler happy m3 = null; // make compiler happy
throw new SSLException(
"Error generating RSA server key exchange", e);
} }
} else { } else {
// RSA_EXPORT with short key, don't need ServerKeyExchange // RSA_EXPORT with short key, don't need ServerKeyExchange
@ -873,8 +949,9 @@ final class ServerHandshaker extends Handshaker {
preferableSignatureAlgorithm, preferableSignatureAlgorithm,
protocolVersion); protocolVersion);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException("Error generating DH server key exchange", e);
m3 = null; // make compiler happy m3 = null; // make compiler happy
throw new SSLException(
"Error generating DH server key exchange", e);
} }
break; break;
case K_DH_ANON: case K_DH_ANON:
@ -892,9 +969,9 @@ final class ServerHandshaker extends Handshaker {
preferableSignatureAlgorithm, preferableSignatureAlgorithm,
protocolVersion); protocolVersion);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throwSSLException(
"Error generating ECDH server key exchange", e);
m3 = null; // make compiler happy m3 = null; // make compiler happy
throw new SSLException(
"Error generating ECDH server key exchange", e);
} }
break; break;
case K_ECDH_RSA: case K_ECDH_RSA:
@ -910,6 +987,7 @@ final class ServerHandshaker extends Handshaker {
m3.print(System.out); m3.print(System.out);
} }
m3.write(output); m3.write(output);
handshakeState.update(m3, resumingSession);
} }
// //
@ -923,7 +1001,7 @@ final class ServerHandshaker extends Handshaker {
// Illegal for anonymous flavors, so we need to check that. // Illegal for anonymous flavors, so we need to check that.
// //
// CertificateRequest is omitted for Kerberos ciphers // CertificateRequest is omitted for Kerberos ciphers
if (doClientAuth != SSLEngineImpl.clauth_none && if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON && keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) { keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) {
@ -931,7 +1009,7 @@ final class ServerHandshaker extends Handshaker {
X509Certificate caCerts[]; X509Certificate caCerts[];
Collection<SignatureAndHashAlgorithm> localSignAlgs = null; Collection<SignatureAndHashAlgorithm> localSignAlgs = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
// We currently use all local upported signature and hash // We currently use all local upported signature and hash
// algorithms. However, to minimize the computation cost // algorithms. However, to minimize the computation cost
// of requested hash algorithms, we may use a restricted // of requested hash algorithms, we may use a restricted
@ -959,6 +1037,7 @@ final class ServerHandshaker extends Handshaker {
m4.print(System.out); m4.print(System.out);
} }
m4.write(output); m4.write(output);
handshakeState.update(m4, resumingSession);
} }
/* /*
@ -970,6 +1049,7 @@ final class ServerHandshaker extends Handshaker {
m5.print(System.out); m5.print(System.out);
} }
m5.write(output); m5.write(output);
handshakeState.update(m5, resumingSession);
/* /*
* Flush any buffered messages so the client will see them. * Flush any buffered messages so the client will see them.
@ -1000,7 +1080,7 @@ final class ServerHandshaker extends Handshaker {
continue; continue;
} }
if (doClientAuth == SSLEngineImpl.clauth_required) { if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
if ((suite.keyExchange == K_DH_ANON) || if ((suite.keyExchange == K_DH_ANON) ||
(suite.keyExchange == K_ECDH_ANON)) { (suite.keyExchange == K_ECDH_ANON)) {
continue; continue;
@ -1043,12 +1123,12 @@ final class ServerHandshaker extends Handshaker {
} }
// must not negotiate the obsoleted weak cipher suites. // must not negotiate the obsoleted weak cipher suites.
if (protocolVersion.v >= suite.obsoleted) { if (protocolVersion.obsoletes(suite)) {
return false; return false;
} }
// must not negotiate unsupported cipher suites. // must not negotiate unsupported cipher suites.
if (protocolVersion.v < suite.supported) { if (!protocolVersion.supports(suite)) {
return false; return false;
} }
@ -1062,7 +1142,7 @@ final class ServerHandshaker extends Handshaker {
tempPublicKey = null; tempPublicKey = null;
Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null; Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null;
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs != null) { if (peerSupportedSignAlgs != null) {
supportedSignAlgs = peerSupportedSignAlgs; supportedSignAlgs = peerSupportedSignAlgs;
} else { } else {
@ -1151,7 +1231,7 @@ final class ServerHandshaker extends Handshaker {
} }
// get preferable peer signature algorithm for server key exchange // get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm = preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm( SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "RSA", privateKey); supportedSignAlgs, "RSA", privateKey);
@ -1169,7 +1249,7 @@ final class ServerHandshaker extends Handshaker {
} }
// get preferable peer signature algorithm for server key exchange // get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm = preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm( SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "RSA", privateKey); supportedSignAlgs, "RSA", privateKey);
@ -1184,7 +1264,7 @@ final class ServerHandshaker extends Handshaker {
break; break;
case K_DHE_DSS: case K_DHE_DSS:
// get preferable peer signature algorithm for server key exchange // get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm = preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm( SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "DSA"); supportedSignAlgs, "DSA");
@ -1202,7 +1282,7 @@ final class ServerHandshaker extends Handshaker {
break; break;
case K_ECDHE_ECDSA: case K_ECDHE_ECDSA:
// get preferable peer signature algorithm for server key exchange // get preferable peer signature algorithm for server key exchange
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
preferableSignatureAlgorithm = preferableSignatureAlgorithm =
SignatureAndHashAlgorithm.getPreferableAlgorithm( SignatureAndHashAlgorithm.getPreferableAlgorithm(
supportedSignAlgs, "ECDSA"); supportedSignAlgs, "ECDSA");
@ -1257,7 +1337,7 @@ final class ServerHandshaker extends Handshaker {
setCipherSuite(suite); setCipherSuite(suite);
// set the peer implicit supported signature algorithms // set the peer implicit supported signature algorithms
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (peerSupportedSignAlgs == null) { if (peerSupportedSignAlgs == null) {
setPeerSupportedSignAlgs(supportedSignAlgs); setPeerSupportedSignAlgs(supportedSignAlgs);
// we had alreay update the session // we had alreay update the session
@ -1571,7 +1651,7 @@ final class ServerHandshaker extends Handshaker {
mesg.print(System.out); mesg.print(System.out);
} }
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
SignatureAndHashAlgorithm signAlg = SignatureAndHashAlgorithm signAlg =
mesg.getPreferableSignatureAlgorithm(); mesg.getPreferableSignatureAlgorithm();
if (signAlg == null) { if (signAlg == null) {
@ -1623,7 +1703,7 @@ final class ServerHandshaker extends Handshaker {
* Verify if client did send the certificate when client * Verify if client did send the certificate when client
* authentication was required, otherwise server should not proceed * authentication was required, otherwise server should not proceed
*/ */
if (doClientAuth == SSLEngineImpl.clauth_required) { if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
// get X500Principal of the end-entity certificate for X509-based // get X500Principal of the end-entity certificate for X509-based
// ciphersuites, or Kerberos principal for Kerberos ciphersuites // ciphersuites, or Kerberos principal for Kerberos ciphersuites
session.getPeerPrincipal(); session.getPeerPrincipal();
@ -1664,8 +1744,9 @@ final class ServerHandshaker extends Handshaker {
* the change_cipher_spec and Finished message. * the change_cipher_spec and Finished message.
*/ */
if (!resumingSession) { if (!resumingSession) {
input.digestNow();
sendChangeCipherAndFinish(true); sendChangeCipherAndFinish(true);
} else {
handshakeFinished = true;
} }
/* /*
@ -1695,7 +1776,8 @@ final class ServerHandshaker extends Handshaker {
private void sendChangeCipherAndFinish(boolean finishedTag) private void sendChangeCipherAndFinish(boolean finishedTag)
throws IOException { throws IOException {
output.flush(); // Reload if this message has been reserved.
handshakeHash.reload();
Finished mesg = new Finished(protocolVersion, handshakeHash, Finished mesg = new Finished(protocolVersion, handshakeHash,
Finished.SERVER, session.getMasterSecret(), cipherSuite); Finished.SERVER, session.getMasterSecret(), cipherSuite);
@ -1713,16 +1795,6 @@ final class ServerHandshaker extends Handshaker {
if (secureRenegotiation) { if (secureRenegotiation) {
serverVerifyData = mesg.getVerifyData(); serverVerifyData = mesg.getVerifyData();
} }
/*
* Update state machine so client MUST send 'finished' next
* The update should only take place if it is not in the fast
* handshake mode since the server has to wait for a finished
* message from the client.
*/
if (finishedTag) {
state = HandshakeMessage.ht_finished;
}
} }
@ -1757,7 +1829,7 @@ final class ServerHandshaker extends Handshaker {
* session will get an SSLPeerUnverifiedException. * session will get an SSLPeerUnverifiedException.
*/ */
if ((description == Alerts.alert_no_certificate) && if ((description == Alerts.alert_no_certificate) &&
(doClientAuth == SSLEngineImpl.clauth_requested)) { (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED)) {
return; return;
} }
@ -1798,7 +1870,7 @@ final class ServerHandshaker extends Handshaker {
* If the client authentication is only *REQUESTED* (e.g. * If the client authentication is only *REQUESTED* (e.g.
* not *REQUIRED*, this is an acceptable condition.) * not *REQUIRED*, this is an acceptable condition.)
*/ */
if (doClientAuth == SSLEngineImpl.clauth_requested) { if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUESTED) {
return; return;
} else { } else {
fatalSE(Alerts.alert_bad_certificate, fatalSE(Alerts.alert_bad_certificate,

View file

@ -61,7 +61,7 @@ public abstract class SunJSSE extends java.security.Provider {
private static String info = "Sun JSSE provider" + private static String info = "Sun JSSE provider" +
"(PKCS12, SunX509/PKIX key/trust factories, " + "(PKCS12, SunX509/PKIX key/trust factories, " +
"SSLv3/TLSv1/TLSv1.1/TLSv1.2)"; "SSLv3/TLSv1/TLSv1.1/TLSv1.2/DTLSv1.0/DTLSv1.2)";
private static String fipsInfo = private static String fipsInfo =
"Sun JSSE provider (FIPS mode, crypto provider "; "Sun JSSE provider (FIPS mode, crypto provider ";
@ -220,6 +220,13 @@ public abstract class SunJSSE extends java.security.Provider {
put("Alg.Alias.SSLContext.SSLv3", "TLSv1"); put("Alg.Alias.SSLContext.SSLv3", "TLSv1");
} }
put("SSLContext.DTLSv1.0",
"sun.security.ssl.SSLContextImpl$DTLS10Context");
put("SSLContext.DTLSv1.2",
"sun.security.ssl.SSLContextImpl$DTLS12Context");
put("SSLContext.DTLS",
"sun.security.ssl.SSLContextImpl$DTLSContext");
put("SSLContext.Default", put("SSLContext.Default",
"sun.security.ssl.SSLContextImpl$DefaultSSLContext"); "sun.security.ssl.SSLContextImpl$DefaultSSLContext");

View file

@ -190,7 +190,7 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
if (session != null) { if (session != null) {
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
String[] peerSupportedSignAlgs = null; String[] peerSupportedSignAlgs = null;
if (session instanceof ExtendedSSLSession) { if (session instanceof ExtendedSSLSession) {
@ -218,7 +218,7 @@ final class X509KeyManagerImpl extends X509ExtendedKeyManager
if (session != null) { if (session != null) {
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
String[] peerSupportedSignAlgs = null; String[] peerSupportedSignAlgs = null;
if (session instanceof ExtendedSSLSession) { if (session instanceof ExtendedSSLSession) {

View file

@ -204,7 +204,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
// create the algorithm constraints // create the algorithm constraints
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) { if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession = ExtendedSSLSession extSession =
(ExtendedSSLSession)session; (ExtendedSSLSession)session;
@ -256,7 +256,7 @@ final class X509TrustManagerImpl extends X509ExtendedTrustManager
// create the algorithm constraints // create the algorithm constraints
ProtocolVersion protocolVersion = ProtocolVersion protocolVersion =
ProtocolVersion.valueOf(session.getProtocol()); ProtocolVersion.valueOf(session.getProtocol());
if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (protocolVersion.useTLS12PlusSpec()) {
if (session instanceof ExtendedSSLSession) { if (session instanceof ExtendedSSLSession) {
ExtendedSSLSession extSession = ExtendedSSLSession extSession =
(ExtendedSSLSession)session; (ExtendedSSLSession)session;

View file

@ -511,11 +511,11 @@ krb5.kdc.bad.policy = tryLast
jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024 jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security # Algorithm restrictions for Secure Socket Layer/Transport Layer Security
# (SSL/TLS) processing # (SSL/TLS/DTLS) processing
# #
# In some environments, certain algorithms or key lengths may be undesirable # In some environments, certain algorithms or key lengths may be undesirable
# when using SSL/TLS. This section describes the mechanism for disabling # when using SSL/TLS/DTLS. This section describes the mechanism for disabling
# algorithms during SSL/TLS security parameters negotiation, including # algorithms during SSL/TLS/DTLS security parameters negotiation, including
# protocol version negotiation, cipher suites selection, peer authentication # protocol version negotiation, cipher suites selection, peer authentication
# and key exchange mechanisms. # and key exchange mechanisms.
# #

View file

@ -0,0 +1,75 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
* @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA256
* @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
* @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
* @run main/othervm CipherSuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_RSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
* @run main/othervm CipherSuite TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
*/
import javax.net.ssl.SSLEngine;
/**
* Test common DTLS cipher suites.
*/
public class CipherSuite extends DTLSOverDatagram {
// use the specific cipher suite
volatile static String cipherSuite;
public static void main(String[] args) throws Exception {
cipherSuite = args[0];
CipherSuite testCase = new CipherSuite();
testCase.runTest(testCase);
}
@Override
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLEngine engine = super.createSSLEngine(isClient);
if (isClient) {
engine.setEnabledCipherSuites(new String[]{cipherSuite});
}
return engine;
}
}

View file

@ -0,0 +1,57 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm ClientAuth
*/
import javax.net.ssl.SSLEngine;
/**
* Test DTLS client authentication.
*/
public class ClientAuth extends DTLSOverDatagram {
public static void main(String[] args) throws Exception {
ClientAuth testCase = new ClientAuth();
testCase.runTest(testCase);
}
@Override
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLEngine engine = super.createSSLEngine(isClient);
if (!isClient) {
engine.setNeedClientAuth(true);
}
return engine;
}
}

View file

@ -0,0 +1,602 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @run main/othervm DTLSOverDatagram
*/
import java.io.*;
import java.nio.*;
import java.net.*;
import java.util.*;
import java.security.*;
import java.security.cert.*;
import javax.net.ssl.*;
import java.util.concurrent.*;
import sun.misc.HexDumpEncoder;
/**
* An example to show the way to use SSLEngine in datagram connections.
*/
public class DTLSOverDatagram {
private static int MAX_HANDSHAKE_LOOPS = 60;
private static int MAX_APP_READ_LOOPS = 10;
/*
* The following is to set up the keystores.
*/
private static String pathToStores = "../etc";
private static String keyStoreFile = "keystore";
private static String trustStoreFile = "truststore";
private static String passwd = "passphrase";
private static String keyFilename =
System.getProperty("test.src", ".") + "/" + pathToStores +
"/" + keyStoreFile;
private static String trustFilename =
System.getProperty("test.src", ".") + "/" + pathToStores +
"/" + trustStoreFile;
private static Exception clientException = null;
private static Exception serverException = null;
private static ByteBuffer serverApp =
ByteBuffer.wrap("Hi Client, I'm Server".getBytes());
private static ByteBuffer clientApp =
ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
/*
* =============================================================
* The test case
*/
public static void main(String[] args) throws Exception {
DTLSOverDatagram testCase = new DTLSOverDatagram();
testCase.runTest(testCase);
}
/*
* Define the server side of the test.
*/
void doServerSide() throws Exception {
DatagramSocket socket = serverDatagramSocket;
socket.setSoTimeout(10000); // 10 second
// create SSLEngine
SSLEngine engine = createSSLEngine(false);
// handshaking
handshake(engine, socket, clientSocketAddr);
// read client application data
receiveAppData(engine, socket, clientApp);
// write server application data
deliverAppData(engine, socket, serverApp, clientSocketAddr);
socket.close();
}
/*
* Define the client side of the test.
*/
void doClientSide() throws Exception {
DatagramSocket socket = clientDatagramSocket;
socket.setSoTimeout(1000); // 1 second read timeout
// create SSLEngine
SSLEngine engine = createSSLEngine(true);
// handshaking
handshake(engine, socket, serverSocketAddr);
// write client application data
deliverAppData(engine, socket, clientApp, serverSocketAddr);
// read server application data
receiveAppData(engine, socket, serverApp);
}
/*
* =============================================================
* The remainder is support stuff for DTLS operations.
*/
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLContext context = getDTLSContext();
SSLEngine engine = context.createSSLEngine();
SSLParameters paras = engine.getSSLParameters();
paras.setMaximumPacketSize(1024);
engine.setUseClientMode(isClient);
engine.setSSLParameters(paras);
return engine;
}
// handshake
void handshake(SSLEngine engine, DatagramSocket socket,
SocketAddress peerAddr) throws Exception {
boolean endLoops = false;
int loops = MAX_HANDSHAKE_LOOPS;
engine.beginHandshake();
while (!endLoops &&
(serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException(
"Too much loops to produce handshake packets");
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
ByteBuffer iNet;
ByteBuffer iApp;
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
// receive ClientHello request and other SSL/TLS records
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
try {
socket.receive(packet);
} catch (SocketTimeoutException ste) {
List<DatagramPacket> packets =
onReceiveTimeout(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
continue;
}
iNet = ByteBuffer.wrap(buf, 0, packet.getLength());
iApp = ByteBuffer.allocate(1024);
} else {
iNet = ByteBuffer.allocate(0);
iApp = ByteBuffer.allocate(1024);
}
SSLEngineResult r = engine.unwrap(iNet, iApp);
SSLEngineResult.Status rs = r.getStatus();
hs = r.getHandshakeStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// the client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect client maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// bad packet, or the client maximum fragment size
// config does not work?
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Buffer underflow: " +
"incorrect client maximum fragment size");
} // otherwise, ignore this packet
} else if (rs == SSLEngineResult.Status.CLOSED) {
endLoops = true;
} // otherwise, SSLEngineResult.Status.OK:
if (rs != SSLEngineResult.Status.OK) {
continue;
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
List<DatagramPacket> packets =
produceHandshakePackets(engine, peerAddr);
for (DatagramPacket p : packets) {
socket.send(p);
}
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
runDelegatedTasks(engine);
} else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
// OK, time to do application data exchange.
endLoops = true;
} else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
endLoops = true;
}
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Not ready for application data yet");
}
}
// deliver application data
void deliverAppData(SSLEngine engine, DatagramSocket socket,
ByteBuffer appData, SocketAddress peerAddr) throws Exception {
// Note: have not consider the packet loses
List<DatagramPacket> packets =
produceApplicationPackets(engine, appData, peerAddr);
appData.flip();
for (DatagramPacket p : packets) {
socket.send(p);
}
}
// receive application data
void receiveAppData(SSLEngine engine,
DatagramSocket socket, ByteBuffer expectedApp) throws Exception {
int loops = MAX_APP_READ_LOOPS;
while ((serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException(
"Too much loops to receive application data");
}
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
ByteBuffer netBuffer = ByteBuffer.wrap(buf, 0, packet.getLength());
ByteBuffer recBuffer = ByteBuffer.allocate(1024);
SSLEngineResult rs = engine.unwrap(netBuffer, recBuffer);
recBuffer.flip();
if (recBuffer.remaining() != 0) {
printHex("Received application data", recBuffer);
if (!recBuffer.equals(expectedApp)) {
System.out.println("Engine status is " + rs);
throw new Exception("Not the right application data");
}
break;
}
}
}
// produce handshake packets
List<DatagramPacket> produceHandshakePackets(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets = new ArrayList<>();
boolean endLoops = false;
int loops = MAX_HANDSHAKE_LOOPS;
while (!endLoops &&
(serverException == null) && (clientException == null)) {
if (--loops < 0) {
throw new RuntimeException(
"Too much loops to produce handshake packets");
}
ByteBuffer oNet = ByteBuffer.allocate(32768);
ByteBuffer oApp = ByteBuffer.allocate(0);
SSLEngineResult r = engine.wrap(oApp, oNet);
oNet.flip();
SSLEngineResult.Status rs = r.getStatus();
SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// the client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect server maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// bad packet, or the client maximum fragment size
// config does not work?
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
throw new Exception("Buffer underflow: " +
"incorrect server maximum fragment size");
} // otherwise, ignore this packet
} else if (rs == SSLEngineResult.Status.CLOSED) {
throw new Exception("SSLEngine has closed");
} // otherwise, SSLEngineResult.Status.OK
// SSLEngineResult.Status.OK:
if (oNet.hasRemaining()) {
byte[] ba = new byte[oNet.remaining()];
oNet.get(ba);
DatagramPacket packet = createHandshakePacket(ba, socketAddr);
packets.add(packet);
}
boolean endInnerLoop = false;
SSLEngineResult.HandshakeStatus nhs = hs;
while (!endInnerLoop) {
if (nhs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
runDelegatedTasks(engine);
nhs = engine.getHandshakeStatus();
} else if ((nhs == SSLEngineResult.HandshakeStatus.FINISHED) ||
(nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) ||
(nhs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
endInnerLoop = true;
endLoops = true;
} else if (nhs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
endInnerLoop = true;
}
}
}
return packets;
}
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
return new DatagramPacket(ba, ba.length, socketAddr);
}
// produce application packets
List<DatagramPacket> produceApplicationPackets(
SSLEngine engine, ByteBuffer source,
SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets = new ArrayList<>();
ByteBuffer appNet = ByteBuffer.allocate(32768);
SSLEngineResult r = engine.wrap(source, appNet);
appNet.flip();
SSLEngineResult.Status rs = r.getStatus();
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// the client maximum fragment size config does not work?
throw new Exception("Buffer overflow: " +
"incorrect server maximum fragment size");
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
// unlikely
throw new Exception("Buffer underflow during wraping");
} else if (rs == SSLEngineResult.Status.CLOSED) {
throw new Exception("SSLEngine has closed");
} // otherwise, SSLEngineResult.Status.OK
// SSLEngineResult.Status.OK:
if (appNet.hasRemaining()) {
byte[] ba = new byte[appNet.remaining()];
appNet.get(ba);
DatagramPacket packet =
new DatagramPacket(ba, ba.length, socketAddr);
packets.add(packet);
}
return packets;
}
// run delegated tasks
void runDelegatedTasks(SSLEngine engine) throws Exception {
Runnable runnable;
while ((runnable = engine.getDelegatedTask()) != null) {
runnable.run();
}
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
throw new Exception("handshake shouldn't need additional tasks");
}
}
// retransmission if timeout
List<DatagramPacket> onReceiveTimeout(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
return new ArrayList<DatagramPacket>();
} else {
// retransmission of handshake messages
return produceHandshakePackets(engine, socketAddr);
}
}
// get DTSL context
SSLContext getDTLSContext() throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore ts = KeyStore.getInstance("JKS");
char[] passphrase = "passphrase".toCharArray();
ks.load(new FileInputStream(keyFilename), passphrase);
ts.load(new FileInputStream(trustFilename), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslCtx = SSLContext.getInstance("DTLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslCtx;
}
/*
* =============================================================
* The remainder is support stuff to kickstart the testing.
*/
// the server side SocketAddress
volatile static SocketAddress serverSocketAddr = null;
// the client side SocketAddress
volatile static SocketAddress clientSocketAddr = null;
// the server side DatagramSocket instance
volatile static DatagramSocket serverDatagramSocket = null;
// the server side DatagramSocket instance
volatile static DatagramSocket clientDatagramSocket = null;
// get server side SocketAddress object
public SocketAddress getServerSocketAddress() {
return serverSocketAddr;
}
// get client side SocketAddress object
public SocketAddress getClientSocketAddress() {
return clientSocketAddr;
}
// get server side DatagramSocket object
public DatagramSocket getServerDatagramSocket() {
return serverDatagramSocket;
}
// get client side DatagramSocket object
public DatagramSocket getClientDatagramSocket() {
return clientDatagramSocket;
}
// Will the handshaking and application data exchange succeed?
public boolean isGoodJob() {
return true;
}
public final void runTest(DTLSOverDatagram testCase) throws Exception {
serverDatagramSocket = new DatagramSocket();
serverSocketAddr = new InetSocketAddress(
InetAddress.getLocalHost(), serverDatagramSocket.getLocalPort());
clientDatagramSocket = new DatagramSocket();
clientSocketAddr = new InetSocketAddress(
InetAddress.getLocalHost(), clientDatagramSocket.getLocalPort());
ExecutorService pool = Executors.newFixedThreadPool(2);
List<Future<String>> list = new ArrayList<Future<String>>();
try {
list.add(pool.submit(new ServerCallable(testCase))); // server task
list.add(pool.submit(new ClientCallable(testCase))); // client task
} finally {
pool.shutdown();
}
Exception reserved = null;
for (Future<String> fut : list) {
try {
System.out.println(fut.get());
} catch (CancellationException |
InterruptedException | ExecutionException cie) {
if (reserved != null) {
cie.addSuppressed(reserved);
reserved = cie;
} else {
reserved = cie;
}
}
}
if (reserved != null) {
throw reserved;
}
}
final static class ServerCallable implements Callable<String> {
DTLSOverDatagram testCase;
ServerCallable(DTLSOverDatagram testCase) {
this.testCase = testCase;
}
@Override
public String call() throws Exception {
try {
testCase.doServerSide();
} catch (Exception e) {
e.printStackTrace(System.out);
serverException = e;
if (testCase.isGoodJob()) {
throw e;
} else {
return "Well done, server!";
}
} finally {
if (serverDatagramSocket != null) {
serverDatagramSocket.close();
}
}
if (testCase.isGoodJob()) {
return "Well done, server!";
} else {
throw new Exception("No expected exception");
}
}
}
final static class ClientCallable implements Callable<String> {
DTLSOverDatagram testCase;
ClientCallable(DTLSOverDatagram testCase) {
this.testCase = testCase;
}
@Override
public String call() throws Exception {
try {
testCase.doClientSide();
} catch (Exception e) {
e.printStackTrace(System.out);
clientException = e;
if (testCase.isGoodJob()) {
throw e;
} else {
return "Well done, client!";
}
} finally {
if (clientDatagramSocket != null) {
clientDatagramSocket.close();
}
}
if (testCase.isGoodJob()) {
return "Well done, client!";
} else {
throw new Exception("No expected exception");
}
}
}
final 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();
}
}
final 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();
}
}
}

View file

@ -0,0 +1,66 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm InvalidCookie
*/
import java.net.DatagramPacket;
import java.net.SocketAddress;
/**
* Test that if the handshake cookie in client side is incorrect, the handshake
* process can continue as if the client does not use cookie.
*/
public class InvalidCookie extends DTLSOverDatagram {
boolean needInvalidCookie = true;
public static void main(String[] args) throws Exception {
InvalidCookie testCase = new InvalidCookie();
testCase.runTest(testCase);
}
@Override
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
if (needInvalidCookie && (ba.length >= 60) &&
(ba[0] == (byte)0x16) && (ba[13] == (byte)0x03)) {
// HelloVerifyRequest
needInvalidCookie = false;
System.out.println("invalidate handshake verify cookie");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
} else {
ba[ba.length - 1] = (byte)0xFF;
}
}
return super.createHandshakePacket(ba, socketAddr);
}
}

View file

@ -0,0 +1,78 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm InvalidRecords
*/
import java.net.DatagramPacket;
import java.net.SocketAddress;
/**
* Test that if handshake messages are crasged, the handshake would fail
* because of handshaking hash verification.
*/
public class InvalidRecords extends DTLSOverDatagram {
boolean needInvalidRecords = true;
public static void main(String[] args) throws Exception {
InvalidRecords testCase = new InvalidRecords();
testCase.runTest(testCase);
}
@Override
public boolean isGoodJob() {
return false;
}
@Override
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
if (needInvalidRecords && (ba.length >= 60) &&
(ba[0x00] == (byte)0x16) && (ba[0x0D] == (byte)0x01) &&
(ba[0x3B] == (byte)0x00) && (ba[0x3C] > 0)) {
// ba[0x00]: record type
// ba[0x0D]: handshake type
// ba[0x3B]: length of session ID
// ba[0x3C]: length of cookie
// ClientHello with cookie
needInvalidRecords = false;
System.out.println("invalidate ClientHello message");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
} else {
ba[ba.length - 1] = (byte)0xFF;
}
}
return super.createHandshakePacket(ba, socketAddr);
}
}

View file

@ -0,0 +1,65 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm NoMacInitialClientHello
*/
import java.net.DatagramPacket;
import java.net.SocketAddress;
/**
* Test that a server is able to discard invalid initial ClientHello silently.
*/
public class NoMacInitialClientHello extends DTLSOverDatagram {
boolean needInvalidRecords = true;
public static void main(String[] args) throws Exception {
NoMacInitialClientHello testCase = new NoMacInitialClientHello();
testCase.runTest(testCase);
}
@Override
DatagramPacket createHandshakePacket(byte[] ba, SocketAddress socketAddr) {
if (needInvalidRecords && (ba.length >= 60) &&
(ba[0] == (byte)0x16) && (ba[13] == (byte)0x01)) { // ClientHello
needInvalidRecords = false;
System.out.println("invalidate ClientHello message");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
} else {
ba[ba.length - 1] = (byte)0xFF;
}
}
return super.createHandshakePacket(ba, socketAddr);
}
}

View file

@ -0,0 +1,65 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm Reordered
*/
import java.util.List;
import java.util.Collections;
import java.net.DatagramPacket;
import java.net.SocketAddress;
import javax.net.ssl.SSLEngine;
/**
* Test that DTLS implementation is able to reorder data traffic.
*/
public class Reordered extends DTLSOverDatagram {
boolean needPacketReorder = true;
public static void main(String[] args) throws Exception {
Reordered testCase = new Reordered();
testCase.runTest(testCase);
}
@Override
List<DatagramPacket> produceHandshakePackets(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets =
super.produceHandshakePackets(engine, socketAddr);
if (needPacketReorder && (!engine.getUseClientMode())) {
needPacketReorder = false;
Collections.reverse(packets);
}
return packets;
}
}

View file

@ -0,0 +1,79 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm Retransmission
*/
import java.util.List;
import java.util.ArrayList;
import java.net.DatagramPacket;
import java.net.SocketAddress;
import javax.net.ssl.SSLEngine;
/**
* Test that DTLS implementation is able to do retransmission internally
* automatically if packet get lost.
*/
public class Retransmission extends DTLSOverDatagram {
boolean needPacketLoss = true;
public static void main(String[] args) throws Exception {
Retransmission testCase = new Retransmission();
testCase.runTest(testCase);
}
@Override
List<DatagramPacket> produceHandshakePackets(
SSLEngine engine, SocketAddress socketAddr) throws Exception {
List<DatagramPacket> packets =
super.produceHandshakePackets(engine, socketAddr);
if (!needPacketLoss || (!engine.getUseClientMode())) {
return packets;
}
List<DatagramPacket> parts = new ArrayList<>();
int lostSeq = 2;
for (DatagramPacket packet : packets) {
lostSeq--;
if (lostSeq == 0) {
needPacketLoss = false;
// loss this packet
System.out.println("Loss a packet");
continue;
}
parts.add(packet);
}
return parts;
}
}

View file

@ -0,0 +1,69 @@
/*
* 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.
*
* 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.
*/
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
/*
* @test
* @bug 8043758
* @summary Datagram Transport Layer Security (DTLS)
* @compile DTLSOverDatagram.java
* @run main/othervm WeakCipherSuite TLS_DH_anon_WITH_AES_128_GCM_SHA256
* @run main/othervm WeakCipherSuite SSL_DH_anon_WITH_DES_CBC_SHA
* @run main/othervm WeakCipherSuite SSL_RSA_WITH_DES_CBC_SHA
* @run main/othervm WeakCipherSuite SSL_DHE_RSA_WITH_DES_CBC_SHA
* @run main/othervm WeakCipherSuite SSL_DHE_DSS_WITH_DES_CBC_SHA
*/
import javax.net.ssl.SSLEngine;
import java.security.Security;
/**
* Test common DTLS weak cipher suites.
*/
public class WeakCipherSuite extends DTLSOverDatagram {
// use the specific cipher suite
volatile static String cipherSuite;
public static void main(String[] args) throws Exception {
// reset security properties to make sure that the algorithms
// and keys used in this test are not disabled.
Security.setProperty("jdk.tls.disabledAlgorithms", "");
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
cipherSuite = args[0];
WeakCipherSuite testCase = new WeakCipherSuite();
testCase.runTest(testCase);
}
@Override
SSLEngine createSSLEngine(boolean isClient) throws Exception {
SSLEngine engine = super.createSSLEngine(isClient);
engine.setEnabledCipherSuites(new String[]{cipherSuite});
return engine;
}
}

View file

@ -391,7 +391,7 @@ public class CheckStatus {
appOut2.rewind(); appOut2.rewind();
log("======================================"); log("======================================");
log("Client Cert"); log("Client Cert and Key Exchange");
result1 = ssle1.wrap(appOut1, oneToTwo); result1 = ssle1.wrap(appOut1, oneToTwo);
checkResult(appOut1, oneToTwo, result1, checkResult(appOut1, oneToTwo, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1); Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
@ -405,22 +405,6 @@ public class CheckStatus {
oneToTwo.compact(); oneToTwo.compact();
log("======================================");
log("Key Exchange");
result1 = ssle1.wrap(appOut1, oneToTwo);
checkResult(appOut1, oneToTwo, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
oneToTwo.flip();
result2 = ssle2.unwrap(oneToTwo, appIn2);
checkResult(oneToTwo, appIn2, result2,
Status.OK, HandshakeStatus.NEED_TASK,
result1.bytesProduced(), 0);
runDelegatedTasks(ssle2);
oneToTwo.compact();
log("======================================"); log("======================================");
log("CCS"); log("CCS");
result1 = ssle1.wrap(appOut1, oneToTwo); result1 = ssle1.wrap(appOut1, oneToTwo);

View file

@ -116,7 +116,7 @@ public class LargeBufs {
if ((result2.bytesConsumed() != 0) && if ((result2.bytesConsumed() != 0) &&
(result2.bytesConsumed() != appBufferMax) && (result2.bytesConsumed() != appBufferMax) &&
(result2.bytesConsumed() != 2 * OFFSET)) { (result2.bytesConsumed() != 2 * OFFSET)) {
throw new Exception("result1: " + result1); throw new Exception("result2: " + result2);
} }
log("wrap1: " + result1); log("wrap1: " + result1);

View file

@ -406,6 +406,7 @@ public class CipherTestUtils {
return params; return params;
}).forEach((params) -> { }).forEach((params) -> {
try { try {
System.out.println("Testing " + params);
runTest(params); runTest(params);
System.out.println("Passed " + params); System.out.println("Passed " + params);
} catch (Exception e) { } catch (Exception e) {

View file

@ -23,15 +23,16 @@
* questions. * questions.
*/ */
//
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
//
/* /*
* @test * @test
* @bug 4873188 * @bug 4873188
* @summary Support TLS 1.1 * @summary Support TLS 1.1
* @run main/othervm ExportableBlockCipher * @run main/othervm ExportableBlockCipher
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.
*
* @author Xuelei Fan * @author Xuelei Fan
*/ */
@ -109,7 +110,7 @@ public class ExportableBlockCipher {
sslIS.read(); sslIS.read();
sslOS.write('A'); sslOS.write('A');
sslOS.flush(); sslOS.flush();
} catch (SSLException ssle) { } catch (IOException ioe) {
// get the expected exception // get the expected exception
interrupted = true; interrupted = true;
} finally { } finally {

View file

@ -23,15 +23,16 @@
* questions. * questions.
*/ */
//
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
//
/* /*
* @test * @test
* @bug 4873188 * @bug 4873188
* @summary Support TLS 1.1 * @summary Support TLS 1.1
* @run main/othervm ExportableStreamCipher * @run main/othervm ExportableStreamCipher
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.
*
* @author Xuelei Fan * @author Xuelei Fan
*/ */
@ -109,7 +110,7 @@ public class ExportableStreamCipher {
sslIS.read(); sslIS.read();
sslOS.write('A'); sslOS.write('A');
sslOS.flush(); sslOS.flush();
} catch (SSLException ssle) { } catch (IOException ioe) {
// get the expected exception // get the expected exception
interrupted = true; interrupted = true;
} finally { } finally {

View file

@ -139,6 +139,11 @@ public class SSLSocketSSLEngineTemplate {
* Main entry point for this test. * Main entry point for this test.
*/ */
public static void main(String args[]) throws Exception { public static void main(String args[]) throws Exception {
// reset security properties to make sure that the algorithms
// and keys used in this test are not disabled.
Security.setProperty("jdk.tls.disabledAlgorithms", "");
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
if (debug) { if (debug) {
System.setProperty("javax.net.debug", "all"); System.setProperty("javax.net.debug", "all");
} }
@ -153,7 +158,12 @@ public class SSLSocketSSLEngineTemplate {
*/ */
SSLSocketSSLEngineTemplate test = SSLSocketSSLEngineTemplate test =
new SSLSocketSSLEngineTemplate(protocol); new SSLSocketSSLEngineTemplate(protocol);
log("-------------------------------------");
log("Testing " + protocol + " for direct buffers ...");
test.runTest(true); test.runTest(true);
log("---------------------------------------");
log("Testing " + protocol + " for indirect buffers ...");
test.runTest(false); test.runTest(false);
} }
@ -329,6 +339,10 @@ public class SSLSocketSSLEngineTemplate {
thread.join(); thread.join();
} }
if (sslSocket != null) {
sslSocket.close();
}
if (serverException != null) { if (serverException != null) {
if (clientException != null) { if (clientException != null) {
serverException.initCause(clientException); serverException.initCause(clientException);

View file

@ -21,19 +21,22 @@
* questions. * questions.
*/ */
//
// SunJSSE does not support dynamic system properties, no way to re-use
// system properties in samevm/agentvm mode.
//
/* /*
* @test * @test
* @bug 4514971 * @bug 4514971
* @summary Verify applications do not read handshake data after failure * @summary Verify applications do not read handshake data after failure
* @run main/othervm ReadHandshake * @run main/othervm ReadHandshake
*
* SunJSSE does not support dynamic system properties, no way to re-use
* system properties in samevm/agentvm mode.
*/ */
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import javax.net.ssl.*; import javax.net.ssl.*;
import java.security.Security;
public class ReadHandshake { public class ReadHandshake {
@ -219,6 +222,10 @@ public class ReadHandshake {
volatile Exception clientException = null; volatile Exception clientException = null;
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
// reset security properties to make sure that the algorithms
// and keys used in this test are not disabled.
Security.setProperty("jdk.tls.disabledAlgorithms", "");
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
if (debug) if (debug)
System.setProperty("javax.net.debug", "all"); System.setProperty("javax.net.debug", "all");

View file

@ -233,7 +233,7 @@ public class LengthCheckTest {
// sent back to the server. // sent back to the server.
if (gotException == false || if (gotException == false ||
!isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL, !isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
TLS_ALERT_INTERNAL_ERROR)) { TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException( throw new SSLException(
"Client failed to throw Alert:fatal:internal_error"); "Client failed to throw Alert:fatal:internal_error");
} }
@ -285,7 +285,7 @@ public class LengthCheckTest {
// sent back to the client. // sent back to the client.
if (gotException == false || if (gotException == false ||
!isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL, !isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,
TLS_ALERT_INTERNAL_ERROR)) { TLS_ALERT_UNEXPECTED_MSG)) {
throw new SSLException( throw new SSLException(
"Server failed to throw Alert:fatal:internal_error"); "Server failed to throw Alert:fatal:internal_error");
} }