internal static bool Equals( SupportedCipherSuites scs1, SupportedCipherSuites scs2) { if (scs1 == scs2) { return(true); } if (scs1 == null || scs2 == null) { return(false); } return(scs1.Equals(scs2)); }
internal bool Equals(SupportedCipherSuites scs) { if (scs == null) { return(false); } if (prefClient != scs.prefClient || prefServer != scs.prefServer) { return(false); } return(M.Equals(suites, scs.suites)); }
public bool Equals(SupportedCipherSuites scs) { if (scs == null) { return(false); } if (prefClient != scs.prefClient || prefServer != scs.prefServer) { return(false); } if (kxReuseDH != scs.kxReuseDH || kxReuseECDH != scs.kxReuseECDH) { return(false); } return(M.Equals(suites, scs.suites)); }
/* * Encode the report as JSON. */ internal void Print(JSON js) { js.OpenInit(false); js.AddPair("connectionName", connName); js.AddPair("connectionPort", connPort); js.AddPair("SNI", sni); if (ssl2Suites != null && ssl2Suites.Length > 0) { js.OpenPairObject("SSLv2"); js.OpenPairArray("suites"); foreach (int s in ssl2Suites) { js.OpenElementObject(); js.AddPair("id", s); js.AddPair("name", CipherSuite.ToNameV2(s)); js.Close(); } js.Close(); js.Close(); } foreach (int v in suites.Keys) { js.OpenPairObject(M.VersionString(v)); SupportedCipherSuites scs = suites[v]; string sel; if (scs.PrefClient) { sel = "client"; } else if (scs.PrefServer) { sel = "server"; } else { sel = "complex"; } js.AddPair("suiteSelection", sel); js.OpenPairArray("suites"); foreach (int s in scs.Suites) { js.OpenElementObject(); js.AddPair("id", s); js.AddPair("name", CipherSuite.ToName(s)); CipherSuite cs; if (CipherSuite.ALL.TryGetValue(s, out cs)) { js.AddPair("strength", cs.Strength); js.AddPair("forwardSecrecy", cs.HasForwardSecrecy); js.AddPair("anonymous", cs.IsAnonymous); js.AddPair("serverKeyType", cs.ServerKeyType); } js.Close(); } js.Close(); js.Close(); } if (ssl2Chain != null) { js.OpenPairObject("ssl2Cert"); PrintCert(js, ssl2Chain, 0); js.Close(); } js.OpenPairArray("ssl3Chains"); foreach (X509Chain xchain in chains.Values) { js.OpenElementObject(); int n = xchain.Elements.Length; js.AddPair("length", n); js.AddPair("decoded", xchain.Decodable); if (xchain.Decodable) { js.AddPair("namesMatch", xchain.NamesMatch); js.AddPair("includesRoot", xchain.IncludesRoot); js.OpenPairArray("signHashes"); foreach (string name in xchain.SignHashes) { js.AddElement(name); } js.Close(); } js.OpenPairArray("certificates"); for (int i = 0; i < n; i++) { js.OpenElementObject(); PrintCert(js, xchain, i); js.Close(); } js.Close(); js.Close(); } js.Close(); js.AddPair("deflateCompress", DeflateCompress); if (serverTimeOffset == Int64.MinValue) { js.AddPair("serverTime", "none"); } else if (serverTimeOffset == Int64.MaxValue) { js.AddPair("serverTime", "random"); } else { DateTime dt = DateTime.UtcNow; dt = dt.AddMilliseconds((double)serverTimeOffset); js.AddPair("serverTime", string.Format( "{0:yyyy-MM-dd HH:mm:ss} UTC", dt)); js.AddPair("serverTimeOffsetMillis", serverTimeOffset); } js.AddPair("secureRenegotiation", doesRenego); js.AddPair("rfc7366EtM", doesEtM); js.AddPair("ssl2HelloFormat", helloV2); if (minDHSize > 0) { js.AddPair("minDHSize", minDHSize); js.AddPair("kxReuseDH", kxReuseDH); } if (minECSize > 0) { js.AddPair("minECSize", minECSize); } if (minECSizeExt > 0) { js.AddPair("minECSizeExt", minECSizeExt); } if (minECSize > 0 || minECSizeExt > 0) { js.AddPair("kxReuseECDH", kxReuseECDH); } if ((namedCurves != null && namedCurves.Length > 0) || curveExplicitPrime > 0 || curveExplicitChar2 > 0) { js.OpenPairArray("namedCurves"); foreach (SSLCurve nc in namedCurves) { js.OpenElementObject(); js.AddPair("name", nc.Name); js.AddPair("size", nc.Size); js.AddPair("spontaneous", IsSpontaneous(nc)); js.Close(); } if (curveExplicitPrime > 0) { js.OpenElementObject(); js.AddPair("name", "explicitPrime"); js.AddPair("size", curveExplicitPrime); js.Close(); } if (curveExplicitChar2 > 0) { js.OpenElementObject(); js.AddPair("name", "explicitChar2"); js.AddPair("size", curveExplicitChar2); js.Close(); } js.Close(); } if (warnings == null) { Analyse(); } js.OpenPairArray("warnings"); foreach (string k in warnings.Keys) { js.OpenElementObject(); js.AddPair("id", k); js.AddPair("text", warnings[k]); js.Close(); } js.Close(); js.Close(); }
/* * Print the report on the provided writer (text version for * humans). */ internal void Print(TextWriter w) { w.WriteLine("Connection: {0}:{1}", connName, connPort); if (sni == null) { w.WriteLine("No SNI sent"); } else { w.WriteLine("SNI: {0}", sni); } if (ssl2Suites != null && ssl2Suites.Length > 0) { w.WriteLine(" {0}", M.VersionString(M.SSLv20)); foreach (int s in ssl2Suites) { w.WriteLine(" {0}", CipherSuite.ToNameV2(s)); } } SupportedCipherSuites last = null; foreach (int v in suites.Keys) { w.Write(" {0}:", M.VersionString(v)); SupportedCipherSuites scs = suites[v]; if (scs.Equals(last)) { w.WriteLine(" idem"); continue; } last = scs; w.WriteLine(); w.Write(" server selection: "); if (scs.PrefClient) { w.WriteLine("uses client preferences"); } else if (scs.PrefServer) { w.WriteLine("enforce server preferences"); } else { w.WriteLine("complex"); } foreach (int s in scs.Suites) { CipherSuite cs; string strength; string fsf; string anon; string kt; if (CipherSuite.ALL.TryGetValue(s, out cs)) { strength = cs.Strength.ToString(); fsf = cs.HasForwardSecrecy ? "f" : "-"; anon = cs.IsAnonymous ? "A" : "-"; kt = cs.ServerKeyType; } else { strength = "?"; fsf = "?"; anon = "?"; kt = "?"; } w.WriteLine(" {0}{1}{2} (key: {3,4}) {4}", strength, fsf, anon, kt, CipherSuite.ToName(s)); } } w.WriteLine("========================================="); if (ssl2Chain != null) { w.WriteLine("+++++ SSLv2 certificate"); PrintCert(w, ssl2Chain, 0); } w.WriteLine("+++++ SSLv3/TLS: {0} certificate chain(s)", chains.Count); foreach (X509Chain xchain in chains.Values) { int n = xchain.Elements.Length; w.WriteLine("+++ chain: length={0}", n); if (xchain.Decodable) { w.WriteLine("names match: {0}", xchain.NamesMatch ? "yes" : "no"); w.WriteLine("includes root: {0}", xchain.IncludesRoot ? "yes" : "no"); w.Write("signature hash(es):"); foreach (string name in xchain.SignHashes) { w.Write(" {0}", name); } w.WriteLine(); } else if (n == 0) { w.WriteLine("CHAIN IS EMPTY"); } else { w.WriteLine("CHAIN PROCESSING ERROR"); } for (int i = 0; i < n; i++) { w.WriteLine("+ certificate order: {0}", i); PrintCert(w, xchain, i); } } w.WriteLine("========================================="); w.WriteLine("Server compression support: {0}", DeflateCompress ? "yes" : "no"); if (serverTimeOffset == Int64.MinValue) { w.WriteLine("Server does not send its system time."); } else if (serverTimeOffset == Int64.MaxValue) { w.WriteLine("Server sends a random system time."); } else { DateTime dt = DateTime.UtcNow; dt = dt.AddMilliseconds((double)serverTimeOffset); w.WriteLine("Server time: {0:yyyy-MM-dd HH:mm:ss} UTC" + " (offset: {1} ms)", dt, serverTimeOffset); } w.WriteLine("Secure renegotiation support: {0}", doesRenego ? "yes" : "no"); w.WriteLine("Encrypt-then-MAC support (RFC 7366): {0}", doesEtM ? "yes" : "no"); w.WriteLine("SSLv2 ClientHello format (for SSLv3+): {0}", helloV2 ? "yes" : "no"); if (minDHSize > 0) { w.WriteLine("Minimum DH size: {0}", minDHSize); w.WriteLine("DH parameter reuse: {0}", kxReuseDH ? "yes" : " no"); } if (minECSize > 0) { w.WriteLine("Minimum EC size (no extension): {0}", minECSize); } if (minECSizeExt > 0) { w.WriteLine("Minimum EC size (with extension): {0}", minECSizeExt); if (minECSize == 0) { w.WriteLine("Server does not use EC without" + " the client extension"); } } if (minECSize > 0 || minECSizeExt > 0) { w.WriteLine("ECDH parameter reuse: {0}", kxReuseECDH ? "yes" : " no"); } if (namedCurves != null && namedCurves.Length > 0) { w.WriteLine("Supported curves (size and name)" + " ('*' = selected by server):"); foreach (SSLCurve nc in namedCurves) { w.WriteLine(" {0} {1,3} {2}", IsSpontaneous(nc) ? "*" : " ", nc.Size, nc.Name); } if (curveExplicitPrime > 0) { w.WriteLine(" explicit prime, size = {0}", curveExplicitPrime); } if (curveExplicitChar2 > 0) { w.WriteLine(" explicit char2, size = {0}", curveExplicitChar2); } } w.WriteLine("========================================="); if (warnings == null) { Analyse(); } if (warnings.Count == 0) { w.WriteLine("No warning."); } else { foreach (string k in warnings.Keys) { w.WriteLine("WARN[{0}]: {1}", k, warnings[k]); } } }
/* * Analyse data and compute the list of relevant warnings. */ internal void Analyse() { warnings = new SortedDictionary <string, string>( StringComparer.Ordinal); if (ssl2Suites != null) { if (ssl2Suites.Length > 0) { warnings["PV002"] = "Server supports SSL 2.0."; } else { warnings["PV005"] = "Server claims to support" + " SSL 2.0, but with no cipher suite"; } } if (suites.ContainsKey(M.SSLv30)) { warnings["PV003"] = "Server supports SSL 3.0."; } if (unknownSKE) { warnings["SK001"] = "Some Server Key Exchange messages" + " could not be processed."; } if (minDHSize > 0 && minDHSize < 2048) { warnings["SK002"] = "Server uses DH parameters smaller" + " than 2048 bits."; } if (minECSize > 0 && minECSize < 192) { warnings["SK003"] = "Server chooses ECDH parameters" + " smaller than 192 bits."; } if (minECSizeExt > 0 && minECSizeExt < 192) { warnings["SK004"] = "Server supports ECDH parameters" + " smaller than 192 bits (if requested)."; } if (NeedsShortHello) { warnings["PV001"] = "Server needs short ClientHello."; } if (NoExtensions) { warnings["PV004"] = "Server does not tolerate extensions."; } if (DeflateCompress) { warnings["CP001"] = "Server supports compression."; } bool hasCS0 = false; bool hasCS1 = false; bool hasCS2 = false; bool hasCSX = false; bool hasRC4 = false; bool hasNoFS = false; foreach (int pv in suites.Keys) { SupportedCipherSuites scs = suites[pv]; foreach (int s in scs.Suites) { CipherSuite cs; if (CipherSuite.ALL.TryGetValue(s, out cs)) { switch (cs.Strength) { case 0: hasCS0 = true; break; case 1: hasCS1 = true; break; case 2: hasCS2 = true; break; } if (cs.IsRC4) { hasRC4 = true; } if (!cs.HasForwardSecrecy) { hasNoFS = true; } } else { hasCSX = true; } } } if (hasCS0) { warnings["CS001"] = "Server supports unencrypted cipher suites."; } if (hasCS1) { warnings["CS002"] = "Server supports very weak" + " cipher suites (40 bits)."; } if (hasCS2) { warnings["CS003"] = "Server supports weak" + " cipher suites (56 bits)."; } if (hasCSX) { warnings["CS004"] = "Server supports unrecognized" + " cipher suites (unknown strength)."; } if (hasRC4) { warnings["CS005"] = "Server supports RC4."; } if (hasNoFS) { warnings["CS006"] = "Server supports cipher suites" + " with no forward secrecy."; } if (!doesRenego) { warnings["RN001"] = "Server does not support" + " secure renegotiation."; } bool hasBadSignHash = false; foreach (X509Chain xchain in chains.Values) { string[] shs = xchain.SignHashes; if (shs == null) { continue; } foreach (string sh in shs) { switch (sh) { case "MD2": case "MD5": case "SHA-1": case "UNKNOWN": hasBadSignHash = true; break; } } } if (hasBadSignHash) { warnings["XC001"] = "Server certificate was signed with" + " a weak/deprecated/unknown hash function."; } }
/* * Set the cipher suites supported for a specific protocol version * (SSLv3+). */ internal void SetCipherSuites(int version, SupportedCipherSuites scs) { suites[version] = scs; }
/* * Get all supported cipher suites with the current configuration. * If there is none, then null is returned (which means that the * protocol version is not supported). */ SupportedCipherSuites GetSupportedCipherSuites() { int num = tb.ComputeMaxCipherSuites(maxRecordLen); if (num < 1) { num = 1; } if (verbose) { Console.WriteLine("[suites: version={0}" + " ({1} suites per hello)]", M.VersionString(tb.MaxVersion), num); } /* * We accumulate hashes of server key exchange parameters * in this map, for DHE/DH_anon and ECDHE/ECDH_anon. This * is used to detect duplicates, i.e. parameter reuse. The * dictionary maps the hash value to an integer whose * upper 16 bits are the cipher suite last associated with * these parameters, and the lower 16 bits are the number * of times these parameters occurred. */ IDictionary <string, uint> kxHashes = new SortedDictionary <string, uint>( StringComparer.Ordinal); /* * 1. Gather all cipher suites supported by the server. */ IDictionary <int, int> suppd = new SortedDictionary <int, int>(); for (int i = 0; i < csl.Count; i += num) { int k = Math.Min(num, csl.Count - i); int[] tt = new int[k]; for (int j = 0; j < k; j++) { tt[j] = csl[i + j]; } foreach (int s in GetSupportedCipherSuites(tt, kxHashes)) { AddToSet(suppd, s); } } int[] supp = SetToArray(suppd); if (supp.Length == 0) { if (verbose) { Console.WriteLine(); } return(null); } SupportedCipherSuites scs = new SupportedCipherSuites(supp); /* * 2. Work out server preferences. We can do that only * if we can send all supported suites in a single * ClientHello. * * Algorithm: we first try suites in numerical order. Then * we try suites in the reverse order of what the server * selected. If the second list is equal to the first one, * then the server enforces its own preferences. If the * second list is the reverse of the first one, then the * server follows client preferences. In all other cases, * the server selection algorithm is deemed "complex". */ if (supp.Length <= num) { int[] supp1 = GetSupportedCipherSuites( supp, kxHashes); int[] suppRev = new int[supp1.Length]; for (int i = 0; i < supp1.Length; i++) { suppRev[i] = supp[supp1.Length - 1 - i]; } int[] supp2 = GetSupportedCipherSuites( suppRev, kxHashes); if (M.Equals(supp2, suppRev)) { scs.PrefClient = true; } else if (M.Equals(supp1, supp2)) { scs.PrefServer = true; scs.Suites = supp1; } } /* * See if there was some parameter reuse. */ foreach (uint v in kxHashes.Values) { if ((v & 0xFFFF) == 1) { continue; } int w = (int)(v >> 16); CipherSuite cs; if (!CipherSuite.ALL.TryGetValue(w, out cs)) { continue; } if (cs.IsDHE) { scs.KXReuseDH = true; } else if (cs.IsECDHE) { scs.KXReuseECDH = true; } } if (verbose) { Console.WriteLine(); } return(scs); }
/* * 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); }
/* * Get all supported cipher suites with the current configuration. * If there is none, then null is returned (which means that the * protocol version is not supported). */ SupportedCipherSuites GetSupportedCipherSuites() { int num = tb.ComputeMaxCipherSuites(maxRecordLen); if (num < 1) { num = 1; } if (verbose) { Console.WriteLine("[suites: version={0}" + " ({1} suites per hello)]", M.VersionString(tb.MaxVersion), num); } /* * 1. Gather all cipher suites supported by the server. */ IDictionary <int, int> suppd = new SortedDictionary <int, int>(); for (int i = 0; i < csl.Count; i += num) { int k = Math.Min(num, csl.Count - i); int[] tt = new int[k]; for (int j = 0; j < k; j++) { tt[j] = csl[i + j]; } foreach (int s in GetSupportedCipherSuites(tt)) { AddToSet(suppd, s); } } int[] supp = SetToArray(suppd); if (supp.Length == 0) { if (verbose) { Console.WriteLine(); } return(null); } SupportedCipherSuites scs = new SupportedCipherSuites(supp); /* * 2. Work out server preferences. We can do that only * if we can send all supported suites in a single * ClientHello. * * Algorithm: we first try suites in numerical order. Then * we try suites in the reverse order of what the server * selected. If the second list is equal to the first one, * then the server enforces its own preferences. If the * second list is the reverse of the first one, then the * server follows client preferences. In all other cases, * the server selection algorithm is deemed "complex". */ if (supp.Length <= num) { int[] supp1 = GetSupportedCipherSuites(supp); int[] suppRev = new int[supp1.Length]; for (int i = 0; i < supp1.Length; i++) { suppRev[i] = supp[supp1.Length - 1 - i]; } int[] supp2 = GetSupportedCipherSuites(suppRev); if (M.Equals(supp2, suppRev)) { scs.PrefClient = true; } else if (M.Equals(supp1, supp2)) { scs.PrefServer = true; scs.Suites = supp1; } } if (verbose) { Console.WriteLine(); } return(scs); }
public static void UpdateReportAggregator(ReportAggregator aggregator, Report report) { String serverUnderTest = $"{report.ConnName}:{report.ConnPort}"; if (report.SSLv2Chain != null) { aggregator.AddSsl2Cert(serverUnderTest, report.SSLv2Chain); X509Cert xc = report.SSLv2Chain.ElementsRev[0]; if (xc != null && xc.ValidTo.CompareTo(DateTime.Now) < 0) { aggregator.AddOverduedCertificate(serverUnderTest, xc.ValidTo); } } if (report.ssl2Suites != null && report.ssl2Suites.Length > 0) { aggregator.AddSuportedSslVersion(M.VersionString(M.SSLv20)); foreach (int s in report.ssl2Suites) { aggregator.AddSupportedCipherSuite(CipherSuite.ToNameV2(s)); } } aggregator.AddSsl3Certs(serverUnderTest, report.chains.Values); InspectCerts(aggregator, report, serverUnderTest); foreach (int v in report.suites.Keys) { aggregator.AddSuportedSslVersion(M.VersionString(v)); SupportedCipherSuites scs = report.suites[v]; if (scs.PrefClient) { aggregator.AddCipherSuiteSelectionMode("uses client preferences"); } else if (scs.PrefServer) { aggregator.AddCipherSuiteSelectionMode("enforce server preferences"); } else { aggregator.AddCipherSuiteSelectionMode("complex"); } foreach (int s in scs.Suites) { CipherSuite cs; string strength; string fsf; string anon; string kt; if (CipherSuite.ALL.TryGetValue(s, out cs)) { strength = cs.Strength.ToString(); fsf = cs.HasForwardSecrecy ? "f" : "-"; anon = cs.IsAnonymous ? "A" : "-"; kt = cs.ServerKeyType; } else { strength = "?"; fsf = "?"; anon = "?"; kt = "?"; } aggregator.AddSupportedCipherSuite($"{strength}{fsf}{anon} (key: {kt,4}) {CipherSuite.ToName(s)}"); } } foreach (var warning in report.Warnings) { aggregator.AddWarning(warning.Value); } }
/* * Print the report on the provided writer (text version for * humans). */ internal void Print(TextWriter w) { w.WriteLine("Connection: {0}:{1}", connName, connPort); if (sni == null) { w.WriteLine("No SNI sent"); } else { w.WriteLine("SNI: {0}", sni); } if (ssl2Suites != null && ssl2Suites.Length > 0) { w.WriteLine(" {0}", M.VersionString(M.SSLv20)); foreach (int s in ssl2Suites) { w.WriteLine(" {0}", CipherSuite.ToNameV2(s)); } } SupportedCipherSuites last = null; foreach (int v in suites.Keys) { w.Write(" {0}:", M.VersionString(v)); SupportedCipherSuites scs = suites[v]; if (scs.Equals(last)) { w.WriteLine(" idem"); continue; } last = scs; w.WriteLine(); w.Write(" server selection: "); if (scs.PrefClient) { w.WriteLine("uses client preferences"); } else if (scs.PrefServer) { w.WriteLine("enforce server preferences"); } else { w.WriteLine("complex"); } foreach (int s in scs.Suites) { CipherSuite cs; string strength; string fsf; string anon; string kt; if (CipherSuite.ALL.TryGetValue(s, out cs)) { strength = cs.Strength.ToString(); fsf = cs.HasForwardSecrecy ? "f" : "-"; anon = cs.IsAnonymous ? "A" : "-"; kt = cs.ServerKeyType; } else { strength = "?"; fsf = "?"; anon = "?"; kt = "?"; } w.WriteLine(" {0}{1}{2} (key: {3,4}) {4}", strength, fsf, anon, kt, CipherSuite.ToName(s)); } } w.WriteLine("========================================="); if (ssl2Chain != null) { w.WriteLine("+++++ SSLv2 certificate"); PrintCert(w, ssl2Chain, 0, withPEM); } w.WriteLine("+++++ SSLv3/TLS: {0} certificate chain(s)", chains.Count); foreach (X509Chain xchain in chains.Values) { PrintChain(w, xchain, withPEM); } w.WriteLine("========================================="); w.WriteLine("Server compression support: {0}", DeflateCompress ? "yes" : "no"); if (serverTimeOffset == Int64.MinValue) { w.WriteLine("Server does not send its system time."); } else if (serverTimeOffset == Int64.MaxValue) { w.WriteLine("Server sends a random system time."); } else { DateTime dt = DateTime.UtcNow; dt = dt.AddMilliseconds((double)serverTimeOffset); w.WriteLine("Server time: {0:yyyy-MM-dd HH:mm:ss} UTC" + " (offset: {1} ms)", dt, serverTimeOffset); } w.WriteLine("Secure renegotiation support: {0}", doesRenego ? "yes" : "no"); if (minDHSize > 0) { w.WriteLine("Minimum DH size: {0}", minDHSize); } if (minECSize > 0) { w.WriteLine("Minimum EC size (no extension): {0}", minECSize); } if (minECSizeExt > 0) { w.WriteLine("Minimum EC size (with extension): {0}", minECSizeExt); } if (namedCurves != null && namedCurves.Length > 0) { w.WriteLine("Supported curves (size and name)" + " ('*' = selected by server):"); foreach (SSLCurve nc in namedCurves) { w.WriteLine(" {0} {1,3} {2}", IsSpontaneous(nc) ? "*" : " ", nc.Size, nc.Name); } if (curveExplicitPrime > 0) { w.WriteLine(" explicit prime, size = {0}", curveExplicitPrime); } if (curveExplicitChar2 > 0) { w.WriteLine(" explicit char2, size = {0}", curveExplicitChar2); } } w.WriteLine("========================================="); if (warnings == null) { Analyse(); } if (warnings.Count == 0) { w.WriteLine("No warning."); } else { foreach (string k in warnings.Keys) { w.WriteLine("WARN[{0}]: {1}", k, warnings[k]); } } }