pubky_common/
crypto.rs

1//! Cryptographic functions (hashing, encryption, and signatures).
2
3use crypto_secretbox::{
4    aead::{Aead, AeadCore, KeyInit, OsRng},
5    XSalsa20Poly1305,
6};
7use rand::random;
8
9pub use crate::keys::{Keypair, PublicKey};
10
11pub use ed25519_dalek::Signature;
12
13/// Blake3 Hash.
14pub type Hash = blake3::Hash;
15
16pub use blake3::hash;
17
18pub use blake3::Hasher;
19
20/// Create a random hash.
21pub fn random_hash() -> Hash {
22    Hash::from_bytes(random())
23}
24
25/// Create an array of random bytes with a size `N`.
26pub fn random_bytes<const N: usize>() -> [u8; N] {
27    let arr: [u8; N] = random();
28
29    arr
30}
31
32/// Encrypt a message using `XSalsa20Poly1305`.
33pub fn encrypt(plain_text: &[u8], encryption_key: &[u8; 32]) -> Vec<u8> {
34    if plain_text.is_empty() {
35        return plain_text.to_vec();
36    }
37
38    let cipher = XSalsa20Poly1305::new(encryption_key.into());
39    let nonce = XSalsa20Poly1305::generate_nonce(&mut OsRng); // unique per message
40    let ciphertext = cipher
41        .encrypt(&nonce, plain_text)
42        .expect("XSalsa20Poly1305 encrypt should be infallible");
43
44    let mut out: Vec<u8> = Vec::with_capacity(nonce.len() + ciphertext.len());
45    out.extend_from_slice(nonce.as_ref());
46    out.extend_from_slice(&ciphertext);
47
48    out
49}
50
51/// Encrypt an encrypted message using `XSalsa20Poly1305`.
52pub fn decrypt(bytes: &[u8], encryption_key: &[u8; 32]) -> Result<Vec<u8>, DecryptError> {
53    if bytes.is_empty() {
54        return Ok(bytes.to_vec());
55    }
56
57    let cipher = XSalsa20Poly1305::new(encryption_key.into());
58
59    if bytes.len() < 24 {
60        return Err(DecryptError::TooSmall(bytes.len()));
61    }
62
63    Ok(cipher.decrypt(bytes[..24].into(), &bytes[24..])?)
64}
65
66#[derive(thiserror::Error, Debug)]
67/// Error while decrypting a message
68pub enum DecryptError {
69    #[error(transparent)]
70    /// Failed to decrypt message.
71    Fail(#[from] crypto_secretbox::Error),
72
73    #[error("Encrypted message too small, expected at least 24 bytes nonce, received {0} bytes")]
74    /// Encrypted message too small, expected at least 24 bytes nonce, received {0} bytes
75    TooSmall(usize),
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn encrypt_decrypt() {
84        let plain_text = "Plain text!";
85        let encryption_key = [0; 32];
86
87        let encrypted = encrypt(plain_text.as_bytes(), &encryption_key);
88        let decrypted = decrypt(&encrypted, &encryption_key).unwrap();
89
90        assert_eq!(decrypted, plain_text.as_bytes())
91    }
92}