1 /* 2 * Copyright (c) 2015, 2017, 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.Map; 32 import java.util.List; 33 import java.util.ArrayList; 34 import java.util.Locale; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.Optional; 38 import sun.security.ssl.SSLExtension.ExtensionConsumer; 39 40 import sun.security.ssl.SSLExtension.SSLExtensionSpec; 41 import sun.security.ssl.SSLHandshake.HandshakeMessage; 42 43 import javax.crypto.Mac; 44 import javax.crypto.SecretKey; 45 46 import static sun.security.ssl.SSLExtension.*; 47 48 /** 49 * Pack of the "pre_shared_key" extension. 50 */ 51 final class PreSharedKeyExtension { 52 static final HandshakeProducer chNetworkProducer = 53 new CHPreSharedKeyProducer(); 54 static final ExtensionConsumer chOnLoadConsumer = 55 new CHPreSharedKeyConsumer(); 56 static final HandshakeAbsence chOnLoadAbsence = 57 new CHPreSharedKeyAbsence(); 58 static final HandshakeConsumer chOnTradeConsumer= 59 new CHPreSharedKeyUpdate(); 60 61 static final HandshakeProducer shNetworkProducer = 62 new SHPreSharedKeyProducer(); 63 static final ExtensionConsumer shOnLoadConsumer = 64 new SHPreSharedKeyConsumer(); 65 static final HandshakeAbsence shOnLoadAbsence = 66 new SHPreSharedKeyAbsence(); 67 68 static final class PskIdentity { 69 final byte[] identity; 70 final int obfuscatedAge; 71 72 public PskIdentity(byte[] identity, int obfuscatedAge) { 73 this.identity = identity; 74 this.obfuscatedAge = obfuscatedAge; 75 } 76 77 public PskIdentity(ByteBuffer m) 78 throws IllegalParameterException, IOException { 79 80 identity = Record.getBytes16(m); 81 if (identity.length == 0) { 82 throw new IllegalParameterException("identity has length 0"); 83 } 84 obfuscatedAge = Record.getInt32(m); 85 } 86 87 int getEncodedLength() { 88 return 2 + identity.length + 4; 89 } 90 91 public void writeEncoded(ByteBuffer m) throws IOException { 92 Record.putBytes16(m, identity); 93 Record.putInt32(m, obfuscatedAge); 94 } 95 @Override 96 public String toString() { 97 return "{" + Utilities.toHexString(identity) + "," + 98 obfuscatedAge + "}"; 99 } 100 } 101 102 static final class CHPreSharedKeySpec implements SSLExtensionSpec { 103 final List<PskIdentity> identities; 104 final List<byte[]> binders; 105 106 CHPreSharedKeySpec(List<PskIdentity> identities, List<byte[]> binders) { 107 this.identities = identities; 108 this.binders = binders; 109 } 110 111 CHPreSharedKeySpec(ByteBuffer m) 112 throws IllegalParameterException, IOException { 113 114 identities = new ArrayList<>(); 115 int idEncodedLength = Record.getInt16(m); 116 int idReadLength = 0; 117 while (idReadLength < idEncodedLength) { 118 PskIdentity id = new PskIdentity(m); 119 identities.add(id); 120 idReadLength += id.getEncodedLength(); 121 } 122 123 binders = new ArrayList<>(); 124 int bindersEncodedLength = Record.getInt16(m); 125 int bindersReadLength = 0; 126 while (bindersReadLength < bindersEncodedLength) { 127 byte[] binder = Record.getBytes8(m); 128 if (binder.length < 32) { 129 throw new IllegalParameterException( 130 "binder has length < 32"); 131 } 132 binders.add(binder); 133 bindersReadLength += 1 + binder.length; 134 } 135 } 136 137 int getIdsEncodedLength() { 138 int idEncodedLength = 0; 139 for(PskIdentity curId : identities) { 140 idEncodedLength += curId.getEncodedLength(); 141 } 142 return idEncodedLength; 143 } 144 145 int getBindersEncodedLength() { 146 return getBindersEncodedLength(binders); 147 } 148 static int getBindersEncodedLength(Iterable<byte[]> binders) { 149 int binderEncodedLength = 0; 150 for (byte[] curBinder : binders) { 151 binderEncodedLength += 1 + curBinder.length; 152 } 153 return binderEncodedLength; 154 } 155 156 byte[] getEncoded() throws IOException { 157 158 int idsEncodedLength = getIdsEncodedLength(); 159 int bindersEncodedLength = getBindersEncodedLength(); 160 int encodedLength = 4 + idsEncodedLength + bindersEncodedLength; 161 byte[] buffer = new byte[encodedLength]; 162 ByteBuffer m = ByteBuffer.wrap(buffer); 163 Record.putInt16(m, idsEncodedLength); 164 for(PskIdentity curId : identities) { 165 curId.writeEncoded(m); 166 } 167 Record.putInt16(m, bindersEncodedLength); 168 for (byte[] curBinder : binders) { 169 Record.putBytes8(m, curBinder); 170 } 171 172 return buffer; 173 } 174 175 @Override 176 public String toString() { 177 MessageFormat messageFormat = new MessageFormat( 178 "\"PreSharedKey\": '{'\n" + 179 " \"identities\" : \"{0}\",\n" + 180 " \"binders\" : \"{1}\",\n" + 181 "'}'", 182 Locale.ENGLISH); 183 184 Object[] messageFields = { 185 Utilities.indent(identitiesString()), 186 Utilities.indent(bindersString()) 187 }; 188 189 return messageFormat.format(messageFields); 190 } 191 192 String identitiesString() { 193 StringBuilder result = new StringBuilder(); 194 for(PskIdentity curId : identities) { 195 result.append(curId.toString() + "\n"); 196 } 197 198 return result.toString(); 199 } 200 201 String bindersString() { 202 StringBuilder result = new StringBuilder(); 203 for(byte[] curBinder : binders) { 204 result.append("{" + Utilities.toHexString(curBinder) + "}\n"); 205 } 206 207 return result.toString(); 208 } 209 } 210 211 static final class SHPreSharedKeySpec implements SSLExtensionSpec { 212 final int selectedIdentity; 213 214 SHPreSharedKeySpec(int selectedIdentity) { 215 this.selectedIdentity = selectedIdentity; 216 } 217 218 SHPreSharedKeySpec(ByteBuffer m) throws IOException { 219 this.selectedIdentity = Record.getInt16(m); 220 } 221 222 byte[] getEncoded() throws IOException { 223 224 byte[] buffer = new byte[2]; 225 ByteBuffer m = ByteBuffer.wrap(buffer); 226 Record.putInt16(m, selectedIdentity); 227 228 return buffer; 229 } 230 231 @Override 232 public String toString() { 233 MessageFormat messageFormat = new MessageFormat( 234 "\"PreSharedKey\": '{'\n" + 235 " \"selected_identity\" : \"{0}\",\n" + 236 "'}'", 237 Locale.ENGLISH); 238 239 Object[] messageFields = { 240 selectedIdentity 241 }; 242 243 return messageFormat.format(messageFields); 244 } 245 246 } 247 248 249 private static class IllegalParameterException extends Exception { 250 251 private static final long serialVersionUID = 0; 252 253 private final String message; 254 255 private IllegalParameterException(String message) { 256 this.message = message; 257 } 258 } 259 260 private static final class CHPreSharedKeyConsumer implements ExtensionConsumer { 261 // Prevent instantiation of this class. 262 private CHPreSharedKeyConsumer() { 263 // blank 264 } 265 266 @Override 267 public void consume(ConnectionContext context, 268 HandshakeMessage message, 269 ByteBuffer buffer) throws IOException { 270 271 ServerHandshakeContext shc = (ServerHandshakeContext) message.handshakeContext; 272 // Is it a supported and enabled extension? 273 if (!shc.sslConfig.isAvailable(SSLExtension.CH_PRE_SHARED_KEY)) { 274 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 275 SSLLogger.fine( 276 "Ignore unavailable pre_shared_key extension"); 277 } 278 return; // ignore the extension 279 } 280 281 CHPreSharedKeySpec pskSpec = null; 282 try { 283 pskSpec = new CHPreSharedKeySpec(buffer); 284 } catch (IOException ioe) { 285 shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 286 return; // fatal() always throws, make the compiler happy. 287 } catch (IllegalParameterException ex) { 288 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, ex.message); 289 } 290 291 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 292 SSLLogger.fine( 293 "Received PSK extension: ", pskSpec); 294 } 295 296 if (shc.pskKeyExchangeModes.isEmpty()) { 297 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 298 "Client sent PSK but does not support PSK modes"); 299 } 300 301 // error if id and binder lists are not the same length 302 if (pskSpec.identities.size() != pskSpec.binders.size()) { 303 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 304 "PSK extension has incorrect number of binders"); 305 } 306 307 shc.handshakeExtensions.put(SSLExtension.CH_PRE_SHARED_KEY, pskSpec); 308 309 SSLSessionContextImpl sessionCache = (SSLSessionContextImpl) 310 message.handshakeContext.sslContext.engineGetServerSessionContext(); 311 312 // The session to resume will be decided below. 313 // It could have been set by previous actions (e.g. PSK received 314 // earlier), and it must be recalculated. 315 shc.isResumption = false; 316 shc.resumingSession = null; 317 318 int idIndex = 0; 319 for (PskIdentity requestedId : pskSpec.identities) { 320 SSLSessionImpl s = sessionCache.get(requestedId.identity); 321 if (s != null && s.getPreSharedKey().isPresent()) { 322 resumeSession(shc, s, idIndex); 323 break; 324 } 325 326 ++idIndex; 327 } 328 } 329 } 330 331 private static final class CHPreSharedKeyUpdate implements HandshakeConsumer { 332 // Prevent instantiation of this class. 333 private CHPreSharedKeyUpdate() { 334 // blank 335 } 336 337 @Override 338 public void consume(ConnectionContext context, 339 HandshakeMessage message) throws IOException { 340 341 ServerHandshakeContext shc = (ServerHandshakeContext) message.handshakeContext; 342 343 if (!shc.isResumption || shc.resumingSession == null) { 344 // not resuming---nothing to do 345 return; 346 } 347 348 CHPreSharedKeySpec chPsk = (CHPreSharedKeySpec)shc.handshakeExtensions.get(SSLExtension.CH_PRE_SHARED_KEY); 349 SHPreSharedKeySpec shPsk = (SHPreSharedKeySpec)shc.handshakeExtensions.get(SSLExtension.SH_PRE_SHARED_KEY); 350 351 if (chPsk == null || shPsk == null) { 352 shc.conContext.fatal(Alert.INTERNAL_ERROR, 353 "Required extensions are unavailable"); 354 } 355 356 byte[] binder = chPsk.binders.get(shPsk.selectedIdentity); 357 358 // set up PSK binder hash 359 HandshakeHash pskBinderHash = shc.handshakeHash.copy(); 360 byte[] lastMessage = pskBinderHash.removeLastReceived(); 361 ByteBuffer messageBuf = ByteBuffer.wrap(lastMessage); 362 // skip the type and length 363 messageBuf.position(4); 364 // read to find the beginning of the binders 365 ClientHello.ClientHelloMessage.readPartial(shc.conContext, messageBuf); 366 int length = messageBuf.position(); 367 messageBuf.position(0); 368 pskBinderHash.receive(messageBuf, length); 369 370 checkBinder(shc, shc.resumingSession, pskBinderHash, binder); 371 372 SSLSessionContextImpl sessionCache = (SSLSessionContextImpl) 373 message.handshakeContext.sslContext.engineGetServerSessionContext(); 374 } 375 } 376 377 private static void resumeSession(ServerHandshakeContext shc, 378 SSLSessionImpl session, 379 int index) 380 throws IOException { 381 382 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 383 SSLLogger.fine( 384 "Resuming session: ", session); 385 } 386 387 // binder will be checked later 388 389 shc.isResumption = true; 390 shc.resumingSession = session; 391 392 SHPreSharedKeySpec pskMsg = new SHPreSharedKeySpec(index); 393 shc.handshakeExtensions.put(SH_PRE_SHARED_KEY, pskMsg); 394 } 395 396 private static void checkBinder(ServerHandshakeContext shc, SSLSessionImpl session, 397 HandshakeHash pskBinderHash, byte[] binder) throws IOException { 398 399 Optional<SecretKey> pskOpt = session.getPreSharedKey(); 400 if (!pskOpt.isPresent()) { 401 shc.conContext.fatal(Alert.INTERNAL_ERROR, 402 "Session has no PSK"); 403 } 404 SecretKey psk = pskOpt.get(); 405 406 SecretKey binderKey = deriveBinderKey(psk, session); 407 byte[] computedBinder = computeBinder(binderKey, session, pskBinderHash); 408 if (!Arrays.equals(binder, computedBinder)) { 409 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 410 "Incorect PSK binder value"); 411 } 412 } 413 414 // Class that produces partial messages used to compute binder hash 415 static final class PartialClientHelloMessage extends HandshakeMessage { 416 417 private final ClientHello.ClientHelloMessage msg; 418 private final CHPreSharedKeySpec psk; 419 420 PartialClientHelloMessage(HandshakeContext ctx, 421 ClientHello.ClientHelloMessage msg, 422 CHPreSharedKeySpec psk) { 423 super(ctx); 424 425 this.msg = msg; 426 this.psk = psk; 427 } 428 429 @Override 430 SSLHandshake handshakeType() { 431 return msg.handshakeType(); 432 } 433 434 private int pskTotalLength() { 435 return psk.getIdsEncodedLength() + 436 psk.getBindersEncodedLength() + 8; 437 } 438 439 @Override 440 int messageLength() { 441 442 if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) != null) { 443 return msg.messageLength(); 444 } else { 445 return msg.messageLength() + pskTotalLength(); 446 } 447 } 448 449 @Override 450 void send(HandshakeOutStream hos) throws IOException { 451 msg.sendCore(hos); 452 453 // complete extensions 454 int extsLen = msg.extensions.length(); 455 if (msg.extensions.get(SSLExtension.CH_PRE_SHARED_KEY) == null) { 456 extsLen += pskTotalLength(); 457 } 458 hos.putInt16(extsLen - 2); 459 // write the complete extensions 460 for (SSLExtension ext : SSLExtension.values()) { 461 byte[] extData = msg.extensions.get(ext); 462 if (extData == null) { 463 continue; 464 } 465 // the PSK could be there from an earlier round 466 if (ext == SSLExtension.CH_PRE_SHARED_KEY) { 467 continue; 468 } 469 System.err.println("partial CH extension: " + ext.name()); 470 int extID = ext.id; 471 hos.putInt16(extID); 472 hos.putBytes16(extData); 473 } 474 475 // partial PSK extension 476 int extID = SSLExtension.CH_PRE_SHARED_KEY.id; 477 hos.putInt16(extID); 478 byte[] encodedPsk = psk.getEncoded(); 479 hos.putInt16(encodedPsk.length); 480 hos.write(encodedPsk, 0, psk.getIdsEncodedLength() + 2); 481 } 482 } 483 484 private static final class CHPreSharedKeyProducer implements HandshakeProducer { 485 486 // Prevent instantiation of this class. 487 private CHPreSharedKeyProducer() { 488 // blank 489 } 490 491 @Override 492 public byte[] produce(ConnectionContext context, 493 HandshakeMessage message) throws IOException { 494 495 // The producing happens in client side only. 496 ClientHandshakeContext chc = (ClientHandshakeContext)context; 497 if (!chc.isResumption || chc.resumingSession == null) { 498 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 499 SSLLogger.fine( 500 "No session to resume."); 501 } 502 return null; 503 } 504 505 Optional<SecretKey> pskOpt = chc.resumingSession.getPreSharedKey(); 506 if (!pskOpt.isPresent()) { 507 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 508 SSLLogger.fine( 509 "Existing session has no PSK."); 510 } 511 return null; 512 } 513 SecretKey psk = pskOpt.get(); 514 Optional<byte[]> pskIdOpt = chc.resumingSession.getPskIdentity(); 515 if (!pskIdOpt.isPresent()) { 516 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 517 SSLLogger.fine( 518 "PSK has no identity, or identity was already used"); 519 } 520 return null; 521 } 522 byte[] pskId = pskIdOpt.get(); 523 524 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 525 SSLLogger.fine( 526 "Found resumable session. Preparing PSK message."); 527 } 528 529 List<PskIdentity> identities = new ArrayList<>(); 530 int ageMillis = (int)(System.currentTimeMillis() - chc.resumingSession.getTicketCreationTime()); 531 int obfuscatedAge = ageMillis + chc.resumingSession.getTicketAgeAdd(); 532 identities.add(new PskIdentity(pskId, obfuscatedAge)); 533 534 SecretKey binderKey = deriveBinderKey(psk, chc.resumingSession); 535 ClientHello.ClientHelloMessage clientHello = (ClientHello.ClientHelloMessage) message; 536 CHPreSharedKeySpec pskPrototype = createPskPrototype(chc.resumingSession.getSuite().hashAlg.hashLength, identities); 537 HandshakeHash pskBinderHash = chc.handshakeHash.copy(); 538 539 byte[] binder = computeBinder(binderKey, pskBinderHash, chc.resumingSession, chc, clientHello, pskPrototype); 540 541 List<byte[]> binders = new ArrayList<>(); 542 binders.add(binder); 543 544 CHPreSharedKeySpec pskMessage = new CHPreSharedKeySpec(identities, binders); 545 chc.handshakeExtensions.put(CH_PRE_SHARED_KEY, pskMessage); 546 return pskMessage.getEncoded(); 547 } 548 549 private CHPreSharedKeySpec createPskPrototype(int hashLength, List<PskIdentity> identities) { 550 List<byte[]> binders = new ArrayList<>(); 551 byte[] binderProto = new byte[hashLength]; 552 for (PskIdentity curId : identities) { 553 binders.add(binderProto); 554 } 555 556 return new CHPreSharedKeySpec(identities, binders); 557 } 558 } 559 560 private static byte[] computeBinder(SecretKey binderKey, SSLSessionImpl session, HandshakeHash pskBinderHash) throws IOException { 561 562 pskBinderHash.determine(session.getProtocolVersion(), session.getSuite()); 563 pskBinderHash.update(); 564 byte[] digest = pskBinderHash.digest(); 565 566 return computeBinder(binderKey, session, digest); 567 } 568 569 private static byte[] computeBinder(SecretKey binderKey, HandshakeHash hash, SSLSessionImpl session, 570 HandshakeContext ctx, ClientHello.ClientHelloMessage hello, 571 CHPreSharedKeySpec pskPrototype) throws IOException { 572 573 PartialClientHelloMessage partialMsg = new PartialClientHelloMessage(ctx, hello, pskPrototype); 574 575 SSLEngineOutputRecord record = new SSLEngineOutputRecord(hash); 576 HandshakeOutStream hos = new HandshakeOutStream(record); 577 partialMsg.write(hos); 578 579 hash.determine(session.getProtocolVersion(), session.getSuite()); 580 hash.update(); 581 byte[] digest = hash.digest(); 582 583 return computeBinder(binderKey, session, digest); 584 } 585 586 private static byte[] computeBinder(SecretKey binderKey, SSLSessionImpl session, 587 byte[] digest) throws IOException { 588 589 try { 590 CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg; 591 HKDF hkdf = new HKDF(hashAlg.name); 592 byte[] label = ("tls13 finished").getBytes(); 593 byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo(label, new byte[0], hashAlg.hashLength); 594 SecretKey finishedKey = hkdf.expand(binderKey, hkdfInfo, hashAlg.hashLength, "TlsBinderKey"); 595 596 String hmacAlg = 597 "Hmac" + hashAlg.name.replace("-", ""); 598 try { 599 Mac hmac = JsseJce.getMac(hmacAlg); 600 hmac.init(finishedKey); 601 return hmac.doFinal(digest); 602 } catch (NoSuchAlgorithmException | InvalidKeyException ex) { 603 throw new IOException(ex); 604 } 605 } catch(GeneralSecurityException ex) { 606 throw new IOException(ex); 607 } 608 } 609 610 private static SecretKey deriveBinderKey(SecretKey psk, 611 SSLSessionImpl session) 612 throws IOException { 613 614 try { 615 CipherSuite.HashAlg hashAlg = session.getSuite().hashAlg; 616 HKDF hkdf = new HKDF(hashAlg.name); 617 byte[] zeros = new byte[hashAlg.hashLength]; 618 SecretKey earlySecret = hkdf.extract(zeros, psk, "TlsEarlySecret"); 619 620 byte[] label = ("tls13 res binder").getBytes(); 621 MessageDigest md = MessageDigest.getInstance(hashAlg.toString());; 622 byte[] hkdfInfo = SSLSecretDerivation.createHkdfInfo( 623 label, md.digest(new byte[0]), hashAlg.hashLength); 624 return hkdf.expand(earlySecret, hkdfInfo, hashAlg.hashLength, 625 "TlsBinderKey"); 626 627 } catch (GeneralSecurityException ex) { 628 throw new IOException(ex); 629 } 630 } 631 632 private static final class CHPreSharedKeyAbsence implements HandshakeAbsence { 633 @Override 634 public void absent(ConnectionContext context, 635 HandshakeMessage message) throws IOException { 636 637 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 638 SSLLogger.fine( 639 "Handling pre_shared_key absence."); 640 } 641 642 ServerHandshakeContext shc = (ServerHandshakeContext)context; 643 644 // Resumption is only determined by PSK, when enabled 645 shc.resumingSession = null; 646 shc.isResumption = false; 647 } 648 } 649 650 private static final class SHPreSharedKeyConsumer implements ExtensionConsumer { 651 // Prevent instantiation of this class. 652 private SHPreSharedKeyConsumer() { 653 654 } 655 656 @Override 657 public void consume(ConnectionContext context, 658 HandshakeMessage message, ByteBuffer buffer) throws IOException { 659 660 ClientHandshakeContext chc = (ClientHandshakeContext) message.handshakeContext; 661 662 SHPreSharedKeySpec shPsk = new SHPreSharedKeySpec(buffer); 663 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 664 SSLLogger.fine( 665 "Received pre_shared_key extension: ", shPsk); 666 } 667 668 if (!chc.handshakeExtensions.containsKey(SSLExtension.CH_PRE_SHARED_KEY)) { 669 chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 670 "Server sent unexpected pre_shared_key extension"); 671 } 672 673 // The PSK identity should not be reused, even if it is 674 // not selected. 675 chc.resumingSession.consumePskIdentity(); 676 677 if (shPsk.selectedIdentity != 0) { 678 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, 679 "Selected identity index is not in correct range."); 680 } 681 682 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 683 SSLLogger.fine( 684 "Resuming session: ", chc.resumingSession); 685 } 686 687 // remove the session from the cache 688 SSLSessionContextImpl sessionCache = (SSLSessionContextImpl) 689 chc.sslContext.engineGetClientSessionContext(); 690 sessionCache.remove(chc.resumingSession.getSessionId()); 691 } 692 } 693 694 private static final class SHPreSharedKeyAbsence implements HandshakeAbsence { 695 @Override 696 public void absent(ConnectionContext context, 697 HandshakeMessage message) throws IOException { 698 699 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 700 SSLLogger.fine( 701 "Handling pre_shared_key absence."); 702 } 703 704 ClientHandshakeContext chc = (ClientHandshakeContext)context; 705 706 if (!chc.handshakeExtensions.containsKey(SSLExtension.CH_PRE_SHARED_KEY)) { 707 // absence is expected---nothing to do 708 return; 709 } 710 711 // The PSK identity should not be reused, even if it is 712 // not selected. 713 chc.resumingSession.consumePskIdentity(); 714 715 // If the client requested to resume, the server refused 716 chc.resumingSession = null; 717 chc.isResumption = false; 718 } 719 } 720 721 private static final class SHPreSharedKeyProducer implements HandshakeProducer { 722 723 // Prevent instantiation of this class. 724 private SHPreSharedKeyProducer() { 725 // blank 726 } 727 728 @Override 729 public byte[] produce(ConnectionContext context, 730 HandshakeMessage message) throws IOException { 731 732 ServerHandshakeContext shc = (ServerHandshakeContext) 733 message.handshakeContext; 734 SHPreSharedKeySpec psk = (SHPreSharedKeySpec) 735 shc.handshakeExtensions.get(SH_PRE_SHARED_KEY); 736 if (psk == null) { 737 return null; 738 } 739 740 return psk.getEncoded(); 741 } 742 } 743 }