use core::{fmt, str::FromStr};
use alloc::{
string::{String, ToString},
vec::Vec,
};
use k256::ecdsa::SigningKey;
use serde::{Deserialize, Serialize};
use crate::Signer;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SignerEnv {
SecretEccNistP256(String),
}
impl Signer {
pub const ENV: &str = "VALENCE_SIGNER";
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())
}
pub fn to_public(&self) -> Vec<u8> {
match self {
Signer::SecretEccNistP256(secret) => secret.verifying_key().to_sec1_bytes().to_vec(),
}
}
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 {
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);
}
}