use crate::{Result, SodiumError};
use std::convert::TryFrom;
pub const KEYBYTES: usize = libsodium_sys::crypto_shorthash_KEYBYTES as usize;
pub const BYTES: usize = libsodium_sys::crypto_shorthash_BYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl Key {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"key must be exactly {KEYBYTES} bytes"
)));
}
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(bytes);
Ok(Key(key))
}
pub fn generate() -> Self {
let bytes = crate::random::bytes(KEYBYTES);
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(&bytes);
Key(key)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
pub fn shorthash(input: &[u8], key: &Key) -> [u8; BYTES] {
let mut out = [0u8; BYTES];
unsafe {
libsodium_sys::crypto_shorthash(
out.as_mut_ptr(),
input.as_ptr(),
input.len() as u64,
key.as_bytes().as_ptr(),
);
}
out
}
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Key {
type Error = SodiumError;
fn try_from(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}
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
}
}
pub mod siphash24 {
use super::*;
pub const KEYBYTES: usize = libsodium_sys::crypto_shorthash_siphash24_KEYBYTES as usize;
pub const BYTES: usize = libsodium_sys::crypto_shorthash_siphash24_BYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl Key {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"key must be exactly {KEYBYTES} bytes"
)));
}
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(bytes);
Ok(Key(key))
}
pub fn generate() -> Self {
let bytes = crate::random::bytes(KEYBYTES);
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(&bytes);
Key(key)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
pub fn shorthash(input: &[u8], key: &Key) -> [u8; BYTES] {
let mut out = [0u8; BYTES];
unsafe {
libsodium_sys::crypto_shorthash_siphash24(
out.as_mut_ptr(),
input.as_ptr(),
input.len() as u64,
key.as_bytes().as_ptr(),
);
}
out
}
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Key {
type Error = SodiumError;
fn try_from(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}
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
}
}
}
pub mod siphashx24 {
use super::*;
pub const KEYBYTES: usize = libsodium_sys::crypto_shorthash_siphashx24_KEYBYTES as usize;
pub const BYTES: usize = libsodium_sys::crypto_shorthash_siphashx24_BYTES as usize;
#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl Key {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != KEYBYTES {
return Err(SodiumError::InvalidInput(format!(
"key must be exactly {KEYBYTES} bytes"
)));
}
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(bytes);
Ok(Key(key))
}
pub fn generate() -> Self {
let bytes = crate::random::bytes(KEYBYTES);
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(&bytes);
Key(key)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
pub fn shorthash(input: &[u8], key: &Key) -> [u8; BYTES] {
let mut out = [0u8; BYTES];
unsafe {
libsodium_sys::crypto_shorthash_siphashx24(
out.as_mut_ptr(),
input.as_ptr(),
input.len() as u64,
key.as_bytes().as_ptr(),
);
}
out
}
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Key {
type Error = SodiumError;
fn try_from(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}
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
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shorthash() {
let key = Key::generate();
let data = b"test data";
let hash = shorthash(data, &key);
assert_eq!(hash.len(), BYTES);
let hash2 = shorthash(data, &key);
assert_eq!(hash, hash2);
let data2 = b"different data";
let hash3 = shorthash(data2, &key);
assert_ne!(hash, hash3);
let key2 = Key::generate();
let hash4 = shorthash(data, &key2);
assert_ne!(hash, hash4);
}
#[test]
fn test_siphash24() {
let key = siphash24::Key::generate();
let data = b"test data";
let hash = siphash24::shorthash(data, &key);
assert_eq!(hash.len(), siphash24::BYTES);
let hash2 = siphash24::shorthash(data, &key);
assert_eq!(hash, hash2);
}
#[test]
fn test_siphashx24() {
let key = siphashx24::Key::generate();
let data = b"test data";
let hash = siphashx24::shorthash(data, &key);
assert_eq!(hash.len(), siphashx24::BYTES);
let hash2 = siphashx24::shorthash(data, &key);
assert_eq!(hash, hash2);
}
#[test]
fn test_key_traits() {
let key_bytes = [0u8; KEYBYTES];
let key = Key::from(key_bytes);
assert_eq!(key.as_ref(), &key_bytes);
let bytes_back: [u8; KEYBYTES] = key.clone().into();
assert_eq!(bytes_back, key_bytes);
let key_from_slice = Key::try_from(&key_bytes[..]).unwrap();
assert_eq!(key_from_slice, key);
let short_slice = vec![0u8; KEYBYTES - 1];
assert!(Key::try_from(short_slice.as_slice()).is_err());
let long_slice = vec![0u8; KEYBYTES + 1];
assert!(Key::try_from(long_slice.as_slice()).is_err());
}
#[test]
fn test_siphash24_key_traits() {
let key_bytes = [0u8; siphash24::KEYBYTES];
let key = siphash24::Key::from(key_bytes);
assert_eq!(key.as_ref(), &key_bytes);
let bytes_back: [u8; siphash24::KEYBYTES] = key.clone().into();
assert_eq!(bytes_back, key_bytes);
let key_from_slice = siphash24::Key::try_from(&key_bytes[..]).unwrap();
assert_eq!(key_from_slice, key);
let short_slice = vec![0u8; siphash24::KEYBYTES - 1];
assert!(siphash24::Key::try_from(short_slice.as_slice()).is_err());
let long_slice = vec![0u8; siphash24::KEYBYTES + 1];
assert!(siphash24::Key::try_from(long_slice.as_slice()).is_err());
}
#[test]
fn test_siphashx24_key_traits() {
let key_bytes = [0u8; siphashx24::KEYBYTES];
let key = siphashx24::Key::from(key_bytes);
assert_eq!(key.as_ref(), &key_bytes);
let bytes_back: [u8; siphashx24::KEYBYTES] = key.clone().into();
assert_eq!(bytes_back, key_bytes);
let key_from_slice = siphashx24::Key::try_from(&key_bytes[..]).unwrap();
assert_eq!(key_from_slice, key);
let short_slice = vec![0u8; siphashx24::KEYBYTES - 1];
assert!(siphashx24::Key::try_from(short_slice.as_slice()).is_err());
let long_slice = vec![0u8; siphashx24::KEYBYTES + 1];
assert!(siphashx24::Key::try_from(long_slice.as_slice()).is_err());
}
#[test]
fn test_key_conversion_roundtrip() {
let original_key = Key::generate();
let bytes: [u8; KEYBYTES] = original_key.clone().into();
let reconstructed_key = Key::from(bytes);
assert_eq!(original_key, reconstructed_key);
let key_via_try = Key::try_from(&bytes[..]).unwrap();
assert_eq!(original_key, key_via_try);
}
#[test]
fn test_siphash24_key_conversion_roundtrip() {
let original_key = siphash24::Key::generate();
let bytes: [u8; siphash24::KEYBYTES] = original_key.clone().into();
let reconstructed_key = siphash24::Key::from(bytes);
assert_eq!(original_key, reconstructed_key);
let key_via_try = siphash24::Key::try_from(&bytes[..]).unwrap();
assert_eq!(original_key, key_via_try);
}
#[test]
fn test_siphashx24_key_conversion_roundtrip() {
let original_key = siphashx24::Key::generate();
let bytes: [u8; siphashx24::KEYBYTES] = original_key.clone().into();
let reconstructed_key = siphashx24::Key::from(bytes);
assert_eq!(original_key, reconstructed_key);
let key_via_try = siphashx24::Key::try_from(&bytes[..]).unwrap();
assert_eq!(original_key, key_via_try);
}
#[test]
fn test_as_ref_consistency() {
let key = Key::generate();
assert_eq!(key.as_ref(), key.as_bytes());
let key24 = siphash24::Key::generate();
assert_eq!(key24.as_ref(), key24.as_bytes());
let keyx24 = siphashx24::Key::generate();
assert_eq!(keyx24.as_ref(), keyx24.as_bytes());
}
#[test]
fn test_try_from_error_messages() {
let short_slice = vec![0u8; 5];
let result = Key::try_from(short_slice.as_slice());
assert!(result.is_err());
if let Err(SodiumError::InvalidInput(msg)) = result {
assert!(msg.contains(&KEYBYTES.to_string()));
} else {
panic!("Expected InvalidInput error");
}
let result24 = siphash24::Key::try_from(short_slice.as_slice());
assert!(result24.is_err());
if let Err(SodiumError::InvalidInput(msg)) = result24 {
assert!(msg.contains(&siphash24::KEYBYTES.to_string()));
} else {
panic!("Expected InvalidInput error");
}
let resultx24 = siphashx24::Key::try_from(short_slice.as_slice());
assert!(resultx24.is_err());
if let Err(SodiumError::InvalidInput(msg)) = resultx24 {
assert!(msg.contains(&siphashx24::KEYBYTES.to_string()));
} else {
panic!("Expected InvalidInput error");
}
}
}