package org.bouncycastle.pqc.crypto.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.bouncycastle.asn1.ASN1BitString;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.pqc.crypto.mqom.MQOMParameters;
import org.bouncycastle.pqc.crypto.mqom.MQOMPublicKeyParameters;
import org.bouncycastle.pqc.crypto.sdith.SDitHParameters;
import org.bouncycastle.pqc.crypto.sdith.SDitHPublicKeyParameters;
import org.bouncycastle.pqc.crypto.uov.UOVParameters;
import org.bouncycastle.pqc.crypto.uov.UOVPublicKeyParameters;
import org.bouncycastle.asn1.iana.IANAObjectIdentifiers;
import org.bouncycastle.internal.asn1.isara.IsaraObjectIdentifiers;
import org.bouncycastle.pqc.asn1.CMCEPublicKey;
import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers;
import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams;
import org.bouncycastle.pqc.asn1.XMSSKeyParams;
import org.bouncycastle.pqc.asn1.XMSSMTKeyParams;
import org.bouncycastle.pqc.asn1.XMSSPublicKey;
import org.bouncycastle.pqc.crypto.aimer.AIMerParameters;
import org.bouncycastle.pqc.crypto.aimer.AIMerPublicKeyParameters;
import org.bouncycastle.pqc.legacy.bike.BIKEParameters;
import org.bouncycastle.pqc.legacy.bike.BIKEPublicKeyParameters;
import org.bouncycastle.pqc.crypto.cmce.CMCEParameters;
import org.bouncycastle.pqc.crypto.cmce.CMCEPublicKeyParameters;
import org.bouncycastle.pqc.crypto.crystals.dilithium.DilithiumParameters;
import org.bouncycastle.pqc.crypto.crystals.dilithium.DilithiumPublicKeyParameters;
import org.bouncycastle.pqc.crypto.faest.FaestParameters;
import org.bouncycastle.pqc.crypto.faest.FaestPublicKeyParameters;
import org.bouncycastle.pqc.crypto.qruov.QRUOVParameters;
import org.bouncycastle.pqc.crypto.qruov.QRUOVPublicKeyParameters;
import org.bouncycastle.pqc.crypto.falcon.FalconParameters;
import org.bouncycastle.pqc.crypto.falcon.FalconPublicKeyParameters;
import org.bouncycastle.pqc.crypto.frodo.FrodoParameters;
import org.bouncycastle.pqc.crypto.frodo.FrodoPublicKeyParameters;
import org.bouncycastle.pqc.crypto.haetae.HAETAEParameters;
import org.bouncycastle.pqc.crypto.haetae.HAETAEPublicKeyParameters;
import org.bouncycastle.pqc.crypto.hawk.HawkParameters;
import org.bouncycastle.pqc.crypto.hawk.HawkPublicKeyParameters;
import org.bouncycastle.pqc.crypto.hqc.HQCParameters;
import org.bouncycastle.pqc.crypto.hqc.HQCPublicKeyParameters;
import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters;
import org.bouncycastle.pqc.crypto.lms.LMSKeyParameters;
import org.bouncycastle.pqc.crypto.mayo.MayoParameters;
import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameters;
import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters;
import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters;
import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters;
import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters;
import org.bouncycastle.pqc.crypto.newhope.NHPublicKeyParameters;
import org.bouncycastle.pqc.crypto.ntru.NTRUParameters;
import org.bouncycastle.pqc.crypto.ntru.NTRUPublicKeyParameters;
import org.bouncycastle.pqc.crypto.ntruplus.NTRUPlusParameters;
import org.bouncycastle.pqc.crypto.ntruplus.NTRUPlusPublicKeyParameters;
import org.bouncycastle.pqc.crypto.ntruprime.NTRULPRimeParameters;
import org.bouncycastle.pqc.crypto.ntruprime.NTRULPRimePublicKeyParameters;
import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimeParameters;
import org.bouncycastle.pqc.crypto.ntruprime.SNTRUPrimePublicKeyParameters;
import org.bouncycastle.pqc.crypto.saber.SABERParameters;
import org.bouncycastle.pqc.crypto.saber.SABERPublicKeyParameters;
import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAParameters;
import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPublicKeyParameters;
import org.bouncycastle.pqc.crypto.snova.SnovaParameters;
import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters;
import org.bouncycastle.pqc.crypto.sqisign.SQIsignParameters;
import org.bouncycastle.pqc.crypto.sqisign.SQIsignPublicKeyParameters;
import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPublicKeyParameters;
import org.bouncycastle.pqc.crypto.xmss.XMSSMTParameters;
import org.bouncycastle.pqc.crypto.xmss.XMSSMTPublicKeyParameters;
import org.bouncycastle.pqc.crypto.xmss.XMSSParameters;
import org.bouncycastle.pqc.crypto.xmss.XMSSPublicKeyParameters;
import org.bouncycastle.pqc.legacy.bike.BIKEParameters;
import org.bouncycastle.pqc.legacy.bike.BIKEPublicKeyParameters;
import org.bouncycastle.pqc.legacy.picnic.PicnicParameters;
import org.bouncycastle.pqc.legacy.picnic.PicnicPublicKeyParameters;
import org.bouncycastle.pqc.legacy.rainbow.RainbowParameters;
import org.bouncycastle.pqc.legacy.rainbow.RainbowPublicKeyParameters;
import org.bouncycastle.pqc.legacy.sphincsplus.SPHINCSPlusParameters;
import org.bouncycastle.pqc.legacy.sphincsplus.SPHINCSPlusPublicKeyParameters;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;

