Skip to main content

securedrop_protocol_minimal/
ciphertext.rs

1use crate::constants::{LEN_DH_ITEM, LEN_KMID, LEN_XWING_ENCAPS_KEY};
2use crate::message::MessageCiphertext;
3use crate::metadata::MetadataCiphertext;
4use alloc::vec::Vec;
5use anyhow::Error;
6
7/// The full submission `(C_S, X, Z)` sent from sender to server in step 6.
8///
9/// - `C_S = (ct^APKE, ct^PKE)`: the two ciphertexts
10/// - `X = g^x`: ephemeral DH public key (hint)
11/// - `Z = (pk_R^fetch)^x`: DH share for fetching (hint)
12///
13/// The server stores `(id, C_S, X, Z)` per message.
14#[derive(Debug, Clone)]
15pub struct Envelope {
16    /// `ct^APKE`: SD-APKE ciphertext `((c1, cp), c2)` - the encrypted message
17    pub(crate) ct_apke: MessageCiphertext,
18
19    /// `ct^PKE`: SD-PKE ciphertext `(c, c')` - the encrypted sender APKE public key
20    pub(crate) ct_pke: MetadataCiphertext,
21
22    /// `X = g^x`: ephemeral DH public key for the hint
23    pub(crate) mgdh_pubkey: [u8; LEN_DH_ITEM],
24
25    /// `Z = (pk_R^fetch)^x`: DH share for fetching
26    pub(crate) mgdh: [u8; LEN_DH_ITEM],
27}
28
29impl Envelope {
30    // Used for benchmarks - see wasm_bindgen
31    pub fn size_hint(&self) -> usize {
32        self.ct_apke.len() + self.ct_pke.len()
33    }
34
35    pub fn cmessage_len(&self) -> usize {
36        self.ct_apke.len()
37    }
38
39    // SD-PKE ciphertext byte length: encapsulation c + AEAD ciphertext c'
40    pub fn cmetadata_len(&self) -> usize {
41        self.ct_pke.len()
42    }
43}
44
45#[derive(Debug, Clone)]
46/// Toy pt structure - TODO: provide params in correct order
47pub struct Plaintext {
48    /// Metadata key: $pk_S^{PKE}$ in the spec
49    pub sender_reply_pubkey_hybrid: [u8; LEN_XWING_ENCAPS_KEY],
50    /// Fetching key: $pk_S^{fetch}$ in the spec
51    pub sender_fetch_key: [u8; LEN_DH_ITEM],
52    /// Message
53    pub msg: Vec<u8>,
54}
55
56impl Plaintext {
57    pub fn to_bytes(&self) -> alloc::vec::Vec<u8> {
58        // TODO: Deviates from spec
59        let mut buf = Vec::new();
60
61        buf.extend_from_slice(&self.sender_reply_pubkey_hybrid);
62        buf.extend_from_slice(&self.sender_fetch_key);
63        buf.extend_from_slice(&self.msg);
64
65        buf
66    }
67
68    pub fn len(&self) -> usize {
69        LEN_XWING_ENCAPS_KEY + LEN_DH_ITEM + self.msg.len()
70    }
71
72    // Toy parsing only
73    pub fn from_bytes(pt_bytes: &[u8]) -> Result<Self, Error> {
74        let mut offset = 0;
75
76        let mut sender_reply_pubkey_hybrid = [0u8; LEN_XWING_ENCAPS_KEY];
77        sender_reply_pubkey_hybrid
78            .copy_from_slice(&pt_bytes[offset..offset + LEN_XWING_ENCAPS_KEY]);
79        offset += LEN_XWING_ENCAPS_KEY;
80
81        let mut sender_fetch_key = [0u8; LEN_DH_ITEM];
82        sender_fetch_key.copy_from_slice(&pt_bytes[offset..offset + LEN_DH_ITEM]);
83        offset += LEN_DH_ITEM;
84
85        let msg = pt_bytes[offset..].to_vec();
86
87        Ok(Plaintext {
88            sender_reply_pubkey_hybrid,
89            sender_fetch_key,
90            msg,
91        })
92    }
93}
94
95#[derive(Clone, Debug)]
96pub struct FetchResponse {
97    pub(crate) enc_id: [u8; LEN_KMID],   // aka kmid
98    pub(crate) pmgdh: [u8; LEN_DH_ITEM], // aka per-request clue
99}
100
101impl FetchResponse {
102    pub fn new(enc_id: [u8; LEN_KMID], pmgdh: [u8; LEN_DH_ITEM]) -> Self {
103        Self { enc_id, pmgdh }
104    }
105}