etf_sdk/
lib.rs

1use wasm_bindgen::prelude::*;
2use ark_serialize::CanonicalDeserialize;
3use ark_std::{test_rng, UniformRand, ops::Mul};
4use ark_bls12_381::{Fr, G1Affine as G1, G2Affine as G2};
5use ark_ec::AffineRepr;
6use etf_crypto_primitives::{
7    ibe::fullident::BfIbe,
8    proofs::verifier::IbeDleqVerifier,
9    client::etf_client::DefaultEtfClient,
10    utils::{convert_to_bytes, hash_to_g1},
11};
12use serde::{Deserialize, Serialize};
13
14use crate::api::{
15    EtfApi, DefaultApi,
16};
17
18pub mod api;
19
20pub enum ApiError {
21    EncryptionError,
22    WasmBindError,
23}
24
25// TODO: enhance error types (using thiserror?)
26
27/// a wrapper around the DefaultEtfClient so that it can be compiled to wasm
28#[wasm_bindgen]
29pub struct EtfApiWrapper {    
30    pps: (JsValue, JsValue),
31}
32
33#[wasm_bindgen]
34impl EtfApiWrapper {
35
36    /// p and q are the IBE parameters, both elements of G2
37    #[wasm_bindgen(constructor)]
38    pub fn create(p: JsValue, q: JsValue) -> Self {
39        // TODO: verification
40        Self { pps: (p, q) }
41    }
42
43    #[wasm_bindgen]
44    pub fn version(&self) -> JsValue {
45        serde_wasm_bindgen::to_value(b"v0.0.3-dev").unwrap()
46    }
47
48    /// a wrapper function around the DefaultApi 'encrypt' implementation
49    /// returns a ciphertext blob containing both aes ct and etf ct
50    ///
51    ///
52    #[wasm_bindgen]
53    pub fn encrypt(
54        &self,
55        message_bytes: JsValue, // &[u8], 
56        slot_id_bytes: JsValue, // Vec<Vec<u8>>,
57        t: u8,
58        seed_bytes: JsValue,
59    ) -> Result<JsValue, JsError> {
60        // convert JsValue to types
61        let ibe_pp : Vec<u8> = serde_wasm_bindgen::from_value(self.pps.0.clone())
62            .map_err(|_| JsError::new("could not decode ibe pp"))?;
63        let p_pub : Vec<u8> = serde_wasm_bindgen::from_value(self.pps.1.clone())
64            .map_err(|_| JsError::new("could not decode p pub"))?;
65        let message : Vec<u8> = serde_wasm_bindgen::from_value(message_bytes)
66            .map_err(|_| JsError::new("could not decode message"))?;
67        let slot_ids: Vec<Vec<u8>> = serde_wasm_bindgen::from_value(slot_id_bytes)
68            .map_err(|_| JsError::new("could not decode slot ids"))?;
69        let seed: Vec<u8> = serde_wasm_bindgen::from_value(seed_bytes)
70            .map_err(|_| JsError::new("could not decode seed"))?;
71        // TODO: this should probably be an async future... in the future
72        let out = 
73            DefaultApi::<IbeDleqVerifier, BfIbe, DefaultEtfClient<BfIbe>>::encrypt(
74                ibe_pp, p_pub, &message, slot_ids, t, &seed)
75                    .map_err(|_| JsError::new("encrypt failed"))?;
76        serde_wasm_bindgen::to_value(&out)
77            .map_err(|_| JsError::new("could not convert to JsValue"))
78    }
79
80    #[wasm_bindgen]
81    pub fn decrypt(
82        &self,
83        ciphertext_bytes: JsValue, // Vec<u8>
84        nonce_bytes: JsValue, // Vec<u8>
85        capsule_bytes: JsValue, // Vec<Vec<u8>>
86        sks_bytes: JsValue, // Vec<Vec<u8>>
87    ) -> Result<JsValue, JsError> {
88        let ibe_pp : Vec<u8> = serde_wasm_bindgen::from_value(self.pps.0.clone())
89            .map_err(|_| JsError::new("could not decode ibe pp"))?;
90        let ct: Vec<u8>  = serde_wasm_bindgen::from_value(ciphertext_bytes)
91            .map_err(|_| JsError::new("could not decode the ciphertext"))?;
92        let nonce: Vec<u8>  = serde_wasm_bindgen::from_value(nonce_bytes)
93            .map_err(|_| JsError::new("could not decode the nonce"))?;
94
95        let capsule: Vec<Vec<u8>> = serde_wasm_bindgen::from_value(capsule_bytes)
96            .map_err(|_| JsError::new("could not decode the capsule"))?;
97        let sks: Vec<Vec<u8>> = serde_wasm_bindgen::from_value(sks_bytes)
98            .map_err(|_| JsError::new("could not decode the secret keys"))?;
99        let out = 
100            DefaultApi::<IbeDleqVerifier, BfIbe, DefaultEtfClient<BfIbe>>::decrypt(
101                ibe_pp, ct, nonce, capsule, sks)
102                .map_err(|_| JsError::new("decryption failed"))?;
103        serde_wasm_bindgen::to_value(&out)
104            .map_err(|_| JsError::new("could not convert to JsValue"))
105    }
106}
107
108#[derive(Debug, Serialize, Deserialize)]
109pub struct IbeTestParams {
110    pub p: Vec<u8>,
111    pub q: Vec<u8>,
112    pub s: Vec<u8>,
113}
114
115// the functions below are for testing purposes only
116// TODO: wrap this in a feature? how do features work with wasm?
117// #[cfg_attr(tarpaulin, skip)]
118#[wasm_bindgen]
119pub fn random_ibe_params() -> Result<JsValue, JsError> {
120    let ibe_pp: G2 = G2::generator();
121    let s = Fr::rand(&mut test_rng());
122    let p_pub: G2 = ibe_pp.mul(s).into();
123
124    serde_wasm_bindgen::to_value(&IbeTestParams {
125        p: convert_to_bytes::<G2, 96>(ibe_pp).to_vec(),
126        q: convert_to_bytes::<G2, 96>(p_pub).to_vec(),
127        s: convert_to_bytes::<Fr, 32>(s).to_vec(),
128    }).map_err(|_| JsError::new("could not convert ibe test params to JsValue"))
129}
130
131// #[cfg_attr(tarpaulin, skip)]
132#[wasm_bindgen]
133pub fn ibe_extract(x: JsValue, ids_bytes: JsValue) -> Result<JsValue, JsError> {
134    let ids: Vec<Vec<u8>> = serde_wasm_bindgen::from_value(ids_bytes)
135        .map_err(|_| JsError::new("could not decode ids"))?;
136    let sk_bytes: Vec<u8> = serde_wasm_bindgen::from_value(x)
137        .map_err(|_| JsError::new("could not decode secret x"))?;
138    let s = Fr::deserialize_compressed(&sk_bytes[..]).unwrap();
139    let mut secrets: Vec<(Vec<u8>, Vec<u8>)> = Vec::new();
140    for id in ids {
141        let pk = hash_to_g1(&id);
142        let sk = pk.mul(s);
143        let pk_bytes = convert_to_bytes::<G1, 48>(pk);
144        let sk_bytes = convert_to_bytes::<G1, 48>(sk.into());
145        secrets.push((sk_bytes.to_vec(), pk_bytes.to_vec()));
146    }
147
148    serde_wasm_bindgen::to_value(&secrets)
149        .map_err(|_| JsError::new("could not convert secrets to JsValue"))
150}
151
152#[cfg(test)]
153mod test {
154    use super::*;
155    use ark_std::test_rng;
156    use wasm_bindgen_test::*;
157
158    #[wasm_bindgen_test]
159    pub fn wrapper_setup_works() {
160        let x = serde_wasm_bindgen::to_value(&vec![1,2,3,4]).unwrap();
161        let etf = EtfApiWrapper::create(x.clone(), x);
162        let v = etf.version();
163        let version: Vec<u8> = serde_wasm_bindgen::from_value(v).unwrap();
164        assert_eq!(version, b"v0.0.3-dev".to_vec());
165    }
166
167    #[wasm_bindgen_test]
168    pub fn wrapper_can_encrypt() {
169        let message_js = serde_wasm_bindgen::to_value(b"test").unwrap();
170        let slot_ids = vec![vec![1,2,3], vec![2,3,4]];
171        let slot_ids_js = serde_wasm_bindgen::to_value(&slot_ids).unwrap();
172        let seed_js = serde_wasm_bindgen::to_value(b"seed").unwrap();
173
174        let s = Fr::rand(&mut test_rng());
175        let g = G2::rand(&mut test_rng());
176        let p: G2 = g.mul(s).into();
177        let g_bytes = convert_to_bytes::<G2, 96>(g).to_vec();
178        let p_bytes = convert_to_bytes::<G2, 96>(p).to_vec();
179        let x1 = serde_wasm_bindgen::to_value(&g_bytes).unwrap();
180        let x2 = serde_wasm_bindgen::to_value(&p_bytes).unwrap();
181        let etf = EtfApiWrapper::create(x1, x2);
182        match etf.encrypt(message_js, slot_ids_js, 3, seed_js) {
183            Ok(ct) => {
184                assert!(!ct.is_null());
185            },
186            Err(_) =>{
187                panic!("test should pass");
188            }
189        }
190    }
191
192    // #[wasm_bindgen_test]
193    // pub fn wrapper_can_decrypt() {
194    //     let message_js = serde_wasm_bindgen::to_value(b"test").unwrap();
195    //     let slot_ids = vec![vec![1, 2, 3], vec![2, 3, 4]];
196    //     let slot_ids_js = serde_wasm_bindgen::to_value(&slot_ids).unwrap();
197
198    //     let s = Fr::rand(&mut test_rng());
199    //     let g = G2::rand(&mut test_rng());
200    //     let p: G2 = g.mul(s).into();
201    //     let g_bytes = convert_to_bytes::<G2, 96>(g).to_vec();
202    //     let p_bytes = convert_to_bytes::<G2, 96>(p).to_vec();
203    //     let x1 = serde_wasm_bindgen::to_value(&g_bytes).unwrap();
204    //     let x2 = serde_wasm_bindgen::to_value(&p_bytes).unwrap();
205    //     let etf = EtfApiWrapper::create(x1, x2);
206    //     match etf.encrypt(message_js, slot_ids_js, 3) {
207    //         Ok(ct) => {
208    //             let t: crypto::client::client::AesIbeCt = serde_wasm_bindgen::from_value(ct).unwrap();
209    //             let ct_bytes = serde_wasm_bindgen::to_value(&t.aes_ct.ciphertext).unwrap();
210    //             let nonce_bytes = serde_wasm_bindgen::to_value(&t.aes_ct.nonce).unwrap();
211    //             let capsule_bytes = serde_wasm_bindgen::to_value(&t.etf_ct).unwrap();
212    //             // calc valid secrets d = sQ
213    //             let secrets: Vec<Vec<u8>> = slot_ids.iter().map(|id| {
214    //                 let q = hash_to_g1(&id);
215    //                 let d = q.mul(s);
216    //                 convert_to_bytes::<G1, 48>(d.into()).to_vec()
217    //             }).collect::<Vec<_>>();
218
219    //             let sks = serde_wasm_bindgen::to_value(&secrets).unwrap();
220
221    //             match etf.decrypt(ct_bytes, nonce_bytes, capsule_bytes, sks) {
222    //                 Ok(m_js) => {
223    //                     let m: Vec<u8> = serde_wasm_bindgen::from_value(m_js).unwrap();
224    //                     assert_eq!(m, b"test".to_vec());
225    //                 }, 
226    //                 Err(_) => {
227    //                     panic!("test should pass, but decryption failed");
228    //                 }
229    //             }
230    //         },
231    //         Err(_) => {
232    //             panic!("test should pass, but encryption failed");
233    //         }
234    //     }
235    // }
236}