Ejemplo n.º 1
0
 void AddServerNamedCurve(SSLCurve sc)
 {
     if (sc != null)
     {
         namedCurves[sc.Id] = sc;
     }
 }
Ejemplo n.º 2
0
 /*
  * 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);
 }
Ejemplo n.º 3
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);
    }
Ejemplo n.º 4
0
    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();
    }
Ejemplo n.º 5
0
 static void Add(int id, string name, int size)
 {
     ALL[id] = new SSLCurve(id, name, size);
 }