blob: 0bcf8cba51ae037473c0b27e3e80a70a7e959e76 [file] [log] [blame]
package org.bouncycastle.crypto.tls;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Hashtable;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.field.PolynomialExtensionField;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Integers;
public class TlsECCUtils
{
public static final Integer EXT_elliptic_curves = Integers.valueOf(ExtensionType.elliptic_curves);
public static final Integer EXT_ec_point_formats = Integers.valueOf(ExtensionType.ec_point_formats);
private static final String[] CURVE_NAMES = new String[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
"sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
"sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
"secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
"brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves) throws IOException
{
extensions.put(EXT_elliptic_curves, createSupportedEllipticCurvesExtension(namedCurves));
}
public static void addSupportedPointFormatsExtension(Hashtable extensions, short[] ecPointFormats)
throws IOException
{
extensions.put(EXT_ec_point_formats, createSupportedPointFormatsExtension(ecPointFormats));
}
public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions) throws IOException
{
byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_elliptic_curves);
return extensionData == null ? null : readSupportedEllipticCurvesExtension(extensionData);
}
public static short[] getSupportedPointFormatsExtension(Hashtable extensions) throws IOException
{
byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_ec_point_formats);
return extensionData == null ? null : readSupportedPointFormatsExtension(extensionData);
}
public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves) throws IOException
{
if (namedCurves == null || namedCurves.length < 1)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
return TlsUtils.encodeUint16ArrayWithUint16Length(namedCurves);
}
public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) throws IOException
{
if (ecPointFormats == null || !Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
{
/*
* RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
* contain the value 0 (uncompressed) as one of the items in the list of point formats.
*/
// NOTE: We add it at the end (lowest preference)
ecPointFormats = Arrays.append(ecPointFormats, ECPointFormat.uncompressed);
}
return TlsUtils.encodeUint8ArrayWithUint8Length(ecPointFormats);
}
public static int[] readSupportedEllipticCurvesExtension(byte[] extensionData) throws IOException
{
if (extensionData == null)
{
throw new IllegalArgumentException("'extensionData' cannot be null");
}
ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
int length = TlsUtils.readUint16(buf);
if (length < 2 || (length & 1) != 0)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
}
int[] namedCurves = TlsUtils.readUint16Array(length / 2, buf);
TlsProtocol.assertEmpty(buf);
return namedCurves;
}
public static short[] readSupportedPointFormatsExtension(byte[] extensionData) throws IOException
{
if (extensionData == null)
{
throw new IllegalArgumentException("'extensionData' cannot be null");
}
ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
short length = TlsUtils.readUint8(buf);
if (length < 1)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
}
short[] ecPointFormats = TlsUtils.readUint8Array(length, buf);
TlsProtocol.assertEmpty(buf);
if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
{
/*
* RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
* contain the value 0 (uncompressed) as one of the items in the list of point formats.
*/
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
return ecPointFormats;
}
public static String getNameOfNamedCurve(int namedCurve)
{
return isSupportedNamedCurve(namedCurve) ? CURVE_NAMES[namedCurve - 1] : null;
}
public static ECDomainParameters getParametersForNamedCurve(int namedCurve)
{
String curveName = getNameOfNamedCurve(namedCurve);
if (curveName == null)
{
return null;
}
// Parameters are lazily created the first time a particular curve is accessed
X9ECParameters ecP = CustomNamedCurves.getByName(curveName);
if (ecP == null)
{
ecP = ECNamedCurveTable.getByName(curveName);
if (ecP == null)
{
return null;
}
}
// It's a bit inefficient to do this conversion every time
return new ECDomainParameters(ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
}
public static boolean hasAnySupportedNamedCurves()
{
return CURVE_NAMES.length > 0;
}
public static boolean containsECCCipherSuites(int[] cipherSuites)
{
for (int i = 0; i < cipherSuites.length; ++i)
{
if (isECCCipherSuite(cipherSuites[i]))
{
return true;
}
}
return false;
}
public static boolean isECCCipherSuite(int cipherSuite)
{
switch (cipherSuite)
{
/*
* RFC 4492
*/
case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA:
case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
/*
* RFC 5289
*/
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
/*
* RFC 5489
*/
case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
/*
* RFC 6367
*/
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
/*
* RFC 7251
*/
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
/*
* draft-ietf-tls-chacha20-poly1305-04
*/
case CipherSuite.DRAFT_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.DRAFT_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256:
case CipherSuite.DRAFT_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
/*
* draft-zauner-tls-aes-ocb-04
*/
case CipherSuite.DRAFT_TLS_ECDHE_RSA_WITH_AES_128_OCB:
case CipherSuite.DRAFT_TLS_ECDHE_RSA_WITH_AES_256_OCB:
case CipherSuite.DRAFT_TLS_ECDHE_ECDSA_WITH_AES_128_OCB:
case CipherSuite.DRAFT_TLS_ECDHE_ECDSA_WITH_AES_256_OCB:
case CipherSuite.DRAFT_TLS_ECDHE_PSK_WITH_AES_128_OCB:
case CipherSuite.DRAFT_TLS_ECDHE_PSK_WITH_AES_256_OCB:
return true;
default:
return false;
}
}
public static boolean areOnSameCurve(ECDomainParameters a, ECDomainParameters b)
{
return a != null && a.equals(b);
}
public static boolean isSupportedNamedCurve(int namedCurve)
{
return (namedCurve > 0 && namedCurve <= CURVE_NAMES.length);
}
public static boolean isCompressionPreferred(short[] ecPointFormats, short compressionFormat)
{
if (ecPointFormats == null)
{
return false;
}
for (int i = 0; i < ecPointFormats.length; ++i)
{
short ecPointFormat = ecPointFormats[i];
if (ecPointFormat == ECPointFormat.uncompressed)
{
return false;
}
if (ecPointFormat == compressionFormat)
{
return true;
}
}
return false;
}
public static byte[] serializeECFieldElement(int fieldSize, BigInteger x) throws IOException
{
return BigIntegers.asUnsignedByteArray((fieldSize + 7) / 8, x);
}
public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point) throws IOException
{
ECCurve curve = point.getCurve();
/*
* RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. Here, the
* format MUST conform to what the server has requested through a Supported Point Formats
* Extension if this extension was used, and MUST be uncompressed if this extension was not
* used.
*/
boolean compressed = false;
if (ECAlgorithms.isFpCurve(curve))
{
compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime);
}
else if (ECAlgorithms.isF2mCurve(curve))
{
compressed = isCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2);
}
return point.getEncoded(compressed);
}
public static byte[] serializeECPublicKey(short[] ecPointFormats, ECPublicKeyParameters keyParameters)
throws IOException
{
return serializeECPoint(ecPointFormats, keyParameters.getQ());
}
public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding) throws IOException
{
int requiredLength = (fieldSize + 7) / 8;
if (encoding.length != requiredLength)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
}
return new BigInteger(1, encoding);
}
public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding) throws IOException
{
if (encoding == null || encoding.length < 1)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
short actualFormat;
switch (encoding[0])
{
case 0x02: // compressed
case 0x03: // compressed
{
if (ECAlgorithms.isF2mCurve(curve))
{
actualFormat = ECPointFormat.ansiX962_compressed_char2;
}
else if (ECAlgorithms.isFpCurve(curve))
{
actualFormat = ECPointFormat.ansiX962_compressed_prime;
}
else
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
break;
}
case 0x04: // uncompressed
{
actualFormat = ECPointFormat.uncompressed;
break;
}
case 0x00: // infinity
case 0x06: // hybrid
case 0x07: // hybrid
default:
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
if (actualFormat != ECPointFormat.uncompressed
&& (ecPointFormats == null || !Arrays.contains(ecPointFormats, actualFormat)))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
return curve.decodePoint(encoding);
}
public static ECPublicKeyParameters deserializeECPublicKey(short[] ecPointFormats, ECDomainParameters curve_params,
byte[] encoding) throws IOException
{
try
{
ECPoint Y = deserializeECPoint(ecPointFormats, curve_params.getCurve(), encoding);
return new ECPublicKeyParameters(Y, curve_params);
}
catch (RuntimeException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
public static byte[] calculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey)
{
ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
basicAgreement.init(privateKey);
BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey);
/*
* RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by
* FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for
* any given field; leading zeros found in this octet string MUST NOT be truncated.
*/
return BigIntegers.asUnsignedByteArray(basicAgreement.getFieldSize(), agreementValue);
}
public static AsymmetricCipherKeyPair generateECKeyPair(SecureRandom random, ECDomainParameters ecParams)
{
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
keyPairGenerator.init(new ECKeyGenerationParameters(ecParams, random));
return keyPairGenerator.generateKeyPair();
}
public static ECPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, short[] ecPointFormats,
ECDomainParameters ecParams, OutputStream output) throws IOException
{
AsymmetricCipherKeyPair kp = generateECKeyPair(random, ecParams);
ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic();
writeECPoint(ecPointFormats, ecPublicKey.getQ(), output);
return (ECPrivateKeyParameters) kp.getPrivate();
}
// TODO Refactor around ServerECDHParams before making this public
static ECPrivateKeyParameters generateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves,
short[] ecPointFormats, OutputStream output) throws IOException
{
/* First we try to find a supported named curve from the client's list. */
int namedCurve = -1;
if (namedCurves == null)
{
// TODO Let the peer choose the default named curve
namedCurve = NamedCurve.secp256r1;
}
else
{
for (int i = 0; i < namedCurves.length; ++i)
{
int entry = namedCurves[i];
if (NamedCurve.isValid(entry) && isSupportedNamedCurve(entry))
{
namedCurve = entry;
break;
}
}
}
ECDomainParameters ecParams = null;
if (namedCurve >= 0)
{
ecParams = getParametersForNamedCurve(namedCurve);
}
else
{
/* If no named curves are suitable, check if the client supports explicit curves. */
if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
{
ecParams = getParametersForNamedCurve(NamedCurve.secp256r1);
}
else if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
{
ecParams = getParametersForNamedCurve(NamedCurve.sect283r1);
}
}
if (ecParams == null)
{
/*
* NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
* a suitable curve.
*/
throw new TlsFatalAlert(AlertDescription.internal_error);
}
if (namedCurve < 0)
{
writeExplicitECParameters(ecPointFormats, ecParams, output);
}
else
{
writeNamedECParameters(namedCurve, output);
}
return generateEphemeralClientKeyExchange(random, ecPointFormats, ecParams, output);
}
public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key) throws IOException
{
// TODO Check RFC 4492 for validation
return key;
}
public static int readECExponent(int fieldSize, InputStream input) throws IOException
{
BigInteger K = readECParameter(input);
if (K.bitLength() < 32)
{
int k = K.intValue();
if (k > 0 && k < fieldSize)
{
return k;
}
}
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
public static BigInteger readECFieldElement(int fieldSize, InputStream input) throws IOException
{
return deserializeECFieldElement(fieldSize, TlsUtils.readOpaque8(input));
}
public static BigInteger readECParameter(InputStream input) throws IOException
{
// TODO Are leading zeroes okay here?
return new BigInteger(1, TlsUtils.readOpaque8(input));
}
public static ECDomainParameters readECParameters(int[] namedCurves, short[] ecPointFormats, InputStream input)
throws IOException
{
try
{
short curveType = TlsUtils.readUint8(input);
switch (curveType)
{
case ECCurveType.explicit_prime:
{
checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves);
BigInteger prime_p = readECParameter(input);
BigInteger a = readECFieldElement(prime_p.bitLength(), input);
BigInteger b = readECFieldElement(prime_p.bitLength(), input);
byte[] baseEncoding = TlsUtils.readOpaque8(input);
BigInteger order = readECParameter(input);
BigInteger cofactor = readECParameter(input);
ECCurve curve = new ECCurve.Fp(prime_p, a, b, order, cofactor);
ECPoint base = deserializeECPoint(ecPointFormats, curve, baseEncoding);
return new ECDomainParameters(curve, base, order, cofactor);
}
case ECCurveType.explicit_char2:
{
checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves);
int m = TlsUtils.readUint16(input);
short basis = TlsUtils.readUint8(input);
if (!ECBasisType.isValid(basis))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
int k1 = readECExponent(m, input), k2 = -1, k3 = -1;
if (basis == ECBasisType.ec_basis_pentanomial)
{
k2 = readECExponent(m, input);
k3 = readECExponent(m, input);
}
BigInteger a = readECFieldElement(m, input);
BigInteger b = readECFieldElement(m, input);
byte[] baseEncoding = TlsUtils.readOpaque8(input);
BigInteger order = readECParameter(input);
BigInteger cofactor = readECParameter(input);
ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial)
? new ECCurve.F2m(m, k1, k2, k3, a, b, order, cofactor)
: new ECCurve.F2m(m, k1, a, b, order, cofactor);
ECPoint base = deserializeECPoint(ecPointFormats, curve, baseEncoding);
return new ECDomainParameters(curve, base, order, cofactor);
}
case ECCurveType.named_curve:
{
int namedCurve = TlsUtils.readUint16(input);
if (!NamedCurve.refersToASpecificNamedCurve(namedCurve))
{
/*
* RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a
* specific curve. Values of NamedCurve that indicate support for a class of
* explicitly defined curves are not allowed here [...].
*/
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
checkNamedCurve(namedCurves, namedCurve);
return getParametersForNamedCurve(namedCurve);
}
default:
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
catch (RuntimeException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
private static void checkNamedCurve(int[] namedCurves, int namedCurve) throws IOException
{
if (namedCurves != null && !Arrays.contains(namedCurves, namedCurve))
{
/*
* RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
* unless they can complete the handshake while respecting the choice of curves
* and compression techniques specified by the client.
*/
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
}
public static void writeECExponent(int k, OutputStream output) throws IOException
{
BigInteger K = BigInteger.valueOf(k);
writeECParameter(K, output);
}
public static void writeECFieldElement(ECFieldElement x, OutputStream output) throws IOException
{
TlsUtils.writeOpaque8(x.getEncoded(), output);
}
public static void writeECFieldElement(int fieldSize, BigInteger x, OutputStream output) throws IOException
{
TlsUtils.writeOpaque8(serializeECFieldElement(fieldSize, x), output);
}
public static void writeECParameter(BigInteger x, OutputStream output) throws IOException
{
TlsUtils.writeOpaque8(BigIntegers.asUnsignedByteArray(x), output);
}
public static void writeExplicitECParameters(short[] ecPointFormats, ECDomainParameters ecParameters,
OutputStream output) throws IOException
{
ECCurve curve = ecParameters.getCurve();
if (ECAlgorithms.isFpCurve(curve))
{
TlsUtils.writeUint8(ECCurveType.explicit_prime, output);
writeECParameter(curve.getField().getCharacteristic(), output);
}
else if (ECAlgorithms.isF2mCurve(curve))
{
PolynomialExtensionField field = (PolynomialExtensionField)curve.getField();
int[] exponents = field.getMinimalPolynomial().getExponentsPresent();
TlsUtils.writeUint8(ECCurveType.explicit_char2, output);
int m = exponents[exponents.length - 1];
TlsUtils.checkUint16(m);
TlsUtils.writeUint16(m, output);
if (exponents.length == 3)
{
TlsUtils.writeUint8(ECBasisType.ec_basis_trinomial, output);
writeECExponent(exponents[1], output);
}
else if (exponents.length == 5)
{
TlsUtils.writeUint8(ECBasisType.ec_basis_pentanomial, output);
writeECExponent(exponents[1], output);
writeECExponent(exponents[2], output);
writeECExponent(exponents[3], output);
}
else
{
throw new IllegalArgumentException("Only trinomial and pentomial curves are supported");
}
}
else
{
throw new IllegalArgumentException("'ecParameters' not a known curve type");
}
writeECFieldElement(curve.getA(), output);
writeECFieldElement(curve.getB(), output);
TlsUtils.writeOpaque8(serializeECPoint(ecPointFormats, ecParameters.getG()), output);
writeECParameter(ecParameters.getN(), output);
writeECParameter(ecParameters.getH(), output);
}
public static void writeECPoint(short[] ecPointFormats, ECPoint point, OutputStream output) throws IOException
{
TlsUtils.writeOpaque8(serializeECPoint(ecPointFormats, point), output);
}
public static void writeNamedECParameters(int namedCurve, OutputStream output) throws IOException
{
if (!NamedCurve.refersToASpecificNamedCurve(namedCurve))
{
/*
* RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific
* curve. Values of NamedCurve that indicate support for a class of explicitly defined
* curves are not allowed here [...].
*/
throw new TlsFatalAlert(AlertDescription.internal_error);
}
TlsUtils.writeUint8(ECCurveType.named_curve, output);
TlsUtils.checkUint16(namedCurve);
TlsUtils.writeUint16(namedCurve, output);
}
}