secretbox 0.1.3

Rust implementation of the secretbox encryption algorithm
Documentation
#![deprecated(since = "0.1.3", note = "use the crypto_box crate instead.")]
#[cfg(feature = "unsafe-do-not-enable")]
pub mod chacha20;
#[cfg(feature = "unsafe-do-not-enable")]
pub mod csprng;
#[cfg(feature = "unsafe-do-not-enable")]
pub(crate) mod kdf;
#[cfg(feature = "unsafe-do-not-enable")]
pub mod poly1305;
#[cfg(feature = "unsafe-do-not-enable")]
pub mod salsa20;

use chacha20poly1305::aead::{AeadInPlace, NewAead};

#[cfg(feature = "unsafe-do-not-enable")]
uint::construct_uint! {
    pub struct U256(4);
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CipherType {
    Salsa20,
    Chacha20,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct SecretBox {
    key: [u8; 32],
    cipher: CipherType,
}

impl SecretBox {
    /// Creates a new SecretBox instance
    ///
    /// Returns None if the passed key is shorter than 32 bytes
    pub fn new<R>(key: R, cipher: CipherType) -> Option<Self>
    where
        R: AsRef<[u8]>,
    {
        let k = key.as_ref();
        if k.len() != 32 {
            return None;
        }
        let mut key = [0_u8; 32];
        key.copy_from_slice(k);
        Some(Self { key, cipher })
    }
    /// Creates a new SecretBox instance by doing an ECDH key exchange using curve25519
    ///
    /// Returns None if the passed public key is not 32 bytes long.
    #[cfg(feature = "curve25519")]
    pub fn from_ecdh<T, R>(
        peer_public_key: T,
        rng: &mut R,
        cipher: CiperType,
    ) -> Option<(Self, [u8; 32])>
    where
        T: AsRef<[u8]>,
        R: rand::Rng + rand::CryptoRng,
    {
        let k = peer_public_key.as_ref();
        if k.len() != 32 {
            return None;
        }
        let mut key = [0_u8; 32];
        key.copy_from_slice(k);
        let peer_pubkey: x25519_dalek::PublicKey = From::from(key);
        let privkey = x25519_dalek::EphermalSecret::new(rng);
        let pubkey: x25519_dalek::PublicKey = From::from(&privkey);
        let shared_secret = privkey.diffie_hellman(&peer_pubkey);
        Some((
            Self {
                key: *shared_secret.as_bytes(),
                cipher,
            },
            *pubkey.as_bytes(),
        ))
    }
    /// Creates a new SecretBox instance with a generated key
    pub fn from_random_key<R>(rng: &mut R, cipher: CipherType) -> (Self, [u8; 32])
    where
        R: rand::Rng + rand::CryptoRng,
    {
        let mut buf = [0u8; 32];
        rng.fill_bytes(&mut buf);
        (Self { key: buf, cipher }, buf)
    }
    /// This function returns an encrypted and authenticated copy of the message. The key and nonce
    /// pair must be unique for every message.
    pub fn seal(&self, message: &[u8], nonce: [u8; 24]) -> Vec<u8> {
        match self.cipher {
            CipherType::Chacha20 => {
                let nonce = chacha20poly1305::XNonce::from_slice(&nonce);
                let aead = chacha20poly1305::XChaCha20Poly1305::new_from_slice(&self.key).unwrap();
                let mut buf = Vec::with_capacity(message.len() + 16);
                buf.extend([0; 16]);
                buf.extend(message);
                let tag = aead
                    .encrypt_in_place_detached(nonce, &[], &mut buf[16..])
                    .unwrap();
                buf[0..16].copy_from_slice(tag.as_slice());
                buf
            }
            CipherType::Salsa20 => {
                let nonce = xsalsa20poly1305::Nonce::from_slice(&nonce);
                let aead = xsalsa20poly1305::XSalsa20Poly1305::new_from_slice(&self.key).unwrap();
                let mut buf = Vec::with_capacity(message.len() + 16);
                buf.extend([0; 16]);
                buf.extend(message);
                let tag = aead
                    .encrypt_in_place_detached(nonce, &[], &mut buf[16..])
                    .unwrap();
                buf[0..16].copy_from_slice(tag.as_slice());
                buf
            }
        }
    }

    /// This function works like the above, except that it automatically generates a unique nonce.
    pub fn easy_seal(&self, message: &[u8]) -> Vec<u8> {
        use rand::RngCore;
        let mut rng = rand::rngs::OsRng;
        let mut nonce = [0u8; 24];
        rng.fill_bytes(&mut nonce);
        let mut v = Vec::with_capacity(message.len() + 16 + 24);
        v.extend_from_slice(&nonce);
        v.extend_from_slice(&self.seal(message, nonce));
        v
    }

    /// This function works like unseal, except that it finds the nonce automatically.
    pub fn easy_unseal(&self, data: &[u8]) -> Option<Vec<u8>> {
        let mut nonce = [0; 24];
        nonce.copy_from_slice(&data[..24]);
        self.unseal(&data[24..], nonce)
    }

    /// This function tries to authenticate and decrypt a box
    pub fn unseal(&self, data: &[u8], nonce: [u8; 24]) -> Option<Vec<u8>> {
        match self.cipher {
            CipherType::Chacha20 => {
                let (tag, data) = data.split_at(16);
                let tag = chacha20poly1305::Tag::from_slice(tag);
                let nonce = chacha20poly1305::XNonce::from_slice(&nonce);
                let aead = chacha20poly1305::XChaCha20Poly1305::new_from_slice(&self.key).unwrap();
                let mut buf = Vec::with_capacity(data.len());
                buf.extend(data);
                aead.decrypt_in_place_detached(nonce, &[], &mut buf, tag)
                    .ok()?;
                Some(buf)
            }
            CipherType::Salsa20 => {
                let (tag, data) = data.split_at(16);
                let tag = chacha20poly1305::Tag::from_slice(tag);
                let nonce = xsalsa20poly1305::Nonce::from_slice(&nonce);
                let aead = xsalsa20poly1305::XSalsa20Poly1305::new_from_slice(&self.key).unwrap();
                let mut buf = Vec::with_capacity(data.len());
                buf.extend(data);
                aead.decrypt_in_place_detached(nonce, &[], &mut buf, tag)
                    .ok()?;
                Some(buf)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn pynacl_vector() {
        let key = b"\x1b\x27\x55\x64\x73\xe9\x85\xd4\x62\xcd\x51\x19\x7a\x9a\x46\xc7\x60\x09\x54\x9e\xac\x64\x74\xf2\x06\xc4\xee\x08\x44\xf6\x83\x89".clone();
        let nonce = b"\x69\x69\x6e\xe9\x55\xb6\x2b\x73\xcd\x62\xbd\xa8\x75\xfc\x73\xd6\x82\x19\xe0\x03\x6b\x7a\x0b\x37".clone();
        let plain = b"\xbe\x07\x5f\xc5\x3c\x81\xf2\xd5\xcf\x14\x13\x16\xeb\xeb\x0c\x7b\x52\x28\xc5\x2a\x4c\x62\xcb\xd4\x4b\x66\x84\x9b\x64\x24\x4f\xfc\xe5\xec\xba\xaf\x33\xbd\x75\x1a\x1a\xc7\x28\xd4\x5e\x6c\x61\x29\x6c\xdc\x3c\x01\x23\x35\x61\xf4\x1d\xb6\x6c\xce\x31\x4a\xdb\x31\x0e\x3b\xe8\x25\x0c\x46\xf0\x6d\xce\xea\x3a\x7f\xa1\x34\x80\x57\xe2\xf6\x55\x6a\xd6\xb1\x31\x8a\x02\x4a\x83\x8f\x21\xaf\x1f\xde\x04\x89\x77\xeb\x48\xf5\x9f\xfd\x49\x24\xca\x1c\x60\x90\x2e\x52\xf0\xa0\x89\xbc\x76\x89\x70\x40\xe0\x82\xf9\x37\x76\x38\x48\x64\x5e\x07\x05".clone();
        let ciphertext = b"\xf3\xff\xc7\x70\x3f\x94\x00\xe5\x2a\x7d\xfb\x4b\x3d\x33\x05\xd9\x8e\x99\x3b\x9f\x48\x68\x12\x73\xc2\x96\x50\xba\x32\xfc\x76\xce\x48\x33\x2e\xa7\x16\x4d\x96\xa4\x47\x6f\xb8\xc5\x31\xa1\x18\x6a\xc0\xdf\xc1\x7c\x98\xdc\xe8\x7b\x4d\xa7\xf0\x11\xec\x48\xc9\x72\x71\xd2\xc2\x0f\x9b\x92\x8f\xe2\x27\x0d\x6f\xb8\x63\xd5\x17\x38\xb4\x8e\xee\xe3\x14\xa7\xcc\x8a\xb9\x32\x16\x45\x48\xe5\x26\xae\x90\x22\x43\x68\x51\x7a\xcf\xea\xbd\x6b\xb3\x73\x2b\xc0\xe9\xda\x99\x83\x2b\x61\xca\x01\xb6\xde\x56\x24\x4a\x9e\x88\xd5\xf9\xb3\x79\x73\xf6\x22\xa4\x3d\x14\xa6\x59\x9b\x1f\x65\x4c\xb4\x5a\x74\xe3\x55\xa5".clone();
        let s = SecretBox::new(key, CipherType::Salsa20).unwrap();
        let output = s.seal(&plain[..], nonce);
        println!("output: {}, ciphertext: {}", output.len(), ciphertext.len());
        assert_eq!(&output[..], &ciphertext[..]);
        let output2 = s.unseal(&output[..], nonce).unwrap();
        assert_eq!(&output2[..], &plain[..]);
    }

    #[test]
    fn easy_seal_unseal() {
        use rand::RngCore;
        let mut rng = rand::rngs::OsRng;
        let mut key = [0u8; 32];
        rng.fill_bytes(&mut key);
        let plain = b"hello world".to_vec();
        let s = SecretBox::new(key, CipherType::Salsa20).unwrap();
        let sealed = s.easy_seal(&plain);
        let unsealed = s.easy_unseal(&sealed).unwrap();
        assert_eq!(&unsealed[..], &plain[..]);
    }
}