ts-crypto 0.4.0

Cryptography abstraction for my projects
Documentation
//! Any supported cryptographic key and associated methods.

use const_oid::db::{
    rfc5753::ID_EC_PUBLIC_KEY,
    rfc5912::{RSA_ENCRYPTION, SECP_256_R_1, SECP_384_R_1, SECP_521_R_1},
    rfc8410::{ID_ED_448, ID_ED_25519},
};
use der::{Decode, asn1::OctetString, pem::PemLabel};
use pkcs8::DecodePrivateKey;
use rsa::pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey};
use spki::DecodePublicKey;

use crate::{
    EdwardsSigningKey, EdwardsVerifyingKey, EllipticSigningKey, EllipticVerifyingKey,
    RsaSigningKey, RsaVerifyingKey,
};

/// Any supported verifying key.
pub enum VerifyingKey {
    /// An RSA key.
    Rsa(RsaVerifyingKey),
    /// An elliptic curve key.
    Elliptic(EllipticVerifyingKey),
    /// An Edwards curve key.
    Edwards(EdwardsVerifyingKey),
}

/// Any supported signing key.
#[expect(clippy::large_enum_variant)]
pub enum SigningKey {
    /// An RSA key.
    Rsa(RsaSigningKey),
    /// An elliptic curve key.
    Elliptic(EllipticSigningKey),
    /// And Edwards curve key.
    Edwards(EdwardsSigningKey),
}

impl VerifyingKey {
    /// Parse a verifying key from a PEM.
    pub fn from_pem(pem: &[u8]) -> Option<Self> {
        let (label, der) = der::pem::decode_vec(pem).ok()?;
        match label {
            spki::SubjectPublicKeyInfoOwned::PEM_LABEL => {
                let spki = spki::SubjectPublicKeyInfoRef::from_der(&der).ok()?;
                match spki.algorithm.oid {
                    ID_EC_PUBLIC_KEY => match spki.algorithm.parameters_oid().ok()? {
                        SECP_256_R_1 => {
                            let key = p256::ecdsa::VerifyingKey::from_public_key_der(&der).ok()?;
                            Some(Self::Elliptic(EllipticVerifyingKey::Prime256(key)))
                        }
                        SECP_384_R_1 => {
                            let key = p384::ecdsa::VerifyingKey::from_public_key_der(&der).ok()?;
                            Some(Self::Elliptic(EllipticVerifyingKey::Prime384(key)))
                        }
                        SECP_521_R_1 => {
                            let key = p521::ecdsa::VerifyingKey::from_public_key_der(&der).ok()?;
                            Some(Self::Elliptic(EllipticVerifyingKey::Prime521(key)))
                        }
                        _ => None,
                    },
                    RSA_ENCRYPTION => {
                        let key = rsa::RsaPublicKey::from_public_key_der(&der).ok()?;
                        Some(Self::Rsa(RsaVerifyingKey(key)))
                    }
                    ID_ED_25519 => {
                        let key = ed25519_dalek::VerifyingKey::from_public_key_der(&der).ok()?;
                        Some(Self::Edwards(EdwardsVerifyingKey::Ed25519(key)))
                    }
                    ID_ED_448 => {
                        let key = ed448_goldilocks::VerifyingKey::from_public_key_der(&der).ok()?;
                        Some(Self::Edwards(EdwardsVerifyingKey::Ed448(key)))
                    }
                    _ => None,
                }
            }
            rsa::pkcs1::RsaPublicKey::PEM_LABEL => {
                let key = rsa::RsaPublicKey::from_pkcs1_der(&der).ok()?;
                Some(Self::Rsa(RsaVerifyingKey(key)))
            }
            _ => None,
        }
    }

    /// Get the ID for this key.
    pub fn key_id(&self) -> Vec<u8> {
        match &self {
            Self::Rsa(key) => key.key_id(),
            Self::Elliptic(key) => key.key_id(),
            Self::Edwards(key) => key.key_id(),
        }
    }
}

