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

View File

@@ -10,4 +10,9 @@ repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md" readme = "readme.md"
[dependencies] [dependencies]
anyhow = "1.0.75"
rosenpass-sodium = { path = "../sodium" } 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 mod aead {
pub use rosenpass_sodium::aead::chacha20poly1305_ietf::{ pub use rosenpass_sodium::aead::chacha20poly1305_ietf::{
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
}; };
} }
/// Authenticated encryption with associated data with a constant nonce
pub mod xaead { pub mod xaead {
pub use rosenpass_sodium::aead::xchacha20poly1305_ietf::{ pub use rosenpass_sodium::aead::xchacha20poly1305_ietf::{
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, 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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [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 /// # Examples
/// ///
/// ``` /// ```
/// use rosenpass_constant_time::xor_into; /// use rosenpass_constant_time::xor;
/// let mut a = String::from("hello").into_bytes(); /// use rosenpass_to::To;
/// let b = b"world"; /// assert_eq!(
/// xor_into(&mut a, b); /// xor(b"world").to_this(|| b"hello".to_vec()),
/// assert_eq!(&a, b"\x1f\n\x1e\x00\x0b"); /// b"\x1f\n\x1e\x00\x0b");
/// ``` /// ```
///
/// # Panics
///
/// If source and destination are of different sizes.
#[inline] #[inline]
pub fn xor_into(a: &mut [u8], b: &[u8]) { pub fn xor<'a>(src: &'a [u8]) -> impl To<[u8], ()> + 'a {
assert!(a.len() == b.len()); with_destination(|dst: &mut [u8]| {
for (av, bv) in a.iter_mut().zip(b.iter()) { assert!(src.len() == dst.len());
*av ^= *bv; for (dv, sv) in dst.iter_mut().zip(src.iter()) {
} *dv ^= *sv;
}
})
} }

View File

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

View File

@@ -4,8 +4,8 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target; use libfuzzer_sys::fuzz_target;
use rosenpass::sodium::mac_into; use rosenpass_sodium::{hash::blake2b, init as sodium_init};
use rosenpass_sodium::init as sodium_init; use rosenpass_to::To;
#[derive(arbitrary::Arbitrary, Debug)] #[derive(arbitrary::Arbitrary, Debug)]
pub struct Blake2b { pub struct Blake2b {
@@ -18,5 +18,5 @@ fuzz_target!(|input: Blake2b| {
let mut out = [0u8; 32]; 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-constant-time = { path = "../constant-time" }
rosenpass-sodium = { path = "../sodium" } rosenpass-sodium = { path = "../sodium" }
rosenpass-ciphers = { path = "../ciphers" } rosenpass-ciphers = { path = "../ciphers" }
rosenpass-to = { path = "../to" }
anyhow = { version = "1.0.71", features = ["backtrace"] } anyhow = { version = "1.0.71", features = ["backtrace"] }
static_assertions = "1.1.0" static_assertions = "1.1.0"
memoffset = "0.9.0" memoffset = "0.9.0"

View File

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

View File

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

View File

@@ -17,23 +17,16 @@
//! //!
//! TODO Base the construction on a proper Dec function //! TODO Base the construction on a proper Dec function
pub struct Iprf([u8; KEY_SIZE]); use rosenpass_ciphers::{KEY_LEN, hash};
pub struct IprfBranch([u8; KEY_SIZE]);
pub struct SecretIprf(Secret<KEY_SIZE>);
pub struct SecretIprfBranch(Secret<KEY_SIZE>);
pub fn prf_into(out: &mut [u8], key: &[u8], data: &[u8]) { pub struct Iprf([u8; KEY_LEN]);
// TODO: The error handling with sodium is a scurge pub struct IprfBranch([u8; KEY_LEN]);
hmac_into(out, key, data).unwrap() pub struct SecretIprf(Secret<KEY_LEN>);
} pub struct SecretIprfBranch(Secret<KEY_LEN>);
pub fn prf(key: &[u8], data: &[u8]) -> [u8; KEY_SIZE] {
mutating([0u8; KEY_SIZE], |r| prf_into(r, key, data))
}
impl Iprf { impl Iprf {
fn zero() -> Self { fn zero() -> Self {
Self([0u8; KEY_SIZE]) Self([0u8; KEY_LEN])
} }
fn dup(self) -> IprfBranch { fn dup(self) -> IprfBranch {
@@ -42,25 +35,25 @@ impl Iprf {
// TODO: Protocol! Use domain separation to ensure that // TODO: Protocol! Use domain separation to ensure that
fn mix(self, v: &[u8]) -> Self { 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 { fn mix_secret<const N: usize>(self, v: Secret<N>) -> SecretIprf {
SecretIprf::prf_invoc(&self.0, v.secret()) SecretIprf::prf_invoc(&self.0, v.secret())
} }
fn into_value(self) -> [u8; KEY_SIZE] { fn into_value(self) -> [u8; KEY_LEN] {
self.0 self.0
} }
fn extract(self, v: &[u8], dst: &mut [u8]) { fn extract(self, v: &[u8], dst: &mut [u8]) {
prf_into(&self.0, v, dst) hash(&self.0, v).to(dst)
} }
} }
impl IprfBranch { impl IprfBranch {
fn mix(&self, v: &[u8]) -> Iprf { 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 { fn mix_secret<const N: usize>(&self, v: Secret<N>) -> SecretIprf {
@@ -71,7 +64,7 @@ impl IprfBranch {
impl SecretIprf { impl SecretIprf {
fn prf_invoc(k: &[u8], d: &[u8]) -> SecretIprf { fn prf_invoc(k: &[u8], d: &[u8]) -> SecretIprf {
mutating(SecretIprf(Secret::zero()), |r| { 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()) Self::prf_invoc(self.0.secret(), v.secret())
} }
fn into_secret(self) -> Secret<KEY_SIZE> { fn into_secret(self) -> Secret<KEY_LEN> {
self.0 self.0
} }
fn into_secret_slice(self, v: &[u8], dst: &[u8]) { 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 super::RosenpassError;
use crate::{pqkem::*, sodium}; use crate::pqkem::*;
use rosenpass_ciphers::{aead, xaead}; use rosenpass_ciphers::{aead, xaead, KEY_LEN};
// Macro magic //////////////////////////////////////////////////////////////// // Macro magic ////////////////////////////////////////////////////////////////
@@ -265,9 +265,9 @@ data_lense! { Envelope<M> :=
payload: M::LEN, payload: M::LEN,
/// Message Authentication Code (mac) over all bytes until (exclusive) /// Message Authentication Code (mac) over all bytes until (exclusive)
/// `mac` itself /// `mac` itself
mac: sodium::MAC_SIZE, mac: 16,
/// Currently unused, TODO: do something with this /// Currently unused, TODO: do something with this
cookie: sodium::MAC_SIZE cookie: 16
} }
data_lense! { InitHello := data_lense! { InitHello :=
@@ -320,11 +320,11 @@ data_lense! { EmptyData :=
data_lense! { Biscuit := data_lense! { Biscuit :=
/// H(spki) Ident ifies the initiator /// H(spki) Ident ifies the initiator
pidi: sodium::KEY_SIZE, pidi: KEY_LEN,
/// The biscuit number (replay protection) /// The biscuit number (replay protection)
biscuit_no: 12, biscuit_no: 12,
/// Chaining key /// Chaining key
ck: sodium::KEY_SIZE ck: KEY_LEN
} }
data_lense! { DataMsg := data_lense! { DataMsg :=
@@ -389,20 +389,17 @@ pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG
#[cfg(test)] #[cfg(test)]
mod test_constants { mod test_constants {
use crate::{ use crate::msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN};
msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN}, use rosenpass_ciphers::{xaead, KEY_LEN};
sodium,
};
use rosenpass_ciphers::xaead;
#[test] #[test]
fn sodium_keysize() { fn sodium_keysize() {
assert_eq!(sodium::KEY_SIZE, 32); assert_eq!(KEY_LEN, 32);
} }
#[test] #[test]
fn biscuit_pt_len() { fn biscuit_pt_len() {
assert_eq!(BISCUIT_PT_LEN, 2 * sodium::KEY_SIZE + 12); assert_eq!(BISCUIT_PT_LEN, 2 * KEY_LEN + 12);
} }
#[test] #[test]

View File

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

View File

@@ -73,10 +73,9 @@ use crate::{
msgs::*, msgs::*,
pqkem::*, pqkem::*,
prftree::{SecretPrfTree, SecretPrfTreeBranch}, prftree::{SecretPrfTree, SecretPrfTreeBranch},
sodium::*,
}; };
use anyhow::{bail, ensure, Context, Result}; 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 rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase};
use std::collections::hash_map::{ use std::collections::hash_map::{
Entry::{Occupied, Vacant}, Entry::{Occupied, Vacant},
@@ -145,10 +144,10 @@ pub type SSk = Secret<{ StaticKEM::SK_LEN }>;
pub type EPk = Public<{ EphemeralKEM::PK_LEN }>; pub type EPk = Public<{ EphemeralKEM::PK_LEN }>;
pub type ESk = Secret<{ EphemeralKEM::SK_LEN }>; pub type ESk = Secret<{ EphemeralKEM::SK_LEN }>;
pub type SymKey = Secret<KEY_SIZE>; pub type SymKey = Secret<KEY_LEN>;
pub type SymHash = Public<KEY_SIZE>; 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 SessionId = Public<SESSION_ID_LEN>;
pub type BiscuitId = Public<BISCUIT_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> { 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(); 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) self.mix(ct)
} }
pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> { 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(); 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) self.mix(ct)
} }
@@ -1448,7 +1447,7 @@ impl CryptoServer {
.mix(peer.get(self).psk.secret())?; .mix(peer.get(self).psk.secret())?;
// IHI8 // 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 // Update the handshake hash last (not changing any state on prior error
peer.hs().insert(self, hs)?; peer.hs().insert(self, hs)?;
@@ -1514,7 +1513,7 @@ impl CryptoServer {
core.store_biscuit(self, peer, rh.biscuit_mut())?; core.store_biscuit(self, peer, rh.biscuit_mut())?;
// RHR7 // RHR7
core.encrypt_and_mix(rh.auth_mut(), &NOTHING)?; core.encrypt_and_mix(rh.auth_mut(), &[])?;
Ok(peer) Ok(peer)
} }
@@ -1600,7 +1599,7 @@ impl CryptoServer {
ic.biscuit_mut().copy_from_slice(rh.biscuit()); ic.biscuit_mut().copy_from_slice(rh.biscuit());
// ICI4 // 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 // Split() We move the secrets into the session; we do not
// delete the InitiatorHandshake, just clear it's secrets because // delete the InitiatorHandshake, just clear it's secrets because
@@ -1630,7 +1629,7 @@ impl CryptoServer {
)?; )?;
// ICR2 // ICR2
core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &NOTHING)?; core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &[])?;
// ICR3 // ICR3
core.mix(ic.sidi())?.mix(ic.sidr())?; core.mix(ic.sidi())?.mix(ic.sidr())?;
@@ -1686,7 +1685,7 @@ impl CryptoServer {
let n = cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]); let n = cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]);
let k = ses.txkm.secret(); 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) Ok(peer)
} }
@@ -1723,7 +1722,7 @@ impl CryptoServer {
&mut [0u8; 0], &mut [0u8; 0],
s.txkt.secret(), s.txkt.secret(),
&cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]), &cat!(aead::NONCE_LEN; rc.ctr(), &[0u8; 4]),
&NOTHING, &[],
rc.auth(), 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] [dependencies]
rosenpass-util = { path = "../util" } rosenpass-util = { path = "../util" }
rosenpass-to = { path = "../to" }
anyhow = { version = "1.0.71", features = ["backtrace"] } anyhow = { version = "1.0.71", features = ["backtrace"] }
libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] } libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] }
log = { version = "0.4.17" } 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 aead;
pub mod hash;
pub mod helpers; pub mod helpers;