ucan_key_support/
rsa.rs

1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3
4use rsa::{
5    pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey},
6    Pkcs1v15Sign, RsaPrivateKey, RsaPublicKey,
7};
8
9use sha2::{Digest, Sha256};
10use ucan::crypto::{JwtSignatureAlgorithm, KeyMaterial};
11
12pub use ucan::crypto::did::RSA_MAGIC_BYTES;
13
14pub fn bytes_to_rsa_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
15    println!("Trying to parse RSA key...");
16    // NOTE: DID bytes are PKCS1, but we store RSA keys as PKCS8
17    let public_key = RsaPublicKey::from_pkcs1_der(&bytes)?;
18
19    Ok(Box::new(RsaKeyMaterial(public_key, None)))
20}
21
22#[derive(Clone)]
23pub struct RsaKeyMaterial(pub RsaPublicKey, pub Option<RsaPrivateKey>);
24
25#[cfg_attr(target_arch="wasm32", async_trait(?Send))]
26#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
27impl KeyMaterial for RsaKeyMaterial {
28    fn get_jwt_algorithm_name(&self) -> String {
29        JwtSignatureAlgorithm::RS256.to_string()
30    }
31
32    async fn get_did(&self) -> Result<String> {
33        let bytes = match self.0.to_pkcs1_der() {
34            Ok(document) => [RSA_MAGIC_BYTES, document.as_bytes()].concat(),
35            Err(error) => {
36                // TODO: Probably shouldn't swallow this error...
37                warn!("Could not get RSA public key bytes for DID: {:?}", error);
38                Vec::new()
39            }
40        };
41        Ok(format!("did:key:z{}", bs58::encode(bytes).into_string()))
42    }
43
44    async fn sign(&self, payload: &[u8]) -> Result<Vec<u8>> {
45        let mut hasher = Sha256::new();
46        hasher.update(payload);
47        let hashed = hasher.finalize();
48
49        match &self.1 {
50            Some(private_key) => {
51                let padding = Pkcs1v15Sign::new::<Sha256>();
52                let signature = private_key.sign(padding, hashed.as_ref())?;
53                info!("SIGNED!");
54                Ok(signature)
55            }
56            None => Err(anyhow!("No private key; cannot sign data")),
57        }
58    }
59
60    async fn verify(&self, payload: &[u8], signature: &[u8]) -> Result<()> {
61        let mut hasher = Sha256::new();
62        hasher.update(payload);
63        let hashed = hasher.finalize();
64        let padding = Pkcs1v15Sign::new::<Sha256>();
65
66        self.0
67            .verify(padding, hashed.as_ref(), signature)
68            .map_err(|error| anyhow!(error))
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::{bytes_to_rsa_key, RsaKeyMaterial, RSA_MAGIC_BYTES};
75
76    use rsa::{pkcs8::DecodePrivateKey, RsaPrivateKey, RsaPublicKey};
77    use ucan::{
78        builder::UcanBuilder,
79        crypto::{did::DidParser, KeyMaterial},
80        ucan::Ucan,
81    };
82
83    #[cfg(target_arch = "wasm32")]
84    use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
85
86    #[cfg(target_arch = "wasm32")]
87    wasm_bindgen_test_configure!(run_in_browser);
88
89    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
90    #[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
91    async fn it_can_sign_and_verify_a_ucan() {
92        let private_key =
93            RsaPrivateKey::from_pkcs8_der(include_bytes!("./fixtures/rsa_key.pk8")).unwrap();
94        let public_key = RsaPublicKey::from(&private_key);
95
96        let key_material = RsaKeyMaterial(public_key, Some(private_key));
97        let token_string = UcanBuilder::default()
98            .issued_by(&key_material)
99            .for_audience(key_material.get_did().await.unwrap().as_str())
100            .with_lifetime(60)
101            .build()
102            .unwrap()
103            .sign()
104            .await
105            .unwrap()
106            .encode()
107            .unwrap();
108
109        let mut did_parser = DidParser::new(&[(RSA_MAGIC_BYTES, bytes_to_rsa_key)]);
110
111        let ucan = Ucan::try_from(token_string).unwrap();
112        ucan.check_signature(&mut did_parser).await.unwrap();
113    }
114}