use std::sync::Arc;
use affinidi_tdk::secrets_resolver::secrets::{KeyType, Secret};
use serde::Serialize;
use serde::de::DeserializeOwned;
use trust_tasks_proof::affinidi::{CryptoSuite, SignOptions, sign_trust_task};
use trust_tasks_rs::TrustTask;
use crate::trust_tasks::proof::WRITE_PROOF_PURPOSE;
#[derive(Clone)]
pub struct ReplySigner {
secret: Arc<Secret>,
suite: CryptoSuite,
}
impl std::fmt::Debug for ReplySigner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReplySigner")
.field("verification_method", &self.secret.id)
.finish_non_exhaustive()
}
}
fn suite_for(key_type: KeyType) -> Option<CryptoSuite> {
match key_type {
KeyType::Ed25519 => Some(CryptoSuite::EddsaJcs2022),
KeyType::P256 | KeyType::P384 => Some(CryptoSuite::EcdsaJcs2019),
_ => None,
}
}
impl ReplySigner {
pub fn new(secret: Secret) -> Option<Self> {
let suite = suite_for(secret.get_key_type())?;
Some(Self {
secret: Arc::new(secret),
suite,
})
}
pub fn from_profile(did: &str, secrets: &[Secret]) -> Option<Self> {
secrets
.iter()
.filter(|s| s.id.split('#').next() == Some(did) && s.id.contains('#'))
.find_map(|s| Self::new(s.clone()))
}
pub fn for_config(config: &crate::configs::TrustRegistryConfig) -> Option<Self> {
let profile = &config.didcomm_config.profile_config;
Self::from_profile(&profile.did, &profile.secrets)
}
pub fn verification_method(&self) -> &str {
&self.secret.id
}
pub async fn sign<P: Serialize + DeserializeOwned>(
&self,
doc: &TrustTask<P>,
) -> Result<TrustTask<P>, String> {
let value = serde_json::to_value(doc).map_err(|e| e.to_string())?;
let signed = sign_trust_task(
&value,
self.secret.as_ref(),
SignOptions::new()
.with_proof_purpose(WRITE_PROOF_PURPOSE)
.with_cryptosuite(self.suite),
)
.await
.map_err(|e| e.to_string())?;
serde_json::from_value(signed).map_err(|e| e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_operational_key_is_picked_over_the_key_agreement_key() {
let did = "did:peer:2.Vz6Mk.Ez6LS";
let mut agreement = Secret::generate_x25519(None, Some(&[2; 32])).expect("x25519");
agreement.id = format!("{did}#key-2");
let mut signing = Secret::generate_ed25519(None, Some(&[1; 32]));
signing.id = format!("{did}#key-1");
let signer = ReplySigner::from_profile(did, &[agreement, signing]).expect("a signer");
assert_eq!(signer.verification_method(), format!("{did}#key-1"));
}
#[test]
fn a_key_of_another_did_is_never_picked() {
let mut other = Secret::generate_ed25519(None, Some(&[1; 32]));
other.id = "did:example:someone-else#key-1".to_string();
assert!(ReplySigner::from_profile("did:example:registry", &[other]).is_none());
}
}