/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.jss.pkcs11;

import java.security.NoSuchAlgorithmException;
import java.util.Hashtable;

import org.mozilla.jss.crypto.Algorithm;
import org.mozilla.jss.crypto.CMACAlgorithm;
import org.mozilla.jss.crypto.EncryptionAlgorithm;
import org.mozilla.jss.crypto.HMACAlgorithm;
import org.mozilla.jss.crypto.KeyWrapAlgorithm;
import org.mozilla.jss.crypto.SignatureAlgorithm;

/**
 * PKCS #11 Key Types
 * These are the possible types for keys in the
 * wrapper library.
 * Key types are implemented as flyweights.
 *
 * Although the KeyType class is public, it should
 * be considered private. We made the KeyType class
 * public so that we can force it to load during
 * CryptoManager.initialize(), before we install JSS
 * as a provider.
**/
public final class KeyType {
    protected KeyType() {}

    protected KeyType(Algorithm[] algs, String name) {
        int i;

        assert(algs!=null);

        algorithms = algs.clone();

        // Register this key as the key type for each of its algorithms
        for(i=0; i < algorithms.length; i++) {
            assert(! algHash.containsKey(algorithms[i]) );
            algHash.put(algorithms[i], this);
        }
        this.name = name;
    }

    /**
     * Returns an array of algorithms supported by this key type.
     */
    public Algorithm[] supportedAlgorithms() {
        return algorithms;
    }

    /**
     * Returns the KeyType corresponding to the given Algorithm.  If there
     * is no KeyType registered for this algorithm, a NoSuchAlgorithmException
     * is thrown.
     */
    static public KeyType getKeyTypeFromAlgorithm(Algorithm alg)
        throws NoSuchAlgorithmException
    {
        assert(alg!=null);
        Object obj = algHash.get(alg);

        if(obj == null) {
            throw new NoSuchAlgorithmException();
        }

        assert( obj instanceof KeyType );

        return (KeyType) obj;
    }

    @Override
    public String toString() {
        return name;
    }


    //////////////////////////////////////////////////////////////
    // Instance Data
    //////////////////////////////////////////////////////////////

    // An array of algorithms supported by this key type
    protected Algorithm[] algorithms;

    protected String name;


    //////////////////////////////////////////////////////////////
    // Class Data
    //////////////////////////////////////////////////////////////

    // A hash table associating a key type with each algorithm
    static protected Hashtable<Algorithm, KeyType> algHash;
    static {
        algHash = new Hashtable<>();
    }




    //////////////////////////////////////////////////////////////
    // Key Types
    //////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////
    static public final KeyType
    NULL    = new KeyType(new Algorithm[0], "NULL");

    //////////////////////////////////////////////////////////////
    static public final KeyType
    RSA     = new KeyType (new Algorithm[]
                    {
                    SignatureAlgorithm.RSASignature,
                    SignatureAlgorithm.RSASignatureWithMD2Digest,
                    SignatureAlgorithm.RSASignatureWithMD5Digest,
                    SignatureAlgorithm.RSASignatureWithSHA1Digest,
                    SignatureAlgorithm.RSASignatureWithSHA256Digest,
                    SignatureAlgorithm.RSASignatureWithSHA384Digest,
                    SignatureAlgorithm.RSASignatureWithSHA512Digest,
                    SignatureAlgorithm.RSAPSSSignature,
                    SignatureAlgorithm.RSAPSSSignatureWithSHA256Digest,
                    SignatureAlgorithm.RSAPSSSignatureWithSHA384Digest,
                    SignatureAlgorithm.RSAPSSSignatureWithSHA512Digest,
                    KeyWrapAlgorithm.RSA,
                    KeyWrapAlgorithm.RSA_OAEP,
                    },
                    "RSA"
                );

    //////////////////////////////////////////////////////////////
    static public final KeyType
    DSA     = new KeyType(new Algorithm[]
                    {
                    SignatureAlgorithm.DSASignature,
                    SignatureAlgorithm.DSASignatureWithSHA1Digest
                    },
                    "DSA"
                );

    //////////////////////////////////////////////////////////////
    static public final KeyType
    EC     = new KeyType(new Algorithm[]
                    {
                    SignatureAlgorithm.ECSignature,
                    SignatureAlgorithm.ECSignatureWithSHA1Digest,
                    SignatureAlgorithm.ECSignatureWithSHA256Digest,
                    SignatureAlgorithm.ECSignatureWithSHA384Digest,
                    SignatureAlgorithm.ECSignatureWithSHA512Digest
                    },
                    "EC"
                );

