use base64::{prelude::BASE64_URL_SAFE, Engine};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use sha3::Digest;
use crate::Result;
use crate::SignerUser;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignerSigned<T> {
pub sig: String,
pub msg: String,
pub pubkey: String,
#[serde(skip)]
pub _marker: std::marker::PhantomData<T>,
}
impl<T> SignerSigned<T>
where
T: Serialize + DeserializeOwned,
{
pub fn from_value(user: &SignerUser, value: &T) -> Result<Self> {
let msg = serde_json::to_string(value)?;
let msg = msg.as_bytes();
let mut hasher = sha3::Sha3_256::new();
hasher.update(&msg);
let hash = hasher.finalize();
let keypair = user.to_keypair()?;
let sig = keypair.sign_simple(b"", hash.as_slice());
Ok(SignerSigned {
sig: BASE64_URL_SAFE.encode(sig.to_bytes()),
msg: BASE64_URL_SAFE.encode(msg),
pubkey: user.public.pub_key.clone(),
_marker: std::marker::PhantomData,
})
}
pub fn from_field(pubkey: &str, msg: &str, sig: &str) -> Result<Self> {
Ok(SignerSigned {
sig: sig.to_string(),
msg: msg.to_string(),
pubkey: pubkey.to_string(),
_marker: std::marker::PhantomData,
})
}
pub fn verify_to_value(&self) -> Result<T> {
let msg = BASE64_URL_SAFE.decode(&self.msg)?;
let sig = BASE64_URL_SAFE.decode(&self.sig)?;
let mut hasher = sha3::Sha3_256::new();
hasher.update(&msg);
let hash = hasher.finalize();
let sig = schnorrkel::Signature::from_bytes(&sig)
.map_err(|e| crate::SignerError::Msg(format!("decode signature failed: {}", e)))?;
let pub_key = schnorrkel::PublicKey::from_bytes(&BASE64_URL_SAFE.decode(&self.pubkey)?)
.map_err(|e| crate::SignerError::Msg(format!("decode public key failed: {}", e)))?;
pub_key
.verify_simple(b"", hash.as_slice(), &sig)
.map_err(|e| crate::SignerError::Msg(format!("verify signature failed: {}", e)))?;
let value = serde_json::from_str(&String::from_utf8(msg)?)?;
Ok(value)
}
}