private_box/
private_box.rs1use core::mem::size_of;
2use ssb_crypto::{Keypair, PublicKey};
3use zerocopy::{AsBytes, FromBytes, LayoutVerified};
4
5use ssb_crypto::ephemeral::{
8 derive_shared_secret_pk, derive_shared_secret_sk, generate_ephemeral_keypair, EphPublicKey,
9};
10use ssb_crypto::secretbox::{Hmac, Key, Nonce};
11
12const MAX_RECIPIENTS: usize = 8;
13
14#[cfg(feature = "sodium")]
18pub fn init() {
19 ssb_crypto::sodium::init();
20}
21
22#[derive(AsBytes, FromBytes)]
23#[repr(C, packed)]
24pub struct MsgKey {
25 recp_count: u8,
26 key: Key,
27}
28impl MsgKey {
29 fn zeroed() -> MsgKey {
30 MsgKey {
31 recp_count: 0,
32 key: Key([0; 32]),
33 }
34 }
35 pub fn as_array(&self) -> [u8; 33] {
36 let mut out = [0; 33];
37 out.copy_from_slice(self.as_bytes());
38 out
39 }
40}
41
42#[derive(AsBytes, FromBytes)]
43#[repr(C, packed)]
44struct BoxedKey {
45 hmac: Hmac,
46 msg_key: [u8; 33],
47}
48
49pub fn encrypted_size(text: &[u8], recps: &[PublicKey]) -> usize {
51 size_of::<Nonce>() + size_of::<EphPublicKey>() + recps.len() * size_of::<BoxedKey>() + size_of::<Hmac>() + text.len()
56}
57
58fn set_prefix<'a>(buf: &'a mut [u8], prefix: &[u8]) -> &'a mut [u8] {
59 let (p, rest) = buf.split_at_mut(prefix.len());
60 p.copy_from_slice(prefix);
61 rest
62}
63
64pub fn encrypt(plaintext: &[u8], recipients: &[PublicKey]) -> Vec<u8> {
92 let mut out = vec![0; encrypted_size(plaintext, recipients)];
93 encrypt_into(plaintext, recipients, &mut out);
94 out
95}
96
97pub fn encrypt_into(plaintext: &[u8], recipients: &[PublicKey], mut out: &mut [u8]) {
98 if recipients.len() > MAX_RECIPIENTS || recipients.len() == 0 {
99 panic!(
100 "Number of recipients must be less than {}, greater than 0",
101 MAX_RECIPIENTS
102 );
103 }
104 assert!(out.len() >= encrypted_size(plaintext, recipients));
105
106 let nonce = Nonce::generate();
107 let (eph_pk, eph_sk) = generate_ephemeral_keypair();
108
109 let mkey = MsgKey {
110 recp_count: recipients.len() as u8,
111 key: Key::generate(),
112 };
113
114 let mut rest = set_prefix(&mut out, nonce.as_bytes());
115 let rest = set_prefix(&mut rest, eph_pk.as_bytes());
116 let (keys, rest) = rest.split_at_mut(recipients.len() * size_of::<BoxedKey>());
117 let mut keychunks = keys.chunks_mut(size_of::<BoxedKey>());
118
119 for pk in recipients {
120 let kkey = Key(derive_shared_secret_pk(&eph_sk, pk).unwrap().0);
121 let mut msg_key = mkey.as_array();
122 let hmac = kkey.seal(&mut msg_key, &nonce);
123 keychunks
124 .next()
125 .unwrap()
126 .copy_from_slice(BoxedKey { hmac, msg_key }.as_bytes());
127 }
128
129 let (hmac_buf, text) = rest.split_at_mut(Hmac::SIZE);
130 text.copy_from_slice(plaintext);
131
132 let hmac = mkey.key.seal(text, &nonce);
133 hmac_buf.copy_from_slice(hmac.as_bytes());
134}
135
136const BOXED_KEY_SIZE_BYTES: usize = 32 + 1 + 16;
137
138pub fn decrypt(cyphertext: &[u8], keypair: &Keypair) -> Option<Vec<u8>> {
165 let msg_key = decrypt_key(cyphertext, keypair)?;
166 decrypt_body(cyphertext, &msg_key)
167}
168
169pub fn decrypt_key(cyphertext: &[u8], keypair: &Keypair) -> Option<MsgKey> {
171 let nonce = Nonce::from_slice(&cyphertext[0..24])?;
172 let eph_pk = EphPublicKey::from_slice(&cyphertext[24..56])?;
173
174 let key_key = Key(derive_shared_secret_sk(&keypair.secret, &eph_pk)?.0);
175 let mut msg_key = MsgKey::zeroed();
176
177 &cyphertext[56..]
178 .chunks_exact(BOXED_KEY_SIZE_BYTES)
179 .take(MAX_RECIPIENTS)
180 .find(|b| key_key.open_attached_into(b, &nonce, msg_key.as_bytes_mut()))?;
181
182 Some(msg_key)
183}
184
185pub fn decrypt_body(cyphertext: &[u8], msg_key: &MsgKey) -> Option<Vec<u8>> {
187 let nonce = Nonce::from_slice(&cyphertext[0..24])?;
188 let boxed_msg = &cyphertext[(56 + BOXED_KEY_SIZE_BYTES * msg_key.recp_count as usize)..];
189 let mut out = vec![0; boxed_msg.len() - Hmac::SIZE];
190 if msg_key.key.open_attached_into(&boxed_msg, &nonce, &mut out) {
191 Some(out)
192 } else {
193 None
194 }
195}
196
197pub fn decrypt_body_with_key_bytes(cyphertext: &[u8], msg_key: &[u8]) -> Option<Vec<u8>> {
199 let key = LayoutVerified::<&[u8], MsgKey>::new(msg_key)
200 .unwrap()
201 .into_ref();
202 decrypt_body(cyphertext, key)
203}
204
205#[cfg(test)]
206mod tests {
207 use crate::*;
208 use base64::decode;
209 use serde_derive::{Deserialize, Serialize};
210 use serde_json;
211
212 use std::error::Error;
213 use std::fs::File;
214 use std::path::Path;
215
216 use ssb_crypto::Keypair;
217
218 #[derive(Serialize, Deserialize)]
219 struct Key {
220 secret: String,
221 public: String,
222 }
223
224 #[derive(Serialize, Deserialize)]
225 struct TestData {
226 cypher_text: String,
227 msg: String,
228 keys: Vec<Key>,
229 }
230
231 fn read_test_data_from_file<P: AsRef<Path>>(path: P) -> Result<TestData, Box<dyn Error>> {
232 let file = File::open(path)?;
233 let t = serde_json::from_reader(file)?;
234 Ok(t)
235 }
236
237 #[test]
238 fn simple() {
239 let msg: [u8; 3] = [0, 1, 2];
240
241 let alice = Keypair::generate();
243 let bob = Keypair::generate();
244
245 let recps = [alice.public, bob.public];
246 let cypher = encrypt(&msg, &recps);
247
248 let alice_result = decrypt(&cypher, &alice);
249 let bob_result = decrypt(&cypher, &bob);
250
251 assert_eq!(alice_result.unwrap(), msg);
252 assert_eq!(bob_result.unwrap(), msg);
253 }
254
255 #[test]
256 fn is_js_compatible() {
257 let test_data = read_test_data_from_file("./test/simple.json").unwrap();
258
259 let cypher = decode(&test_data.cypher_text).unwrap();
260 let keys: Vec<Keypair> = test_data
261 .keys
262 .iter()
263 .map(|key| Keypair::from_base64(&key.secret).unwrap())
264 .collect();
265
266 let alice = &keys[0];
267 let bob = &keys[1];
268
269 assert_eq!(decrypt(&cypher, &alice).unwrap(), test_data.msg.as_bytes());
271 assert_eq!(decrypt(&cypher, &bob).unwrap(), test_data.msg.as_bytes());
272 }
273 #[test]
274 #[should_panic]
275 fn passing_too_many_recipients_panics() {
276 let msg: [u8; 3] = [0, 1, 2];
277
278 let alice = Keypair::generate();
280 let recps = vec![alice.public; 9];
281 let _ = encrypt(&msg, &recps);
282 }
283 #[test]
284 #[should_panic]
285 fn passing_zero_recipients_panics() {
286 let msg: [u8; 3] = [0, 1, 2];
287
288 let recps: [PublicKey; 0] = [];
291 let _ = encrypt(&msg, &recps);
292 }
293}