portal_lib/protocol/
encrypted.rs

1use crate::errors::PortalError::*;
2use serde::{Deserialize, Serialize};
3use std::convert::TryInto;
4use std::error::Error;
5
6// Nonce generation
7use rand::Rng;
8
9// Encryption
10#[cfg(not(feature = "ring-backend"))]
11use chacha20poly1305::{aead::AeadInPlace, aead::NewAead, ChaCha20Poly1305, Key, Nonce, Tag};
12
13#[cfg(feature = "ring-backend")]
14use ring::aead::{Aad, LessSafeKey, Nonce, Tag, UnboundKey, CHACHA20_POLY1305};
15
16/// We store 128bits but only need 96bit nonces
17const NONCE_SIZE: usize = 12;
18const TAG_SIZE: usize = 16;
19
20/// An abstraction around a nonce sequence. Safely
21/// ensures there is no nonce re-use during a session
22/// with a single key.
23#[derive(PartialEq, Eq, Debug)]
24pub struct NonceSequence([u8; TAG_SIZE]);
25
26/// All encrypted messages must have associated state data (nonce, tag)
27#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
28pub struct EncryptedMessage {
29    /// Provides storage for chacha20poly1305::Nonce
30    pub nonce: [u8; NONCE_SIZE],
31    /// Provides storage for chacha20poly1305::Tag
32    pub tag: [u8; TAG_SIZE],
33    /// Length of follow-on data. Data is not owned
34    /// directly to prevent copies
35    pub len: usize,
36}
37
38#[cfg(not(feature = "ring-backend"))]
39impl EncryptedMessage {
40    /// Create an encrypted message out of an arbitrary serializable
41    /// type
42    pub fn encrypt(
43        key: &[u8],
44        nseq: &mut NonceSequence,
45        data: &mut [u8],
46    ) -> Result<Self, Box<dyn Error>> {
47        // Init state to send
48        let mut state = Self {
49            nonce: nseq.next_unique()?,
50            ..Default::default()
51        };
52
53        // Obtain the next nonce
54        let nonce = Nonce::from_slice(&state.nonce);
55
56        // Obtain the cipher from the key
57        let cha_key = Key::from_slice(key);
58        let cipher = ChaCha20Poly1305::new(cha_key);
59
60        // Set the length
61        state.len = data.len();
62
63        // Encrypt the data in-place
64        let tag = cipher
65            .encrypt_in_place_detached(nonce, b"", data)
66            .or(Err(EncryptError))?;
67
68        // Save the tag in our current state
69        state.tag = tag.into();
70        Ok(state)
71    }
72
73    /// Decrypt the provided data in-place
74    pub fn decrypt(&mut self, key: &[u8], data: &mut [u8]) -> Result<usize, Box<dyn Error>> {
75        // Obtain the cipher from the key
76        let cha_key = Key::from_slice(key);
77        let cipher = ChaCha20Poly1305::new(cha_key);
78
79        // The nonce & tag are self contained
80        let nonce = Nonce::from_slice(&self.nonce);
81        let tag = Tag::from_slice(&self.tag);
82
83        // Decrypt the data in place
84        cipher
85            .decrypt_in_place_detached(nonce, b"", data, tag)
86            .or(Err(DecryptError))?;
87
88        Ok(data.len())
89    }
90}
91
92#[cfg(feature = "ring-backend")]
93impl EncryptedMessage {
94    /// Create an encrypted message out of an arbitrary serializable
95    /// type
96    pub fn encrypt(
97        key: &[u8],
98        nseq: &mut NonceSequence,
99        data: &mut [u8],
100    ) -> Result<Self, Box<dyn Error>> {
101        // Init state to send
102        let mut state = Self::default();
103
104        // Init the key
105        let ring_key_chacha20 =
106            LessSafeKey::new(UnboundKey::new(&CHACHA20_POLY1305, key).or(Err(CryptoError))?);
107
108        // Obtain the next nonce
109        state.nonce = nseq.next()?;
110        let ring_nonce = Nonce::assume_unique_for_key(state.nonce);
111
112        // Set the length
113        state.len = data.len();
114
115        // Encrypt the data in-place.
116        let tag = ring_key_chacha20
117            .seal_in_place_separate_tag(ring_nonce, Aad::empty(), data)
118            .or(Err(EncryptError))?;
119
120        // Save the tag in our current state
121        state.tag = tag.as_ref().try_into().or(Err(EncryptError))?;
122        Ok(state)
123    }
124
125    /// Decrypt the provided data in-place
126    pub fn decrypt(&mut self, key: &[u8], data: &mut [u8]) -> Result<usize, Box<dyn Error>> {
127        // Init the key
128        let ring_key_chacha20 =
129            LessSafeKey::new(UnboundKey::new(&CHACHA20_POLY1305, key).or(Err(CryptoError))?);
130
131        // The nonce & tag are self contained
132        let ring_tag: Tag = self.tag.try_into().or(Err(DecryptError))?;
133        let ring_nonce = Nonce::assume_unique_for_key(self.nonce);
134
135        // Decrypt the data in place
136        ring_key_chacha20
137            .open_in_place_separate_tag(ring_nonce, Aad::empty(), ring_tag, data, 0..)
138            .or(Err(DecryptError))?;
139
140        Ok(data.len())
141    }
142}
143
144impl Default for NonceSequence {
145    fn default() -> Self {
146        Self::new()
147    }
148}
149
150impl NonceSequence {
151    /// Initialize the sequence by generating a random 128bit nonce
152    pub fn new() -> Self {
153        let mut rng = rand::thread_rng();
154        Self(rng.gen::<[u8; 16]>())
155    }
156
157    /// Advance the sequence by incrementing the internal state
158    /// and returning the current state. Similar nonces in TLS 1.3
159    pub fn next_unique(&mut self) -> Result<[u8; NONCE_SIZE], Box<dyn Error>> {
160        // Save the old value
161        let old = self.0;
162
163        // Increment & store the new value
164        let new = u128::from_be_bytes(self.0).wrapping_shr(32);
165        self.0 = new.wrapping_add(1).wrapping_shl(32).to_be_bytes();
166
167        // Return the old value as a nonce
168        Ok(old[..NONCE_SIZE].try_into().or(Err(CryptoError))?)
169    }
170}