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;
enum NamedGroup {
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),
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),
SECT233_K1(0x0006, "sect233k1", "1.3.132.0.26",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12),
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),
SECT283_K1(0x0009, "sect283k1", "1.3.132.0.16",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12),
SECT283_R1(0x000A, "sect283r1", "1.3.132.0.17",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12),
SECT409_K1(0x000B, "sect409k1", "1.3.132.0.36",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12),
SECT409_R1(0x000C, "sect409r1", "1.3.132.0.37",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12),
SECT571_K1(0x000D, "sect571k1", "1.3.132.0.38",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_12),
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),
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),
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),
SECP256_R1(0x0017, "secp256r1", "1.2.840.10045.3.1.7",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_13),
SECP384_R1(0x0018, "secp384r1", "1.3.132.0.34",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_13),
SECP521_R1(0x0019, "secp521r1", "1.3.132.0.35",
NamedGroupType.NAMED_GROUP_ECDHE,
ProtocolVersion.PROTOCOLS_TO_13),
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),
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),
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;
final NamedGroupType type;
final String name;
final String oid;
final String algorithm;
final ProtocolVersion[] supportedProtocols;
private final NamedGroupFunctions functions;
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<NamedGroupFunctions> getFunctions() {
return Optional.ofNullable(functions);
}
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;
AlgorithmParameters aps = ng.functions.getParameters(ng);
try {
ngParams = aps.getParameterSpec(DHParameterSpec.class);
} catch (InvalidParameterSpecException ipse) {
}
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<ProtocolVersion> 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<CipherSuite> 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;
}
AlgorithmParameters getParameters() {
Optional<NamedGroupFunctions> ngf = getFunctions();
if (ngf.isEmpty()) {
return null;
}
return ngf.get().getParameters(this);
}
AlgorithmParameterSpec getParameterSpec() {
Optional<NamedGroupFunctions> ngf = getFunctions();
if (ngf.isEmpty()) {
return null;
}
return ngf.get().getParameterSpec(this);
}
byte[] encodePossessionPublicKey(
NamedGroupPossession namedGroupPossession) {
Optional<NamedGroupFunctions> ngf = getFunctions();
if (ngf.isEmpty()) {
return null;
}
return ngf.get().encodePossessionPublicKey(namedGroupPossession);
}
SSLCredentials decodeCredentials(byte[] encoded,
AlgorithmConstraints constraints,
ExceptionSupplier onConstraintFail)
throws IOException, GeneralSecurityException {
Optional<NamedGroupFunctions> ngf = getFunctions();
if (ngf.isEmpty()) {
return null;
}
return ngf.get().decodeCredentials(this, encoded, constraints,
onConstraintFail);
}
SSLPossession createPossession(SecureRandom random) {
Optional<NamedGroupFunctions> ngf = getFunctions();
if (ngf.isEmpty()) {
return null;
}
return ngf.get().createPossession(this, random);
}
SSLKeyDerivation createKeyDerivation(HandshakeContext hc)
throws IOException {
Optional<NamedGroupFunctions> ngf = getFunctions();
if (ngf.isEmpty()) {
return null;
}
return ngf.get().createKeyDerivation(hc);
}
boolean isAvailableGroup() {
Optional<NamedGroupFunctions> ngfOpt = getFunctions();
if (ngfOpt.isEmpty()) {
return false;
}
NamedGroupFunctions ngf = ngfOpt.get();
return ngf.isAvailable(this);
}
enum NamedGroupType {
NAMED_GROUP_ECDHE,
NAMED_GROUP_FFDHE,
NAMED_GROUP_XDH,
NAMED_GROUP_ARBITRARY,
NAMED_GROUP_NONE;
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(NamedGroupType[] namedGroupTypes,
NamedGroupType namedGroupType) {
for (NamedGroupType ng : namedGroupTypes) {
if (ng == namedGroupType) {
return true;
}
}
return false;
}
}
interface ExceptionSupplier {
void apply(String s) throws SSLException;
}
private static abstract class NamedGroupFunctions {
protected static final Map<NamedGroup, AlgorithmParameters>
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<AlgorithmParameters> 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<AlgorithmParameters> getParametersImpl(
NamedGroup ng);
public abstract AlgorithmParameterSpec getParameterSpec(NamedGroup ng);
public abstract boolean isAvailable(NamedGroup ng);
}
private static class FFDHFunctions extends NamedGroupFunctions {
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) {
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<AlgorithmParameters> 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 {
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<AlgorithmParameters> 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 {
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<AlgorithmParameters> getParametersImpl(
NamedGroup ng) {
return Optional.empty();
}
}
}