Skip to main content

wasmium_securemem/
prekey_vault.rs

1mod prekey_vault {
2    use crate::{EncryptedVault, ProtectedEd25519KeyPair, SecureMemVault};
3    use chacha20poly1305::{
4        aead::{Aead, NewAead},
5        Key, XChaCha8Poly1305, XNonce,
6    };
7    use ed25519_dalek::Signature;
8    use nanorand::{ChaCha8, Rng};
9    use secrecy::{ExposeSecret, Secret};
10    use wasmium_errors::{WasmiumError, WasmiumResult};
11    use wasmium_random::WasmiumRandom;
12    use zeroize::Zeroize;
13
14    const VAULT_PAGES: usize = 4;
15    const EACH_VAULT_PAGE_SIZE: usize = 4096_usize;
16
17    lazy_static::lazy_static! {
18        static ref PREKEY: [[u8; EACH_VAULT_PAGE_SIZE]; VAULT_PAGES] = {
19            let mut pages = [[0_u8; EACH_VAULT_PAGE_SIZE]; VAULT_PAGES];
20
21            (0..VAULT_PAGES).for_each(|vault_page_index| {
22                let mut chacha_rng = ChaCha8::new();
23                let mut random_bytes = [0; EACH_VAULT_PAGE_SIZE];
24                (0..EACH_VAULT_PAGE_SIZE).for_each(|index| {
25                    random_bytes[index] = chacha_rng.generate::<u8>();
26                });
27
28                pages[vault_page_index] = random_bytes;
29            });
30
31            pages
32        };
33    }
34
35    impl EncryptedVault {
36        fn sealing_key() -> Secret<SecureMemVault> {
37            let mut blake3_hasher = blake3::Hasher::new();
38            PREKEY.into_iter().for_each(|page| {
39                blake3_hasher.update(&page);
40            });
41
42            SecureMemVault::new(blake3_hasher.finalize())
43        }
44
45        pub fn encrypt_secret(
46            user_secrets: &mut ProtectedEd25519KeyPair,
47        ) -> WasmiumResult<EncryptedVault> {
48            let sealing_key = EncryptedVault::sealing_key();
49            let key = Key::from_slice(sealing_key.expose_secret().0.as_bytes()); // 32-bytes
50            let aead = XChaCha8Poly1305::new(key);
51
52            let random_nonce = Secret::new(WasmiumRandom::secure_alphanumeric24()); // Can be public
53            let nonce = XNonce::from_slice(random_nonce.expose_secret()); // 24-bytes; unique
54            let ciphertext = match aead.encrypt(nonce, &user_secrets.0.to_bytes()[..]) {
55                Ok(ciphertext) => Secret::new(ciphertext),
56                Err(_) => return Err(WasmiumError::XChaCha8Poly1305EncryptionError),
57            };
58
59            user_secrets.zeroize();
60
61            Ok(EncryptedVault {
62                secret: ciphertext,
63                nonce: random_nonce,
64            })
65        }
66
67        pub fn decrypt_and_sign(&self, message: &[u8]) -> WasmiumResult<Signature> {
68            let sealing_key = EncryptedVault::sealing_key();
69            let key = Key::from_slice(sealing_key.expose_secret().0.as_bytes()); // 32-bytes
70            let aead = XChaCha8Poly1305::new(key);
71
72            let nonce = XNonce::from_slice(self.nonce.expose_secret()); // 24-bytes; unique
73            let plaintext = match aead.decrypt(nonce, self.secret.expose_secret().as_ref()) {
74                Ok(plaintext) => Secret::new(plaintext),
75                Err(_) => return Err(WasmiumError::XChaCha8Poly1305EncryptionError),
76            };
77
78            let keypair = ProtectedEd25519KeyPair::from_bytes(&plaintext.expose_secret())?;
79
80            keypair.try_sign(message)
81        }
82    }
83}