libhydrogen 0.4.6

A modern and easy to use cryptography library
Documentation
//! Secret-key authenticated encryption
//!
//! A single key is used both to encrypt/sign and verify/decrypt messages. For
//! this reason, it is critical to keep the key confidential.
//!
//! # Examples
//! ```
//! let key_data = [
//!     64, 33, 195, 234, 107, 63, 107, 237, 113, 199, 183, 130, 203, 194, 247, 31, 76, 51, 203,
//!     163, 126, 238, 206, 125, 225, 74, 103, 105, 133, 181, 61, 189,
//! ];
//!
//! let key = libhydrogen::secretbox::Key::from(key_data);
//! let context = libhydrogen::secretbox::Context::default();
//! let ciphertext = libhydrogen::secretbox::encrypt(b"hello world", 1, &context, &key);
//!
//! let decrypted = libhydrogen::secretbox::decrypt(&ciphertext, 1, &context, &key).unwrap();
//!
//! println!("{}", String::from_utf8(decrypted).unwrap());
//! ```

use core::mem::MaybeUninit;

use super::ensure_initialized;
use crate::errors::*;
use crate::ffi;
use crate::utils;

pub const CONTEXTBYTES: usize = ffi::hydro_secretbox_CONTEXTBYTES as usize;
pub const HEADERBYTES: usize = ffi::hydro_secretbox_HEADERBYTES as usize;
pub const KEYBYTES: usize = ffi::hydro_secretbox_KEYBYTES as usize;
pub const PROBEBYTES: usize = ffi::hydro_secretbox_PROBEBYTES as usize;

#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
pub struct Context([u8; CONTEXTBYTES]);

#[derive(Debug, Clone)]
pub struct Key([u8; KEYBYTES]);

#[derive(Debug, Clone)]
pub struct Probe([u8; PROBEBYTES]);

pub fn encrypt(input: &[u8], msg_id: u64, context: &Context, key: &Key) -> Vec<u8> {
    let out_len = HEADERBYTES + input.len();
    let mut out = Vec::with_capacity(out_len);
    unsafe {
        out.set_len(out_len);
        ffi::hydro_secretbox_encrypt(
            out.as_mut_ptr(),
            input.as_ptr() as *const _,
            input.len(),
            msg_id,
            context.0.as_ptr() as *const _,
            key.0.as_ptr(),
        );
    }
    out
}

pub fn decrypt(
    input: &[u8],
    msg_id: u64,
    context: &Context,
    key: &Key,
) -> Result<Vec<u8>, HydroError> {
    if input.len() < HEADERBYTES {
        return Err(HydroError::DecryptionError);
    }
    let out_len = input.len() - HEADERBYTES;
    let mut out: Vec<u8> = Vec::with_capacity(out_len);
    unsafe {
        out.set_len(out_len);
        if ffi::hydro_secretbox_decrypt(
            out.as_mut_ptr() as *mut _,
            input.as_ptr(),
            input.len(),
            msg_id,
            context.0.as_ptr() as *const _,
            key.0.as_ptr(),
        ) != 0
        {
            return Err(HydroError::DecryptionError);
        }
    }
    Ok(out)
}

impl Probe {
    pub fn create(input: &[u8], context: &Context, key: &Key) -> Probe {
        if input.len() < HEADERBYTES {
            panic!("A probe cannot be created for an impossible ciphertext")
        }
        unsafe {
            let mut probe = MaybeUninit::<Probe>::uninit();
            ffi::hydro_secretbox_probe_create(
                (*probe.as_mut_ptr()).0.as_mut_ptr(),
                input.as_ptr(),
                input.len(),
                context.0.as_ptr() as *const _,
                key.0.as_ptr(),
            );
            probe.assume_init()
        }
    }

    pub fn verify(&self, input: &[u8], context: &Context, key: &Key) -> Result<(), HydroError> {
        if unsafe {
            ffi::hydro_secretbox_probe_verify(
                self.0.as_ptr(),
                input.as_ptr(),
                input.len(),
                context.0.as_ptr() as *const _,
                key.0.as_ptr(),
            )
        } != 0
        {
            return Err(HydroError::InvalidProbe);
        }
        Ok(())
    }
}

impl Drop for Key {
    fn drop(&mut self) {
        utils::memzero(self)
    }
}

impl From<[u8; KEYBYTES]> for Key {
    #[inline]
    fn from(key: [u8; KEYBYTES]) -> Key {
        Key(key)
    }
}

impl From<Key> for [u8; KEYBYTES] {
    #[inline]
    fn from(val: Key) -> Self {
        val.0
    }
}

impl AsRef<[u8]> for Key {
    fn as_ref(&self) -> &[u8] {
        &self.0 as &[u8]
    }
}

impl PartialEq for Key {
    fn eq(&self, other: &Self) -> bool {
        utils::equal(self, other)
    }
}

impl Eq for Key {}

impl Key {
    pub fn gen() -> Key {
        ensure_initialized();
        unsafe {
            let mut key = MaybeUninit::<Key>::uninit();
            ffi::hydro_kdf_keygen((*key.as_mut_ptr()).0.as_mut_ptr());
            key.assume_init()
        }
    }
}

impl Drop for Probe {
    fn drop(&mut self) {
        utils::memzero(self)
    }
}

impl From<[u8; PROBEBYTES]> for Probe {
    #[inline]
    fn from(probe: [u8; PROBEBYTES]) -> Probe {
        Probe(probe)
    }
}

impl From<Probe> for [u8; PROBEBYTES] {
    #[inline]
    fn from(val: Probe) -> Self {
        val.0
    }
}

impl AsRef<[u8]> for Probe {
    fn as_ref(&self) -> &[u8] {
        &self.0 as &[u8]
    }
}

impl PartialEq for Probe {
    fn eq(&self, other: &Self) -> bool {
        utils::equal(self, other)
    }
}

impl Eq for Probe {}

impl From<&'static str> for Context {
    fn from(context_str: &'static str) -> Context {
        let context_str_u8 = context_str.as_bytes();
        let context_str_u8_len = context_str_u8.len();
        if context_str_u8_len > CONTEXTBYTES {
            panic!("Context too long");
        }
        let mut context = Context::default();
        context.0[..context_str_u8_len].copy_from_slice(context_str_u8);
        context
    }
}

impl From<[u8; CONTEXTBYTES]> for Context {
    #[inline]
    fn from(context: [u8; CONTEXTBYTES]) -> Context {
        Context(context)
    }
}

impl From<Context> for [u8; CONTEXTBYTES] {
    #[inline]
    fn from(val: Context) -> Self {
        val.0
    }
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[test]
    fn test_secretbox() {
        init().unwrap();

        let context = "tests".into();
        let key = secretbox::Key::gen();

        let ciphertext = secretbox::encrypt(b"test message", 1, &context, &key);
        let decrypted = secretbox::decrypt(&ciphertext, 1, &context, &key).unwrap();
        assert_eq!(decrypted, b"test message");

        let probe = secretbox::Probe::create(&ciphertext, &context, &key);
        probe.verify(&ciphertext, &context, &key).unwrap();

        let contextx: [u8; secretbox::CONTEXTBYTES] = context.into();
        let contexty: secretbox::Context = contextx.into();
        assert_eq!(context, contexty);
    }
}