710 lines
29 KiB
C#
710 lines
29 KiB
C#
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
|
#pragma warning disable
|
|
using System;
|
|
using System.Collections;
|
|
using System.IO;
|
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X9;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Agreement;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.EC;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Math;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Math.EC;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Math.Field;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
|
|
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Tls
|
|
{
|
|
public abstract class TlsEccUtilities
|
|
{
|
|
private static readonly string[] CurveNames = 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(IDictionary extensions, int[] namedCurves)
|
|
{
|
|
extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves);
|
|
}
|
|
|
|
public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats)
|
|
{
|
|
extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats);
|
|
}
|
|
|
|
public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions)
|
|
{
|
|
byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves);
|
|
return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData);
|
|
}
|
|
|
|
public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions)
|
|
{
|
|
byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats);
|
|
return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData);
|
|
}
|
|
|
|
public static byte[] CreateSupportedEllipticCurvesExtension(int[] namedCurves)
|
|
{
|
|
if (namedCurves == null || namedCurves.Length < 1)
|
|
throw new TlsFatalAlert(AlertDescription.internal_error);
|
|
|
|
return TlsUtilities.EncodeUint16ArrayWithUint16Length(namedCurves);
|
|
}
|
|
|
|
public static byte[] CreateSupportedPointFormatsExtension(byte[] ecPointFormats)
|
|
{
|
|
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 TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats);
|
|
}
|
|
|
|
public static int[] ReadSupportedEllipticCurvesExtension(byte[] extensionData)
|
|
{
|
|
if (extensionData == null)
|
|
throw new ArgumentNullException("extensionData");
|
|
|
|
MemoryStream buf = new MemoryStream(extensionData, false);
|
|
|
|
int length = TlsUtilities.ReadUint16(buf);
|
|
if (length < 2 || (length & 1) != 0)
|
|
throw new TlsFatalAlert(AlertDescription.decode_error);
|
|
|
|
int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf);
|
|
|
|
TlsProtocol.AssertEmpty(buf);
|
|
|
|
return namedCurves;
|
|
}
|
|
|
|
public static byte[] ReadSupportedPointFormatsExtension(byte[] extensionData)
|
|
{
|
|
byte[] ecPointFormats = TlsUtilities.DecodeUint8ArrayWithUint8Length(extensionData);
|
|
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) ? CurveNames[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.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
|
|
}
|
|
|
|
public static bool HasAnySupportedNamedCurves()
|
|
{
|
|
return CurveNames.Length > 0;
|
|
}
|
|
|
|
public static bool ContainsEccCipherSuites(int[] cipherSuites)
|
|
{
|
|
for (int i = 0; i < cipherSuites.Length; ++i)
|
|
{
|
|
if (IsEccCipherSuite(cipherSuites[i]))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static bool 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 bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b)
|
|
{
|
|
return a != null && a.Equals(b);
|
|
}
|
|
|
|
public static bool IsSupportedNamedCurve(int namedCurve)
|
|
{
|
|
return (namedCurve > 0 && namedCurve <= CurveNames.Length);
|
|
}
|
|
|
|
public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat)
|
|
{
|
|
if (ecPointFormats == null)
|
|
return false;
|
|
|
|
for (int i = 0; i < ecPointFormats.Length; ++i)
|
|
{
|
|
byte ecPointFormat = ecPointFormats[i];
|
|
if (ecPointFormat == ECPointFormat.uncompressed)
|
|
return false;
|
|
if (ecPointFormat == compressionFormat)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static byte[] SerializeECFieldElement(int fieldSize, BigInteger x)
|
|
{
|
|
return BigIntegers.AsUnsignedByteArray((fieldSize + 7) / 8, x);
|
|
}
|
|
|
|
public static byte[] SerializeECPoint(byte[] ecPointFormats, ECPoint point)
|
|
{
|
|
ECCurve curve = point.Curve;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
bool 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(byte[] ecPointFormats, ECPublicKeyParameters keyParameters)
|
|
{
|
|
return SerializeECPoint(ecPointFormats, keyParameters.Q);
|
|
}
|
|
|
|
public static BigInteger DeserializeECFieldElement(int fieldSize, byte[] encoding)
|
|
{
|
|
int requiredLength = (fieldSize + 7) / 8;
|
|
if (encoding.Length != requiredLength)
|
|
throw new TlsFatalAlert(AlertDescription.decode_error);
|
|
return new BigInteger(1, encoding);
|
|
}
|
|
|
|
public static ECPoint DeserializeECPoint(byte[] ecPointFormats, ECCurve curve, byte[] encoding)
|
|
{
|
|
if (encoding == null || encoding.Length < 1)
|
|
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
|
|
|
|
byte 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(byte[] ecPointFormats, ECDomainParameters curve_params,
|
|
byte[] encoding)
|
|
{
|
|
try
|
|
{
|
|
ECPoint Y = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding);
|
|
return new ECPublicKeyParameters(Y, curve_params);
|
|
}
|
|
catch (Exception 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, byte[] ecPointFormats,
|
|
ECDomainParameters ecParams, Stream output)
|
|
{
|
|
AsymmetricCipherKeyPair kp = GenerateECKeyPair(random, ecParams);
|
|
|
|
ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public;
|
|
WriteECPoint(ecPointFormats, ecPublicKey.Q, output);
|
|
|
|
return (ECPrivateKeyParameters)kp.Private;
|
|
}
|
|
|
|
// TODO Refactor around ServerECDHParams before making this public
|
|
internal static ECPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves,
|
|
byte[] ecPointFormats, Stream output)
|
|
{
|
|
/* 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)
|
|
{
|
|
// TODO Check RFC 4492 for validation
|
|
return key;
|
|
}
|
|
|
|
public static int ReadECExponent(int fieldSize, Stream input)
|
|
{
|
|
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, Stream input)
|
|
{
|
|
return DeserializeECFieldElement(fieldSize, TlsUtilities.ReadOpaque8(input));
|
|
}
|
|
|
|
public static BigInteger ReadECParameter(Stream input)
|
|
{
|
|
// TODO Are leading zeroes okay here?
|
|
return new BigInteger(1, TlsUtilities.ReadOpaque8(input));
|
|
}
|
|
|
|
public static ECDomainParameters ReadECParameters(int[] namedCurves, byte[] ecPointFormats, Stream input)
|
|
{
|
|
try
|
|
{
|
|
byte curveType = TlsUtilities.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 = TlsUtilities.ReadOpaque8(input);
|
|
BigInteger order = ReadECParameter(input);
|
|
BigInteger cofactor = ReadECParameter(input);
|
|
ECCurve curve = new FpCurve(prime_p, a, b, order, cofactor);
|
|
ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding);
|
|
return new ECDomainParameters(curve, basePoint, order, cofactor);
|
|
}
|
|
case ECCurveType.explicit_char2:
|
|
{
|
|
CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves);
|
|
|
|
int m = TlsUtilities.ReadUint16(input);
|
|
byte basis = TlsUtilities.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 = TlsUtilities.ReadOpaque8(input);
|
|
BigInteger order = ReadECParameter(input);
|
|
BigInteger cofactor = ReadECParameter(input);
|
|
|
|
ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial)
|
|
? new F2mCurve(m, k1, k2, k3, a, b, order, cofactor)
|
|
: new F2mCurve(m, k1, a, b, order, cofactor);
|
|
|
|
ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding);
|
|
|
|
return new ECDomainParameters(curve, basePoint, order, cofactor);
|
|
}
|
|
case ECCurveType.named_curve:
|
|
{
|
|
int namedCurve = TlsUtilities.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 (Exception e)
|
|
{
|
|
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
|
|
}
|
|
}
|
|
|
|
private static void CheckNamedCurve(int[] namedCurves, int namedCurve)
|
|
{
|
|
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, Stream output)
|
|
{
|
|
BigInteger K = BigInteger.ValueOf(k);
|
|
WriteECParameter(K, output);
|
|
}
|
|
|
|
public static void WriteECFieldElement(ECFieldElement x, Stream output)
|
|
{
|
|
TlsUtilities.WriteOpaque8(x.GetEncoded(), output);
|
|
}
|
|
|
|
public static void WriteECFieldElement(int fieldSize, BigInteger x, Stream output)
|
|
{
|
|
TlsUtilities.WriteOpaque8(SerializeECFieldElement(fieldSize, x), output);
|
|
}
|
|
|
|
public static void WriteECParameter(BigInteger x, Stream output)
|
|
{
|
|
TlsUtilities.WriteOpaque8(BigIntegers.AsUnsignedByteArray(x), output);
|
|
}
|
|
|
|
public static void WriteExplicitECParameters(byte[] ecPointFormats, ECDomainParameters ecParameters,
|
|
Stream output)
|
|
{
|
|
ECCurve curve = ecParameters.Curve;
|
|
|
|
if (ECAlgorithms.IsFpCurve(curve))
|
|
{
|
|
TlsUtilities.WriteUint8(ECCurveType.explicit_prime, output);
|
|
|
|
WriteECParameter(curve.Field.Characteristic, output);
|
|
}
|
|
else if (ECAlgorithms.IsF2mCurve(curve))
|
|
{
|
|
IPolynomialExtensionField field = (IPolynomialExtensionField)curve.Field;
|
|
int[] exponents = field.MinimalPolynomial.GetExponentsPresent();
|
|
|
|
TlsUtilities.WriteUint8(ECCurveType.explicit_char2, output);
|
|
|
|
int m = exponents[exponents.Length - 1];
|
|
TlsUtilities.CheckUint16(m);
|
|
TlsUtilities.WriteUint16(m, output);
|
|
|
|
if (exponents.Length == 3)
|
|
{
|
|
TlsUtilities.WriteUint8(ECBasisType.ec_basis_trinomial, output);
|
|
WriteECExponent(exponents[1], output);
|
|
}
|
|
else if (exponents.Length == 5)
|
|
{
|
|
TlsUtilities.WriteUint8(ECBasisType.ec_basis_pentanomial, output);
|
|
WriteECExponent(exponents[1], output);
|
|
WriteECExponent(exponents[2], output);
|
|
WriteECExponent(exponents[3], output);
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("Only trinomial and pentomial curves are supported");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException("'ecParameters' not a known curve type");
|
|
}
|
|
|
|
WriteECFieldElement(curve.A, output);
|
|
WriteECFieldElement(curve.B, output);
|
|
TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, ecParameters.G), output);
|
|
WriteECParameter(ecParameters.N, output);
|
|
WriteECParameter(ecParameters.H, output);
|
|
}
|
|
|
|
public static void WriteECPoint(byte[] ecPointFormats, ECPoint point, Stream output)
|
|
{
|
|
TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, point), output);
|
|
}
|
|
|
|
public static void WriteNamedECParameters(int namedCurve, Stream output)
|
|
{
|
|
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);
|
|
}
|
|
|
|
TlsUtilities.WriteUint8(ECCurveType.named_curve, output);
|
|
TlsUtilities.CheckUint16(namedCurve);
|
|
TlsUtilities.WriteUint16(namedCurve, output);
|
|
}
|
|
}
|
|
}
|
|
#pragma warning restore
|
|
#endif
|