ucan_key_support/
p256.rs

1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3
4use p256::ecdsa::{
5    self,
6    signature::{Signer, Verifier},
7    Signature, SigningKey as P256PrivateKey, VerifyingKey as P256PublicKey,
8};
9
10use ucan::crypto::KeyMaterial;
11
12pub use ucan::crypto::{did::P256_MAGIC_BYTES, JwtSignatureAlgorithm};
13
14pub fn bytes_to_p256_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
15    let public_key = P256PublicKey::try_from(bytes.as_slice())?;
16    Ok(Box::new(P256KeyMaterial(public_key, None)))
17}
18
19/// Support for NIST P-256 keys, aka secp256r1, aka ES256
20#[derive(Clone)]
21pub struct P256KeyMaterial(pub P256PublicKey, pub Option<P256PrivateKey>);
22
23#[cfg_attr(target_arch="wasm32", async_trait(?Send))]
24#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
25impl KeyMaterial for P256KeyMaterial {
26    fn get_jwt_algorithm_name(&self) -> String {
27        JwtSignatureAlgorithm::ES256.to_string()
28    }
29
30    async fn get_did(&self) -> Result<String> {
31        let bytes = [P256_MAGIC_BYTES, &self.0.to_encoded_point(true).to_bytes()].concat();
32        Ok(format!("did:key:z{}", bs58::encode(bytes).into_string()))
33    }
34
35    async fn sign(&self, payload: &[u8]) -> Result<Vec<u8>> {
36        match self.1 {
37            Some(ref private_key) => {
38                let signature: ecdsa::Signature = private_key.sign(payload);
39                Ok(signature.to_vec())
40            }
41            None => Err(anyhow!("No private key; cannot sign data")),
42        }
43    }
44
45    async fn verify(&self, payload: &[u8], signature: &[u8]) -> Result<()> {
46        let signature = Signature::try_from(signature)?;
47        self.0
48            .verify(payload, &signature)
49            .map_err(|error| anyhow!("Could not verify signature: {:?}", error))
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::{bytes_to_p256_key, P256KeyMaterial, P256_MAGIC_BYTES};
56    use p256::ecdsa::{SigningKey as P256PrivateKey, VerifyingKey as P256PublicKey};
57    use ucan::{
58        builder::UcanBuilder,
59        crypto::{did::DidParser, KeyMaterial},
60        ucan::Ucan,
61    };
62
63    #[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
64    async fn it_can_sign_and_verify_a_ucan() {
65        let private_key = P256PrivateKey::random(&mut p256::elliptic_curve::rand_core::OsRng);
66        let public_key = P256PublicKey::from(&private_key);
67
68        let key_material = P256KeyMaterial(public_key, Some(private_key));
69        let token_string = UcanBuilder::default()
70            .issued_by(&key_material)
71            .for_audience(key_material.get_did().await.unwrap().as_str())
72            .with_lifetime(60)
73            .build()
74            .unwrap()
75            .sign()
76            .await
77            .unwrap()
78            .encode()
79            .unwrap();
80
81        let mut did_parser = DidParser::new(&[(P256_MAGIC_BYTES, bytes_to_p256_key)]);
82
83        let ucan = Ucan::try_from(token_string).unwrap();
84        ucan.check_signature(&mut did_parser).await.unwrap();
85    }
86}