1 /* 2 * Copyright (c) 2015, 2018, 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 26 package sun.security.ssl; 27 28 import java.io.IOException; 29 import java.nio.ByteBuffer; 30 import java.security.CryptoPrimitive; 31 import java.security.GeneralSecurityException; 32 import java.text.MessageFormat; 33 import java.util.Arrays; 34 import java.util.Collections; 35 import java.util.EnumSet; 36 import java.util.LinkedList; 37 import java.util.List; 38 import java.util.Locale; 39 import java.util.Map; 40 import javax.net.ssl.SSLProtocolException; 41 import sun.security.ssl.DHKeyExchange.DHECredentials; 42 import sun.security.ssl.DHKeyExchange.DHEPossession; 43 import sun.security.ssl.ECDHKeyExchange.ECDHECredentials; 44 import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; 45 import sun.security.ssl.KeyShareExtension.CHKeyShareSpec; 46 import sun.security.ssl.SSLExtension.ExtensionConsumer; 47 import sun.security.ssl.SSLExtension.SSLExtensionSpec; 48 import sun.security.ssl.SSLHandshake.HandshakeMessage; 49 import sun.security.ssl.SupportedGroupsExtension.NamedGroup; 50 import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; 51 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; 52 import sun.security.util.HexDumpEncoder; 53 54 /** 55 * Pack of the "key_share" extensions. 56 */ 57 final class KeyShareExtension { 58 static final HandshakeProducer chNetworkProducer = 59 new CHKeyShareProducer(); 60 static final ExtensionConsumer chOnLoadConsumer = 61 new CHKeyShareConsumer(); 62 static final SSLStringizer chStringizer = 63 new CHKeyShareStringizer(); 64 65 static final HandshakeProducer shNetworkProducer = 66 new SHKeyShareProducer(); 67 static final ExtensionConsumer shOnLoadConsumer = 68 new SHKeyShareConsumer(); 69 static final HandshakeAbsence shOnLoadAbsence = 70 new SHKeyShareAbsence(); 71 static final SSLStringizer shStringizer = 72 new SHKeyShareStringizer(); 73 74 static final HandshakeProducer hrrNetworkProducer = 75 new HRRKeyShareProducer(); 76 static final ExtensionConsumer hrrOnLoadConsumer = 77 new HRRKeyShareConsumer(); 78 static final HandshakeProducer hrrNetworkReproducer = 79 new HRRKeyShareReproducer(); 80 static final SSLStringizer hrrStringizer = 81 new HRRKeyShareStringizer(); 82 83 /** 84 * The key share entry used in "key_share" extensions. 85 */ 86 private static final class KeyShareEntry { 87 final int namedGroupId; 88 final byte[] keyExchange; 89 90 private KeyShareEntry(int namedGroupId, byte[] keyExchange) { 91 this.namedGroupId = namedGroupId; 92 this.keyExchange = keyExchange; 93 } 94 95 private byte[] getEncoded() { 96 byte[] buffer = new byte[keyExchange.length + 4]; 97 // 2: named group id 98 // +2: key exchange length 99 ByteBuffer m = ByteBuffer.wrap(buffer); 100 try { 101 Record.putInt16(m, namedGroupId); 102 Record.putBytes16(m, keyExchange); 103 } catch (IOException ioe) { 104 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 105 SSLLogger.warning( 106 "Unlikely IOException", ioe); 107 } 108 } 109 110 return buffer; 111 } 112 113 private int getEncodedSize() { 114 return keyExchange.length + 4; // 2: named group id 115 // +2: key exchange length 116 } 117 118 @Override 119 public String toString() { 120 MessageFormat messageFormat = new MessageFormat( 121 "\n'{'\n" + 122 " \"named group\": {0}\n" + 123 " \"key_exchange\": '{'\n" + 124 "{1}\n" + 125 " '}'\n" + 126 "'}',", Locale.ENGLISH); 127 128 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 129 Object[] messageFields = { 130 NamedGroup.nameOf(namedGroupId), 131 Utilities.indent(hexEncoder.encode(keyExchange), " ") 132 }; 133 134 return messageFormat.format(messageFields); 135 } 136 } 137 138 /** 139 * The "key_share" extension in a ClientHello handshake message. 140 */ 141 static final class CHKeyShareSpec implements SSLExtensionSpec { 142 final List<KeyShareEntry> clientShares; 143 144 private CHKeyShareSpec(List<KeyShareEntry> clientShares) { 145 this.clientShares = clientShares; 146 } 147 148 private CHKeyShareSpec(ByteBuffer buffer) throws IOException { 149 // struct { 150 // KeyShareEntry client_shares<0..2^16-1>; 151 // } KeyShareClientHello; 152 if (buffer.remaining() < 2) { 153 throw new SSLProtocolException( 154 "Invalid key_share extension: " + 155 "insufficient data (length=" + buffer.remaining() + ")"); 156 } 157 158 int listLen = Record.getInt16(buffer); 159 if (listLen != buffer.remaining()) { 160 throw new SSLProtocolException( 161 "Invalid key_share extension: " + 162 "incorrect list length (length=" + listLen + ")"); 163 } 164 165 List<KeyShareEntry> keyShares = new LinkedList<>(); 166 while (buffer.hasRemaining()) { 167 int namedGroupId = Record.getInt16(buffer); 168 byte[] keyExchange = Record.getBytes16(buffer); 169 if (keyExchange.length == 0) { 170 throw new SSLProtocolException( 171 "Invalid key_share extension: empty key_exchange"); 172 } 173 174 keyShares.add(new KeyShareEntry(namedGroupId, keyExchange)); 175 } 176 177 this.clientShares = Collections.unmodifiableList(keyShares); 178 } 179 180 @Override 181 public String toString() { 182 MessageFormat messageFormat = new MessageFormat( 183 "\"client_shares\": '['{0}\n']'", Locale.ENGLISH); 184 185 StringBuilder builder = new StringBuilder(512); 186 for (KeyShareEntry entry : clientShares) { 187 builder.append(entry.toString()); 188 } 189 190 Object[] messageFields = { 191 Utilities.indent(builder.toString()) 192 }; 193 194 return messageFormat.format(messageFields); 195 } 196 } 197 198 private static final class CHKeyShareStringizer implements SSLStringizer { 199 @Override 200 public String toString(ByteBuffer buffer) { 201 try { 202 return (new CHKeyShareSpec(buffer)).toString(); 203 } catch (IOException ioe) { 204 // For debug logging only, so please swallow exceptions. 205 return ioe.getMessage(); 206 } 207 } 208 } 209 210 /** 211 * Network data producer of the extension in a ClientHello 212 * handshake message. 213 */ 214 private static final 215 class CHKeyShareProducer implements HandshakeProducer { 216 // Prevent instantiation of this class. 217 private CHKeyShareProducer() { 218 // blank 219 } 220 221 @Override 222 public byte[] produce(ConnectionContext context, 223 HandshakeMessage message) throws IOException { 224 // The producing happens in client side only. 225 ClientHandshakeContext chc = (ClientHandshakeContext)context; 226 227 // Is it a supported and enabled extension? 228 if (!chc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) { 229 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 230 SSLLogger.fine( 231 "Ignore unavailable key_share extension"); 232 } 233 return null; 234 } 235 236 List<NamedGroup> namedGroups; 237 if (chc.serverSelectedNamedGroup != null) { 238 // Response to HelloRetryRequest 239 namedGroups = Arrays.asList(chc.serverSelectedNamedGroup); 240 } else { 241 namedGroups = chc.clientRequestedNamedGroups; 242 if (namedGroups == null || namedGroups.isEmpty()) { 243 // No supported groups. 244 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 245 SSLLogger.warning( 246 "Ignore key_share extension, no supported groups"); 247 } 248 return null; 249 } 250 } 251 252 List<KeyShareEntry> keyShares = new LinkedList<>(); 253 for (NamedGroup ng : namedGroups) { 254 SSLKeyExchange ke = SSLKeyExchange.valueOf(ng); 255 if (ke == null) { 256 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 257 SSLLogger.warning( 258 "No key exchange for named group " + ng.name); 259 } 260 continue; 261 } 262 263 SSLPossession[] poses = ke.createPossessions(chc); 264 for (SSLPossession pos : poses) { 265 // update the context 266 chc.handshakePossessions.add(pos); 267 if (!(pos instanceof ECDHEPossession) && 268 !(pos instanceof DHEPossession)) { 269 // May need more possesion types in the future. 270 continue; 271 } 272 273 keyShares.add(new KeyShareEntry(ng.id, pos.encode())); 274 } 275 276 // One key share entry only. Too much key share entries makes 277 // the ClientHello handshake message really big. 278 if (!keyShares.isEmpty()) { 279 break; 280 } 281 } 282 283 int listLen = 0; 284 for (KeyShareEntry entry : keyShares) { 285 listLen += entry.getEncodedSize(); 286 } 287 byte[] extData = new byte[listLen + 2]; // 2: list length 288 ByteBuffer m = ByteBuffer.wrap(extData); 289 Record.putInt16(m, listLen); 290 for (KeyShareEntry entry : keyShares) { 291 m.put(entry.getEncoded()); 292 } 293 294 // update the context 295 chc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE, 296 new CHKeyShareSpec(keyShares)); 297 298 return extData; 299 } 300 } 301 302 /** 303 * Network data consumer of the extension in a ClientHello 304 * handshake message. 305 */ 306 private static final class CHKeyShareConsumer implements ExtensionConsumer { 307 // Prevent instantiation of this class. 308 private CHKeyShareConsumer() { 309 // blank 310 } 311 312 @Override 313 public void consume(ConnectionContext context, 314 HandshakeMessage message, ByteBuffer buffer) throws IOException { 315 // The consuming happens in server side only. 316 ServerHandshakeContext shc = (ServerHandshakeContext)context; 317 318 if (shc.handshakeExtensions.containsKey(SSLExtension.CH_KEY_SHARE)) { 319 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 320 SSLLogger.fine( 321 "The key_share extension has been loaded"); 322 } 323 return; 324 } 325 326 // Is it a supported and enabled extension? 327 if (!shc.sslConfig.isAvailable(SSLExtension.CH_KEY_SHARE)) { 328 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 329 SSLLogger.fine( 330 "Ignore unavailable key_share extension"); 331 } 332 return; // ignore the extension 333 } 334 335 // Parse the extension 336 CHKeyShareSpec spec; 337 try { 338 spec = new CHKeyShareSpec(buffer); 339 } catch (IOException ioe) { 340 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 341 } 342 343 List<SSLCredentials> credentials = new LinkedList<>(); 344 for (KeyShareEntry entry : spec.clientShares) { 345 NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId); 346 if (ng == null || !SupportedGroups.isActivatable( 347 shc.algorithmConstraints, ng)) { 348 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 349 SSLLogger.fine( 350 "Ignore unsupported named group: " + 351 NamedGroup.nameOf(entry.namedGroupId)); 352 } 353 continue; 354 } 355 356 if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) { 357 try { 358 ECDHECredentials ecdhec = 359 ECDHECredentials.valueOf(ng, entry.keyExchange); 360 if (ecdhec != null) { 361 if (!shc.algorithmConstraints.permits( 362 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 363 ecdhec.popPublicKey)) { 364 SSLLogger.warning( 365 "ECDHE key share entry does not " + 366 "comply to algorithm constraints"); 367 } else { 368 credentials.add(ecdhec); 369 } 370 } 371 } catch (IOException | GeneralSecurityException ex) { 372 SSLLogger.warning( 373 "Cannot decode named group: " + 374 NamedGroup.nameOf(entry.namedGroupId)); 375 } 376 } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) { 377 try { 378 DHECredentials dhec = 379 DHECredentials.valueOf(ng, entry.keyExchange); 380 if (dhec != null) { 381 if (!shc.algorithmConstraints.permits( 382 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 383 dhec.popPublicKey)) { 384 SSLLogger.warning( 385 "DHE key share entry does not " + 386 "comply to algorithm constraints"); 387 } else { 388 credentials.add(dhec); 389 } 390 } 391 } catch (IOException | GeneralSecurityException ex) { 392 SSLLogger.warning( 393 "Cannot decode named group: " + 394 NamedGroup.nameOf(entry.namedGroupId)); 395 } 396 } 397 } 398 399 if (!credentials.isEmpty()) { 400 shc.handshakeCredentials.addAll(credentials); 401 } else { 402 // New handshake credentials are required from the client side. 403 shc.handshakeProducers.put( 404 SSLHandshake.HELLO_RETRY_REQUEST.id, 405 SSLHandshake.HELLO_RETRY_REQUEST); 406 } 407 408 // update the context 409 shc.handshakeExtensions.put(SSLExtension.CH_KEY_SHARE, spec); 410 } 411 } 412 413 /** 414 * The key share entry used in ServerHello "key_share" extensions. 415 */ 416 static final class SHKeyShareSpec implements SSLExtensionSpec { 417 final KeyShareEntry serverShare; 418 419 SHKeyShareSpec(KeyShareEntry serverShare) { 420 this.serverShare = serverShare; 421 } 422 423 private SHKeyShareSpec(ByteBuffer buffer) throws IOException { 424 // struct { 425 // KeyShareEntry server_share; 426 // } KeyShareServerHello; 427 if (buffer.remaining() < 5) { // 5: minimal server_share 428 throw new SSLProtocolException( 429 "Invalid key_share extension: " + 430 "insufficient data (length=" + buffer.remaining() + ")"); 431 } 432 433 int namedGroupId = Record.getInt16(buffer); 434 byte[] keyExchange = Record.getBytes16(buffer); 435 436 if (buffer.hasRemaining()) { 437 throw new SSLProtocolException( 438 "Invalid key_share extension: unknown extra data"); 439 } 440 441 this.serverShare = new KeyShareEntry(namedGroupId, keyExchange); 442 } 443 444 @Override 445 public String toString() { 446 MessageFormat messageFormat = new MessageFormat( 447 "\"server_share\": '{'\n" + 448 " \"named group\": {0}\n" + 449 " \"key_exchange\": '{'\n" + 450 "{1}\n" + 451 " '}'\n" + 452 "'}',", Locale.ENGLISH); 453 454 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 455 Object[] messageFields = { 456 NamedGroup.nameOf(serverShare.namedGroupId), 457 Utilities.indent( 458 hexEncoder.encode(serverShare.keyExchange), " ") 459 }; 460 461 return messageFormat.format(messageFields); 462 } 463 } 464 465 private static final class SHKeyShareStringizer implements SSLStringizer { 466 @Override 467 public String toString(ByteBuffer buffer) { 468 try { 469 return (new SHKeyShareSpec(buffer)).toString(); 470 } catch (IOException ioe) { 471 // For debug logging only, so please swallow exceptions. 472 return ioe.getMessage(); 473 } 474 } 475 } 476 477 /** 478 * Network data producer of the extension in a ServerHello 479 * handshake message. 480 */ 481 private static final class SHKeyShareProducer implements HandshakeProducer { 482 // Prevent instantiation of this class. 483 private SHKeyShareProducer() { 484 // blank 485 } 486 487 @Override 488 public byte[] produce(ConnectionContext context, 489 HandshakeMessage message) throws IOException { 490 // The producing happens in client side only. 491 ServerHandshakeContext shc = (ServerHandshakeContext)context; 492 493 // In response to key_share request only 494 CHKeyShareSpec kss = 495 (CHKeyShareSpec)shc.handshakeExtensions.get( 496 SSLExtension.CH_KEY_SHARE); 497 if (kss == null) { 498 // Unlikely, no key_share extension requested. 499 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 500 SSLLogger.warning( 501 "Ignore, no client key_share extension"); 502 } 503 return null; 504 } 505 506 // Is it a supported and enabled extension? 507 if (!shc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) { 508 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 509 SSLLogger.warning( 510 "Ignore, no available server key_share extension"); 511 } 512 return null; 513 } 514 515 // use requested key share entries 516 if ((shc.handshakeCredentials == null) || 517 shc.handshakeCredentials.isEmpty()) { 518 // Unlikely, HelloRetryRequest should be used ealier. 519 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 520 SSLLogger.warning( 521 "No available client key share entries"); 522 } 523 return null; 524 } 525 526 KeyShareEntry keyShare = null; 527 for (SSLCredentials cd : shc.handshakeCredentials) { 528 NamedGroup ng = null; 529 if (cd instanceof ECDHECredentials) { 530 ng = ((ECDHECredentials)cd).namedGroup; 531 } else if (cd instanceof DHECredentials) { 532 ng = ((DHECredentials)cd).namedGroup; 533 } 534 535 if (ng == null) { 536 continue; 537 } 538 539 SSLKeyExchange ke = SSLKeyExchange.valueOf(ng); 540 if (ke == null) { 541 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 542 SSLLogger.warning( 543 "No key exchange for named group " + ng.name); 544 } 545 continue; 546 } 547 548 SSLPossession[] poses = ke.createPossessions(shc); 549 for (SSLPossession pos : poses) { 550 if (!(pos instanceof ECDHEPossession) && 551 !(pos instanceof DHEPossession)) { 552 // May need more possesion types in the future. 553 continue; 554 } 555 556 // update the context 557 shc.handshakeKeyExchange = ke; 558 shc.handshakePossessions.add(pos); 559 keyShare = new KeyShareEntry(ng.id, pos.encode()); 560 break; 561 } 562 563 if (keyShare != null) { 564 for (Map.Entry<Byte, HandshakeProducer> me : 565 ke.getHandshakeProducers(shc)) { 566 shc.handshakeProducers.put( 567 me.getKey(), me.getValue()); 568 } 569 570 // We have got one! Don't forgor to break. 571 break; 572 } 573 } 574 575 if (keyShare == null) { 576 // Unlikely, HelloRetryRequest should be used instead ealier. 577 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 578 SSLLogger.warning( 579 "No available server key_share extension"); 580 } 581 return null; 582 } 583 584 byte[] extData = keyShare.getEncoded(); 585 586 // update the context 587 SHKeyShareSpec spec = new SHKeyShareSpec(keyShare); 588 shc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec); 589 590 return extData; 591 } 592 } 593 594 /** 595 * Network data consumer of the extension in a ServerHello 596 * handshake message. 597 */ 598 private static final class SHKeyShareConsumer implements ExtensionConsumer { 599 // Prevent instantiation of this class. 600 private SHKeyShareConsumer() { 601 // blank 602 } 603 604 @Override 605 public void consume(ConnectionContext context, 606 HandshakeMessage message, ByteBuffer buffer) throws IOException { 607 // Happens in client side only. 608 ClientHandshakeContext chc = (ClientHandshakeContext)context; 609 if (chc.clientRequestedNamedGroups == null || 610 chc.clientRequestedNamedGroups.isEmpty()) { 611 // No supported groups. 612 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 613 "Unexpected key_share extension in ServerHello"); 614 } 615 616 // Is it a supported and enabled extension? 617 if (!chc.sslConfig.isAvailable(SSLExtension.SH_KEY_SHARE)) { 618 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 619 "Unsupported key_share extension in ServerHello"); 620 } 621 622 // Parse the extension 623 SHKeyShareSpec spec; 624 try { 625 spec = new SHKeyShareSpec(buffer); 626 } catch (IOException ioe) { 627 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 628 } 629 630 KeyShareEntry keyShare = spec.serverShare; 631 NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId); 632 if (ng == null || !SupportedGroups.isActivatable( 633 chc.algorithmConstraints, ng)) { 634 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 635 "Unsupported named group: " + 636 NamedGroup.nameOf(keyShare.namedGroupId)); 637 } 638 639 SSLKeyExchange ke = SSLKeyExchange.valueOf(ng); 640 if (ke == null) { 641 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 642 "No key exchange for named group " + ng.name); 643 } 644 645 SSLCredentials credentials = null; 646 if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) { 647 try { 648 ECDHECredentials ecdhec = 649 ECDHECredentials.valueOf(ng, keyShare.keyExchange); 650 if (ecdhec != null) { 651 if (!chc.algorithmConstraints.permits( 652 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 653 ecdhec.popPublicKey)) { 654 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 655 "ECDHE key share entry does not " + 656 "comply to algorithm constraints"); 657 } else { 658 credentials = ecdhec; 659 } 660 } 661 } catch (IOException | GeneralSecurityException ex) { 662 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 663 "Cannot decode named group: " + 664 NamedGroup.nameOf(keyShare.namedGroupId)); 665 } 666 } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) { 667 try { 668 DHECredentials dhec = 669 DHECredentials.valueOf(ng, keyShare.keyExchange); 670 if (dhec != null) { 671 if (!chc.algorithmConstraints.permits( 672 EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), 673 dhec.popPublicKey)) { 674 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 675 "DHE key share entry does not " + 676 "comply to algorithm constraints"); 677 } else { 678 credentials = dhec; 679 } 680 } 681 } catch (IOException | GeneralSecurityException ex) { 682 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 683 "Cannot decode named group: " + 684 NamedGroup.nameOf(keyShare.namedGroupId)); 685 } 686 } else { 687 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 688 "Unsupported named group: " + 689 NamedGroup.nameOf(keyShare.namedGroupId)); 690 } 691 692 if (credentials == null) { 693 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 694 "Unsupported named group: " + ng.name); 695 } 696 697 // update the context 698 chc.handshakeKeyExchange = ke; 699 chc.handshakeCredentials.add(credentials); 700 chc.handshakeExtensions.put(SSLExtension.SH_KEY_SHARE, spec); 701 } 702 } 703 704 /** 705 * The absence processing if the extension is not present in 706 * the ServerHello handshake message. 707 */ 708 private static final class SHKeyShareAbsence implements HandshakeAbsence { 709 @Override 710 public void absent(ConnectionContext context, 711 HandshakeMessage message) throws IOException { 712 // The producing happens in client side only. 713 ClientHandshakeContext chc = (ClientHandshakeContext)context; 714 715 // Cannot use the previous requested key shares any more. 716 if (SSLLogger.isOn && SSLLogger.isOn("handshake")) { 717 SSLLogger.fine( 718 "No key_share extension in ServerHello, " + 719 "cleanup the key shares if necessary"); 720 } 721 chc.handshakePossessions.clear(); 722 } 723 } 724 725 /** 726 * The key share entry used in HelloRetryRequest "key_share" extensions. 727 */ 728 static final class HRRKeyShareSpec implements SSLExtensionSpec { 729 final int selectedGroup; 730 731 HRRKeyShareSpec(NamedGroup serverGroup) { 732 this.selectedGroup = serverGroup.id; 733 } 734 735 private HRRKeyShareSpec(ByteBuffer buffer) throws IOException { 736 // struct { 737 // NamedGroup selected_group; 738 // } KeyShareHelloRetryRequest; 739 if (buffer.remaining() != 2) { 740 throw new SSLProtocolException( 741 "Invalid key_share extension: " + 742 "improper data (length=" + buffer.remaining() + ")"); 743 } 744 745 this.selectedGroup = Record.getInt16(buffer); 746 } 747 748 @Override 749 public String toString() { 750 MessageFormat messageFormat = new MessageFormat( 751 "\"selected group\": '['{0}']'", Locale.ENGLISH); 752 753 Object[] messageFields = { 754 NamedGroup.nameOf(selectedGroup) 755 }; 756 return messageFormat.format(messageFields); 757 } 758 } 759 760 private static final class HRRKeyShareStringizer implements SSLStringizer { 761 @Override 762 public String toString(ByteBuffer buffer) { 763 try { 764 return (new HRRKeyShareSpec(buffer)).toString(); 765 } catch (IOException ioe) { 766 // For debug logging only, so please swallow exceptions. 767 return ioe.getMessage(); 768 } 769 } 770 } 771 772 /** 773 * Network data producer of the extension in a HelloRetryRequest 774 * handshake message. 775 */ 776 private static final 777 class HRRKeyShareProducer implements HandshakeProducer { 778 // Prevent instantiation of this class. 779 private HRRKeyShareProducer() { 780 // blank 781 } 782 783 @Override 784 public byte[] produce(ConnectionContext context, 785 HandshakeMessage message) throws IOException { 786 // The producing happens in server side only. 787 ServerHandshakeContext shc = (ServerHandshakeContext) context; 788 789 // Is it a supported and enabled extension? 790 if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) { 791 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 792 "Unsupported key_share extension in HelloRetryRequest"); 793 } 794 795 if (shc.clientRequestedNamedGroups == null || 796 shc.clientRequestedNamedGroups.isEmpty()) { 797 // No supported groups. 798 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 799 "Unexpected key_share extension in HelloRetryRequest"); 800 } 801 802 NamedGroup selectedGroup = null; 803 for (NamedGroup ng : shc.clientRequestedNamedGroups) { 804 if (SupportedGroups.isActivatable( 805 shc.algorithmConstraints, ng)) { 806 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 807 SSLLogger.fine( 808 "HelloRetryRequest selected named group: " + 809 ng.name); 810 } 811 812 selectedGroup = ng; 813 break; 814 } 815 } 816 817 if (selectedGroup == null) { 818 throw shc.conContext.fatal( 819 Alert.UNEXPECTED_MESSAGE, "No common named group"); 820 } 821 822 byte[] extdata = new byte[] { 823 (byte)((selectedGroup.id >> 8) & 0xFF), 824 (byte)(selectedGroup.id & 0xFF) 825 }; 826 827 // update the context 828 shc.serverSelectedNamedGroup = selectedGroup; 829 shc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE, 830 new HRRKeyShareSpec(selectedGroup)); 831 832 return extdata; 833 } 834 } 835 836 /** 837 * Network data producer of the extension for stateless 838 * HelloRetryRequest reconstruction. 839 */ 840 private static final 841 class HRRKeyShareReproducer implements HandshakeProducer { 842 // Prevent instantiation of this class. 843 private HRRKeyShareReproducer() { 844 // blank 845 } 846 847 @Override 848 public byte[] produce(ConnectionContext context, 849 HandshakeMessage message) throws IOException { 850 // The producing happens in server side only. 851 ServerHandshakeContext shc = (ServerHandshakeContext) context; 852 853 // Is it a supported and enabled extension? 854 if (!shc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) { 855 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 856 "Unsupported key_share extension in HelloRetryRequest"); 857 } 858 859 CHKeyShareSpec spec = (CHKeyShareSpec)shc.handshakeExtensions.get( 860 SSLExtension.CH_KEY_SHARE); 861 if (spec != null && spec.clientShares != null && 862 spec.clientShares.size() == 1) { 863 int namedGroupId = spec.clientShares.get(0).namedGroupId; 864 865 byte[] extdata = new byte[] { 866 (byte)((namedGroupId >> 8) & 0xFF), 867 (byte)(namedGroupId & 0xFF) 868 }; 869 870 return extdata; 871 } 872 873 return null; 874 } 875 } 876 877 /** 878 * Network data consumer of the extension in a HelloRetryRequest 879 * handshake message. 880 */ 881 private static final 882 class HRRKeyShareConsumer implements ExtensionConsumer { 883 // Prevent instantiation of this class. 884 private HRRKeyShareConsumer() { 885 // blank 886 } 887 888 @Override 889 public void consume(ConnectionContext context, 890 HandshakeMessage message, ByteBuffer buffer) throws IOException { 891 // The producing happens in client side only. 892 ClientHandshakeContext chc = (ClientHandshakeContext)context; 893 894 // Is it a supported and enabled extension? 895 if (!chc.sslConfig.isAvailable(SSLExtension.HRR_KEY_SHARE)) { 896 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 897 "Unsupported key_share extension in HelloRetryRequest"); 898 } 899 900 if (chc.clientRequestedNamedGroups == null || 901 chc.clientRequestedNamedGroups.isEmpty()) { 902 // No supported groups. 903 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 904 "Unexpected key_share extension in HelloRetryRequest"); 905 } 906 907 // Parse the extension 908 HRRKeyShareSpec spec; 909 try { 910 spec = new HRRKeyShareSpec(buffer); 911 } catch (IOException ioe) { 912 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 913 } 914 915 NamedGroup serverGroup = NamedGroup.valueOf(spec.selectedGroup); 916 if (serverGroup == null) { 917 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 918 "Unsupported HelloRetryRequest selected group: " + 919 NamedGroup.nameOf(spec.selectedGroup)); 920 } 921 922 if (!chc.clientRequestedNamedGroups.contains(serverGroup)) { 923 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 924 "Unexpected HelloRetryRequest selected group: " + 925 serverGroup.name); 926 } 927 928 // update the context 929 930 // When sending the new ClientHello, the client MUST replace the 931 // original "key_share" extension with one containing only a new 932 // KeyShareEntry for the group indicated in the selected_group 933 // field of the triggering HelloRetryRequest. 934 // 935 chc.serverSelectedNamedGroup = serverGroup; 936 chc.handshakeExtensions.put(SSLExtension.HRR_KEY_SHARE, spec); 937 } 938 } 939 }