/**
 * Factory to create asymmetric public key parameters for asymmetric ciphers from range of
 * ASN.1 encoded SubjectPublicKeyInfo objects.
 */
public class PublicKeyFactory
{
    private static Map converters = new HashMap();

    static
    {
        converters.put(PQCObjectIdentifiers.sphincs256, new SPHINCSConverter());
        converters.put(PQCObjectIdentifiers.newHope, new NHConverter());
        converters.put(PQCObjectIdentifiers.xmss, new XMSSConverter());
        converters.put(PQCObjectIdentifiers.xmss_mt, new XMSSMTConverter());
        converters.put(IsaraObjectIdentifiers.id_alg_xmss, new XMSSConverter());
        converters.put(IsaraObjectIdentifiers.id_alg_xmssmt, new XMSSMTConverter());
        converters.put(IANAObjectIdentifiers.id_alg_xmss_hashsig, new XMSSConverter());
        converters.put(IANAObjectIdentifiers.id_alg_xmssmt_hashsig, new XMSSMTConverter());
        converters.put(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig, new LMSConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus, new SPHINCSPlusConverter());

        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_128s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_128f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_128s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_128f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_192s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_192f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_192s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_192f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_256s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_256f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_256s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_128s_r3_simple, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_128f_r3_simple, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_192s_r3_simple, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_192f_r3_simple, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3_simple, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3_simple, new SPHINCSPlusConverter());

        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_128s, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_128f, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_128s, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_128f, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_192s, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_192f, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_192s, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_192f, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_256s, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_sha2_256f, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_256s, new SPHINCSPlusConverter());
        converters.put(BCObjectIdentifiers.sphincsPlus_shake_256f, new SPHINCSPlusConverter());
        converters.put(new ASN1ObjectIdentifier("1.3.9999.6.4.10"), new SPHINCSPlusConverter());

        converters.put(BCObjectIdentifiers.mceliece348864_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece348864f_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece460896_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece460896f_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece6688128_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece6688128f_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece6960119_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece6960119f_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece8192128_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.mceliece8192128f_r3, new CMCEConverter());
        converters.put(BCObjectIdentifiers.frodokem640aes, new FrodoConverter());
        converters.put(BCObjectIdentifiers.frodokem640shake, new FrodoConverter());
        converters.put(BCObjectIdentifiers.frodokem976aes, new FrodoConverter());
        converters.put(BCObjectIdentifiers.frodokem976shake, new FrodoConverter());
        converters.put(BCObjectIdentifiers.frodokem1344aes, new FrodoConverter());
        converters.put(BCObjectIdentifiers.frodokem1344shake, new FrodoConverter());
        converters.put(BCObjectIdentifiers.lightsaberkem128r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.saberkem128r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.firesaberkem128r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.lightsaberkem192r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.saberkem192r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.firesaberkem192r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.lightsaberkem256r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.saberkem256r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.firesaberkem256r3, new SABERConverter());
        converters.put(BCObjectIdentifiers.ulightsaberkemr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.usaberkemr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.ufiresaberkemr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.lightsaberkem90sr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.saberkem90sr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.firesaberkem90sr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.ulightsaberkem90sr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.usaberkem90sr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.ufiresaberkem90sr3, new SABERConverter());
        converters.put(BCObjectIdentifiers.picnicl1fs, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl1ur, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl3fs, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl3ur, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl5fs, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl5ur, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnic3l1, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnic3l3, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnic3l5, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl1full, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl3full, new PicnicConverter());
        converters.put(BCObjectIdentifiers.picnicl5full, new PicnicConverter());
        converters.put(BCObjectIdentifiers.ntruhps2048509, new NtruConverter());
        converters.put(BCObjectIdentifiers.ntruhps2048677, new NtruConverter());
        converters.put(BCObjectIdentifiers.ntruhps4096821, new NtruConverter());
        converters.put(BCObjectIdentifiers.ntruhps40961229, new NtruConverter());
        converters.put(BCObjectIdentifiers.ntruhrss701, new NtruConverter());
        converters.put(BCObjectIdentifiers.ntruhrss1373, new NtruConverter());
        converters.put(BCObjectIdentifiers.falcon_512, new FalconConverter());
        converters.put(BCObjectIdentifiers.falcon_1024, new FalconConverter());
        converters.put(BCObjectIdentifiers.old_falcon_512, new FalconConverter());
        converters.put(BCObjectIdentifiers.old_falcon_1024, new FalconConverter());
        converters.put(NISTObjectIdentifiers.id_alg_ml_kem_512, new MLKEMConverter());
        converters.put(NISTObjectIdentifiers.id_alg_ml_kem_768, new MLKEMConverter());
        converters.put(NISTObjectIdentifiers.id_alg_ml_kem_1024, new MLKEMConverter());
        converters.put(BCObjectIdentifiers.kyber512_aes, new MLKEMConverter());
        converters.put(BCObjectIdentifiers.kyber768_aes, new MLKEMConverter());
        converters.put(BCObjectIdentifiers.kyber1024_aes, new MLKEMConverter());
        converters.put(BCObjectIdentifiers.ntrulpr653, new NTRULPrimeConverter());
        converters.put(BCObjectIdentifiers.ntrulpr761, new NTRULPrimeConverter());
        converters.put(BCObjectIdentifiers.ntrulpr857, new NTRULPrimeConverter());
        converters.put(BCObjectIdentifiers.ntrulpr953, new NTRULPrimeConverter());
        converters.put(BCObjectIdentifiers.ntrulpr1013, new NTRULPrimeConverter());
        converters.put(BCObjectIdentifiers.ntrulpr1277, new NTRULPrimeConverter());
        converters.put(BCObjectIdentifiers.sntrup653, new SNTRUPrimeConverter());
        converters.put(BCObjectIdentifiers.sntrup761, new SNTRUPrimeConverter());
        converters.put(BCObjectIdentifiers.sntrup857, new SNTRUPrimeConverter());
        converters.put(BCObjectIdentifiers.sntrup953, new SNTRUPrimeConverter());
        converters.put(BCObjectIdentifiers.sntrup1013, new SNTRUPrimeConverter());
        converters.put(BCObjectIdentifiers.sntrup1277, new SNTRUPrimeConverter());
        converters.put(NISTObjectIdentifiers.id_ml_dsa_44, new MLDSAConverter());
        converters.put(NISTObjectIdentifiers.id_ml_dsa_65, new MLDSAConverter());
        converters.put(NISTObjectIdentifiers.id_ml_dsa_87, new MLDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512, new MLDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512, new MLDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512, new MLDSAConverter());
        converters.put(BCObjectIdentifiers.dilithium2, new DilithiumConverter());
        converters.put(BCObjectIdentifiers.dilithium3, new DilithiumConverter());
        converters.put(BCObjectIdentifiers.dilithium5, new DilithiumConverter());
        converters.put(BCObjectIdentifiers.bike128, new BIKEConverter());
        converters.put(BCObjectIdentifiers.bike192, new BIKEConverter());
        converters.put(BCObjectIdentifiers.bike256, new BIKEConverter());
        converters.put(BCObjectIdentifiers.hqc128, new HQCConverter());
        converters.put(BCObjectIdentifiers.hqc192, new HQCConverter());
        converters.put(BCObjectIdentifiers.hqc256, new HQCConverter());
        converters.put(BCObjectIdentifiers.rainbow_III_classic, new RainbowConverter());
        converters.put(BCObjectIdentifiers.rainbow_III_circumzenithal, new RainbowConverter());
        converters.put(BCObjectIdentifiers.rainbow_III_compressed, new RainbowConverter());
        converters.put(BCObjectIdentifiers.rainbow_V_classic, new RainbowConverter());
        converters.put(BCObjectIdentifiers.rainbow_V_circumzenithal, new RainbowConverter());
        converters.put(BCObjectIdentifiers.rainbow_V_compressed, new RainbowConverter());

        converters.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128s, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128f, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192s, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192f, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256s, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256f, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_shake_128s, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_shake_128f, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_shake_192s, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_shake_192f, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_shake_256s, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_slh_dsa_shake_256f, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, new SLHDSAConverter());
        converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256, new SLHDSAConverter());

        converters.put(BCObjectIdentifiers.mayo1, new MayoConverter());
        converters.put(BCObjectIdentifiers.mayo2, new MayoConverter());
        converters.put(BCObjectIdentifiers.mayo3, new MayoConverter());
        converters.put(BCObjectIdentifiers.mayo5, new MayoConverter());

        converters.put(BCObjectIdentifiers.snova_24_5_4_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_24_5_4_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_24_5_4_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_24_5_4_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_24_5_5_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_24_5_5_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_24_5_5_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_24_5_5_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_25_8_3_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_25_8_3_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_25_8_3_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_25_8_3_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_29_6_5_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_29_6_5_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_29_6_5_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_29_6_5_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_8_4_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_8_4_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_8_4_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_8_4_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_17_2_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_17_2_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_17_2_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_37_17_2_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_49_11_3_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_49_11_3_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_49_11_3_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_49_11_3_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_56_25_2_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_56_25_2_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_56_25_2_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_56_25_2_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_60_10_4_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_60_10_4_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_60_10_4_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_60_10_4_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_66_15_3_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_66_15_3_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_66_15_3_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_66_15_3_shake_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_75_33_2_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_75_33_2_ssk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_75_33_2_shake_esk, new SnovaConverter());
        converters.put(BCObjectIdentifiers.snova_75_33_2_shake_ssk, new SnovaConverter());

        converters.put(BCObjectIdentifiers.ntruplus768, new NTRUPlusConverter());
        converters.put(BCObjectIdentifiers.ntruplus864, new NTRUPlusConverter());
        converters.put(BCObjectIdentifiers.ntruplus1152, new NTRUPlusConverter());

        converters.put(BCObjectIdentifiers.aimer_128f, new AIMerConverter());
        converters.put(BCObjectIdentifiers.aimer_128s, new AIMerConverter());
        converters.put(BCObjectIdentifiers.aimer_192f, new AIMerConverter());
        converters.put(BCObjectIdentifiers.aimer_192s, new AIMerConverter());
        converters.put(BCObjectIdentifiers.aimer_256f, new AIMerConverter());
        converters.put(BCObjectIdentifiers.aimer_256s, new AIMerConverter());

        converters.put(BCObjectIdentifiers.hawk256, new HawkConverter());
        converters.put(BCObjectIdentifiers.hawk512, new HawkConverter());
        converters.put(BCObjectIdentifiers.hawk1024, new HawkConverter());

        converters.put(BCObjectIdentifiers.faest_128s, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_128f, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_192s, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_192f, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_256s, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_256f, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_em_128s, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_em_128f, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_em_192s, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_em_192f, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_em_256s, new FaestConverter());
        converters.put(BCObjectIdentifiers.faest_em_256f, new FaestConverter());
        converters.put(BCObjectIdentifiers.qruov1q127L3v156m54, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov1q31L3v165m60, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov1q31L10v600m70, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov1q7L10v740m100, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov3q127L3v228m78, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov3q31L3v246m87, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov3q31L10v890m100, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov3q7L10v1100m140, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov5q127L3v306m105, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov5q31L3v324m114, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov5q31L10v1120m120, new QRUOVConverter());
        converters.put(BCObjectIdentifiers.qruov5q7L10v1490m190, new QRUOVConverter());

        converters.put(BCObjectIdentifiers.sqisign_lvl1, new SQIsignConverter());
        converters.put(BCObjectIdentifiers.sqisign_lvl3, new SQIsignConverter());
        converters.put(BCObjectIdentifiers.sqisign_lvl5, new SQIsignConverter());

        converters.put(BCObjectIdentifiers.haetae2, new HaetaeConverter());
        converters.put(BCObjectIdentifiers.haetae3, new HaetaeConverter());
        converters.put(BCObjectIdentifiers.haetae5, new HaetaeConverter());

        converters.put(BCObjectIdentifiers.mqom2_cat1_gf2_fast_r3,     new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf2_fast_r5,     new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf2_short_r3,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf2_short_r5,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf16_fast_r3,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf16_fast_r5,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf16_short_r3,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf16_short_r5,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf256_fast_r3,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf256_fast_r5,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf256_short_r3,  new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat1_gf256_short_r5,  new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf2_fast_r3,     new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf2_fast_r5,     new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf2_short_r3,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf2_short_r5,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf16_fast_r3,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf16_fast_r5,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf16_short_r3,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf16_short_r5,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf256_fast_r3,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf256_fast_r5,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf256_short_r3,  new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat3_gf256_short_r5,  new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf2_fast_r3,     new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf2_fast_r5,     new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf2_short_r3,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf2_short_r5,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf16_fast_r3,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf16_fast_r5,    new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf16_short_r3,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf16_short_r5,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf256_fast_r3,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf256_fast_r5,   new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf256_short_r3,  new MQOMConverter());
        converters.put(BCObjectIdentifiers.mqom2_cat5_gf256_short_r5,  new MQOMConverter());

        converters.put(BCObjectIdentifiers.uov_Is_classic,   new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_Is_pkc,       new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_Is_pkc_skc,   new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_Ip_classic,   new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_Ip_pkc,       new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_Ip_pkc_skc,   new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_III_classic,  new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_III_pkc,      new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_III_pkc_skc,  new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_V_classic,    new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_V_pkc,        new UOVConverter());
        converters.put(BCObjectIdentifiers.uov_V_pkc_skc,    new UOVConverter());

        converters.put(BCObjectIdentifiers.sdith_hypercube_cat1_gf256, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_hypercube_cat3_gf256, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_hypercube_cat5_gf256, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_hypercube_cat1_p251, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_hypercube_cat3_p251, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_hypercube_cat5_p251, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_threshold_cat1_gf256, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_threshold_cat3_gf256, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_threshold_cat5_gf256, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_threshold_cat1_p251, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_threshold_cat3_p251, new SDitHConverter());
        converters.put(BCObjectIdentifiers.sdith_threshold_cat5_p251, new SDitHConverter());
    }

    /**
     * Create a public key from a SubjectPublicKeyInfo encoding
     *
     * @param keyInfoData the SubjectPublicKeyInfo encoding
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(byte[] keyInfoData)
        throws IOException
    {
        if (keyInfoData == null)
        {
            throw new IllegalArgumentException("keyInfoData array null");
        }
        if (keyInfoData.length == 0)
        {
            throw new IllegalArgumentException("keyInfoData array empty");
        }
        return createKey(SubjectPublicKeyInfo.getInstance(ASN1Primitive.fromByteArray(keyInfoData)));
    }

    /**
     * Create a public key from a SubjectPublicKeyInfo encoding read from a stream
     *
     * @param inStr the stream to read the SubjectPublicKeyInfo encoding from
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(InputStream inStr)
        throws IOException
    {
        return createKey(SubjectPublicKeyInfo.getInstance(new ASN1InputStream(inStr).readObject()));
    }

    /**
     * Create a public key from the passed in SubjectPublicKeyInfo
     *
     * @param keyInfo the SubjectPublicKeyInfo containing the key data
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo)
        throws IOException
    {
        if (keyInfo == null)
        {
            throw new IllegalArgumentException("keyInfo argument null");
        }
        return createKey(keyInfo, null);
    }

    /**
     * Create a public key from the passed in SubjectPublicKeyInfo
     *
     * @param keyInfo       the SubjectPublicKeyInfo containing the key data
     * @param defaultParams default parameters that might be needed.
     * @return the appropriate key parameter
     * @throws IOException on an error decoding the key
     */
    public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Object defaultParams)
        throws IOException
    {
        if (keyInfo == null)
        {
            throw new IllegalArgumentException("keyInfo argument null");
        }

        AlgorithmIdentifier algId = keyInfo.getAlgorithm();
        SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algId.getAlgorithm());

        if (converter != null)
        {
            return converter.getPublicKeyParameters(keyInfo, defaultParams);
        }
        else
        {
            throw new IOException("algorithm identifier in public key not recognised: " + algId.getAlgorithm());
        }
    }

    private static abstract class SubjectPublicKeyInfoConverter
    {
        abstract AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException;
    }
    
    private static class SPHINCSConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            return new SPHINCSPublicKeyParameters(keyInfo.getPublicKeyData().getBytes(),
                Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(keyInfo.getAlgorithm().getParameters())));
        }
    }

    private static class NHConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            return new NHPublicKeyParameters(keyInfo.getPublicKeyData().getBytes());
        }
    }

    private static class XMSSConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            XMSSKeyParams keyParams = XMSSKeyParams.getInstance(keyInfo.getAlgorithm().getParameters());

            if (keyParams != null)
            {
                ASN1ObjectIdentifier treeDigest = keyParams.getTreeDigest().getAlgorithm();
                XMSSPublicKey xmssPublicKey = XMSSPublicKey.getInstance(keyInfo.parsePublicKey());

                return new XMSSPublicKeyParameters
                    .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest)))
                    .withPublicSeed(xmssPublicKey.getPublicSeed())
                    .withRoot(xmssPublicKey.getRoot()).build();
            }
            else
            {
                // RFC 9802 carries the raw RFC 8391 key; the legacy draft form wrapped it in an OCTET STRING.
                byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();
                ASN1OctetString data = Utils.parseOctetData(keyEnc);

                if (data != null)
                {
                    keyEnc = data.getOctets();
                }

                if (keyEnc.length < 4)
                {
                    throw new IOException("XMSS public key data too short");
                }

                XMSSParameters parameters = XMSSParameters.lookupByOID(Pack.bigEndianToInt(keyEnc, 0));
                if (parameters == null)
                {
                    throw new IOException("unknown XMSS public key OID: " + Pack.bigEndianToInt(keyEnc, 0));
                }

                try
                {
                    return new XMSSPublicKeyParameters
                        .Builder(parameters)
                        .withPublicKey(keyEnc).build();
                }
                catch (IllegalArgumentException e)
                {
                    throw new IOException("malformed XMSS public key: " + e.getMessage());
                }
            }
        }
    }

    private static class XMSSMTConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            XMSSMTKeyParams keyParams = XMSSMTKeyParams.getInstance(keyInfo.getAlgorithm().getParameters());

            if (keyParams != null)
            {
                ASN1ObjectIdentifier treeDigest = keyParams.getTreeDigest().getAlgorithm();

                XMSSPublicKey xmssMtPublicKey = XMSSPublicKey.getInstance(keyInfo.parsePublicKey());

                return new XMSSMTPublicKeyParameters
                    .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest)))
                    .withPublicSeed(xmssMtPublicKey.getPublicSeed())
                    .withRoot(xmssMtPublicKey.getRoot()).build();
            }
            else
            {
                // RFC 9802 carries the raw RFC 8391 key; the legacy draft form wrapped it in an OCTET STRING.
                byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();
                ASN1OctetString data = Utils.parseOctetData(keyEnc);

                if (data != null)
                {
                    keyEnc = data.getOctets();
                }

                if (keyEnc.length < 4)
                {
                    throw new IOException("XMSS^MT public key data too short");
                }

                XMSSMTParameters parameters = XMSSMTParameters.lookupByOID(Pack.bigEndianToInt(keyEnc, 0));
                if (parameters == null)
                {
                    throw new IOException("unknown XMSS^MT public key OID: " + Pack.bigEndianToInt(keyEnc, 0));
                }

                try
                {
                    return new XMSSMTPublicKeyParameters
                        .Builder(parameters)
                        .withPublicKey(keyEnc).build();
                }
                catch (IllegalArgumentException e)
                {
                    throw new IOException("malformed XMSS^MT public key: " + e.getMessage());
                }
            }
        }
    }

    private static class LMSConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();
            ASN1OctetString data = (ASN1OctetString)Utils.parseData(keyEnc);

            if (data != null)
            {
                return getLmsKeyParameters(data.getOctets());
            }

            return getLmsKeyParameters(keyEnc);
        }

        private LMSKeyParameters getLmsKeyParameters(byte[] keyEnc)
            throws IOException
        {
            return HSSPublicKeyParameters.getInstance(keyEnc);
        }
    }

    private static class SPHINCSPlusConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            try
            {
                byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

                SPHINCSPlusParameters spParams = Utils.sphincsPlusParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new SPHINCSPlusPublicKeyParameters(spParams, Arrays.copyOfRange(keyEnc, 4, keyEnc.length));
            }
            catch (Exception e)
            {
                byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();

                SPHINCSPlusParameters spParams = Utils.sphincsPlusParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new SPHINCSPlusPublicKeyParameters(spParams, keyEnc);
            }
        }
    }

    private static class CMCEConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            try
            {
                byte[] keyEnc = CMCEPublicKey.getInstance(keyInfo.parsePublicKey()).getT();

                CMCEParameters spParams = Utils.mcElieceParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new CMCEPublicKeyParameters(spParams, keyEnc);
            }
            catch (Exception e)
            {
                byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();

                CMCEParameters spParams = Utils.mcElieceParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new CMCEPublicKeyParameters(spParams, keyEnc);
            }
        }
    }

    private static class SABERConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(
                ASN1Sequence.getInstance(keyInfo.parsePublicKey()).getObjectAt(0)).getOctets();

            SABERParameters saberParams = Utils.saberParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new SABERPublicKeyParameters(saberParams, keyEnc);
        }
    }

    private static class FrodoConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            FrodoParameters fParams = Utils.frodoParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new FrodoPublicKeyParameters(fParams, keyEnc);
        }
    }

    private static class PicnicConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            PicnicParameters picnicParams = Utils.picnicParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new PicnicPublicKeyParameters(picnicParams, keyEnc);
        }
    }

    private static class NtruConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();
            ASN1OctetString data = Utils.parseOctetData(keyEnc);

            if (data != null)
            {
                return getNtruPublicKeyParameters(keyInfo, data.getOctets());
            }

            return getNtruPublicKeyParameters(keyInfo, keyEnc);
        }

        private NTRUPublicKeyParameters getNtruPublicKeyParameters(SubjectPublicKeyInfo keyInfo, byte[] keyEnc)
        {

            NTRUParameters ntruParams = Utils.ntruParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new NTRUPublicKeyParameters(ntruParams, keyEnc);
        }
    }

    private static class FalconConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();
