mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-08 22:00:31 -08:00
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:
committed by
Karolin Varner
parent
d44793e07f
commit
09aa0e027e
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
44
ciphers/src/subtle/incorrect_hmac_blake2b.rs
Normal file
44
ciphers/src/subtle/incorrect_hmac_blake2b.rs
Normal 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(())
|
||||
})
|
||||
}
|
||||
1
ciphers/src/subtle/mod.rs
Normal file
1
ciphers/src/subtle/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod incorrect_hmac_blake2b;
|
||||
@@ -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" }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#[macro_use]
|
||||
pub mod sodium;
|
||||
pub mod coloring;
|
||||
#[rustfmt::skip]
|
||||
pub mod labeled_prf;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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" }
|
||||
|
||||
31
sodium/src/hash/blake2b.rs
Normal file
31
sodium/src/hash/blake2b.rs
Normal 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
1
sodium/src/hash/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod blake2b;
|
||||
@@ -16,4 +16,5 @@ pub fn init() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
pub mod aead;
|
||||
pub mod hash;
|
||||
pub mod helpers;
|
||||
|
||||
Reference in New Issue
Block a user