You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
5.4 KiB
208 lines
5.4 KiB
//! Salt for the digest `Context` (see module `ctx`)
|
|
//!
|
|
//! # Salt kinds
|
|
//! * Hard-coded embedded 32 byte salt (default)
|
|
//! * Fixed compile time or global static 32 byte salt
|
|
//! * Fixed runtime 32 byte salt
|
|
//! * Dynamically sized runtime salt
|
|
//! * No salt
|
|
//!
|
|
//! You can also generate a random salt at runtime with `Salt::random()` which uses `getrandom`'s RNG to create a fixed 32 bytes salt.
|
|
//!
|
|
//! # Method of salting
|
|
//! The salt is fed into the hashing function directly after the data.
|
|
//!
|
|
//! # FFI note
|
|
//! When generating a dynamic salt from an FFI context, there is a hard limit of 1024 bytes for safety reasons. This is more than enough data for a salt.
|
|
|
|
#[cfg(feature="ffi")] use malloc_array::*;
|
|
|
|
use getrandom::{
|
|
getrandom,
|
|
Error,
|
|
};
|
|
use hex_literal::hex;
|
|
|
|
use std::{
|
|
io::{
|
|
self,
|
|
Write,
|
|
},
|
|
convert::{TryInto,TryFrom},
|
|
};
|
|
|
|
/// The static salt size. (32 bytes.)
|
|
///
|
|
/// When providing a compile-time or otherwise global salt, it must be of this size.
|
|
pub const SIZE: usize = 32;
|
|
|
|
/// The default embedded static salt.
|
|
///
|
|
/// It is recommended to use your own salt instead of this one, but this is used as the default if a salt option is not provided.
|
|
///
|
|
/// # Guarantee
|
|
/// This salt is guaranteed stay the same throughout all versions and iterations of this library.
|
|
/// It was randomly generated as the 32 bytes hex literal `hex!("6787f049791466d5a31a3aa6f7138d8fbb907fd1785758298b5c97b0f3fb31ff")`.
|
|
pub static EMBEDDED_SALT: &'static [u8; SIZE] = STATIC_SALT;
|
|
|
|
const STATIC_SALT: &[u8; SIZE] = &hex!("6787f049791466d5a31a3aa6f7138d8fbb907fd1785758298b5c97b0f3fb31ff");
|
|
|
|
/// A salt to use for the kana-hash algorithm, or lack thereof.
|
|
///
|
|
/// It is recommended to provide your own salt, but the default `EMBEDDED_SALT` can be used instead.
|
|
#[derive(Clone,PartialEq,Eq,Hash,Debug)]
|
|
pub enum Salt
|
|
{
|
|
/// Do not salt the hash at all
|
|
None,
|
|
/// Salt with a compile-time, global, or leaked 32 byte array
|
|
Static(&'static [u8; SIZE]),
|
|
/// Salt with a runtime 32 byte array
|
|
Fixed([u8; SIZE]),
|
|
/// Salt with a runtime dynamic boxed byte slice of any size (must be at least 1)
|
|
Dynamic(Box<[u8]>),
|
|
}
|
|
|
|
impl Default for Salt
|
|
{
|
|
fn default() -> Self
|
|
{
|
|
Self::Static(STATIC_SALT)
|
|
}
|
|
}
|
|
|
|
impl Salt
|
|
{
|
|
/// A fixed size salt of [SIZE]
|
|
pub fn fixed(array: [u8; SIZE]) -> Self
|
|
{
|
|
Self::Fixed(array)
|
|
}
|
|
/// A salt from a slice
|
|
pub fn unfixed<T>(slice: &T) -> Self
|
|
where T: AsRef<[u8]> + ?Sized
|
|
{
|
|
let slice = slice.as_ref();
|
|
assert!(slice.len() > 0, "Salt expects at least one byte.");
|
|
Self::Dynamic(Vec::from(slice).into_boxed_slice())
|
|
}
|
|
/// No salt
|
|
pub const fn none() -> Self
|
|
{
|
|
Self::None
|
|
}
|
|
/// Try to create a random salt
|
|
pub fn random() -> Result<Self, Error>
|
|
{
|
|
let mut buffer = [0u8; SIZE];
|
|
getrandom(&mut buffer[..])?;
|
|
Ok(Self::Fixed(buffer))
|
|
}
|
|
/// The default embedded salt
|
|
pub const fn internal() -> Self
|
|
{
|
|
Self::Static(STATIC_SALT)
|
|
}
|
|
|
|
/// Get the raw bytes of this salt
|
|
pub fn bytes(&self) -> &[u8]
|
|
{
|
|
match &self {
|
|
Self::Fixed(ar) => &ar[..],
|
|
Self::Dynamic(vec) => &vec[..],
|
|
Self::Static(s) => &s[..],
|
|
_ => &[],
|
|
}
|
|
}
|
|
|
|
/// Append salt bytes to a stream.
|
|
pub fn append<W: Write+?Sized>(&self, to: &mut W) -> io::Result<usize>
|
|
{
|
|
to.write(self.bytes())
|
|
}
|
|
}
|
|
|
|
#[derive(Copy,Clone,Debug)]
|
|
#[repr(C)]
|
|
pub(crate) struct FFI
|
|
{
|
|
salt_type: u8,
|
|
size: u32,
|
|
body: *mut u8,
|
|
}
|
|
|
|
pub(crate) const SALT_TYPE_NONE: u8 = 0;
|
|
pub(crate) const SALT_TYPE_DEFAULT: u8 = 1;
|
|
pub(crate) const SALT_TYPE_SPECIFIC: u8 = 2;
|
|
pub(crate) const SALT_TYPE_RANDOM: u8 = 3;
|
|
|
|
/// We won't try to copy more than this much data.
|
|
const MAX_FFI_SALT_SIZE: usize = 1024;
|
|
|
|
/// Clone a new `Salt` from an `FFI` salt.
|
|
#[cfg(feature="ffi")] pub(crate) unsafe fn clone_from_raw(ptr: *const FFI) -> Salt
|
|
{
|
|
let ffi = &*ptr;
|
|
match ffi.salt_type {
|
|
SALT_TYPE_SPECIFIC => {
|
|
Salt::Dynamic(HeapArray::from_raw_copied(ffi.body as *const u8, usize::try_from(ffi.size).unwrap()).into_boxed_slice())
|
|
},
|
|
SALT_TYPE_DEFAULT => {
|
|
Salt::default()
|
|
},
|
|
_ => Salt::None,
|
|
}
|
|
}
|
|
/// Consume an `FFI` salt and return a `Salt`.
|
|
#[cfg(feature="ffi")] pub(crate) unsafe fn from_raw(ptr: *mut FFI) -> Salt
|
|
{
|
|
let ffi = &mut *ptr;
|
|
let out = match ffi.salt_type {
|
|
SALT_TYPE_SPECIFIC => {
|
|
Salt::Dynamic(HeapArray::from_raw_parts(ffi.body as *mut u8, usize::try_from(ffi.size).unwrap()).into_boxed_slice())
|
|
},
|
|
SALT_TYPE_DEFAULT => {
|
|
Salt::default()
|
|
},
|
|
_ => Salt::None,
|
|
};
|
|
ffi.salt_type = SALT_TYPE_NONE;
|
|
ffi.size = 0;
|
|
ffi.body = 0 as *mut u8;
|
|
out
|
|
}
|
|
|
|
/// Consume a `Salt` and output a new `FFI` salt.
|
|
#[cfg(feature="ffi")] pub(crate) unsafe fn into_raw(salt: Salt) -> FFI
|
|
{
|
|
unsafe fn allocate(slice: impl AsRef<[u8]>) -> FFI
|
|
{
|
|
let (body, size) = box_with_malloc(slice);
|
|
FFI {
|
|
salt_type: SALT_TYPE_SPECIFIC,
|
|
size: size.try_into().unwrap(),
|
|
body
|
|
}
|
|
}
|
|
|
|
match &salt {
|
|
Salt::None => FFI {
|
|
salt_type: SALT_TYPE_NONE,
|
|
size: 0,
|
|
body: 0 as *mut u8,
|
|
},
|
|
Salt::Static(STATIC_SALT) => FFI {
|
|
salt_type: SALT_TYPE_DEFAULT,
|
|
size: 0,
|
|
body: 0 as *mut u8,
|
|
},
|
|
Salt::Dynamic(bytes) => allocate(&bytes),
|
|
Salt::Fixed(bytes) | &Salt::Static(bytes) => allocate(&bytes),
|
|
}
|
|
}
|
|
|
|
#[cfg(feature="ffi")] fn box_with_malloc(slice: impl AsRef<[u8]>) -> (*mut u8, usize)
|
|
{
|
|
unsafe { HeapArray::from_slice_copied(slice) }.into_raw_parts()
|
|
}
|