use crate::{AlgorithmIdentifier, AlgorithmIdentifierParameters, oids};
use oid::ObjectIdentifier;
use picky_asn1::bit_string::BitString;
use picky_asn1::wrapper::{BitStringAsn1, BitStringAsn1Container, IntegerAsn1, OctetStringAsn1};
use serde::{Deserialize, Serialize, de, ser};
use std::fmt;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PublicKey {
    Rsa(EncapsulatedRsaPublicKey),
    Ec(EncapsulatedEcPoint),
    /// Used For Ed25519, Ed448, X25519, and X448 keys
    Ed(EncapsulatedEcPoint),
    Mldsa(EncapsulatedMldsaPublicKey),
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct RsaPublicKey {
    pub modulus: IntegerAsn1,         // n
    pub public_exponent: IntegerAsn1, // e
}

pub type EncapsulatedRsaPublicKey = BitStringAsn1Container<RsaPublicKey>;

pub type EcPoint = OctetStringAsn1;

pub type EncapsulatedEcPoint = BitStringAsn1;

pub type EncapsulatedMldsaPublicKey = BitStringAsn1;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SubjectPublicKeyInfo {
    pub algorithm: AlgorithmIdentifier,
    pub subject_public_key: PublicKey,
}

impl SubjectPublicKeyInfo {
    pub fn new_rsa_key(modulus: IntegerAsn1, public_exponent: IntegerAsn1) -> Self {
        Self {
            algorithm: AlgorithmIdentifier::new_rsa_encryption(),
            subject_public_key: PublicKey::Rsa(
                RsaPublicKey {
                    modulus,
                    public_exponent,
                }
                .into(),
            ),
        }
    }

    pub fn new_ec_key(curve: ObjectIdentifier, point: BitString) -> Self {
        Self {
            algorithm: AlgorithmIdentifier::new_elliptic_curve(curve.into()),
            subject_public_key: PublicKey::Ec(point.into()),
        }
    }

    pub fn new_ed_key(curve: ObjectIdentifier, point: BitString) -> Self {
        Self {
            algorithm: AlgorithmIdentifier::new_unchecked(curve, AlgorithmIdentifierParameters::None),
            subject_public_key: PublicKey::Ed(point.into()),
        }
    }
}

impl ser::Serialize for SubjectPublicKeyInfo {
    fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
    where
        S: ser::Serializer,
    {
        use ser::SerializeSeq;

        let mut seq = serializer.serialize_seq(Some(2))?;
        seq.serialize_element(&self.algorithm)?;

        match &self.subject_public_key {
            PublicKey::Rsa(key) => seq.serialize_element(key)?,
            PublicKey::Ec(key) => seq.serialize_element(key)?,
            PublicKey::Ed(key) => seq.serialize_element(key)?,
            PublicKey::Mldsa(key) => seq.serialize_element(key)?,
        }

        seq.end()
    }
}

impl<'de> de::Deserialize<'de> for SubjectPublicKeyInfo {
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct Visitor;

        impl<'de> de::Visitor<'de> for Visitor {
            type Value = SubjectPublicKeyInfo;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a valid DER-encoded subject public key info")
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: de::SeqAccess<'de>,
            {
                let algorithm: AlgorithmIdentifier = seq_next_element!(seq, AlgorithmIdentifier, "algorithm oid");

                let subject_public_key = match Into::<String>::into(algorithm.oid()).as_str() {
                    oids::RSA_ENCRYPTION => PublicKey::Rsa(seq_next_element!(seq, SubjectPublicKeyInfo, "rsa key")),
                    oids::EC_PUBLIC_KEY => {
                        PublicKey::Ec(seq_next_element!(seq, SubjectPublicKeyInfo, "elliptic curves key"))
                    }
                    oids::ED25519 | oids::X25519 => {
                        PublicKey::Ed(seq_next_element!(seq, SubjectPublicKeyInfo, "curve25519 key"))
                    }
                    oids::ED448 | oids::X448 => {
                        PublicKey::Ed(seq_next_element!(seq, SubjectPublicKeyInfo, "curve448 key"))
                    }
                    oids::ID_MLDSA_44 | oids::ID_MLDSA_65 | oids::ID_MLDSA_87 => {
                        PublicKey::Mldsa(seq_next_element!(seq, SubjectPublicKeyInfo, "mldsa key"))
                    }
                    _ => {
                        return Err(serde_invalid_value!(
                            SubjectPublicKeyInfo,
                            "unsupported algorithm (unknown oid)",
                            "a supported algorithm"
                        ));
                    }
                };

                Ok(SubjectPublicKeyInfo {
                    algorithm,
                    subject_public_key,
                })
            }
        }

        deserializer.deserialize_seq(Visitor)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use base64::Engine as _;
    use base64::engine::general_purpose;
    use crypto_bigint::BoxedUint;

    #[test]
    fn rsa_subject_public_key_info() {
        let encoded = general_purpose::STANDARD
            .decode(
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsiLoIx\
             mXaZAFRBKtHYZhiF8m+pYR+xGIpupvsdDEvKO92D6fIccgVLIW6p6sSNk\
             oXx5J6KDSMbA/chy5M6pRvJkaCXCI4zlCPMYvPhI8OxN3RYPfdQTLpgPy\
             wrlfdn2CAum7o4D8nR4NJacB3NfPnS9tsJ2L3p5iHviuTB4xm03IKmPPq\
             saJy+nXUFC1XS9E/PseVHRuNvKa7WmlwSZngQzKAVSIwqpgCc+oP1pKEe\
             J0M3LHFo8ao5SuzhfXUIGrPnkUKEE3m7B0b8xXZfP1N6ELoonWDK+RMgY\
             IBaZdgBhPfHxF8KfTHvSzcUzWZojuR+ynaFL9AJK+8RiXnB4CJwIDAQAB",
            )
            .expect("invalid base64");

        // RSA algorithm identifier

        let algorithm = AlgorithmIdentifier::new_rsa_encryption();
        check_serde!(algorithm: AlgorithmIdentifier in encoded[4..19]);

        // RSA modulus and public exponent

        let modulus = IntegerAsn1::from_bytes_be_signed(vec![
            0x00, 0xb2, 0x22, 0xe8, 0x23, 0x19, 0x97, 0x69, 0x90, 0x5, 0x44, 0x12, 0xad, 0x1d, 0x86, 0x61, 0x88, 0x5f,
            0x26, 0xfa, 0x96, 0x11, 0xfb, 0x11, 0x88, 0xa6, 0xea, 0x6f, 0xb1, 0xd0, 0xc4, 0xbc, 0xa3, 0xbd, 0xd8, 0x3e,
            0x9f, 0x21, 0xc7, 0x20, 0x54, 0xb2, 0x16, 0xea, 0x9e, 0xac, 0x48, 0xd9, 0x28, 0x5f, 0x1e, 0x49, 0xe8, 0xa0,
            0xd2, 0x31, 0xb0, 0x3f, 0x72, 0x1c, 0xb9, 0x33, 0xaa, 0x51, 0xbc, 0x99, 0x1a, 0x9, 0x70, 0x88, 0xe3, 0x39,
            0x42, 0x3c, 0xc6, 0x2f, 0x3e, 0x12, 0x3c, 0x3b, 0x13, 0x77, 0x45, 0x83, 0xdf, 0x75, 0x4, 0xcb, 0xa6, 0x3,
            0xf2, 0xc2, 0xb9, 0x5f, 0x76, 0x7d, 0x82, 0x2, 0xe9, 0xbb, 0xa3, 0x80, 0xfc, 0x9d, 0x1e, 0xd, 0x25, 0xa7,
            0x1, 0xdc, 0xd7, 0xcf, 0x9d, 0x2f, 0x6d, 0xb0, 0x9d, 0x8b, 0xde, 0x9e, 0x62, 0x1e, 0xf8, 0xae, 0x4c, 0x1e,
            0x31, 0x9b, 0x4d, 0xc8, 0x2a, 0x63, 0xcf, 0xaa, 0xc6, 0x89, 0xcb, 0xe9, 0xd7, 0x50, 0x50, 0xb5, 0x5d, 0x2f,
            0x44, 0xfc, 0xfb, 0x1e, 0x54, 0x74, 0x6e, 0x36, 0xf2, 0x9a, 0xed, 0x69, 0xa5, 0xc1, 0x26, 0x67, 0x81, 0xc,
            0xca, 0x1, 0x54, 0x88, 0xc2, 0xaa, 0x60, 0x9, 0xcf, 0xa8, 0x3f, 0x5a, 0x4a, 0x11, 0xe2, 0x74, 0x33, 0x72,
            0xc7, 0x16, 0x8f, 0x1a, 0xa3, 0x94, 0xae, 0xce, 0x17, 0xd7, 0x50, 0x81, 0xab, 0x3e, 0x79, 0x14, 0x28, 0x41,
            0x37, 0x9b, 0xb0, 0x74, 0x6f, 0xcc, 0x57, 0x65, 0xf3, 0xf5, 0x37, 0xa1, 0xb, 0xa2, 0x89, 0xd6, 0xc, 0xaf,
            0x91, 0x32, 0x6, 0x8, 0x5, 0xa6, 0x5d, 0x80, 0x18, 0x4f, 0x7c, 0x7c, 0x45, 0xf0, 0xa7, 0xd3, 0x1e, 0xf4,
            0xb3, 0x71, 0x4c, 0xd6, 0x66, 0x88, 0xee, 0x47, 0xec, 0xa7, 0x68, 0x52, 0xfd, 0x0, 0x92, 0xbe, 0xf1, 0x18,
            0x97, 0x9c, 0x1e, 0x2, 0x27,
        ]);
        check_serde!(modulus: IntegerAsn1 in encoded[28..289]);

        let public_exponent: IntegerAsn1 = BoxedUint::from(65537u32)
            .to_be_bytes_trimmed_vartime()
            .into_vec()
            .into();
        check_serde!(public_exponent: IntegerAsn1 in encoded[289..294]);

        // RSA public key

        let subject_public_key: EncapsulatedRsaPublicKey = RsaPublicKey {
            modulus,
            public_exponent,
        }
        .into();
        check_serde!(subject_public_key: EncapsulatedRsaPublicKey in encoded[19..294]);

        // full encode / decode

        let info = SubjectPublicKeyInfo {
            algorithm,
            subject_public_key: PublicKey::Rsa(subject_public_key),
        };
        check_serde!(info: SubjectPublicKeyInfo in encoded);
    }

    #[test]
    fn mldsa_subject_public_key_info() {
        let encoded = general_purpose::STANDARD
            .decode(
                "MIIHsjALBglghkgBZQMEAxIDggehAP+0XbIbRe0Q/4BxA/8364At4Au/YOfj+3Yl\
                MVtAWCFTZD2Am3vefxOLOx6yHqLyL47/X1k7KalVeCZAwvVBMwXnhBKtD3j6FRhY\
                GkdDkmmqFxfxUz1OsuokW/NKufV+ErSFMLS47ooQxGWUtSStzVCd7dIA8LYhn9dz\
                +a4gocqyxhYZBfbms/tAcCtKbd0Q9kUNYvWvgHx/fNE3v0RJh83qwA4u0pRrl0bA\
                qjjA3DXp/+QTayAUgM7o68r8njrD4ql3OMB0ErL7nHrIjiAHkPCJuDN6nwIkXbx0\
                ikmKWMbyydb5cK43Z2/HLZAr6sMTiBD8wz0HYD/zpZw8+bRN4Snnrc7Iypi5jH1Y\
                tZlc8suTVdnnvlSCFE+c73DZMhmhGbz16b5PQQTe64DC2PH9Brq7FW0IPID24igP\
                ISAlkQsnrQPKTSjw8xKXojcukojyg19bWZa/decA/te3z/aJccu1mqPWGaei56Hp\
                6B+2pYTQgbbyp09txNgaCtQTi2xJzOB5SjsvwWWkrIABiojGmVHyqm1gmUtfuvU7\
                MNbuI7ple1AXpG2QRz7wqBCc5por4zfKfpljor3fjTIHJdIkq8wFf4TNokheHs1u\
                00y6qCQ3WwR7dTUZZ43Pc8MYwODSH1qa8JhLgRwBSr2d+30JqnkeEZ0PorPxWyYJ\
                ggbUJflotjGZ7D++xyR2G12nI4ZC5pl09fenKYuF0yC/Psf53378wpqnCz5LoGmy\
                Q0FMSk0WkO9Es52lpD4KBbWT8OIDH/uwEOe+AX0EymrRl5SwDKD9asPHJdGcb4Kd\
                V74cc/z7v4O84I5lnEXcJ97PXkSvuR/HDgw/ho6u/EYGcQHj/OCfALYkKM0y9o/K\
                3ww5CTvzAYZE2+TsKIMqfM4gwVNCdg9aEnHqVwjncNa/qRJyrvDwFQaZIkxznd8j\
                1S9CHhfEQLTuH4WtGZm6Fn6hCfxXtWouHuFLZxPT2D6V4Rq1TNNA9vFVoPcib5Jn\
                U3SHCCEud8bXCvcui9CAepc6G80WnfwQC3l2hOIR9BgEc40rJw0d2+GYudzXiCFw\
                1+oGkib/YMXQ2fu1D7AGH4l3dIV5hFgv8nSAFpQ4PePmLTUen3wq+09ykb4/4M9b\
                kwmJzB0xtbII5DZ7jShtVPOWblcxMjKSN99WKnP8uy1s6kXw7gzXMZ6r80gIzsCs\
                i0hiOxgZIZVVVc8oApJuyVu0QkDw0wlI3u/2vEFHHhesZG00bza/Bs9PK8fjIzcf\
                9zFP6JzvTxXCcJU3oIphljgssQd591s31jOAQk2jNb18sXesHD+YgmmS9KgnP8sa\
                GpYMD7ru+5sGsV8rVksl1PJuHT3k7ejbTUSHmLDLdlpFqFmYpA5beBslFQAZSYDM\
                CJT4JIZNo9h0mssIVtlJAavG5D0wotZEnJfLpHEYDwwyuhyEToWmnEUZoZu5CxiH\
                MmvC+PPLNA7zLgdYgCDbEgJdGZag/Tv3C7bJCr+SX4EjLIxdT8dlHehF9bsRSwj9\
                dISSwXBBl8B0zcRBmqsS453nx1EvMOSfhc3IVOWNWpkIxIcUJ/mYyVih5W78mHGu\
                8AbpxNzPmOgwSiTV2Rax72c5SPzC3zULrJMvh3US9yyrHEPYcAgAzLbk/6XYIycq\
                l5VfmNBqDlgeyAyEmuYuUft0kEKUmzLPsJ4AmX2H4Gvo/o8BQTcQ02iit/j5OzWV\
                dKvpL3jrSCsChz+uPmyeA5m9B6X54Adi+ndDX0jHfPZQRBOYyctLXFdhUB8Svwba\
                0Fq/qvWT0jtc7HxOdlCQZFaWEwSDKj8bjyJaCAGpRmr3+MD/fFBDyHxcOJS3uMvv\
                txXJAhEtVSHt5a4BO9STVMZ+UuIu8oECnTD9pz10zZ1yEflER79KDoUz435HVdfe\
                yvEwxN0qhwIpX7GMY/MJbCnkFiUwPaWT/elRrp6I2NSpwurJ4cn2QiXEDEYr1c83\
                pqDW+kMUi9Jju/EgmPuTRsu7EE0o10nE59/cjnG8SF+KEa22QXoPx8/FrqkeaEgE\
                jAhAUbgZikzqSMN6m77feOpXYTOMiL7Ez2Hdcugu49bkWqrRpx39mtAvIhclurYK\
                yI9eoAJRqfxIjvV7hlDt9sNFzqO7Hybl+MGxW43HGumFbuvn4rT1VQS1ndg0Ycdx\
                NLgWwldhLdqv03+iO3wCDEdFsg76NCqYNRf9l2DRZoQ6b5luTcpq1Nhnpi+FKulj\
                AEY5ePBm171I7odWhnzaWMBBs2RT2KxuCXUJpZTMesQNXPvE03tXtzjH0M1ruRpW\
                gfZSn6HJAYixsRZR8cXtZvGuvIwcrtgQnALShONnhUwDGhMWZlOB6druxFc99FLh\
                lk9Z88mboh6VzmqPIqSleRKqN4QETq0L2TVT/vasjYifGX5dHW7EvAmySmOOW04e\
                cScxkoV+lfg14chFazX7NMY/pqb3j3qqxJf/nrfGRjIF3B10SsXgYl84i1HRNNm6\
                bj54VCkTLKkarMYnAyUCJoB3+rwTD7K7YVtCH+qTYYnaPKcw4acnhpJNJC2TtLaY\
                q1gZYIE8zjFGPJSxmkWLW9Sc00mevzP8kwB7CmgxCnyQNAkX/V6QxyapQf9Yv8Ns\
                cnERZ5pe",
            )
            .expect("invalid base64");

        // MLDSA algorithm identifier
        let algorithm = AlgorithmIdentifier::new_mldsa_65();
        check_serde!(algorithm: AlgorithmIdentifier in encoded[4..17]);

        // MLDSA public
        let public = vec![
            0xff, 0xb4, 0x5d, 0xb2, 0x1b, 0x45, 0xed, 0x10, 0xff, 0x80, 0x71, 0x03, 0xff, 0x37, 0xeb, 0x80, 0x2d, 0xe0,
            0x0b, 0xbf, 0x60, 0xe7, 0xe3, 0xfb, 0x76, 0x25, 0x31, 0x5b, 0x40, 0x58, 0x21, 0x53, 0x64, 0x3d, 0x80, 0x9b,
            0x7b, 0xde, 0x7f, 0x13, 0x8b, 0x3b, 0x1e, 0xb2, 0x1e, 0xa2, 0xf2, 0x2f, 0x8e, 0xff, 0x5f, 0x59, 0x3b, 0x29,
            0xa9, 0x55, 0x78, 0x26, 0x40, 0xc2, 0xf5, 0x41, 0x33, 0x05, 0xe7, 0x84, 0x12, 0xad, 0x0f, 0x78, 0xfa, 0x15,
            0x18, 0x58, 0x1a, 0x47, 0x43, 0x92, 0x69, 0xaa, 0x17, 0x17, 0xf1, 0x53, 0x3d, 0x4e, 0xb2, 0xea, 0x24, 0x5b,
            0xf3, 0x4a, 0xb9, 0xf5, 0x7e, 0x12, 0xb4, 0x85, 0x30, 0xb4, 0xb8, 0xee, 0x8a, 0x10, 0xc4, 0x65, 0x94, 0xb5,
            0x24, 0xad, 0xcd, 0x50, 0x9d, 0xed, 0xd2, 0x00, 0xf0, 0xb6, 0x21, 0x9f, 0xd7, 0x73, 0xf9, 0xae, 0x20, 0xa1,
            0xca, 0xb2, 0xc6, 0x16, 0x19, 0x05, 0xf6, 0xe6, 0xb3, 0xfb, 0x40, 0x70, 0x2b, 0x4a, 0x6d, 0xdd, 0x10, 0xf6,
            0x45, 0x0d, 0x62, 0xf5, 0xaf, 0x80, 0x7c, 0x7f, 0x7c, 0xd1, 0x37, 0xbf, 0x44, 0x49, 0x87, 0xcd, 0xea, 0xc0,
            0x0e, 0x2e, 0xd2, 0x94, 0x6b, 0x97, 0x46, 0xc0, 0xaa, 0x38, 0xc0, 0xdc, 0x35, 0xe9, 0xff, 0xe4, 0x13, 0x6b,
            0x20, 0x14, 0x80, 0xce, 0xe8, 0xeb, 0xca, 0xfc, 0x9e, 0x3a, 0xc3, 0xe2, 0xa9, 0x77, 0x38, 0xc0, 0x74, 0x12,
            0xb2, 0xfb, 0x9c, 0x7a, 0xc8, 0x8e, 0x20, 0x07, 0x90, 0xf0, 0x89, 0xb8, 0x33, 0x7a, 0x9f, 0x02, 0x24, 0x5d,
            0xbc, 0x74, 0x8a, 0x49, 0x8a, 0x58, 0xc6, 0xf2, 0xc9, 0xd6, 0xf9, 0x70, 0xae, 0x37, 0x67, 0x6f, 0xc7, 0x2d,
            0x90, 0x2b, 0xea, 0xc3, 0x13, 0x88, 0x10, 0xfc, 0xc3, 0x3d, 0x07, 0x60, 0x3f, 0xf3, 0xa5, 0x9c, 0x3c, 0xf9,
            0xb4, 0x4d, 0xe1, 0x29, 0xe7, 0xad, 0xce, 0xc8, 0xca, 0x98, 0xb9, 0x8c, 0x7d, 0x58, 0xb5, 0x99, 0x5c, 0xf2,
            0xcb, 0x93, 0x55, 0xd9, 0xe7, 0xbe, 0x54, 0x82, 0x14, 0x4f, 0x9c, 0xef, 0x70, 0xd9, 0x32, 0x19, 0xa1, 0x19,
            0xbc, 0xf5, 0xe9, 0xbe, 0x4f, 0x41, 0x04, 0xde, 0xeb, 0x80, 0xc2, 0xd8, 0xf1, 0xfd, 0x06, 0xba, 0xbb, 0x15,
            0x6d, 0x08, 0x3c, 0x80, 0xf6, 0xe2, 0x28, 0x0f, 0x21, 0x20, 0x25, 0x91, 0x0b, 0x27, 0xad, 0x03, 0xca, 0x4d,
            0x28, 0xf0, 0xf3, 0x12, 0x97, 0xa2, 0x37, 0x2e, 0x92, 0x88, 0xf2, 0x83, 0x5f, 0x5b, 0x59, 0x96, 0xbf, 0x75,
            0xe7, 0x00, 0xfe, 0xd7, 0xb7, 0xcf, 0xf6, 0x89, 0x71, 0xcb, 0xb5, 0x9a, 0xa3, 0xd6, 0x19, 0xa7, 0xa2, 0xe7,
            0xa1, 0xe9, 0xe8, 0x1f, 0xb6, 0xa5, 0x84, 0xd0, 0x81, 0xb6, 0xf2, 0xa7, 0x4f, 0x6d, 0xc4, 0xd8, 0x1a, 0x0a,
            0xd4, 0x13, 0x8b, 0x6c, 0x49, 0xcc, 0xe0, 0x79, 0x4a, 0x3b, 0x2f, 0xc1, 0x65, 0xa4, 0xac, 0x80, 0x01, 0x8a,
            0x88, 0xc6, 0x99, 0x51, 0xf2, 0xaa, 0x6d, 0x60, 0x99, 0x4b, 0x5f, 0xba, 0xf5, 0x3b, 0x30, 0xd6, 0xee, 0x23,
            0xba, 0x65, 0x7b, 0x50, 0x17, 0xa4, 0x6d, 0x90, 0x47, 0x3e, 0xf0, 0xa8, 0x10, 0x9c, 0xe6, 0x9a, 0x2b, 0xe3,
            0x37, 0xca, 0x7e, 0x99, 0x63, 0xa2, 0xbd, 0xdf, 0x8d, 0x32, 0x07, 0x25, 0xd2, 0x24, 0xab, 0xcc, 0x05, 0x7f,
            0x84, 0xcd, 0xa2, 0x48, 0x5e, 0x1e, 0xcd, 0x6e, 0xd3, 0x4c, 0xba, 0xa8, 0x24, 0x37, 0x5b, 0x04, 0x7b, 0x75,
            0x35, 0x19, 0x67, 0x8d, 0xcf, 0x73, 0xc3, 0x18, 0xc0, 0xe0, 0xd2, 0x1f, 0x5a, 0x9a, 0xf0, 0x98, 0x4b, 0x81,
            0x1c, 0x01, 0x4a, 0xbd, 0x9d, 0xfb, 0x7d, 0x09, 0xaa, 0x79, 0x1e, 0x11, 0x9d, 0x0f, 0xa2, 0xb3, 0xf1, 0x5b,
            0x26, 0x09, 0x82, 0x06, 0xd4, 0x25, 0xf9, 0x68, 0xb6, 0x31, 0x99, 0xec, 0x3f, 0xbe, 0xc7, 0x24, 0x76, 0x1b,
            0x5d, 0xa7, 0x23, 0x86, 0x42, 0xe6, 0x99, 0x74, 0xf5, 0xf7, 0xa7, 0x29, 0x8b, 0x85, 0xd3, 0x20, 0xbf, 0x3e,
            0xc7, 0xf9, 0xdf, 0x7e, 0xfc, 0xc2, 0x9a, 0xa7, 0x0b, 0x3e, 0x4b, 0xa0, 0x69, 0xb2, 0x43, 0x41, 0x4c, 0x4a,
            0x4d, 0x16, 0x90, 0xef, 0x44, 0xb3, 0x9d, 0xa5, 0xa4, 0x3e, 0x0a, 0x05, 0xb5, 0x93, 0xf0, 0xe2, 0x03, 0x1f,
            0xfb, 0xb0, 0x10, 0xe7, 0xbe, 0x01, 0x7d, 0x04, 0xca, 0x6a, 0xd1, 0x97, 0x94, 0xb0, 0x0c, 0xa0, 0xfd, 0x6a,
            0xc3, 0xc7, 0x25, 0xd1, 0x9c, 0x6f, 0x82, 0x9d, 0x57, 0xbe, 0x1c, 0x73, 0xfc, 0xfb, 0xbf, 0x83, 0xbc, 0xe0,
            0x8e, 0x65, 0x9c, 0x45, 0xdc, 0x27, 0xde, 0xcf, 0x5e, 0x44, 0xaf, 0xb9, 0x1f, 0xc7, 0x0e, 0x0c, 0x3f, 0x86,
            0x8e, 0xae, 0xfc, 0x46, 0x06, 0x71, 0x01, 0xe3, 0xfc, 0xe0, 0x9f, 0x00, 0xb6, 0x24, 0x28, 0xcd, 0x32, 0xf6,
            0x8f, 0xca, 0xdf, 0x0c, 0x39, 0x09, 0x3b, 0xf3, 0x01, 0x86, 0x44, 0xdb, 0xe4, 0xec, 0x28, 0x83, 0x2a, 0x7c,
            0xce, 0x20, 0xc1, 0x53, 0x42, 0x76, 0x0f, 0x5a, 0x12, 0x71, 0xea, 0x57, 0x08, 0xe7, 0x70, 0xd6, 0xbf, 0xa9,
            0x12, 0x72, 0xae, 0xf0, 0xf0, 0x15, 0x06, 0x99, 0x22, 0x4c, 0x73, 0x9d, 0xdf, 0x23, 0xd5, 0x2f, 0x42, 0x1e,
            0x17, 0xc4, 0x40, 0xb4, 0xee, 0x1f, 0x85, 0xad, 0x19, 0x99, 0xba, 0x16, 0x7e, 0xa1, 0x09, 0xfc, 0x57, 0xb5,
            0x6a, 0x2e, 0x1e, 0xe1, 0x4b, 0x67, 0x13, 0xd3, 0xd8, 0x3e, 0x95, 0xe1, 0x1a, 0xb5, 0x4c, 0xd3, 0x40, 0xf6,
            0xf1, 0x55, 0xa0, 0xf7, 0x22, 0x6f, 0x92, 0x67, 0x53, 0x74, 0x87, 0x08, 0x21, 0x2e, 0x77, 0xc6, 0xd7, 0x0a,
            0xf7, 0x2e, 0x8b, 0xd0, 0x80, 0x7a, 0x97, 0x3a, 0x1b, 0xcd, 0x16, 0x9d, 0xfc, 0x10, 0x0b, 0x79, 0x76, 0x84,
            0xe2, 0x11, 0xf4, 0x18, 0x04, 0x73, 0x8d, 0x2b, 0x27, 0x0d, 0x1d, 0xdb, 0xe1, 0x98, 0xb9, 0xdc, 0xd7, 0x88,
            0x21, 0x70, 0xd7, 0xea, 0x06, 0x92, 0x26, 0xff, 0x60, 0xc5, 0xd0, 0xd9, 0xfb, 0xb5, 0x0f, 0xb0, 0x06, 0x1f,
            0x89, 0x77, 0x74, 0x85, 0x79, 0x84, 0x58, 0x2f, 0xf2, 0x74, 0x80, 0x16, 0x94, 0x38, 0x3d, 0xe3, 0xe6, 0x2d,
            0x35, 0x1e, 0x9f, 0x7c, 0x2a, 0xfb, 0x4f, 0x72, 0x91, 0xbe, 0x3f, 0xe0, 0xcf, 0x5b, 0x93, 0x09, 0x89, 0xcc,
            0x1d, 0x31, 0xb5, 0xb2, 0x08, 0xe4, 0x36, 0x7b, 0x8d, 0x28, 0x6d, 0x54, 0xf3, 0x96, 0x6e, 0x57, 0x31, 0x32,
            0x32, 0x92, 0x37, 0xdf, 0x56, 0x2a, 0x73, 0xfc, 0xbb, 0x2d, 0x6c, 0xea, 0x45, 0xf0, 0xee, 0x0c, 0xd7, 0x31,
            0x9e, 0xab, 0xf3, 0x48, 0x08, 0xce, 0xc0, 0xac, 0x8b, 0x48, 0x62, 0x3b, 0x18, 0x19, 0x21, 0x95, 0x55, 0x55,
            0xcf, 0x28, 0x02, 0x92, 0x6e, 0xc9, 0x5b, 0xb4, 0x42, 0x40, 0xf0, 0xd3, 0x09, 0x48, 0xde, 0xef, 0xf6, 0xbc,
            0x41, 0x47, 0x1e, 0x17, 0xac, 0x64, 0x6d, 0x34, 0x6f, 0x36, 0xbf, 0x06, 0xcf, 0x4f, 0x2b, 0xc7, 0xe3, 0x23,
            0x37, 0x1f, 0xf7, 0x31, 0x4f, 0xe8, 0x9c, 0xef, 0x4f, 0x15, 0xc2, 0x70, 0x95, 0x37, 0xa0, 0x8a, 0x61, 0x96,
            0x38, 0x2c, 0xb1, 0x07, 0x79, 0xf7, 0x5b, 0x37, 0xd6, 0x33, 0x80, 0x42, 0x4d, 0xa3, 0x35, 0xbd, 0x7c, 0xb1,
            0x77, 0xac, 0x1c, 0x3f, 0x98, 0x82, 0x69, 0x92, 0xf4, 0xa8, 0x27, 0x3f, 0xcb, 0x1a, 0x1a, 0x96, 0x0c, 0x0f,
            0xba, 0xee, 0xfb, 0x9b, 0x06, 0xb1, 0x5f, 0x2b, 0x56, 0x4b, 0x25, 0xd4, 0xf2, 0x6e, 0x1d, 0x3d, 0xe4, 0xed,
            0xe8, 0xdb, 0x4d, 0x44, 0x87, 0x98, 0xb0, 0xcb, 0x76, 0x5a, 0x45, 0xa8, 0x59, 0x98, 0xa4, 0x0e, 0x5b, 0x78,
            0x1b, 0x25, 0x15, 0x00, 0x19, 0x49, 0x80, 0xcc, 0x08, 0x94, 0xf8, 0x24, 0x86, 0x4d, 0xa3, 0xd8, 0x74, 0x9a,
            0xcb, 0x08, 0x56, 0xd9, 0x49, 0x01, 0xab, 0xc6, 0xe4, 0x3d, 0x30, 0xa2, 0xd6, 0x44, 0x9c, 0x97, 0xcb, 0xa4,
            0x71, 0x18, 0x0f, 0x0c, 0x32, 0xba, 0x1c, 0x84, 0x4e, 0x85, 0xa6, 0x9c, 0x45, 0x19, 0xa1, 0x9b, 0xb9, 0x0b,
            0x18, 0x87, 0x32, 0x6b, 0xc2, 0xf8, 0xf3, 0xcb, 0x34, 0x0e, 0xf3, 0x2e, 0x07, 0x58, 0x80, 0x20, 0xdb, 0x12,
            0x02, 0x5d, 0x19, 0x96, 0xa0, 0xfd, 0x3b, 0xf7, 0x0b, 0xb6, 0xc9, 0x0a, 0xbf, 0x92, 0x5f, 0x81, 0x23, 0x2c,
            0x8c, 0x5d, 0x4f, 0xc7, 0x65, 0x1d, 0xe8, 0x45, 0xf5, 0xbb, 0x11, 0x4b, 0x08, 0xfd, 0x74, 0x84, 0x92, 0xc1,
            0x70, 0x41, 0x97, 0xc0, 0x74, 0xcd, 0xc4, 0x41, 0x9a, 0xab, 0x12, 0xe3, 0x9d, 0xe7, 0xc7, 0x51, 0x2f, 0x30,
            0xe4, 0x9f, 0x85, 0xcd, 0xc8, 0x54, 0xe5, 0x8d, 0x5a, 0x99, 0x08, 0xc4, 0x87, 0x14, 0x27, 0xf9, 0x98, 0xc9,
            0x58, 0xa1, 0xe5, 0x6e, 0xfc, 0x98, 0x71, 0xae, 0xf0, 0x06, 0xe9, 0xc4, 0xdc, 0xcf, 0x98, 0xe8, 0x30, 0x4a,
            0x24, 0xd5, 0xd9, 0x16, 0xb1, 0xef, 0x67, 0x39, 0x48, 0xfc, 0xc2, 0xdf, 0x35, 0x0b, 0xac, 0x93, 0x2f, 0x87,
            0x75, 0x12, 0xf7, 0x2c, 0xab, 0x1c, 0x43, 0xd8, 0x70, 0x08, 0x00, 0xcc, 0xb6, 0xe4, 0xff, 0xa5, 0xd8, 0x23,
            0x27, 0x2a, 0x97, 0x95, 0x5f, 0x98, 0xd0, 0x6a, 0x0e, 0x58, 0x1e, 0xc8, 0x0c, 0x84, 0x9a, 0xe6, 0x2e, 0x51,
            0xfb, 0x74, 0x90, 0x42, 0x94, 0x9b, 0x32, 0xcf, 0xb0, 0x9e, 0x00, 0x99, 0x7d, 0x87, 0xe0, 0x6b, 0xe8, 0xfe,
            0x8f, 0x01, 0x41, 0x37, 0x10, 0xd3, 0x68, 0xa2, 0xb7, 0xf8, 0xf9, 0x3b, 0x35, 0x95, 0x74, 0xab, 0xe9, 0x2f,
            0x78, 0xeb, 0x48, 0x2b, 0x02, 0x87, 0x3f, 0xae, 0x3e, 0x6c, 0x9e, 0x03, 0x99, 0xbd, 0x07, 0xa5, 0xf9, 0xe0,
            0x07, 0x62, 0xfa, 0x77, 0x43, 0x5f, 0x48, 0xc7, 0x7c, 0xf6, 0x50, 0x44, 0x13, 0x98, 0xc9, 0xcb, 0x4b, 0x5c,
            0x57, 0x61, 0x50, 0x1f, 0x12, 0xbf, 0x06, 0xda, 0xd0, 0x5a, 0xbf, 0xaa, 0xf5, 0x93, 0xd2, 0x3b, 0x5c, 0xec,
            0x7c, 0x4e, 0x76, 0x50, 0x90, 0x64, 0x56, 0x96, 0x13, 0x04, 0x83, 0x2a, 0x3f, 0x1b, 0x8f, 0x22, 0x5a, 0x08,
            0x01, 0xa9, 0x46, 0x6a, 0xf7, 0xf8, 0xc0, 0xff, 0x7c, 0x50, 0x43, 0xc8, 0x7c, 0x5c, 0x38, 0x94, 0xb7, 0xb8,
            0xcb, 0xef, 0xb7, 0x15, 0xc9, 0x02, 0x11, 0x2d, 0x55, 0x21, 0xed, 0xe5, 0xae, 0x01, 0x3b, 0xd4, 0x93, 0x54,
            0xc6, 0x7e, 0x52, 0xe2, 0x2e, 0xf2, 0x81, 0x02, 0x9d, 0x30, 0xfd, 0xa7, 0x3d, 0x74, 0xcd, 0x9d, 0x72, 0x11,
            0xf9, 0x44, 0x47, 0xbf, 0x4a, 0x0e, 0x85, 0x33, 0xe3, 0x7e, 0x47, 0x55, 0xd7, 0xde, 0xca, 0xf1, 0x30, 0xc4,
            0xdd, 0x2a, 0x87, 0x02, 0x29, 0x5f, 0xb1, 0x8c, 0x63, 0xf3, 0x09, 0x6c, 0x29, 0xe4, 0x16, 0x25, 0x30, 0x3d,
            0xa5, 0x93, 0xfd, 0xe9, 0x51, 0xae, 0x9e, 0x88, 0xd8, 0xd4, 0xa9, 0xc2, 0xea, 0xc9, 0xe1, 0xc9, 0xf6, 0x42,
            0x25, 0xc4, 0x0c, 0x46, 0x2b, 0xd5, 0xcf, 0x37, 0xa6, 0xa0, 0xd6, 0xfa, 0x43, 0x14, 0x8b, 0xd2, 0x63, 0xbb,
            0xf1, 0x20, 0x98, 0xfb, 0x93, 0x46, 0xcb, 0xbb, 0x10, 0x4d, 0x28, 0xd7, 0x49, 0xc4, 0xe7, 0xdf, 0xdc, 0x8e,
            0x71, 0xbc, 0x48, 0x5f, 0x8a, 0x11, 0xad, 0xb6, 0x41, 0x7a, 0x0f, 0xc7, 0xcf, 0xc5, 0xae, 0xa9, 0x1e, 0x68,
            0x48, 0x04, 0x8c, 0x08, 0x40, 0x51, 0xb8, 0x19, 0x8a, 0x4c, 0xea, 0x48, 0xc3, 0x7a, 0x9b, 0xbe, 0xdf, 0x78,
            0xea, 0x57, 0x61, 0x33, 0x8c, 0x88, 0xbe, 0xc4, 0xcf, 0x61, 0xdd, 0x72, 0xe8, 0x2e, 0xe3, 0xd6, 0xe4, 0x5a,
            0xaa, 0xd1, 0xa7, 0x1d, 0xfd, 0x9a, 0xd0, 0x2f, 0x22, 0x17, 0x25, 0xba, 0xb6, 0x0a, 0xc8, 0x8f, 0x5e, 0xa0,
            0x02, 0x51, 0xa9, 0xfc, 0x48, 0x8e, 0xf5, 0x7b, 0x86, 0x50, 0xed, 0xf6, 0xc3, 0x45, 0xce, 0xa3, 0xbb, 0x1f,
            0x26, 0xe5, 0xf8, 0xc1, 0xb1, 0x5b, 0x8d, 0xc7, 0x1a, 0xe9, 0x85, 0x6e, 0xeb, 0xe7, 0xe2, 0xb4, 0xf5, 0x55,
            0x04, 0xb5, 0x9d, 0xd8, 0x34, 0x61, 0xc7, 0x71, 0x34, 0xb8, 0x16, 0xc2, 0x57, 0x61, 0x2d, 0xda, 0xaf, 0xd3,
            0x7f, 0xa2, 0x3b, 0x7c, 0x02, 0x0c, 0x47, 0x45, 0xb2, 0x0e, 0xfa, 0x34, 0x2a, 0x98, 0x35, 0x17, 0xfd, 0x97,
            0x60, 0xd1, 0x66, 0x84, 0x3a, 0x6f, 0x99, 0x6e, 0x4d, 0xca, 0x6a, 0xd4, 0xd8, 0x67, 0xa6, 0x2f, 0x85, 0x2a,
            0xe9, 0x63, 0x00, 0x46, 0x39, 0x78, 0xf0, 0x66, 0xd7, 0xbd, 0x48, 0xee, 0x87, 0x56, 0x86, 0x7c, 0xda, 0x58,
            0xc0, 0x41, 0xb3, 0x64, 0x53, 0xd8, 0xac, 0x6e, 0x09, 0x75, 0x09, 0xa5, 0x94, 0xcc, 0x7a, 0xc4, 0x0d, 0x5c,
            0xfb, 0xc4, 0xd3, 0x7b, 0x57, 0xb7, 0x38, 0xc7, 0xd0, 0xcd, 0x6b, 0xb9, 0x1a, 0x56, 0x81, 0xf6, 0x52, 0x9f,
            0xa1, 0xc9, 0x01, 0x88, 0xb1, 0xb1, 0x16, 0x51, 0xf1, 0xc5, 0xed, 0x66, 0xf1, 0xae, 0xbc, 0x8c, 0x1c, 0xae,
            0xd8, 0x10, 0x9c, 0x02, 0xd2, 0x84, 0xe3, 0x67, 0x85, 0x4c, 0x03, 0x1a, 0x13, 0x16, 0x66, 0x53, 0x81, 0xe9,
            0xda, 0xee, 0xc4, 0x57, 0x3d, 0xf4, 0x52, 0xe1, 0x96, 0x4f, 0x59, 0xf3, 0xc9, 0x9b, 0xa2, 0x1e, 0x95, 0xce,
            0x6a, 0x8f, 0x22, 0xa4, 0xa5, 0x79, 0x12, 0xaa, 0x37, 0x84, 0x04, 0x4e, 0xad, 0x0b, 0xd9, 0x35, 0x53, 0xfe,
            0xf6, 0xac, 0x8d, 0x88, 0x9f, 0x19, 0x7e, 0x5d, 0x1d, 0x6e, 0xc4, 0xbc, 0x09, 0xb2, 0x4a, 0x63, 0x8e, 0x5b,
            0x4e, 0x1e, 0x71, 0x27, 0x31, 0x92, 0x85, 0x7e, 0x95, 0xf8, 0x35, 0xe1, 0xc8, 0x45, 0x6b, 0x35, 0xfb, 0x34,
            0xc6, 0x3f, 0xa6, 0xa6, 0xf7, 0x8f, 0x7a, 0xaa, 0xc4, 0x97, 0xff, 0x9e, 0xb7, 0xc6, 0x46, 0x32, 0x05, 0xdc,
            0x1d, 0x74, 0x4a, 0xc5, 0xe0, 0x62, 0x5f, 0x38, 0x8b, 0x51, 0xd1, 0x34, 0xd9, 0xba, 0x6e, 0x3e, 0x78, 0x54,
            0x29, 0x13, 0x2c, 0xa9, 0x1a, 0xac, 0xc6, 0x27, 0x03, 0x25, 0x02, 0x26, 0x80, 0x77, 0xfa, 0xbc, 0x13, 0x0f,
            0xb2, 0xbb, 0x61, 0x5b, 0x42, 0x1f, 0xea, 0x93, 0x61, 0x89, 0xda, 0x3c, 0xa7, 0x30, 0xe1, 0xa7, 0x27, 0x86,
            0x92, 0x4d, 0x24, 0x2d, 0x93, 0xb4, 0xb6, 0x98, 0xab, 0x58, 0x19, 0x60, 0x81, 0x3c, 0xce, 0x31, 0x46, 0x3c,
            0x94, 0xb1, 0x9a, 0x45, 0x8b, 0x5b, 0xd4, 0x9c, 0xd3, 0x49, 0x9e, 0xbf, 0x33, 0xfc, 0x93, 0x00, 0x7b, 0x0a,
            0x68, 0x31, 0x0a, 0x7c, 0x90, 0x34, 0x09, 0x17, 0xfd, 0x5e, 0x90, 0xc7, 0x26, 0xa9, 0x41, 0xff, 0x58, 0xbf,
            0xc3, 0x6c, 0x72, 0x71, 0x11, 0x67, 0x9a, 0x5e,
        ];

        //MLDSA public key
        let subject_public_key: EncapsulatedMldsaPublicKey = BitString::with_bytes(public).into();
        check_serde!(subject_public_key: EncapsulatedMldsaPublicKey in encoded[17..1974]);

        // full encode / decode
        let info = SubjectPublicKeyInfo {
            algorithm,
            subject_public_key: PublicKey::Mldsa(subject_public_key),
        };
        check_serde!(info: SubjectPublicKeyInfo in encoded);
    }
}
