/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * @author Boris Kuznetsov
 * @version $Revision$
 */

package org.apache.harmony.xnet.provider.jsse;

import java.security.GeneralSecurityException;
import java.util.Hashtable;

import javax.crypto.Cipher;

/**
 * Represents Cipher Suite as defined in TLS 1.0 spec., 
 * A.5. The CipherSuite;
 * C. CipherSuite definitions.
 * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec.</a>
 * 
 */
public class CipherSuite {

    /**
     * true if this cipher suite is supported
     */ 
    boolean supported = true;

    /**
     * cipher suite key exchange
     */
    final int keyExchange;

    /**
     * cipher
     */
    final String cipherName;
    
    /**
     * Cipher information
     */
    final int keyMaterial;
    final int expandedKeyMaterial;
    final int effectiveKeyBytes;
    final int IVSize;
    final private int blockSize;
    
    // cipher suite code
    private final byte[] cipherSuiteCode;

    // cipher suite name
    private final String name;
    
    // true if cipher suite is exportable
    private final boolean isExportable;

    // Hash algorithm
    final private String hashName;
    
    // MAC algorithm
    final private String hmacName;
    
    // Hash size
    final private int hashSize;

    /**
     * key exchange values
     */
    static int KeyExchange_RSA = 1;
    static int KeyExchange_RSA_EXPORT = 2;
    static int KeyExchange_DHE_DSS = 3;
    static int KeyExchange_DHE_DSS_EXPORT = 4;
    static int KeyExchange_DHE_RSA = 5;
    static int KeyExchange_DHE_RSA_EXPORT = 6;
    static int KeyExchange_DH_DSS = 7;
    static int KeyExchange_DH_RSA = 8;
    static int KeyExchange_DH_anon = 9;
    static int KeyExchange_DH_anon_EXPORT = 10;
    static int KeyExchange_DH_DSS_EXPORT = 11;
    static int KeyExchange_DH_RSA_EXPORT = 12;

