chore: Move hashing functions into sodium/ciphers crate

This finishes the last step of removing sodium.rs from the rosenpass crate
itself and also removes the NOTHING and NONCE0 constants.

Hashing functions now use destination parameters;
rosenpass_constant_time::xor now does too.
This commit is contained in:
Karolin Varner
2023-11-28 15:06:27 +01:00
committed by Karolin Varner
parent d44793e07f
commit 09aa0e027e
21 changed files with 203 additions and 202 deletions

17
Cargo.lock generated
View File

@@ -1074,6 +1074,7 @@ dependencies = [
"rosenpass-ciphers",
"rosenpass-constant-time",
"rosenpass-sodium",
"rosenpass-to",
"rosenpass-util",
"serde",
"stacker",
@@ -1087,12 +1088,20 @@ dependencies = [
name = "rosenpass-ciphers"
version = "0.1.0"
dependencies = [
"anyhow",
"rosenpass-constant-time",
"rosenpass-sodium",
"rosenpass-to",
"static_assertions",
"zeroize",
]
[[package]]
name = "rosenpass-constant-time"
version = "0.1.0"
dependencies = [
"rosenpass-to",
]
[[package]]
name = "rosenpass-fuzzing"
@@ -1103,6 +1112,7 @@ dependencies = [
"rosenpass",
"rosenpass-ciphers",
"rosenpass-sodium",
"rosenpass-to",
"stacker",
]
@@ -1113,6 +1123,7 @@ dependencies = [
"anyhow",
"libsodium-sys-stable",
"log",
"rosenpass-to",
"rosenpass-util",
]
@@ -1712,6 +1723,12 @@ dependencies = [
"syn",
]
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
[[package]]
name = "zip"
version = "0.6.6"

View File

@@ -10,4 +10,9 @@ repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
[dependencies]
anyhow = "1.0.75"
rosenpass-sodium = { path = "../sodium" }
rosenpass-to = { path = "../to" }
rosenpass-constant-time = { path = "../constant-time" }
static_assertions = "1.1.0"
zeroize = "1.7.0"

View File

@@ -1,11 +1,28 @@
use static_assertions::const_assert;
pub mod subtle;
pub const KEY_LEN: usize = 32;
const_assert!(KEY_LEN == aead::KEY_LEN);
const_assert!(KEY_LEN == xaead::KEY_LEN);
const_assert!(KEY_LEN == hash::KEY_LEN);
/// Authenticated encryption with associated data
pub mod aead {
pub use rosenpass_sodium::aead::chacha20poly1305_ietf::{
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
};
}
/// Authenticated encryption with associated data with a constant nonce
pub mod xaead {
pub use rosenpass_sodium::aead::xchacha20poly1305_ietf::{
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
};
}
pub mod hash {
pub use crate::subtle::incorrect_hmac_blake2b::{
hash, KEY_LEN, KEY_MAX, KEY_MIN, OUT_MAX, OUT_MIN,
};
}

View File

@@ -0,0 +1,44 @@
use anyhow::ensure;
use rosenpass_constant_time::xor;
use rosenpass_sodium::hash::blake2b;
use rosenpass_to::{ops::copy_slice, with_destination, To};
use zeroize::Zeroizing;
pub const KEY_LEN: usize = 32;
pub const KEY_MIN: usize = KEY_LEN;
pub const KEY_MAX: usize = KEY_LEN;
pub const OUT_MIN: usize = blake2b::OUT_MIN;
pub const OUT_MAX: usize = blake2b::OUT_MAX;
/// This is a woefully incorrect implementation of hmac_blake2b.
/// See <https://github.com/rosenpass/rosenpass/issues/68#issuecomment-1563612222>
///
/// It accepts 32 byte keys, exclusively.
///
/// This will be replaced, likely by Kekkac at some point soon.
/// <https://github.com/rosenpass/rosenpass/pull/145>
#[inline]
pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<()>> + 'a {
const IPAD: [u8; KEY_LEN] = [0x36u8; KEY_LEN];
const OPAD: [u8; KEY_LEN] = [0x5Cu8; KEY_LEN];
with_destination(|out: &mut [u8]| {
// Not bothering with padding; the implementation
// uses appropriately sized keys.
ensure!(key.len() == KEY_LEN);
type Key = Zeroizing<[u8; KEY_LEN]>;
let mut tmp_key = Key::default();
copy_slice(key).to(tmp_key.as_mut());
xor(&IPAD).to(tmp_key.as_mut());
let mut outer_data = Key::default();
blake2b::hash(tmp_key.as_ref(), data).to(outer_data.as_mut())?;
copy_slice(key).to(tmp_key.as_mut());
xor(&OPAD).to(tmp_key.as_mut());
blake2b::hash(tmp_key.as_ref(), outer_data.as_ref()).to(out)?;
Ok(())
})
}

View File

@@ -0,0 +1 @@
pub mod incorrect_hmac_blake2b;

View File

@@ -12,3 +12,4 @@ readme = "readme.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rosenpass-to = { path = "../to" }

View File

@@ -1,18 +1,26 @@
/// Xors a and b element-wise and writes the result into a.
use rosenpass_to::{with_destination, To};
/// Xors the source into the destination
///
/// # Examples
///
/// ```
/// use rosenpass_constant_time::xor_into;
/// let mut a = String::from("hello").into_bytes();
/// let b = b"world";
/// xor_into(&mut a, b);
/// assert_eq!(&a, b"\x1f\n\x1e\x00\x0b");
/// use rosenpass_constant_time::xor;
/// use rosenpass_to::To;
/// assert_eq!(
/// xor(b"world").to_this(|| b"hello".to_vec()),
/// b"\x1f\n\x1e\x00\x0b");
/// ```
///
/// # Panics
///
/// If source and destination are of different sizes.
#[inline]
pub fn xor_into(a: &mut [u8], b: &[u8]) {
assert!(a.len() == b.len());
for (av, bv) in a.iter_mut().zip(b.iter()) {
*av ^= *bv;
}
pub fn xor<'a>(src: &'a [u8]) -> impl To<[u8], ()> + 'a {
with_destination(|dst: &mut [u8]| {
assert!(src.len() == dst.len());
for (dv, sv) in dst.iter_mut().zip(src.iter()) {
*dv ^= *sv;
}
})
}

