TlsEccUtilities.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using System.IO;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X9;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Agreement;
  9. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.EC;
  10. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators;
  11. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  12. using BestHTTP.SecureProtocol.Org.BouncyCastle.Math;
  13. using BestHTTP.SecureProtocol.Org.BouncyCastle.Math.EC;
  14. using BestHTTP.SecureProtocol.Org.BouncyCastle.Math.Field;
  15. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
  16. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  17. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Tls
  18. {
  19. public abstract class TlsEccUtilities
  20. {
  21. private static readonly string[] CurveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
  22. "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
  23. "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
  24. "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
  25. "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
  26. public static void AddSupportedEllipticCurvesExtension(IDictionary extensions, int[] namedCurves)
  27. {
  28. extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves);
  29. }
  30. public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats)
  31. {
  32. extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats);
  33. }
  34. public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions)
  35. {
  36. byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves);
  37. return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData);
  38. }
  39. public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions)
  40. {
  41. byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats);
  42. return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData);
  43. }
  44. public static byte[] CreateSupportedEllipticCurvesExtension(int[] namedCurves)
  45. {
  46. if (namedCurves == null || namedCurves.Length < 1)
  47. throw new TlsFatalAlert(AlertDescription.internal_error);
  48. return TlsUtilities.EncodeUint16ArrayWithUint16Length(namedCurves);
  49. }
  50. public static byte[] CreateSupportedPointFormatsExtension(byte[] ecPointFormats)
  51. {
  52. if (ecPointFormats == null || !Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed))
  53. {
  54. /*
  55. * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
  56. * contain the value 0 (uncompressed) as one of the items in the list of point formats.
  57. */
  58. // NOTE: We add it at the end (lowest preference)
  59. ecPointFormats = Arrays.Append(ecPointFormats, ECPointFormat.uncompressed);
  60. }
  61. return TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats);
  62. }
  63. public static int[] ReadSupportedEllipticCurvesExtension(byte[] extensionData)
  64. {
  65. if (extensionData == null)
  66. throw new ArgumentNullException("extensionData");
  67. MemoryStream buf = new MemoryStream(extensionData, false);
  68. int length = TlsUtilities.ReadUint16(buf);
  69. if (length < 2 || (length & 1) != 0)
  70. throw new TlsFatalAlert(AlertDescription.decode_error);
  71. int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf);
  72. TlsProtocol.AssertEmpty(buf);
  73. return namedCurves;
  74. }
  75. public static byte[] ReadSupportedPointFormatsExtension(byte[] extensionData)
  76. {
  77. byte[] ecPointFormats = TlsUtilities.DecodeUint8ArrayWithUint8Length(extensionData);
  78. if (!Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed))
  79. {
  80. /*
  81. * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
  82. * contain the value 0 (uncompressed) as one of the items in the list of point formats.
  83. */
  84. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  85. }
  86. return ecPointFormats;
  87. }
  88. public static string GetNameOfNamedCurve(int namedCurve)
  89. {
  90. return IsSupportedNamedCurve(namedCurve) ? CurveNames[namedCurve - 1] : null;
  91. }
  92. public static ECDomainParameters GetParametersForNamedCurve(int namedCurve)
  93. {
  94. string curveName = GetNameOfNamedCurve(namedCurve);
  95. if (curveName == null)
  96. return null;
  97. // Parameters are lazily created the first time a particular curve is accessed
  98. X9ECParameters ecP = CustomNamedCurves.GetByName(curveName);
  99. if (ecP == null)
  100. {
  101. ecP = ECNamedCurveTable.GetByName(curveName);
  102. if (ecP == null)
  103. return null;
  104. }
  105. // It's a bit inefficient to do this conversion every time
  106. return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
  107. }
  108. public static bool HasAnySupportedNamedCurves()
  109. {
  110. return CurveNames.Length > 0;
  111. }
  112. public static bool ContainsEccCipherSuites(int[] cipherSuites)
  113. {
  114. for (int i = 0; i < cipherSuites.Length; ++i)
  115. {
  116. if (IsEccCipherSuite(cipherSuites[i]))
  117. return true;
  118. }
  119. return false;
  120. }
  121. public static bool IsEccCipherSuite(int cipherSuite)
  122. {
  123. switch (cipherSuite)
  124. {
  125. /*
  126. * RFC 4492
  127. */
  128. case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
  129. case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
  130. case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
  131. case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
  132. case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
  133. case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
  134. case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
  135. case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
  136. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
  137. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
  138. case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
  139. case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
  140. case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
  141. case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
  142. case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
  143. case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
  144. case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
  145. case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
  146. case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
  147. case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
  148. case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA:
  149. case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA:
  150. case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
  151. case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
  152. case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
  153. /*
  154. * RFC 5289
  155. */
  156. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
  157. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
  158. case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
  159. case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
  160. case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
  161. case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
  162. case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
  163. case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
  164. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
  165. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
  166. case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
  167. case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
  168. case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
  169. case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
  170. case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
  171. case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
  172. /*
  173. * RFC 5489
  174. */
  175. case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
  176. case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
  177. case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
  178. case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
  179. case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
  180. case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
  181. case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
  182. case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
  183. case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
  184. /*
  185. * RFC 6367
  186. */
  187. case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
  188. case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
  189. case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
  190. case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
  191. case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
  192. case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
  193. case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
  194. case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
  195. case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
  196. case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
  197. case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
  198. case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
  199. case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
  200. case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
  201. case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
  202. case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
  203. case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
  204. case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
  205. /*
  206. * RFC 7251
  207. */
  208. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
  209. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
  210. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
  211. case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
  212. /*
  213. * draft-ietf-tls-chacha20-poly1305-04
  214. */
  215. case CipherSuite.DRAFT_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
  216. case CipherSuite.DRAFT_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256:
  217. case CipherSuite.DRAFT_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
  218. /*
  219. * draft-zauner-tls-aes-ocb-04
  220. */
  221. case CipherSuite.DRAFT_TLS_ECDHE_RSA_WITH_AES_128_OCB:
  222. case CipherSuite.DRAFT_TLS_ECDHE_RSA_WITH_AES_256_OCB:
  223. case CipherSuite.DRAFT_TLS_ECDHE_ECDSA_WITH_AES_128_OCB:
  224. case CipherSuite.DRAFT_TLS_ECDHE_ECDSA_WITH_AES_256_OCB:
  225. case CipherSuite.DRAFT_TLS_ECDHE_PSK_WITH_AES_128_OCB:
  226. case CipherSuite.DRAFT_TLS_ECDHE_PSK_WITH_AES_256_OCB:
  227. return true;
  228. default:
  229. return false;
  230. }
  231. }
  232. public static bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b)
  233. {
  234. return a != null && a.Equals(b);
  235. }
  236. public static bool IsSupportedNamedCurve(int namedCurve)
  237. {
  238. return (namedCurve > 0 && namedCurve <= CurveNames.Length);
  239. }
  240. public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat)
  241. {
  242. if (ecPointFormats == null)
  243. return false;
  244. for (int i = 0; i < ecPointFormats.Length; ++i)
  245. {
  246. byte ecPointFormat = ecPointFormats[i];
  247. if (ecPointFormat == ECPointFormat.uncompressed)
  248. return false;
  249. if (ecPointFormat == compressionFormat)
  250. return true;
  251. }
  252. return false;
  253. }
  254. public static byte[] SerializeECFieldElement(int fieldSize, BigInteger x)
  255. {
  256. return BigIntegers.AsUnsignedByteArray((fieldSize + 7) / 8, x);
  257. }
  258. public static byte[] SerializeECPoint(byte[] ecPointFormats, ECPoint point)
  259. {
  260. ECCurve curve = point.Curve;
  261. /*
  262. * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format. Here, the
  263. * format MUST conform to what the server has requested through a Supported Point Formats
  264. * Extension if this extension was used, and MUST be uncompressed if this extension was not
  265. * used.
  266. */
  267. bool compressed = false;
  268. if (ECAlgorithms.IsFpCurve(curve))
  269. {
  270. compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime);
  271. }
  272. else if (ECAlgorithms.IsF2mCurve(curve))
  273. {
  274. compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2);
  275. }
  276. return point.GetEncoded(compressed);
  277. }
  278. public static byte[] SerializeECPublicKey(byte[] ecPointFormats, ECPublicKeyParameters keyParameters)
  279. {
  280. return SerializeECPoint(ecPointFormats, keyParameters.Q);
  281. }
  282. public static BigInteger DeserializeECFieldElement(int fieldSize, byte[] encoding)
  283. {
  284. int requiredLength = (fieldSize + 7) / 8;
  285. if (encoding.Length != requiredLength)
  286. throw new TlsFatalAlert(AlertDescription.decode_error);
  287. return new BigInteger(1, encoding);
  288. }
  289. public static ECPoint DeserializeECPoint(byte[] ecPointFormats, ECCurve curve, byte[] encoding)
  290. {
  291. if (encoding == null || encoding.Length < 1)
  292. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  293. byte actualFormat;
  294. switch (encoding[0])
  295. {
  296. case 0x02: // compressed
  297. case 0x03: // compressed
  298. {
  299. if (ECAlgorithms.IsF2mCurve(curve))
  300. {
  301. actualFormat = ECPointFormat.ansiX962_compressed_char2;
  302. }
  303. else if (ECAlgorithms.IsFpCurve(curve))
  304. {
  305. actualFormat = ECPointFormat.ansiX962_compressed_prime;
  306. }
  307. else
  308. {
  309. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  310. }
  311. break;
  312. }
  313. case 0x04: // uncompressed
  314. {
  315. actualFormat = ECPointFormat.uncompressed;
  316. break;
  317. }
  318. case 0x00: // infinity
  319. case 0x06: // hybrid
  320. case 0x07: // hybrid
  321. default:
  322. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  323. }
  324. if (actualFormat != ECPointFormat.uncompressed
  325. && (ecPointFormats == null || !Arrays.Contains(ecPointFormats, actualFormat)))
  326. {
  327. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  328. }
  329. return curve.DecodePoint(encoding);
  330. }
  331. public static ECPublicKeyParameters DeserializeECPublicKey(byte[] ecPointFormats, ECDomainParameters curve_params,
  332. byte[] encoding)
  333. {
  334. try
  335. {
  336. ECPoint Y = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding);
  337. return new ECPublicKeyParameters(Y, curve_params);
  338. }
  339. catch (Exception e)
  340. {
  341. throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
  342. }
  343. }
  344. public static byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey)
  345. {
  346. ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
  347. basicAgreement.Init(privateKey);
  348. BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey);
  349. /*
  350. * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by
  351. * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for
  352. * any given field; leading zeros found in this octet string MUST NOT be truncated.
  353. */
  354. return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue);
  355. }
  356. public static AsymmetricCipherKeyPair GenerateECKeyPair(SecureRandom random, ECDomainParameters ecParams)
  357. {
  358. ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
  359. keyPairGenerator.Init(new ECKeyGenerationParameters(ecParams, random));
  360. return keyPairGenerator.GenerateKeyPair();
  361. }
  362. public static ECPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, byte[] ecPointFormats,
  363. ECDomainParameters ecParams, Stream output)
  364. {
  365. AsymmetricCipherKeyPair kp = GenerateECKeyPair(random, ecParams);
  366. ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public;
  367. WriteECPoint(ecPointFormats, ecPublicKey.Q, output);
  368. return (ECPrivateKeyParameters)kp.Private;
  369. }
  370. // TODO Refactor around ServerECDHParams before making this public
  371. internal static ECPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, int[] namedCurves,
  372. byte[] ecPointFormats, Stream output)
  373. {
  374. /* First we try to find a supported named curve from the client's list. */
  375. int namedCurve = -1;
  376. if (namedCurves == null)
  377. {
  378. // TODO Let the peer choose the default named curve
  379. namedCurve = NamedCurve.secp256r1;
  380. }
  381. else
  382. {
  383. for (int i = 0; i < namedCurves.Length; ++i)
  384. {
  385. int entry = namedCurves[i];
  386. if (NamedCurve.IsValid(entry) && IsSupportedNamedCurve(entry))
  387. {
  388. namedCurve = entry;
  389. break;
  390. }
  391. }
  392. }
  393. ECDomainParameters ecParams = null;
  394. if (namedCurve >= 0)
  395. {
  396. ecParams = GetParametersForNamedCurve(namedCurve);
  397. }
  398. else
  399. {
  400. /* If no named curves are suitable, check if the client supports explicit curves. */
  401. if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
  402. {
  403. ecParams = GetParametersForNamedCurve(NamedCurve.secp256r1);
  404. }
  405. else if (Arrays.Contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
  406. {
  407. ecParams = GetParametersForNamedCurve(NamedCurve.sect283r1);
  408. }
  409. }
  410. if (ecParams == null)
  411. {
  412. /*
  413. * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
  414. * a suitable curve.
  415. */
  416. throw new TlsFatalAlert(AlertDescription.internal_error);
  417. }
  418. if (namedCurve < 0)
  419. {
  420. WriteExplicitECParameters(ecPointFormats, ecParams, output);
  421. }
  422. else
  423. {
  424. WriteNamedECParameters(namedCurve, output);
  425. }
  426. return GenerateEphemeralClientKeyExchange(random, ecPointFormats, ecParams, output);
  427. }
  428. public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key)
  429. {
  430. // TODO Check RFC 4492 for validation
  431. return key;
  432. }
  433. public static int ReadECExponent(int fieldSize, Stream input)
  434. {
  435. BigInteger K = ReadECParameter(input);
  436. if (K.BitLength < 32)
  437. {
  438. int k = K.IntValue;
  439. if (k > 0 && k < fieldSize)
  440. {
  441. return k;
  442. }
  443. }
  444. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  445. }
  446. public static BigInteger ReadECFieldElement(int fieldSize, Stream input)
  447. {
  448. return DeserializeECFieldElement(fieldSize, TlsUtilities.ReadOpaque8(input));
  449. }
  450. public static BigInteger ReadECParameter(Stream input)
  451. {
  452. // TODO Are leading zeroes okay here?
  453. return new BigInteger(1, TlsUtilities.ReadOpaque8(input));
  454. }
  455. public static ECDomainParameters ReadECParameters(int[] namedCurves, byte[] ecPointFormats, Stream input)
  456. {
  457. try
  458. {
  459. byte curveType = TlsUtilities.ReadUint8(input);
  460. switch (curveType)
  461. {
  462. case ECCurveType.explicit_prime:
  463. {
  464. CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves);
  465. BigInteger prime_p = ReadECParameter(input);
  466. BigInteger a = ReadECFieldElement(prime_p.BitLength, input);
  467. BigInteger b = ReadECFieldElement(prime_p.BitLength, input);
  468. byte[] baseEncoding = TlsUtilities.ReadOpaque8(input);
  469. BigInteger order = ReadECParameter(input);
  470. BigInteger cofactor = ReadECParameter(input);
  471. ECCurve curve = new FpCurve(prime_p, a, b, order, cofactor);
  472. ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding);
  473. return new ECDomainParameters(curve, basePoint, order, cofactor);
  474. }
  475. case ECCurveType.explicit_char2:
  476. {
  477. CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves);
  478. int m = TlsUtilities.ReadUint16(input);
  479. byte basis = TlsUtilities.ReadUint8(input);
  480. if (!ECBasisType.IsValid(basis))
  481. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  482. int k1 = ReadECExponent(m, input), k2 = -1, k3 = -1;
  483. if (basis == ECBasisType.ec_basis_pentanomial)
  484. {
  485. k2 = ReadECExponent(m, input);
  486. k3 = ReadECExponent(m, input);
  487. }
  488. BigInteger a = ReadECFieldElement(m, input);
  489. BigInteger b = ReadECFieldElement(m, input);
  490. byte[] baseEncoding = TlsUtilities.ReadOpaque8(input);
  491. BigInteger order = ReadECParameter(input);
  492. BigInteger cofactor = ReadECParameter(input);
  493. ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial)
  494. ? new F2mCurve(m, k1, k2, k3, a, b, order, cofactor)
  495. : new F2mCurve(m, k1, a, b, order, cofactor);
  496. ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding);
  497. return new ECDomainParameters(curve, basePoint, order, cofactor);
  498. }
  499. case ECCurveType.named_curve:
  500. {
  501. int namedCurve = TlsUtilities.ReadUint16(input);
  502. if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve))
  503. {
  504. /*
  505. * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a
  506. * specific curve. Values of NamedCurve that indicate support for a class of
  507. * explicitly defined curves are not allowed here [...].
  508. */
  509. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  510. }
  511. CheckNamedCurve(namedCurves, namedCurve);
  512. return GetParametersForNamedCurve(namedCurve);
  513. }
  514. default:
  515. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  516. }
  517. }
  518. catch (Exception e)
  519. {
  520. throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
  521. }
  522. }
  523. private static void CheckNamedCurve(int[] namedCurves, int namedCurve)
  524. {
  525. if (namedCurves != null && !Arrays.Contains(namedCurves, namedCurve))
  526. {
  527. /*
  528. * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
  529. * unless they can complete the handshake while respecting the choice of curves
  530. * and compression techniques specified by the client.
  531. */
  532. throw new TlsFatalAlert(AlertDescription.illegal_parameter);
  533. }
  534. }
  535. public static void WriteECExponent(int k, Stream output)
  536. {
  537. BigInteger K = BigInteger.ValueOf(k);
  538. WriteECParameter(K, output);
  539. }
  540. public static void WriteECFieldElement(ECFieldElement x, Stream output)
  541. {
  542. TlsUtilities.WriteOpaque8(x.GetEncoded(), output);
  543. }
  544. public static void WriteECFieldElement(int fieldSize, BigInteger x, Stream output)
  545. {
  546. TlsUtilities.WriteOpaque8(SerializeECFieldElement(fieldSize, x), output);
  547. }
  548. public static void WriteECParameter(BigInteger x, Stream output)
  549. {
  550. TlsUtilities.WriteOpaque8(BigIntegers.AsUnsignedByteArray(x), output);
  551. }
  552. public static void WriteExplicitECParameters(byte[] ecPointFormats, ECDomainParameters ecParameters,
  553. Stream output)
  554. {
  555. ECCurve curve = ecParameters.Curve;
  556. if (ECAlgorithms.IsFpCurve(curve))
  557. {
  558. TlsUtilities.WriteUint8(ECCurveType.explicit_prime, output);
  559. WriteECParameter(curve.Field.Characteristic, output);
  560. }
  561. else if (ECAlgorithms.IsF2mCurve(curve))
  562. {
  563. IPolynomialExtensionField field = (IPolynomialExtensionField)curve.Field;
  564. int[] exponents = field.MinimalPolynomial.GetExponentsPresent();
  565. TlsUtilities.WriteUint8(ECCurveType.explicit_char2, output);
  566. int m = exponents[exponents.Length - 1];
  567. TlsUtilities.CheckUint16(m);
  568. TlsUtilities.WriteUint16(m, output);
  569. if (exponents.Length == 3)
  570. {
  571. TlsUtilities.WriteUint8(ECBasisType.ec_basis_trinomial, output);
  572. WriteECExponent(exponents[1], output);
  573. }
  574. else if (exponents.Length == 5)
  575. {
  576. TlsUtilities.WriteUint8(ECBasisType.ec_basis_pentanomial, output);
  577. WriteECExponent(exponents[1], output);
  578. WriteECExponent(exponents[2], output);
  579. WriteECExponent(exponents[3], output);
  580. }
  581. else
  582. {
  583. throw new ArgumentException("Only trinomial and pentomial curves are supported");
  584. }
  585. }
  586. else
  587. {
  588. throw new ArgumentException("'ecParameters' not a known curve type");
  589. }
  590. WriteECFieldElement(curve.A, output);
  591. WriteECFieldElement(curve.B, output);
  592. TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, ecParameters.G), output);
  593. WriteECParameter(ecParameters.N, output);
  594. WriteECParameter(ecParameters.H, output);
  595. }
  596. public static void WriteECPoint(byte[] ecPointFormats, ECPoint point, Stream output)
  597. {
  598. TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, point), output);
  599. }
  600. public static void WriteNamedECParameters(int namedCurve, Stream output)
  601. {
  602. if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve))
  603. {
  604. /*
  605. * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific
  606. * curve. Values of NamedCurve that indicate support for a class of explicitly defined
  607. * curves are not allowed here [...].
  608. */
  609. throw new TlsFatalAlert(AlertDescription.internal_error);
  610. }
  611. TlsUtilities.WriteUint8(ECCurveType.named_curve, output);
  612. TlsUtilities.CheckUint16(namedCurve);
  613. TlsUtilities.WriteUint16(namedCurve, output);
  614. }
  615. }
  616. }
  617. #pragma warning restore
  618. #endif