Skip to main content

pg_core/client/rust/
mod.rs

1//! This module utilizes the symmetric primitives provided by [`Rust
2//! Crypto`](https://github.com/RustCrypto). The streaming interface, enabled using the feature
3//! `stream` is a small wrapper around [`aead::stream`]. This feature enables an interface
4//! to encrypt data using asynchronous byte streams, specifically from an
5//! [AsyncRead][`futures::io::AsyncRead`] into an [AsyncWrite][`futures::io::AsyncWrite`].
6
7use alloc::string::ToString;
8use alloc::vec::Vec;
9
10use crate::artifacts::{PublicKey, UserSecretKey, VerifyingKey};
11use crate::client::*;
12use crate::error::Error;
13use crate::identity::EncryptionPolicy;
14
15use aead::{Aead, KeyInit};
16use aes_gcm::{Aes128Gcm, Nonce};
17use ibe::kem::cgw_kv::CGWKV;
18use ibs::gg::Signer;
19use rand::{CryptoRng, RngCore};
20
21#[cfg(feature = "stream")]
22pub mod stream;
23
24/// In-memory configuration for a [`Sealer`].
25#[derive(Debug)]
26pub struct SealerMemoryConfig {
27    key: [u8; KEY_SIZE],
28    nonce: [u8; IV_SIZE],
29}
30
31/// In-memory configuration for an [`Unsealer`].
32#[derive(Debug)]
33pub struct UnsealerMemoryConfig {
34    message_len: usize,
35}
36
37impl SealerConfig for SealerMemoryConfig {}
38impl super::sealed::SealerConfig for SealerMemoryConfig {}
39
40impl UnsealerConfig for UnsealerMemoryConfig {}
41impl super::sealed::UnsealerConfig for UnsealerMemoryConfig {}
42
43impl From<aead::Error> for Error {
44    fn from(_: aead::Error) -> Self {
45        Self::Symmetric
46    }
47}
48
49impl From<aes_gcm::aes::cipher::InvalidLength> for Error {
50    fn from(_: aes_gcm::aes::cipher::InvalidLength) -> Self {
51        Self::Symmetric
52    }
53}
54
55#[derive(Debug, Serialize, Deserialize)]
56struct MessageAndSignature {
57    message: Vec<u8>,
58    sig: SignatureExt,
59}
60
61impl<'r, R: RngCore + CryptoRng> Sealer<'r, R, SealerMemoryConfig> {
62    /// Create a new [`Sealer`].
63    pub fn new(
64        mpk: &PublicKey<CGWKV>,
65        policies: &EncryptionPolicy,
66        pub_sign_key: &SigningKeyExt,
67        rng: &'r mut R,
68    ) -> Result<Self, Error> {
69        let (header, ss) = Header::new(mpk, policies, rng)?;
70        let Algorithm::Aes128Gcm(iv) = header.algo;
71
72        let mut key = [0u8; KEY_SIZE];
73        let mut nonce = [0u8; IV_SIZE];
74        key.copy_from_slice(&ss.0[..KEY_SIZE]);
75        nonce.copy_from_slice(&iv.0[..IV_SIZE]);
76
77        Ok(Self {
78            rng,
79            header,
80            pub_sign_key: pub_sign_key.clone(),
81            priv_sign_key: None,
82            config: SealerMemoryConfig { key, nonce },
83        })
84    }
85
86    /// Seals the entire payload.
87    pub fn seal(mut self, message: impl AsRef<[u8]>) -> Result<Vec<u8>, Error> {
88        let mut out = Vec::with_capacity(message.as_ref().len() + 1024);
89
90        out.extend_from_slice(&PRELUDE);
91        out.extend_from_slice(&VERSION_V3.to_be_bytes());
92
93        self.header = self.header.with_mode(Mode::InMemory {
94            size: message.as_ref().len().try_into()?,
95        });
96
97        let header_buf = crate::bincode_compat::serialize(&self.header)?;
98        out.extend_from_slice(&u32::try_from(header_buf.len())?.to_be_bytes());
99        out.extend_from_slice(&header_buf);
100
101        let signer = Signer::new().chain(header_buf);
102        let h_sig = signer.clone().sign(&self.pub_sign_key.key.0, self.rng);
103
104        let h_sig_ext = SignatureExt {
105            sig: h_sig,
106            pol: self.pub_sign_key.policy.clone(),
107        };
108
109        let h_sig_ext_bytes = crate::bincode_compat::serialize(&h_sig_ext)?;
110        out.extend_from_slice(&u32::try_from(h_sig_ext_bytes.len())?.to_be_bytes());
111        out.extend_from_slice(&h_sig_ext_bytes);
112
113        let m_sig_key = self.priv_sign_key.unwrap_or(self.pub_sign_key);
114        let m_sig = signer.chain(&message).sign(&m_sig_key.key.0, self.rng);
115
116        let aead = Aes128Gcm::new_from_slice(&self.config.key)?;
117        let nonce = Nonce::from(self.config.nonce);
118
119        let enc_input = crate::bincode_compat::serialize(&MessageAndSignature {
120            message: message.as_ref().to_vec(),
121            sig: SignatureExt {
122                sig: m_sig,
123                pol: m_sig_key.policy,
124            },
125        })?;
126
127        let ciphertext = aead.encrypt(&nonce, enc_input.as_ref())?;
128
129        out.extend_from_slice(&ciphertext);
130
131        Ok(out)
132    }
133}
134
135impl Unsealer<Vec<u8>, UnsealerMemoryConfig> {
136    /// Create a new [`Unsealer`].
137    pub fn new(input: impl AsRef<[u8]>, vk: &VerifyingKey) -> Result<Self, Error> {
138        let b = input.as_ref();
139        let (preamble_bytes, b) = try_split_at(b, PREAMBLE_SIZE, "preamble")?;
140        let (version, header_len) = preamble_checked(preamble_bytes)?;
141
142        let (header_bytes, b) = try_split_at(b, header_len, "header")?;
143        let (h_sig_len_bytes, b) = try_split_at(b, SIG_SIZE_SIZE, "header signature length")?;
144        let h_sig_len = u32::from_be_bytes(h_sig_len_bytes.try_into()?);
145        let (h_sig_bytes, ct) = try_split_at(b, h_sig_len as usize, "header signature")?;
146
147        let h_sig_ext: SignatureExt = crate::bincode_compat::deserialize(h_sig_bytes)?;
148        let id = h_sig_ext.pol.derive_ibs()?;
149
150        let verifier = Verifier::default().chain(header_bytes);
151
152        if !verifier.clone().verify(&vk.0, &h_sig_ext.sig, &id) {
153            return Err(Error::IncorrectSignature);
154        }
155
156        let header: Header = crate::bincode_compat::deserialize(header_bytes)?;
157        let message_len = match header.mode {
158            Mode::InMemory { size } => size as usize,
159            _ => return Err(Error::ModeNotSupported(header.mode)),
160        };
161
162        Ok(Self {
163            version,
164            header,
165            pub_id: h_sig_ext.pol,
166            r: ct.to_vec(),
167            verifier,
168            vk: vk.clone(),
169            config: UnsealerMemoryConfig { message_len },
170        })
171    }
172
173    /// Unseals the payload.
174    pub fn unseal(
175        self,
176        ident: &str,
177        usk: &UserSecretKey<CGWKV>,
178    ) -> Result<(Vec<u8>, VerificationResult), Error> {
179        let rec_info = self
180            .header
181            .recipients
182            .get(ident)
183            .ok_or_else(|| Error::UnknownIdentifier(ident.to_string()))?;
184
185        let ss = rec_info.decaps(usk)?;
186        let key = &ss.0[..KEY_SIZE];
187
188        let Algorithm::Aes128Gcm(iv) = self.header.algo;
189
190        let aead = Aes128Gcm::new_from_slice(key)?;
191        let nonce = Nonce::from(iv.0);
192
193        let plain = aead.decrypt(&nonce, &*self.r)?;
194
195        let msg: MessageAndSignature = crate::bincode_compat::deserialize(&plain)?;
196        let id = msg.sig.pol.derive_ibs()?;
197
198        if !self
199            .verifier
200            .chain(&msg.message)
201            .verify(&self.vk.0, &msg.sig.sig, &id)
202        {
203            return Err(Error::IncorrectSignature);
204        }
205
206        debug_assert_eq!(self.config.message_len, msg.message.len());
207
208        let private = if self.pub_id == msg.sig.pol {
209            None
210        } else {
211            Some(msg.sig.pol)
212        };
213
214        Ok((
215            msg.message,
216            VerificationResult {
217                public: self.pub_id,
218                private,
219            },
220        ))
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227    use crate::test::TestSetup;
228
229    #[test]
230    fn test_seal_memory() {
231        let mut rng = rand::thread_rng();
232        let setup = TestSetup::new(&mut rng);
233
234        // Alice email
235        let pub_sign_key = &setup.signing_keys[0];
236        // Alice bsn
237        let priv_sign_key = &setup.signing_keys[1];
238
239        let input = b"SECRET DATA";
240        let sealed = Sealer::<_, SealerMemoryConfig>::new(
241            &setup.ibe_pk,
242            &setup.policy,
243            &pub_sign_key,
244            &mut rng,
245        )
246        .unwrap()
247        .with_priv_signing_key(priv_sign_key.clone())
248        .seal(input)
249        .unwrap();
250
251        // Take Bob's USK for email + name
252        let usk = &setup.usks[2];
253        let (original, verified_policy) =
254            Unsealer::<_, UnsealerMemoryConfig>::new(sealed, &setup.ibs_pk)
255                .unwrap()
256                .unseal("Bob", &usk)
257                .unwrap();
258
259        assert_eq!(&input.to_vec(), &original);
260
261        let expected = VerificationResult {
262            public: setup.policies[0].clone(),
263            private: Some(setup.policies[1].clone()),
264        };
265
266        assert_eq!(&verified_policy, &expected);
267    }
268
269    #[test]
270    fn test_seal_unseal_wrong_usk() {
271        let mut rng = rand::thread_rng();
272        let setup = TestSetup::new(&mut rng);
273
274        let pub_sign_key = &setup.signing_keys[0];
275        let priv_sign_key = &setup.signing_keys[1];
276
277        let input = b"SECRET DATA";
278        let sealed = Sealer::<_, SealerMemoryConfig>::new(
279            &setup.ibe_pk,
280            &setup.policy,
281            &pub_sign_key,
282            &mut rng,
283        )
284        .unwrap()
285        .with_priv_signing_key(priv_sign_key.clone())
286        .seal(input)
287        .unwrap();
288
289        // Take Charlie's USK for only name.
290        let usk = &setup.usks[4];
291        let res = Unsealer::<_, UnsealerMemoryConfig>::new(sealed, &setup.ibs_pk)
292            .unwrap()
293            .unseal("Charlie", &usk);
294
295        assert!(matches!(res, Err(Error::KEM)));
296    }
297
298    #[test]
299    fn test_seal_unseal_wrong_id() {
300        let mut rng = rand::thread_rng();
301        let setup = TestSetup::new(&mut rng);
302
303        let pub_sign_key = &setup.signing_keys[0];
304        let priv_sign_key = &setup.signing_keys[1];
305
306        let input = b"SECRET DATA";
307        let sealed = Sealer::<_, SealerMemoryConfig>::new(
308            &setup.ibe_pk,
309            &setup.policy,
310            &pub_sign_key,
311            &mut rng,
312        )
313        .unwrap()
314        .with_priv_signing_key(priv_sign_key.clone())
315        .seal(input)
316        .unwrap();
317
318        let usk = &setup.usks[4];
319        let res = Unsealer::<_, UnsealerMemoryConfig>::new(sealed, &setup.ibs_pk)
320            .unwrap()
321            .unseal("Daniel", &usk);
322
323        assert!(matches!(res, Err(Error::UnknownIdentifier(_))));
324    }
325
326    #[test]
327    fn test_unseal_rejects_empty_input() {
328        let mut rng = rand::thread_rng();
329        let setup = TestSetup::new(&mut rng);
330        let res = Unsealer::<_, UnsealerMemoryConfig>::new(&[] as &[u8], &setup.ibs_pk);
331        // Must not panic — should surface as NotPostGuard / FormatViolation.
332        assert!(res.is_err());
333    }
334
335    #[test]
336    fn test_unseal_rejects_truncated_after_preamble() {
337        let mut rng = rand::thread_rng();
338        let setup = TestSetup::new(&mut rng);
339
340        let pub_sign_key = &setup.signing_keys[0];
341        let priv_sign_key = &setup.signing_keys[1];
342
343        let sealed = Sealer::<_, SealerMemoryConfig>::new(
344            &setup.ibe_pk,
345            &setup.policy,
346            &pub_sign_key,
347            &mut rng,
348        )
349        .unwrap()
350        .with_priv_signing_key(priv_sign_key.clone())
351        .seal(b"SECRET DATA")
352        .unwrap();
353
354        // Keep the full preamble (so header_len parses) but truncate the body.
355        let mut truncated = sealed;
356        truncated.truncate(PREAMBLE_SIZE + 1);
357
358        let res = Unsealer::<_, UnsealerMemoryConfig>::new(truncated, &setup.ibs_pk);
359        match res {
360            Err(Error::FormatViolation(_)) => {}
361            other => panic!("expected FormatViolation, got {:?}", other),
362        }
363    }
364
365    #[test]
366    fn test_unseal_rejects_garbage_input() {
367        let mut rng = rand::thread_rng();
368        let setup = TestSetup::new(&mut rng);
369        // 1 KiB of zeros — no valid prelude, no valid lengths.
370        let garbage = vec![0u8; 1024];
371        let res = Unsealer::<_, UnsealerMemoryConfig>::new(garbage, &setup.ibs_pk);
372        assert!(res.is_err());
373    }
374
375    fn seal_memory<R: rand::RngCore + rand::CryptoRng>(setup: &TestSetup, rng: &mut R) -> Vec<u8> {
376        let pub_sign_key = &setup.signing_keys[0];
377        let priv_sign_key = &setup.signing_keys[1];
378        Sealer::<_, SealerMemoryConfig>::new(&setup.ibe_pk, &setup.policy, pub_sign_key, rng)
379            .unwrap()
380            .with_priv_signing_key(priv_sign_key.clone())
381            .seal(b"SECRET DATA")
382            .unwrap()
383    }
384
385    #[test]
386    fn test_unseal_rejects_input_shorter_than_preamble() {
387        let mut rng = rand::thread_rng();
388        let setup = TestSetup::new(&mut rng);
389        // One byte short of a preamble — preamble split must fail cleanly.
390        let buf = vec![0u8; PREAMBLE_SIZE - 1];
391        match Unsealer::<_, UnsealerMemoryConfig>::new(buf, &setup.ibs_pk) {
392            Err(Error::FormatViolation(msg)) => assert!(msg.contains("preamble")),
393            other => panic!("expected FormatViolation(preamble), got {:?}", other),
394        }
395    }
396
397    #[test]
398    fn test_unseal_rejects_truncated_inside_header() {
399        let mut rng = rand::thread_rng();
400        let setup = TestSetup::new(&mut rng);
401        let sealed = seal_memory(&setup, &mut rng);
402
403        // Keep preamble intact but drop most of the header.
404        let mut truncated = sealed;
405        truncated.truncate(PREAMBLE_SIZE + 4);
406
407        match Unsealer::<_, UnsealerMemoryConfig>::new(truncated, &setup.ibs_pk) {
408            Err(Error::FormatViolation(msg)) => assert!(msg.contains("header")),
409            other => panic!("expected FormatViolation(header), got {:?}", other),
410        }
411    }
412
413    #[test]
414    fn test_unseal_rejects_truncated_before_sig_len() {
415        let mut rng = rand::thread_rng();
416        let setup = TestSetup::new(&mut rng);
417        let sealed = seal_memory(&setup, &mut rng);
418
419        // Parse the header length so we know where the sig length begins,
420        // then cut the input right before the sig length bytes.
421        let (_, header_len) =
422            preamble_checked(&sealed[..PREAMBLE_SIZE]).expect("preamble should parse");
423        let cut = PREAMBLE_SIZE + header_len;
424
425        // Ensure we're strictly before the end of the sig-length field.
426        assert!(cut + SIG_SIZE_SIZE <= sealed.len());
427
428        let truncated = sealed[..cut + 1].to_vec();
429
430        match Unsealer::<_, UnsealerMemoryConfig>::new(truncated, &setup.ibs_pk) {
431            Err(Error::FormatViolation(msg)) => {
432                assert!(msg.contains("header signature length"))
433            }
434            other => panic!(
435                "expected FormatViolation(header signature length), got {:?}",
436                other
437            ),
438        }
439    }
440
441    #[test]
442    fn test_unseal_rejects_truncated_inside_sig_bytes() {
443        let mut rng = rand::thread_rng();
444        let setup = TestSetup::new(&mut rng);
445        let sealed = seal_memory(&setup, &mut rng);
446
447        let (_, header_len) =
448            preamble_checked(&sealed[..PREAMBLE_SIZE]).expect("preamble should parse");
449        // Keep preamble + header + sig-length + 1 byte of sig — sig is then truncated.
450        let cut = PREAMBLE_SIZE + header_len + SIG_SIZE_SIZE + 1;
451        assert!(cut < sealed.len(), "sealed output unexpectedly short");
452
453        let truncated = sealed[..cut].to_vec();
454
455        match Unsealer::<_, UnsealerMemoryConfig>::new(truncated, &setup.ibs_pk) {
456            Err(Error::FormatViolation(msg)) => {
457                assert!(msg.contains("header signature") && !msg.contains("length"))
458            }
459            other => panic!(
460                "expected FormatViolation(header signature), got {:?}",
461                other
462            ),
463        }
464    }
465
466    #[test]
467    fn test_unseal_rejects_wrong_prelude() {
468        let mut rng = rand::thread_rng();
469        let setup = TestSetup::new(&mut rng);
470        let mut sealed = seal_memory(&setup, &mut rng);
471
472        // Flip a byte in the prelude — must fall through as NotPostGuard,
473        // never panic.
474        sealed[0] = sealed[0].wrapping_add(1);
475
476        match Unsealer::<_, UnsealerMemoryConfig>::new(sealed, &setup.ibs_pk) {
477            Err(Error::NotPostGuard) => {}
478            other => panic!("expected NotPostGuard, got {:?}", other),
479        }
480    }
481}