View File

@@ -21,6 +21,9 @@ path = "../sodium"
[dependencies.rosenpass-ciphers]
path = "../ciphers"
[dependencies.rosenpass-to]
path = "../to"
[[bin]]
name = "fuzz_handle_msg"
path = "fuzz_targets/handle_msg.rs"

View File

@@ -4,8 +4,8 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target;
use rosenpass::sodium::mac_into;
use rosenpass_sodium::init as sodium_init;
use rosenpass_sodium::{hash::blake2b, init as sodium_init};
use rosenpass_to::To;
#[derive(arbitrary::Arbitrary, Debug)]
pub struct Blake2b {
@@ -18,5 +18,5 @@ fuzz_target!(|input: Blake2b| {
let mut out = [0u8; 32];
mac_into(&mut out, &input.key, &input.data).unwrap();
blake2b::hash(&input.key, &input.data).to(&mut out).unwrap();
});

View File

@@ -18,6 +18,7 @@ rosenpass-util = { path = "../util" }
rosenpass-constant-time = { path = "../constant-time" }
rosenpass-sodium = { path = "../sodium" }
rosenpass-ciphers = { path = "../ciphers" }
rosenpass-to = { path = "../to" }
anyhow = { version = "1.0.71", features = ["backtrace"] }
static_assertions = "1.1.0"
memoffset = "0.9.0"

View File

@@ -1,10 +1,10 @@
//! Pseudo Random Functions (PRFs) with a tree-like label scheme which
//! ensures their uniqueness
use {
crate::{prftree::PrfTree, sodium::KEY_SIZE},
anyhow::Result,
};
use crate::prftree::PrfTree;
use anyhow::Result;
use rosenpass_ciphers::KEY_LEN;
pub fn protocol() -> Result<PrfTree> {
PrfTree::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes())
@@ -30,7 +30,7 @@ prflabel!(protocol, _ckextract, "chaining key extract");
macro_rules! prflabel_leaf {
($base:ident, $name:ident, $($lbl:expr),* ) => {
pub fn $name() -> Result<[u8; KEY_SIZE]> {
pub fn $name() -> Result<[u8; KEY_LEN]> {
let t = $base()?;
$( let t = t.mix($lbl.as_bytes())?; )*
Ok(t.into_value())

View File

@@ -1,5 +1,3 @@
#[macro_use]
pub mod sodium;
pub mod coloring;
#[rustfmt::skip]
pub mod labeled_prf;

View File

@@ -17,23 +17,16 @@
//!
//! TODO Base the construction on a proper Dec function
pub struct Iprf([u8; KEY_SIZE]);
pub struct IprfBranch([u8; KEY_SIZE]);
pub struct SecretIprf(Secret<KEY_SIZE>);
pub struct SecretIprfBranch(Secret<KEY_SIZE>);
use rosenpass_ciphers::{KEY_LEN, hash};
pub fn prf_into(out: &mut [u8], key: &[u8], data: &[u8]) {
// TODO: The error handling with sodium is a scurge
hmac_into(out, key, data).unwrap()
}
pub fn prf(key: &[u8], data: &[u8]) -> [u8; KEY_SIZE] {
mutating([0u8; KEY_SIZE], |r| prf_into(r, key, data))
}
pub struct Iprf([u8; KEY_LEN]);
pub struct IprfBranch([u8; KEY_LEN]);
pub struct SecretIprf(Secret<KEY_LEN>);
pub struct SecretIprfBranch(Secret<KEY_LEN>);
impl Iprf {
fn zero() -> Self {
Self([0u8; KEY_SIZE])
Self([0u8; KEY_LEN])
}
fn dup(self) -> IprfBranch {
@@ -42,25 +35,25 @@ impl Iprf {
// TODO: Protocol! Use domain separation to ensure that
fn mix(self, v: &[u8]) -> Self {
Self(prf(&self.0, v))
Self(hash(&self.0, v).collect<[u8; KEY_LEN]>())
}
fn mix_secret<const N: usize>(self, v: Secret<N>) -> SecretIprf {
SecretIprf::prf_invoc(&self.0, v.secret())
}
fn into_value(self) -> [u8; KEY_SIZE] {
fn into_value(self) -> [u8; KEY_LEN] {
self.0
}
fn extract(self, v: &[u8], dst: &mut [u8]) {
prf_into(&self.0, v, dst)
hash(&self.0, v).to(dst)
}
}
impl IprfBranch {
fn mix(&self, v: &[u8]) -> Iprf {
Iprf(prf(self.0, v))
Iprf(hash(self.0, v).collect<[u8; KEY_LEN]>())
}
fn mix_secret<const N: usize>(&self, v: Secret<N>) -> SecretIprf {
@@ -71,7 +64,7 @@ impl IprfBranch {
impl SecretIprf {
fn prf_invoc(k: &[u8], d: &[u8]) -> SecretIprf {
mutating(SecretIprf(Secret::zero()), |r| {
prf_into(k, d, r.secret_mut())
hash(k, d).to(r.secret_mut())
})
}
@@ -87,12 +80,12 @@ impl SecretIprf {
Self::prf_invoc(self.0.secret(), v.secret())
}
fn into_secret(self) -> Secret<KEY_SIZE> {
fn into_secret(self) -> Secret<KEY_LEN> {
self.0
}
fn into_secret_slice(self, v: &[u8], dst: &[u8]) {
prf_into(self.0.secret(), v, dst)
hash(self.0.secret(), v).to(dst)
}
}

View File

@@ -44,8 +44,8 @@
//! ```
use super::RosenpassError;
use crate::{pqkem::*, sodium};
use rosenpass_ciphers::{aead, xaead};
use crate::pqkem::*;
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
// Macro magic ////////////////////////////////////////////////////////////////
@@ -265,9 +265,9 @@ data_lense! { Envelope<M> :=
payload: M::LEN,
/// Message Authentication Code (mac) over all bytes until (exclusive)
/// `mac` itself
mac: sodium::MAC_SIZE,
mac: 16,
/// Currently unused, TODO: do something with this
cookie: sodium::MAC_SIZE
cookie: 16
}
data_lense! { InitHello :=
@@ -320,11 +320,11 @@ data_lense! { EmptyData :=
data_lense! { Biscuit :=
/// H(spki) Ident ifies the initiator
pidi: sodium::KEY_SIZE,
pidi: KEY_LEN,
/// The biscuit number (replay protection)
biscuit_no: 12,
/// Chaining key
ck: sodium::KEY_SIZE
ck: KEY_LEN
}
data_lense! { DataMsg :=
@@ -389,20 +389,17 @@ pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG
#[cfg(test)]
mod test_constants {
use crate::{
msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN},
sodium,
};
use rosenpass_ciphers::xaead;
use crate::msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN};
use rosenpass_ciphers::{xaead, KEY_LEN};
#[test]
fn sodium_keysize() {
assert_eq!(sodium::KEY_SIZE, 32);
assert_eq!(KEY_LEN, 32);
}
#[test]
fn biscuit_pt_len() {
assert_eq!(BISCUIT_PT_LEN, 2 * sodium::KEY_SIZE + 12);
assert_eq!(BISCUIT_PT_LEN, 2 * KEY_LEN + 12);
}
#[test]

View File

@@ -1,25 +1,23 @@
//! Implementation of the tree-like structure used for the label derivation in [labeled_prf](crate::labeled_prf)
use {
crate::{
coloring::Secret,
sodium::{hmac, hmac_into, KEY_SIZE},
},
anyhow::Result,
};
use crate::coloring::Secret;
use anyhow::Result;
use rosenpass_ciphers::{hash, KEY_LEN};
use rosenpass_to::To;
// TODO Use a proper Dec interface
#[derive(Clone, Debug)]
pub struct PrfTree([u8; KEY_SIZE]);
pub struct PrfTree([u8; KEY_LEN]);
#[derive(Clone, Debug)]
pub struct PrfTreeBranch([u8; KEY_SIZE]);
pub struct PrfTreeBranch([u8; KEY_LEN]);
#[derive(Clone, Debug)]
pub struct SecretPrfTree(Secret<KEY_SIZE>);
pub struct SecretPrfTree(Secret<KEY_LEN>);
#[derive(Clone, Debug)]
pub struct SecretPrfTreeBranch(Secret<KEY_SIZE>);
pub struct SecretPrfTreeBranch(Secret<KEY_LEN>);
impl PrfTree {
pub fn zero() -> Self {
Self([0u8; KEY_SIZE])
Self([0u8; KEY_LEN])
}
pub fn dup(self) -> PrfTreeBranch {
@@ -32,21 +30,21 @@ impl PrfTree {
// TODO: Protocol! Use domain separation to ensure that
pub fn mix(self, v: &[u8]) -> Result<Self> {
Ok(Self(hmac(&self.0, v)?))
Ok(Self(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?))
}
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretPrfTree> {
SecretPrfTree::prf_invoc(&self.0, v.secret())
}
pub fn into_value(self) -> [u8; KEY_SIZE] {
pub fn into_value(self) -> [u8; KEY_LEN] {
self.0
}
}
impl PrfTreeBranch {
pub fn mix(&self, v: &[u8]) -> Result<PrfTree> {
Ok(PrfTree(hmac(&self.0, v)?))
Ok(PrfTree(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?))
}
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretPrfTree> {
@@ -57,7 +55,7 @@ impl PrfTreeBranch {
impl SecretPrfTree {
pub fn prf_invoc(k: &[u8], d: &[u8]) -> Result<SecretPrfTree> {
let mut r = SecretPrfTree(Secret::zero());
hmac_into(r.0.secret_mut(), k, d)?;
hash::hash(k, d).to(r.0.secret_mut())?;
Ok(r)
}
@@ -69,7 +67,7 @@ impl SecretPrfTree {
SecretPrfTreeBranch(self.0)
}
pub fn danger_from_secret(k: Secret<KEY_SIZE>) -> Self {
pub fn danger_from_secret(k: Secret<KEY_LEN>) -> Self {
Self(k)
}
@@ -81,12 +79,12 @@ impl SecretPrfTree {
Self::prf_invoc(self.0.secret(), v.secret())
}
pub fn into_secret(self) -> Secret<KEY_SIZE> {
pub fn into_secret(self) -> Secret<KEY_LEN> {
self.0
}
pub fn into_secret_slice(mut self, v: &[u8], dst: &[u8]) -> Result<()> {
hmac_into(self.0.secret_mut(), v, dst)
hash::hash(v, dst).to(self.0.secret_mut())
}
}
@@ -102,7 +100,7 @@ impl SecretPrfTreeBranch {
// TODO: This entire API is not very nice; we need this for biscuits, but
// it might be better to extract a special "biscuit"
// labeled subkey and reinitialize the chain with this
pub fn danger_into_secret(self) -> Secret<KEY_SIZE> {
pub fn danger_into_secret(self) -> Secret<KEY_LEN> {
self.0
}
}

View File

@@ -73,10 +73,9 @@ use crate::{
msgs::*,
pqkem::*,
prftree::{SecretPrfTree, SecretPrfTreeBranch},
sodium::*,
};
use anyhow::{bail, ensure, Context, Result};
use rosenpass_ciphers::{aead, xaead};
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase};
use std::collections::hash_map::{
Entry::{Occupied, Vacant},
@@ -145,10 +144,10 @@ pub type SSk = Secret<{ StaticKEM::SK_LEN }>;
pub type EPk = Public<{ EphemeralKEM::PK_LEN }>;
pub type ESk = Secret<{ EphemeralKEM::SK_LEN }>;
pub type SymKey = Secret<KEY_SIZE>;
pub type SymHash = Public<KEY_SIZE>;
pub type SymKey = Secret<KEY_LEN>;
pub type SymHash = Public<KEY_LEN>;
pub type PeerId = Public<KEY_SIZE>;
pub type PeerId = Public<KEY_LEN>;
pub type SessionId = Public<SESSION_ID_LEN>;
pub type BiscuitId = Public<BISCUIT_ID_LEN>;
@@ -1240,13 +1239,13 @@ impl HandshakeState {
pub fn encrypt_and_mix(&mut self, ct: &mut [u8], pt: &[u8]) -> Result<&mut Self> {
let k = self.ck.mix(&lprf::hs_enc()?)?.into_secret();
aead::encrypt(ct, k.secret(), &NONCE0, &NOTHING, pt)?;
aead::encrypt(ct, k.secret(), &[0u8; aead::NONCE_LEN], &[], pt)?;
self.mix(ct)
}
pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> {
let k = self.ck.mix(&lprf::hs_enc()?)?.into_secret();
aead::decrypt(pt, k.secret(), &NONCE0, &NOTHING, ct)?;
aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?;
self.mix(ct)
}
@@ -1448,7 +1447,7 @@ impl CryptoServer {
.mix(peer.get(self).psk.secret())?;
// IHI8
hs.core.encrypt_and_mix(ih.auth_mut(), &NOTHING)?;
hs.core.encrypt_and_mix(ih.auth_mut(), &[])?;
// Update the handshake hash last (not changing any state on prior error
peer.hs().insert(self, hs)?;
@@ -1514,7 +1513,7 @@ impl CryptoServer {
core.store_biscuit(self, peer, rh.biscuit_mut())?;
// RHR7
core.encrypt_and_mix(rh.auth_mut(), &NOTHING)?;
core.encrypt_and_mix(rh.auth_mut(), &[])?;
Ok(peer)
}
@@ -1600,7 +1599,7 @@ impl CryptoServer {
ic.biscuit_mut().copy_from_slice(rh.biscuit());
// ICI4
core.encrypt_and_mix(ic.auth_mut(), &NOTHING)?;
core.encrypt_and_mix(ic.auth_mut(), &[])?;
// Split() We move the secrets into the session; we do not
// delete the InitiatorHandshake, just clear it's secrets because
@@ -1630,7 +1629,7 @@ impl CryptoServer {
)?;
// ICR2
core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &NOTHING)?;
core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &[])?;
// ICR3
core.mix(ic.sidi())?.mix(ic.sidr())?;
@@ -1686,7 +1685,7 @@ impl CryptoServer {
let n = cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]);
let k = ses.txkm.secret();
aead::encrypt(rc.auth_mut(), k, &n, &NOTHING, &NOTHING)?; // ct, k, n, ad, pt
aead::encrypt(rc.auth_mut(), k, &n, &[], &[])?; // ct, k, n, ad, pt
Ok(peer)
}
@@ -1723,7 +1722,7 @@ impl CryptoServer {
&mut [0u8; 0],
s.txkt.secret(),
&cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]),
&NOTHING,
&[],
rc.auth(),
)?;
}

View File

@@ -1,115 +0,0 @@
//! Bindings and helpers for accessing libsodium functions
use anyhow::{ensure, Result};
use libsodium_sys as libsodium;
use rosenpass_constant_time::xor_into;
use rosenpass_util::attempt;
use static_assertions::const_assert_eq;
use std::os::raw::c_ulonglong;
use std::ptr::null as nullptr;
pub const NONCE0: [u8; libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize] =
[0u8; libsodium::crypto_aead_chacha20poly1305_IETF_NPUBBYTES as usize];
pub const NOTHING: [u8; 0] = [0u8; 0];
pub const KEY_SIZE: usize = 32;
pub const MAC_SIZE: usize = 16;
const_assert_eq!(
KEY_SIZE,
libsodium::crypto_aead_chacha20poly1305_IETF_KEYBYTES as usize
);
const_assert_eq!(KEY_SIZE, libsodium::crypto_generichash_BYTES as usize);
macro_rules! sodium_call {
($name:ident, $($args:expr),*) => { attempt!({
ensure!(unsafe{libsodium::$name($($args),*)} > -1,
"Error in libsodium's {}.", stringify!($name));
Ok(())
})};
($name:ident) => { sodium_call!($name, ) };
}
#[inline]
fn blake2b_flexible(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> {
const KEY_MIN: usize = libsodium::crypto_generichash_KEYBYTES_MIN as usize;
const KEY_MAX: usize = libsodium::crypto_generichash_KEYBYTES_MAX as usize;
const OUT_MIN: usize = libsodium::crypto_generichash_BYTES_MIN as usize;
const OUT_MAX: usize = libsodium::crypto_generichash_BYTES_MAX as usize;
assert!(key.is_empty() || (KEY_MIN <= key.len() && key.len() <= KEY_MAX));
assert!(OUT_MIN <= out.len() && out.len() <= OUT_MAX);
let kptr = match key.len() {
// NULL key
0 => nullptr(),
_ => key.as_ptr(),
};
sodium_call!(
crypto_generichash_blake2b,
out.as_mut_ptr(),
out.len(),
data.as_ptr(),
data.len() as c_ulonglong,
kptr,
key.len()
)
}
// TODO: Use proper streaming hash; for mix_hash too.
#[inline]
pub fn hash_into(out: &mut [u8], data: &[u8]) -> Result<()> {
assert!(out.len() == KEY_SIZE);
blake2b_flexible(out, &NOTHING, data)
}
#[inline]
pub fn hash(data: &[u8]) -> Result<[u8; KEY_SIZE]> {
let mut r = [0u8; KEY_SIZE];
hash_into(&mut r, data)?;
Ok(r)
}
#[inline]
pub fn mac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> {
assert!(out.len() == KEY_SIZE);
assert!(key.len() == KEY_SIZE);
blake2b_flexible(out, key, data)
}
#[inline]
pub fn mac(key: &[u8], data: &[u8]) -> Result<[u8; KEY_SIZE]> {
let mut r = [0u8; KEY_SIZE];
mac_into(&mut r, key, data)?;
Ok(r)
}
#[inline]
pub fn mac16(key: &[u8], data: &[u8]) -> Result<[u8; 16]> {
assert!(key.len() == KEY_SIZE);
let mut out = [0u8; 16];
blake2b_flexible(&mut out, key, data)?;
Ok(out)
}
#[inline]
pub fn hmac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> {
// Not bothering with padding; the implementation
// uses appropriately sized keys.
ensure!(key.len() == KEY_SIZE);
const IPAD: [u8; KEY_SIZE] = [0x36u8; KEY_SIZE];
let mut temp_key = [0u8; KEY_SIZE];
temp_key.copy_from_slice(key);
xor_into(&mut temp_key, &IPAD);
let outer_data = mac(&temp_key, data)?;
const OPAD: [u8; KEY_SIZE] = [0x5Cu8; KEY_SIZE];
temp_key.copy_from_slice(key);
xor_into(&mut temp_key, &OPAD);
mac_into(out, &temp_key, &outer_data)
}
#[inline]
pub fn hmac(key: &[u8], data: &[u8]) -> Result<[u8; KEY_SIZE]> {
let mut r = [0u8; KEY_SIZE];
hmac_into(&mut r, key, data)?;
Ok(r)
}

View File

@@ -11,6 +11,7 @@ readme = "readme.md"
[dependencies]
rosenpass-util = { path = "../util" }
rosenpass-to = { path = "../to" }
anyhow = { version = "1.0.71", features = ["backtrace"] }
libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] }
log = { version = "0.4.17" }

View File

@@ -0,0 +1,31 @@
use libsodium_sys as libsodium;
use rosenpass_to::{with_destination, To};
use std::ffi::c_ulonglong;
use std::ptr::null;
pub const KEY_MIN: usize = libsodium::crypto_generichash_blake2b_KEYBYTES_MIN as usize;
pub const KEY_MAX: usize = libsodium::crypto_generichash_blake2b_KEYBYTES_MAX as usize;
pub const OUT_MIN: usize = libsodium::crypto_generichash_blake2b_BYTES_MIN as usize;
pub const OUT_MAX: usize = libsodium::crypto_generichash_blake2b_BYTES_MAX as usize;
#[inline]
pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<()>> + 'a {
with_destination(|out: &mut [u8]| {
assert!(key.is_empty() || (KEY_MIN <= key.len() && key.len() <= KEY_MAX));
assert!(OUT_MIN <= out.len() && out.len() <= OUT_MAX);
let kptr = match key.len() {
// NULL key
0 => null(),
_ => key.as_ptr(),
};
sodium_call!(
crypto_generichash_blake2b,
out.as_mut_ptr(),
out.len(),
data.as_ptr(),
data.len() as c_ulonglong,
kptr,
key.len()
)
})
}

1
sodium/src/hash/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod blake2b;

View File

@@ -16,4 +16,5 @@ pub fn init() -> anyhow::Result<()> {
}
pub mod aead;
pub mod hash;
pub mod helpers;