diff options
Diffstat (limited to 'adenosine-pds/src')
-rw-r--r-- | adenosine-pds/src/ucan_p256.rs | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/adenosine-pds/src/ucan_p256.rs b/adenosine-pds/src/ucan_p256.rs new file mode 100644 index 0000000..9fe89ed --- /dev/null +++ b/adenosine-pds/src/ucan_p256.rs @@ -0,0 +1,91 @@ +/// Implement UCAN KeyMaterial trait for p256 +/// +/// This is needed because the 'ucan-key-support' crate does not include support for this key type. +use anyhow::{anyhow, Result}; +use async_trait::async_trait; + +use p256::ecdsa::signature::{Signer, Verifier}; +use p256::ecdsa::{Signature, SigningKey as P256PrivateKey, VerifyingKey as P256PublicKey}; + +use ucan::crypto::KeyMaterial; + +pub use ucan::crypto::{did::P256_MAGIC_BYTES, JwtSignatureAlgorithm}; + +pub fn bytes_to_p256_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> { + let public_key = P256PublicKey::try_from(bytes.as_slice())?; + Ok(Box::new(P256KeyMaterial(public_key, None))) +} + +#[derive(Clone)] +pub struct P256KeyMaterial(pub P256PublicKey, pub Option<P256PrivateKey>); + +#[cfg_attr(target_arch="wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl KeyMaterial for P256KeyMaterial { + fn get_jwt_algorithm_name(&self) -> String { + JwtSignatureAlgorithm::ES256.to_string() + } + + async fn get_did(&self) -> Result<String> { + let bytes = [ + P256_MAGIC_BYTES, + &self.0.to_encoded_point(true).to_bytes().to_vec(), + ] + .concat(); + Ok(format!("did:key:z{}", bs58::encode(bytes).into_string())) + } + + async fn sign(&self, payload: &[u8]) -> Result<Vec<u8>> { + match self.1 { + Some(ref private_key) => { + let signature = private_key.sign(payload); + Ok(signature.to_vec()) + } + None => Err(anyhow!("No private key; cannot sign data")), + } + } + + async fn verify(&self, payload: &[u8], signature: &[u8]) -> Result<()> { + let signature = Signature::try_from(signature)?; + self.0 + .verify(payload, &signature) + .map_err(|error| anyhow!("Could not verify signature: {:?}", error)) + } +} + +#[cfg(test)] +mod tests { + use super::{bytes_to_p256_key, P256KeyMaterial, P256_MAGIC_BYTES}; + use p256::ecdsa::signature::{Signer, Verifier}; + use p256::ecdsa::{SigningKey as P256PrivateKey, VerifyingKey as P256PublicKey}; + use ucan::{ + builder::UcanBuilder, + crypto::{did::DidParser, KeyMaterial}, + ucan::Ucan, + }; + + #[cfg_attr(not(target_arch = "wasm32"), tokio::test)] + async fn it_can_sign_and_verify_a_ucan() { + let rng = rand::thread_rng(); + let private_key = P256PrivateKey::new(rng); + let public_key = P256PublicKey::from(&private_key); + + let key_material = P256KeyMaterial(public_key, Some(private_key)); + let token_string = UcanBuilder::default() + .issued_by(&key_material) + .for_audience(key_material.get_did().await.unwrap().as_str()) + .with_lifetime(60) + .build() + .unwrap() + .sign() + .await + .unwrap() + .encode() + .unwrap(); + + let mut did_parser = DidParser::new(&[(P256_MAGIC_BYTES, bytes_to_p256_key)]); + + let ucan = Ucan::try_from(token_string).unwrap(); + ucan.check_signature(&mut did_parser).await.unwrap(); + } +} |