# HG changeset patch # User wetmore # Date 1560391080 25200 # Node ID 946f7f2d321cbe231e7b82390f8bf51c36c7c2e9 # Parent 1357c4996b2e54a03595ea505b9bc91b839da201 8171279: Support X25519 and X448 in TLS Reviewed-by: xuelei, mullan diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/CipherSuite.java --- a/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Wed Jun 12 18:58:00 2019 -0700 @@ -35,8 +35,8 @@ import static sun.security.ssl.CipherSuite.KeyExchange.*; import static sun.security.ssl.CipherSuite.MacAlg.*; import static sun.security.ssl.SSLCipher.*; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; -import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*; +import sun.security.ssl.NamedGroup.NamedGroupType; +import static sun.security.ssl.NamedGroup.NamedGroupType.*; /** * Enum for SSL/(D)TLS cipher suites. @@ -184,7 +184,7 @@ K_DHE_DSS, B_AES_128, M_SHA256, H_SHA256), // - // not forward screcy cipher suites. + // not forward secret cipher suites. // // AES_256(GCM) @@ -1106,11 +1106,18 @@ K_DH_ANON ("DH_anon", true, true, NAMED_GROUP_FFDHE), K_DH_ANON_EXPORT("DH_anon_EXPORT", true, true, NAMED_GROUP_NONE), - K_ECDH_ECDSA ("ECDH_ECDSA", true, false, NAMED_GROUP_ECDHE), - K_ECDH_RSA ("ECDH_RSA", true, false, NAMED_GROUP_ECDHE), - K_ECDHE_ECDSA ("ECDHE_ECDSA", true, false, NAMED_GROUP_ECDHE), - K_ECDHE_RSA ("ECDHE_RSA", true, false, NAMED_GROUP_ECDHE), - K_ECDH_ANON ("ECDH_anon", true, true, NAMED_GROUP_ECDHE), + // These KeyExchanges can use either ECDHE/XDH, so we'll use a + // varargs here. + K_ECDH_ECDSA ("ECDH_ECDSA", JsseJce.ALLOW_ECC, false, + NAMED_GROUP_ECDHE, NAMED_GROUP_XDH), + K_ECDH_RSA ("ECDH_RSA", JsseJce.ALLOW_ECC, false, + NAMED_GROUP_ECDHE, NAMED_GROUP_XDH), + K_ECDHE_ECDSA ("ECDHE_ECDSA", JsseJce.ALLOW_ECC, false, + NAMED_GROUP_ECDHE, NAMED_GROUP_XDH), + K_ECDHE_RSA ("ECDHE_RSA", JsseJce.ALLOW_ECC, false, + NAMED_GROUP_ECDHE, NAMED_GROUP_XDH), + K_ECDH_ANON ("ECDH_anon", JsseJce.ALLOW_ECC, true, + NAMED_GROUP_ECDHE, NAMED_GROUP_XDH), // renegotiation protection request signaling cipher suite K_SCSV ("SCSV", true, true, NAMED_GROUP_NONE); @@ -1118,19 +1125,16 @@ // name of the key exchange algorithm, e.g. DHE_DSS final String name; final boolean allowed; - final NamedGroupType groupType; + final NamedGroupType[] groupTypes; private final boolean alwaysAvailable; private final boolean isAnonymous; KeyExchange(String name, boolean allowed, - boolean isAnonymous, NamedGroupType groupType) { + boolean isAnonymous, NamedGroupType... groupTypes) { this.name = name; - if (groupType == NAMED_GROUP_ECDHE) { - this.allowed = JsseJce.ALLOW_ECC; - } else { - this.allowed = allowed; - } - this.groupType = groupType; + this.groupTypes = groupTypes; + this.allowed = allowed; + this.alwaysAvailable = allowed && (!name.startsWith("EC")); this.isAnonymous = isAnonymous; } @@ -1140,7 +1144,8 @@ return true; } - if (groupType == NAMED_GROUP_ECDHE) { + if (NamedGroupType.arrayContains( + groupTypes, NamedGroupType.NAMED_GROUP_ECDHE)) { return (allowed && JsseJce.isEcAvailable()); } else { return allowed; diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/DHClientKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -42,7 +42,6 @@ import sun.security.ssl.DHKeyExchange.DHECredentials; import sun.security.ssl.DHKeyExchange.DHEPossession; import sun.security.ssl.SSLHandshake.HandshakeMessage; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; import sun.security.util.HexDumpEncoder; /** diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/DHKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -36,19 +36,12 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; -import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidKeySpecException; -import javax.crypto.KeyAgreement; -import javax.crypto.SecretKey; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; import javax.crypto.spec.DHPublicKeySpec; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.SSLHandshakeException; import sun.security.action.GetPropertyAction; -import sun.security.ssl.CipherSuite.HashAlg; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; +import sun.security.ssl.NamedGroup.NamedGroupType; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; import sun.security.ssl.X509Authentication.X509Possession; import sun.security.util.KeyUtil; @@ -61,7 +54,7 @@ static final SSLKeyAgreementGenerator kaGenerator = new DHEKAGenerator(); - static final class DHECredentials implements SSLCredentials { + static final class DHECredentials implements NamedGroupCredentials { final DHPublicKey popPublicKey; final NamedGroup namedGroup; @@ -70,6 +63,16 @@ this.namedGroup = namedGroup; } + @Override + public PublicKey getPublicKey() { + return popPublicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + static DHECredentials valueOf(NamedGroup ng, byte[] encodedPublic) throws IOException, GeneralSecurityException { @@ -98,7 +101,7 @@ } } - static final class DHEPossession implements SSLPossession { + static final class DHEPossession implements NamedGroupPossession { final PrivateKey privateKey; final DHPublicKey publicKey; final NamedGroup namedGroup; @@ -174,13 +177,13 @@ // Generate and validate DHPublicKeySpec private KeyPair generateDHKeyPair( KeyPairGenerator kpg) throws GeneralSecurityException { - boolean doExtraValiadtion = + boolean doExtraValidation = (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName())); boolean isRecovering = false; for (int i = 0; i <= 2; i++) { // Try to recover from failure. KeyPair kp = kpg.generateKeyPair(); // validate the Diffie-Hellman public key - if (doExtraValiadtion) { + if (doExtraValidation) { DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic()); try { KeyUtil.validate(spec); @@ -231,6 +234,21 @@ return encoded; } + + @Override + public PublicKey getPublicKey() { + return publicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + @Override + public PrivateKey getPrivateKey() { + return privateKey; + } } private static final class @@ -298,7 +316,7 @@ // Used for ServerKeyExchange, TLS 1.2 and prior versions. @Override public SSLPossession createPossession(HandshakeContext context) { - NamedGroup preferableNamedGroup = null; + NamedGroup preferableNamedGroup; if (!useLegacyEphemeralDHKeys && (context.clientRequestedNamedGroups != null) && (!context.clientRequestedNamedGroups.isEmpty())) { @@ -306,7 +324,8 @@ SupportedGroups.getPreferredGroup( context.negotiatedProtocol, context.algorithmConstraints, - NamedGroupType.NAMED_GROUP_FFDHE, + new NamedGroupType [] { + NamedGroupType.NAMED_GROUP_FFDHE }, context.clientRequestedNamedGroups); if (preferableNamedGroup != null) { return new DHEPossession(preferableNamedGroup, @@ -392,7 +411,7 @@ private static final class DHEKAGenerator implements SSLKeyAgreementGenerator { - static private DHEKAGenerator instance = new DHEKAGenerator(); + private static final DHEKAGenerator instance = new DHEKAGenerator(); // Prevent instantiation of this class. private DHEKAGenerator() { @@ -442,93 +461,8 @@ "No sufficient DHE key agreement parameters negotiated"); } - return new DHEKAKeyDerivation(context, + return new KAKeyDerivation("DiffieHellman", context, dhePossession.privateKey, dheCredentials.popPublicKey); } - - private static final - class DHEKAKeyDerivation implements SSLKeyDerivation { - private final HandshakeContext context; - private final PrivateKey localPrivateKey; - private final PublicKey peerPublicKey; - - DHEKAKeyDerivation(HandshakeContext context, - PrivateKey localPrivateKey, - PublicKey peerPublicKey) { - this.context = context; - this.localPrivateKey = localPrivateKey; - this.peerPublicKey = peerPublicKey; - } - - @Override - public SecretKey deriveKey(String algorithm, - AlgorithmParameterSpec params) throws IOException { - if (!context.negotiatedProtocol.useTLS13PlusSpec()) { - return t12DeriveKey(algorithm, params); - } else { - return t13DeriveKey(algorithm, params); - } - } - - private SecretKey t12DeriveKey(String algorithm, - AlgorithmParameterSpec params) throws IOException { - try { - KeyAgreement ka = KeyAgreement.getInstance("DiffieHellman"); - ka.init(localPrivateKey); - ka.doPhase(peerPublicKey, true); - SecretKey preMasterSecret = - ka.generateSecret("TlsPremasterSecret"); - SSLMasterKeyDerivation mskd = - SSLMasterKeyDerivation.valueOf( - context.negotiatedProtocol); - if (mskd == null) { - // unlikely - throw new SSLHandshakeException( - "No expected master key derivation for protocol: " + - context.negotiatedProtocol.name); - } - SSLKeyDerivation kd = mskd.createKeyDerivation( - context, preMasterSecret); - return kd.deriveKey("MasterSecret", params); - } catch (GeneralSecurityException gse) { - throw (SSLHandshakeException) new SSLHandshakeException( - "Could not generate secret").initCause(gse); - } - } - - private SecretKey t13DeriveKey(String algorithm, - AlgorithmParameterSpec params) throws IOException { - try { - KeyAgreement ka = KeyAgreement.getInstance("DiffieHellman"); - ka.init(localPrivateKey); - ka.doPhase(peerPublicKey, true); - SecretKey sharedSecret = - ka.generateSecret("TlsPremasterSecret"); - - HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg; - SSLKeyDerivation kd = context.handshakeKeyDerivation; - HKDF hkdf = new HKDF(hashAlg.name); - if (kd == null) { // No PSK is in use. - // If PSK is not in use Early Secret will still be - // HKDF-Extract(0, 0). - byte[] zeros = new byte[hashAlg.hashLength]; - SecretKeySpec ikm = - new SecretKeySpec(zeros, "TlsPreSharedSecret"); - SecretKey earlySecret = - hkdf.extract(zeros, ikm, "TlsEarlySecret"); - kd = new SSLSecretDerivation(context, earlySecret); - } - - // derive salt secret - SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null); - - // derive handshake secret - return hkdf.extract(saltSecret, sharedSecret, algorithm); - } catch (GeneralSecurityException gse) { - throw (SSLHandshakeException) new SSLHandshakeException( - "Could not generate secret").initCause(gse); - } - } - } } } diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/DHServerKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -48,7 +48,6 @@ import sun.security.ssl.DHKeyExchange.DHECredentials; import sun.security.ssl.DHKeyExchange.DHEPossession; import sun.security.ssl.SSLHandshake.HandshakeMessage; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; import sun.security.ssl.X509Authentication.X509Credentials; import sun.security.ssl.X509Authentication.X509Possession; import sun.security.util.HexDumpEncoder; diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/ECDHClientKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -27,31 +27,28 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.security.AlgorithmConstraints; -import java.security.CryptoPrimitive; import java.security.GeneralSecurityException; -import java.security.KeyFactory; import java.security.PublicKey; import java.security.interfaces.ECPublicKey; +import java.security.interfaces.XECPublicKey; +import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPublicKeySpec; +import java.security.spec.NamedParameterSpec; import java.text.MessageFormat; -import java.util.EnumSet; import java.util.Locale; import javax.crypto.SecretKey; -import javax.net.ssl.SSLHandshakeException; -import sun.security.ssl.ECDHKeyExchange.ECDHECredentials; -import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; import sun.security.ssl.SSLHandshake.HandshakeMessage; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; import sun.security.ssl.X509Authentication.X509Credentials; import sun.security.ssl.X509Authentication.X509Possession; -import sun.security.util.ECUtil; import sun.security.util.HexDumpEncoder; /** * Pack of the "ClientKeyExchange" handshake message. + * + * This file is used by both the ECDH/ECDHE/XDH code since much of the + * code is the same between the EC named groups (i.e. + * x25519/x448/secp*r1), even though the APIs are very different (i.e. + * ECPublicKey/XECPublicKey, KeyExchange.getInstance("EC"/"XDH"), etc.). */ final class ECDHClientKeyExchange { static final SSLConsumer ecdhHandshakeConsumer = @@ -65,19 +62,17 @@ new ECDHEClientKeyExchangeProducer(); /** - * The ECDH/ECDHE ClientKeyExchange handshake message. + * The ECDH/ECDHE/XDH ClientKeyExchange handshake message. */ private static final class ECDHClientKeyExchangeMessage extends HandshakeMessage { private final byte[] encodedPoint; ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, - ECPublicKey publicKey) { + byte[] encodedPublicKey) { super(handshakeContext); - ECPoint point = publicKey.getW(); - ECParameterSpec params = publicKey.getParams(); - encodedPoint = ECUtil.encodePoint(point, params.getCurve()); + this.encodedPoint = encodedPublicKey; } ECDHClientKeyExchangeMessage(HandshakeContext handshakeContext, @@ -90,34 +85,6 @@ } } - // Check constraints of the specified EC public key. - static void checkConstraints(AlgorithmConstraints constraints, - ECPublicKey publicKey, - byte[] encodedPoint) throws SSLHandshakeException { - - try { - ECParameterSpec params = publicKey.getParams(); - ECPoint point = - ECUtil.decodePoint(encodedPoint, params.getCurve()); - ECPublicKeySpec spec = new ECPublicKeySpec(point, params); - - KeyFactory kf = KeyFactory.getInstance("EC"); - ECPublicKey peerPublicKey = - (ECPublicKey)kf.generatePublic(spec); - - // check constraints of ECPublicKey - if (!constraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - peerPublicKey)) { - throw new SSLHandshakeException( - "ECPublicKey does not comply to algorithm constraints"); - } - } catch (GeneralSecurityException | java.io.IOException e) { - throw (SSLHandshakeException) new SSLHandshakeException( - "Could not generate ECPublicKey").initCause(e); - } - } - @Override public SSLHandshake handshakeType() { return SSLHandshake.CLIENT_KEY_EXCHANGE; @@ -194,24 +161,41 @@ } PublicKey publicKey = x509Credentials.popPublicKey; - if (!publicKey.getAlgorithm().equals("EC")) { + + NamedGroup namedGroup = null; + String algorithm = publicKey.getAlgorithm(); + + // Determine which NamedGroup we'll be using, then use + // the creator functions. + if (algorithm.equals("EC")) { + ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); + namedGroup = NamedGroup.valueOf(params); + } else if (algorithm.equals("XDH")) { + AlgorithmParameterSpec params = + ((XECPublicKey)publicKey).getParams(); + if (params instanceof NamedParameterSpec) { + String name = ((NamedParameterSpec)params).getName(); + namedGroup = NamedGroup.nameOf(name); + } + } else { throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Not EC server certificate for ECDH client key exchange"); + "Not EC/XDH server certificate for " + + "ECDH client key exchange"); } - ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); - NamedGroup namedGroup = NamedGroup.valueOf(params); if (namedGroup == null) { throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Unsupported EC server cert for ECDH client key exchange"); + "Unsupported EC/XDH server cert for " + + "ECDH client key exchange"); } - ECDHEPossession ecdhePossession = new ECDHEPossession( - namedGroup, chc.sslContext.getSecureRandom()); - chc.handshakePossessions.add(ecdhePossession); + SSLPossession sslPossession = namedGroup.createPossession( + chc.sslContext.getSecureRandom()); + + chc.handshakePossessions.add(sslPossession); ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage( - chc, ecdhePossession.publicKey); + chc, sslPossession.encode()); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDH ClientKeyExchange handshake message", cke); @@ -283,18 +267,35 @@ "No expected EC server cert for ECDH client key exchange"); } - ECParameterSpec params = x509Possession.getECParameterSpec(); - if (params == null) { + // Determine which NamedGroup we'll be using, then use + // the creator functions. + NamedGroup namedGroup = null; + + // Iteratively determine the X509Possession type's ParameterSpec. + ECParameterSpec ecParams = x509Possession.getECParameterSpec(); + NamedParameterSpec namedParams = null; + if (ecParams != null) { + namedGroup = NamedGroup.valueOf(ecParams); + } + + // Wasn't EC, try XEC. + if (ecParams == null) { + namedParams = x509Possession.getXECParameterSpec(); + namedGroup = NamedGroup.nameOf(namedParams.getName()); + } + + // Can't figure this out, bail. + if ((ecParams == null) && (namedParams == null)) { // unlikely, have been checked during cipher suite negotiation. throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Not EC server cert for ECDH client key exchange"); + "Not EC/XDH server cert for ECDH client key exchange"); } - NamedGroup namedGroup = NamedGroup.valueOf(params); + // unlikely, have been checked during cipher suite negotiation. if (namedGroup == null) { - // unlikely, have been checked during cipher suite negotiation. throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Unsupported EC server cert for ECDH client key exchange"); + "Unknown named group in server cert for " + + "ECDH client key exchange"); } SSLKeyExchange ke = SSLKeyExchange.valueOf( @@ -306,7 +307,7 @@ "Not supported key exchange type"); } - // parse the handshake message + // parse either handshake message containing either EC/XEC. ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage(shc, message); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { @@ -316,27 +317,17 @@ // create the credentials try { - ECPoint point = - ECUtil.decodePoint(cke.encodedPoint, params.getCurve()); - ECPublicKeySpec spec = new ECPublicKeySpec(point, params); - - KeyFactory kf = KeyFactory.getInstance("EC"); - ECPublicKey peerPublicKey = - (ECPublicKey)kf.generatePublic(spec); + NamedGroup ng = namedGroup; // "effectively final" the lambda + // AlgorithmConstraints are checked internally. + SSLCredentials sslCredentials = namedGroup.decodeCredentials( + cke.encodedPoint, shc.algorithmConstraints, + s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ClientKeyExchange " + ng + ": " + s)); - // check constraints of peer ECPublicKey - if (!shc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - peerPublicKey)) { - throw new SSLHandshakeException( - "ECPublicKey does not comply to algorithm constraints"); - } - - shc.handshakeCredentials.add(new ECDHECredentials( - peerPublicKey, namedGroup)); - } catch (GeneralSecurityException | java.io.IOException e) { - throw (SSLHandshakeException)(new SSLHandshakeException( - "Could not generate ECPublicKey").initCause(e)); + shc.handshakeCredentials.add(sslCredentials); + } catch (GeneralSecurityException e) { + throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Cannot decode ECDH PublicKey: " + namedGroup); } // update the states @@ -374,25 +365,37 @@ // The producing happens in client side only. ClientHandshakeContext chc = (ClientHandshakeContext)context; - ECDHECredentials ecdheCredentials = null; + SSLCredentials sslCredentials = null; + NamedGroup ng = null; + PublicKey publicKey = null; + + // Find a good EC/XEC credential to use, determine the + // NamedGroup to use for creating Possessions/Credentials/Keys. for (SSLCredentials cd : chc.handshakeCredentials) { - if (cd instanceof ECDHECredentials) { - ecdheCredentials = (ECDHECredentials)cd; + if (cd instanceof NamedGroupCredentials) { + NamedGroupCredentials creds = (NamedGroupCredentials)cd; + ng = creds.getNamedGroup(); + publicKey = creds.getPublicKey(); + sslCredentials = cd; break; } } - if (ecdheCredentials == null) { + if (sslCredentials == null) { throw chc.conContext.fatal(Alert.INTERNAL_ERROR, "No ECDHE credentials negotiated for client key exchange"); } - ECDHEPossession ecdhePossession = new ECDHEPossession( - ecdheCredentials, chc.sslContext.getSecureRandom()); - chc.handshakePossessions.add(ecdhePossession); + SSLPossession sslPossession = ng.createPossession( + chc.sslContext.getSecureRandom()); + + chc.handshakePossessions.add(sslPossession); + + // Write the EC/XEC message. ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage( - chc, ecdhePossession.publicKey); + chc, sslPossession.encode()); + if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced ECDHE ClientKeyExchange handshake message", cke); @@ -450,23 +453,29 @@ // The consuming happens in server side only. ServerHandshakeContext shc = (ServerHandshakeContext)context; - ECDHEPossession ecdhePossession = null; + SSLPossession sslPossession = null; + NamedGroup namedGroup = null; + + // Find a good EC/XEC credential to use, determine the + // NamedGroup to use for creating Possessions/Credentials/Keys. for (SSLPossession possession : shc.handshakePossessions) { - if (possession instanceof ECDHEPossession) { - ecdhePossession = (ECDHEPossession)possession; + if (possession instanceof NamedGroupPossession) { + NamedGroupPossession poss = + (NamedGroupPossession)possession; + namedGroup = poss.getNamedGroup(); + sslPossession = poss; break; } } - if (ecdhePossession == null) { + + if (sslPossession == null) { // unlikely throw shc.conContext.fatal(Alert.INTERNAL_ERROR, "No expected ECDHE possessions for client key exchange"); } - ECParameterSpec params = ecdhePossession.publicKey.getParams(); - NamedGroup namedGroup = NamedGroup.valueOf(params); if (namedGroup == null) { - // unlikely, have been checked during cipher suite negotiation. + // unlikely, have been checked during cipher suite negotiation throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "Unsupported EC server cert for ECDHE client key exchange"); } @@ -480,7 +489,7 @@ "Not supported key exchange type"); } - // parse the handshake message + // parse the EC/XEC handshake message ECDHClientKeyExchangeMessage cke = new ECDHClientKeyExchangeMessage(shc, message); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { @@ -490,27 +499,17 @@ // create the credentials try { - ECPoint point = - ECUtil.decodePoint(cke.encodedPoint, params.getCurve()); - ECPublicKeySpec spec = new ECPublicKeySpec(point, params); - - KeyFactory kf = KeyFactory.getInstance("EC"); - ECPublicKey peerPublicKey = - (ECPublicKey)kf.generatePublic(spec); + NamedGroup ng = namedGroup; // "effectively final" the lambda + // AlgorithmConstraints are checked internally. + SSLCredentials sslCredentials = namedGroup.decodeCredentials( + cke.encodedPoint, shc.algorithmConstraints, + s -> shc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ClientKeyExchange " + ng + ": " + s)); - // check constraints of peer ECPublicKey - if (!shc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - peerPublicKey)) { - throw new SSLHandshakeException( - "ECPublicKey does not comply to algorithm constraints"); - } - - shc.handshakeCredentials.add(new ECDHECredentials( - peerPublicKey, namedGroup)); - } catch (GeneralSecurityException | java.io.IOException e) { - throw (SSLHandshakeException)(new SSLHandshakeException( - "Could not generate ECPublicKey").initCause(e)); + shc.handshakeCredentials.add(sslCredentials); + } catch (GeneralSecurityException e) { + throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Cannot decode named group: " + namedGroup); } // update the states diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/ECDHKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -36,7 +36,6 @@ import java.security.PublicKey; import java.security.SecureRandom; import java.security.interfaces.ECPublicKey; -import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; @@ -44,25 +43,30 @@ import java.util.EnumSet; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.SSLHandshakeException; -import sun.security.ssl.CipherSuite.HashAlg; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; +import sun.security.ssl.NamedGroup.NamedGroupType; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; import sun.security.ssl.X509Authentication.X509Credentials; import sun.security.ssl.X509Authentication.X509Possession; +import sun.security.ssl.XDHKeyExchange.XDHECredentials; +import sun.security.ssl.XDHKeyExchange.XDHEPossession; import sun.security.util.ECUtil; final class ECDHKeyExchange { static final SSLPossessionGenerator poGenerator = new ECDHEPossessionGenerator(); - static final SSLKeyAgreementGenerator ecdheKAGenerator = - new ECDHEKAGenerator(); static final SSLKeyAgreementGenerator ecdhKAGenerator = new ECDHKAGenerator(); - static final class ECDHECredentials implements SSLCredentials { + // TLSv1.3 + static final SSLKeyAgreementGenerator ecdheKAGenerator = + new ECDHEKAGenerator(); + + // TLSv1-1.2, the KA gets more difficult with EC/XEC keys + static final SSLKeyAgreementGenerator ecdheXdhKAGenerator = + new ECDHEXDHKAGenerator(); + + static final class ECDHECredentials implements NamedGroupCredentials { final ECPublicKey popPublicKey; final NamedGroup namedGroup; @@ -71,6 +75,16 @@ this.namedGroup = namedGroup; } + @Override + public PublicKey getPublicKey() { + return popPublicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + static ECDHECredentials valueOf(NamedGroup namedGroup, byte[] encodedPoint) throws IOException, GeneralSecurityException { @@ -98,7 +112,7 @@ } } - static final class ECDHEPossession implements SSLPossession { + static final class ECDHEPossession implements NamedGroupPossession { final PrivateKey privateKey; final ECPublicKey publicKey; final NamedGroup namedGroup; @@ -199,6 +213,21 @@ "Could not generate ECPublicKey").initCause(e); } } + + @Override + public PublicKey getPublicKey() { + return publicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + @Override + public PrivateKey getPrivateKey() { + return privateKey; + } } private static final @@ -210,24 +239,31 @@ @Override public SSLPossession createPossession(HandshakeContext context) { - NamedGroup preferableNamedGroup = null; + + NamedGroup preferableNamedGroup; + + // Find most preferred EC or XEC groups if ((context.clientRequestedNamedGroups != null) && (!context.clientRequestedNamedGroups.isEmpty())) { preferableNamedGroup = SupportedGroups.getPreferredGroup( context.negotiatedProtocol, context.algorithmConstraints, - NamedGroupType.NAMED_GROUP_ECDHE, + new NamedGroupType[] { + NamedGroupType.NAMED_GROUP_ECDHE, + NamedGroupType.NAMED_GROUP_XDH }, context.clientRequestedNamedGroups); } else { preferableNamedGroup = SupportedGroups.getPreferredGroup( context.negotiatedProtocol, context.algorithmConstraints, - NamedGroupType.NAMED_GROUP_ECDHE); + new NamedGroupType[] { + NamedGroupType.NAMED_GROUP_ECDHE, + NamedGroupType.NAMED_GROUP_XDH }); } if (preferableNamedGroup != null) { - return new ECDHEPossession(preferableNamedGroup, - context.sslContext.getSecureRandom()); + return preferableNamedGroup.createPossession( + context.sslContext.getSecureRandom()); } // no match found, cannot use this cipher suite. @@ -298,7 +334,7 @@ "No sufficient ECDHE key agreement parameters negotiated"); } - return new ECDHEKAKeyDerivation(shc, + return new KAKeyDerivation("ECDH", shc, x509Possession.popPrivateKey, ecdheCredentials.popPublicKey); } @@ -347,7 +383,7 @@ "No sufficient ECDH key agreement parameters negotiated"); } - return new ECDHEKAKeyDerivation(chc, + return new KAKeyDerivation("ECDH", chc, ecdhePossession.privateKey, x509Credentials.popPublicKey); } } @@ -391,94 +427,73 @@ "No sufficient ECDHE key agreement parameters negotiated"); } - return new ECDHEKAKeyDerivation(context, + return new KAKeyDerivation("ECDH", context, ecdhePossession.privateKey, ecdheCredentials.popPublicKey); } } + /* + * A Generator for TLSv1-1.2 to create a ECDHE or a XDH KeyDerivation + * object depending on the negotiated group. + */ private static final - class ECDHEKAKeyDerivation implements SSLKeyDerivation { - private final HandshakeContext context; - private final PrivateKey localPrivateKey; - private final PublicKey peerPublicKey; - - ECDHEKAKeyDerivation(HandshakeContext context, - PrivateKey localPrivateKey, - PublicKey peerPublicKey) { - this.context = context; - this.localPrivateKey = localPrivateKey; - this.peerPublicKey = peerPublicKey; + class ECDHEXDHKAGenerator implements SSLKeyAgreementGenerator { + // Prevent instantiation of this class. + private ECDHEXDHKAGenerator() { + // blank } @Override - public SecretKey deriveKey(String algorithm, - AlgorithmParameterSpec params) throws IOException { - if (!context.negotiatedProtocol.useTLS13PlusSpec()) { - return t12DeriveKey(algorithm, params); - } else { - return t13DeriveKey(algorithm, params); - } - } + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + + NamedGroupPossession namedGroupPossession = null; + NamedGroupCredentials namedGroupCredentials = null; + NamedGroup namedGroup = null; - private SecretKey t12DeriveKey(String algorithm, - AlgorithmParameterSpec params) throws IOException { - try { - KeyAgreement ka = KeyAgreement.getInstance("ECDH"); - ka.init(localPrivateKey); - ka.doPhase(peerPublicKey, true); - SecretKey preMasterSecret = - ka.generateSecret("TlsPremasterSecret"); - - SSLMasterKeyDerivation mskd = - SSLMasterKeyDerivation.valueOf( - context.negotiatedProtocol); - if (mskd == null) { - // unlikely - throw new SSLHandshakeException( - "No expected master key derivation for protocol: " + - context.negotiatedProtocol.name); + // Find a possession/credential combo using the same named group + search: + for (SSLPossession poss : context.handshakePossessions) { + for (SSLCredentials cred : context.handshakeCredentials) { + if (((poss instanceof ECDHEPossession) && + (cred instanceof ECDHECredentials)) || + (((poss instanceof XDHEPossession) && + (cred instanceof XDHECredentials)))) { + NamedGroupPossession p = (NamedGroupPossession)poss; + NamedGroupCredentials c = (NamedGroupCredentials)cred; + if (p.getNamedGroup() != c.getNamedGroup()) { + continue; + } else { + namedGroup = p.getNamedGroup(); + } + namedGroupPossession = p; + namedGroupCredentials = c; + break search; + } } - SSLKeyDerivation kd = mskd.createKeyDerivation( - context, preMasterSecret); - return kd.deriveKey("MasterSecret", params); - } catch (GeneralSecurityException gse) { - throw (SSLHandshakeException) new SSLHandshakeException( - "Could not generate secret").initCause(gse); } - } - private SecretKey t13DeriveKey(String algorithm, - AlgorithmParameterSpec params) throws IOException { - try { - KeyAgreement ka = KeyAgreement.getInstance("ECDH"); - ka.init(localPrivateKey); - ka.doPhase(peerPublicKey, true); - SecretKey sharedSecret = - ka.generateSecret("TlsPremasterSecret"); + if (namedGroupPossession == null || namedGroupCredentials == null) { + throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No sufficient ECDHE/XDH key agreement " + + "parameters negotiated"); + } - HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg; - SSLKeyDerivation kd = context.handshakeKeyDerivation; - HKDF hkdf = new HKDF(hashAlg.name); - if (kd == null) { // No PSK is in use. - // If PSK is not in use Early Secret will still be - // HKDF-Extract(0, 0). - byte[] zeros = new byte[hashAlg.hashLength]; - SecretKeySpec ikm = - new SecretKeySpec(zeros, "TlsPreSharedSecret"); - SecretKey earlySecret = - hkdf.extract(zeros, ikm, "TlsEarlySecret"); - kd = new SSLSecretDerivation(context, earlySecret); - } + String alg; + switch (namedGroup.type) { + case NAMED_GROUP_ECDHE: + alg = "ECDH"; + break; + case NAMED_GROUP_XDH: + alg = "XDH"; + break; + default: + throw new RuntimeException("Unexpected named group type"); + } - // derive salt secret - SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null); - - // derive handshake secret - return hkdf.extract(saltSecret, sharedSecret, algorithm); - } catch (GeneralSecurityException gse) { - throw (SSLHandshakeException) new SSLHandshakeException( - "Could not generate secret").initCause(gse); - } + return new KAKeyDerivation(alg, context, + namedGroupPossession.getPrivateKey(), + namedGroupCredentials.getPublicKey()); } } } diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/ECDHServerKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -27,32 +27,21 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.security.CryptoPrimitive; +import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; -import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.InvalidKeySpecException; import java.text.MessageFormat; -import java.util.EnumSet; import java.util.Locale; -import sun.security.ssl.ECDHKeyExchange.ECDHECredentials; -import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; import sun.security.ssl.SSLHandshake.HandshakeMessage; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; import sun.security.ssl.X509Authentication.X509Credentials; import sun.security.ssl.X509Authentication.X509Possession; -import sun.security.util.ECUtil; import sun.security.util.HexDumpEncoder; /** @@ -80,14 +69,14 @@ // signature bytes, or null if anonymous private final byte[] paramsSignature; - // public key object encapsulated in this message - private final ECPublicKey publicKey; - private final boolean useExplicitSigAlgorithm; // the signature algorithm used by this ServerKeyExchange message private final SignatureScheme signatureScheme; + // the parsed credential object + private SSLCredentials sslCredentials; + ECDHServerKeyExchangeMessage( HandshakeContext handshakeContext) throws IOException { super(handshakeContext); @@ -96,38 +85,38 @@ ServerHandshakeContext shc = (ServerHandshakeContext)handshakeContext; - ECDHEPossession ecdhePossession = null; + // Find the Possessions needed + NamedGroupPossession namedGroupPossession = null; X509Possession x509Possession = null; for (SSLPossession possession : shc.handshakePossessions) { - if (possession instanceof ECDHEPossession) { - ecdhePossession = (ECDHEPossession)possession; + if (possession instanceof NamedGroupPossession) { + namedGroupPossession = (NamedGroupPossession)possession; if (x509Possession != null) { break; } } else if (possession instanceof X509Possession) { x509Possession = (X509Possession)possession; - if (ecdhePossession != null) { + if (namedGroupPossession != null) { break; } } } - if (ecdhePossession == null) { + if (namedGroupPossession == null) { // unlikely throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "No ECDHE credentials negotiated for server key exchange"); } - publicKey = ecdhePossession.publicKey; - ECParameterSpec params = publicKey.getParams(); - ECPoint point = publicKey.getW(); - publicPoint = ECUtil.encodePoint(point, params.getCurve()); + // Find the NamedGroup used for the ephemeral keys. + namedGroup = namedGroupPossession.getNamedGroup(); + publicPoint = namedGroup.encodePossessionPublicKey( + namedGroupPossession); - this.namedGroup = NamedGroup.valueOf(params); if ((namedGroup == null) || (namedGroup.oid == null) ) { // unlikely throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Unnamed EC parameter spec: " + params); + "Missing Named Group"); } if (x509Possession == null) { @@ -216,39 +205,23 @@ "Unsupported named group: " + namedGroup); } - if (namedGroup.oid == null) { - throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Unknown named EC curve: " + namedGroup); - } - - ECParameterSpec parameters = - ECUtil.getECParameterSpec(null, namedGroup.oid); - if (parameters == null) { - throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "No supported EC parameter: " + namedGroup); - } - publicPoint = Record.getBytes8(m); if (publicPoint.length == 0) { throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Insufficient ECPoint data: " + namedGroup); + "Insufficient Point data: " + namedGroup); } - ECPublicKey ecPublicKey = null; try { - ECPoint point = - ECUtil.decodePoint(publicPoint, parameters.getCurve()); - KeyFactory factory = KeyFactory.getInstance("EC"); - ecPublicKey = (ECPublicKey)factory.generatePublic( - new ECPublicKeySpec(point, parameters)); - } catch (NoSuchAlgorithmException | - InvalidKeySpecException | IOException ex) { - throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, - "Invalid ECPoint: " + namedGroup, ex); + sslCredentials = namedGroup.decodeCredentials( + publicPoint, handshakeContext.algorithmConstraints, + s -> chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, + "ServerKeyExchange " + namedGroup + ": " + (s))); + } catch (GeneralSecurityException ex) { + throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, + "Cannot decode named group: " + + NamedGroup.nameOf(namedGroupId)); } - publicKey = ecPublicKey; - X509Credentials x509Credentials = null; for (SSLCredentials cd : chc.handshakeCredentials) { if (cd instanceof X509Credentials) { @@ -529,6 +502,7 @@ // The consuming happens in client side only. ClientHandshakeContext chc = (ClientHandshakeContext)context; + // AlgorithmConstraints are checked during decoding ECDHServerKeyExchangeMessage skem = new ECDHServerKeyExchangeMessage(chc, message); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { @@ -537,22 +511,9 @@ } // - // validate - // - // check constraints of EC PublicKey - if (!chc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - skem.publicKey)) { - throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, - "ECDH ServerKeyExchange does not comply " + - "to algorithm constraints"); - } - - // // update // - chc.handshakeCredentials.add( - new ECDHECredentials(skem.publicKey, skem.namedGroup)); + chc.handshakeCredentials.add(skem.sslCredentials); // // produce diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java --- a/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ import sun.security.ssl.SSLExtension.ExtensionConsumer; import sun.security.ssl.SSLExtension.SSLExtensionSpec; import sun.security.ssl.SSLHandshake.HandshakeMessage; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; +import sun.security.ssl.NamedGroup.NamedGroupType; /** * Pack of the "ec_point_formats" extensions [RFC 4492]. diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/HandshakeContext.java --- a/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/HandshakeContext.java Wed Jun 12 18:58:00 2019 -0700 @@ -46,9 +46,8 @@ import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLHandshakeException; import javax.security.auth.x500.X500Principal; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; -import static sun.security.ssl.SupportedGroupsExtension.NamedGroupType.*; +import sun.security.ssl.NamedGroup.NamedGroupType; +import static sun.security.ssl.NamedGroup.NamedGroupType.*; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; abstract class HandshakeContext implements ConnectionContext { @@ -519,31 +518,38 @@ return true; } - boolean available; - NamedGroupType groupType = suite.keyExchange.groupType; - if (groupType != NAMED_GROUP_NONE) { - Boolean checkedStatus = cachedStatus.get(groupType); - if (checkedStatus == null) { - available = SupportedGroups.isActivatable( - algorithmConstraints, groupType); - cachedStatus.put(groupType, available); + // Is at least one of the group types available? + boolean groupAvailable, retval = false; + NamedGroupType[] groupTypes = suite.keyExchange.groupTypes; + for (NamedGroupType groupType : groupTypes) { + if (groupType != NAMED_GROUP_NONE) { + Boolean checkedStatus = cachedStatus.get(groupType); + if (checkedStatus == null) { + groupAvailable = SupportedGroups.isActivatable( + algorithmConstraints, groupType); + cachedStatus.put(groupType, groupAvailable); - if (!available && - SSLLogger.isOn && SSLLogger.isOn("verbose")) { - SSLLogger.fine("No activated named group"); + if (!groupAvailable && + SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine( + "No activated named group in " + groupType); + } + } else { + groupAvailable = checkedStatus; } + + retval |= groupAvailable; } else { - available = checkedStatus; + retval |= true; } + } - if (!available && SSLLogger.isOn && SSLLogger.isOn("verbose")) { - SSLLogger.fine( - "No active named group, ignore " + suite); - } - return available; - } else { - return true; + if (!retval && SSLLogger.isOn && SSLLogger.isOn("verbose")) { + SSLLogger.fine("No active named group(s), ignore " + suite); } + + return retval; + } else if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { SSLLogger.fine("Ignore disabled cipher suite: " + suite); } diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java Wed Jun 12 18:58:00 2019 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.ssl; + +import javax.crypto.KeyAgreement; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.SSLHandshakeException; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.AlgorithmParameterSpec; + +/** + * A common class for creating various KeyDerivation types. + */ +public class KAKeyDerivation implements SSLKeyDerivation { + + private final String algorithmName; + private final HandshakeContext context; + private final PrivateKey localPrivateKey; + private final PublicKey peerPublicKey; + + KAKeyDerivation(String algorithmName, + HandshakeContext context, + PrivateKey localPrivateKey, + PublicKey peerPublicKey) { + this.algorithmName = algorithmName; + this.context = context; + this.localPrivateKey = localPrivateKey; + this.peerPublicKey = peerPublicKey; + } + + @Override + public SecretKey deriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + if (!context.negotiatedProtocol.useTLS13PlusSpec()) { + return t12DeriveKey(algorithm, params); + } else { + return t13DeriveKey(algorithm, params); + } + } + + /** + * Handle the TLSv1-1.2 objects, which don't use the HKDF algorithms. + */ + private SecretKey t12DeriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + try { + KeyAgreement ka = KeyAgreement.getInstance(algorithmName); + ka.init(localPrivateKey); + ka.doPhase(peerPublicKey, true); + SecretKey preMasterSecret + = ka.generateSecret("TlsPremasterSecret"); + SSLMasterKeyDerivation mskd + = SSLMasterKeyDerivation.valueOf( + context.negotiatedProtocol); + if (mskd == null) { + // unlikely + throw new SSLHandshakeException( + "No expected master key derivation for protocol: " + + context.negotiatedProtocol.name); + } + SSLKeyDerivation kd = mskd.createKeyDerivation( + context, preMasterSecret); + return kd.deriveKey("MasterSecret", params); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(gse); + } + } + + /** + * Handle the TLSv1.3 objects, which use the HKDF algorithms. + */ + private SecretKey t13DeriveKey(String algorithm, + AlgorithmParameterSpec params) throws IOException { + try { + KeyAgreement ka = KeyAgreement.getInstance(algorithmName); + ka.init(localPrivateKey); + ka.doPhase(peerPublicKey, true); + SecretKey sharedSecret + = ka.generateSecret("TlsPremasterSecret"); + + CipherSuite.HashAlg hashAlg = context.negotiatedCipherSuite.hashAlg; + SSLKeyDerivation kd = context.handshakeKeyDerivation; + HKDF hkdf = new HKDF(hashAlg.name); + if (kd == null) { // No PSK is in use. + // If PSK is not in use Early Secret will still be + // HKDF-Extract(0, 0). + byte[] zeros = new byte[hashAlg.hashLength]; + SecretKeySpec ikm + = new SecretKeySpec(zeros, "TlsPreSharedSecret"); + SecretKey earlySecret + = hkdf.extract(zeros, ikm, "TlsEarlySecret"); + kd = new SSLSecretDerivation(context, earlySecret); + } + + // derive salt secret + SecretKey saltSecret = kd.deriveKey("TlsSaltSecret", null); + + // derive handshake secret + return hkdf.extract(saltSecret, sharedSecret, algorithm); + } catch (GeneralSecurityException gse) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(gse); + } + } +} diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java --- a/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/KeyShareExtension.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,27 +27,19 @@ import java.io.IOException; import java.nio.ByteBuffer; -import java.security.CryptoPrimitive; import java.security.GeneralSecurityException; import java.text.MessageFormat; import java.util.Arrays; import java.util.Collections; -import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import javax.net.ssl.SSLProtocolException; -import sun.security.ssl.DHKeyExchange.DHECredentials; -import sun.security.ssl.DHKeyExchange.DHEPossession; -import sun.security.ssl.ECDHKeyExchange.ECDHECredentials; -import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; import sun.security.ssl.KeyShareExtension.CHKeyShareSpec; import sun.security.ssl.SSLExtension.ExtensionConsumer; import sun.security.ssl.SSLExtension.SSLExtensionSpec; import sun.security.ssl.SSLHandshake.HandshakeMessage; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; import sun.security.util.HexDumpEncoder; @@ -264,8 +256,7 @@ for (SSLPossession pos : poses) { // update the context chc.handshakePossessions.add(pos); - if (!(pos instanceof ECDHEPossession) && - !(pos instanceof DHEPossession)) { + if (!(pos instanceof NamedGroupPossession)) { // May need more possesion types in the future. continue; } @@ -353,46 +344,18 @@ continue; } - if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) { - try { - ECDHECredentials ecdhec = - ECDHECredentials.valueOf(ng, entry.keyExchange); - if (ecdhec != null) { - if (!shc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - ecdhec.popPublicKey)) { - SSLLogger.warning( - "ECDHE key share entry does not " + - "comply to algorithm constraints"); - } else { - credentials.add(ecdhec); - } - } - } catch (IOException | GeneralSecurityException ex) { - SSLLogger.warning( - "Cannot decode named group: " + - NamedGroup.nameOf(entry.namedGroupId)); + try { + SSLCredentials kaCred = + ng.decodeCredentials(entry.keyExchange, + shc.algorithmConstraints, + s -> SSLLogger.warning(s)); + if (kaCred != null) { + credentials.add(kaCred); } - } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) { - try { - DHECredentials dhec = - DHECredentials.valueOf(ng, entry.keyExchange); - if (dhec != null) { - if (!shc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - dhec.popPublicKey)) { - SSLLogger.warning( - "DHE key share entry does not " + - "comply to algorithm constraints"); - } else { - credentials.add(dhec); - } - } - } catch (IOException | GeneralSecurityException ex) { - SSLLogger.warning( - "Cannot decode named group: " + - NamedGroup.nameOf(entry.namedGroupId)); - } + } catch (GeneralSecurityException ex) { + SSLLogger.warning( + "Cannot decode named group: " + + NamedGroup.nameOf(entry.namedGroupId)); } } @@ -526,10 +489,9 @@ KeyShareEntry keyShare = null; for (SSLCredentials cd : shc.handshakeCredentials) { NamedGroup ng = null; - if (cd instanceof ECDHECredentials) { - ng = ((ECDHECredentials)cd).namedGroup; - } else if (cd instanceof DHECredentials) { - ng = ((DHECredentials)cd).namedGroup; + if (cd instanceof NamedGroupCredentials) { + NamedGroupCredentials creds = (NamedGroupCredentials)cd; + ng = creds.getNamedGroup(); } if (ng == null) { @@ -547,8 +509,7 @@ SSLPossession[] poses = ke.createPossessions(shc); for (SSLPossession pos : poses) { - if (!(pos instanceof ECDHEPossession) && - !(pos instanceof DHEPossession)) { + if (!(pos instanceof NamedGroupPossession)) { // May need more possesion types in the future. continue; } @@ -567,7 +528,7 @@ me.getKey(), me.getValue()); } - // We have got one! Don't forgor to break. + // We have got one! Don't forget to break. break; } } @@ -643,49 +604,16 @@ } SSLCredentials credentials = null; - if (ng.type == NamedGroupType.NAMED_GROUP_ECDHE) { - try { - ECDHECredentials ecdhec = - ECDHECredentials.valueOf(ng, keyShare.keyExchange); - if (ecdhec != null) { - if (!chc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - ecdhec.popPublicKey)) { - throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, - "ECDHE key share entry does not " + - "comply to algorithm constraints"); - } else { - credentials = ecdhec; - } - } - } catch (IOException | GeneralSecurityException ex) { - throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, - "Cannot decode named group: " + - NamedGroup.nameOf(keyShare.namedGroupId)); + try { + SSLCredentials kaCred = ng.decodeCredentials( + keyShare.keyExchange, chc.algorithmConstraints, + s -> chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, s)); + if (kaCred != null) { + credentials = kaCred; } - } else if (ng.type == NamedGroupType.NAMED_GROUP_FFDHE) { - try { - DHECredentials dhec = - DHECredentials.valueOf(ng, keyShare.keyExchange); - if (dhec != null) { - if (!chc.algorithmConstraints.permits( - EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - dhec.popPublicKey)) { - throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, - "DHE key share entry does not " + - "comply to algorithm constraints"); - } else { - credentials = dhec; - } - } - } catch (IOException | GeneralSecurityException ex) { - throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, - "Cannot decode named group: " + - NamedGroup.nameOf(keyShare.namedGroupId)); - } - } else { + } catch (GeneralSecurityException ex) { throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, - "Unsupported named group: " + + "Cannot decode named group: " + NamedGroup.nameOf(keyShare.namedGroupId)); } diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/NamedGroup.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroup.java Wed Jun 12 18:58:00 2019 -0700 @@ -0,0 +1,781 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.ssl; + +import javax.crypto.spec.DHParameterSpec; +import javax.net.ssl.SSLException; +import java.io.IOException; +import java.security.*; +import java.security.spec.*; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import javax.crypto.*; +import sun.security.ssl.DHKeyExchange.DHEPossession; +import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; + +import sun.security.util.ECUtil; + +/** + * An enum containing all known named groups for use in TLS. + * + * The enum also contains the required properties of each group and the + * required functions (e.g. encoding/decoding). + */ +enum NamedGroup { + // Elliptic Curves (RFC 4492) + // + // See sun.security.util.CurveDB for the OIDs + // NIST K-163 + + SECT163_K1(0x0001, "sect163k1", "1.3.132.0.1", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECT163_R1(0x0002, "sect163r1", "1.3.132.0.2", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-163 + SECT163_R2(0x0003, "sect163r2", "1.3.132.0.15", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECT193_R1(0x0004, "sect193r1", "1.3.132.0.24", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECT193_R2(0x0005, "sect193r2", "1.3.132.0.25", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-233 + SECT233_K1(0x0006, "sect233k1", "1.3.132.0.26", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-233 + SECT233_R1(0x0007, "sect233r1", "1.3.132.0.27", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECT239_K1(0x0008, "sect239k1", "1.3.132.0.3", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-283 + SECT283_K1(0x0009, "sect283k1", "1.3.132.0.16", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-283 + SECT283_R1(0x000A, "sect283r1", "1.3.132.0.17", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-409 + SECT409_K1(0x000B, "sect409k1", "1.3.132.0.36", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-409 + SECT409_R1(0x000C, "sect409r1", "1.3.132.0.37", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST K-571 + SECT571_K1(0x000D, "sect571k1", "1.3.132.0.38", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST B-571 + SECT571_R1(0x000E, "sect571r1", "1.3.132.0.39", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECP160_K1(0x000F, "secp160k1", "1.3.132.0.9", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECP160_R1(0x0010, "secp160r1", "1.3.132.0.8", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECP160_R2(0x0011, "secp160r2", "1.3.132.0.30", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECP192_K1(0x0012, "secp192k1", "1.3.132.0.31", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST P-192 + SECP192_R1(0x0013, "secp192r1", "1.2.840.10045.3.1.1", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECP224_K1(0x0014, "secp224k1", "1.3.132.0.32", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST P-224 + SECP224_R1(0x0015, "secp224r1", "1.3.132.0.33", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + SECP256_K1(0x0016, "secp256k1", "1.3.132.0.10", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_12), + + // NIST P-256 + SECP256_R1(0x0017, "secp256r1", "1.2.840.10045.3.1.7", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_13), + + // NIST P-384 + SECP384_R1(0x0018, "secp384r1", "1.3.132.0.34", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_13), + + // NIST P-521 + SECP521_R1(0x0019, "secp521r1", "1.3.132.0.35", + NamedGroupType.NAMED_GROUP_ECDHE, + ProtocolVersion.PROTOCOLS_TO_13), + + // x25519 and x448 (RFC 8422/8446) + X25519(0x001D, "x25519", "1.3.101.110", + NamedGroupType.NAMED_GROUP_XDH, + ProtocolVersion.PROTOCOLS_TO_13), + X448(0x001E, "x448", "1.3.101.111", + NamedGroupType.NAMED_GROUP_XDH, + ProtocolVersion.PROTOCOLS_TO_13), + + // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919) + FFDHE_2048(0x0100, "ffdhe2048", null, + NamedGroupType.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_3072(0x0101, "ffdhe3072", null, + NamedGroupType.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_4096(0x0102, "ffdhe4096", null, + NamedGroupType.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_6144(0x0103, "ffdhe6144", null, + NamedGroupType.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13), + FFDHE_8192(0x0104, "ffdhe8192", null, + NamedGroupType.NAMED_GROUP_FFDHE, + ProtocolVersion.PROTOCOLS_TO_13), + + // Elliptic Curves (RFC 4492) + // + // arbitrary prime and characteristic-2 curves + ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves", null, + NamedGroupType.NAMED_GROUP_ARBITRARY, + ProtocolVersion.PROTOCOLS_TO_12), + ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves", null, + NamedGroupType.NAMED_GROUP_ARBITRARY, + ProtocolVersion.PROTOCOLS_TO_12); + + final int id; // hash + signature + final NamedGroupType type; // group type + final String name; // literal name + final String oid; // object identifier of the named group + final String algorithm; // signature algorithm + final ProtocolVersion[] supportedProtocols; + private final NamedGroupFunctions functions; // may be null + + // Constructor used for all NamedGroup types + private NamedGroup(int id, String name, String oid, + NamedGroupType namedGroupType, + ProtocolVersion[] supportedProtocols) { + this.id = id; + this.name = name; + this.oid = oid; + this.type = namedGroupType; + this.supportedProtocols = supportedProtocols; + + if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) { + this.functions = ECDHFunctions.getInstance(); + this.algorithm = "EC"; + } else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) { + this.functions = FFDHFunctions.getInstance(); + this.algorithm = "DiffieHellman"; + } else if (this.type == NamedGroupType.NAMED_GROUP_XDH) { + this.functions = XDHFunctions.getInstance(); + this.algorithm = "XDH"; + } else if (this.type == NamedGroupType.NAMED_GROUP_ARBITRARY) { + this.functions = null; + this.algorithm = "EC"; + } else { + throw new RuntimeException("Unexpected Named Group Type"); + } + } + + private Optional getFunctions() { + return Optional.ofNullable(functions); + } + + // The next set of methods search & retrieve NamedGroups. + + static NamedGroup valueOf(int id) { + for (NamedGroup group : NamedGroup.values()) { + if (group.id == id) { + return group; + } + } + + return null; + } + + static NamedGroup valueOf(ECParameterSpec params) { + String oid = ECUtil.getCurveName(null, params); + if ((oid != null) && (!oid.isEmpty())) { + for (NamedGroup group : NamedGroup.values()) { + if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) + && oid.equals(group.oid)) { + return group; + } + } + } + + return null; + } + + static NamedGroup valueOf(DHParameterSpec params) { + for (NamedGroup ng : NamedGroup.values()) { + if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) { + continue; + } + + DHParameterSpec ngParams = null; + // functions is non-null for FFDHE type + AlgorithmParameters aps = ng.functions.getParameters(ng); + try { + ngParams = aps.getParameterSpec(DHParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + // should be unlikely + } + + if (ngParams == null) { + continue; + } + + if (ngParams.getP().equals(params.getP()) + && ngParams.getG().equals(params.getG())) { + return ng; + } + } + + return null; + } + + static NamedGroup nameOf(String name) { + for (NamedGroup group : NamedGroup.values()) { + if (group.name.equals(name)) { + return group; + } + } + + return null; + } + + static String nameOf(int id) { + for (NamedGroup group : NamedGroup.values()) { + if (group.id == id) { + return group.name; + } + } + + return "UNDEFINED-NAMED-GROUP(" + id + ")"; + } + + // Are the NamedGroups available for the protocol desired? + + boolean isAvailable(List protocolVersions) { + for (ProtocolVersion pv : supportedProtocols) { + if (protocolVersions.contains(pv)) { + return true; + } + } + return false; + } + + boolean isAvailable(ProtocolVersion protocolVersion) { + for (ProtocolVersion pv : supportedProtocols) { + if (protocolVersion == pv) { + return true; + } + } + return false; + } + + // Are the NamedGroups available for the ciphersuites desired? + + boolean isSupported(List cipherSuites) { + for (CipherSuite cs : cipherSuites) { + boolean isMatch = isAvailable(cs.supportedProtocols); + if (isMatch && ((cs.keyExchange == null) + || (NamedGroupType.arrayContains( + cs.keyExchange.groupTypes, type)))) { + return true; + } + } + return false; + } + + // lazy loading of parameters + AlgorithmParameters getParameters() { + Optional ngf = getFunctions(); + if (ngf.isEmpty()) { + return null; + } + return ngf.get().getParameters(this); + } + + // The next set of methods use the NamedGroupFunctions table + // to do various operations in a consistent way. + + AlgorithmParameterSpec getParameterSpec() { + Optional ngf = getFunctions(); + if (ngf.isEmpty()) { + return null; + } + return ngf.get().getParameterSpec(this); + } + + byte[] encodePossessionPublicKey( + NamedGroupPossession namedGroupPossession) { + + Optional ngf = getFunctions(); + if (ngf.isEmpty()) { + return null; + } + return ngf.get().encodePossessionPublicKey(namedGroupPossession); + } + + SSLCredentials decodeCredentials(byte[] encoded, + AlgorithmConstraints constraints, + ExceptionSupplier onConstraintFail) + throws IOException, GeneralSecurityException { + + Optional ngf = getFunctions(); + if (ngf.isEmpty()) { + return null; + } + return ngf.get().decodeCredentials(this, encoded, constraints, + onConstraintFail); + } + + SSLPossession createPossession(SecureRandom random) { + + Optional ngf = getFunctions(); + if (ngf.isEmpty()) { + return null; + } + return ngf.get().createPossession(this, random); + } + + SSLKeyDerivation createKeyDerivation(HandshakeContext hc) + throws IOException { + + Optional ngf = getFunctions(); + if (ngf.isEmpty()) { + return null; + } + return ngf.get().createKeyDerivation(hc); + + } + + boolean isAvailableGroup() { + Optional ngfOpt = getFunctions(); + if (ngfOpt.isEmpty()) { + return false; + } + NamedGroupFunctions ngf = ngfOpt.get(); + return ngf.isAvailable(this); + } + + enum NamedGroupType { + NAMED_GROUP_ECDHE, // Elliptic Curve Groups (ECDHE) + NAMED_GROUP_FFDHE, // Finite Field Groups (DHE) + NAMED_GROUP_XDH, // Finite Field Groups (XDH) + NAMED_GROUP_ARBITRARY, // arbitrary prime and curves (ECDHE) + NAMED_GROUP_NONE; // Not predefined named group + + boolean isSupported(List cipherSuites) { + for (CipherSuite cs : cipherSuites) { + if (cs.keyExchange == null || + arrayContains(cs.keyExchange.groupTypes, this)) { + return true; + } + } + + return false; + } + + static boolean arrayContains(NamedGroupType[] namedGroupTypes, + NamedGroupType namedGroupType) { + for (NamedGroupType ng : namedGroupTypes) { + if (ng == namedGroupType) { + return true; + } + } + return false; + } + } + + interface ExceptionSupplier { + void apply(String s) throws SSLException; + } + + /* + * A list of functions to do NamedGroup operations in a + * algorithm-independent and consistent way. + */ + private static abstract class NamedGroupFunctions { + + // cache to speed up the parameters construction + protected static final Map + namedGroupParams = new ConcurrentHashMap<>(); + + protected void checkConstraints(PublicKey publicKey, + AlgorithmConstraints constraints, + ExceptionSupplier onConstraintFail) + throws SSLException { + + if (!constraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + publicKey)) { + + onConstraintFail.apply("key share entry does not " + + "comply with algorithm constraints"); + } + } + + public AlgorithmParameters getParameters(NamedGroup ng) { + + AlgorithmParameters result = namedGroupParams.get(ng); + if (result == null) { + Optional paramsOpt = getParametersImpl(ng); + if (paramsOpt.isPresent()) { + result = paramsOpt.get(); + namedGroupParams.put(ng, result); + } + } + + return result; + } + + public abstract byte[] encodePossessionPublicKey( + NamedGroupPossession namedGroupPossession); + + public abstract SSLCredentials decodeCredentials( + NamedGroup ng, byte[] encoded, + AlgorithmConstraints constraints, + ExceptionSupplier onConstraintFail) + throws IOException, GeneralSecurityException; + + public abstract SSLPossession createPossession(NamedGroup ng, + SecureRandom random); + + public abstract SSLKeyDerivation createKeyDerivation( + HandshakeContext hc) throws IOException; + + protected abstract Optional getParametersImpl( + NamedGroup ng); + + public abstract AlgorithmParameterSpec getParameterSpec(NamedGroup ng); + + public abstract boolean isAvailable(NamedGroup ng); + } + + private static class FFDHFunctions extends NamedGroupFunctions { + + // lazy initialization + private static class FunctionsHolder { + private static final FFDHFunctions instance = new FFDHFunctions(); + } + + private static FFDHFunctions getInstance() { + return FunctionsHolder.instance; + } + + @Override + public byte[] encodePossessionPublicKey( + NamedGroupPossession namedGroupPossession) { + return ((DHEPossession)namedGroupPossession).encode(); + } + + @Override + public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded, + AlgorithmConstraints constraints, + ExceptionSupplier onConstraintFail) + throws IOException, GeneralSecurityException { + + DHKeyExchange.DHECredentials result + = DHKeyExchange.DHECredentials.valueOf(ng, encoded); + + checkConstraints(result.getPublicKey(), constraints, + onConstraintFail); + + return result; + } + + @Override + public SSLPossession createPossession( + NamedGroup ng, SecureRandom random) { + return new DHKeyExchange.DHEPossession(ng, random); + } + + @Override + public SSLKeyDerivation createKeyDerivation(HandshakeContext hc) + throws IOException { + + return DHKeyExchange.kaGenerator.createKeyDerivation(hc); + } + + @Override + public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) { + return getDHParameterSpec(ng); + } + + DHParameterSpec getDHParameterSpec(NamedGroup ng) { + + AlgorithmParameters params = getParameters(ng); + try { + return params.getParameterSpec(DHParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + // should be unlikely + return getPredefinedDHParameterSpec(ng); + } + } + + private static DHParameterSpec getFFDHEDHParameterSpec( + NamedGroup namedGroup) { + + DHParameterSpec spec = null; + switch (namedGroup) { + case FFDHE_2048: + spec = PredefinedDHParameterSpecs.ffdheParams.get(2048); + break; + case FFDHE_3072: + spec = PredefinedDHParameterSpecs.ffdheParams.get(3072); + break; + case FFDHE_4096: + spec = PredefinedDHParameterSpecs.ffdheParams.get(4096); + break; + case FFDHE_6144: + spec = PredefinedDHParameterSpecs.ffdheParams.get(6144); + break; + case FFDHE_8192: + spec = PredefinedDHParameterSpecs.ffdheParams.get(8192); + } + + return spec; + } + + private static DHParameterSpec getPredefinedDHParameterSpec( + NamedGroup namedGroup) { + + DHParameterSpec spec = null; + switch (namedGroup) { + case FFDHE_2048: + spec = PredefinedDHParameterSpecs.definedParams.get(2048); + break; + case FFDHE_3072: + spec = PredefinedDHParameterSpecs.definedParams.get(3072); + break; + case FFDHE_4096: + spec = PredefinedDHParameterSpecs.definedParams.get(4096); + break; + case FFDHE_6144: + spec = PredefinedDHParameterSpecs.definedParams.get(6144); + break; + case FFDHE_8192: + spec = PredefinedDHParameterSpecs.definedParams.get(8192); + } + + return spec; + } + + @Override + public boolean isAvailable(NamedGroup ng) { + + AlgorithmParameters params = getParameters(ng); + return params != null; + } + + @Override + protected Optional getParametersImpl( + NamedGroup ng) { + try { + AlgorithmParameters params + = AlgorithmParameters.getInstance("DiffieHellman"); + AlgorithmParameterSpec spec + = getFFDHEDHParameterSpec(ng); + params.init(spec); + return Optional.of(params); + } catch (InvalidParameterSpecException + | NoSuchAlgorithmException ex) { + return Optional.empty(); + } + } + + } + + private static class ECDHFunctions extends NamedGroupFunctions { + + // lazy initialization + private static class FunctionsHolder { + private static final ECDHFunctions instance = new ECDHFunctions(); + } + + private static ECDHFunctions getInstance() { + return FunctionsHolder.instance; + } + + @Override + public byte[] encodePossessionPublicKey( + NamedGroupPossession namedGroupPossession) { + return ((ECDHEPossession)namedGroupPossession).encode(); + } + + @Override + public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded, + AlgorithmConstraints constraints, + ExceptionSupplier onConstraintFail) + throws IOException, GeneralSecurityException { + + ECDHKeyExchange.ECDHECredentials result + = ECDHKeyExchange.ECDHECredentials.valueOf(ng, encoded); + + checkConstraints(result.getPublicKey(), constraints, + onConstraintFail); + + return result; + } + + @Override + public SSLPossession createPossession( + NamedGroup ng, SecureRandom random) { + return new ECDHKeyExchange.ECDHEPossession(ng, random); + } + + @Override + public SSLKeyDerivation createKeyDerivation(HandshakeContext hc) + throws IOException { + + return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc); + } + + @Override + public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) { + return SupportedGroupsExtension.SupportedGroups + .getECGenParamSpec(ng); + } + + @Override + public boolean isAvailable(NamedGroup ng) { + + AlgorithmParameters params = getParameters(ng); + return params != null; + } + + @Override + protected Optional getParametersImpl( + NamedGroup ng) { + try { + AlgorithmParameters params + = AlgorithmParameters.getInstance("EC"); + AlgorithmParameterSpec spec + = new ECGenParameterSpec(ng.oid); + params.init(spec); + return Optional.of(params); + } catch (InvalidParameterSpecException + | NoSuchAlgorithmException ex) { + return Optional.empty(); + } + } + } + + private static class XDHFunctions extends NamedGroupFunctions { + + // lazy initialization + private static class FunctionsHolder { + private static final XDHFunctions instance = new XDHFunctions(); + } + + private static XDHFunctions getInstance() { + return FunctionsHolder.instance; + } + + @Override + public byte[] encodePossessionPublicKey(NamedGroupPossession poss) { + return ((XDHKeyExchange.XDHEPossession)poss).encode(); + } + + @Override + public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded, + AlgorithmConstraints constraints, + ExceptionSupplier onConstraintFail) + throws IOException, GeneralSecurityException { + + XDHKeyExchange.XDHECredentials result + = XDHKeyExchange.XDHECredentials.valueOf(ng, encoded); + + checkConstraints(result.getPublicKey(), constraints, + onConstraintFail); + + return result; + } + + @Override + public SSLPossession createPossession( + NamedGroup ng, SecureRandom random) { + return new XDHKeyExchange.XDHEPossession(ng, random); + } + + @Override + public SSLKeyDerivation createKeyDerivation(HandshakeContext hc) + throws IOException { + return XDHKeyExchange.xdheKAGenerator.createKeyDerivation(hc); + } + + @Override + public AlgorithmParameterSpec getParameterSpec(NamedGroup ng) { + return new NamedParameterSpec(ng.name); + } + + @Override + public boolean isAvailable(NamedGroup ng) { + + try { + KeyAgreement.getInstance(ng.algorithm); + return true; + } catch (NoSuchAlgorithmException ex) { + return false; + } + } + + @Override + protected Optional getParametersImpl( + NamedGroup ng) { + return Optional.empty(); + } + } +} diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/NamedGroupCredentials.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroupCredentials.java Wed Jun 12 18:58:00 2019 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.PublicKey; + +interface NamedGroupCredentials extends SSLCredentials { + + PublicKey getPublicKey(); + + NamedGroup getNamedGroup(); + +} diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/NamedGroupPossession.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/NamedGroupPossession.java Wed Jun 12 18:58:00 2019 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.PrivateKey; +import java.security.PublicKey; + +interface NamedGroupPossession extends SSLPossession { + + NamedGroup getNamedGroup(); + + PublicKey getPublicKey(); + + PrivateKey getPrivateKey(); +} diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/SSLExtension.java --- a/src/java.base/share/classes/sun/security/ssl/SSLExtension.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/SSLExtension.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -484,6 +484,25 @@ final SSLHandshake handshakeType; final String name; final ProtocolVersion[] supportedProtocols; + + /* + * networkProducer: produces outbound handshake data. + * + * onLoadConsumer: parses inbound data. It may not be appropriate + * to act until all of the message inputs have + * been parsed. (e.g. parsing keyShares and choosing + * a local value without having seen the SupportedGroups + * extension.) + * + * onLoadAbsence: if a missing message needs special handling + * during the load phase. + * + * onTradeConsumer: act on the parsed message once all inbound data has + * been traded and parsed. + * + * onTradeAbsence: if a missing message needs special handling + * during the trade phase. + */ final HandshakeProducer networkProducer; final ExtensionConsumer onLoadConsumer; final HandshakeAbsence onLoadAbsence; diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java --- a/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/SSLKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import sun.security.ssl.DHKeyExchange.DHEPossession; -import sun.security.ssl.ECDHKeyExchange.ECDHEPossession; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; import sun.security.ssl.X509Authentication.X509Possession; @@ -243,8 +239,7 @@ static SSLKeyExchange valueOf(NamedGroup namedGroup) { SSLKeyAgreement ka = T13KeyAgreement.valueOf(namedGroup); if (ka != null) { - return new SSLKeyExchange( - null, T13KeyAgreement.valueOf(namedGroup)); + return new SSLKeyExchange(null, ka); } return null; @@ -337,7 +332,7 @@ ECDH ("ecdh", null, ECDHKeyExchange.ecdhKAGenerator), ECDHE ("ecdhe", ECDHKeyExchange.poGenerator, - ECDHKeyExchange.ecdheKAGenerator); + ECDHKeyExchange.ecdheXdhKAGenerator); final String name; final SSLPossessionGenerator possessionGenerator; @@ -570,27 +565,13 @@ @Override public SSLPossession createPossession(HandshakeContext hc) { - if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { - return new ECDHEPossession( - namedGroup, hc.sslContext.getSecureRandom()); - } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) { - return new DHEPossession( - namedGroup, hc.sslContext.getSecureRandom()); - } - - return null; + return namedGroup.createPossession(hc.sslContext.getSecureRandom()); } @Override public SSLKeyDerivation createKeyDerivation( HandshakeContext hc) throws IOException { - if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { - return ECDHKeyExchange.ecdheKAGenerator.createKeyDerivation(hc); - } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) { - return DHKeyExchange.kaGenerator.createKeyDerivation(hc); - } - - return null; + return namedGroup.createKeyDerivation(hc); } } } diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java --- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -313,7 +313,9 @@ handshakeBuffer.put(handshakeFrag); handshakeBuffer.rewind(); break; - } if (remaining == handshakeMessageLen) { + } + + if (remaining == handshakeMessageLen) { if (handshakeHash.isHashable(handshakeType)) { handshakeHash.receive(handshakeFrag); } diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/SignatureScheme.java --- a/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/SignatureScheme.java Wed Jun 12 18:58:00 2019 -0700 @@ -26,7 +26,6 @@ package sun.security.ssl; import java.security.*; -import java.security.interfaces.ECPrivateKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.MGF1ParameterSpec; @@ -39,8 +38,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; -import sun.security.ssl.SupportedGroupsExtension.NamedGroupType; +import sun.security.ssl.NamedGroup.NamedGroupType; import sun.security.ssl.X509Authentication.X509Possession; import sun.security.util.KeyUtil; import sun.security.util.SignatureUtil; @@ -432,10 +430,10 @@ } for (SignatureScheme ss : schemes) { if (ss.isAvailable && (keySize >= ss.minimalKeySize) && - ss.handshakeSupportedProtocols.contains(version) && - keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) { - if (ss.namedGroup != null && - ss.namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { + ss.handshakeSupportedProtocols.contains(version) && + keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) { + if ((ss.namedGroup != null) && (ss.namedGroup.type == + NamedGroupType.NAMED_GROUP_ECDHE)) { ECParameterSpec params = x509Possession.getECParameterSpec(); if (params != null && diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java --- a/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/SupportedGroupsExtension.java Wed Jun 12 18:58:00 2019 -0700 @@ -30,29 +30,24 @@ import java.security.AlgorithmConstraints; import java.security.AlgorithmParameters; import java.security.CryptoPrimitive; -import java.security.NoSuchAlgorithmException; -import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; -import java.util.Map; -import javax.crypto.spec.DHParameterSpec; import javax.net.ssl.SSLProtocolException; import sun.security.action.GetPropertyAction; +import sun.security.ssl.NamedGroup.NamedGroupType; import static sun.security.ssl.SSLExtension.CH_SUPPORTED_GROUPS; import static sun.security.ssl.SSLExtension.EE_SUPPORTED_GROUPS; import sun.security.ssl.SSLExtension.ExtensionConsumer; import sun.security.ssl.SSLExtension.SSLExtensionSpec; import sun.security.ssl.SSLHandshake.HandshakeMessage; -import sun.security.util.ECUtil; + /** * Pack of the "supported_groups" extensions [RFC 4492/7919]. @@ -158,320 +153,11 @@ } } - static enum NamedGroupType { - NAMED_GROUP_ECDHE ("EC"), - NAMED_GROUP_FFDHE ("DiffieHellman"), - NAMED_GROUP_X25519 ("x25519"), - NAMED_GROUP_X448 ("x448"), - NAMED_GROUP_ARBITRARY ("EC"), - NAMED_GROUP_NONE (""); - - private final String algorithm; - - private NamedGroupType(String algorithm) { - this.algorithm = algorithm; - } - - boolean isSupported(List cipherSuites) { - for (CipherSuite cs : cipherSuites) { - if (cs.keyExchange == null || - cs.keyExchange.groupType == this) { - return true; - } - } - - return false; - } - } - - static enum NamedGroup { - // Elliptic Curves (RFC 4492) - // - // See sun.security.util.CurveDB for the OIDs - // NIST K-163 - SECT163_K1 (0x0001, "sect163k1", "1.3.132.0.1", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT163_R1 (0x0002, "sect163r1", "1.3.132.0.2", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST B-163 - SECT163_R2 (0x0003, "sect163r2", "1.3.132.0.15", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT193_R1 (0x0004, "sect193r1", "1.3.132.0.24", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT193_R2 (0x0005, "sect193r2", "1.3.132.0.25", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST K-233 - SECT233_K1 (0x0006, "sect233k1", "1.3.132.0.26", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST B-233 - SECT233_R1 (0x0007, "sect233r1", "1.3.132.0.27", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECT239_K1 (0x0008, "sect239k1", "1.3.132.0.3", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST K-283 - SECT283_K1 (0x0009, "sect283k1", "1.3.132.0.16", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST B-283 - SECT283_R1 (0x000A, "sect283r1", "1.3.132.0.17", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST K-409 - SECT409_K1 (0x000B, "sect409k1", "1.3.132.0.36", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST B-409 - SECT409_R1 (0x000C, "sect409r1", "1.3.132.0.37", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST K-571 - SECT571_K1 (0x000D, "sect571k1", "1.3.132.0.38", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST B-571 - SECT571_R1 (0x000E, "sect571r1", "1.3.132.0.39", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP160_K1 (0x000F, "secp160k1", "1.3.132.0.9", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP160_R1 (0x0010, "secp160r1", "1.3.132.0.8", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP160_R2 (0x0011, "secp160r2", "1.3.132.0.30", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP192_K1 (0x0012, "secp192k1", "1.3.132.0.31", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST P-192 - SECP192_R1 (0x0013, "secp192r1", "1.2.840.10045.3.1.1", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP224_K1 (0x0014, "secp224k1", "1.3.132.0.32", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - // NIST P-224 - SECP224_R1 (0x0015, "secp224r1", "1.3.132.0.33", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - SECP256_K1 (0x0016, "secp256k1", "1.3.132.0.10", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_12), - - // NIST P-256 - SECP256_R1 (0x0017, "secp256r1", "1.2.840.10045.3.1.7", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_13), - - // NIST P-384 - SECP384_R1 (0x0018, "secp384r1", "1.3.132.0.34", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_13), - - // NIST P-521 - SECP521_R1 (0x0019, "secp521r1", "1.3.132.0.35", - NamedGroupType.NAMED_GROUP_ECDHE, - ProtocolVersion.PROTOCOLS_TO_13), - - // x25519 and x448 - X25519 (0x001D, "x25519", null, - NamedGroupType.NAMED_GROUP_X25519, - ProtocolVersion.PROTOCOLS_TO_13), - X448 (0x001E, "x448", null, - NamedGroupType.NAMED_GROUP_X448, - ProtocolVersion.PROTOCOLS_TO_13), - - // Finite Field Diffie-Hellman Ephemeral Parameters (RFC 7919) - FFDHE_2048 (0x0100, "ffdhe2048", null, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_3072 (0x0101, "ffdhe3072", null, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_4096 (0x0102, "ffdhe4096", null, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_6144 (0x0103, "ffdhe6144", null, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - FFDHE_8192 (0x0104, "ffdhe8192", null, - NamedGroupType.NAMED_GROUP_FFDHE, - ProtocolVersion.PROTOCOLS_TO_13), - - // Elliptic Curves (RFC 4492) - // - // arbitrary prime and characteristic-2 curves - ARBITRARY_PRIME (0xFF01, "arbitrary_explicit_prime_curves", null, - NamedGroupType.NAMED_GROUP_ARBITRARY, - ProtocolVersion.PROTOCOLS_TO_12), - ARBITRARY_CHAR2 (0xFF02, "arbitrary_explicit_char2_curves", null, - NamedGroupType.NAMED_GROUP_ARBITRARY, - ProtocolVersion.PROTOCOLS_TO_12); - - final int id; // hash + signature - final NamedGroupType type; // group type - final String name; // literal name - final String oid; // object identifier of the named group - final String algorithm; // signature algorithm - final ProtocolVersion[] supportedProtocols; - - private NamedGroup(int id, String name, String oid, - NamedGroupType namedGroupType, - ProtocolVersion[] supportedProtocols) { - this.id = id; - this.type = namedGroupType; - this.name = name; - this.oid = oid; - this.algorithm = namedGroupType.algorithm; - this.supportedProtocols = supportedProtocols; - } - - static NamedGroup valueOf(int id) { - for (NamedGroup group : NamedGroup.values()) { - if (group.id == id) { - return group; - } - } - - return null; - } - - static NamedGroup valueOf(ECParameterSpec params) { - String oid = ECUtil.getCurveName(null, params); - if ((oid != null) && (!oid.isEmpty())) { - for (NamedGroup group : NamedGroup.values()) { - if ((group.type == NamedGroupType.NAMED_GROUP_ECDHE) && - oid.equals(group.oid)) { - return group; - } - } - } - - return null; - } - - static NamedGroup valueOf(DHParameterSpec params) { - for (Map.Entry me : - SupportedGroups.namedGroupParams.entrySet()) { - NamedGroup ng = me.getKey(); - if (ng.type != NamedGroupType.NAMED_GROUP_FFDHE) { - continue; - } - - DHParameterSpec ngParams = null; - AlgorithmParameters aps = me.getValue(); - try { - ngParams = aps.getParameterSpec(DHParameterSpec.class); - } catch (InvalidParameterSpecException ipse) { - // should be unlikely - } - - if (ngParams == null) { - continue; - } - - if (ngParams.getP().equals(params.getP()) && - ngParams.getG().equals(params.getG())) { - return ng; - } - } - - return null; - } - - static NamedGroup nameOf(String name) { - for (NamedGroup group : NamedGroup.values()) { - if (group.name.equals(name)) { - return group; - } - } - - return null; - } - - static String nameOf(int id) { - for (NamedGroup group : NamedGroup.values()) { - if (group.id == id) { - return group.name; - } - } - - return "UNDEFINED-NAMED-GROUP(" + id + ")"; - } - - boolean isAvailable(List protocolVersions) { - for (ProtocolVersion pv : supportedProtocols) { - if (protocolVersions.contains(pv)) { - return true; - } - } - return false; - } - - boolean isAvailable(ProtocolVersion protocolVersion) { - for (ProtocolVersion pv : supportedProtocols) { - if (protocolVersion == pv) { - return true; - } - } - return false; - } - - boolean isSupported(List cipherSuites) { - for (CipherSuite cs : cipherSuites) { - boolean isMatch = isAvailable(cs.supportedProtocols); - if (isMatch && (cs.keyExchange == null || - cs.keyExchange.groupType == type)) { - return true; - } - } - return false; - } - - // lazy loading of parameters - AlgorithmParameters getParameters() { - return SupportedGroups.namedGroupParams.get(this); - } - - AlgorithmParameterSpec getParameterSpec() { - if (this.type == NamedGroupType.NAMED_GROUP_ECDHE) { - return SupportedGroups.getECGenParamSpec(this); - } else if (this.type == NamedGroupType.NAMED_GROUP_FFDHE) { - return SupportedGroups.getDHParameterSpec(this); - } - - return null; - } - } - static class SupportedGroups { // To switch off the supported_groups extension for DHE cipher suite. static final boolean enableFFDHE = Utilities.getBooleanProperty("jsse.enableFFDHE", true); - // cache to speed up the parameters construction - static final Map namedGroupParams = new HashMap<>(); - // the supported named groups static final NamedGroup[] supportedNamedGroups; @@ -516,10 +202,19 @@ } } else { // default groups NamedGroup[] groups = new NamedGroup[] { - // NIST curves first + + // Primary XDH (RFC 7748) curves + NamedGroup.X25519, + + // Primary NIST curves (e.g. used in TLSv1.3) NamedGroup.SECP256_R1, NamedGroup.SECP384_R1, NamedGroup.SECP521_R1, + + // Secondary XDH curves + NamedGroup.X448, + + // Secondary NIST curves NamedGroup.SECT283_K1, NamedGroup.SECT283_R1, NamedGroup.SECT409_K1, @@ -530,7 +225,7 @@ // non-NIST curves NamedGroup.SECP256_K1, - // FFDHE 2048 + // FFDHE (RFC 7919) NamedGroup.FFDHE_2048, NamedGroup.FFDHE_3072, NamedGroup.FFDHE_4096, @@ -560,126 +255,27 @@ // check whether the group is supported by the underlying providers private static boolean isAvailableGroup(NamedGroup namedGroup) { - AlgorithmParameters params = null; - AlgorithmParameterSpec spec = null; - if (namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) { - if (namedGroup.oid != null) { - try { - params = AlgorithmParameters.getInstance("EC"); - spec = new ECGenParameterSpec(namedGroup.oid); - } catch (NoSuchAlgorithmException e) { - return false; - } - } - } else if (namedGroup.type == NamedGroupType.NAMED_GROUP_FFDHE) { - try { - params = AlgorithmParameters.getInstance("DiffieHellman"); - spec = getFFDHEDHParameterSpec(namedGroup); - } catch (NoSuchAlgorithmException e) { - return false; - } - } // Otherwise, unsupported. - - if ((params != null) && (spec != null)) { - try { - params.init(spec); - } catch (InvalidParameterSpecException e) { - return false; - } - - // cache the parameters - namedGroupParams.put(namedGroup, params); - - return true; - } - - return false; + return namedGroup.isAvailableGroup(); } - private static DHParameterSpec getFFDHEDHParameterSpec( - NamedGroup namedGroup) { - DHParameterSpec spec = null; - switch (namedGroup) { - case FFDHE_2048: - spec = PredefinedDHParameterSpecs.ffdheParams.get(2048); - break; - case FFDHE_3072: - spec = PredefinedDHParameterSpecs.ffdheParams.get(3072); - break; - case FFDHE_4096: - spec = PredefinedDHParameterSpecs.ffdheParams.get(4096); - break; - case FFDHE_6144: - spec = PredefinedDHParameterSpecs.ffdheParams.get(6144); - break; - case FFDHE_8192: - spec = PredefinedDHParameterSpecs.ffdheParams.get(8192); + static ECGenParameterSpec getECGenParamSpec(NamedGroup ng) { + if (ng.type != NamedGroupType.NAMED_GROUP_ECDHE) { + throw new RuntimeException( + "Not a named EC group: " + ng); } - return spec; - } - - private static DHParameterSpec getPredefinedDHParameterSpec( - NamedGroup namedGroup) { - DHParameterSpec spec = null; - switch (namedGroup) { - case FFDHE_2048: - spec = PredefinedDHParameterSpecs.definedParams.get(2048); - break; - case FFDHE_3072: - spec = PredefinedDHParameterSpecs.definedParams.get(3072); - break; - case FFDHE_4096: - spec = PredefinedDHParameterSpecs.definedParams.get(4096); - break; - case FFDHE_6144: - spec = PredefinedDHParameterSpecs.definedParams.get(6144); - break; - case FFDHE_8192: - spec = PredefinedDHParameterSpecs.definedParams.get(8192); - } - - return spec; - } - - static ECGenParameterSpec getECGenParamSpec(NamedGroup namedGroup) { - if (namedGroup.type != NamedGroupType.NAMED_GROUP_ECDHE) { - throw new RuntimeException( - "Not a named EC group: " + namedGroup); - } - - AlgorithmParameters params = namedGroupParams.get(namedGroup); - if (params == null) { - throw new RuntimeException( - "Not a supported EC named group: " + namedGroup); - } - + // parameters are non-null + AlgorithmParameters params = ng.getParameters(); try { return params.getParameterSpec(ECGenParameterSpec.class); } catch (InvalidParameterSpecException ipse) { // should be unlikely - return new ECGenParameterSpec(namedGroup.oid); + return new ECGenParameterSpec(ng.oid); } } - static DHParameterSpec getDHParameterSpec(NamedGroup namedGroup) { - if (namedGroup.type != NamedGroupType.NAMED_GROUP_FFDHE) { - throw new RuntimeException( - "Not a named DH group: " + namedGroup); - } - - AlgorithmParameters params = namedGroupParams.get(namedGroup); - if (params == null) { - throw new RuntimeException( - "Not a supported DH named group: " + namedGroup); - } - - try { - return params.getParameterSpec(DHParameterSpec.class); - } catch (InvalidParameterSpecException ipse) { - // should be unlikely - return getPredefinedDHParameterSpec(namedGroup); - } + static AlgorithmParameters getParameters(NamedGroup ng) { + return ng.getParameters(); } // Is there any supported group permitted by the constraints? @@ -692,7 +288,7 @@ if (constraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), namedGroup.algorithm, - namedGroupParams.get(namedGroup))) { + getParameters(namedGroup))) { return true; } @@ -723,7 +319,7 @@ return constraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), namedGroup.algorithm, - namedGroupParams.get(namedGroup)); + getParameters(namedGroup)); } // Is the named group supported? @@ -739,16 +335,16 @@ static NamedGroup getPreferredGroup( ProtocolVersion negotiatedProtocol, - AlgorithmConstraints constraints, NamedGroupType type, + AlgorithmConstraints constraints, NamedGroupType[] types, List requestedNamedGroups) { for (NamedGroup namedGroup : requestedNamedGroups) { - if ((namedGroup.type == type) && + if ((NamedGroupType.arrayContains(types, namedGroup.type)) && namedGroup.isAvailable(negotiatedProtocol) && isSupported(namedGroup) && constraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), namedGroup.algorithm, - namedGroupParams.get(namedGroup))) { + getParameters(namedGroup))) { return namedGroup; } } @@ -758,14 +354,14 @@ static NamedGroup getPreferredGroup( ProtocolVersion negotiatedProtocol, - AlgorithmConstraints constraints, NamedGroupType type) { + AlgorithmConstraints constraints, NamedGroupType[] types) { for (NamedGroup namedGroup : supportedNamedGroups) { - if ((namedGroup.type == type) && + if ((NamedGroupType.arrayContains(types, namedGroup.type)) && namedGroup.isAvailable(negotiatedProtocol) && constraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), namedGroup.algorithm, - namedGroupParams.get(namedGroup))) { + getParameters(namedGroup))) { return namedGroup; } } @@ -813,7 +409,7 @@ ng.isSupported(chc.activeCipherSuites) && chc.algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - ng.algorithm, namedGroupParams.get(ng))) { + ng.algorithm, getParameters(ng))) { namedGroups.add(ng); } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( @@ -940,7 +536,7 @@ ng.isSupported(shc.activeCipherSuites) && shc.algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), - ng.algorithm, namedGroupParams.get(ng))) { + ng.algorithm, getParameters(ng))) { namedGroups.add(ng); } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/TransportContext.java --- a/src/java.base/share/classes/sun/security/ssl/TransportContext.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/TransportContext.java Wed Jun 12 18:58:00 2019 -0700 @@ -39,7 +39,6 @@ import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSocket; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; /** * SSL/(D)TLS transportation context. diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/Utilities.java --- a/src/java.base/share/classes/sun/security/ssl/Utilities.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/Utilities.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,8 +61,8 @@ int size = serverNames.size(); List sniList = (size != 0) ? - new ArrayList(serverNames) : - new ArrayList(1); + new ArrayList<>(serverNames) : + new ArrayList<>(1); boolean reset = false; for (int i = 0; i < size; i++) { @@ -147,7 +147,7 @@ static String indent(String source, String prefix) { StringBuilder builder = new StringBuilder(); if (source == null) { - builder.append("\n" + prefix + ""); + builder.append("\n").append(prefix).append(""); } else { String[] lines = lineBreakPatern.split(source); boolean isFirst = true; @@ -232,4 +232,21 @@ } return b; } + + static void reverseBytes(byte[] arr) { + int i = 0; + int j = arr.length - 1; + + while (i < j) { + swap(arr, i, j); + i++; + j--; + } + } + + private static void swap(byte[] arr, int i, int j) { + byte tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } } diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/X509Authentication.java --- a/src/java.base/share/classes/sun/security/ssl/X509Authentication.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/java.base/share/classes/sun/security/ssl/X509Authentication.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,13 +30,15 @@ import java.security.cert.X509Certificate; import java.security.interfaces.ECKey; import java.security.interfaces.ECPublicKey; +import java.security.interfaces.XECKey; +import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECParameterSpec; +import java.security.spec.NamedParameterSpec; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Map; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSocket; import javax.net.ssl.X509ExtendedKeyManager; -import sun.security.ssl.SupportedGroupsExtension.NamedGroup; import sun.security.ssl.SupportedGroupsExtension.SupportedGroups; enum X509Authentication implements SSLAuthentication { @@ -148,6 +150,35 @@ return null; } + + // Similar to above, but for XEC. + NamedParameterSpec getXECParameterSpec() { + if (popPrivateKey == null || + !"XEC".equals(popPrivateKey.getAlgorithm())) { + return null; + } + + if (popPrivateKey instanceof XECKey) { + AlgorithmParameterSpec params = + ((XECKey)popPrivateKey).getParams(); + if (params instanceof NamedParameterSpec){ + return (NamedParameterSpec)params; + } + } else if (popCerts != null && popCerts.length != 0) { + // The private key not extractable, get the parameters from + // the X.509 certificate. + PublicKey publicKey = popCerts[0].getPublicKey(); + if (publicKey instanceof XECKey) { + AlgorithmParameterSpec params = + ((XECKey)publicKey).getParams(); + if (params instanceof NamedParameterSpec){ + return (NamedParameterSpec)params; + } + } + } + + return null; + } } static final class X509Credentials implements SSLCredentials { diff -r 1357c4996b2e -r 946f7f2d321c src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/sun/security/ssl/XDHKeyExchange.java Wed Jun 12 18:58:00 2019 -0700 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.ssl; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.XECPublicKey; +import java.security.spec.*; +import sun.security.ssl.NamedGroup.NamedGroupType; +import sun.security.util.*; + +/** + * Specifics for XEC/XDH Keys/Exchanges + */ +final class XDHKeyExchange { + + static final SSLKeyAgreementGenerator xdheKAGenerator + = new XDHEKAGenerator(); + + static final class XDHECredentials implements NamedGroupCredentials { + + final XECPublicKey popPublicKey; + final NamedGroup namedGroup; + + XDHECredentials(XECPublicKey popPublicKey, NamedGroup namedGroup) { + this.popPublicKey = popPublicKey; + this.namedGroup = namedGroup; + } + + @Override + public PublicKey getPublicKey() { + return popPublicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + /** + * Parse the encoded Point into the XDHECredentials using the + * namedGroup. + */ + static XDHECredentials valueOf(NamedGroup namedGroup, + byte[] encodedPoint) throws IOException, + GeneralSecurityException { + + if (namedGroup.type != NamedGroupType.NAMED_GROUP_XDH) { + throw new RuntimeException( + "Credentials decoding: Not XDH named group"); + } + + if (encodedPoint == null || encodedPoint.length == 0) { + return null; + } + + byte[] uBytes = encodedPoint.clone(); + Utilities.reverseBytes(uBytes); + BigInteger u = new BigInteger(1, uBytes); + + XECPublicKeySpec xecPublicKeySpec = new XECPublicKeySpec( + new NamedParameterSpec(namedGroup.name), u); + KeyFactory factory = KeyFactory.getInstance(namedGroup.algorithm); + XECPublicKey publicKey = (XECPublicKey) factory.generatePublic( + xecPublicKeySpec); + + return new XDHECredentials(publicKey, namedGroup); + } + } + + static final class XDHEPossession implements NamedGroupPossession { + + final PrivateKey privateKey; + final XECPublicKey publicKey; + final NamedGroup namedGroup; + + XDHEPossession(NamedGroup namedGroup, SecureRandom random) { + try { + KeyPairGenerator kpg + = KeyPairGenerator.getInstance(namedGroup.algorithm); + AlgorithmParameterSpec params = namedGroup.getParameterSpec(); + kpg.initialize(params, random); + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + publicKey = (XECPublicKey) kp.getPublic(); + } catch (GeneralSecurityException e) { + throw new RuntimeException( + "Could not generate XDH keypair", e); + } + + this.namedGroup = namedGroup; + } + + @Override + public byte[] encode() { + + byte[] uBytes = ECUtil.trimZeroes(publicKey.getU().toByteArray()); + + int expLength; + switch (namedGroup) { + case X25519: + expLength = 32; + break; + case X448: + expLength = 56; + break; + default: + throw new RuntimeException("Invalid XDH group"); + } + + if (uBytes.length > expLength) { + throw new RuntimeException("Encoded XDH key too large"); + } + + if (uBytes.length != expLength) { + byte[] tmp = new byte[expLength]; + System.arraycopy(uBytes, 0, tmp, + expLength - uBytes.length, uBytes.length); + uBytes = tmp; + } + + Utilities.reverseBytes(uBytes); + return (uBytes); + } + + @Override + public PublicKey getPublicKey() { + return publicKey; + } + + @Override + public NamedGroup getNamedGroup() { + return namedGroup; + } + + @Override + public PrivateKey getPrivateKey() { + return privateKey; + } + } + + private static final class XDHEKAGenerator + implements SSLKeyAgreementGenerator { + + // Prevent instantiation of this class. + private XDHEKAGenerator() { + // blank + } + + @Override + public SSLKeyDerivation createKeyDerivation( + HandshakeContext context) throws IOException { + XDHEPossession xdhePossession = null; + XDHECredentials xdheCredentials = null; + for (SSLPossession poss : context.handshakePossessions) { + if (!(poss instanceof XDHEPossession)) { + continue; + } + + NamedGroup ng = ((XDHEPossession) poss).namedGroup; + for (SSLCredentials cred : context.handshakeCredentials) { + if (!(cred instanceof XDHECredentials)) { + continue; + } + if (ng.equals(((XDHECredentials) cred).namedGroup)) { + xdheCredentials = (XDHECredentials) cred; + break; + } + } + + if (xdheCredentials != null) { + xdhePossession = (XDHEPossession) poss; + break; + } + } + + if (xdhePossession == null || xdheCredentials == null) { + context.conContext.fatal(Alert.HANDSHAKE_FAILURE, + "No sufficient XDHE key agreement " + + "parameters negotiated"); + } + + return new KAKeyDerivation("XDH", context, + xdhePossession.privateKey, xdheCredentials.popPublicKey); + } + } +} diff -r 1357c4996b2e -r 946f7f2d321c src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ import javax.crypto.KeyAgreementSpi; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; import java.util.function.Function; public class XDHKeyAgreement extends KeyAgreementSpi { @@ -202,7 +203,15 @@ throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { - throw new NoSuchAlgorithmException("Not supported"); + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + + if (!(algorithm.equals("TlsPremasterSecret"))) { + throw new NoSuchAlgorithmException( + "Only supported for algorithm TlsPremasterSecret"); + } + return new SecretKeySpec(engineGenerateSecret(), algorithm); } static class X25519 extends XDHKeyAgreement { diff -r 1357c4996b2e -r 946f7f2d321c src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java Wed Jun 12 23:21:24 2019 +0200 +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -156,7 +156,7 @@ new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name); bySize.put(bits, params); byOid.put(oid, params); - byName.put(name, params); + byName.put(name.toLowerCase(), params); } public static Optional getByOid(ObjectIdentifier id) { @@ -166,7 +166,7 @@ return Optional.ofNullable(SIZE_MAP.get(size)); } public static Optional getByName(String name) { - return Optional.ofNullable(NAME_MAP.get(name)); + return Optional.ofNullable(NAME_MAP.get(name.toLowerCase())); } boolean oidEquals(XECParameters other) { diff -r 1357c4996b2e -r 946f7f2d321c test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java --- a/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java Wed Jun 12 23:21:24 2019 +0200 +++ b/test/jdk/javax/net/ssl/templates/SSLSocketTemplate.java Wed Jun 12 18:58:00 2019 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -189,10 +189,15 @@ } /* + * Configure the client side socket. + */ + protected void configureClientSocket(SSLSocket socket) { + } + + /* * Configure the server side socket. */ protected void configureServerSocket(SSLServerSocket socket) { - } /* @@ -317,6 +322,7 @@ try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) { try { + configureClientSocket(sslSocket); sslSocket.connect( new InetSocketAddress("localhost", serverPort), 15000); } catch (IOException ioe) { diff -r 1357c4996b2e -r 946f7f2d321c test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/sun/security/ssl/CipherSuite/SupportedGroups.java Wed Jun 12 18:58:00 2019 -0700 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + /* + * @test + * @bug 8171279 + * @library /javax/net/ssl/templates + * @summary Test TLS connection with each individual supported group + * @run main/othervm SupportedGroups x25519 + * @run main/othervm SupportedGroups x448 + * @run main/othervm SupportedGroups secp256r1 + * @run main/othervm SupportedGroups secp384r1 + * @run main/othervm SupportedGroups secp521r1 + * @run main/othervm SupportedGroups ffdhe2048 + * @run main/othervm SupportedGroups ffdhe3072 + * @run main/othervm SupportedGroups ffdhe4096 + * @run main/othervm SupportedGroups ffdhe6144 + * @run main/othervm SupportedGroups ffdhe8192 + */ +import java.util.Arrays; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLServerSocket; + +public class SupportedGroups extends SSLSocketTemplate { + + private static volatile int index; + private static final String[][][] protocols = { + {{"TLSv1.3"}, {"TLSv1.3"}}, + {{"TLSv1.3", "TLSv1.2"}, {"TLSv1.2"}}, + {{"TLSv1.2"}, {"TLSv1.3", "TLSv1.2"}}, + {{"TLSv1.2"}, {"TLSv1.2"}} + }; + + // Servers are configured before clients, increment test case after. + @Override + protected void configureClientSocket(SSLSocket socket) { + String[] ps = protocols[index][0]; + + System.out.print("Setting client protocol(s): "); + Arrays.stream(ps).forEachOrdered(System.out::print); + System.out.println(); + + socket.setEnabledProtocols(ps); + } + + @Override + protected void configureServerSocket(SSLServerSocket serverSocket) { + String[] ps = protocols[index][1]; + + System.out.print("Setting server protocol(s): "); + Arrays.stream(ps).forEachOrdered(System.out::print); + System.out.println(); + + serverSocket.setEnabledProtocols(ps); + } + + /* + * Run the test case. + */ + public static void main(String[] args) throws Exception { + System.setProperty("jdk.tls.namedGroups", args[0]); + + for (index = 0; index < protocols.length; index++) { + (new SupportedGroups()).run(); + } + } +}