use failure::Fail;
use getrandom::getrandom;
#[cfg(feature = "hmac")]
use hmac::Hmac;
#[cfg(feature = "pbkdf2")]
use pbkdf2::pbkdf2;
#[cfg(feature = "sha2")]
use sha2::Sha256;
use std::fmt::{self, Debug};
use zeroize::Zeroize;
pub const SIZE: usize = 32;
pub const DEFAULT_PASSWORD: &[u8] = b"password";
pub const PBKDF2_SALT: &[u8] = b"Yubico";
pub const PBKDF2_ITERATIONS: usize = 10_000;
#[derive(Clone)]
pub struct Key(pub(crate) [u8; SIZE]);
impl Key {
pub fn random() -> Self {
let mut challenge = [0u8; SIZE];
getrandom(&mut challenge).expect("RNG failure!");
Key(challenge)
}
#[cfg(feature = "passwords")]
pub fn derive_from_password(password: &[u8]) -> Self {
let mut kdf_output = [0u8; SIZE];
pbkdf2::<Hmac<Sha256>>(password, PBKDF2_SALT, PBKDF2_ITERATIONS, &mut kdf_output);
Self::new(kdf_output)
}
pub fn from_slice(key_slice: &[u8]) -> Result<Self, Error> {
ensure!(
key_slice.len() == SIZE,
ErrorKind::SizeInvalid,
"expected {}-byte key, got {}",
SIZE,
key_slice.len()
);
let mut key_bytes = [0u8; SIZE];
key_bytes.copy_from_slice(key_slice);
Ok(Key(key_bytes))
}
pub fn new(key_bytes: [u8; SIZE]) -> Self {
Key(key_bytes)
}
pub fn as_secret_slice(&self) -> &[u8] {
&self.0
}
pub(crate) fn enc_key(&self) -> &[u8] {
&self.0[..16]
}
pub(crate) fn mac_key(&self) -> &[u8] {
&self.0[16..]
}
}
impl Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "yubihsm::authentication::Key(...)")
}
}
#[cfg(feature = "passwords")]
impl Default for Key {
fn default() -> Self {
Key::derive_from_password(DEFAULT_PASSWORD)
}
}
impl Drop for Key {
fn drop(&mut self) {
self.0.zeroize();
}
}
impl From<[u8; SIZE]> for Key {
fn from(key_bytes: [u8; SIZE]) -> Key {
Key::new(key_bytes)
}
}
impl_array_serializers!(Key, SIZE);
pub type Error = crate::Error<ErrorKind>;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
#[fail(display = "invalid size")]
SizeInvalid,
}