use crate::crypto::compat;
use crate::crypto::secret::Secret;
use crate::crypto::util::randombytes_into;
use crate::error::*;
use halite_sys;
use libc::{c_char, c_ulonglong};
use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
pub const DIGEST_BYTES: usize = halite_sys::crypto_hash_sha512_BYTES as usize;
pub const SALT_BYTES: usize = halite_sys::crypto_pwhash_scryptsalsa208sha256_SALTBYTES as usize;
pub const OPS_LIMIT_INTERACTIVE: usize =
halite_sys::crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE as usize;
pub const OPS_LIMIT_SENSITIVE: usize =
halite_sys::crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE as usize;
pub const MEM_LIMIT_INTERACTIVE: usize =
halite_sys::crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE as usize;
pub const MEM_LIMIT_SENSITIVE: usize =
halite_sys::crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE as usize;
#[derive(Clone, Eq, PartialEq)]
pub struct Digest([u8; DIGEST_BYTES]);
impl fmt::Debug for Digest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x?}", self.0.as_ref())
}
}
impl Serialize for Digest {
fn serialize<S: Serializer>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(DIGEST_BYTES))?;
for element in self.0.as_ref() {
seq.serialize_element(element)?;
}
seq.end()
}
}
impl<'de> Deserialize<'de> for Digest {
fn deserialize<D: Deserializer<'de>>(
deserializer: D,
) -> ::std::result::Result<Digest, D::Error> {
struct DigestVisitor;
impl<'vde> Visitor<'vde> for DigestVisitor {
type Value = Digest;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a sequence of {} bytes", DIGEST_BYTES)
}
fn visit_seq<A: SeqAccess<'vde>>(
self,
mut seq: A,
) -> ::std::result::Result<Self::Value, A::Error> {
let mut digest = Digest([0; DIGEST_BYTES]);
for i in 0..DIGEST_BYTES {
digest.0[i] = match seq.next_element()? {
Some(val) => val,
None => return Err(::serde::de::Error::invalid_length(i + 1, &self)),
};
}
if seq.next_element::<u8>()?.is_some() {
return Err(::serde::de::Error::invalid_length(DIGEST_BYTES + 1, &self));
}
Ok(digest)
}
}
deserializer.deserialize_seq(DigestVisitor)
}
}
impl Digest {
pub fn from_bytes(data: &[u8]) -> Self {
let mut digest = Digest([0; DIGEST_BYTES]);
debug_assert!(crate::init_done());
unsafe {
halite_sys::crypto_hash_sha512(digest.0.as_mut_ptr(), data.as_ptr(), data.len() as u64);
}
digest
}
pub fn from_secret(secret: &Secret) -> Self {
Self::from_bytes(unsafe { secret.as_slice() })
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Salt(compat::Salt);
impl Default for Salt {
fn default() -> Self {
let mut s = Salt(compat::Salt::default());
randombytes_into(&mut s.0.0);
s
}
}
pub fn derive_key(
out: &mut Secret,
password: &Secret,
salt: &Salt,
ops_limit: usize,
mem_limit: usize,
) -> Result<()> {
debug_assert!(crate::init_done());
if unsafe {
halite_sys::crypto_pwhash_scryptsalsa208sha256(
out.slice_ptr(),
out.len() as c_ulonglong,
password.slice_ptr() as *const c_char,
password.len() as c_ulonglong,
salt.0.0.as_ptr(),
ops_limit as c_ulonglong,
mem_limit,
)
} == 0
{
Ok(())
} else {
Err(Error::Internal(format!(
"deriving key from password failed"
)))
}
}