impl SigningKey {
    /// Parse a signing key from a PEM.
    pub fn from_pem(pem: &[u8]) -> Option<Self> {
        let (label, der) = der::pem::decode_vec(pem).ok()?;

        match label {
            sec1::EcPrivateKey::PEM_LABEL => {
                let sec1 = sec1::EcPrivateKey::from_der(&der).ok()?;
                let curve = sec1.parameters?.named_curve()?;
                match curve {
                    SECP_256_R_1 => {
                        let key = p256::ecdsa::SigningKey::from_slice(sec1.private_key).ok()?;
                        Some(Self::Elliptic(EllipticSigningKey::Prime256(key)))
                    }
                    SECP_384_R_1 => {
                        let key = p384::ecdsa::SigningKey::from_slice(sec1.private_key).ok()?;
                        Some(Self::Elliptic(EllipticSigningKey::Prime384(key)))
                    }
                    SECP_521_R_1 => {
                        let key = p521::ecdsa::SigningKey::from_slice(sec1.private_key).ok()?;
                        Some(Self::Elliptic(EllipticSigningKey::Prime521(key)))
                    }
                    _ => None,
                }
            }
            rsa::pkcs1::RsaPrivateKey::PEM_LABEL => {
                let key = rsa::RsaPrivateKey::from_pkcs1_der(&der).ok()?;
                Some(Self::Rsa(RsaSigningKey(key)))
            }
            pkcs8::PrivateKeyInfoOwned::PEM_LABEL => {
                let pkcs8 = pkcs8::PrivateKeyInfoRef::from_der(&der).ok()?;
                let algorithm = pkcs8.algorithm.oid;
                match algorithm {
                    ID_EC_PUBLIC_KEY => match pkcs8.algorithm.parameters_oid().ok()? {
                        SECP_256_R_1 => {
                            let key = p256::ecdsa::SigningKey::from_pkcs8_der(&der).ok()?;
                            Some(Self::Elliptic(EllipticSigningKey::Prime256(key)))
                        }
                        SECP_384_R_1 => {
                            let key = p384::ecdsa::SigningKey::from_pkcs8_der(&der).ok()?;
                            Some(Self::Elliptic(EllipticSigningKey::Prime384(key)))
                        }
                        SECP_521_R_1 => {
                            let key = p521::ecdsa::SigningKey::from_pkcs8_der(&der).ok()?;
                            Some(Self::Elliptic(EllipticSigningKey::Prime521(key)))
                        }
                        _ => None,
                    },
                    RSA_ENCRYPTION => {
                        let key = rsa::RsaPrivateKey::from_pkcs8_der(&der).ok()?;
                        Some(Self::Rsa(RsaSigningKey(key)))
                    }
                    ID_ED_25519 => {
                        let key = ed25519_dalek::SigningKey::from_pkcs8_der(&der).ok()?;
                        Some(Self::Edwards(EdwardsSigningKey::Ed25519(key)))
                    }
                    ID_ED_448 => {
                        // The current rust-crypto implementation of from_pkcs8 for ed448 fails, so some manual decoding is required.
                        // See <https://github.com/RustCrypto/elliptic-curves/issues/1326>
                        let octet_string =
                            <OctetString as Decode>::from_der(pkcs8.private_key.as_bytes()).ok()?;
                        let secret_key =
                            ed448_goldilocks::SecretKey::try_from(octet_string.as_bytes()).ok()?;
                        let key = ed448_goldilocks::SigningKey::from(secret_key);

                        // let key = ed448_goldilocks::SigningKey::from_pkcs8_der(&der).ok()?;
                        Some(Self::Edwards(EdwardsSigningKey::Ed448(key)))
                    }
                    _ => None,
                }
            }
            _ => None,
        }
    }

    /// Get the verifying key for this signing key.
    pub fn verifying_key(&self) -> VerifyingKey {
        match &self {
            Self::Rsa(key) => VerifyingKey::Rsa(key.verifying_key()),
            Self::Elliptic(key) => VerifyingKey::Elliptic(key.verifying_key()),
            Self::Edwards(key) => VerifyingKey::Edwards(key.verifying_key()),
        }
    }
}