etf_sdk/
api.rs

1use etf_crypto_primitives::{
2    proofs::{dleq::DLEQProof, verifier::DleqVerifier},
3    ibe::fullident::Ibe,
4    client::etf_client::{EtfClient, AesIbeCt},
5};
6
7use rand_chacha::ChaCha20Rng;
8use ark_std::rand::SeedableRng;
9use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
10
11#[derive(Debug)]
12pub enum Error {
13    EncryptionError,
14    DecryptionError,
15}
16
17// these are the funcs that I want to compile to wasm
18pub trait EtfApi<D: DleqVerifier, I: Ibe, E: EtfClient<I>> {
19
20    /// verify the DLEQ proof
21    fn verify(
22        id: Vec<u8>, 
23        dleq_proof: DLEQProof,
24        extras: Vec<u8>,
25    ) -> bool;
26
27    /// encrypt the message for the given slot ids
28    fn encrypt(
29        ibe_pp_bytes: Vec<u8>, 
30        p_pub: Vec<u8>, 
31        message: &[u8], 
32        slot_ids: Vec<Vec<u8>>, 
33        t: u8,
34        seed: &[u8],
35    ) -> Result<AesIbeCt, Error>;
36
37    // decrypt the message with the given sk
38    fn decrypt(
39        ibe_pp_bytes: Vec<u8>,
40        ciphertext: Vec<u8>,
41        nonce: Vec<u8>,
42        capsule: Vec<Vec<u8>>, 
43        sks: Vec<Vec<u8>>,
44    ) -> Result<Vec<u8>, Error>;
45}
46
47///  the default implementation of the etf api
48/// https://stackoverflow.com/questions/50200197/how-do-i-share-a-struct-containing-a-phantom-pointer-among-threads
49#[derive(Debug, CanonicalSerialize, CanonicalDeserialize)]
50// pub struct DefaultApi<D: DleqVerifier, I: Ibe, E: EtfClient<I>> {
51pub struct DefaultApi<D: DleqVerifier, I: Ibe, E: EtfClient<I>> {
52    // ibe: BfIbe,
53    _d: ark_std::marker::PhantomData<fn() -> D>,
54    _i: ark_std::marker::PhantomData<fn() -> I>,
55    _e: ark_std::marker::PhantomData<fn() -> E>,
56}
57
58impl<D: DleqVerifier, I: Ibe, E: EtfClient<I>> EtfApi<D, I, E> for DefaultApi<D, I, E>  {
59
60    /// verify a dleq proof using the IbeDleqVerifier
61    /// The verifier expects a specific G1 generator and a specific hash to g1 function
62    /// which the dleq proof must have used, otherwise it will fail
63    ///
64    /// * `id`:
65    /// * `proof`:
66    /// * `extras`: 
67    ///
68    fn verify(
69        id: Vec<u8>,
70        proof: DLEQProof,
71        extras: Vec<u8>,
72    ) -> bool {
73        D::verify(id, proof, extras)
74    }
75
76    /// encrypt a message using AES-GCM
77    /// with the ephemeral secret split into shares and encrypted for the future slot ids
78    /// TODO: more intelligent error mapping...
79    ///
80    fn encrypt(
81        ibe_pp_bytes: Vec<u8>,
82        p_pub_bytes: Vec<u8>,
83        message: &[u8], 
84        slot_ids: Vec<Vec<u8>>,
85        t: u8,
86        seed: &[u8],
87    ) -> Result<AesIbeCt, Error> {
88        let seed_hash = etf_crypto_primitives::utils::sha256
89            (&etf_crypto_primitives::utils::sha256(seed));
90        let rng = ChaCha20Rng::from_seed(seed_hash.try_into().expect("should be 32 bytes; qed"));
91        let res = E::encrypt(ibe_pp_bytes, p_pub_bytes, message, slot_ids, t, rng)
92            .map_err(|_| Error::EncryptionError)?;
93        Ok(res)
94    }
95
96    fn decrypt(
97        ibe_pp_bytes: Vec<u8>,
98        ciphertext: Vec<u8>,
99        nonce: Vec<u8>,
100        capsule: Vec<Vec<u8>>, 
101        sks: Vec<Vec<u8>>,
102    ) -> Result<Vec<u8>, Error> {
103        let res = E::decrypt(ibe_pp_bytes, ciphertext, nonce, capsule, sks)
104            .map_err(|_| Error::DecryptionError)?;
105        Ok(res)
106    }
107}
108
109#[cfg(test)]
110pub mod tests {
111    use super::*;
112    use ark_std::{test_rng, UniformRand, ops::Mul, rand::{CryptoRng, Rng}};
113    use ark_bls12_381::{G1Affine as G1, G2Affine as G2, G1Projective, G2Projective, Fr};
114    use ark_ec::AffineRepr;
115    use ark_serialize::CanonicalSerialize;
116    use etf_crypto_primitives::{
117        utils::hash_to_g1,
118        client::etf_client::{AesIbeCt, ClientError},
119        ibe::fullident::{IbeCiphertext, Ibe},
120        encryption::aes::AESOutput,
121        utils::convert_to_bytes,
122    };
123
124
125    // A mock implementation of DleqVerifier trait for testing
126    struct MockDleqVerifier;
127
128    impl DleqVerifier for MockDleqVerifier {
129        // Implement the required methods for the trait
130        fn verify(_id: Vec<u8>, _proof: DLEQProof, _extras: Vec<u8>) -> bool {
131            true
132        }
133    }
134 
135    // A mock implementation of EtfClient trait for testing
136    struct MockEtfClient;
137
138    impl<I: Ibe> EtfClient<I> for MockEtfClient {
139        // Implement the required methods for the trait
140 
141        fn encrypt<R: Rng + CryptoRng + Sized>(
142            _p: Vec<u8>, _q: Vec<u8>, _m: &[u8], _ids: Vec<Vec<u8>>, _t: u8, _rng: R,
143        ) -> Result<AesIbeCt, ClientError> {
144            Ok(AesIbeCt {
145                aes_ct: AESOutput {
146                    ciphertext: vec![1, 2, 3],
147                    nonce: vec![2, 3, 4],
148                    key: vec![3, 4, 5],
149                },
150                etf_ct:  vec![vec![4], vec![5], vec![6]].into(),
151            })
152        }
153        fn decrypt(
154            _p: Vec<u8>, 
155            _ct: Vec<u8>, 
156            _nonce: Vec<u8>, 
157            _capsule: Vec<Vec<u8>>, 
158            _secrets: Vec<Vec<u8>>
159        ) -> Result<Vec<u8>, ClientError> {
160            Ok(vec![5, 6, 7])
161        }
162    }
163
164    struct MockIbe;
165
166    impl Ibe for MockIbe {
167        fn encrypt<R: Rng + Sized>(
168            ibe_pp: G2Projective, 
169            _p_pub: G2Projective,
170            _message: &[u8;32], 
171            _identity: &[u8], 
172            _rng: R
173        ) -> IbeCiphertext {
174            IbeCiphertext{ u: ibe_pp, v: Vec::new(), w: Vec::new() }
175        }
176    
177        fn decrypt(_ibe_pp: G2Projective, _ciphertext: IbeCiphertext, _sk: G1Projective) -> Vec<u8> {
178            Vec::new()
179        }
180    }
181 
182    #[test]
183    fn default_api_can_verify() {
184        let x = Fr::rand(&mut test_rng());
185        let id = b"test";
186        let g = G1::generator();
187        let h = hash_to_g1(id);
188
189
190        // create IBE public parameters
191        let ibe_pp: G2 = G2::generator().into();
192        let s = Fr::rand(&mut test_rng());
193        let p_pub: G2 = ibe_pp.mul(s).into();
194
195        let mut ibe_pp_bytes = Vec::new();
196        ibe_pp.serialize_compressed(&mut ibe_pp_bytes).unwrap();
197
198        let mut p_pub_bytes = Vec::new();
199        p_pub.serialize_compressed(&mut p_pub_bytes).unwrap();
200
201        let proof = DLEQProof::new(x, g, h, vec![], test_rng());
202        assert!(
203            DefaultApi::<MockDleqVerifier, MockIbe, MockEtfClient>::verify(
204                id.to_vec(), proof, vec![]) == true);
205    }
206
207    #[test]
208    fn api_encryption_works() {
209        let message = b"this is a test";
210        let slot_ids = vec![b"sl1".to_vec(), b"sl2".to_vec(), b"sl3".to_vec()];
211        let t = 2;
212        let ibe_pp: G2 = G2::generator().into();
213        let s = Fr::rand(&mut test_rng());
214        let p_pub: G2 = ibe_pp.mul(s).into();
215
216        let ibe_pp_bytes = convert_to_bytes::<G2, 96>(ibe_pp);
217        let p_pub_bytes = convert_to_bytes::<G2, 96>(p_pub);
218
219        match DefaultApi::<MockDleqVerifier, MockIbe, MockEtfClient>::
220            encrypt(ibe_pp_bytes.to_vec(), p_pub_bytes.to_vec(), message, slot_ids, t, b"seed") {
221                Ok(_) => { },
222                Err(_) => { panic!("the encrypt call should work") },
223        }
224    }
225
226    #[test]
227    fn api_decryption_works() {
228        match DefaultApi::<MockDleqVerifier, MockIbe, MockEtfClient>::
229            decrypt(vec![], vec![], vec![], vec![vec![1]], vec![]) {
230                Ok(_) => { },
231                Err(_) => { panic!("the decrypt call should work") },
232        }
233    }
234}