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.InvalidAlgorithmParameterException;
  32 import java.security.InvalidKeyException;
  33 import java.security.KeyFactory;
  34 import java.security.NoSuchAlgorithmException;
  35 import java.security.Signature;
  36 import java.security.SignatureException;
  37 import java.security.interfaces.ECPublicKey;
  38 import java.security.spec.ECParameterSpec;
  39 import java.security.spec.ECPoint;
  40 import java.security.spec.ECPublicKeySpec;
  41 import java.security.spec.InvalidKeySpecException;
  42 import java.text.MessageFormat;
  43 import java.util.EnumSet;
  44 import java.util.Locale;
  45 import sun.security.ssl.ECDHKeyExchange.ECDHECredentials;
  46 import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
  47 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  48 import sun.security.ssl.SupportedGroupsExtension.NamedGroup;
  49 import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
  50 import sun.security.ssl.X509Authentication.X509Credentials;
  51 import sun.security.ssl.X509Authentication.X509Possession;
  52 import sun.security.util.HexDumpEncoder;
  53 
  54 /**
  55  * Pack of the ServerKeyExchange handshake message.
  56  */
  57 final class ECDHServerKeyExchange {
  58     static final SSLConsumer ecdheHandshakeConsumer =
  59             new ECDHServerKeyExchangeConsumer();
  60     static final HandshakeProducer ecdheHandshakeProducer =
  61             new ECDHServerKeyExchangeProducer();
  62 
  63     /**
  64      * The ECDH ServerKeyExchange handshake message.
  65      */
  66     private static final
  67             class ECDHServerKeyExchangeMessage extends HandshakeMessage {
  68         private static final byte CURVE_NAMED_CURVE = (byte)0x03;
  69 
  70         // id of the named curve
  71         private final NamedGroup namedGroup;
  72 
  73         // encoded public point
  74         private final byte[] publicPoint;
  75 
  76         // signature bytes, or null if anonymous
  77         private final byte[] paramsSignature;
  78 
  79         // public key object encapsulated in this message
  80         private final ECPublicKey publicKey;
  81 
  82         private final boolean useExplicitSigAlgorithm;
  83 
  84         // the signature algorithm used by this ServerKeyExchange message
  85         private final SignatureScheme signatureScheme;
  86 
  87         ECDHServerKeyExchangeMessage(
  88                 HandshakeContext handshakeContext) throws IOException {
  89             super(handshakeContext);
  90 
  91             // This happens in server side only.
  92             ServerHandshakeContext shc =
  93                     (ServerHandshakeContext)handshakeContext;
  94 
  95             ECDHEPossession ecdhePossession = null;
  96             X509Possession x509Possession = null;
  97             for (SSLPossession possession : shc.handshakePossessions) {
  98                 if (possession instanceof ECDHEPossession) {
  99                     ecdhePossession = (ECDHEPossession)possession;
 100                     if (x509Possession != null) {
 101                         break;
 102                     }
 103                 } else if (possession instanceof X509Possession) {
 104                     x509Possession = (X509Possession)possession;
 105                     if (ecdhePossession != null) {
 106                         break;
 107                     }
 108                 }
 109             }
 110 
 111             if (ecdhePossession == null) {
 112                 // unlikely
 113                 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 114                     "No ECDHE credentials negotiated for server key exchange");
 115             }
 116 
 117             publicKey = ecdhePossession.publicKey;
 118             ECParameterSpec params = publicKey.getParams();
 119             ECPoint point = publicKey.getW();
 120             publicPoint = JsseJce.encodePoint(point, params.getCurve());
 121 
 122             this.namedGroup = NamedGroup.valueOf(params);
 123             if ((namedGroup == null) || (namedGroup.oid == null) ) {
 124                 // unlikely
 125                 shc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 126                     "Unnamed EC parameter spec: " + params);
 127             }
 128 
 129             if (x509Possession == null) {
 130                 // anonymous, no authentication, no signature
 131                 paramsSignature = null;
 132                 signatureScheme = null;
 133                 useExplicitSigAlgorithm = false;
 134             } else {
 135                 useExplicitSigAlgorithm =
 136                         shc.negotiatedProtocol.useTLS12PlusSpec();
 137                 Signature signer = null;
 138                 if (useExplicitSigAlgorithm) {
 139                     signatureScheme = SignatureScheme.getPreferableAlgorithm(
 140                             shc.peerRequestedSignatureSchemes,
 141                             x509Possession.popPrivateKey,
 142                             shc.negotiatedProtocol);
 143                     if (signatureScheme == null) {
 144                         // Unlikely, the credentials generator should have
 145                         // selected the preferable signature algorithm properly.
 146                         shc.conContext.fatal(Alert.INTERNAL_ERROR,
 147                                 "No preferred signature algorithm for " +
 148                                 x509Possession.popPrivateKey.getAlgorithm() +
 149                                 "  key");
 150                     }
 151                     try {
 152                         signer = signatureScheme.getSignature();
 153                     } catch (NoSuchAlgorithmException |
 154                             InvalidAlgorithmParameterException nsae) {
 155                         shc.conContext.fatal(Alert.INTERNAL_ERROR,
 156                             "Unsupported signature algorithm: " +
 157                             signatureScheme.name, nsae);
 158                     }
 159                 } else {
 160                     signatureScheme = null;
 161                     try {
 162                         signer = getSignature(
 163                             x509Possession.popPrivateKey.getAlgorithm());
 164                     } catch (NoSuchAlgorithmException e) {
 165                         shc.conContext.fatal(Alert.INTERNAL_ERROR,
 166                             "Unsupported signature algorithm: " +
 167                             x509Possession.popPrivateKey.getAlgorithm(), e);
 168                     }
 169                 }
 170 
 171                 byte[] signature = null;
 172                 try {
 173                     signer.initSign(x509Possession.popPrivateKey);
 174                     updateSignature(signer, shc.clientHelloRandom.randomBytes,
 175                             shc.serverHelloRandom.randomBytes,
 176                             namedGroup.id, publicPoint);
 177                     signature = signer.sign();
 178                 } catch (InvalidKeyException | SignatureException ex) {
 179                     shc.conContext.fatal(Alert.INTERNAL_ERROR,
 180                         "Failed to sign ecdhe parameters: " +
 181                         x509Possession.popPrivateKey.getAlgorithm(), ex);
 182                 }
 183                 paramsSignature = signature;
 184             }
 185         }
 186 
 187         ECDHServerKeyExchangeMessage(HandshakeContext handshakeContext,
 188                 ByteBuffer m) throws IOException {
 189             super(handshakeContext);
 190 
 191             // This happens in client side only.
 192             ClientHandshakeContext chc =
 193                     (ClientHandshakeContext)handshakeContext;
 194 
 195             byte curveType = (byte)Record.getInt8(m);
 196             if (curveType != CURVE_NAMED_CURVE) {
 197                 // Unlikely as only the named curves should be negotiated.
 198                 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 199                     "Unsupported ECCurveType: " + curveType);
 200             }
 201 
 202             int namedGroupId = Record.getInt16(m);
 203             this.namedGroup = NamedGroup.valueOf(namedGroupId);
 204             if (namedGroup == null) {
 205                 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 206                     "Unknown named group ID: " + namedGroupId);
 207             }
 208 
 209             if (!SupportedGroups.isSupported(namedGroup)) {
 210                 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 211                     "Unsupported named group: " + namedGroup);
 212             }
 213 
 214             if (namedGroup.oid == null) {
 215                 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 216                     "Unknown named EC curve: " + namedGroup);
 217             }
 218 
 219             ECParameterSpec parameters =
 220                     JsseJce.getECParameterSpec(namedGroup.oid);
 221             if (parameters == null) {
 222                 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 223                     "No supported EC parameter: " + namedGroup);
 224             }
 225 
 226             publicPoint = Record.getBytes8(m);
 227             if (publicPoint.length == 0) {
 228                 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 229                     "Insufficient ECPoint data: " + namedGroup);
 230             }
 231 
 232             ECPublicKey ecPublicKey = null;
 233             try {
 234                 ECPoint point =
 235                         JsseJce.decodePoint(publicPoint, parameters.getCurve());
 236                 KeyFactory factory = JsseJce.getKeyFactory("EC");
 237                 ecPublicKey = (ECPublicKey)factory.generatePublic(
 238                     new ECPublicKeySpec(point, parameters));
 239             } catch (NoSuchAlgorithmException |
 240                     InvalidKeySpecException | IOException ex) {
 241                 chc.conContext.fatal(Alert.ILLEGAL_PARAMETER,
 242                     "Invalid ECPoint: " + namedGroup, ex);
 243             }
 244 
 245             publicKey = ecPublicKey;
 246 
 247             X509Credentials x509Credentials = null;
 248             for (SSLCredentials cd : chc.handshakeCredentials) {
 249                 if (cd instanceof X509Credentials) {
 250                     x509Credentials = (X509Credentials)cd;
 251                     break;
 252                 }
 253             }
 254 
 255             if (x509Credentials == null) {
 256                 // anonymous, no authentication, no signature
 257                 if (m.hasRemaining()) {
 258                     chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
 259                         "Invalid DH ServerKeyExchange: unknown extra data");
 260                 }
 261                 this.signatureScheme = null;
 262                 this.paramsSignature = null;
 263                 this.useExplicitSigAlgorithm = false;
 264 
 265                 return;
 266             }
 267 
 268             this.useExplicitSigAlgorithm =
 269                     chc.negotiatedProtocol.useTLS12PlusSpec();
 270             if (useExplicitSigAlgorithm) {
 271                 int ssid = Record.getInt16(m);
 272                 signatureScheme = SignatureScheme.valueOf(ssid);
 273                 if (signatureScheme == null) {
 274                     chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
 275                         "Invalid signature algorithm (" + ssid +
 276                         ") used in ECDH ServerKeyExchange handshake message");
 277                 }
 278 
 279                 if (!chc.localSupportedSignAlgs.contains(signatureScheme)) {
 280                     chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
 281                         "Unsupported signature algorithm (" +
 282                         signatureScheme.name +
 283                         ") used in ECDH ServerKeyExchange handshake message");
 284                 }
 285             } else {
 286                 signatureScheme = null;
 287             }
 288 
 289             // read and verify the signature
 290             paramsSignature = Record.getBytes16(m);
 291             Signature signer;
 292             if (useExplicitSigAlgorithm) {
 293                 try {
 294                     signer = signatureScheme.getSignature();
 295                 } catch (NoSuchAlgorithmException |
 296                         InvalidAlgorithmParameterException nsae) {
 297                     chc.conContext.fatal(Alert.INTERNAL_ERROR,
 298                         "Unsupported signature algorithm: " +
 299                         signatureScheme.name, nsae);
 300 
 301                     return;     // make the compiler happe
 302                 }
 303             } else {
 304                 try {
 305                     signer = getSignature(
 306                         x509Credentials.popPublicKey.getAlgorithm());
 307                 } catch (NoSuchAlgorithmException e) {
 308                     chc.conContext.fatal(Alert.INTERNAL_ERROR,
 309                         "Unsupported signature algorithm: " +
 310                         x509Credentials.popPublicKey.getAlgorithm(), e);
 311 
 312                     return;     // make the compiler happe
 313                 }
 314             }
 315 
 316             try {
 317                 signer.initVerify(x509Credentials.popPublicKey);
 318                 updateSignature(signer,
 319                         chc.clientHelloRandom.randomBytes,
 320                         chc.serverHelloRandom.randomBytes,
 321                         namedGroup.id, publicPoint);
 322 
 323                 if (!signer.verify(paramsSignature)) {
 324                     chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
 325                         "Invalid ECDH ServerKeyExchange signature");
 326                 }
 327             } catch (InvalidKeyException | SignatureException ex) {
 328                 chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
 329                         "Cannot verify ECDH ServerKeyExchange signature", ex);
 330             }
 331         }
 332 
 333         @Override
 334         public SSLHandshake handshakeType() {
 335             return SSLHandshake.SERVER_KEY_EXCHANGE;
 336         }
 337 
 338         @Override
 339         public int messageLength() {
 340             int sigLen = 0;
 341             if (paramsSignature != null) {
 342                 sigLen = 2 + paramsSignature.length;
 343                 if (useExplicitSigAlgorithm) {
 344                     sigLen += SignatureScheme.sizeInRecord();
 345                 }
 346             }
 347 
 348             return 4 + publicPoint.length + sigLen;
 349         }
 350 
 351         @Override
 352         public void send(HandshakeOutStream hos) throws IOException {
 353             hos.putInt8(CURVE_NAMED_CURVE);
 354             hos.putInt16(namedGroup.id);
 355             hos.putBytes8(publicPoint);
 356             if (paramsSignature != null) {
 357                 if (useExplicitSigAlgorithm) {
 358                     hos.putInt16(signatureScheme.id);
 359                 }
 360 
 361                 hos.putBytes16(paramsSignature);
 362             }
 363         }
 364 
 365         @Override
 366         public String toString() {
 367             if (useExplicitSigAlgorithm) {
 368                 MessageFormat messageFormat = new MessageFormat(
 369                     "\"ECDH ServerKeyExchange\": '{'\n" +
 370                     "  \"parameters\": '{'\n" +
 371                     "    \"named group\": \"{0}\"\n" +
 372                     "    \"ecdh public\": '{'\n" +
 373                     "{1}\n" +
 374                     "    '}',\n" +
 375                     "  '}',\n" +
 376                     "  \"digital signature\":  '{'\n" +
 377                     "    \"signature algorithm\": \"{2}\"\n" +
 378                     "    \"signature\": '{'\n" +
 379                     "{3}\n" +
 380                     "    '}',\n" +
 381                     "  '}'\n" +
 382                     "'}'",
 383                     Locale.ENGLISH);
 384 
 385                 HexDumpEncoder hexEncoder = new HexDumpEncoder();
 386                 Object[] messageFields = {
 387                     namedGroup.name,
 388                     Utilities.indent(
 389                             hexEncoder.encodeBuffer(publicPoint), "      "),
 390                     signatureScheme.name,
 391                     Utilities.indent(
 392                             hexEncoder.encodeBuffer(paramsSignature), "      ")
 393                 };
 394                 return messageFormat.format(messageFields);
 395             } else if (paramsSignature != null) {
 396                 MessageFormat messageFormat = new MessageFormat(
 397                     "\"ECDH ServerKeyExchange\": '{'\n" +
 398                     "  \"parameters\":  '{'\n" +
 399                     "    \"named group\": \"{0}\"\n" +
 400                     "    \"ecdh public\": '{'\n" +
 401                     "{1}\n" +
 402                     "    '}',\n" +
 403                     "  '}',\n" +
 404                     "  \"signature\": '{'\n" +
 405                     "{2}\n" +
 406                     "  '}'\n" +
 407                     "'}'",
 408                     Locale.ENGLISH);
 409 
 410                 HexDumpEncoder hexEncoder = new HexDumpEncoder();
 411                 Object[] messageFields = {
 412                     namedGroup.name,
 413                     Utilities.indent(
 414                             hexEncoder.encodeBuffer(publicPoint), "      "),
 415                     Utilities.indent(
 416                             hexEncoder.encodeBuffer(paramsSignature), "    ")
 417                 };
 418 
 419                 return messageFormat.format(messageFields);
 420             } else {    // anonymous
 421                 MessageFormat messageFormat = new MessageFormat(
 422                     "\"ECDH ServerKeyExchange\": '{'\n" +
 423                     "  \"parameters\":  '{'\n" +
 424                     "    \"named group\": \"{0}\"\n" +
 425                     "    \"ecdh public\": '{'\n" +
 426                     "{1}\n" +
 427                     "    '}',\n" +
 428                     "  '}'\n" +
 429                     "'}'",
 430                     Locale.ENGLISH);
 431 
 432                 HexDumpEncoder hexEncoder = new HexDumpEncoder();
 433                 Object[] messageFields = {
 434                     namedGroup.name,
 435                     Utilities.indent(
 436                             hexEncoder.encodeBuffer(publicPoint), "      "),
 437                 };
 438 
 439                 return messageFormat.format(messageFields);
 440             }
 441         }
 442 
 443         private static Signature getSignature(String keyAlgorithm)
 444                 throws NoSuchAlgorithmException {
 445             switch (keyAlgorithm) {
 446                 case "EC":
 447                     return JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA);
 448                 case "RSA":
 449                     return RSASignature.getInstance();
 450                 default:
 451                     throw new NoSuchAlgorithmException(
 452                         "neither an RSA or a EC key : " + keyAlgorithm);
 453             }
 454         }
 455 
 456         private static void updateSignature(Signature sig,
 457                 byte[] clntNonce, byte[] svrNonce, int namedGroupId,
 458                 byte[] publicPoint) throws SignatureException {
 459             sig.update(clntNonce);
 460             sig.update(svrNonce);
 461 
 462             sig.update(CURVE_NAMED_CURVE);
 463             sig.update((byte)((namedGroupId >> 8) & 0xFF));
 464             sig.update((byte)(namedGroupId & 0xFF));
 465             sig.update((byte)publicPoint.length);
 466             sig.update(publicPoint);
 467         }
 468     }
 469 
 470     /**
 471      * The ECDH "ServerKeyExchange" handshake message producer.
 472      */
 473     private static final
 474             class ECDHServerKeyExchangeProducer implements HandshakeProducer {
 475         // Prevent instantiation of this class.
 476         private ECDHServerKeyExchangeProducer() {
 477             // blank
 478         }
 479 
 480         @Override
 481         public byte[] produce(ConnectionContext context,
 482                 HandshakeMessage message) throws IOException {
 483             // The producing happens in server side only.
 484             ServerHandshakeContext shc = (ServerHandshakeContext)context;
 485             ECDHServerKeyExchangeMessage skem =
 486                     new ECDHServerKeyExchangeMessage(shc);
 487             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 488                 SSLLogger.fine(
 489                     "Produced ECDH ServerKeyExchange handshake message", skem);
 490             }
 491 
 492             // Output the handshake message.
 493             skem.write(shc.handshakeOutput);
 494             shc.handshakeOutput.flush();
 495 
 496             // The handshake message has been delivered.
 497             return null;
 498         }
 499     }
 500 
 501     /**
 502      * The ECDH "ServerKeyExchange" handshake message consumer.
 503      */
 504     private static final
 505             class ECDHServerKeyExchangeConsumer implements SSLConsumer {
 506         // Prevent instantiation of this class.
 507         private ECDHServerKeyExchangeConsumer() {
 508             // blank
 509         }
 510 
 511         @Override
 512         public void consume(ConnectionContext context,
 513                 ByteBuffer message) throws IOException {
 514             // The consuming happens in client side only.
 515             ClientHandshakeContext chc = (ClientHandshakeContext)context;
 516 
 517             ECDHServerKeyExchangeMessage skem =
 518                     new ECDHServerKeyExchangeMessage(chc, message);
 519             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 520                 SSLLogger.fine(
 521                     "Consuming ECDH ServerKeyExchange handshake message", skem);
 522             }
 523 
 524             //
 525             // validate
 526             //
 527             // check constraints of EC PublicKey
 528             if (!chc.algorithmConstraints.permits(
 529                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),
 530                     skem.publicKey)) {
 531                 chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY,
 532                         "ECDH ServerKeyExchange does not comply " +
 533                         "to algorithm constraints");
 534             }
 535 
 536             //
 537             // update
 538             //
 539             chc.handshakeCredentials.add(
 540                     new ECDHECredentials(skem.publicKey, skem.namedGroup));
 541 
 542             //
 543             // produce
 544             //
 545             // Need no new handshake message producers here.
 546         }
 547     }
 548 }
 549