Skip to main content

securedrop_protocol_minimal/
ciphertext.rs

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