using System; using System.Collections; using System.IO; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Agreement; using Org.BouncyCastle.Crypto.EC; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Math.Field; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls; public abstract class TlsEccUtilities { private static readonly string[] CurveNames = new string[28] { "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[10] = CreateSupportedEllipticCurvesExtension(namedCurves); } public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats) { extensions[11] = CreateSupportedPointFormatsExtension(ecPointFormats); } public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions) { byte[] extensionData = TlsUtilities.GetExtensionData(extensions, 10); if (extensionData != null) { return ReadSupportedEllipticCurvesExtension(extensionData); } return null; } public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions) { byte[] extensionData = TlsUtilities.GetExtensionData(extensions, 11); if (extensionData != null) { return ReadSupportedPointFormatsExtension(extensionData); } return null; } public static byte[] CreateSupportedEllipticCurvesExtension(int[] namedCurves) { if (namedCurves == null || namedCurves.Length < 1) { throw new TlsFatalAlert(80); } return TlsUtilities.EncodeUint16ArrayWithUint16Length(namedCurves); } public static byte[] CreateSupportedPointFormatsExtension(byte[] ecPointFormats) { if (ecPointFormats == null || !Arrays.Contains(ecPointFormats, 0)) { ecPointFormats = Arrays.Append(ecPointFormats, 0); } return TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats); } public static int[] ReadSupportedEllipticCurvesExtension(byte[] extensionData) { if (extensionData == null) { throw new ArgumentNullException("extensionData"); } MemoryStream memoryStream = new MemoryStream(extensionData, writable: false); int num = TlsUtilities.ReadUint16(memoryStream); if (num < 2 || (num & 1) != 0) { throw new TlsFatalAlert(50); } int[] result = TlsUtilities.ReadUint16Array(num / 2, memoryStream); TlsProtocol.AssertEmpty(memoryStream); return result; } public static byte[] ReadSupportedPointFormatsExtension(byte[] extensionData) { byte[] array = TlsUtilities.DecodeUint8ArrayWithUint8Length(extensionData); if (!Arrays.Contains(array, 0)) { throw new TlsFatalAlert(47); } return array; } public static string GetNameOfNamedCurve(int namedCurve) { if (!IsSupportedNamedCurve(namedCurve)) { return null; } return CurveNames[namedCurve - 1]; } public static ECDomainParameters GetParametersForNamedCurve(int namedCurve) { string nameOfNamedCurve = GetNameOfNamedCurve(namedCurve); if (nameOfNamedCurve == null) { return null; } X9ECParameters byName = CustomNamedCurves.GetByName(nameOfNamedCurve); if (byName == null) { byName = ECNamedCurveTable.GetByName(nameOfNamedCurve); if (byName == null) { return null; } } return new ECDomainParameters(byName.Curve, byName.G, byName.N, byName.H, byName.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) { case 49153: case 49154: case 49155: case 49156: case 49157: case 49158: case 49159: case 49160: case 49161: case 49162: case 49163: case 49164: case 49165: case 49166: case 49167: case 49168: case 49169: case 49170: case 49171: case 49172: case 49173: case 49174: case 49175: case 49176: case 49177: case 49187: case 49188: case 49189: case 49190: case 49191: case 49192: case 49193: case 49194: case 49195: case 49196: case 49197: case 49198: case 49199: case 49200: case 49201: case 49202: case 49203: case 49204: case 49205: case 49206: case 49207: case 49208: case 49209: case 49210: case 49211: case 49266: case 49267: case 49268: case 49269: case 49270: case 49271: case 49272: case 49273: case 49286: case 49287: case 49288: case 49289: case 49290: case 49291: case 49292: case 49293: case 49306: case 49307: case 49324: case 49325: case 49326: case 49327: case 52392: case 52393: case 52396: case 65282: case 65283: case 65284: case 65285: case 65300: case 65301: return true; default: return false; } } public static bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b) { return a?.Equals(b) ?? false; } public static bool IsSupportedNamedCurve(int namedCurve) { if (namedCurve > 0) { return namedCurve <= CurveNames.Length; } return false; } public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat) { if (ecPointFormats == null) { return false; } foreach (byte b in ecPointFormats) { if (b == 0) { return false; } if (b == 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; bool compressed = false; if (ECAlgorithms.IsFpCurve(curve)) { compressed = IsCompressionPreferred(ecPointFormats, 1); } else if (ECAlgorithms.IsF2mCurve(curve)) { compressed = IsCompressionPreferred(ecPointFormats, 2); } 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 num = (fieldSize + 7) / 8; if (encoding.Length != num) { throw new TlsFatalAlert(50); } return new BigInteger(1, encoding); } public static ECPoint DeserializeECPoint(byte[] ecPointFormats, ECCurve curve, byte[] encoding) { if (encoding == null || encoding.Length < 1) { throw new TlsFatalAlert(47); } byte b; switch (encoding[0]) { case 2: case 3: if (ECAlgorithms.IsF2mCurve(curve)) { b = 2; break; } if (ECAlgorithms.IsFpCurve(curve)) { b = 1; break; } throw new TlsFatalAlert(47); case 4: b = 0; break; default: throw new TlsFatalAlert(47); } if (b != 0 && (ecPointFormats == null || !Arrays.Contains(ecPointFormats, b))) { throw new TlsFatalAlert(47); } return curve.DecodePoint(encoding); } public static ECPublicKeyParameters DeserializeECPublicKey(byte[] ecPointFormats, ECDomainParameters curve_params, byte[] encoding) { try { ECPoint q = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding); return new ECPublicKeyParameters(q, curve_params); } catch (Exception alertCause) { throw new TlsFatalAlert(47, alertCause); } } public static byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) { ECDHBasicAgreement eCDHBasicAgreement = new ECDHBasicAgreement(); eCDHBasicAgreement.Init(privateKey); BigInteger n = eCDHBasicAgreement.CalculateAgreement(publicKey); return BigIntegers.AsUnsignedByteArray(eCDHBasicAgreement.GetFieldSize(), n); } public static AsymmetricCipherKeyPair GenerateECKeyPair(SecureRandom random, ECDomainParameters ecParams) { ECKeyPairGenerator eCKeyPairGenerator = new ECKeyPairGenerator(); eCKeyPairGenerator.Init(new ECKeyGenerationParameters(ecParams, random)); return eCKeyPairGenerator.GenerateKeyPair(); } public static ECPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, byte[] ecPointFormats, ECDomainParameters ecParams, Stream output) { AsymmetricCipherKeyPair asymmetricCipherKeyPair = GenerateECKeyPair(random, ecParams); ECPublicKeyParameters eCPublicKeyParameters = (ECPublicKeyParameters)asymmetricCipherKeyPair.Public; WriteECPoint(ecPointFormats, eCPublicKeyParameters.Q, output); return (ECPrivateKeyParameters)asymmetricCipherKeyPair.Private; } internal static ECPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves, byte[] ecPointFormats, Stream output) { int num = -1; if (namedCurves == null) { num = 23; } else { foreach (int num2 in namedCurves) { if (NamedCurve.IsValid(num2) && IsSupportedNamedCurve(num2)) { num = num2; break; } } } ECDomainParameters eCDomainParameters = null; if (num >= 0) { eCDomainParameters = GetParametersForNamedCurve(num); } else if (Arrays.Contains(namedCurves, 65281)) { eCDomainParameters = GetParametersForNamedCurve(23); } else if (Arrays.Contains(namedCurves, 65282)) { eCDomainParameters = GetParametersForNamedCurve(10); } if (eCDomainParameters == null) { throw new TlsFatalAlert(80); } if (num < 0) { WriteExplicitECParameters(ecPointFormats, eCDomainParameters, output); } else { WriteNamedECParameters(num, output); } return GenerateEphemeralClientKeyExchange(random, ecPointFormats, eCDomainParameters, output); } public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key) { return key; } public static int ReadECExponent(int fieldSize, Stream input) { BigInteger bigInteger = ReadECParameter(input); if (bigInteger.BitLength < 32) { int intValue = bigInteger.IntValue; if (intValue > 0 && intValue < fieldSize) { return intValue; } } throw new TlsFatalAlert(47); } public static BigInteger ReadECFieldElement(int fieldSize, Stream input) { return DeserializeECFieldElement(fieldSize, TlsUtilities.ReadOpaque8(input)); } public static BigInteger ReadECParameter(Stream input) { return new BigInteger(1, TlsUtilities.ReadOpaque8(input)); } public static ECDomainParameters ReadECParameters(int[] namedCurves, byte[] ecPointFormats, Stream input) { try { switch (TlsUtilities.ReadUint8(input)) { case 1: { CheckNamedCurve(namedCurves, 65281); BigInteger bigInteger3 = ReadECParameter(input); BigInteger a2 = ReadECFieldElement(bigInteger3.BitLength, input); BigInteger b3 = ReadECFieldElement(bigInteger3.BitLength, input); byte[] encoding2 = TlsUtilities.ReadOpaque8(input); BigInteger bigInteger4 = ReadECParameter(input); BigInteger bigInteger5 = ReadECParameter(input); ECCurve curve2 = new FpCurve(bigInteger3, a2, b3, bigInteger4, bigInteger5); ECPoint g2 = DeserializeECPoint(ecPointFormats, curve2, encoding2); return new ECDomainParameters(curve2, g2, bigInteger4, bigInteger5); } case 2: { CheckNamedCurve(namedCurves, 65282); int num = TlsUtilities.ReadUint16(input); byte b = TlsUtilities.ReadUint8(input); if (!ECBasisType.IsValid(b)) { throw new TlsFatalAlert(47); } int num2 = ReadECExponent(num, input); int k = -1; int k2 = -1; if (b == 2) { k = ReadECExponent(num, input); k2 = ReadECExponent(num, input); } BigInteger a = ReadECFieldElement(num, input); BigInteger b2 = ReadECFieldElement(num, input); byte[] encoding = TlsUtilities.ReadOpaque8(input); BigInteger bigInteger = ReadECParameter(input); BigInteger bigInteger2 = ReadECParameter(input); ECCurve curve = ((b == 2) ? new F2mCurve(num, num2, k, k2, a, b2, bigInteger, bigInteger2) : new F2mCurve(num, num2, a, b2, bigInteger, bigInteger2)); ECPoint g = DeserializeECPoint(ecPointFormats, curve, encoding); return new ECDomainParameters(curve, g, bigInteger, bigInteger2); } case 3: { int namedCurve = TlsUtilities.ReadUint16(input); if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve)) { throw new TlsFatalAlert(47); } CheckNamedCurve(namedCurves, namedCurve); return GetParametersForNamedCurve(namedCurve); } default: throw new TlsFatalAlert(47); } } catch (Exception alertCause) { throw new TlsFatalAlert(47, alertCause); } } private static void CheckNamedCurve(int[] namedCurves, int namedCurve) { if (namedCurves != null && !Arrays.Contains(namedCurves, namedCurve)) { throw new TlsFatalAlert(47); } } public static void WriteECExponent(int k, Stream output) { BigInteger x = BigInteger.ValueOf(k); WriteECParameter(x, 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(1, output); WriteECParameter(curve.Field.Characteristic, output); } else { if (!ECAlgorithms.IsF2mCurve(curve)) { throw new ArgumentException("'ecParameters' not a known curve type"); } IPolynomialExtensionField polynomialExtensionField = (IPolynomialExtensionField)curve.Field; int[] exponentsPresent = polynomialExtensionField.MinimalPolynomial.GetExponentsPresent(); TlsUtilities.WriteUint8(2, output); int i = exponentsPresent[^1]; TlsUtilities.CheckUint16(i); TlsUtilities.WriteUint16(i, output); if (exponentsPresent.Length == 3) { TlsUtilities.WriteUint8(1, output); WriteECExponent(exponentsPresent[1], output); } else { if (exponentsPresent.Length != 5) { throw new ArgumentException("Only trinomial and pentomial curves are supported"); } TlsUtilities.WriteUint8(2, output); WriteECExponent(exponentsPresent[1], output); WriteECExponent(exponentsPresent[2], output); WriteECExponent(exponentsPresent[3], output); } } 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)) { throw new TlsFatalAlert(80); } TlsUtilities.WriteUint8(3, output); TlsUtilities.CheckUint16(namedCurve); TlsUtilities.WriteUint16(namedCurve, output); } }