synta-certificate 0.2.6

X.509 certificate structures for synta ASN.1 library
Documentation
//! Public key algorithm identification and decoded key data.
//!
//! [`decode_public_key_info`] inspects a `SubjectPublicKeyInfo`'s algorithm
//! OID and parameters, then returns an algorithm-specific [`PublicKeyInfo`]
//! that callers can pattern-match for display or further processing without
//! re-implementing algorithm dispatch in each consumer.

use synta::{types::oid::ObjectIdentifier, Element};

use crate::{ec_curve_key_bits, ec_curve_nist_name, ec_curve_short_name, oids};

/// Decoded public key data, keyed by algorithm family.
pub enum PublicKeyInfo {
    /// RSA public key parsed from `SEQUENCE { INTEGER (n), INTEGER (e) }`.
    Rsa {
        /// Raw modulus bytes from the INTEGER TLV (may begin with 0x00 sign byte).
        modulus: Vec<u8>,
        /// Public exponent (typically 65537).
        exponent: u64,
        /// Key size in bits: `(modulus − leading 0x00) × 8`.
        bit_count: usize,
    },
    /// Elliptic-curve public key (id-ecPublicKey).
    Ec {
        /// Raw key bytes from the BIT STRING (uncompressed or compressed point).
        key_bytes: Vec<u8>,
        /// Key size in bits derived from the named curve, or BIT STRING length.
        bit_count: usize,
        /// Short OID name for the curve, e.g. `"prime256v1"` (None if unknown).
        curve_short_name: Option<&'static str>,
        /// NIST curve name, e.g. `"P-256"` (None if not a NIST named curve).
        curve_nist_name: Option<&'static str>,
        /// Dotted OID string for the curve, used as fallback when curve_short_name is None.
        curve_oid_str: String,
    },
    /// Any other algorithm — raw BIT STRING bytes and algorithm name.
    Unknown {
        /// Human-readable algorithm name, or dotted OID if unrecognised.
        alg_name: String,
        /// Raw key bytes from the BIT STRING.
        key_bytes: Vec<u8>,
        /// Key size in bits (BIT STRING bit length).
        bit_count: usize,
    },
}

/// Decode a `SubjectPublicKeyInfo` into algorithm-specific [`PublicKeyInfo`].
///
/// - `alg_oid`: the `AlgorithmIdentifier.algorithm` OID.
/// - `alg_params`: the `AlgorithmIdentifier.parameters` (e.g. EC curve OID).
/// - `key_bytes`: the BIT STRING content (unused-bits byte already stripped).
/// - `key_bit_len`: the BIT STRING's reported bit length.
pub fn decode_public_key_info(
    alg_oid: &ObjectIdentifier,
    alg_params: Option<&Element<'_>>,
    key_bytes: &[u8],
    key_bit_len: usize,
) -> PublicKeyInfo {
    match alg_oid.components() {
        oids::RSA_ENCRYPTION => {
            if let Ok(rsa) =
                synta::Decoder::new(key_bytes, synta::Encoding::Der).decode::<crate::RsaPublicKey>()
            {
                let modulus = rsa.modulus.as_bytes().to_vec();
                let bit_count = modulus.strip_prefix(&[0u8]).unwrap_or(&modulus).len() * 8;
                let exp_bytes = rsa.public_exponent.as_bytes();
                let exponent = exp_bytes
                    .iter()
                    .fold(0u64, |acc, &b| (acc << 8) | (b as u64));
                return PublicKeyInfo::Rsa {
                    modulus,
                    exponent,
                    bit_count,
                };
            }
            // Malformed key — fall through to Unknown.
            PublicKeyInfo::Unknown {
                alg_name: "rsaEncryption".to_string(),
                key_bytes: key_bytes.to_vec(),
                bit_count: key_bit_len,
            }
        }
        oids::EC_PUBLIC_KEY => {
            if let Some(Element::ObjectIdentifier(curve_oid)) = alg_params {
                let c = curve_oid.components();
                let bit_count = ec_curve_key_bits(c).unwrap_or(key_bit_len);
                let curve_short_name = ec_curve_short_name(c);
                let curve_nist_name = ec_curve_nist_name(c);
                let curve_oid_str = c
                    .iter()
                    .map(|n| n.to_string())
                    .collect::<Vec<_>>()
                    .join(".");
                return PublicKeyInfo::Ec {
                    key_bytes: key_bytes.to_vec(),
                    bit_count,
                    curve_short_name,
                    curve_nist_name,
                    curve_oid_str,
                };
            }
            // EC without a named-curve parameter — treat as unknown.
            PublicKeyInfo::Unknown {
                alg_name: "id-ecPublicKey".to_string(),
                key_bytes: key_bytes.to_vec(),
                bit_count: key_bit_len,
            }
        }
        _ => {
            let alg_name = crate::identify_public_key_algorithm(alg_oid)
                .unwrap_or(crate::names::OTHER)
                .to_string();
            PublicKeyInfo::Unknown {
                alg_name,
                key_bytes: key_bytes.to_vec(),
                bit_count: key_bit_len,
            }
        }
    }
}