valence-crypto-utils 0.1.2

A collection of crypto utilities for the Valence ecosystem.
Documentation
use core::{fmt, str::FromStr};

use alloc::{
    string::{String, ToString},
    vec::Vec,
};
use k256::ecdsa::SigningKey;
use serde::{Deserialize, Serialize};

use crate::Signer;

/// A [Signer] representation for environment variables.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SignerEnv {
    /// A secp256k1 signer representation.
    SecretEccNistP256(String),
}

impl Signer {
    /// Default environment variable name.
    pub const ENV: &str = "VALENCE_SIGNER";

    /// Attempts to create a secp256k1 signer from the provided secret.
    pub fn from_secret(secret: &[u8]) -> anyhow::Result<Self> {
        let secret = SigningKey::from_slice(secret)
            .map_err(|e| anyhow::anyhow!("error parsing the secret: {e}"))?;

        Ok(secret.into())
    }

    /// Returns the public identity of the signer.
    pub fn to_public(&self) -> Vec<u8> {
        match self {
            Signer::SecretEccNistP256(secret) => secret.verifying_key().to_sec1_bytes().to_vec(),
        }
    }

    /// Writes the serialized instance as environment variable into the formatter.
    pub fn to_env(&self) -> String {
        SignerEnv::from(self).to_string()
    }
}

impl From<&Signer> for SignerEnv {
    fn from(signer: &Signer) -> Self {
        match signer {
            Signer::SecretEccNistP256(secret) => {
                let secret = secret.to_bytes();
                let secret = const_hex::encode(secret);

                Self::SecretEccNistP256(secret)
            }
        }
    }
}

impl fmt::Display for Signer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use Signer::*;

        match self {
            SecretEccNistP256(secret) => {
                let public = secret.verifying_key().to_sec1_bytes();
                let public = const_hex::encode(public);

                write!(f, "EccNistP256({public})")
            }
        }
    }
}

impl fmt::Display for SignerEnv {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let env = serde_json::to_string(&self).expect("serialization is infallible");

        write!(f, "{env}")
    }
}

impl TryFrom<SignerEnv> for Signer {
    type Error = anyhow::Error;

    fn try_from(signer: SignerEnv) -> anyhow::Result<Self> {
        match signer {
            SignerEnv::SecretEccNistP256(secret) => {
                let secret = const_hex::decode(secret)?;

                Signer::from_secret(&secret)
            }
        }
    }
}

impl From<SigningKey> for Signer {
    fn from(secret: SigningKey) -> Self {
        Self::SecretEccNistP256(secret)
    }
}

impl FromStr for Signer {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> anyhow::Result<Self> {
        SignerEnv::from_str(s).and_then(Signer::try_from)
    }
}

impl FromStr for SignerEnv {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> anyhow::Result<Self> {
        Ok(serde_json::from_str(s)?)
    }
}

#[cfg(feature = "std")]
impl Signer {
    /// Attempts to create a signer from the default environment variable [Signer::ENV].
    pub fn try_from_env() -> anyhow::Result<Self> {
        let env = ::std::env::var(Self::ENV)?;

        Self::from_str(&env)
    }
}

#[cfg(all(test, feature = "std"))]
mod tests {
    use std::env;

    use super::*;

    #[test]
    fn signer_from_env_works() {
        let secret = "b0cb16ba60d3b770eb5e001efaa8f5b94270f4e8e858f673f5e896db11d21698";
        let secret = const_hex::decode(secret).unwrap();
        let signer = Signer::from_secret(&secret).unwrap();
        let public = signer.to_public();

        unsafe {
            env::set_var(Signer::ENV, signer.to_env());
        }

        let deserialized = Signer::try_from_env().unwrap().to_public();

        assert_eq!(public, deserialized);
    }
}