engine/vault/
crypto_box.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use runtime::{
5    locked_memory::LockedMemory,
6    memories::{buffer::Buffer, noncontiguous_memory::*},
7};
8use serde::{Deserialize, Serialize};
9use std::{
10    fmt::Debug,
11    hash::{Hash, Hasher},
12    marker::PhantomData,
13};
14use zeroize::{ZeroizeOnDrop, Zeroizing};
15
16/// A provider interface between the vault and a crypto box. See libsodium's [secretbox](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretbox) for an example.
17pub trait BoxProvider: 'static + Sized + Ord + PartialOrd {
18    type Error: Debug;
19
20    /// defines the key length for the [`BoxProvider`].
21    fn box_key_len() -> usize;
22    /// defines the size of the Nonce combined with the Ad for the [`BoxProvider`].
23    fn box_overhead() -> usize;
24
25    /// seals some data into the crypto box using the [`Key`] and the associated data.
26    fn box_seal(key: &Key<Self>, ad: &[u8], data: &[u8]) -> Result<Vec<u8>, Self::Error>;
27
28    /// opens a crypto box to get data using the [`Key`] and the associated data.
29    fn box_open(key: &Key<Self>, ad: &[u8], data: &[u8]) -> Result<Vec<u8>, Self::Error>;
30
31    /// fills a buffer [`&mut [u8]`] with secure random bytes.
32    fn random_buf(buf: &mut [u8]) -> Result<(), Self::Error>;
33
34    /// creates a vector with secure random bytes based off of an inputted [`usize`] length.
35    fn random_vec(len: usize) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
36        let mut buf = Zeroizing::new(vec![0; len]);
37        Self::random_buf(&mut buf)?;
38        Ok(buf)
39    }
40}
41
42/// A key to the crypto box.  [`Key`] is stored on the heap which makes it easier to erase. Makes use of the
43/// [`Buffer<u8>`] type to protect the data.
44#[derive(Serialize, Deserialize, ZeroizeOnDrop)]
45pub struct Key<T: BoxProvider> {
46    /// the guarded raw bytes that make up the key
47    pub key: Buffer<u8>,
48
49    /// phantom data to call to the provider.
50    #[serde(skip_serializing, skip_deserializing)]
51    _box_provider: PhantomData<T>,
52}
53
54impl<T: BoxProvider> Key<T> {
55    /// generate a random key using secure random bytes
56    pub fn random() -> Self {
57        Self {
58            key: {
59                Buffer::alloc(
60                    T::random_vec(T::box_key_len())
61                        .expect("failed to generate random key")
62                        .as_slice(),
63                    T::box_key_len(),
64                )
65            },
66            _box_provider: PhantomData,
67        }
68    }
69
70    /// attempts to load a key from inputted data
71    ///
72    /// Return `None` if the key length doesn't match [`BoxProvider::box_key_len`].
73    pub fn load(key: Zeroizing<Vec<u8>>) -> Option<Self> {
74        if key.len() == T::box_key_len() {
75            Some(Self {
76                key: Buffer::alloc(key.as_slice(), T::box_key_len()),
77                _box_provider: PhantomData,
78            })
79        } else {
80            None
81        }
82    }
83}
84
85impl<T: BoxProvider> Clone for Key<T> {
86    fn clone(&self) -> Self {
87        Self {
88            key: self.key.clone(),
89            _box_provider: PhantomData,
90        }
91    }
92}
93
94impl<T: BoxProvider> Eq for Key<T> {}
95
96impl<T: BoxProvider> PartialEq for Key<T> {
97    fn eq(&self, other: &Self) -> bool {
98        self.key == other.key && self._box_provider == other._box_provider
99    }
100}
101
102impl<T: BoxProvider> PartialOrd for Key<T> {
103    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
104        Some(self.cmp(other))
105    }
106}
107
108impl<T: BoxProvider> Ord for Key<T> {
109    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
110        self.key.borrow().cmp(&*other.key.borrow())
111    }
112}
113
114impl<T: BoxProvider> Hash for Key<T> {
115    fn hash<H: Hasher>(&self, state: &mut H) {
116        self.key.borrow().hash(state);
117        self._box_provider.hash(state);
118    }
119}
120
121impl<T: BoxProvider> Debug for Key<T> {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        write!(f, "KeyData")
124    }
125}
126
127/// trait for encryptable data. Allows the data to be encrypted.
128pub trait Encrypt<T: From<Vec<u8>>>: AsRef<[u8]> {
129    /// encrypts a raw data and creates a type T from the ciphertext
130    fn encrypt<P: BoxProvider, AD: AsRef<[u8]>>(&self, key: &Key<P>, ad: AD) -> Result<T, P::Error> {
131        let sealed = P::box_seal(key, ad.as_ref(), self.as_ref())?;
132        Ok(T::from(sealed))
133    }
134}
135
136#[derive(Debug)]
137pub enum DecryptError<E: Debug> {
138    Invalid,
139    Provider(E),
140}
141
142/// Trait for decryptable data. Allows the data to be decrypted.
143pub trait Decrypt<T: TryFrom<Vec<u8>>>: AsRef<[u8]> {
144    /// decrypts raw data and creates a new type T from the plaintext
145    fn decrypt<P: BoxProvider, AD: AsRef<[u8]>>(&self, key: &Key<P>, ad: AD) -> Result<T, DecryptError<P::Error>> {
146        let opened = P::box_open(key, ad.as_ref(), self.as_ref()).map_err(DecryptError::Provider)?;
147        T::try_from(opened).map_err(|_| DecryptError::Invalid)
148    }
149}
150
151//####### NON CONTIGUOUS KEY
152
153/// A key to the crypto box.  [`NCKey`] is stored on the heap which makes it easier to erase. Makes use of the
154/// [`NonContiguousMemory`] type to protect the data.
155#[derive(Serialize, Deserialize, ZeroizeOnDrop)]
156pub struct NCKey<T: BoxProvider> {
157    /// the guarded raw bytes that make up the key
158    pub key: NonContiguousMemory,
159
160    /// phantom data to call to the provider.
161    #[serde(skip_serializing, skip_deserializing)]
162    _box_provider: PhantomData<T>,
163}
164
165impl<T: BoxProvider> NCKey<T> {
166    /// generate a random key using secure random bytes
167    pub fn random() -> Self {
168        Self {
169            key: {
170                NonContiguousMemory::alloc(
171                    T::random_vec(T::box_key_len())
172                        .expect("failed to generate random key")
173                        .as_slice(),
174                    T::box_key_len(),
175                    NC_CONFIGURATION,
176                )
177                .unwrap_or_else(|e| panic!("{}", e))
178            },
179            _box_provider: PhantomData,
180        }
181    }
182
183    /// attempts to load a key from inputted data
184    ///
185    /// Return `None` if the key length doesn't match [`BoxProvider::box_key_len`].
186    pub fn load(key: Zeroizing<Vec<u8>>) -> Option<Self> {
187        if key.len() == T::box_key_len() {
188            Some(Self {
189                key: NonContiguousMemory::alloc(key.as_slice(), T::box_key_len(), NC_CONFIGURATION)
190                    .unwrap_or_else(|e| panic!("{}", e)),
191                _box_provider: PhantomData,
192            })
193        } else {
194            None
195        }
196    }
197
198    pub fn encrypt_key<AD: AsRef<[u8]>>(&self, data: &Key<T>, ad: AD) -> Result<Vec<u8>, T::Error> {
199        let key = Key {
200            key: self.key.unlock().unwrap_or_else(|e| panic!("{}", e)),
201            _box_provider: PhantomData,
202        };
203        T::box_seal(&key, ad.as_ref(), &data.key.borrow())
204    }
205
206    pub fn decrypt_key<AD: AsRef<[u8]>>(&self, data: Vec<u8>, ad: AD) -> Result<Key<T>, DecryptError<T::Error>> {
207        let key = Key {
208            key: self.key.unlock().unwrap_or_else(|e| panic!("{}", e)),
209            _box_provider: PhantomData,
210        };
211        let opened = T::box_open(&key, ad.as_ref(), &data).map_err(DecryptError::Provider)?;
212        Key::load(opened.into()).ok_or(DecryptError::Invalid)
213    }
214
215    // /// get the key's bytes from the [`Buffer`]
216    // pub fn bytes<'a>(&'a self) -> Ref<'a, u8> {
217    //     // hacks the guarded type.  Probably not the best solution.
218    //     let buf = self.key.unlock().expect("Failed to unlock non-contiguous memory");
219    //     buf.borrow()
220    // }
221}
222
223impl<T: BoxProvider> Clone for NCKey<T> {
224    fn clone(&self) -> Self {
225        Self {
226            key: self.key.clone(),
227            _box_provider: PhantomData,
228        }
229    }
230}
231
232impl<T: BoxProvider> Eq for NCKey<T> {}
233
234impl<T: BoxProvider> PartialEq for NCKey<T> {
235    fn eq(&self, other: &Self) -> bool {
236        let buf1 = self.key.unlock().unwrap_or_else(|e| panic!("{}", e));
237        let buf2 = other.key.unlock().unwrap_or_else(|e| panic!("{}", e));
238        buf1 == buf2 && self._box_provider == other._box_provider
239    }
240}
241
242impl<T: BoxProvider> PartialOrd for NCKey<T> {
243    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
244        Some(self.cmp(other))
245    }
246}
247
248impl<T: BoxProvider> Ord for NCKey<T> {
249    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
250        let buf1 = self.key.unlock().unwrap_or_else(|e| panic!("{}", e));
251        let buf2 = other.key.unlock().unwrap_or_else(|e| panic!("{}", e));
252        let b = buf1.borrow().cmp(&*buf2.borrow());
253        b
254    }
255}
256
257impl<T: BoxProvider> Hash for NCKey<T> {
258    fn hash<H: Hasher>(&self, state: &mut H) {
259        let buf = self.key.unlock().unwrap_or_else(|e| panic!("{}", e));
260        buf.borrow().hash(state);
261        self._box_provider.hash(state);
262    }
263}
264
265impl<T: BoxProvider> Debug for NCKey<T> {
266    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267        write!(f, "KeyData")
268    }
269}