use crate::crypto::compat::{self, Compatible};
use crate::crypto::digest::{Digest, Salt, derive_key};
use crate::crypto::secret::Secret;
use crate::crypto::util::*;
use crate::error::*;
use halite_sys;
use libc::c_ulonglong;
use serde::{Deserialize, Serialize};
pub const NONCE_BYTES: usize = halite_sys::crypto_secretbox_xsalsa20poly1305_NONCEBYTES as usize;
pub const KEY_BYTES: usize = halite_sys::crypto_secretbox_xsalsa20poly1305_KEYBYTES as usize;
pub const TAG_BYTES: usize = halite_sys::crypto_secretbox_xsalsa20poly1305_MACBYTES as usize;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Nonce {
nonce: compat::Nonce,
}
impl Default for Nonce {
fn default() -> Self {
let mut nonce = Nonce::new();
randombytes_into(&mut nonce.nonce.0);
nonce
}
}
impl Nonce {
pub fn new() -> Self {
Nonce {
nonce: compat::Nonce([0; NONCE_BYTES]),
}
}
pub fn from_slice(bytes: &[u8]) -> Result<Self> {
Ok(Nonce {
nonce: compat::Nonce::from_slice(bytes)?,
})
}
pub fn increment(mut self) -> Self {
debug_assert!(crate::init_done());
unsafe {
halite_sys::sodium_increment(self.nonce.0.as_mut_ptr(), NONCE_BYTES);
}
self
}
pub fn as_bytes(&self) -> &[u8] {
&self.nonce.0
}
}
pub trait AbstractKey: Sized {
type Error: std::error::Error;
fn get_digest(&self) -> Digest;
fn serialize(&self) -> std::result::Result<Secret, Self::Error>;
fn deserialize(data: Secret) -> std::result::Result<Self, Self::Error>;
fn encrypt(
&self,
plaintext: &Secret,
nonce: Option<Nonce>,
) -> std::result::Result<(Option<Nonce>, Vec<u8>), Self::Error>;
fn decrypt(
&self,
nonce: Option<&Nonce>,
ciphertext: &[u8],
) -> std::result::Result<Secret, Self::Error>;
}
pub struct Key {
key_data: Secret,
}
const KEY_SERDE_COMPAT_PREFIX: &'static [u8] = &[0x81, 0xa3, 0x4b, 0x65, 0x79, 0x91, 0xc4, 0x20];
const KEY_SERDE_COMPAT_PREFIX_ALT: &'static [u8] = &[0x81, 0x00, 0x91, 0xc4, 0x20];
impl AbstractKey for Key {
type Error = Error;
fn get_digest(&self) -> Digest {
Digest::from_secret(&self.key_data)
}
fn serialize(&self) -> std::result::Result<Secret, Self::Error> {
let mut ser = Secret::with_len(self.key_data.len() + KEY_SERDE_COMPAT_PREFIX.len())?;
unsafe {
ser.as_mut_slice()[0..KEY_SERDE_COMPAT_PREFIX.len()]
.copy_from_slice(KEY_SERDE_COMPAT_PREFIX);
ser.as_mut_slice()[KEY_SERDE_COMPAT_PREFIX.len()..]
.copy_from_slice(self.key_data.as_slice());
}
Ok(ser)
}
fn deserialize(mut data: Secret) -> std::result::Result<Self, Self::Error> {
let to_skip = if unsafe { data.as_slice() }.starts_with(KEY_SERDE_COMPAT_PREFIX) {
KEY_SERDE_COMPAT_PREFIX.len()
} else if unsafe { data.as_slice() }.starts_with(KEY_SERDE_COMPAT_PREFIX_ALT) {
KEY_SERDE_COMPAT_PREFIX_ALT.len()
} else {
return Err(Error::InvalidArgument(format!(
"invalid Key data; missing expected prefix bytes"
)));
};
unsafe {
std::ptr::copy(
data.slice_ptr().offset(to_skip as isize),
data.slice_ptr(),
KEY_BYTES,
);
}
data.resize(KEY_BYTES)?;
Ok(Key { key_data: data })
}
fn encrypt(
&self,
plaintext: &Secret,
nonce: Option<Nonce>,
) -> std::result::Result<(Option<Nonce>, Vec<u8>), Self::Error> {
let nonce = nonce.unwrap_or_else(Nonce::default);
let buf = plaintext.try_clone()?;
let mut tag = [0; TAG_BYTES];
debug_assert!(crate::init_done());
unsafe {
halite_sys::crypto_secretbox_detached(
buf.slice_ptr(),
tag.as_mut_ptr(),
buf.slice_ptr(),
buf.len() as c_ulonglong,
nonce.nonce.0.as_ptr(),
self.key_data.slice_ptr(),
);
}
let mut ret = Vec::new();
ret.extend_from_slice(&tag);
ret.extend_from_slice(unsafe { buf.as_slice() });
Ok((Some(nonce), ret))
}
fn decrypt(
&self,
nonce: Option<&Nonce>,
ciphertext: &[u8],
) -> std::result::Result<Secret, Self::Error> {
if ciphertext.len() < TAG_BYTES {
return Err(Error::InvalidArgument(format!(
"can't decrypt ciphertext which is missing an authentication tag"
))
.into());
}
let nonce = match nonce {
None => {
return Err(Error::InvalidArgument(format!(
"decrypting with a Key requires a Nonce"
))
.into());
}
Some(n) => n,
};
let (tag, ciphertext) = ciphertext.split_at(TAG_BYTES);
let mut plaintext = Secret::with_len(ciphertext.len())?;
unsafe { plaintext.as_mut_slice() }.copy_from_slice(ciphertext);
debug_assert!(crate::init_done());
if unsafe {
halite_sys::crypto_secretbox_open_detached(
plaintext.slice_ptr(),
plaintext.slice_ptr(),
tag.as_ptr(),
plaintext.len() as c_ulonglong,
nonce.nonce.0.as_ptr(),
self.key_data.slice_ptr(),
)
} == 0
{
Ok(plaintext)
} else {
Err(Error::InvalidArgument(format!("failed to decrypt with incorrect Key")).into())
}
}
}
impl Key {
pub fn new_random() -> Result<Self> {
let mut key_buffer = Secret::with_len(KEY_BYTES)?;
randombytes_into_secret(&mut key_buffer);
Ok(Key {
key_data: key_buffer,
})
}
pub fn new_password(
password: &Secret,
salt: &Salt,
ops_limit: usize,
mem_limit: usize,
) -> Result<Self> {
let mut key_buffer = Secret::with_len(KEY_BYTES)?;
derive_key(&mut key_buffer, password, salt, ops_limit, mem_limit)?;
Ok(Key {
key_data: key_buffer,
})
}
}