use crate::{Result, SodiumError};
use libc;
pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_BYTES_MIN as usize;
pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_BYTES_MAX as usize;
pub const CONTEXTBYTES: usize = libsodium_sys::crypto_kdf_CONTEXTBYTES as usize;
pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_KEYBYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Key {
type Error = crate::SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"key must be exactly {KEYBYTES} bytes"
)));
}
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(slice);
Ok(Key(key))
}
}
impl From<[u8; KEYBYTES]> for Key {
fn from(bytes: [u8; KEYBYTES]) -> Self {
Key(bytes)
}
}
impl From<Key> for [u8; KEYBYTES] {
fn from(key: Key) -> Self {
key.0
}
}
impl Key {
pub fn generate() -> Result<Self> {
let mut key = [0u8; KEYBYTES];
unsafe {
libsodium_sys::crypto_kdf_keygen(key.as_mut_ptr());
}
Ok(Key(key))
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"key must be exactly {KEYBYTES} bytes"
)));
}
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(slice);
Ok(Key(key))
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
pub fn derive_from_key(
subkey_len: usize,
subkey_id: u64,
context: &[u8],
master_key: &Key,
) -> Result<Vec<u8>> {
if !(BYTES_MIN..=BYTES_MAX).contains(&subkey_len) {
return Err(SodiumError::InvalidInput(format!(
"subkey length must be between {BYTES_MIN} and {BYTES_MAX}"
)));
}
if context.len() != CONTEXTBYTES {
return Err(SodiumError::InvalidInput(format!(
"context must be exactly {CONTEXTBYTES} bytes"
)));
}
let mut subkey = vec![0u8; subkey_len];
let result = unsafe {
libsodium_sys::crypto_kdf_derive_from_key(
subkey.as_mut_ptr(),
subkey_len as libc::size_t,
subkey_id,
context.as_ptr() as *const std::os::raw::c_char,
master_key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError("key derivation failed".into()));
}
Ok(subkey)
}
pub mod blake2b {
use super::*;
pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_blake2b_BYTES_MIN as usize;
pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_blake2b_BYTES_MAX as usize;
pub const CONTEXTBYTES: usize = libsodium_sys::crypto_kdf_blake2b_CONTEXTBYTES as usize;
pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_blake2b_KEYBYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Key {
type Error = crate::SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"key must be exactly {KEYBYTES} bytes"
)));
}
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(slice);
Ok(Key(key))
}
}
impl From<[u8; KEYBYTES]> for Key {
fn from(bytes: [u8; KEYBYTES]) -> Self {
Key(bytes)
}
}
impl From<Key> for [u8; KEYBYTES] {
fn from(key: Key) -> Self {
key.0
}
}
impl Key {
pub fn generate() -> Result<Self> {
let mut key = [0u8; KEYBYTES];
unsafe {
libsodium_sys::crypto_kdf_keygen(key.as_mut_ptr());
}
Ok(Key(key))
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"key must be exactly {KEYBYTES} bytes"
)));
}
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(slice);
Ok(Key(key))
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
pub fn derive_from_key(
subkey_len: usize,
subkey_id: u64,
context: &[u8],
master_key: &Key,
) -> Result<Vec<u8>> {
if !(BYTES_MIN..=BYTES_MAX).contains(&subkey_len) {
return Err(SodiumError::InvalidInput(format!(
"subkey length must be between {BYTES_MIN} and {BYTES_MAX} bytes"
)));
}
if context.len() != CONTEXTBYTES {
return Err(SodiumError::InvalidInput(format!(
"context must be exactly {CONTEXTBYTES} bytes"
)));
}
let mut subkey = vec![0u8; subkey_len];
let result = unsafe {
libsodium_sys::crypto_kdf_blake2b_derive_from_key(
subkey.as_mut_ptr(),
subkey_len as libc::size_t,
subkey_id,
context.as_ptr() as *const std::os::raw::c_char,
master_key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError("key derivation failed".into()));
}
Ok(subkey)
}
}
pub mod hkdf {
use super::*;
pub mod sha256 {
use super::*;
pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_hkdf_sha256_BYTES_MAX as usize;
pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_hkdf_sha256_BYTES_MIN as usize;
pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_hkdf_sha256_KEYBYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Prk([u8; KEYBYTES]);
impl AsRef<[u8]> for Prk {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Prk {
type Error = crate::SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"PRK must be exactly {KEYBYTES} bytes"
)));
}
let mut prk = [0u8; KEYBYTES];
prk.copy_from_slice(slice);
Ok(Prk(prk))
}
}
impl From<[u8; KEYBYTES]> for Prk {
fn from(bytes: [u8; KEYBYTES]) -> Self {
Prk(bytes)
}
}
impl From<Prk> for [u8; KEYBYTES] {
fn from(prk: Prk) -> Self {
prk.0
}
}
impl Prk {
pub fn as_bytes(&self) -> &[u8; KEYBYTES] {
&self.0
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"PRK must be exactly {KEYBYTES} bytes"
)));
}
let mut prk = [0u8; KEYBYTES];
prk.copy_from_slice(slice);
Ok(Self(prk))
}
}
pub fn statebytes() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_statebytes() as usize }
}
pub fn keybytes() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_keybytes() as usize }
}
pub fn bytes_min() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_bytes_min() as usize }
}
pub fn bytes_max() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha256_bytes_max() as usize }
}
pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> Result<Prk> {
let mut prk = [0u8; KEYBYTES];
let salt_ptr = match salt {
Some(s) => s.as_ptr(),
None => std::ptr::null(),
};
let salt_len = salt.map_or(0, |s| s.len());
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha256_extract(
prk.as_mut_ptr(),
salt_ptr,
salt_len as libc::size_t,
ikm.as_ptr(),
ikm.len() as libc::size_t,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-256 extract phase failed".into(),
));
}
Ok(Prk(prk))
}
pub fn keygen() -> Prk {
let mut prk = [0u8; KEYBYTES];
unsafe {
libsodium_sys::crypto_kdf_hkdf_sha256_keygen(prk.as_mut_ptr());
}
Prk(prk)
}
pub fn expand(out_len: usize, ctx: Option<&[u8]>, prk: &Prk) -> Result<Vec<u8>> {
if out_len > BYTES_MAX {
return Err(SodiumError::InvalidInput(format!(
"output length must be at most {BYTES_MAX} bytes"
)));
}
let mut out = vec![0u8; out_len];
let ctx_ptr = match ctx {
Some(c) => c.as_ptr() as *const std::os::raw::c_char,
None => std::ptr::null(),
};
let ctx_len = ctx.map_or(0, |c| c.len());
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha256_expand(
out.as_mut_ptr(),
out_len as libc::size_t,
ctx_ptr,
ctx_len as libc::size_t,
prk.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-256 expand phase failed".into(),
));
}
Ok(out)
}
pub struct State {
state: Box<libsodium_sys::crypto_kdf_hkdf_sha256_state>,
}
impl Default for State {
fn default() -> Self {
Self::new()
}
}
impl State {
pub fn new() -> Self {
let state = unsafe {
let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
.expect("Invalid layout for crypto_kdf_hkdf_sha256_state");
let ptr = std::alloc::alloc_zeroed(layout)
as *mut libsodium_sys::crypto_kdf_hkdf_sha256_state;
Box::from_raw(ptr)
};
Self { state }
}
pub fn extract_init(&mut self, salt: Option<&[u8]>) -> Result<()> {
let salt_ptr = match salt {
Some(s) => s.as_ptr(),
None => std::ptr::null(),
};
let salt_len = salt.map_or(0, |s| s.len());
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha256_extract_init(
self.state.as_mut(),
salt_ptr,
salt_len as libc::size_t,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-256 extract init failed".into(),
));
}
Ok(())
}
pub fn extract_update(&mut self, ikm: &[u8]) -> Result<()> {
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha256_extract_update(
self.state.as_mut(),
ikm.as_ptr(),
ikm.len() as libc::size_t,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-256 extract update failed".into(),
));
}
Ok(())
}
pub fn extract_final(&mut self) -> Result<Prk> {
let mut prk = [0u8; KEYBYTES];
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha256_extract_final(
self.state.as_mut(),
prk.as_mut_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-256 extract final failed".into(),
));
}
Ok(Prk(prk))
}
}
impl Drop for State {
fn drop(&mut self) {
unsafe {
let ptr = Box::into_raw(std::mem::replace(
&mut self.state,
Box::new(std::mem::zeroed()),
));
let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
.expect("Invalid layout for crypto_kdf_hkdf_sha256_state");
std::alloc::dealloc(ptr as *mut u8, layout);
}
}
}
impl zeroize::Zeroize for State {
fn zeroize(&mut self) {
unsafe {
std::ptr::write_bytes(
self.state.as_mut() as *mut _ as *mut u8,
0,
statebytes(),
);
}
}
}
}
pub mod sha512 {
use super::*;
pub const BYTES_MAX: usize = libsodium_sys::crypto_kdf_hkdf_sha512_BYTES_MAX as usize;
pub const BYTES_MIN: usize = libsodium_sys::crypto_kdf_hkdf_sha512_BYTES_MIN as usize;
pub const KEYBYTES: usize = libsodium_sys::crypto_kdf_hkdf_sha512_KEYBYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Prk([u8; KEYBYTES]);
impl AsRef<[u8]> for Prk {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Prk {
type Error = crate::SodiumError;
fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"PRK must be exactly {KEYBYTES} bytes"
)));
}
let mut prk = [0u8; KEYBYTES];
prk.copy_from_slice(slice);
Ok(Prk(prk))
}
}
impl From<[u8; KEYBYTES]> for Prk {
fn from(bytes: [u8; KEYBYTES]) -> Self {
Prk(bytes)
}
}
impl From<Prk> for [u8; KEYBYTES] {
fn from(prk: Prk) -> Self {
prk.0
}
}
impl Prk {
pub fn as_bytes(&self) -> &[u8; KEYBYTES] {
&self.0
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"PRK must be exactly {KEYBYTES} bytes"
)));
}
let mut prk = [0u8; KEYBYTES];
prk.copy_from_slice(slice);
Ok(Self(prk))
}
}
pub fn statebytes() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_statebytes() as usize }
}
pub fn keybytes() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_keybytes() as usize }
}
pub fn bytes_min() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_bytes_min() as usize }
}
pub fn bytes_max() -> usize {
unsafe { libsodium_sys::crypto_kdf_hkdf_sha512_bytes_max() as usize }
}
pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> Result<Prk> {
let mut prk = [0u8; KEYBYTES];
let salt_ptr = match salt {
Some(s) => s.as_ptr(),
None => std::ptr::null(),
};
let salt_len = salt.map_or(0, |s| s.len());
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha512_extract(
prk.as_mut_ptr(),
salt_ptr,
salt_len as libc::size_t,
ikm.as_ptr(),
ikm.len() as libc::size_t,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-512 extract phase failed".into(),
));
}
Ok(Prk(prk))
}
pub fn keygen() -> Prk {
let mut prk = [0u8; KEYBYTES];
unsafe {
libsodium_sys::crypto_kdf_hkdf_sha512_keygen(prk.as_mut_ptr());
}
Prk(prk)
}
pub fn expand(out_len: usize, ctx: Option<&[u8]>, prk: &Prk) -> Result<Vec<u8>> {
if out_len > BYTES_MAX {
return Err(SodiumError::InvalidInput(format!(
"output length must be at most {BYTES_MAX} bytes"
)));
}
let mut out = vec![0u8; out_len];
let ctx_ptr = match ctx {
Some(c) => c.as_ptr() as *const std::os::raw::c_char,
None => std::ptr::null(),
};
let ctx_len = ctx.map_or(0, |c| c.len());
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha512_expand(
out.as_mut_ptr(),
out_len as libc::size_t,
ctx_ptr,
ctx_len as libc::size_t,
prk.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-512 expand phase failed".into(),
));
}
Ok(out)
}
pub struct State {
state: Box<libsodium_sys::crypto_kdf_hkdf_sha512_state>,
}
impl Default for State {
fn default() -> Self {
Self::new()
}
}
impl State {
pub fn new() -> Self {
let state = unsafe {
let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
.expect("Invalid layout for crypto_kdf_hkdf_sha512_state");
let ptr = std::alloc::alloc_zeroed(layout)
as *mut libsodium_sys::crypto_kdf_hkdf_sha512_state;
Box::from_raw(ptr)
};
Self { state }
}
pub fn extract_init(&mut self, salt: Option<&[u8]>) -> Result<()> {
let salt_ptr = match salt {
Some(s) => s.as_ptr(),
None => std::ptr::null(),
};
let salt_len = salt.map_or(0, |s| s.len());
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha512_extract_init(
self.state.as_mut(),
salt_ptr,
salt_len as libc::size_t,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-512 extract init failed".into(),
));
}
Ok(())
}
pub fn extract_update(&mut self, ikm: &[u8]) -> Result<()> {
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha512_extract_update(
self.state.as_mut(),
ikm.as_ptr(),
ikm.len() as libc::size_t,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-512 extract update failed".into(),
));
}
Ok(())
}
pub fn extract_final(&mut self) -> Result<Prk> {
let mut prk = [0u8; KEYBYTES];
let result = unsafe {
libsodium_sys::crypto_kdf_hkdf_sha512_extract_final(
self.state.as_mut(),
prk.as_mut_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"HKDF-SHA-512 extract final failed".into(),
));
}
Ok(Prk(prk))
}
}
impl Drop for State {
fn drop(&mut self) {
unsafe {
let ptr = Box::into_raw(std::mem::replace(
&mut self.state,
Box::new(std::mem::zeroed()),
));
let layout = std::alloc::Layout::from_size_align(statebytes(), 8)
.expect("Invalid layout for crypto_kdf_hkdf_sha512_state");
std::alloc::dealloc(ptr as *mut u8, layout);
}
}
}
impl zeroize::Zeroize for State {
fn zeroize(&mut self) {
unsafe {
std::ptr::write_bytes(
self.state.as_mut() as *mut _ as *mut u8,
0,
statebytes(),
);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kdf() {
let master_key = Key::generate().unwrap();
let context = b"Examples";
let subkey = derive_from_key(32, 1, context, &master_key).unwrap();
assert_eq!(subkey.len(), 32);
let subkey2 = derive_from_key(32, 2, context, &master_key).unwrap();
assert_ne!(subkey, subkey2);
let invalid_length_result = derive_from_key(BYTES_MAX + 1, 1, context, &master_key);
assert!(invalid_length_result.is_err());
let invalid_context = b"Short";
let invalid_context_result = derive_from_key(32, 1, invalid_context, &master_key);
assert!(invalid_context_result.is_err());
}
#[test]
fn test_blake2b() {
let master_key = blake2b::Key::generate().unwrap();
let context = b"Examples";
let subkey = blake2b::derive_from_key(32, 1, context, &master_key).unwrap();
assert_eq!(subkey.len(), 32);
let subkey2 = blake2b::derive_from_key(32, 2, context, &master_key).unwrap();
assert_ne!(subkey, subkey2);
}
#[test]
fn test_hkdf_sha256() {
let ikm = b"input key material";
let salt = Some(b"salt".as_ref());
let prk = hkdf::sha256::extract(salt, ikm).unwrap();
assert_eq!(prk.as_bytes().len(), hkdf::sha256::KEYBYTES);
let ctx = Some(b"context".as_ref());
let out_len = 32;
let out = hkdf::sha256::expand(out_len, ctx, &prk).unwrap();
assert_eq!(out.len(), out_len);
let ctx2 = Some(b"different context".as_ref());
let out2 = hkdf::sha256::expand(out_len, ctx2, &prk).unwrap();
assert_ne!(out, out2);
let random_prk = hkdf::sha256::keygen();
assert_eq!(random_prk.as_bytes().len(), hkdf::sha256::KEYBYTES);
}
#[test]
fn test_hkdf_sha512() {
let ikm = b"input key material";
let salt = Some(b"salt".as_ref());
let prk = hkdf::sha512::extract(salt, ikm).unwrap();
assert_eq!(prk.as_bytes().len(), hkdf::sha512::KEYBYTES);
let ctx = Some(b"context".as_ref());
let out_len = 64;
let out = hkdf::sha512::expand(out_len, ctx, &prk).unwrap();
assert_eq!(out.len(), out_len);
let ctx2 = Some(b"different context".as_ref());
let out2 = hkdf::sha512::expand(out_len, ctx2, &prk).unwrap();
assert_ne!(out, out2);
let random_prk = hkdf::sha512::keygen();
assert_eq!(random_prk.as_bytes().len(), hkdf::sha512::KEYBYTES);
}
#[test]
fn test_hkdf_sha256_state() {
let ikm1 = b"input key";
let ikm2 = b" material";
let salt = Some(b"salt".as_ref());
let mut combined_ikm = Vec::new();
combined_ikm.extend_from_slice(ikm1);
combined_ikm.extend_from_slice(ikm2);
let prk1 = hkdf::sha256::extract(salt, &combined_ikm).unwrap();
let mut state = hkdf::sha256::State::new();
state.extract_init(salt).unwrap();
state.extract_update(ikm1).unwrap();
state.extract_update(ikm2).unwrap();
let prk2 = state.extract_final().unwrap();
assert_eq!(prk1.as_bytes(), prk2.as_bytes());
}
#[test]
fn test_hkdf_sha512_state() {
let ikm1 = b"input key";
let ikm2 = b" material";
let salt = Some(b"salt".as_ref());
let mut combined_ikm = Vec::new();
combined_ikm.extend_from_slice(ikm1);
combined_ikm.extend_from_slice(ikm2);
let prk1 = hkdf::sha512::extract(salt, &combined_ikm).unwrap();
let mut state = hkdf::sha512::State::new();
state.extract_init(salt).unwrap();
state.extract_update(ikm1).unwrap();
state.extract_update(ikm2).unwrap();
let prk2 = state.extract_final().unwrap();
assert_eq!(prk1.as_bytes(), prk2.as_bytes());
}
#[test]
fn test_main_key_traits() {
let key = Key::generate().unwrap();
let key_ref: &[u8] = key.as_ref();
assert_eq!(key_ref.len(), KEYBYTES);
let bytes = [0x42; KEYBYTES];
let key_from_slice = Key::try_from(&bytes[..]).unwrap();
assert_eq!(key_from_slice.as_ref(), &bytes);
let invalid_bytes = [0x42; KEYBYTES - 1];
assert!(Key::try_from(&invalid_bytes[..]).is_err());
let key_from_bytes = Key::from(bytes);
assert_eq!(key_from_bytes.as_ref(), &bytes);
let key = Key::from(bytes);
let bytes_from_key: [u8; KEYBYTES] = key.into();
assert_eq!(bytes_from_key, bytes);
}
#[test]
fn test_blake2b_key_traits() {
let key = blake2b::Key::generate().unwrap();
let key_ref: &[u8] = key.as_ref();
assert_eq!(key_ref.len(), blake2b::KEYBYTES);
let bytes = [0x42; blake2b::KEYBYTES];
let key_from_slice = blake2b::Key::try_from(&bytes[..]).unwrap();
assert_eq!(key_from_slice.as_ref(), &bytes);
let invalid_bytes = [0x42; blake2b::KEYBYTES - 1];
assert!(blake2b::Key::try_from(&invalid_bytes[..]).is_err());
let key_from_bytes = blake2b::Key::from(bytes);
assert_eq!(key_from_bytes.as_ref(), &bytes);
let key = blake2b::Key::from(bytes);
let bytes_from_key: [u8; blake2b::KEYBYTES] = key.into();
assert_eq!(bytes_from_key, bytes);
}
#[test]
fn test_hkdf_sha256_prk_traits() {
let prk = hkdf::sha256::keygen();
let prk_ref: &[u8] = prk.as_ref();
assert_eq!(prk_ref.len(), hkdf::sha256::KEYBYTES);
let bytes = [0x42; hkdf::sha256::KEYBYTES];
let prk_from_slice = hkdf::sha256::Prk::try_from(&bytes[..]).unwrap();
assert_eq!(prk_from_slice.as_ref(), &bytes);
let invalid_bytes = [0x42; hkdf::sha256::KEYBYTES - 1];
assert!(hkdf::sha256::Prk::try_from(&invalid_bytes[..]).is_err());
let prk_from_bytes = hkdf::sha256::Prk::from(bytes);
assert_eq!(prk_from_bytes.as_ref(), &bytes);
let prk = hkdf::sha256::Prk::from(bytes);
let bytes_from_prk: [u8; hkdf::sha256::KEYBYTES] = prk.into();
assert_eq!(bytes_from_prk, bytes);
}
#[test]
fn test_hkdf_sha512_prk_traits() {
let prk = hkdf::sha512::keygen();
let prk_ref: &[u8] = prk.as_ref();
assert_eq!(prk_ref.len(), hkdf::sha512::KEYBYTES);
let bytes = [0x42; hkdf::sha512::KEYBYTES];
let prk_from_slice = hkdf::sha512::Prk::try_from(&bytes[..]).unwrap();
assert_eq!(prk_from_slice.as_ref(), &bytes);
let invalid_bytes = [0x42; hkdf::sha512::KEYBYTES - 1];
assert!(hkdf::sha512::Prk::try_from(&invalid_bytes[..]).is_err());
let prk_from_bytes = hkdf::sha512::Prk::from(bytes);
assert_eq!(prk_from_bytes.as_ref(), &bytes);
let prk = hkdf::sha512::Prk::from(bytes);
let bytes_from_prk: [u8; hkdf::sha512::KEYBYTES] = prk.into();
assert_eq!(bytes_from_prk, bytes);
}
}