/*
 *  © Darren McDonald 2010
 *
 *  This file is part of athena-ssl-cipher-check.
 *
 *  athena-ssl-cipher-check is free software: you can redistribute it
 *  and/or modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation, either version 3 of the License,
 *  or (at your option) any later version.
 *
 *  athena-ssl-cipher-check is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 *  Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with athena-ssl-cipher-check. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

package athenasslciphercheck;

public class ssl2CipherCheck {
    public int resetCount = 0;
    public int timedOutCount = 0;
    public int unidentifiedCount = 0;
    public int rejectedCount = 0;

    ssl2CipherCheck( )
    {

    }

    public void check()
    {
        byte[] helloMessage = new byte[30];

        byte[] cipherSpec = { (byte) 0x01, (byte) 0x00, (byte) 0x80,   //SSL_CK_RC4_128_WITH_MD5
                              (byte) 0x02, (byte) 0x00, (byte) 0x80,   //SSL_CK_RC4_128_EXPORT40_WITH_MD5
                              (byte) 0x03, (byte) 0x00, (byte) 0x80,   //SSL_CK_RC2_128_CBC_WITH_MD5
                              (byte) 0x04, (byte) 0x00, (byte) 0x80,   //SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5
                              (byte) 0x05, (byte) 0x00, (byte) 0x80,   //SSL_CK_IDEA_128_CBC_WITH_MD5
                              (byte) 0x06, (byte) 0x00, (byte) 0x40,   //SSL_CK_DES_64_CBC_WITH_MD5
                              (byte) 0x07, (byte) 0x00, (byte) 0xC0,   //SSL_CK_DES_192_EDE3_CBC_WITH_MD5
                              (byte) 0x08, (byte) 0x00, (byte) 0x80};  //SSL_CK_RC4_64_WITH_MD5
        //If you can add to this list please let me know via athena@dmcdonald.net

        int timeOutCounter = 0;

        //                  v - change this if length of cipherSpec Changes
        for( int i = 0; i < 8; i++ )
        {
            sslv2MessageFactory.makeMessage( helloMessage, cipherSpec, i*3 );
            connection conn = new connection();
            if( !conn.connect( ) )
            {
                break;
            }

            conn.send(helloMessage);

            int tmp = 0x00;
            int length1 = conn.recv();

            if( length1 == -3 )
                {
                    timeOutCounter++;
                    if( false /*timeOutCounter > 3*/ )
                    {
                        System.out.println("SSLv2 being skipped due to timeouts");
                        return;
                    }
                    break;
                }

            int length2 = conn.recv();

            //3-6
            tmp = conn.recv();
            tmp = conn.recv();
            tmp = conn.recv();
            tmp = conn.recv();
            tmp = conn.recv();

            //7-8 (certificate length)
            int certL = conn.recv()*256;
            certL += conn.recv();

            //9-10 (cipher length
            int cipherL = conn.recv()*256;
            cipherL += conn.recv();

            //connection id length (dont care)
            tmp = conn.recv();
            tmp = conn.recv();

            //read through cert (dont care)
            for( int x = 0; x < certL; x++)
            {
                tmp = conn.recv();
            }

            for( int x = 0; x < cipherL/3; x++)
            {
                int cpr1 = conn.recv();
                int cpr2 = conn.recv();
                int cpr3 = conn.recv();

                for( int n = 0; n < cipherSpec.length; n += 3)
                {
                    if( cipherSpec[n] == (byte) cpr1 &&
                        cipherSpec[n + 1] == (byte)cpr2 &&
                        cipherSpec[n + 2] == (byte) cpr3 )
                    {
                        //stop ciphers being repeated multiple times
                        cipherSpec[n] = 0x00;
                        cipherSpec[n + 1] = 0x00;
                        cipherSpec[n + 2] = 0x00;

                        //print out result
                        switch( n/3 )
                        {
                            case 0:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_RC4_128_MD5", null, 128, 0 ), "SSLv2" );
                                break;
                            case 1:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_RC4_128_EXPORT40_MD5", null, 40, 0 ), "SSLv2" );
                                break;
                            case 2:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_RC2_128_CBC_MD5", null, 128, 0 ), "SSLv2" );
                                break;
                            case 3:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_RC2_128_CBC_EXPORT40_MD5", null, 40, 0 ), "SSLv2" );
                                break;
                            case 4:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_IDEA_128_CBC_MD5", null, 128, 0 ), "SSLv2" );
                                break;
                            case 5:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_DES_64_CBC_MD5", null, 56, 0 ), "SSLv2" );
                                break;
                            case 6:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_DES_192_EDE3_CBC_MD5", null, 168, 0 ), "SSLv2" );
                                break;
                            case 7:
                                Results.addResultLine( new Cipher( "SSL_CK_WITH_RC4_64_MD5", null, 64, 0 ), "SSLv2" );
                                break;
                            default:  //This should not happen
                                Results.addResultLine( new Cipher( "Unknown SSLv2 Cipher found", null, 0, 0 ), "SSLv2" );
                        }
                    }
                }
            }

            conn.close();
        }
    }

    //As with TLS/SSlv3, status is a mess, replace with debug mode
    private void printStats() {
        if( resetCount == 0 &&
            timedOutCount == 0 &&
            unidentifiedCount == 0 &&
            rejectedCount == 0 )
            return;

        System.out.println("");
        boolean first = true;
        if( resetCount > 0 )
        {
            System.out.print(resetCount + " connections reset");
            first = false;
        }

        if( timedOutCount > 0 )
        {
            if( !first )
                System.out.print(", ");
            System.out.print(resetCount + " connections reset");
            first = false;
        }

        if( unidentifiedCount > 0 )
        {
            if( !first )
                System.out.print(", ");
            System.out.print(unidentifiedCount + " connections reset");
            first = false;
        }

        if( rejectedCount > 0 )
        {
            if( !first )
                System.out.print(", ");
            System.out.print(rejectedCount + " connections reset");
            first = false;
        }
    }
}
