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