    /**
     * TLS cipher suite codes
     */
    static byte[] code_TLS_NULL_WITH_NULL_NULL = { 0x00, 0x00 };
    static byte[] code_TLS_RSA_WITH_NULL_MD5 = { 0x00, 0x01 };
    static byte[] code_TLS_RSA_WITH_NULL_SHA = { 0x00, 0x02 };
    static byte[] code_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = { 0x00, 0x03 };
    static byte[] code_TLS_RSA_WITH_RC4_128_MD5 = { 0x00, 0x04 };
    static byte[] code_TLS_RSA_WITH_RC4_128_SHA = { 0x00, 0x05 };
    static byte[] code_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00, 0x06 };
    static byte[] code_TLS_RSA_WITH_IDEA_CBC_SHA = { 0x00, 0x07 };
    static byte[] code_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x08 };
    static byte[] code_TLS_RSA_WITH_DES_CBC_SHA = { 0x00, 0x09 };
    static byte[] code_TLS_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x0A };
    static byte[] code_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x0B };
    static byte[] code_TLS_DH_DSS_WITH_DES_CBC_SHA = { 0x00, 0x0C };
    static byte[] code_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x0D };
    static byte[] code_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x0E };
    static byte[] code_TLS_DH_RSA_WITH_DES_CBC_SHA = { 0x00, 0x0F };
    static byte[] code_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x10 };
    static byte[] code_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x11 };
    static byte[] code_TLS_DHE_DSS_WITH_DES_CBC_SHA = { 0x00, 0x12 };
    static byte[] code_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x13 };
    static byte[] code_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x14 };
    static byte[] code_TLS_DHE_RSA_WITH_DES_CBC_SHA = { 0x00, 0x15 };
    static byte[] code_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x16 };
    static byte[] code_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00, 0x17 };
    static byte[] code_TLS_DH_anon_WITH_RC4_128_MD5 = { 0x00, 0x18 };
    static byte[] code_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00, 0x19 };
    static byte[] code_TLS_DH_anon_WITH_DES_CBC_SHA = { 0x00, 0x1A };
    static byte[] code_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = { 0x00, 0x1B };

    static CipherSuite TLS_NULL_WITH_NULL_NULL = new CipherSuite(
            "TLS_NULL_WITH_NULL_NULL", true, 0, null, null,
            code_TLS_NULL_WITH_NULL_NULL);

    static CipherSuite TLS_RSA_WITH_NULL_MD5 = new CipherSuite(
            "TLS_RSA_WITH_NULL_MD5", true, KeyExchange_RSA, null, "MD5",
            code_TLS_RSA_WITH_NULL_MD5);

    static CipherSuite TLS_RSA_WITH_NULL_SHA = new CipherSuite(
            "TLS_RSA_WITH_NULL_SHA", true, KeyExchange_RSA, null, "SHA",
            code_TLS_RSA_WITH_NULL_SHA);

    static CipherSuite TLS_RSA_EXPORT_WITH_RC4_40_MD5 = new CipherSuite(
            "TLS_RSA_EXPORT_WITH_RC4_40_MD5", true, KeyExchange_RSA_EXPORT,
            "RC4_40", "MD5", code_TLS_RSA_EXPORT_WITH_RC4_40_MD5);

    static CipherSuite TLS_RSA_WITH_RC4_128_MD5 = new CipherSuite(
            "TLS_RSA_WITH_RC4_128_MD5", false, KeyExchange_RSA, "RC4_128",
            "MD5", code_TLS_RSA_WITH_RC4_128_MD5);

    static CipherSuite TLS_RSA_WITH_RC4_128_SHA = new CipherSuite(
            "TLS_RSA_WITH_RC4_128_SHA", false, KeyExchange_RSA, "RC4_128",
            "SHA", code_TLS_RSA_WITH_RC4_128_SHA);

    static CipherSuite TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = new CipherSuite(
            "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", true, KeyExchange_RSA_EXPORT,
            "RC2_CBC_40", "MD5", code_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5);

    static CipherSuite TLS_RSA_WITH_IDEA_CBC_SHA = new CipherSuite(
            "TLS_RSA_WITH_IDEA_CBC_SHA", false, KeyExchange_RSA, "IDEA_CBC",
            "SHA", code_TLS_RSA_WITH_IDEA_CBC_SHA);

    static CipherSuite TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
            "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", true, KeyExchange_RSA_EXPORT,
            "DES40_CBC", "SHA", code_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA);

    static CipherSuite TLS_RSA_WITH_DES_CBC_SHA = new CipherSuite(
            "TLS_RSA_WITH_DES_CBC_SHA", false, KeyExchange_RSA, "DES_CBC",
            "SHA", code_TLS_RSA_WITH_DES_CBC_SHA);

    static CipherSuite TLS_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
            "TLS_RSA_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_RSA,
            "3DES_EDE_CBC", "SHA", code_TLS_RSA_WITH_3DES_EDE_CBC_SHA);

    static CipherSuite TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
            "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", true,
            KeyExchange_DH_DSS_EXPORT, "DES40_CBC", "SHA",
            code_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA);

    static CipherSuite TLS_DH_DSS_WITH_DES_CBC_SHA = new CipherSuite(
            "TLS_DH_DSS_WITH_DES_CBC_SHA", false, KeyExchange_DH_DSS,
            "DES_CBC", "SHA", code_TLS_DH_DSS_WITH_DES_CBC_SHA);

    static CipherSuite TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
            "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DH_DSS,
            "3DES_EDE_CBC", "SHA", code_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA);

    static CipherSuite TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
            "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", true,
            KeyExchange_DH_RSA_EXPORT, "DES40_CBC", "SHA",
            code_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA);

    static CipherSuite TLS_DH_RSA_WITH_DES_CBC_SHA = new CipherSuite(
            "TLS_DH_RSA_WITH_DES_CBC_SHA", false, KeyExchange_DH_RSA,
            "DES_CBC", "SHA", code_TLS_DH_RSA_WITH_DES_CBC_SHA);

    static CipherSuite TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
            "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DH_RSA,
            "3DES_EDE_CBC", "SHA", code_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA);

    static CipherSuite TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
            "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", true,
            KeyExchange_DHE_DSS_EXPORT, "DES40_CBC", "SHA",
            code_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA);

    static CipherSuite TLS_DHE_DSS_WITH_DES_CBC_SHA = new CipherSuite(
            "TLS_DHE_DSS_WITH_DES_CBC_SHA", false, KeyExchange_DHE_DSS,
            "DES_CBC", "SHA", code_TLS_DHE_DSS_WITH_DES_CBC_SHA);

    static CipherSuite TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
            "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DHE_DSS,
            "3DES_EDE_CBC", "SHA", code_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);

    static CipherSuite TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
            "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", true,
            KeyExchange_DHE_RSA_EXPORT, "DES40_CBC", "SHA",
            code_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA);

    static CipherSuite TLS_DHE_RSA_WITH_DES_CBC_SHA = new CipherSuite(
            "TLS_DHE_RSA_WITH_DES_CBC_SHA", false, KeyExchange_DHE_RSA,
            "DES_CBC", "SHA", code_TLS_DHE_RSA_WITH_DES_CBC_SHA);

    static CipherSuite TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
            "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DHE_RSA,
            "3DES_EDE_CBC", "SHA", code_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);

    static CipherSuite TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = new CipherSuite(
            "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", true,
            KeyExchange_DH_anon_EXPORT, "RC4_40", "MD5",
            code_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5);

    static CipherSuite TLS_DH_anon_WITH_RC4_128_MD5 = new CipherSuite(
            "TLS_DH_anon_WITH_RC4_128_MD5", false, KeyExchange_DH_anon,
            "RC4_128", "MD5", code_TLS_DH_anon_WITH_RC4_128_MD5);

    static CipherSuite TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = new CipherSuite(
            "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", true,
            KeyExchange_DH_anon_EXPORT, "DES40_CBC", "SHA",
            code_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA);

    static CipherSuite TLS_DH_anon_WITH_DES_CBC_SHA = new CipherSuite(
            "TLS_DH_anon_WITH_DES_CBC_SHA", false, KeyExchange_DH_anon,
            "DES_CBC", "SHA", code_TLS_DH_anon_WITH_DES_CBC_SHA);

    static CipherSuite TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = new CipherSuite(
            "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", false, KeyExchange_DH_anon,
            "3DES_EDE_CBC", "SHA", code_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA);

    // array for quick access to cipher suite by code
    private static CipherSuite[] cuitesByCode = { 
            TLS_NULL_WITH_NULL_NULL,
            TLS_RSA_WITH_NULL_MD5,
            TLS_RSA_WITH_NULL_SHA,
            TLS_RSA_EXPORT_WITH_RC4_40_MD5,
            TLS_RSA_WITH_RC4_128_MD5,
            TLS_RSA_WITH_RC4_128_SHA,
            TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
            TLS_RSA_WITH_IDEA_CBC_SHA,
            TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
            TLS_RSA_WITH_DES_CBC_SHA,
            TLS_RSA_WITH_3DES_EDE_CBC_SHA,
            TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
            TLS_DH_DSS_WITH_DES_CBC_SHA,
            TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
            TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
            TLS_DH_RSA_WITH_DES_CBC_SHA,
            TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
            TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
            TLS_DHE_DSS_WITH_DES_CBC_SHA,
            TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
            TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
            TLS_DHE_RSA_WITH_DES_CBC_SHA,
            TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
            TLS_DH_anon_EXPORT_WITH_RC4_40_MD5,
            TLS_DH_anon_WITH_RC4_128_MD5,
            TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
            TLS_DH_anon_WITH_DES_CBC_SHA,
            TLS_DH_anon_WITH_3DES_EDE_CBC_SHA
            };

    // hash for quick access to cipher suite by name 
    private static Hashtable cuitesByName;

    /**
     * array of supported sipher suites.
     * Set of supported suites is defined at the moment provider's start 
     */
