From a18658847c76ce4c662bce35c32aa76e7aa295f0 Mon Sep 17 00:00:00 2001 From: Katherine Watson Date: Sun, 16 Jun 2024 21:45:48 -0700 Subject: [PATCH] Move static KEM public key to new PublicBox struct --- rosenpass/src/cli.rs | 4 +- rosenpass/src/protocol.rs | 46 +++++------ rp/src/key.rs | 6 +- secret-memory/Cargo.toml | 2 +- secret-memory/src/lib.rs | 1 + secret-memory/src/public.rs | 149 ++++++++++++++++++++++++++++++++++++ 6 files changed, 177 insertions(+), 31 deletions(-) diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 81fad4f..75e1d00 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -6,7 +6,7 @@ use rosenpass_secret_memory::file::StoreSecret; use rosenpass_secret_memory::{ secret_policy_try_use_memfd_secrets, secret_policy_use_only_malloc_secrets, }; -use rosenpass_util::file::{LoadValue, LoadValueB64}; +use rosenpass_util::file::{LoadValue, LoadValueB64, StoreValue}; use rosenpass_wireguard_broker::brokers::native_unix::{ NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError, }; @@ -370,7 +370,7 @@ impl CliCommand { fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> { let mut ssk = crate::protocol::SSk::random(); let mut spk = crate::protocol::SPk::random(); - StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?; + StaticKem::keygen(ssk.secret_mut(), &mut *spk)?; ssk.store_secret(secret_key)?; spk.store(public_key) } diff --git a/rosenpass/src/protocol.rs b/rosenpass/src/protocol.rs index a3c61c7..4877b29 100644 --- a/rosenpass/src/protocol.rs +++ b/rosenpass/src/protocol.rs @@ -32,11 +32,11 @@ //! //! // initialize secret and public key for peer a ... //! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero()); -//! StaticKem::keygen(peer_a_sk.secret_mut(), peer_a_pk.secret_mut())?; +//! StaticKem::keygen(peer_a_sk.secret_mut(), &mut *peer_a_pk)?; //! //! // ... and for peer b //! let (mut peer_b_sk, mut peer_b_pk) = (SSk::zero(), SPk::zero()); -//! StaticKem::keygen(peer_b_sk.secret_mut(), peer_b_pk.secret_mut())?; +//! StaticKem::keygen(peer_b_sk.secret_mut(), &mut *peer_b_pk)?; //! //! // initialize server and a pre-shared key //! let psk = SymKey::random(); @@ -88,7 +88,7 @@ use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_constant_time as constant_time; -use rosenpass_secret_memory::{Public, Secret}; +use rosenpass_secret_memory::{Public, PublicBox, Secret}; use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase}; use zerocopy::{AsBytes, FromBytes, Ref}; @@ -163,7 +163,7 @@ pub fn has_happened(ev: Timing, now: Timing) -> bool { // DATA STRUCTURES & BASIC TRAITS & ACCESSORS //// -pub type SPk = Secret<{ StaticKem::PK_LEN }>; // Just Secret<> instead of Public<> so it gets allocated on the heap +pub type SPk = PublicBox<{ StaticKem::PK_LEN }>; pub type SSk = Secret<{ StaticKem::SK_LEN }>; pub type EPk = Public<{ EphemeralKem::PK_LEN }>; pub type ESk = Secret<{ EphemeralKem::SK_LEN }>; @@ -548,7 +548,7 @@ impl CryptoServer { pub fn pidm(&self) -> Result { Ok(Public::new( hash_domains::peerid()? - .mix(self.spkm.secret())? + .mix(&*self.spkm)? .into_value())) } @@ -708,7 +708,7 @@ impl Peer { pub fn pidt(&self) -> Result { Ok(Public::new( hash_domains::peerid()? - .mix(self.spkt.secret())? + .mix(&*self.spkt)? .into_value())) } } @@ -1016,9 +1016,7 @@ impl CryptoServer { ); let cookie_value = active_cookie_value.unwrap(); - let cookie_key = hash_domains::cookie_key()? - .mix(self.spkm.secret())? - .into_value(); + let cookie_key = hash_domains::cookie_key()?.mix(&*self.spkm)?.into_value(); let mut msg_out = truncating_cast_into::(tx_buf)?; @@ -1509,7 +1507,7 @@ where /// Calculate the message authentication code (`mac`) and also append cookie value pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { let mac = hash_domains::mac()? - .mix(peer.get(srv).spkt.secret())? + .mix(&*peer.get(srv).spkt)? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; self.mac.copy_from_slice(mac.into_value()[..16].as_ref()); self.seal_cookie(peer, srv)?; @@ -1536,7 +1534,7 @@ where /// Check the message authentication code pub fn check_seal(&self, srv: &CryptoServer) -> Result { let expected = hash_domains::mac()? - .mix(srv.spkm.secret())? + .mix(&*srv.spkm)? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; Ok(constant_time::memcmp( &self.mac, @@ -1641,7 +1639,7 @@ impl HandshakeState { // calculate ad contents let ad = hash_domains::biscuit_ad()? - .mix(srv.spkm.secret())? + .mix(&*srv.spkm)? .mix(self.sidi.as_slice())? .mix(self.sidr.as_slice())? .into_value(); @@ -1676,7 +1674,7 @@ impl HandshakeState { // Calculate additional data fields let ad = hash_domains::biscuit_ad()? - .mix(srv.spkm.secret())? + .mix(&*srv.spkm)? .mix(sidi.as_slice())? .mix(sidr.as_slice())? .into_value(); @@ -1763,7 +1761,7 @@ impl CryptoServer { let mut hs = InitiatorHandshake::zero_with_timestamp(self); // IHI1 - hs.core.init(peer.get(self).spkt.secret())?; + hs.core.init(&*peer.get(self).spkt)?; // IHI2 hs.core.sidi.randomize(); @@ -1780,7 +1778,7 @@ impl CryptoServer { hs.core .encaps_and_mix::( ih.sctr.as_mut_slice(), - peer.get(self).spkt.secret(), + &*peer.get(self).spkt, )?; // IHI6 @@ -1788,9 +1786,7 @@ impl CryptoServer { .encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm()?.as_ref())?; // IHI7 - hs.core - .mix(self.spkm.secret())? - .mix(peer.get(self).psk.secret())?; + hs.core.mix(&*self.spkm)?.mix(peer.get(self).psk.secret())?; // IHI8 hs.core.encrypt_and_mix(ih.auth.as_mut_slice(), &[])?; @@ -1807,7 +1803,7 @@ impl CryptoServer { core.sidi = SessionId::from_slice(&ih.sidi); // IHR1 - core.init(self.spkm.secret())?; + core.init(&*self.spkm)?; // IHR4 core.mix(&ih.sidi)?.mix(&ih.epki)?; @@ -1815,7 +1811,7 @@ impl CryptoServer { // IHR5 core.decaps_and_mix::( self.sskm.secret(), - self.spkm.secret(), + &*self.spkm, &ih.sctr, )?; @@ -1828,7 +1824,7 @@ impl CryptoServer { }; // IHR7 - core.mix(peer.get(self).spkt.secret())? + core.mix(&*peer.get(self).spkt)? .mix(peer.get(self).psk.secret())?; // IHR8 @@ -1848,7 +1844,7 @@ impl CryptoServer { // RHR5 core.encaps_and_mix::( &mut rh.scti, - peer.get(self).spkt.secret(), + &*peer.get(self).spkt, )?; // RHR6 @@ -1916,7 +1912,7 @@ impl CryptoServer { // RHI5 core.decaps_and_mix::( self.sskm.secret(), - self.spkm.secret(), + &*self.spkm, &rh.scti, )?; @@ -2113,7 +2109,7 @@ impl CryptoServer { ), }?; - let spkt = peer.get(self).spkt.secret(); + let spkt = &*peer.get(self).spkt; let cookie_key = hash_domains::cookie_key()?.mix(spkt)?.into_value(); let cookie_value = peer.cv().update_mut(self).unwrap(); @@ -2255,7 +2251,7 @@ mod test { fn keygen() -> Result<(SSk, SPk)> { // TODO: Copied from the benchmark; deduplicate let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); - StaticKem::keygen(sk.secret_mut(), pk.secret_mut())?; + StaticKem::keygen(sk.secret_mut(), &mut *pk)?; Ok((sk, pk)) } diff --git a/rp/src/key.rs b/rp/src/key.rs index 8f1274c..552d6ce 100644 --- a/rp/src/key.rs +++ b/rp/src/key.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::{anyhow, Result}; -use rosenpass_util::file::{LoadValueB64, StoreValueB64}; +use rosenpass_util::file::{LoadValueB64, StoreValue, StoreValueB64}; use zeroize::Zeroize; use rosenpass::protocol::{SPk, SSk}; @@ -56,8 +56,8 @@ pub fn genkey(private_keys_dir: &Path) -> Result<()> { if !pqsk_path.exists() && !pqpk_path.exists() { let mut pqsk = SSk::random(); let mut pqpk = SPk::random(); - StaticKem::keygen(pqsk.secret_mut(), pqpk.secret_mut())?; - pqpk.store_secret(pqpk_path)?; + StaticKem::keygen(pqsk.secret_mut(), &mut *pqpk)?; + pqpk.store(pqpk_path)?; pqsk.store_secret(pqsk_path)?; } else { eprintln!( diff --git a/secret-memory/Cargo.toml b/secret-memory/Cargo.toml index 84e8268..f434e59 100644 --- a/secret-memory/Cargo.toml +++ b/secret-memory/Cargo.toml @@ -23,4 +23,4 @@ log = { workspace = true } allocator-api2-tests = { workspace = true } tempfile = {workspace = true} base64ct = {workspace = true} -procspawn = {workspace = true} \ No newline at end of file +procspawn = {workspace = true} diff --git a/secret-memory/src/lib.rs b/secret-memory/src/lib.rs index 7addfc0..2ecbb9d 100644 --- a/secret-memory/src/lib.rs +++ b/secret-memory/src/lib.rs @@ -6,6 +6,7 @@ pub mod alloc; mod public; pub use crate::public::Public; +pub use crate::public::PublicBox; mod secret; pub use crate::secret::Secret; diff --git a/secret-memory/src/public.rs b/secret-memory/src/public.rs index 6bcdacb..8d29eea 100644 --- a/secret-memory/src/public.rs +++ b/secret-memory/src/public.rs @@ -277,3 +277,152 @@ mod tests { } } } + +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct PublicBox { + pub inner: Box>, +} + +impl PublicBox { + /// Create a new [Public] from a byte slice + pub fn from_slice(value: &[u8]) -> Self { + copy_slice(value).to_this(Self::zero) // TODO: fix + } + + /// Create a new [Public] from a byte array + pub fn new(value: [u8; N]) -> Self { + Self { + inner: Box::new(Public::new(value)), + } + } + + /// Create a zero initialized [Public] + pub fn zero() -> Self { + Self { + inner: Box::new(Public::zero()), + } + } + + /// Create a random initialized [Public] + pub fn random() -> Self { + mutating(Self::zero(), |r| r.randomize()) + } + + /// Randomize all bytes in an existing [Public] + pub fn randomize(&mut self) { + (**self).try_fill(&mut crate::rand::rng()).unwrap() + } +} + +impl Randomize for PublicBox { + fn try_fill(&mut self, rng: &mut R) -> Result<(), rand::Error> { + (**self).try_fill(rng) + } +} + +impl fmt::Debug for PublicBox { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + debug_crypto_array(&**self, fmt) + } +} + +impl Deref for PublicBox { + type Target = [u8; N]; + + fn deref(&self) -> &[u8; N] { + &*self.inner + } +} + +impl DerefMut for PublicBox { + fn deref_mut(&mut self) -> &mut [u8; N] { + &mut *self.inner + } +} + +impl Borrow<[u8]> for PublicBox { + fn borrow(&self) -> &[u8] { + &**self + } +} + +impl BorrowMut<[u8]> for PublicBox { + fn borrow_mut(&mut self) -> &mut [u8] { + &mut **self + } +} + +impl LoadValue for PublicBox { + type Error = anyhow::Error; + + fn load>(path: P) -> anyhow::Result { + let mut v = Self::random(); + fopen_r(path)?.read_exact_to_end(&mut *v)?; + Ok(v) + } +} + +impl StoreValue for PublicBox { + type Error = anyhow::Error; + + fn store>(&self, path: P) -> anyhow::Result<()> { + std::fs::write(path, **self)?; + Ok(()) + } +} + +impl LoadValueB64 for PublicBox { + type Error = anyhow::Error; + + fn load_b64>(path: P) -> Result + where + Self: Sized, + { + let mut f = [0u8; F]; + let mut v = PublicBox::zero(); + let p = path.as_ref(); + + let len = fopen_r(p)? + .read_slice_to_end(&mut f) + .with_context(|| format!("Could not load file {p:?}"))?; + + b64_decode(&f[0..len], &mut *v) + .with_context(|| format!("Could not decode base64 file {p:?}"))?; + + Ok(v) + } +} + +impl StoreValueB64 for PublicBox { + type Error = anyhow::Error; + + fn store_b64>(&self, path: P) -> anyhow::Result<()> { + let p = path.as_ref(); + let mut f = [0u8; F]; + let encoded_str = b64_encode(&**self, &mut f) + .with_context(|| format!("Could not encode base64 file {p:?}"))?; + fopen_w(p, Visibility::Public)? + .write_all(encoded_str.as_bytes()) + .with_context(|| format!("Could not write file {p:?}"))?; + Ok(()) + } +} + +impl StoreValueB64Writer for PublicBox { + type Error = anyhow::Error; + + fn store_b64_writer( + &self, + mut writer: W, + ) -> Result<(), Self::Error> { + let mut f = [0u8; F]; + let encoded_str = + b64_encode(&**self, &mut f).with_context(|| "Could not encode secret to base64")?; + + writer + .write_all(encoded_str.as_bytes()) + .with_context(|| "Could not write base64 to writer")?; + Ok(()) + } +}