예제 #1
0
    /*
     * 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);
    }