rust-auth-utils 1.0.0

A rust port of @better-auth/utils.
Documentation
// based on https://github.com/better-auth/utils/blob/main/src/ecdsa.ts

use crate::types::{ECDSACurve, ExportKeyFormat, SHAFamily};
use generic_array::GenericArray;
use p256::ecdsa::{SigningKey, VerifyingKey};
use rand_core::OsRng;
use signature::{Signer, Verifier};

pub struct ECDSA;

impl ECDSA {
    pub async fn generate_key_pair(
        curve: Option<ECDSACurve>,
    ) -> Result<(Vec<u8>, Vec<u8>), Box<dyn std::error::Error>> {
        let curve = curve.unwrap_or(ECDSACurve::P256);

        match curve {
            ECDSACurve::P256 => {
                let signing_key = SigningKey::random(&mut OsRng);
                let verifying_key = signing_key.verifying_key();

                let private_key = signing_key.to_bytes().to_vec();
                let public_key = verifying_key.to_encoded_point(false).as_bytes().to_vec();

                Ok((private_key, public_key))
            }
            _ => Err("Currently only P-256 curve is supported".into()),
        }
    }

    pub async fn import_private_key(
        private_key: impl AsRef<[u8]>,
        curve: ECDSACurve,
        _extractable: bool,
    ) -> Result<SigningKey, Box<dyn std::error::Error>> {
        match curve {
            ECDSACurve::P256 => {
                let bytes = private_key.as_ref();
                let array_ref = GenericArray::from_slice(bytes);
                let key = SigningKey::from_bytes(array_ref)?;
                Ok(key)
            }
            _ => Err("Currently only P-256 curve is supported".into()),
        }
    }

    pub async fn import_public_key(
        public_key: impl AsRef<[u8]>,
        curve: ECDSACurve,
        _extractable: bool,
    ) -> Result<VerifyingKey, Box<dyn std::error::Error>> {
        match curve {
            ECDSACurve::P256 => {
                let key = VerifyingKey::from_sec1_bytes(public_key.as_ref())?;
                Ok(key)
            }
            _ => Err("Currently only P-256 curve is supported".into()),
        }
    }

    pub async fn sign(
        private_key: &SigningKey,
        data: impl AsRef<[u8]>,
        hash: Option<SHAFamily>,
    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
        let hash = hash.unwrap_or(SHAFamily::SHA256);

        match hash {
            SHAFamily::SHA256 => {
                let signature: p256::ecdsa::Signature = private_key.sign(data.as_ref());
                Ok(signature.to_vec())
            }
            _ => Err("Currently only SHA-256 is supported for signing".into()),
        }
    }

    pub async fn verify(
        public_key: &VerifyingKey,
        signature: impl AsRef<[u8]>,
        data: impl AsRef<[u8]>,
        hash: Option<SHAFamily>,
    ) -> Result<bool, Box<dyn std::error::Error>> {
        let hash = hash.unwrap_or(SHAFamily::SHA256);

        match hash {
            SHAFamily::SHA256 => {
                let sig = p256::ecdsa::Signature::from_slice(signature.as_ref())?;
                Ok(public_key.verify(data.as_ref(), &sig).is_ok())
            }
            _ => Err("Currently only SHA-256 is supported for verification".into()),
        }
    }

    pub async fn export_key(
        key: &(impl AsRef<[u8]> + ?Sized),
        format: ExportKeyFormat,
    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
        match format {
            ExportKeyFormat::SPKI => {
                if let Ok(public_key) = VerifyingKey::from_sec1_bytes(key.as_ref()) {
                    Ok(public_key.to_encoded_point(false).as_bytes().to_vec())
                } else {
                    Err("Key is not a valid public key".into())
                }
            }
            ExportKeyFormat::PKCS8 => {
                let array_ref = GenericArray::from_slice(key.as_ref());
                if let Ok(private_key) = SigningKey::from_bytes(array_ref) {
                    Ok(private_key.to_bytes().to_vec())
                } else {
                    Err("Key is not a valid private key".into())
                }
            }
            _ => Err("Unsupported export format".into()),
        }
    }

    pub async fn export_key_private(
        key: &SigningKey,
        format: ExportKeyFormat,
    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
        match format {
            ExportKeyFormat::PKCS8 => Ok(key.to_bytes().to_vec()),
            _ => Err("Unsupported export format for private key".into()),
        }
    }

    pub async fn export_key_public(
        key: &VerifyingKey,
        format: ExportKeyFormat,
    ) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
        match format {
            ExportKeyFormat::SPKI => Ok(key.to_encoded_point(false).as_bytes().to_vec()),
            _ => Err("Unsupported export format for public key".into()),
        }
    }
}