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.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import javax.crypto.KeyAgreement;
import sun.security.ssl.DHKeyExchange.DHEPossession;
import sun.security.ssl.ECDHKeyExchange.ECDHEPossession;
import sun.security.util.CurveDB;
enum NamedGroup {
SECT163_K1(0x0001, "sect163k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect163k1")),
SECT163_R1(0x0002, "sect163r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect163r1")),
SECT163_R2(0x0003, "sect163r2",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect163r2")),
SECT193_R1(0x0004, "sect193r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect193r1")),
SECT193_R2(0x0005, "sect193r2",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect193r2")),
SECT233_K1(0x0006, "sect233k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect233k1")),
SECT233_R1(0x0007, "sect233r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect233r1")),
SECT239_K1(0x0008, "sect239k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect239k1")),
SECT283_K1(0x0009, "sect283k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect283k1")),
SECT283_R1(0x000A, "sect283r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect283r1")),
SECT409_K1(0x000B, "sect409k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect409k1")),
SECT409_R1(0x000C, "sect409r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect409r1")),
SECT571_K1(0x000D, "sect571k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect571k1")),
SECT571_R1(0x000E, "sect571r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("sect571r1")),
SECP160_K1(0x000F, "secp160k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp160k1")),
SECP160_R1(0x0010, "secp160r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp160r1")),
SECP160_R2(0x0011, "secp160r2",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp160r2")),
SECP192_K1(0x0012, "secp192k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp192k1")),
SECP192_R1(0x0013, "secp192r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp192r1")),
SECP224_K1(0x0014, "secp224k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp224k1")),
SECP224_R1(0x0015, "secp224r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp224r1")),
SECP256_K1(0x0016, "secp256k1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12,
CurveDB.lookup("secp256k1")),
SECP256_R1(0x0017, "secp256r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_13,
CurveDB.lookup("secp256r1")),
SECP384_R1(0x0018, "secp384r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_13,
CurveDB.lookup("secp384r1")),
SECP521_R1(0x0019, "secp521r1",
NamedGroupSpec.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_13,
CurveDB.lookup("secp521r1")),
X25519(0x001D, "x25519",
NamedGroupSpec.NAMED_GROUP_XDH,
ProtocolVersion.PROTOCOLS_TO_13,
NamedParameterSpec.X25519),
X448(0x001E, "x448",
NamedGroupSpec.NAMED_GROUP_XDH,
ProtocolVersion.PROTOCOLS_TO_13,
NamedParameterSpec.X448),
FFDHE_2048(0x0100, "ffdhe2048",
NamedGroupSpec.NAMED_GROUP_FFDHE,
ProtocolVersion.PROTOCOLS_TO_13,
PredefinedDHParameterSpecs.ffdheParams.get(2048)),
FFDHE_3072(0x0101, "ffdhe3072",
NamedGroupSpec.NAMED_GROUP_FFDHE,
ProtocolVersion.PROTOCOLS_TO_13,
PredefinedDHParameterSpecs.ffdheParams.get(3072)),
FFDHE_4096(0x0102, "ffdhe4096",
NamedGroupSpec.NAMED_GROUP_FFDHE,
ProtocolVersion.PROTOCOLS_TO_13,
PredefinedDHParameterSpecs.ffdheParams.get(4096)),
FFDHE_6144(0x0103, "ffdhe6144",
NamedGroupSpec.NAMED_GROUP_FFDHE,
ProtocolVersion.PROTOCOLS_TO_13,
PredefinedDHParameterSpecs.ffdheParams.get(6144)),
FFDHE_8192(0x0104, "ffdhe8192",
NamedGroupSpec.NAMED_GROUP_FFDHE,
ProtocolVersion.PROTOCOLS_TO_13,
PredefinedDHParameterSpecs.ffdheParams.get(8192)),
ARBITRARY_PRIME(0xFF01, "arbitrary_explicit_prime_curves",
NamedGroupSpec.NAMED_GROUP_ARBITRARY,
ProtocolVersion.PROTOCOLS_TO_12,
null),
ARBITRARY_CHAR2(0xFF02, "arbitrary_explicit_char2_curves",
NamedGroupSpec.NAMED_GROUP_ARBITRARY,
ProtocolVersion.PROTOCOLS_TO_12,
null);
final int id;
final String name;
final NamedGroupSpec spec;
final ProtocolVersion[] supportedProtocols;
final String algorithm;
final AlgorithmParameterSpec keAlgParamSpec;
final AlgorithmParameters keAlgParams;
final boolean isAvailable;
private static final Set<CryptoPrimitive> KEY_AGREEMENT_PRIMITIVE_SET =
Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.KEY_AGREEMENT));
private NamedGroup(int id, String name,
NamedGroupSpec namedGroupSpec,
ProtocolVersion[] supportedProtocols,
AlgorithmParameterSpec keAlgParamSpec) {
this.id = id;
this.name = name;
this.spec = namedGroupSpec;
this.algorithm = namedGroupSpec.algorithm;
this.supportedProtocols = supportedProtocols;
this.keAlgParamSpec = keAlgParamSpec;
AlgorithmParameters algParams = null;
boolean mediator = (keAlgParamSpec != null);
if (mediator && (namedGroupSpec == NamedGroupSpec.NAMED_GROUP_ECDHE)) {
mediator = JsseJce.isEcAvailable();
}
if (mediator) {
try {
algParams =
AlgorithmParameters.getInstance(namedGroupSpec.algorithm);
algParams.init(keAlgParamSpec);
} catch (InvalidParameterSpecException
| NoSuchAlgorithmException exp) {
if (namedGroupSpec != NamedGroupSpec.NAMED_GROUP_XDH) {
mediator = false;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"No AlgorithmParameters for " + name, exp);
}
} else {
algParams = null;
try {
KeyAgreement.getInstance(name);
} catch (NoSuchAlgorithmException nsae) {
mediator = false;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"No AlgorithmParameters for " + name, nsae);
}
}
}
}
}
this.isAvailable = mediator;
this.keAlgParams = mediator ? algParams : null;
}
static NamedGroup valueOf(int id) {
for (NamedGroup group : NamedGroup.values()) {
if (group.id == id) {
return group;
}
}
return null;
}
static NamedGroup valueOf(ECParameterSpec params) {
for (NamedGroup ng : NamedGroup.values()) {
if (ng.spec == NamedGroupSpec.NAMED_GROUP_ECDHE) {
if ((params == ng.keAlgParamSpec) ||
(ng.keAlgParamSpec == CurveDB.lookup(params))) {
return ng;
}
}
}
return null;
}
static NamedGroup valueOf(DHParameterSpec params) {
for (NamedGroup ng : NamedGroup.values()) {
if (ng.spec != NamedGroupSpec.NAMED_GROUP_FFDHE) {
continue;
}
DHParameterSpec ngParams = (DHParameterSpec)ng.keAlgParamSpec;
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.equalsIgnoreCase(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<ProtocolVersion> protocolVersions) {
if (this.isAvailable) {
for (ProtocolVersion pv : supportedProtocols) {
if (protocolVersions.contains(pv)) {
return true;
}
}
}
return false;
}
boolean isAvailable(ProtocolVersion protocolVersion) {
if (this.isAvailable) {
for (ProtocolVersion pv : supportedProtocols) {
if (protocolVersion == pv) {
return true;
}
}
}
return false;
}
boolean isSupported(List<CipherSuite> cipherSuites) {
for (CipherSuite cs : cipherSuites) {
boolean isMatch = isAvailable(cs.supportedProtocols);
if (isMatch && ((cs.keyExchange == null)
|| (NamedGroupSpec.arrayContains(
cs.keyExchange.groupTypes, spec)))) {
return true;
}
}
return false;
}
boolean isPermitted(AlgorithmConstraints constraints) {
return constraints.permits(KEY_AGREEMENT_PRIMITIVE_SET,
this.name, null) &&
constraints.permits(KEY_AGREEMENT_PRIMITIVE_SET,
this.algorithm, this.keAlgParams);
}
byte[] encodePossessionPublicKey(
NamedGroupPossession namedGroupPossession) {
return spec.encodePossessionPublicKey(namedGroupPossession);
}
SSLCredentials decodeCredentials(byte[] encoded,
AlgorithmConstraints constraints,
ExceptionSupplier onConstraintFail)
throws IOException, GeneralSecurityException {
return spec.decodeCredentials(
this, encoded, constraints, onConstraintFail);
}
SSLPossession createPossession(SecureRandom random) {
return spec.createPossession(this, random);
}
SSLKeyDerivation createKeyDerivation(
HandshakeContext hc) throws IOException {
return spec.createKeyDerivation(hc);
}
interface ExceptionSupplier {
void apply(String s) throws SSLException;
}
private interface NamedGroupScheme {
default 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");
}
}
byte[] encodePossessionPublicKey(
NamedGroupPossession namedGroupPossession);
SSLCredentials decodeCredentials(
NamedGroup ng, byte[] encoded,
AlgorithmConstraints constraints,
ExceptionSupplier onConstraintFail
) throws IOException, GeneralSecurityException;
SSLPossession createPossession(NamedGroup ng, SecureRandom random);
SSLKeyDerivation createKeyDerivation(
HandshakeContext hc) throws IOException;
}
enum NamedGroupSpec implements NamedGroupScheme {
NAMED_GROUP_ECDHE("EC", ECDHEScheme.instance),
NAMED_GROUP_FFDHE("DiffieHellman", FFDHEScheme.instance),
NAMED_GROUP_XDH("XDH", XDHScheme.instance),
NAMED_GROUP_ARBITRARY("EC", null),
NAMED_GROUP_NONE("", null);
private final String algorithm;
private final NamedGroupScheme scheme;
private NamedGroupSpec(String algorithm, NamedGroupScheme scheme) {
this.algorithm = algorithm;
this.scheme = scheme;
}
boolean isSupported(List<CipherSuite> cipherSuites) {
for (CipherSuite cs : cipherSuites) {
if (cs.keyExchange == null ||
arrayContains(cs.keyExchange.groupTypes, this)) {
return true;
}
}
return false;
}
static boolean arrayContains(NamedGroupSpec[] namedGroupTypes,
NamedGroupSpec namedGroupType) {
for (NamedGroupSpec ng : namedGroupTypes) {
if (ng == namedGroupType) {
return true;
}
}
return false;
}
@Override
public byte[] encodePossessionPublicKey(
NamedGroupPossession namedGroupPossession) {
if (scheme != null) {
return scheme.encodePossessionPublicKey(namedGroupPossession);
}
return null;
}
@Override
public SSLCredentials decodeCredentials(NamedGroup ng, byte[] encoded,
AlgorithmConstraints constraints,
ExceptionSupplier onConstraintFail
) throws IOException, GeneralSecurityException {
if (scheme != null) {
return scheme.decodeCredentials(
ng, encoded, constraints, onConstraintFail);
}
return null;
}
@Override
public SSLPossession createPossession(
NamedGroup ng, SecureRandom random) {
if (scheme != null) {
return scheme.createPossession(ng, random);
}
return null;
}
@Override
public SSLKeyDerivation createKeyDerivation(
HandshakeContext hc) throws IOException {
if (scheme != null) {
return scheme.createKeyDerivation(hc);
}
return null;
}
}
private static class FFDHEScheme implements NamedGroupScheme {
private static final FFDHEScheme instance = new FFDHEScheme();
@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);
}
}
private static class ECDHEScheme implements NamedGroupScheme {
private static final ECDHEScheme instance = new ECDHEScheme();
@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);
}
}
private static class XDHScheme implements NamedGroupScheme {
private static final XDHScheme instance = new XDHScheme();
@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);
}
}
}