//  TODO Dinamical supported suites: new providers may be dynamically 
//  added/removed and the set of supportes suites may be changed
    static CipherSuite[] supportedCipherSuites;

    /**
     * array of supported sipher suites names
     */
    static String[] supportedCipherSuiteNames;

    /**
     * default sipher suites
     */
    static CipherSuite[] defaultCipherSuites;
    
    static {
        int count = 0;
        cuitesByName = new Hashtable();
        for (int i = 0; i < cuitesByCode.length; i++) {
            cuitesByName.put(cuitesByCode[i].getName(), cuitesByCode[i]);
            if (cuitesByCode[i].supported) {
                count++;
            }
        }
        supportedCipherSuites = new CipherSuite[count];
        supportedCipherSuiteNames = new String[count];
        count = 0;
        for (int i = 0; i < cuitesByCode.length; i++) {
            if (cuitesByCode[i].supported) {
                supportedCipherSuites[count] = cuitesByCode[i];
                supportedCipherSuiteNames[count] = supportedCipherSuites[count].getName();
                count++;
            }
        }

        CipherSuite[] defaultPretendent = { 
                TLS_RSA_WITH_RC4_128_MD5,
                TLS_RSA_WITH_RC4_128_SHA,
                // TLS_RSA_WITH_AES_128_CBC_SHA,
                // TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                // LS_DHE_DSS_WITH_AES_128_CBC_SHA,
                TLS_RSA_WITH_3DES_EDE_CBC_SHA,
                TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
                TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA,
                TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_DHE_DSS_WITH_DES_CBC_SHA,
                TLS_RSA_EXPORT_WITH_RC4_40_MD5,
                TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
                TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
                TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 
                };
        count = 0;
        for (int i = 0; i < defaultPretendent.length; i++) {
            if (defaultPretendent[i].supported) {
                count++;
            }
        }
        defaultCipherSuites = new CipherSuite[count];
        count = 0;
        for (int i = 0; i < defaultPretendent.length; i++) {
            if (defaultPretendent[i].supported) {
                defaultCipherSuites[count++] = defaultPretendent[i];
            }
        }
    }

    /**
     * Returns CipherSuite by name
     * @param name
     * @return
     */
    public static CipherSuite getByName(String name) {
        return (CipherSuite) cuitesByName.get(name);
    }

    /**
     * Returns CipherSuite based on TLS CipherSuite code
     * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., A.5. The CipherSuite</a>
     * @param b1
     * @param b2
     * @return
     */
    public static CipherSuite getByCode(byte b1, byte b2) {
        if (b1 != 0 || b2 > cuitesByCode.length) {
            // Unknoun
            return new CipherSuite("UNKNOUN_" + b1 + "_" + b2, false, 0, "",
                    "", new byte[] { b1, b2 });
        }
        return cuitesByCode[b2];
    }

    /**
     * Returns CipherSuite based on V2CipherSpec code
     * as described in TLS 1.0 spec., E. Backward Compatibility With SSL
     * 
     * @param b1
     * @param b2
     * @param b3
     * @return CipherSuite
     */
    public static CipherSuite getByCode(byte b1, byte b2, byte b3) {
        if (b1 == 0 && b2 == 0) {
            if (b3 <= cuitesByCode.length) {
                return cuitesByCode[b3];
            }
        }
        // as TLSv1 equivalent of V2CipherSpec should be included in
        // V2ClientHello, ignore V2CipherSpec
        return new CipherSuite("UNKNOUN_" + b1 + "_" + b2 + "_" + b3, false, 0,
                "", "", new byte[] { b1, b2, b3 });
    }

    /**
     * Creates CipherSuite
     * @param name
     * @param isExportable
     * @param keyExchange
     * @param cipherName
     * @param hash
     * @param code
     */
    public CipherSuite(String name, boolean isExportable, int keyExchange,
            String cipherName, String hash, byte[] code) {
        this.name = name;
        this.keyExchange = keyExchange;
        this.isExportable = isExportable;
        if (cipherName == null) {
            this.cipherName = null;
            keyMaterial = 0;
            expandedKeyMaterial = 0;
            effectiveKeyBytes = 0;
            IVSize = 0;
            blockSize = 0;
        } else if ("IDEA_CBC".equals(cipherName)) {
            this.cipherName = "IDEA/CBC/NoPadding";
            keyMaterial = 16;
            expandedKeyMaterial = 16;
            effectiveKeyBytes = 16;
            IVSize = 8;
            blockSize = 8;
        } else if ("RC2_CBC_40".equals(cipherName)) {
            this.cipherName = "RC2/CBC/NoPadding";
            keyMaterial = 5;
            expandedKeyMaterial = 16;
            effectiveKeyBytes = 5;
            IVSize = 8;
            blockSize = 8;
        } else if ("RC4_40".equals(cipherName)) {
            this.cipherName = "RC4";
            keyMaterial = 5;
            expandedKeyMaterial = 16;
            effectiveKeyBytes = 5;
            IVSize = 0;
            blockSize = 0;
        } else if ("RC4_128".equals(cipherName)) {
            this.cipherName = "RC4";
            keyMaterial = 16;
            expandedKeyMaterial = 16;
            effectiveKeyBytes = 16;
            IVSize = 0;
            blockSize = 0;
        } else if ("DES40_CBC".equals(cipherName)) {
            this.cipherName = "DES/CBC/NoPadding";
            keyMaterial = 5;
            expandedKeyMaterial = 8;
            effectiveKeyBytes = 5;
            IVSize = 8;
            blockSize = 8;
        } else if ("DES_CBC".equals(cipherName)) {
            this.cipherName = "DES/CBC/NoPadding";
            keyMaterial = 8;
            expandedKeyMaterial = 8;
            effectiveKeyBytes = 7;
            IVSize = 8;
            blockSize = 8;
        } else if ("3DES_EDE_CBC".equals(cipherName)) {
            this.cipherName = "DESede/CBC/NoPadding";
            keyMaterial = 24;
            expandedKeyMaterial = 24;
            effectiveKeyBytes = 24;
            IVSize = 8;
            blockSize = 8;
        } else {
            this.cipherName = cipherName;
            keyMaterial = 0;
            expandedKeyMaterial = 0;
            effectiveKeyBytes = 0;
            IVSize = 0;
            blockSize = 0;
        }

        if ("MD5".equals(hash)) {
            this.hmacName = "HmacMD5";
            this.hashName = "MD5";
            hashSize = 16;
        } else if ("SHA".equals(hash)) {
            this.hmacName = "HmacSHA1";
            this.hashName = "SHA-1";
            hashSize = 20;
        } else {
            this.hmacName = null;
            this.hashName = null;
            hashSize = 0;
        }

        cipherSuiteCode = code;

        if (this.cipherName != null) {
            try {
                Cipher.getInstance(this.cipherName);
            } catch (GeneralSecurityException e) {
                supported = false;
            }
        }

    }

    /**
     * Returns true if cipher suite is anonymous
     * @return
     */
    public boolean isAnonymous() {
        if (keyExchange == KeyExchange_DH_anon
                || keyExchange == KeyExchange_DH_anon_EXPORT) {
            return true;
        }
        return false;
    }

    /**
     * Returns array of supported CipherSuites
     * @return
     */
    public static CipherSuite[] getSupported() {
        return supportedCipherSuites;
    }

    /**
     * Returns array of supported cipher suites names
     * @return
     */
    public static String[] getSupportedCipherSuiteNames() {
        return (String[]) supportedCipherSuiteNames.clone();
    }

    /**
     * Returns cipher suite name
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     * Returns cipher suite code as byte array
     * @return
     */
    public byte[] toBytes() {
        return cipherSuiteCode;
    }

    /**
     * Returns cipher suite description
     */
    public String toString() {
        return name + ": " + cipherSuiteCode[0] + " " + cipherSuiteCode[1];
    }

    /**
     * Compares this cipher suite to the specified object.
     */
    public boolean equals(Object obj) {
        if (obj instanceof CipherSuite
                && this.cipherSuiteCode[0] == ((CipherSuite) obj).cipherSuiteCode[0]
                && this.cipherSuiteCode[1] == ((CipherSuite) obj).cipherSuiteCode[1]) {
            return true;
        }
        return false;
    }

    /**
     * Returns cipher algorithm name
     * @return
     */
    public String getBulkEncryptionAlgorithm() {
        return cipherName;
    }

    /**
     * Returns cipher block size
     * @return
     */
    public int getBlockSize() {
        return blockSize;
    }

    /**
     * Returns MAC algorithm name
     * @return
     */
    public String getHmacName() {
        return hmacName;
    }

    /**
     * Returns hash algorithm name
     * @return
     */
    public String getHashName() {
        return hashName;
    }

    /**
     * Returns hash size
     * @return
     */
    public int getMACLength() {
        return hashSize;
    }

    /**
     * Indicates whether this cipher suite is exportable
     * @return
     */
    public boolean isExportable() {
        return isExportable;
    }

}
