summaryrefslogtreecommitdiffstats
path: root/adenosine-pds/src/ucan_p256.rs
blob: 21e8a9a6faa97fb0a1d1302f5565a070ce6f8194 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/// 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 private_key = P256PrivateKey::random(&mut p256::elliptic_curve::rand_core::OsRng);
        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(&token_string).unwrap();
        ucan.check_signature(&mut did_parser).await.unwrap();
    }
}