//            FalconPublicKey falconPublicKey = FalconPublicKey.getInstance(keyInfo.parsePublicKey());

            FalconParameters falconParams = Utils.falconParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new FalconPublicKeyParameters(falconParams, Arrays.copyOfRange(keyEnc, 1, keyEnc.length));

        }
    }

    static class MLKEMConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            MLKEMParameters parameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                // we're a raw encoding
            return new MLKEMPublicKeyParameters(parameters, keyInfo.getPublicKeyData().getOctets());
        }

        static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters parameters, ASN1BitString publicKeyData)
        {
            try
            {
                ASN1Primitive obj = ASN1Primitive.fromByteArray(publicKeyData.getOctets());
                if (obj instanceof ASN1Sequence)
                {
                    ASN1Sequence keySeq = ASN1Sequence.getInstance(obj);

                    return new MLKEMPublicKeyParameters(parameters,
                        ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(),
                        ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets());
                }
                else
                {
                    byte[] encKey = ASN1OctetString.getInstance(obj).getOctets();

                    return new MLKEMPublicKeyParameters(parameters, encKey);
                }
            }
            catch (Exception e)
            {
                // we're a raw encoding
                return new MLKEMPublicKeyParameters(parameters, publicKeyData.getOctets());
            }
        }
    }

    private static class NTRULPrimeConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            NTRULPRimeParameters ntruLPRimeParams = Utils.ntrulprimeParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new NTRULPRimePublicKeyParameters(ntruLPRimeParams, keyEnc);
        }
    }

    private static class SNTRUPrimeConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            SNTRUPrimeParameters ntruLPRimeParams = Utils.sntruprimeParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new SNTRUPrimePublicKeyParameters(ntruLPRimeParams, keyEnc);
        }
    }

    static class DilithiumConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            DilithiumParameters dilithiumParams = Utils.dilithiumParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return getPublicKeyParams(dilithiumParams, keyInfo.getPublicKeyData());
        }

        static DilithiumPublicKeyParameters getPublicKeyParams(DilithiumParameters dilithiumParams, ASN1BitString publicKeyData)
        {
            try
            {
                ASN1Primitive obj = ASN1Primitive.fromByteArray(publicKeyData.getOctets());
                if (obj instanceof ASN1Sequence)
                {
                    ASN1Sequence keySeq = ASN1Sequence.getInstance(obj);

                    return new DilithiumPublicKeyParameters(dilithiumParams,
                        ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(),
                        ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets());
                }
                else
                {
                    byte[] encKey = ASN1OctetString.getInstance(obj).getOctets();

                    return new DilithiumPublicKeyParameters(dilithiumParams, encKey);
                }
            }
            catch (Exception e)
            {
                // we're a raw encoding
                return new DilithiumPublicKeyParameters(dilithiumParams, publicKeyData.getOctets());
            }
        }
    }

    static class MLDSAConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            MLDSAParameters dilithiumParams = Utils.mldsaParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return getPublicKeyParams(dilithiumParams, keyInfo.getPublicKeyData());
        }

        static MLDSAPublicKeyParameters getPublicKeyParams(MLDSAParameters mlDsaParams, ASN1BitString publicKeyData)
        {
            try
            {
                ASN1Primitive obj = ASN1Primitive.fromByteArray(publicKeyData.getOctets());
                if (obj instanceof ASN1Sequence)
                {
                    ASN1Sequence keySeq = ASN1Sequence.getInstance(obj);

                    return new MLDSAPublicKeyParameters(mlDsaParams,
                        ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(),
                        ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets());
                }
                else
                {
                    byte[] encKey = ASN1OctetString.getInstance(obj).getOctets();

                    return new MLDSAPublicKeyParameters(mlDsaParams, encKey);
                }
            }
            catch (Exception e)
            {
                // we're a raw encoding
                return new MLDSAPublicKeyParameters(mlDsaParams, publicKeyData.getOctets());
            }
        }
    }

    private static class BIKEConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            try
            {
                byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

                BIKEParameters bikeParams = Utils.bikeParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new BIKEPublicKeyParameters(bikeParams, keyEnc);
            }
            catch (Exception e)
            {
                byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();

                BIKEParameters bikeParams = Utils.bikeParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new BIKEPublicKeyParameters(bikeParams, keyEnc);
            }
        }
    }

    private static class HQCConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            try
            {
                byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

                HQCParameters hqcParams = Utils.hqcParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new HQCPublicKeyParameters(hqcParams, keyEnc);
            }
            catch (Exception e)
            {
                // raw encoding
                byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();

                HQCParameters hqcParams = Utils.hqcParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new HQCPublicKeyParameters(hqcParams, keyEnc);
            }
        }
    }

    private static class SLHDSAConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            try
            {
                byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

                SLHDSAParameters spParams = Utils.slhdsaParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new SLHDSAPublicKeyParameters(spParams, Arrays.copyOfRange(keyEnc, 4, keyEnc.length));
            }
            catch (Exception e)
            {
                byte[] keyEnc = keyInfo.getPublicKeyData().getOctets();

                SLHDSAParameters spParams = Utils.slhdsaParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

                return new SLHDSAPublicKeyParameters(spParams, keyEnc);
            }
        }
    }

    private static class RainbowConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            RainbowParameters rainbowParams = Utils.rainbowParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new RainbowPublicKeyParameters(rainbowParams, keyEnc);
        }
    }

    private static class MayoConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            MayoParameters mayoParams = Utils.mayoParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new MayoPublicKeyParameters(mayoParams, keyEnc);
        }
    }

    private static class SnovaConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            SnovaParameters snovaParams = Utils.snovaParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new SnovaPublicKeyParameters(snovaParams, keyEnc);
        }
    }

    private static class SQIsignConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            SQIsignParameters sqisignParams = Utils.sqisignParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new SQIsignPublicKeyParameters(sqisignParams, keyEnc);
        }
    }

    private static class FaestConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            FaestParameters faestParams = Utils.faestParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new FaestPublicKeyParameters(faestParams, keyEnc);
        }
    }

    private static class QRUOVConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            QRUOVParameters qruovParams = Utils.qruovParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new QRUOVPublicKeyParameters(qruovParams, keyEnc);
        }
    }

    private static class HaetaeConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            HAETAEParameters haetaeParams = Utils.haetaeParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new HAETAEPublicKeyParameters(haetaeParams, keyEnc);
        }
    }

    private static class NTRUPlusConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            NTRUPlusParameters ntruPlusParams = Utils.ntruPlusParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new NTRUPlusPublicKeyParameters(ntruPlusParams, keyEnc);
        }
    }

    private static class AIMerConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            AIMerParameters aimerParams = Utils.aimerParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new AIMerPublicKeyParameters(aimerParams, keyEnc);
        }
    }

    private static class HawkConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets();

            HawkParameters hawkParams = Utils.hawkParamsLookup(keyInfo.getAlgorithm().getAlgorithm());

            return new HawkPublicKeyParameters(hawkParams, keyEnc, 0, keyEnc.length);
        }
    }

    static class MQOMConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            MQOMParameters mqomParams = Utils.mqomParamsLookup(keyInfo.getAlgorithm().getAlgorithm());
            return new MQOMPublicKeyParameters(mqomParams, keyInfo.getPublicKeyData().getOctets());
        }
    }

    static class UOVConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            UOVParameters params = Utils.uovParamsLookup(keyInfo.getAlgorithm().getAlgorithm());
            return new UOVPublicKeyParameters(params, keyInfo.getPublicKeyData().getOctets());
        }
    }

    static class SDitHConverter
        extends SubjectPublicKeyInfoConverter
    {
        AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams)
            throws IOException
        {
            SDitHParameters params = Utils.sdithParamsLookup(keyInfo.getAlgorithm().getAlgorithm());
            return new SDitHPublicKeyParameters(params, keyInfo.getPublicKeyData().getOctets());
        }
    }
}
