use crate::util::read_file_to_string;
use anyhow::anyhow;
use qos_p256::P256Pair;
use std::future::Future;
use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;
pub trait Pair: Send + Sync {
fn sign(
&self,
message: Vec<u8>,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Vec<u8>>> + Send + '_>>;
fn public_key(&self) -> Vec<u8>;
}
#[derive(Clone)]
pub struct LocalPair {
pair: Arc<P256Pair>,
}
impl LocalPair {
pub async fn from_master_seed(path: &Path) -> anyhow::Result<Self> {
let hex_seed = read_file_to_string(path).await?;
Self::from_hex_seed(&hex_seed)
}
pub fn from_hex_seed(hex_seed: &str) -> anyhow::Result<Self> {
let bytes_seed: [u8; 32] = hex::decode(hex_seed.trim())?
.try_into()
.map_err(|v: Vec<u8>| anyhow!("seed must be exactly 32 bytes, got {}", v.len()))?;
let pair = P256Pair::from_master_seed(&bytes_seed)
.map_err(|_| anyhow!("could not create key from seed"))?;
Ok(Self {
pair: Arc::new(pair),
})
}
}
impl Pair for LocalPair {
fn sign(
&self,
message: Vec<u8>,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Vec<u8>>> + Send + '_>> {
let pair2 = Arc::clone(&self.pair);
Box::pin(async move {
tokio::task::spawn_blocking(move || {
pair2
.sign(&message)
.map_err(|_| anyhow!("failed to sign with local signer"))
})
.await?
})
}
fn public_key(&self) -> Vec<u8> {
self.pair.public_key().to_bytes()
}
}