void AddServerNamedCurve(SSLCurve sc) { if (sc != null) { namedCurves[sc.Id] = sc; } }
/* * Test whether a given named curve is part of the "spontaneous" * named curves. */ bool IsSpontaneous(SSLCurve sc) { if (spontaneousNamedCurves == null) { return(false); } for (int i = 0; i < spontaneousNamedCurves.Length; i++) { if (sc.Id == spontaneousNamedCurves[i].Id) { return(true); } } return(false); }
/* * Run the tests and return the report. */ internal Report Run() { string sni = explicitSNI; if (sni == null) { sni = serverName; } else if (sni == "-") { sni = null; } /* * Accumulate time offsets between client and server. */ timeOffsets = new List <long>(); /* * To keep track of DHE/ECDHE parameters. */ minDHSize = 0; minECSize = 0; minECSizeExt = 0; namedCurves = new SortedDictionary <int, SSLCurve>(); curveExplicitPrime = 0; curveExplicitChar2 = 0; unknownSKE = false; doesRenego = false; doesEtM = false; /* * Overall process: * * 1. First, try SSL 2.0. This is a single connection. * After this test, everything else uses SSL 3.0+. * * 2. Try to confirm that we are talking to an actual * SSL/TLS server and obtain its tolerance to variants: * maximum client version, presence of extensions, * large ClientHello messages. * * 3. For each supported protocol version, find * accepted cipher suites, then work out server's * behaviour for suite selection (client order, server * order, other). * * 4. Print report for cipher suites. * * 5. Print other information (compression support, * certificate information, DHE/ECDHE details...). */ rp = new Report(); rp.ConnName = serverName; rp.ConnPort = serverPort; rp.SNI = sni; /* * SSL 2.0 attempt. */ if (minVersion <= M.SSLv20) { if (verbose) { Console.WriteLine("[trying version=SSLv2]"); } SSL2 v2 = DoConnectV2(); if (v2 != null) { if (verbose) { Console.WriteLine("[SSLv2 supported," + " {0} cipher suite(s)]", v2.CipherSuites.Length); } } } /* * Make the list of cipher suites we are interested in. */ csl = new List <int>(); if (allSuites) { for (int i = 1; i <= 0xFFFF; i++) { if (i == M.TLS_EMPTY_RENEGOTIATION_INFO_SCSV || i == M.TLS_FALLBACK_SCSV) { continue; } csl.Add(i); } } else { foreach (int s in CipherSuite.ALL.Keys) { if (s != 0) { csl.Add(s); } } } /* * Create a test builder and populate it with the * configured information. */ tb = new SSLTestBuilder(); tb.ServerName = sni; tb.MaxVersion = maxVersion; withExts = true; gotSSLAnswer = false; serverCompress = false; sslAlert = -1; maxRecordLen = 8192; if (addECExt) { List <int> rx = new List <int>(); foreach (int x in SSLCurve.ALL.Keys) { rx.Add(x); } tb.SupportedCurves = rx.ToArray(); } /* * Each try entails using a protocol version, a * maximum record length, and optional extensions. * We then try all chunks of cipher suites (in our * list of cipher suites to try) until we get a * successfull handshake. * * On error, we try reducing maximum record length. * If that still fails, we lower the maximum version. * If even SSL 3.0 fails with a small record, then * we try again the whole process without extensions. */ for (;;) { maxRecordLen = 8192; if (TryConnect() || gotReadTimeout) { break; } maxRecordLen = 1024; if (TryConnect() || gotReadTimeout) { break; } maxRecordLen = 256; if (TryConnect() || gotReadTimeout) { break; } int v = tb.MaxVersion; if (v > M.SSLv30) { tb.MaxVersion = v - 1; continue; } if (withExts) { withExts = false; tb.DisableExtensions(); tb.MaxVersion = maxVersion; continue; } /* * No success. */ if (gotSSLAnswer && sslAlert >= 0) { throw new SSLAlertException(sslAlert); } else { string msg = "Could not initiate a handshake" + " (not SSL/TLS?)"; if (gotReadTimeout) { msg += " [read timeout]"; } throw new Exception(msg); } } if (maxRecordLen < 8192) { rp.NeedsShortHello = true; } if (!withExts) { rp.NoExtensions = true; } maxVersion = tb.MaxVersion; int startVersion = minVersion; if (startVersion < M.SSLv30) { startVersion = M.SSLv30; } /* * Now extract supported cipher suites for each protocol * version. We also try to get the highest version for * which EC-based cipher suites are supported, and * extract all supported EC-based cipher suites for * that version. * * For each such protocol version, we also try connecting * with a ClientHello in V2 format; we do so while ensuring * that the total hello length is no more than 127 bytes, * for maximum interoperability. Note that the V2 format * has no room for any extension. */ int maxECVersion = -1; int[] suppEC = null; for (int v = startVersion; v <= maxVersion; v++) { tb.MaxVersion = v; SupportedCipherSuites scs = GetSupportedCipherSuites(); if (scs == null) { continue; } rp.SetCipherSuites(v, scs); int[] ecs = scs.GetKnownECSuites(); if (ecs.Length > 0) { maxECVersion = v; suppEC = ecs; } if (scs.KXReuseDH) { rp.KXReuseDH = true; } if (scs.KXReuseECDH) { rp.KXReuseECDH = true; } /* * Check V2 format for ClientHello. */ int savedRV = tb.RecordVersion; tb.RecordVersion = M.SSLv20; if (DoConnect() != null) { rp.SupportsV2Hello = true; } tb.RecordVersion = savedRV; } /* * At that point, if the server used an EC-based cipher * suite, and we did not present a Supported Elliptic * Curves extension, then the server selected the * curve(s) all by itself. If we always presented that * extension, then we want to try talking to the server * without it, to see if it accepts doing EC at all * without the extension, and, if yes, what curve it may * use in that case. */ int[] spontaneousEC; SSLCurve[] spontaneousNamedCurves; if (addECExt && withExts && maxECVersion >= 0) { if (verbose) { Console.WriteLine("[spontaneous EC support," + " version={0}, {1} suite(s)]", M.VersionString(maxECVersion), suppEC.Length); } IDictionary <int, SSLCurve> oldNamedCurves = namedCurves; namedCurves = new SortedDictionary <int, SSLCurve>(); tb.MaxVersion = maxECVersion; tb.SupportedCurves = null; spontaneousEC = GetSupportedCipherSuites(suppEC, null); spontaneousNamedCurves = M.ToValueArray(namedCurves); foreach (int s in namedCurves.Keys) { oldNamedCurves[s] = namedCurves[s]; } namedCurves = oldNamedCurves; if (verbose) { Console.WriteLine(); } } else { spontaneousEC = suppEC; spontaneousNamedCurves = M.ToValueArray(namedCurves); } /* * We now try to enumerate all supported EC curves. */ if (withExts && maxECVersion >= 0) { tb.MaxVersion = maxECVersion; tb.CipherSuites = suppEC; if (verbose) { Console.WriteLine("[elliptic curve enumeration," + " version={0}, {1} suite(s)]", M.VersionString(maxECVersion), suppEC.Length); } /* * Try named curves. */ IDictionary <int, int> rec = new SortedDictionary <int, int>(); foreach (int id in SSLCurve.ALL.Keys) { AddToSet(rec, id); } while (rec.Count > 0) { tb.SupportedCurves = SetToArray(rec); SSLTestResult tr = DoConnect(); if (tr == null) { break; } SSLCurve sc = tr.Curve; if (sc == null) { break; } if (!rec.ContainsKey(sc.Id)) { break; } rec.Remove(sc.Id); } /* * Try explicit curves, prime and char2. */ tb.SupportedCurves = new int[] { SSLCurve.EXPLICIT_PRIME }; DoConnect(); tb.SupportedCurves = new int[] { SSLCurve.EXPLICIT_CHAR2 }; DoConnect(); if (verbose) { Console.WriteLine(); } } rp.DeflateCompress = serverCompress; rp.ServerTimeOffset = GetServerTimeOffset(); rp.SupportsSecureRenegotiation = doesRenego; rp.SupportsEncryptThenMAC = doesEtM; rp.MinDHSize = minDHSize; rp.MinECSize = minECSize; rp.MinECSizeExt = minECSizeExt; rp.NamedCurves = M.ToValueArray(namedCurves); rp.SpontaneousEC = spontaneousEC; rp.SpontaneousNamedCurves = spontaneousNamedCurves; rp.CurveExplicitPrime = curveExplicitPrime; rp.CurveExplicitChar2 = curveExplicitChar2; rp.UnknownSKE = unknownSKE; return(rp); }
void ParseServerKeyExchangeInner(HMParser hm) { CipherSuite cs; if (!CipherSuite.ALL.TryGetValue(selectedCipherSuite, out cs)) { unknownSKE = true; hm.Close(true); return; } if (cs.IsDHE) { /* * If this is DHE_PSK, then there is first a * "key hint" to skip. */ if (cs.IsPSK) { hm.ReadBlobVar(2); } /* * DH parameters: p, g, y. We are only interested * in p. */ byte[] p = hm.ReadBlobVar(2); dhSize = M.BitLength(p); hm.ReadBlobVar(2); hm.ReadBlobVar(2); if (cs.ServerKeyType != "none") { if (version >= M.TLSv12) { /* * Hash-and-sign identifiers. */ hm.Read2(); } hm.ReadBlobVar(2); } } else if (cs.IsECDHE) { /* * If this is ECDHE_PSK, then there is first a * "key hint" to skip. */ if (cs.IsPSK) { hm.ReadBlobVar(2); } /* * Read curve type: one byte. */ switch (hm.Read1()) { case 1: /* * explicit_prime: p, a, b, G, * order, cofactor. */ hm.ReadBlobVar(1); hm.ReadBlobVar(1); hm.ReadBlobVar(1); hm.ReadBlobVar(1); ecSize = M.AdjustedBitLength(hm.ReadBlobVar(1)); hm.ReadBlobVar(1); curveExplicitPrime = true; break; case 2: /* explicit_char2 */ hm.Read2(); switch (hm.Read1()) { case 1: /* trinomial */ hm.ReadBlobVar(1); break; case 2: /* pentanomial */ hm.ReadBlobVar(1); hm.ReadBlobVar(1); hm.ReadBlobVar(1); break; default: hm.Close(true); unknownSKE = true; return; } hm.ReadBlobVar(1); hm.ReadBlobVar(1); hm.ReadBlobVar(1); ecSize = M.AdjustedBitLength(hm.ReadBlobVar(1)); hm.ReadBlobVar(1); curveExplicitChar2 = true; break; case 3: /* * named_curve. */ int id = hm.Read2(); if (SSLCurve.ALL.TryGetValue(id, out curve)) { ecSize = curve.Size; } else { curve = null; hm.Close(true); unknownSKE = true; return; } break; default: hm.Close(true); unknownSKE = true; return; } /* * Read public key: one curve point. */ hm.ReadBlobVar(1); if (cs.ServerKeyType != "none") { if (version >= M.TLSv12) { /* * Hash-and-sign identifiers. */ hm.Read2(); } hm.ReadBlobVar(2); } } else if (cs.IsRSAExport) { /* * If cipher suite uses RSA key exchange and is * flagged "export" then it may send an ephemeral * RSA key pair, which will be weak and probably * not very ephemeral, since RSA key pair generation * is kinda expensive. * * Format: modulus, public exponent, signature. */ hm.ReadBlobVar(2); hm.ReadBlobVar(2); if (version >= M.TLSv12) { /* * Hash-and-sign identifiers. */ hm.Read2(); } hm.ReadBlobVar(2); } else if (cs.IsSRP) { /* * SRP parameters are: N, g, s, B. N is the * modulus. */ dhSize = M.BitLength(hm.ReadBlobVar(2)); hm.ReadBlobVar(2); hm.ReadBlobVar(1); hm.ReadBlobVar(2); /* * RFC 5054 says that there is a signature, * except if the server sent no certificate. What * happens at the encoding level is unclear, so * we skip the remaining bytes. */ hm.SkipRemainder(); } else if (cs.IsPSK) { /* * Key hint from the server. */ hm.ReadBlobVar(2); } else { throw new IOException("Unexpected ServerKeyExchange"); } hm.Close(); }
static void Add(int id, string name, int size) { ALL[id] = new SSLCurve(id, name, size); }