From 498f9e0b0b87e6d497b235474c0db669076d73ff Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 3 Nov 2022 17:29:27 -0700 Subject: start adding ucan support --- adenosine-pds/Cargo.toml | 4 ++ adenosine-pds/src/ucan_p256.rs | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 adenosine-pds/src/ucan_p256.rs (limited to 'adenosine-pds') diff --git a/adenosine-pds/Cargo.toml b/adenosine-pds/Cargo.toml index 8fdc138..432e893 100644 --- a/adenosine-pds/Cargo.toml +++ b/adenosine-pds/Cargo.toml @@ -36,6 +36,10 @@ data-encoding = "*" k256 = { version = "*", features = ["ecdsa"] } p256 = { version = "*", features = ["ecdsa"] } multibase = "*" +ucan = "0.7.0-alpha.1" +# TODO: replace this with data-encoding or similar; this is only needed for ucan_p256 stuff +bs58 = "*" +async-trait = "*" [package.metadata.deb] maintainer = "Bryan Newbold " 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) -> Result> { + 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); + +#[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 { + 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> { + 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(); + } +} -- cgit v1.2.3