    //////////////////////////////////////////////////////////////
    static public final KeyType
    MLDSA     = new KeyType(new Algorithm[]
                    {
                    SignatureAlgorithm.MLDSA44,
                    SignatureAlgorithm.MLDSA65,
                    SignatureAlgorithm.MLDSA87
                    },
                    "ML-DSA"
                );
    //////////////////////////////////////////////////////////////
    /**
     * @deprecated As of NSS 3.11, FORTEZZA is no longer supported.
     * This is just a placeholder for backward compatibility.
     */
    @Deprecated
    static public final KeyType
    FORTEZZA = new KeyType(new Algorithm[0], "FORTEZZA");

    //////////////////////////////////////////////////////////////
    static public final KeyType
    DH      = new KeyType(new Algorithm[0], "DH");

    //////////////////////////////////////////////////////////////
    static public final KeyType
    KEA     = new KeyType(new Algorithm[0], "KEA");

    //////////////////////////////////////////////////////////////
    static public final KeyType
    DES     = new KeyType(new Algorithm[]
                            {
                            KeyWrapAlgorithm.DES_ECB,
                            KeyWrapAlgorithm.DES_CBC,
                            KeyWrapAlgorithm.DES_CBC_PAD,
                            EncryptionAlgorithm.DES_ECB,
                            EncryptionAlgorithm.DES_CBC,
                            EncryptionAlgorithm.DES_CBC_PAD
                            },
                            "DES"
                        );

    //////////////////////////////////////////////////////////////
    static public final KeyType
    DES3     = new KeyType(new Algorithm[]
                            {
                            KeyWrapAlgorithm.DES3_ECB,
                            KeyWrapAlgorithm.DES3_CBC,
                            KeyWrapAlgorithm.DES3_CBC_PAD,
                            EncryptionAlgorithm.DES3_ECB,
                            EncryptionAlgorithm.DES3_CBC,
                            EncryptionAlgorithm.DES3_CBC_PAD
                            },
                            "DESede"
                        );

    //////////////////////////////////////////////////////////////
    static public final KeyType
    AES       = new KeyType(new Algorithm[]
                            {
                            KeyWrapAlgorithm.AES_ECB,
                            KeyWrapAlgorithm.AES_CBC,
                            KeyWrapAlgorithm.AES_CBC_PAD,
                            KeyWrapAlgorithm.AES_KEY_WRAP,
                            KeyWrapAlgorithm.AES_KEY_WRAP_PAD,
			    KeyWrapAlgorithm.AES_KEY_WRAP_PAD_KWP,
                            EncryptionAlgorithm.AES_128_ECB,
                            EncryptionAlgorithm.AES_128_CBC,
                            EncryptionAlgorithm.AES_192_ECB,
                            EncryptionAlgorithm.AES_192_CBC,
                            EncryptionAlgorithm.AES_256_ECB,
                            EncryptionAlgorithm.AES_256_CBC,
                            EncryptionAlgorithm.AES_CBC_PAD,
                            EncryptionAlgorithm.AES_128_CBC_PAD,
                            EncryptionAlgorithm.AES_192_CBC_PAD,
                            EncryptionAlgorithm.AES_256_CBC_PAD,
                            CMACAlgorithm.AES
                            },
                            "AES"
                        );

    //////////////////////////////////////////////////////////////
    static public final KeyType
    RC4     = new KeyType(new Algorithm[]
                            {
                            EncryptionAlgorithm.RC4
                            },
                            "RC4"
                        );

    //////////////////////////////////////////////////////////////
    static public final KeyType
    RC2     = new KeyType(new Algorithm[]
                            {
                            EncryptionAlgorithm.RC2_CBC,
                            EncryptionAlgorithm.RC2_CBC_PAD
                            },
                            "RC2"
                        );

    //////////////////////////////////////////////////////////////
    @Deprecated(since="5.0.1", forRemoval=true)
    static public final KeyType
    SHA1_HMAC = new KeyType(new Algorithm[]
                            {
                            HMACAlgorithm.SHA1
                            },
                            "SHA1_HMAC"
                        );

    static public final KeyType
    SHA256_HMAC = new KeyType(new Algorithm[]
                              {
                              HMACAlgorithm.SHA256
                              },
                              "SHA256_HMAC"
                        );

    static public final KeyType
    SHA384_HMAC = new KeyType(new Algorithm[]
                              {
                              HMACAlgorithm.SHA384
                              },
                              "SHA384_HMAC"
                        );
    static public final KeyType
    SHA512_HMAC = new KeyType(new Algorithm[]
                              {
                              HMACAlgorithm.SHA512
                              },
                              "SHA512_HMAC"
                        );

    static public final KeyType GENERIC_SECRET =
        new KeyType(new Algorithm[] { }, "GENERIC_SECRET");

}
