ts-crypto 0.4.0

Cryptography abstraction for my projects
Documentation
//! Edwards curve keys

use ed25519_dalek::Signer;
use sha2::{Digest, Sha256};
use signature::SignatureEncoding;

/// An edwards curve verifying key.
pub enum EdwardsVerifyingKey {
    /// Using Curve25519.
    Ed25519(ed25519_dalek::VerifyingKey),
    /// Using Curve448.
    Ed448(ed448_goldilocks::VerifyingKey),
}

/// An edwards curve signing key.
#[expect(clippy::large_enum_variant)]
pub enum EdwardsSigningKey {
    /// Using Curve25519
    Ed25519(ed25519_dalek::SigningKey),
    /// Using Curve448
    Ed448(ed448_goldilocks::SigningKey),
}

impl EdwardsVerifyingKey {
    /// Get the raw verifying key.
    pub fn raw_key(&self) -> Vec<u8> {
        match &self {
            Self::Ed25519(key) => key.as_bytes().to_vec(),
            Self::Ed448(key) => key.as_bytes().to_vec(),
        }
    }

    /// Returns if this key verifies the signature to match the message.
    pub fn verifies(&self, signature: &[u8], message: &[u8]) -> bool {
        match &self {
            Self::Ed25519(key) => {
                let Ok(signature) = ed25519_dalek::Signature::from_slice(signature) else {
                    return false;
                };
                key.verify_strict(message, &signature).is_ok()
            }

            Self::Ed448(key) => {
                let Ok(signature) = ed448_goldilocks::Signature::from_slice(signature) else {
                    return false;
                };
                key.verify_raw(&signature, message).is_ok()
            }
        }
    }

    /// Create an edwards key from a raw key.
    pub fn from_raw_key(raw_key: &[u8]) -> Option<Self> {
        match raw_key.len() {
            ed25519_dalek::PUBLIC_KEY_LENGTH => {
                #[allow(clippy::missing_panics_doc)]
                let raw_key: &[u8; ed25519_dalek::PUBLIC_KEY_LENGTH] = raw_key
                    .try_into()
                    .expect("<&[u8;T]>::try_from(&[u8]) should succeed if <&[u8]>::len() == T");
                let key = ed25519_dalek::VerifyingKey::from_bytes(raw_key).ok()?;
                Some(Self::Ed25519(key))
            }
            ed448_goldilocks::PUBLIC_KEY_LENGTH => {
                #[allow(clippy::missing_panics_doc)]
                let raw_key: &[u8; ed448_goldilocks::PUBLIC_KEY_LENGTH] = raw_key
                    .try_into()
                    .expect("<&[u8;T]>::try_from(&[u8]) should succeed if <&[u8]>::len() == T");
                let key = ed448_goldilocks::VerifyingKey::from_bytes(raw_key).ok()?;
                Some(Self::Ed448(key))
            }
            _ => None,
        }
    }

    /// Returns the ID for this key.
    pub fn key_id(&self) -> Vec<u8> {
        let key = self.raw_key();
        let digest = Sha256::digest(key);
        digest.to_vec()
    }
}

impl EdwardsSigningKey {
    /// Sign the message using this key.
    pub fn sign(&self, message: &[u8]) -> Vec<u8> {
        match &self {
            Self::Ed25519(key) => key.sign(message).to_vec(),
            Self::Ed448(key) => key.sign_raw(message).to_bytes().to_vec(),
        }
    }

    /// Get the verifying key for this signing key
    pub fn verifying_key(&self) -> EdwardsVerifyingKey {
        match &self {
            Self::Ed25519(key) => EdwardsVerifyingKey::Ed25519(key.verifying_key()),
            Self::Ed448(key) => EdwardsVerifyingKey::Ed448(key.verifying_key()),
        }
    }
}