replace StaticSecret with PrivateKey32

This commit is contained in:
Jazz Turner-Baggs 2026-01-30 11:40:45 -08:00
parent c1abc82f28
commit 5adee47a97
No known key found for this signature in database
9 changed files with 65 additions and 37 deletions

1
Cargo.lock generated
View File

@ -473,6 +473,7 @@ dependencies = [
"crypto",
"hex",
"prost",
"rand",
"rand_core",
"safer-ffi",
"thiserror",

View File

@ -13,6 +13,7 @@ crypto = { path = "../crypto" }
hex = "0.4.3"
prost = "0.14.1"
rand_core = { version = "0.6" }
rand = "0.8.5"
safer-ffi = "0.1.13"
thiserror = "2.0.17"
x25519-dalek = { version = "2.0.1", features = ["static_secrets", "reusable_secrets", "getrandom"] }

View File

@ -2,8 +2,7 @@ pub use blake2::Digest;
use blake2::{Blake2b, digest};
use prost::bytes::Bytes;
pub use crypto::PublicKey32;
pub use x25519_dalek::StaticSecret;
pub use crypto::{PrivateKey32, PublicKey32};
pub trait CopyBytes {
fn copy_to_bytes(&self) -> Bytes;

View File

@ -1,11 +1,10 @@
use blake2::{Blake2b512, Digest};
use std::fmt;
use crate::crypto::{PublicKey32, StaticSecret};
use x25519_dalek::PublicKey;
use crate::crypto::{PrivateKey32, PublicKey32};
pub struct Identity {
secret: StaticSecret,
secret: PrivateKey32,
}
impl fmt::Debug for Identity {
@ -20,7 +19,7 @@ impl fmt::Debug for Identity {
impl Identity {
pub fn new() -> Self {
Self {
secret: StaticSecret::random(),
secret: PrivateKey32::random(),
}
}
@ -29,10 +28,10 @@ impl Identity {
}
pub fn public_key(&self) -> PublicKey32 {
PublicKey::from(&self.secret).into()
PublicKey32::from(&self.secret)
}
pub fn secret(&self) -> &StaticSecret {
pub fn secret(&self) -> &PrivateKey32 {
&self.secret
}
}

View File

@ -5,7 +5,7 @@ use blake2::{
use crypto::{DomainSeparator, PrekeyBundle, SecretKey32, X3Handshake};
use rand_core::{CryptoRng, RngCore};
use crate::crypto::{PublicKey32, StaticSecret};
use crate::crypto::{PrivateKey32, PublicKey32};
type Blake2bMac256 = Blake2bMac<U32>;
@ -21,7 +21,7 @@ pub struct InboxHandshake {}
impl InboxHandshake {
/// Performs
pub fn perform_as_initiator<R: RngCore + CryptoRng>(
identity_keypair: &StaticSecret,
identity_keypair: &PrivateKey32,
recipient_bundle: &PrekeyBundle,
rng: &mut R,
) -> (SecretKey32, PublicKey32) {
@ -42,9 +42,9 @@ impl InboxHandshake {
/// * `initiator_identity` - Initiator's identity public key
/// * `initiator_ephemeral` - Initiator's ephemeral public key
pub fn perform_as_responder(
identity_keypair: &StaticSecret,
signed_prekey: &StaticSecret,
onetime_prekey: Option<&StaticSecret>,
identity_keypair: &PrivateKey32,
signed_prekey: &PrivateKey32,
onetime_prekey: Option<&PrivateKey32>,
initiator_identity: &PublicKey32,
initiator_ephemeral: &PublicKey32,
) -> SecretKey32 {
@ -85,12 +85,12 @@ mod tests {
let mut rng = OsRng;
// Alice (initiator) generates her identity key
let alice_identity = StaticSecret::random_from_rng(&mut rng);
let alice_identity = PrivateKey32::random_from_rng(&mut rng);
let alice_identity_pub = PublicKey32::from(&alice_identity);
// Bob (responder) generates his keys
let bob_identity = StaticSecret::random_from_rng(&mut rng);
let bob_signed_prekey = StaticSecret::random_from_rng(&mut rng);
let bob_identity = PrivateKey32::random_from_rng(&mut rng);
let bob_signed_prekey = PrivateKey32::random_from_rng(&mut rng);
let bob_signed_prekey_pub = PublicKey32::from(&bob_signed_prekey);
// Create Bob's prekey bundle

View File

@ -9,7 +9,7 @@ use crypto::{PrekeyBundle, SecretKey32};
use crate::context::Introduction;
use crate::conversation::{ChatError, ConversationId, Convo, ConvoFactory, Id, PrivateV1Convo};
use crate::crypto::{Blake2b128, CopyBytes, Digest, PublicKey32, StaticSecret};
use crate::crypto::{Blake2b128, CopyBytes, Digest, PrivateKey32, PublicKey32};
use crate::identity::Identity;
use crate::inbox::handshake::InboxHandshake;
use crate::proto;
@ -24,7 +24,7 @@ fn delivery_address_for_installation(_: PublicKey32) -> String {
pub struct Inbox {
ident: Rc<Identity>,
local_convo_id: String,
ephemeral_keys: HashMap<String, StaticSecret>,
ephemeral_keys: HashMap<String, PrivateKey32>,
}
impl<'a> std::fmt::Debug for Inbox {
@ -46,7 +46,7 @@ impl Inbox {
Self {
ident,
local_convo_id,
ephemeral_keys: HashMap::<String, StaticSecret>::new(),
ephemeral_keys: HashMap::<String, PrivateKey32>::new(),
}
}
@ -56,7 +56,7 @@ impl Inbox {
}
pub fn create_bundle(&mut self) -> PrekeyBundle {
let ephemeral = StaticSecret::random();
let ephemeral = PrivateKey32::random_from_rng(&mut OsRng);
let signed_prekey = PublicKey32::from(&ephemeral);
self.ephemeral_keys
@ -193,7 +193,7 @@ impl Inbox {
Ok(frame)
}
fn lookup_ephemeral_key(&self, key: &str) -> Result<&StaticSecret, ChatError> {
fn lookup_ephemeral_key(&self, key: &str) -> Result<&PrivateKey32, ChatError> {
self.ephemeral_keys
.get(key)
.ok_or_else(|| return ChatError::UnknownEphemeralKey())

View File

@ -1,4 +1,5 @@
use generic_array::{GenericArray, typenum::U32};
use rand_core::{CryptoRng, OsRng, RngCore};
use std::{fmt::Debug, ops::Deref};
use x25519_dalek;
use zeroize::{Zeroize, ZeroizeOnDrop};
@ -18,6 +19,12 @@ impl From<&x25519_dalek::StaticSecret> for PublicKey32 {
}
}
impl From<&PrivateKey32> for PublicKey32 {
fn from(value: &PrivateKey32) -> Self {
Self(x25519_dalek::PublicKey::from(&value.0))
}
}
impl From<[u8; 32]> for PublicKey32 {
fn from(value: [u8; 32]) -> Self {
Self(x25519_dalek::PublicKey::from(value))
@ -37,6 +44,27 @@ impl AsRef<[u8]> for PublicKey32 {
}
}
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct PrivateKey32(x25519_dalek::StaticSecret);
impl PrivateKey32 {
pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
Self(x25519_dalek::StaticSecret::random_from_rng(csprng))
}
//TODO: Remove. Force internal callers provide Rng to make deterministic testing possible
pub fn random() -> PrivateKey32 {
Self::random_from_rng(&mut OsRng)
}
}
impl Deref for PrivateKey32 {
type Target = x25519_dalek::StaticSecret;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Zeroize, ZeroizeOnDrop, PartialEq)]
pub struct SecretKey32([u8; 32]);

View File

@ -1,5 +1,5 @@
mod keys;
mod x3dh;
pub use keys::{PublicKey32, SecretKey32};
pub use keys::{PrivateKey32, PublicKey32, SecretKey32};
pub use x3dh::{DomainSeparator, PrekeyBundle, X3Handshake};

View File

@ -3,9 +3,9 @@ use std::marker::PhantomData;
use hkdf::Hkdf;
use rand_core::{CryptoRng, RngCore};
use sha2::Sha256;
use x25519_dalek::{SharedSecret, StaticSecret};
use x25519_dalek::SharedSecret;
use crate::keys::{PublicKey32, SecretKey32};
use crate::keys::{PrivateKey32, PublicKey32, SecretKey32};
/// A prekey bundle containing the public keys needed to initiate an X3DH key exchange.
#[derive(Clone, Debug)]
@ -66,12 +66,12 @@ impl<D: DomainSeparator> X3Handshake<D> {
/// # Returns
/// A tuple of (shared secret bytes, ephemeral public key)
pub fn initator<R: RngCore + CryptoRng>(
identity_keypair: &StaticSecret,
identity_keypair: &PrivateKey32,
recipient_bundle: &PrekeyBundle,
rng: &mut R,
) -> (SecretKey32, PublicKey32) {
// Generate ephemeral key for this handshake (using StaticSecret for multiple DH operations)
let ephemeral_secret = StaticSecret::random_from_rng(rng);
// Generate ephemeral key for this handshake
let ephemeral_secret = PrivateKey32::random_from_rng(rng);
let ephemeral_public = PublicKey32::from(&ephemeral_secret);
// Perform the 4 Diffie-Hellman operations
@ -101,9 +101,9 @@ impl<D: DomainSeparator> X3Handshake<D> {
/// # Returns
/// The derived shared secret bytes
pub fn responder(
identity_keypair: &StaticSecret,
signed_prekey: &StaticSecret,
onetime_prekey: Option<&StaticSecret>,
identity_keypair: &PrivateKey32,
signed_prekey: &PrivateKey32,
onetime_prekey: Option<&PrivateKey32>,
initiator_identity: &PublicKey32,
initiator_ephemeral: &PublicKey32,
) -> SecretKey32 {
@ -134,17 +134,17 @@ mod tests {
let mut rng = OsRng;
// Alice (initiator) generates her identity key
let alice_identity = StaticSecret::random_from_rng(&mut rng);
let alice_identity = PrivateKey32::random_from_rng(&mut rng);
let alice_identity_pub = PublicKey32::from(&alice_identity);
// Bob (responder) generates his keys
let bob_identity = StaticSecret::random_from_rng(&mut rng);
let bob_identity = PrivateKey32::random_from_rng(&mut rng);
let bob_identity_pub = PublicKey32::from(&bob_identity);
let bob_signed_prekey = StaticSecret::random_from_rng(&mut rng);
let bob_signed_prekey = PrivateKey32::random_from_rng(&mut rng);
let bob_signed_prekey_pub = PublicKey32::from(&bob_signed_prekey);
let bob_onetime_prekey = StaticSecret::random_from_rng(&mut rng);
let bob_onetime_prekey = PrivateKey32::random_from_rng(&mut rng);
let bob_onetime_prekey_pub = PublicKey32::from(&bob_onetime_prekey);
// Create Bob's prekey bundle (with one-time prekey)
@ -177,14 +177,14 @@ mod tests {
let mut rng = OsRng;
// Alice (initiator) generates her identity key
let alice_identity = StaticSecret::random_from_rng(&mut rng);
let alice_identity = PrivateKey32::random_from_rng(&mut rng);
let alice_identity_pub = PublicKey32::from(&alice_identity);
// Bob (responder) generates his keys
let bob_identity = StaticSecret::random_from_rng(&mut rng);
let bob_identity = PrivateKey32::random_from_rng(&mut rng);
let bob_identity_pub = PublicKey32::from(&bob_identity);
let bob_signed_prekey = StaticSecret::random_from_rng(&mut rng);
let bob_signed_prekey = PrivateKey32::random_from_rng(&mut rng);
let bob_signed_prekey_pub = PublicKey32::from(&bob_signed_prekey);
// Create Bob's prekey bundle (without one-time prekey)