1 /*
   2  * Copyright (c) 2015, 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.nio.ByteBuffer;
  29 import java.security.*;
  30 import java.text.MessageFormat;
  31 import java.util.List;
  32 import java.util.ArrayList;
  33 import java.util.Locale;
  34 import java.util.Arrays;
  35 import java.util.Objects;
  36 import java.util.Collection;
  37 import javax.crypto.Mac;
  38 import javax.crypto.SecretKey;
  39 import javax.net.ssl.SSLPeerUnverifiedException;
  40 import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
  41 import sun.security.ssl.ClientHello.ClientHelloMessage;
  42 import sun.security.ssl.SSLExtension.ExtensionConsumer;
  43 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
  44 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  45 import static sun.security.ssl.SSLExtension.*;
  46 
  47 /**
  48  * Pack of the "pre_shared_key" extension.
  49  */
  50 final class PreSharedKeyExtension {
  51     static final HandshakeProducer chNetworkProducer =
  52             new CHPreSharedKeyProducer();
  53     static final ExtensionConsumer chOnLoadConsumer =
  54             new CHPreSharedKeyConsumer();
  55     static final HandshakeAbsence chOnLoadAbsence =
  56             new CHPreSharedKeyAbsence();
  57     static final HandshakeConsumer chOnTradeConsumer =
  58             new CHPreSharedKeyUpdate();
  59     static final SSLStringizer chStringizer =
  60             new CHPreSharedKeyStringizer();
  61 
  62     static final HandshakeProducer shNetworkProducer =
  63             new SHPreSharedKeyProducer();
  64     static final ExtensionConsumer shOnLoadConsumer =
  65             new SHPreSharedKeyConsumer();
  66     static final HandshakeAbsence shOnLoadAbsence =
  67             new SHPreSharedKeyAbsence();
  68     static final SSLStringizer shStringizer =
  69             new SHPreSharedKeyStringizer();
  70 
  71     private static final class PskIdentity {
  72         final byte[] identity;
  73         final int obfuscatedAge;
  74 
  75         PskIdentity(byte[] identity, int obfuscatedAge) {
  76             this.identity = identity;
  77             this.obfuscatedAge = obfuscatedAge;
  78         }
  79 
  80         int getEncodedLength() {
  81             return 2 + identity.length + 4;
  82         }
  83 
  84         void writeEncoded(ByteBuffer m) throws IOException {
  85             Record.putBytes16(m, identity);
  86             Record.putInt32(m, obfuscatedAge);
  87         }
  88 
  89         @Override
  90         public String toString() {
  91             return "{" + Utilities.toHexString(identity) + "," +
  92                 obfuscatedAge + "}";
  93         }
  94     }
  95 
  96     private static final
  97             class CHPreSharedKeySpec implements SSLExtensionSpec {
  98         final List<PskIdentity> identities;
  99         final List<byte[]> binders;
 100 
 101         CHPreSharedKeySpec(List<PskIdentity> identities, List<byte[]> binders) {
 102             this.identities = identities;
 103             this.binders = binders;
 104         }
 105 
 106         CHPreSharedKeySpec(HandshakeContext context,
 107                 ByteBuffer m) throws IOException {
 108             // struct {
 109             //     PskIdentity identities<7..2^16-1>;
 110             //     PskBinderEntry binders<33..2^16-1>;
 111             // } OfferedPsks;
 112             if (m.remaining() < 44) {
 113                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 114                     "Invalid pre_shared_key extension: " +
 115                     "insufficient data (length=" + m.remaining() + ")");
 116             }
 117 
 118             int idEncodedLength = Record.getInt16(m);
 119             if (idEncodedLength < 7) {
 120                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 121                     "Invalid pre_shared_key extension: " +
 122                     "insufficient identities (length=" + idEncodedLength + ")");
 123             }
 124 
 125             identities = new ArrayList<>();
 126             int idReadLength = 0;
 127             while (idReadLength < idEncodedLength) {
 128                 byte[] id = Record.getBytes16(m);
 129                 if (id.length < 1) {
 130                     throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 131                         "Invalid pre_shared_key extension: " +
 132                         "insufficient identity (length=" + id.length + ")");
 133                 }
 134                 int obfuscatedTicketAge = Record.getInt32(m);
 135 
 136                 PskIdentity pskId = new PskIdentity(id, obfuscatedTicketAge);
 137                 identities.add(pskId);
 138                 idReadLength += pskId.getEncodedLength();
 139             }
 140 
 141             if (m.remaining() < 35) {
 142                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 143                         "Invalid pre_shared_key extension: " +
 144                         "insufficient binders data (length=" +
 145                         m.remaining() + ")");
 146             }
 147 
 148             int bindersEncodedLen = Record.getInt16(m);
 149             if (bindersEncodedLen < 33) {
 150                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 151                         "Invalid pre_shared_key extension: " +
 152                         "insufficient binders (length=" +
 153                         bindersEncodedLen + ")");
 154             }
 155 
 156             binders = new ArrayList<>();
 157             int bindersReadLength = 0;
 158             while (bindersReadLength < bindersEncodedLen) {
 159                 byte[] binder = Record.getBytes8(m);
 160                 if (binder.length < 32) {
 161                     throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 162                             "Invalid pre_shared_key extension: " +
 163                             "insufficient binder entry (length=" +
 164                             binder.length + ")");
 165                 }
 166                 binders.add(binder);
 167                 bindersReadLength += 1 + binder.length;
 168             }
 169         }
 170 
 171         int getIdsEncodedLength() {
 172             int idEncodedLength = 0;
 173             for (PskIdentity curId : identities) {
 174                 idEncodedLength += curId.getEncodedLength();
 175             }
 176 
 177             return idEncodedLength;
 178         }
 179 
 180         int getBindersEncodedLength() {
 181             int binderEncodedLength = 0;
 182             for (byte[] curBinder : binders) {
 183                 binderEncodedLength += 1 + curBinder.length;
 184             }
 185 
 186             return binderEncodedLength;
 187         }
 188 
 189         byte[] getEncoded() throws IOException {
 190             int idsEncodedLength = getIdsEncodedLength();
 191             int bindersEncodedLength = getBindersEncodedLength();
 192             int encodedLength = 4 + idsEncodedLength + bindersEncodedLength;
 193             byte[] buffer = new byte[encodedLength];
 194             ByteBuffer m = ByteBuffer.wrap(buffer);
 195             Record.putInt16(m, idsEncodedLength);
 196             for (PskIdentity curId : identities) {
 197                 curId.writeEncoded(m);
 198             }
 199             Record.putInt16(m, bindersEncodedLength);
 200             for (byte[] curBinder : binders) {
 201                 Record.putBytes8(m, curBinder);
 202             }
 203 
 204             return buffer;
 205         }
 206 
 207         @Override
 208         public String toString() {
 209             MessageFormat messageFormat = new MessageFormat(
 210                 "\"PreSharedKey\": '{'\n" +
 211                 "  \"identities\"    : \"{0}\",\n" +
 212                 "  \"binders\"       : \"{1}\",\n" +
 213                 "'}'",
 214                 Locale.ENGLISH);
 215 
 216             Object[] messageFields = {
 217                 Utilities.indent(identitiesString()),
 218                 Utilities.indent(bindersString())
 219             };
 220 
 221             return messageFormat.format(messageFields);
 222         }
 223 
 224         String identitiesString() {
 225             StringBuilder result = new StringBuilder();
 226             for (PskIdentity curId : identities) {
 227                 result.append(curId.toString() + "\n");
 228             }
 229 
 230             return result.toString();
 231         }
 232 
 233         String bindersString() {
 234             StringBuilder result = new StringBuilder();
 235             for (byte[] curBinder : binders) {
 236                 result.append("{" + Utilities.toHexString(curBinder) + "}\n");
 237             }
 238 
 239             return result.toString();
 240         }
 241     }
 242 
 243     private static final
 244             class CHPreSharedKeyStringizer implements SSLStringizer {
 245         @Override
 246         public String toString(ByteBuffer buffer) {
 247             try {
 248                 // As the HandshakeContext parameter of CHPreSharedKeySpec
 249                 // constructor is used for fatal alert only, we can use
 250                 // null HandshakeContext here as we don't care about exception.
 251                 //
 252                 // Please take care of this code if the CHPreSharedKeySpec
 253                 // constructor is updated in the future.
 254                 return (new CHPreSharedKeySpec(null, buffer)).toString();
 255             } catch (Exception ex) {
 256                 // For debug logging only, so please swallow exceptions.
 257                 return ex.getMessage();
 258             }
 259         }
 260     }
 261 
 262     private static final
 263             class SHPreSharedKeySpec implements SSLExtensionSpec {
 264         final int selectedIdentity;
 265 
 266         SHPreSharedKeySpec(int selectedIdentity) {
 267             this.selectedIdentity = selectedIdentity;
 268         }
 269 
 270         SHPreSharedKeySpec(HandshakeContext context,
 271                 ByteBuffer m) throws IOException {
 272             if (m.remaining() < 2) {
 273                 throw context.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 274                         "Invalid pre_shared_key extension: " +
 275                         "insufficient selected_identity (length=" +
 276                         m.remaining() + ")");
 277             }
 278             this.selectedIdentity = Record.getInt16(m);
 279         }
 280 
 281         byte[] getEncoded() throws IOException {
 282             return new byte[] {
 283                 (byte)((selectedIdentity >> 8) & 0xFF),
 284                 (byte)(selectedIdentity & 0xFF)
 285             };
 286         }
 287 
 288         @Override
 289         public String toString() {
 290             MessageFormat messageFormat = new MessageFormat(
 291                 "\"PreSharedKey\": '{'\n" +
 292                 "  \"selected_identity\"      : \"{0}\",\n" +
 293                 "'}'",
 294                 Locale.ENGLISH);
 295 
 296             Object[] messageFields = {
 297                 Utilities.byte16HexString(selectedIdentity)
 298             };
 299 
 300             return messageFormat.format(messageFields);
 301         }
 302     }
 303 
 304     private static final
 305             class SHPreSharedKeyStringizer implements SSLStringizer {
 306         @Override
 307         public String toString(ByteBuffer buffer) {
 308             try {
 309                 // As the HandshakeContext parameter of SHPreSharedKeySpec
 310                 // constructor is used for fatal alert only, we can use
 311                 // null HandshakeContext here as we don't care about exception.
 312                 //
 313                 // Please take care of this code if the SHPreSharedKeySpec
 314                 // constructor is updated in the future.
 315                 return (new SHPreSharedKeySpec(null, buffer)).toString();
 316             } catch (Exception ex) {
 317                 // For debug logging only, so please swallow exceptions.
 318                 return ex.getMessage();
 319             }
 320         }
 321     }
 322 
 323     private static final
 324             class CHPreSharedKeyConsumer implements ExtensionConsumer {
 325         // Prevent instantiation of this class.
 326         private CHPreSharedKeyConsumer() {
 327             // blank
 328         }
 329 
 330         @Override
 331         public void consume(ConnectionContext context,
 332                             HandshakeMessage message,
 333                             ByteBuffer buffer) throws IOException {
 334             ClientHelloMessage clientHello = (ClientHelloMessage) message;
 335             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 336             // Is it a supported and enabled extension?
 337             if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) {
 338                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 339                     SSLLogger.fine(
 340                             "Ignore unavailable pre_shared_key extension");
 341                 }
 342                 return;     // ignore the extension
 343             }
 344 
 345             // Parse the extension.
 346             CHPreSharedKeySpec pskSpec = null;
 347             try {
 348                 pskSpec = new CHPreSharedKeySpec(shc, buffer);
 349             } catch (IOException ioe) {
 350                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
 351             }
 352 
 353             // The "psk_key_exchange_modes" extension should have been loaded.
 354             if (!shc.handshakeExtensions.containsKey(
 355                     SSLExtension.PSK_KEY_EXCHANGE_MODES)) {
 356                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 357                         "Client sent PSK but not PSK modes, or the PSK " +
 358                         "extension is not the last extension");
 359             }
 360 
 361             // error if id and binder lists are not the same length
 362             if (pskSpec.identities.size() != pskSpec.binders.size()) {
 363                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 364                         "PSK extension has incorrect number of binders");
 365             }
 366 
 367             if (shc.isResumption) {     // resumingSession may not be set
 368                 SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
 369                         shc.sslContext.engineGetServerSessionContext();
 370                 int idIndex = 0;
 371                 for (PskIdentity requestedId : pskSpec.identities) {
 372                     SSLSessionImpl s = sessionCache.get(requestedId.identity);
 373                     if (s != null && canRejoin(clientHello, shc, s)) {
 374                         if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 375                             SSLLogger.fine("Resuming session: ", s);
 376                         }
 377 
 378                         // binder will be checked later
 379                         shc.resumingSession = s;
 380                         shc.handshakeExtensions.put(SH_PRE_SHARED_KEY,
 381                             new SHPreSharedKeySpec(idIndex));   // for the index
 382                         break;
 383                     }
 384 
 385                     ++idIndex;
 386                 }
 387 
 388                 if (idIndex == pskSpec.identities.size()) {
 389                     // no resumable session
 390                     shc.isResumption = false;
 391                     shc.resumingSession = null;
 392                 }
 393             }
 394 
 395             // update the context
 396             shc.handshakeExtensions.put(
 397                 SSLExtension.CH_PRE_SHARED_KEY, pskSpec);
 398         }
 399     }
 400 
 401     private static boolean canRejoin(ClientHelloMessage clientHello,
 402         ServerHandshakeContext shc, SSLSessionImpl s) {
 403 
 404         boolean result = s.isRejoinable() && (s.getPreSharedKey() != null);
 405 
 406         // Check protocol version
 407         if (result && s.getProtocolVersion() != shc.negotiatedProtocol) {
 408             if (SSLLogger.isOn &&
 409                 SSLLogger.isOn("ssl,handshake,verbose")) {
 410 
 411                 SSLLogger.finest("Can't resume, incorrect protocol version");
 412             }
 413             result = false;
 414         }
 415 
 416         // Make sure that the server handshake context's localSupportedSignAlgs
 417         // field is populated.  This is particularly important when
 418         // client authentication was used in an initial session and it is
 419         // now being resumed.
 420         if (shc.localSupportedSignAlgs == null) {
 421             shc.localSupportedSignAlgs =
 422                     SignatureScheme.getSupportedAlgorithms(
 423                             shc.algorithmConstraints, shc.activeProtocols);
 424         }
 425 
 426         // Validate the required client authentication.
 427         if (result &&
 428             (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED)) {
 429             try {
 430                 s.getPeerPrincipal();
 431             } catch (SSLPeerUnverifiedException e) {
 432                 if (SSLLogger.isOn &&
 433                         SSLLogger.isOn("ssl,handshake,verbose")) {
 434                     SSLLogger.finest(
 435                         "Can't resume, " +
 436                         "client authentication is required");
 437                 }
 438                 result = false;
 439             }
 440 
 441             // Make sure the list of supported signature algorithms matches
 442             Collection<SignatureScheme> sessionSigAlgs =
 443                 s.getLocalSupportedSignatureSchemes();
 444             if (result &&
 445                 !shc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
 446 
 447                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 448                     SSLLogger.fine("Can't resume. Session uses different " +
 449                         "signature algorithms");
 450                 }
 451                 result = false;
 452             }
 453         }
 454 
 455         // ensure that the endpoint identification algorithm matches the
 456         // one in the session
 457         String identityAlg = shc.sslConfig.identificationProtocol;
 458         if (result && identityAlg != null) {
 459             String sessionIdentityAlg = s.getIdentificationProtocol();
 460             if (!identityAlg.equalsIgnoreCase(sessionIdentityAlg)) {
 461                 if (SSLLogger.isOn &&
 462                     SSLLogger.isOn("ssl,handshake,verbose")) {
 463 
 464                     SSLLogger.finest("Can't resume, endpoint id" +
 465                         " algorithm does not match, requested: " +
 466                         identityAlg + ", cached: " + sessionIdentityAlg);
 467                 }
 468                 result = false;
 469             }
 470         }
 471 
 472         // Ensure cipher suite can be negotiated
 473         if (result && (!shc.isNegotiable(s.getSuite()) ||
 474             !clientHello.cipherSuites.contains(s.getSuite()))) {
 475             if (SSLLogger.isOn &&
 476                     SSLLogger.isOn("ssl,handshake,verbose")) {
 477                 SSLLogger.finest(
 478                     "Can't resume, unavailable session cipher suite");
 479             }
 480             result = false;
 481         }
 482 
 483         return result;
 484     }
 485 
 486     private static final
 487             class CHPreSharedKeyUpdate implements HandshakeConsumer {
 488         // Prevent instantiation of this class.
 489         private CHPreSharedKeyUpdate() {
 490             // blank
 491         }
 492 
 493         @Override
 494         public void consume(ConnectionContext context,
 495                 HandshakeMessage message) throws IOException {
 496             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 497             if (!shc.isResumption || shc.resumingSession == null) {
 498                 // not resuming---nothing to do
 499                 return;
 500             }
 501 
 502             CHPreSharedKeySpec chPsk = (CHPreSharedKeySpec)
 503                     shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY);
 504             SHPreSharedKeySpec shPsk = (SHPreSharedKeySpec)
 505                     shc.handshakeExtensions.get(SSLExtension.SH_PRE_SHARED_KEY);
 506             if (chPsk == null || shPsk == null) {
 507                 throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
 508                         "Required extensions are unavailable");
 509             }
 510 
 511             byte[] binder = chPsk.binders.get(shPsk.selectedIdentity);
 512 
 513             // set up PSK binder hash
 514             HandshakeHash pskBinderHash = shc.handshakeHash.copy();
 515             byte[] lastMessage = pskBinderHash.removeLastReceived();
 516             ByteBuffer messageBuf = ByteBuffer.wrap(lastMessage);
 517             // skip the type and length
 518             messageBuf.position(4);
 519             // read to find the beginning of the binders
 520             ClientHelloMessage.readPartial(shc.conContext, messageBuf);
 521             int length = messageBuf.position();
 522             messageBuf.position(0);
 523             pskBinderHash.receive(messageBuf, length);
 524 
 525             checkBinder(shc, shc.resumingSession, pskBinderHash, binder);
 526         }
 527     }
 528 
 529     private static void checkBinder(ServerHandshakeContext shc,
 530             SSLSessionImpl session,
 531             HandshakeHash pskBinderHash, byte[] binder) throws IOException {
 532         SecretKey psk = session.getPreSharedKey();
 533         if (psk == null) {
 534             throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
 535                     "Session has no PSK");
 536         }
 537 
 538         SecretKey binderKey = deriveBinderKey(shc, psk, session);
 539         byte[] computedBinder =
 540                 computeBinder(shc, binderKey, session, pskBinderHash);
 541         if (!Arrays.equals(binder, computedBinder)) {
 542             throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 543                     "Incorect PSK binder value");
 544         }
 545     }
 546 
 547     // Class that produces partial messages used to compute binder hash
 548     static final class PartialClientHelloMessage extends HandshakeMessage {
 549 
 550         private final ClientHello.ClientHelloMessage msg;
 551         private final CHPreSharedKeySpec psk;
 552 
 553         PartialClientHelloMessage(HandshakeContext ctx,
 554                                   ClientHello.ClientHelloMessage msg,
 555                                   CHPreSharedKeySpec psk) {
 556             super(ctx);
 557 
 558             this.msg = msg;
 559             this.psk = psk;
 560         }
 561 
 562         @Override
 563         SSLHandshake handshakeType() {
 564             return msg.handshakeType();
 565         }
 566 
 567         private int pskTotalLength() {
 568             return psk.getIdsEncodedLength() +
 569                 psk.getBindersEncodedLength() + 8;
 570         }
 571 
 572         @Override
 573         int messageLength() {
 574 
 575             if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) != null) {
 576                 return msg.messageLength();
 577             } else {
 578                 return msg.messageLength() + pskTotalLength();
 579             }
 580         }
 581 
 582         @Override
 583         void send(HandshakeOutStream hos) throws IOException {
 584             msg.sendCore(hos);
 585 
 586             // complete extensions
 587             int extsLen = msg.extensions.length();
 588             if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) == null) {
 589                 extsLen += pskTotalLength();
 590             }
 591             hos.putInt16(extsLen - 2);
 592             // write the complete extensions
 593             for (SSLExtension ext : SSLExtension.values()) {
 594                 byte[] extData = msg.extensions.get(ext);
 595                 if (extData == null) {
 596                     continue;
 597                 }
 598                 // the PSK could be there from an earlier round
 599                 if (ext == SSLExtension.CH_PRE_SHARED_KEY) {
 600                     continue;
 601                 }
 602                 int extID = ext.id;
 603                 hos.putInt16(extID);
 604                 hos.putBytes16(extData);
 605             }
 606 
 607             // partial PSK extension
 608             int extID = SSLExtension.CH_PRE_SHARED_KEY.id;
 609             hos.putInt16(extID);
 610             byte[] encodedPsk = psk.getEncoded();
 611             hos.putInt16(encodedPsk.length);
 612             hos.write(encodedPsk, 0, psk.getIdsEncodedLength() + 2);
 613         }
 614     }
 615 
 616     private static final
 617             class CHPreSharedKeyProducer implements HandshakeProducer {
 618         // Prevent instantiation of this class.
 619         private CHPreSharedKeyProducer() {
 620             // blank
 621         }
 622 
 623         @Override
 624         public byte[] produce(ConnectionContext context,
 625                 HandshakeMessage message) throws IOException {
 626 
 627             // The producing happens in client side only.
 628             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 629             if (!chc.isResumption || chc.resumingSession == null) {
 630                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 631                     SSLLogger.fine("No session to resume.");
 632                 }
 633                 return null;
 634             }
 635 
 636             // Make sure the list of supported signature algorithms matches
 637             Collection<SignatureScheme> sessionSigAlgs =
 638                 chc.resumingSession.getLocalSupportedSignatureSchemes();
 639             if (!chc.localSupportedSignAlgs.containsAll(sessionSigAlgs)) {
 640                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 641                     SSLLogger.fine("Existing session uses different " +
 642                         "signature algorithms");
 643                 }
 644                 return null;
 645             }
 646 
 647             // The session must have a pre-shared key
 648             SecretKey psk = chc.resumingSession.getPreSharedKey();
 649             if (psk == null) {
 650                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 651                     SSLLogger.fine("Existing session has no PSK.");
 652                 }
 653                 return null;
 654             }
 655 
 656             // The PSK ID can only be used in one connections, but this method
 657             // may be called twice in a connection if the server sends HRR.
 658             // ID is saved in the context so it can be used in the second call.
 659             if (chc.pskIdentity == null) {
 660                 chc.pskIdentity = chc.resumingSession.consumePskIdentity();
 661             }
 662 
 663             if (chc.pskIdentity == null) {
 664                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 665                     SSLLogger.fine(
 666                         "PSK has no identity, or identity was already used");
 667                 }
 668                 return null;
 669             }
 670 
 671             //The session cannot be used again. Remove it from the cache.
 672             SSLSessionContextImpl sessionCache = (SSLSessionContextImpl)
 673                 chc.sslContext.engineGetClientSessionContext();
 674             sessionCache.remove(chc.resumingSession.getSessionId());
 675 
 676             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 677                 SSLLogger.fine(
 678                     "Found resumable session. Preparing PSK message.");
 679             }
 680 
 681             List<PskIdentity> identities = new ArrayList<>();
 682             int ageMillis = (int)(System.currentTimeMillis() -
 683                     chc.resumingSession.getTicketCreationTime());
 684             int obfuscatedAge =
 685                     ageMillis + chc.resumingSession.getTicketAgeAdd();
 686             identities.add(new PskIdentity(chc.pskIdentity, obfuscatedAge));
 687 
 688             SecretKey binderKey =
 689                     deriveBinderKey(chc, psk, chc.resumingSession);
 690             ClientHelloMessage clientHello = (ClientHelloMessage)message;
 691             CHPreSharedKeySpec pskPrototype = createPskPrototype(
 692                 chc.resumingSession.getSuite().hashAlg.hashLength, identities);
 693             HandshakeHash pskBinderHash = chc.handshakeHash.copy();
 694 
 695             byte[] binder = computeBinder(chc, binderKey, pskBinderHash,
 696                     chc.resumingSession, chc, clientHello, pskPrototype);
 697 
 698             List<byte[]> binders = new ArrayList<>();
 699             binders.add(binder);
 700 
 701             CHPreSharedKeySpec pskMessage =
 702                     new CHPreSharedKeySpec(identities, binders);
 703             chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage);
 704             return pskMessage.getEncoded();
 705         }
 706 
 707         private CHPreSharedKeySpec createPskPrototype(
 708                 int hashLength, List<PskIdentity> identities) {
 709             List<byte[]> binders = new ArrayList<>();
 710             byte[] binderProto = new byte[hashLength];
 711             for (PskIdentity curId : identities) {
 712                 binders.add(binderProto);
 713             }
 714 
 715             return new CHPreSharedKeySpec(identities, binders);
 716         }
 717     }
 718 
 719     private static byte[] computeBinder(
 720             HandshakeContext context, SecretKey binderKey,
 721             SSLSessionImpl session,
 722             HandshakeHash pskBinderHash) throws IOException {
 723 
 724         pskBinderHash.determine(
 725                 session.getProtocolVersion(), session.getSuite());
 726         pskBinderHash.update();
 727         byte[] digest = pskBinderHash.digest();
 728 
 729         return computeBinder(context, binderKey, session, digest);
 730     }
 731 
 732     private static byte[] computeBinder(
 733             HandshakeContext context, SecretKey binderKey,
 734             HandshakeHash hash, SSLSessionImpl session,
 735             HandshakeContext ctx, ClientHello.ClientHelloMessage hello,
 736             CHPreSharedKeySpec pskPrototype) throws IOException {
 737 
 738         PartialClientHelloMessage partialMsg =
 739                 new PartialClientHelloMessage(ctx, hello, pskPrototype);
 740 
 741         SSLEngineOutputRecord record = new SSLEngineOutputRecord(hash);
 742         HandshakeOutStream hos = new HandshakeOutStream(record);
 743         partialMsg.write(hos);
 744 
 745         hash.determine(session.getProtocolVersion(), session.getSuite());
 746         hash.update();
 747         byte[] digest = hash.digest();
 748 
 749         return computeBinder(context, binderKey, session, digest);
 750     }
 751 
 752     private static byte[] computeBinder(HandshakeContext context,
 753             SecretKey binderKey,
 754             SSLSessionImpl session, byte[] digest) throws IOException {
 755         try {
 756             CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
 757             HKDF hkdf = new HKDF(hashAlg.name);
 758             byte[] label = ("tls13 finished").getBytes();
 759             byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
 760                     label, new byte[0], hashAlg.hashLength);
 761             SecretKey finishedKey = hkdf.expand(
 762                     binderKey, hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
 763 
 764             String hmacAlg =
 765                 "Hmac" + hashAlg.name.replace("-", "");
 766             try {
 767                 Mac hmac = Mac.getInstance(hmacAlg);
 768                 hmac.init(finishedKey);
 769                 return hmac.doFinal(digest);
 770             } catch (NoSuchAlgorithmException | InvalidKeyException ex) {
 771                 throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
 772             }
 773         } catch (GeneralSecurityException ex) {
 774             throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
 775         }
 776     }
 777 
 778     private static SecretKey deriveBinderKey(HandshakeContext context,
 779             SecretKey psk, SSLSessionImpl session) throws IOException {
 780         try {
 781             CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg;
 782             HKDF hkdf = new HKDF(hashAlg.name);
 783             byte[] zeros = new byte[hashAlg.hashLength];
 784             SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret");
 785 
 786             byte[] label = ("tls13 res binder").getBytes();
 787             MessageDigest md = MessageDigest.getInstance(hashAlg.name);
 788             byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(
 789                     label, md.digest(new byte[0]), hashAlg.hashLength);
 790             return hkdf.expand(earlySecret,
 791                     hkdfInfo, hashAlg.hashLength, "TlsBinderKey");
 792         } catch (GeneralSecurityException ex) {
 793             throw context.conContext.fatal(Alert.INTERNAL_ERROR, ex);
 794         }
 795     }
 796 
 797     private static final
 798             class CHPreSharedKeyAbsence implements HandshakeAbsence {
 799         @Override
 800         public void absent(ConnectionContext context,
 801                            HandshakeMessage message) throws IOException {
 802 
 803             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 804                 SSLLogger.fine(
 805                 "Handling pre_shared_key absence.");
 806             }
 807 
 808             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 809 
 810             // Resumption is only determined by PSK, when enabled
 811             shc.resumingSession = null;
 812             shc.isResumption = false;
 813         }
 814     }
 815 
 816     private static final
 817             class SHPreSharedKeyConsumer implements ExtensionConsumer {
 818         // Prevent instantiation of this class.
 819         private SHPreSharedKeyConsumer() {
 820             // blank
 821         }
 822 
 823         @Override
 824         public void consume(ConnectionContext context,
 825             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 826             // The consuming happens in client side only.
 827             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 828 
 829             // Is it a response of the specific request?
 830             if (!chc.handshakeExtensions.containsKey(
 831                     SSLExtension.CH_PRE_SHARED_KEY)) {
 832                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
 833                     "Server sent unexpected pre_shared_key extension");
 834             }
 835 
 836             SHPreSharedKeySpec shPsk = new SHPreSharedKeySpec(chc, buffer);
 837             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 838                 SSLLogger.fine(
 839                     "Received pre_shared_key extension: ", shPsk);
 840             }
 841 
 842             if (shPsk.selectedIdentity != 0) {
 843                 throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 844                     "Selected identity index is not in correct range.");
 845             }
 846 
 847             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 848                 SSLLogger.fine(
 849                         "Resuming session: ", chc.resumingSession);
 850             }
 851         }
 852     }
 853 
 854     private static final
 855             class SHPreSharedKeyAbsence implements HandshakeAbsence {
 856         @Override
 857         public void absent(ConnectionContext context,
 858                 HandshakeMessage message) throws IOException {
 859             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 860 
 861             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 862                 SSLLogger.fine("Handling pre_shared_key absence.");
 863             }
 864 
 865             // The server refused to resume, or the client did not
 866             // request 1.3 resumption.
 867             chc.resumingSession = null;
 868             chc.isResumption = false;
 869         }
 870     }
 871 
 872     private static final
 873             class SHPreSharedKeyProducer implements HandshakeProducer {
 874         // Prevent instantiation of this class.
 875         private SHPreSharedKeyProducer() {
 876             // blank
 877         }
 878 
 879         @Override
 880         public byte[] produce(ConnectionContext context,
 881                 HandshakeMessage message) throws IOException {
 882             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 883             SHPreSharedKeySpec psk = (SHPreSharedKeySpec)
 884                     shc.handshakeExtensions.get(SH_PRE_SHARED_KEY);
 885             if (psk == null) {
 886                 return null;
 887             }
 888 
 889             return psk.getEncoded();
 890         }
 891     }
 892 }