src/java.base/share/classes/sun/security/ssl/NewSessionTicket.java
branchdatagramsocketimpl-branch
changeset 58678 9cf78a70fa4f
parent 54253 01d8eae542ff
child 58679 9c3209ff7550
equal deleted inserted replaced
58677:13588c901957 58678:9cf78a70fa4f
    26 
    26 
    27 import java.io.IOException;
    27 import java.io.IOException;
    28 import java.math.BigInteger;
    28 import java.math.BigInteger;
    29 import java.nio.ByteBuffer;
    29 import java.nio.ByteBuffer;
    30 import java.security.GeneralSecurityException;
    30 import java.security.GeneralSecurityException;
    31 import java.security.ProviderException;
       
    32 import java.security.SecureRandom;
    31 import java.security.SecureRandom;
    33 import java.text.MessageFormat;
    32 import java.text.MessageFormat;
    34 import java.util.Locale;
    33 import java.util.Locale;
    35 import javax.crypto.SecretKey;
    34 import javax.crypto.SecretKey;
    36 import javax.net.ssl.SSLHandshakeException;
    35 import javax.net.ssl.SSLHandshakeException;
    37 import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
    36 import sun.security.ssl.PskKeyExchangeModesExtension.PskKeyExchangeModesSpec;
    38 
    37 import sun.security.ssl.SessionTicketExtension.SessionTicketSpec;
    39 import sun.security.ssl.SSLHandshake.HandshakeMessage;
    38 import sun.security.ssl.SSLHandshake.HandshakeMessage;
       
    39 import sun.security.util.HexDumpEncoder;
       
    40 
       
    41 import static sun.security.ssl.SSLHandshake.NEW_SESSION_TICKET;
    40 
    42 
    41 /**
    43 /**
    42  * Pack of the NewSessionTicket handshake message.
    44  * Pack of the NewSessionTicket handshake message.
    43  */
    45  */
    44 final class NewSessionTicket {
    46 final class NewSessionTicket {
    45     private static final int MAX_TICKET_LIFETIME = 604800;  // seconds, 7 days
    47     static final int MAX_TICKET_LIFETIME = 604800;  // seconds, 7 days
    46 
       
    47     static final SSLConsumer handshakeConsumer =
    48     static final SSLConsumer handshakeConsumer =
    48         new NewSessionTicketConsumer();
    49         new T13NewSessionTicketConsumer();
       
    50     static final SSLConsumer handshake12Consumer =
       
    51         new T12NewSessionTicketConsumer();
    49     static final SSLProducer kickstartProducer =
    52     static final SSLProducer kickstartProducer =
    50         new NewSessionTicketKickstartProducer();
    53         new NewSessionTicketKickstartProducer();
    51     static final HandshakeProducer handshakeProducer =
    54     static final HandshakeProducer handshake12Producer =
    52         new NewSessionTicketProducer();
    55         new T12NewSessionTicketProducer();
    53 
    56 
    54     /**
    57     /**
    55      * The NewSessionTicketMessage handshake message.
    58      * The NewSessionTicketMessage handshake messages.
    56      */
    59      */
    57     static final class NewSessionTicketMessage extends HandshakeMessage {
    60     abstract static class NewSessionTicketMessage extends HandshakeMessage {
    58         final int ticketLifetime;
    61         int ticketLifetime;
    59         final int ticketAgeAdd;
    62         byte[] ticket = new byte[0];
    60         final byte[] ticketNonce;
    63 
    61         final byte[] ticket;
    64         NewSessionTicketMessage(HandshakeContext context) {
    62         final SSLExtensions extensions;
    65             super(context);
    63 
    66         }
    64         NewSessionTicketMessage(HandshakeContext context,
    67 
       
    68         @Override
       
    69         public SSLHandshake handshakeType() {
       
    70             return NEW_SESSION_TICKET;
       
    71         }
       
    72 
       
    73         // For TLS 1.3 only
       
    74         int getTicketAgeAdd() throws IOException {
       
    75             throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
    76                     "TicketAgeAdd not part of RFC 5077.");
       
    77         }
       
    78 
       
    79         // For TLS 1.3 only
       
    80         byte[] getTicketNonce() throws IOException {
       
    81             throw handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
    82                     "TicketNonce not part of RFC 5077.");
       
    83         }
       
    84 
       
    85         boolean isValid() {
       
    86             return (ticket.length > 0);
       
    87         }
       
    88     }
       
    89     /**
       
    90      * NewSessionTicket for TLS 1.2 and below (RFC 5077)
       
    91      */
       
    92     static final class T12NewSessionTicketMessage extends NewSessionTicketMessage {
       
    93 
       
    94         T12NewSessionTicketMessage(HandshakeContext context,
       
    95                 int ticketLifetime, byte[] ticket) {
       
    96             super(context);
       
    97 
       
    98             this.ticketLifetime = ticketLifetime;
       
    99             this.ticket = ticket;
       
   100         }
       
   101 
       
   102         T12NewSessionTicketMessage(HandshakeContext context,
       
   103                 ByteBuffer m) throws IOException {
       
   104 
       
   105             // RFC5077 struct {
       
   106             //     uint32 ticket_lifetime;
       
   107             //     opaque ticket<0..2^16-1>;
       
   108             // } NewSessionTicket;
       
   109 
       
   110             super(context);
       
   111             if (m.remaining() < 6) {
       
   112                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
       
   113                     "Invalid NewSessionTicket message: insufficient data");
       
   114             }
       
   115 
       
   116             this.ticketLifetime = Record.getInt32(m);
       
   117             this.ticket = Record.getBytes16(m);
       
   118         }
       
   119 
       
   120         @Override
       
   121         public SSLHandshake handshakeType() {
       
   122             return NEW_SESSION_TICKET;
       
   123         }
       
   124 
       
   125         @Override
       
   126         public int messageLength() {
       
   127             return 4 + // ticketLifetime
       
   128                     2 + ticket.length;  // len of ticket + ticket
       
   129         }
       
   130 
       
   131         @Override
       
   132         public void send(HandshakeOutStream hos) throws IOException {
       
   133             hos.putInt32(ticketLifetime);
       
   134             hos.putBytes16(ticket);
       
   135         }
       
   136 
       
   137         @Override
       
   138         public String toString() {
       
   139             MessageFormat messageFormat = new MessageFormat(
       
   140                     "\"NewSessionTicket\": '{'\n" +
       
   141                             "  \"ticket_lifetime\"      : \"{0}\",\n" +
       
   142                             "  \"ticket\"               : '{'\n" +
       
   143                             "{1}\n" +
       
   144                             "  '}'" +
       
   145                             "'}'",
       
   146                 Locale.ENGLISH);
       
   147 
       
   148             HexDumpEncoder hexEncoder = new HexDumpEncoder();
       
   149             Object[] messageFields = {
       
   150                     ticketLifetime,
       
   151                     Utilities.indent(hexEncoder.encode(ticket), "    "),
       
   152             };
       
   153             return messageFormat.format(messageFields);
       
   154         }
       
   155     }
       
   156 
       
   157     /**
       
   158      * NewSessionTicket defined by the TLS 1.3
       
   159      */
       
   160     static final class T13NewSessionTicketMessage extends NewSessionTicketMessage {
       
   161         int ticketAgeAdd;
       
   162         byte[] ticketNonce;
       
   163         SSLExtensions extensions;
       
   164 
       
   165         T13NewSessionTicketMessage(HandshakeContext context,
    65                 int ticketLifetime, SecureRandom generator,
   166                 int ticketLifetime, SecureRandom generator,
    66                 byte[] ticketNonce, byte[] ticket) {
   167                 byte[] ticketNonce, byte[] ticket) {
    67             super(context);
   168             super(context);
    68 
   169 
    69             this.ticketLifetime = ticketLifetime;
   170             this.ticketLifetime = ticketLifetime;
    71             this.ticketNonce = ticketNonce;
   172             this.ticketNonce = ticketNonce;
    72             this.ticket = ticket;
   173             this.ticket = ticket;
    73             this.extensions = new SSLExtensions(this);
   174             this.extensions = new SSLExtensions(this);
    74         }
   175         }
    75 
   176 
    76         NewSessionTicketMessage(HandshakeContext context,
   177         T13NewSessionTicketMessage(HandshakeContext context,
    77                 ByteBuffer m) throws IOException {
   178                 ByteBuffer m) throws IOException {
    78             super(context);
   179             super(context);
    79 
   180 
    80             // struct {
   181             // struct {
    81             //     uint32 ticket_lifetime;
   182             //     uint32 ticket_lifetime;
    82             //     uint32 ticket_age_add;
   183             //     uint32 ticket_age_add;
    83             //     opaque ticket_nonce<0..255>;
   184             //     opaque ticket_nonce<0..255>;
    84             //     opaque ticket<1..2^16-1>;
   185             //     opaque ticket<1..2^16-1>;
    85             //     Extension extensions<0..2^16-2>;
   186             //     Extension extensions<0..2^16-2>;
    86             // } NewSessionTicket;
   187             // } NewSessionTicket;
       
   188 
    87             if (m.remaining() < 14) {
   189             if (m.remaining() < 14) {
    88                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
   190                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
    89                     "Invalid NewSessionTicket message: no sufficient data");
   191                     "Invalid NewSessionTicket message: insufficient data");
    90             }
   192             }
    91 
   193 
    92             this.ticketLifetime = Record.getInt32(m);
   194             this.ticketLifetime = Record.getInt32(m);
    93             this.ticketAgeAdd = Record.getInt32(m);
   195             this.ticketAgeAdd = Record.getInt32(m);
    94             this.ticketNonce = Record.getBytes8(m);
   196             this.ticketNonce = Record.getBytes8(m);
    95 
   197 
    96             if (m.remaining() < 5) {
   198             if (m.remaining() < 5) {
    97                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
   199                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
    98                     "Invalid NewSessionTicket message: no sufficient data");
   200                     "Invalid NewSessionTicket message: insufficient ticket" +
       
   201                             " data");
    99             }
   202             }
   100 
   203 
   101             this.ticket = Record.getBytes16(m);
   204             this.ticket = Record.getBytes16(m);
   102             if (ticket.length == 0) {
   205             if (ticket.length == 0) {
   103                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
   206                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   207                     SSLLogger.fine(
   104                     "No ticket in the NewSessionTicket handshake message");
   208                     "No ticket in the NewSessionTicket handshake message");
       
   209                 }
   105             }
   210             }
   106 
   211 
   107             if (m.remaining() < 2) {
   212             if (m.remaining() < 2) {
   108                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
   213                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
   109                     "Invalid NewSessionTicket message: no sufficient data");
   214                     "Invalid NewSessionTicket message: extra data");
   110             }
   215             }
   111 
   216 
   112             SSLExtension[] supportedExtensions =
   217             SSLExtension[] supportedExtensions =
   113                     context.sslConfig.getEnabledExtensions(
   218                     context.sslConfig.getEnabledExtensions(
   114                             SSLHandshake.NEW_SESSION_TICKET);
   219                             NEW_SESSION_TICKET);
   115             this.extensions = new SSLExtensions(this, m, supportedExtensions);
   220             this.extensions = new SSLExtensions(this, m, supportedExtensions);
   116         }
   221         }
   117 
   222 
   118         @Override
   223         @Override
   119         public SSLHandshake handshakeType() {
   224         public SSLHandshake handshakeType() {
   120             return SSLHandshake.NEW_SESSION_TICKET;
   225             return NEW_SESSION_TICKET;
       
   226         }
       
   227 
       
   228         int getTicketAgeAdd() {
       
   229             return ticketAgeAdd;
       
   230         }
       
   231 
       
   232         byte[] getTicketNonce() {
       
   233             return ticketNonce;
   121         }
   234         }
   122 
   235 
   123         @Override
   236         @Override
   124         public int messageLength() {
   237         public int messageLength() {
       
   238 
   125             int extLen = extensions.length();
   239             int extLen = extensions.length();
   126             if (extLen == 0) {
   240             if (extLen == 0) {
   127                 extLen = 2;     // empty extensions
   241                 extLen = 2;     // empty extensions
   128             }
   242             }
   129 
   243 
   130             return 8 + ticketNonce.length + 1 +
   244             return 4 +// ticketLifetime
   131                        ticket.length + 2 + extLen;
   245                     4 + // ticketAgeAdd
       
   246                     1 + ticketNonce.length + // len of nonce + nonce
       
   247                     2 + ticket.length + // len of ticket + ticket
       
   248                     extLen;
   132         }
   249         }
   133 
   250 
   134         @Override
   251         @Override
   135         public void send(HandshakeOutStream hos) throws IOException {
   252         public void send(HandshakeOutStream hos) throws IOException {
   136             hos.putInt32(ticketLifetime);
   253             hos.putInt32(ticketLifetime);
   151             MessageFormat messageFormat = new MessageFormat(
   268             MessageFormat messageFormat = new MessageFormat(
   152                 "\"NewSessionTicket\": '{'\n" +
   269                 "\"NewSessionTicket\": '{'\n" +
   153                 "  \"ticket_lifetime\"      : \"{0}\",\n" +
   270                 "  \"ticket_lifetime\"      : \"{0}\",\n" +
   154                 "  \"ticket_age_add\"       : \"{1}\",\n" +
   271                 "  \"ticket_age_add\"       : \"{1}\",\n" +
   155                 "  \"ticket_nonce\"         : \"{2}\",\n" +
   272                 "  \"ticket_nonce\"         : \"{2}\",\n" +
   156                 "  \"ticket\"               : \"{3}\",\n" +
   273                 "  \"ticket\"               : '{'\n" +
       
   274                 "{3}\n" +
       
   275                 "  '}'" +
   157                 "  \"extensions\"           : [\n" +
   276                 "  \"extensions\"           : [\n" +
   158                 "{4}\n" +
   277                 "{4}\n" +
   159                 "  ]\n" +
   278                 "  ]\n" +
   160                 "'}'",
   279                 "'}'",
   161                 Locale.ENGLISH);
   280                 Locale.ENGLISH);
   162 
   281 
       
   282             HexDumpEncoder hexEncoder = new HexDumpEncoder();
   163             Object[] messageFields = {
   283             Object[] messageFields = {
   164                 ticketLifetime,
   284                 ticketLifetime,
   165                 "<omitted>",    //ticketAgeAdd should not be logged
   285                 "<omitted>",    //ticketAgeAdd should not be logged
   166                 Utilities.toHexString(ticketNonce),
   286                 Utilities.toHexString(ticketNonce),
   167                 Utilities.toHexString(ticket),
   287                 Utilities.indent(hexEncoder.encode(ticket), "    "),
   168                 Utilities.indent(extensions.toString(), "    ")
   288                 Utilities.indent(extensions.toString(), "    ")
   169             };
   289             };
   170 
   290 
   171             return messageFormat.format(messageFields);
   291             return messageFormat.format(messageFields);
   172         }
   292         }
   193             // blank
   313             // blank
   194         }
   314         }
   195 
   315 
   196         @Override
   316         @Override
   197         public byte[] produce(ConnectionContext context) throws IOException {
   317         public byte[] produce(ConnectionContext context) throws IOException {
       
   318             HandshakeContext hc = (HandshakeContext)context;
       
   319 
   198             // The producing happens in server side only.
   320             // The producing happens in server side only.
       
   321             if (hc instanceof ServerHandshakeContext) {
       
   322                 // Is this session resumable?
       
   323                 if (!hc.handshakeSession.isRejoinable()) {
       
   324                     return null;
       
   325                 }
       
   326 
       
   327                 // What's the requested PSK key exchange modes?
       
   328                 //
       
   329                 // Note that currently, the NewSessionTicket post-handshake is
       
   330                 // produced and delivered only in the current handshake context
       
   331                 // if required.
       
   332                 PskKeyExchangeModesSpec pkemSpec =
       
   333                         (PskKeyExchangeModesSpec) hc.handshakeExtensions.get(
       
   334                                 SSLExtension.PSK_KEY_EXCHANGE_MODES);
       
   335                 if (pkemSpec == null || !pkemSpec.contains(
       
   336                         PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
       
   337                     // Client doesn't support PSK with (EC)DHE key establishment.
       
   338                     return null;
       
   339                 }
       
   340             } else { // PostHandshakeContext
       
   341 
       
   342                 // Check if we have sent a PSK already, then we know it is using a
       
   343                 // allowable PSK exchange key mode
       
   344                 if (!hc.handshakeSession.isPSKable()) {
       
   345                     return null;
       
   346                 }
       
   347             }
       
   348 
       
   349             // get a new session ID
       
   350             SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
       
   351                 hc.sslContext.engineGetServerSessionContext();
       
   352             SessionId newId = new SessionId(true,
       
   353                 hc.sslContext.getSecureRandom());
       
   354 
       
   355             SecretKey resumptionMasterSecret =
       
   356                 hc.handshakeSession.getResumptionMasterSecret();
       
   357             if (resumptionMasterSecret == null) {
       
   358                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   359                     SSLLogger.fine(
       
   360                         "Session has no resumption secret. No ticket sent.");
       
   361                 }
       
   362                 return null;
       
   363             }
       
   364 
       
   365             // construct the PSK and handshake message
       
   366             BigInteger nonce = hc.handshakeSession.incrTicketNonceCounter();
       
   367             byte[] nonceArr = nonce.toByteArray();
       
   368             SecretKey psk = derivePreSharedKey(
       
   369                     hc.negotiatedCipherSuite.hashAlg,
       
   370                     resumptionMasterSecret, nonceArr);
       
   371 
       
   372             int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
       
   373             if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) {
       
   374                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   375                     SSLLogger.fine(
       
   376                         "Session timeout is too long. No ticket sent.");
       
   377                 }
       
   378                 return null;
       
   379             }
       
   380 
       
   381             NewSessionTicketMessage nstm = null;
       
   382 
       
   383             SSLSessionImpl sessionCopy =
       
   384                     new SSLSessionImpl(hc.handshakeSession, newId);
       
   385             sessionCopy.setPreSharedKey(psk);
       
   386             sessionCopy.setPskIdentity(newId.getId());
       
   387 
       
   388             // If a stateless ticket is allowed, attempt to make one
       
   389             if (hc.handshakeSession.isStatelessable(hc)) {
       
   390                 nstm = new T13NewSessionTicketMessage(hc,
       
   391                         sessionTimeoutSeconds,
       
   392                         hc.sslContext.getSecureRandom(),
       
   393                         nonceArr,
       
   394                         new SessionTicketSpec().encrypt(hc, sessionCopy));
       
   395                 // If ticket construction failed, switch to session cache
       
   396                 if (!nstm.isValid()) {
       
   397                     hc.statelessResumption = false;
       
   398                 } else {
       
   399                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   400                         SSLLogger.fine(
       
   401                                 "Produced NewSessionTicket stateless " +
       
   402                                         "handshake message", nstm);
       
   403                     }
       
   404                 }
       
   405             }
       
   406             // If a session cache ticket is being used, make one
       
   407             if (!hc.handshakeSession.isStatelessable(hc)) {
       
   408                 nstm = new T13NewSessionTicketMessage(hc, sessionTimeoutSeconds,
       
   409                         hc.sslContext.getSecureRandom(), nonceArr,
       
   410                         newId.getId());
       
   411                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   412                     SSLLogger.fine(
       
   413                             "Produced NewSessionTicket handshake message",
       
   414                             nstm);
       
   415                 }
       
   416 
       
   417                 // create and cache the new session
       
   418                 // The new session must be a child of the existing session so
       
   419                 // they will be invalidated together, etc.
       
   420                 hc.handshakeSession.addChild(sessionCopy);
       
   421                 sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
       
   422                 sessionCache.put(sessionCopy);
       
   423             }
       
   424 
       
   425             // Output the handshake message.
       
   426             if (nstm != null) {
       
   427                 // should never be null
       
   428                 nstm.write(hc.handshakeOutput);
       
   429                 hc.handshakeOutput.flush();
       
   430             }
       
   431 
       
   432             if (hc instanceof PostHandshakeContext) {
       
   433                 ((PostHandshakeContext) hc).finish();
       
   434             }
       
   435 
       
   436             // The message has been delivered.
       
   437             return null;
       
   438         }
       
   439     }
       
   440 
       
   441     /**
       
   442      * The "NewSessionTicket" handshake message producer for RFC 5077
       
   443      */
       
   444     private static final class T12NewSessionTicketProducer
       
   445             implements HandshakeProducer {
       
   446 
       
   447         // Prevent instantiation of this class.
       
   448         private T12NewSessionTicketProducer() {
       
   449             // blank
       
   450         }
       
   451 
       
   452         @Override
       
   453         public byte[] produce(ConnectionContext context,
       
   454                 HandshakeMessage message) throws IOException {
       
   455 
   199             ServerHandshakeContext shc = (ServerHandshakeContext)context;
   456             ServerHandshakeContext shc = (ServerHandshakeContext)context;
   200 
   457 
   201             // Is this session resumable?
   458             // Is this session resumable?
   202             if (!shc.handshakeSession.isRejoinable()) {
   459             if (!shc.handshakeSession.isRejoinable()) {
   203                 return null;
   460                 return null;
   204             }
   461             }
   205 
   462 
   206             // What's the requested PSK key exchange modes?
       
   207             //
       
   208             // Note that currently, the NewSessionTicket post-handshake is
       
   209             // produced and delivered only in the current handshake context
       
   210             // if required.
       
   211             PskKeyExchangeModesSpec pkemSpec =
       
   212                     (PskKeyExchangeModesSpec)shc.handshakeExtensions.get(
       
   213                             SSLExtension.PSK_KEY_EXCHANGE_MODES);
       
   214             if (pkemSpec == null || !pkemSpec.contains(
       
   215                 PskKeyExchangeModesExtension.PskKeyExchangeMode.PSK_DHE_KE)) {
       
   216                 // Client doesn't support PSK with (EC)DHE key establishment.
       
   217                 return null;
       
   218             }
       
   219 
       
   220             // get a new session ID
   463             // get a new session ID
       
   464             SessionId newId = shc.handshakeSession.getSessionId();
       
   465 
   221             SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
   466             SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
   222                 shc.sslContext.engineGetServerSessionContext();
   467                     shc.sslContext.engineGetServerSessionContext();
   223             SessionId newId = new SessionId(true,
       
   224                 shc.sslContext.getSecureRandom());
       
   225 
       
   226             SecretKey resumptionMasterSecret =
       
   227                 shc.handshakeSession.getResumptionMasterSecret();
       
   228             if (resumptionMasterSecret == null) {
       
   229                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   230                     SSLLogger.fine(
       
   231                         "Session has no resumption secret. No ticket sent.");
       
   232                 }
       
   233                 return null;
       
   234             }
       
   235 
       
   236             // construct the PSK and handshake message
       
   237             BigInteger nonce = shc.handshakeSession.incrTicketNonceCounter();
       
   238             byte[] nonceArr = nonce.toByteArray();
       
   239             SecretKey psk = derivePreSharedKey(
       
   240                     shc.negotiatedCipherSuite.hashAlg,
       
   241                     resumptionMasterSecret, nonceArr);
       
   242 
       
   243             int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
   468             int sessionTimeoutSeconds = sessionCache.getSessionTimeout();
   244             if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) {
   469             if (sessionTimeoutSeconds > MAX_TICKET_LIFETIME) {
   245                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   470                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   246                     SSLLogger.fine(
   471                     SSLLogger.fine(
   247                         "Session timeout is too long. No ticket sent.");
   472                         "Session timeout is too long. No ticket sent.");
   248                 }
   473                 }
   249                 return null;
   474                 return null;
   250             }
   475             }
   251             NewSessionTicketMessage nstm = new NewSessionTicketMessage(shc,
   476 
   252                 sessionTimeoutSeconds, shc.sslContext.getSecureRandom(),
   477             SSLSessionImpl sessionCopy =
   253                 nonceArr, newId.getId());
   478                     new SSLSessionImpl(shc.handshakeSession, newId);
       
   479             sessionCopy.setPskIdentity(newId.getId());
       
   480 
       
   481             NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(shc,
       
   482                     sessionTimeoutSeconds,
       
   483                     new SessionTicketSpec().encrypt(shc, sessionCopy));
   254             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   484             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   255                 SSLLogger.fine(
   485                 SSLLogger.fine(
   256                         "Produced NewSessionTicket handshake message", nstm);
   486                         "Produced NewSessionTicket stateless handshake message", nstm);
   257             }
   487             }
   258 
       
   259             // create and cache the new session
       
   260             // The new session must be a child of the existing session so
       
   261             // they will be invalidated together, etc.
       
   262             SSLSessionImpl sessionCopy =
       
   263                     new SSLSessionImpl(shc.handshakeSession, newId);
       
   264             shc.handshakeSession.addChild(sessionCopy);
       
   265             sessionCopy.setPreSharedKey(psk);
       
   266             sessionCopy.setPskIdentity(newId.getId());
       
   267             sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
       
   268             sessionCache.put(sessionCopy);
       
   269 
   488 
   270             // Output the handshake message.
   489             // Output the handshake message.
   271             nstm.write(shc.handshakeOutput);
   490             nstm.write(shc.handshakeOutput);
   272             shc.handshakeOutput.flush();
   491             shc.handshakeOutput.flush();
   273 
   492 
   274             // The message has been delivered.
   493             // The message has been delivered.
   275             return null;
   494             return null;
   276         }
   495         }
   277     }
   496     }
   278 
   497 
   279     /**
   498     private static final
   280      * The "NewSessionTicket" handshake message producer.
   499     class T13NewSessionTicketConsumer implements SSLConsumer {
   281      */
       
   282     private static final class NewSessionTicketProducer
       
   283             implements HandshakeProducer {
       
   284 
       
   285         // Prevent instantiation of this class.
   500         // Prevent instantiation of this class.
   286         private NewSessionTicketProducer() {
   501         private T13NewSessionTicketConsumer() {
   287             // blank
   502             // blank
   288         }
   503         }
   289 
   504 
   290         @Override
   505         @Override
   291         public byte[] produce(ConnectionContext context,
       
   292                 HandshakeMessage message) throws IOException {
       
   293 
       
   294             // NSTM may be sent in response to handshake messages.
       
   295             // For example: key update
       
   296 
       
   297             throw new ProviderException(
       
   298                 "NewSessionTicket handshake producer not implemented");
       
   299         }
       
   300     }
       
   301 
       
   302     private static final
       
   303             class NewSessionTicketConsumer implements SSLConsumer {
       
   304         // Prevent instantiation of this class.
       
   305         private NewSessionTicketConsumer() {
       
   306             // blank
       
   307         }
       
   308 
       
   309         @Override
       
   310         public void consume(ConnectionContext context,
   506         public void consume(ConnectionContext context,
   311                             ByteBuffer message) throws IOException {
   507                 ByteBuffer message) throws IOException {
   312 
   508 
   313             // Note: Although the resumption master secret depends on the
   509             // Note: Although the resumption master secret depends on the
   314             // client's second flight, servers which do not request client
   510             // client's second flight, servers which do not request client
   315             // authentication MAY compute the remainder of the transcript
   511             // authentication MAY compute the remainder of the transcript
   316             // independently and then send a NewSessionTicket immediately
   512             // independently and then send a NewSessionTicket immediately
   317             // upon sending its Finished rather than waiting for the client
   513             // upon sending its Finished rather than waiting for the client
   318             // Finished.
   514             // Finished.
   319             //
   515             //
   320             // The consuming happens in client side only.  As the server
   516             // The consuming happens in client side only and is received after
   321             // may send the NewSessionTicket before handshake complete, the
   517             // the server's Finished message with PostHandshakeContext.
   322             // context may be a PostHandshakeContext or HandshakeContext
   518 
   323             // instance.
       
   324             HandshakeContext hc = (HandshakeContext)context;
   519             HandshakeContext hc = (HandshakeContext)context;
   325             NewSessionTicketMessage nstm =
   520             NewSessionTicketMessage nstm =
   326                     new NewSessionTicketMessage(hc, message);
   521                     new T13NewSessionTicketMessage(hc, message);
   327             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   522             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   328                 SSLLogger.fine(
   523                 SSLLogger.fine(
   329                 "Consuming NewSessionTicket message", nstm);
   524                 "Consuming NewSessionTicket message", nstm);
   330             }
   525             }
   331 
   526 
       
   527             SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
       
   528                     hc.sslContext.engineGetClientSessionContext();
       
   529 
   332             // discard tickets with timeout 0
   530             // discard tickets with timeout 0
   333             if (nstm.ticketLifetime <= 0 ||
   531             if (nstm.ticketLifetime <= 0 ||
   334                 nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
   532                 nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
   335                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   533                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   336                     SSLLogger.fine(
   534                     SSLLogger.fine(
   337                     "Discarding NewSessionTicket with lifetime "
   535                     "Discarding NewSessionTicket with lifetime "
   338                         + nstm.ticketLifetime, nstm);
   536                         + nstm.ticketLifetime, nstm);
   339                 }
   537                 }
       
   538                 sessionCache.remove(hc.handshakeSession.getSessionId());
   340                 return;
   539                 return;
   341             }
   540             }
   342 
   541 
   343             SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
       
   344                 hc.sslContext.engineGetClientSessionContext();
       
   345 
       
   346             if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
   542             if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
   347                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   543                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   348                     SSLLogger.fine(
   544                     SSLLogger.fine(
   349                     "Session cache lifetime is too long. Discarding ticket.");
   545                     "Session cache lifetime is too long. Discarding ticket.");
   350                 }
   546                 }
   351                 return;
   547                 return;
   352             }
   548             }
   353 
   549 
   354             SSLSessionImpl sessionToSave = hc.conContext.conSession;
   550             SSLSessionImpl sessionToSave = hc.conContext.conSession;
   355 
   551             SecretKey psk = null;
   356             SecretKey resumptionMasterSecret =
   552             if (hc.negotiatedProtocol.useTLS13PlusSpec()) {
   357                 sessionToSave.getResumptionMasterSecret();
   553                 SecretKey resumptionMasterSecret =
   358             if (resumptionMasterSecret == null) {
   554                         sessionToSave.getResumptionMasterSecret();
   359                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   555                 if (resumptionMasterSecret == null) {
   360                     SSLLogger.fine(
   556                     if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
   361                     "Session has no resumption master secret. Ignoring ticket.");
   557                         SSLLogger.fine(
   362                 }
   558                                 "Session has no resumption master secret." +
   363                 return;
   559                                         " Ignoring ticket.");
   364             }
   560                     }
   365 
   561                     return;
   366             // derive the PSK
   562                 }
   367             SecretKey psk = derivePreSharedKey(
   563 
   368                 sessionToSave.getSuite().hashAlg, resumptionMasterSecret,
   564                 // derive the PSK
   369                 nstm.ticketNonce);
   565                 psk = derivePreSharedKey(
       
   566                         sessionToSave.getSuite().hashAlg,
       
   567                         resumptionMasterSecret, nstm.getTicketNonce());
       
   568             }
   370 
   569 
   371             // create and cache the new session
   570             // create and cache the new session
   372             // The new session must be a child of the existing session so
   571             // The new session must be a child of the existing session so
   373             // they will be invalidated together, etc.
   572             // they will be invalidated together, etc.
   374             SessionId newId =
   573             SessionId newId =
   375                 new SessionId(true, hc.sslContext.getSecureRandom());
   574                     new SessionId(true, hc.sslContext.getSecureRandom());
   376             SSLSessionImpl sessionCopy = new SSLSessionImpl(sessionToSave,
   575             SSLSessionImpl sessionCopy = new SSLSessionImpl(sessionToSave,
   377                     newId);
   576                     newId);
   378             sessionToSave.addChild(sessionCopy);
   577             sessionToSave.addChild(sessionCopy);
   379             sessionCopy.setPreSharedKey(psk);
   578             sessionCopy.setPreSharedKey(psk);
   380             sessionCopy.setTicketAgeAdd(nstm.ticketAgeAdd);
   579             sessionCopy.setTicketAgeAdd(nstm.getTicketAgeAdd());
   381             sessionCopy.setPskIdentity(nstm.ticket);
   580             sessionCopy.setPskIdentity(nstm.ticket);
   382             sessionCache.put(sessionCopy);
   581             sessionCache.put(sessionCopy);
   383 
   582 
   384             // clean handshake context
   583             // clean handshake context
   385             hc.conContext.finishPostHandshake();
   584             if (hc.negotiatedProtocol.useTLS13PlusSpec()) {
       
   585                 hc.conContext.finishPostHandshake();
       
   586             }
       
   587         }
       
   588     }
       
   589 
       
   590     private static final
       
   591     class T12NewSessionTicketConsumer implements SSLConsumer {
       
   592         // Prevent instantiation of this class.
       
   593         private T12NewSessionTicketConsumer() {
       
   594             // blank
       
   595         }
       
   596 
       
   597         @Override
       
   598         public void consume(ConnectionContext context,
       
   599                 ByteBuffer message) throws IOException {
       
   600 
       
   601             HandshakeContext hc = (HandshakeContext)context;
       
   602             hc.handshakeConsumers.remove(NEW_SESSION_TICKET.id);
       
   603 
       
   604             NewSessionTicketMessage nstm = new T12NewSessionTicketMessage(hc,
       
   605                     message);
       
   606             if (nstm.ticket.length == 0) {
       
   607                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   608                     SSLLogger.fine("NewSessionTicket ticket was empty");
       
   609                 }
       
   610                 return;
       
   611             }
       
   612 
       
   613             // discard tickets with timeout 0
       
   614             if (nstm.ticketLifetime <= 0 ||
       
   615                 nstm.ticketLifetime > MAX_TICKET_LIFETIME) {
       
   616                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   617                     SSLLogger.fine(
       
   618                     "Discarding NewSessionTicket with lifetime "
       
   619                         + nstm.ticketLifetime, nstm);
       
   620                 }
       
   621                 return;
       
   622             }
       
   623 
       
   624             SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
       
   625                     hc.sslContext.engineGetClientSessionContext();
       
   626 
       
   627             if (sessionCache.getSessionTimeout() > MAX_TICKET_LIFETIME) {
       
   628                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   629                     SSLLogger.fine(
       
   630                     "Session cache lifetime is too long. Discarding ticket.");
       
   631                 }
       
   632                 return;
       
   633             }
       
   634 
       
   635             hc.handshakeSession.setPskIdentity(nstm.ticket);
       
   636             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
       
   637                 SSLLogger.fine("Consuming NewSessionTicket\n" +
       
   638                         nstm.toString());
       
   639             }
   386         }
   640         }
   387     }
   641     }
   388 }
   642 }
   389 
   643