From 143cb380528642448e27cfbb8cd64fc26f90bbf6 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:28:29 -0800 Subject: [PATCH] Migrate to Crypto::PublicKey32 and PrivateKey32 --- Cargo.lock | 1 + conversations/src/crypto.rs | 1 + crypto/src/keys.rs | 31 +++++++++++++----------- double-ratchets/Cargo.toml | 2 +- double-ratchets/src/ffi/doubleratchet.rs | 4 +-- double-ratchets/src/keypair.rs | 18 +++++++------- double-ratchets/src/state.rs | 18 +++++++------- double-ratchets/src/storage/session.rs | 4 +-- double-ratchets/src/storage/types.rs | 13 +++++++--- 9 files changed, 51 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14ee89a..69ff283 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,7 @@ version = "0.0.1" dependencies = [ "blake2", "chacha20poly1305", + "crypto", "hkdf", "rand", "rand_core", diff --git a/conversations/src/crypto.rs b/conversations/src/crypto.rs index 7a85d72..5807c31 100644 --- a/conversations/src/crypto.rs +++ b/conversations/src/crypto.rs @@ -4,6 +4,7 @@ use prost::bytes::Bytes; pub use crypto::{PrivateKey32, PublicKey32}; +// TODO: (P4) Make handing of Keys in Prost easier pub trait CopyBytes { fn copy_to_bytes(&self) -> Bytes; } diff --git a/crypto/src/keys.rs b/crypto/src/keys.rs index 748fa2a..dec77d3 100644 --- a/crypto/src/keys.rs +++ b/crypto/src/keys.rs @@ -1,24 +1,15 @@ use generic_array::{GenericArray, typenum::U32}; use rand_core::{CryptoRng, OsRng, RngCore}; -use std::{fmt::Debug, ops::Deref}; +use std::{ + fmt::Debug, + ops::{Deref, DerefMut}, +}; use x25519_dalek; use zeroize::{Zeroize, ZeroizeOnDrop}; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct PublicKey32(x25519_dalek::PublicKey); -impl From for PublicKey32 { - fn from(value: x25519_dalek::PublicKey) -> Self { - Self(value) - } -} - -impl From<&x25519_dalek::StaticSecret> for PublicKey32 { - fn from(value: &x25519_dalek::StaticSecret) -> Self { - Self(x25519_dalek::PublicKey::from(value)) - } -} - impl From<&PrivateKey32> for PublicKey32 { fn from(value: &PrivateKey32) -> Self { Self(x25519_dalek::PublicKey::from(&value.0)) @@ -38,6 +29,12 @@ impl Deref for PublicKey32 { } } +impl DerefMut for PublicKey32 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + impl AsRef<[u8]> for PublicKey32 { fn as_ref(&self) -> &[u8] { self.0.as_ref() @@ -58,6 +55,12 @@ impl PrivateKey32 { } } +impl From<[u8; 32]> for PrivateKey32 { + fn from(value: [u8; 32]) -> Self { + Self(x25519_dalek::StaticSecret::from(value)) + } +} + impl Deref for PrivateKey32 { type Target = x25519_dalek::StaticSecret; fn deref(&self) -> &Self::Target { diff --git a/double-ratchets/Cargo.toml b/double-ratchets/Cargo.toml index de78550..1e5b0be 100644 --- a/double-ratchets/Cargo.toml +++ b/double-ratchets/Cargo.toml @@ -11,8 +11,8 @@ name = "generate-headers" required-features = ["headers"] [dependencies] -x25519-dalek = { version="2.0.1", features=["static_secrets"] } chacha20poly1305 = "0.10.1" +crypto = { path = "../crypto" } rand_core = "0.6.4" rand = "0.8.5" hkdf = "0.12.4" diff --git a/double-ratchets/src/ffi/doubleratchet.rs b/double-ratchets/src/ffi/doubleratchet.rs index e09cebd..0a7957b 100644 --- a/double-ratchets/src/ffi/doubleratchet.rs +++ b/double-ratchets/src/ffi/doubleratchet.rs @@ -1,5 +1,5 @@ +use crypto::PublicKey32; use safer_ffi::prelude::*; -use x25519_dalek::PublicKey; use crate::{ Header, RatchetState, @@ -22,7 +22,7 @@ fn double_ratchet_init_sender( shared_secret: [u8; 32], remote_pub: [u8; 32], ) -> repr_c::Box { - let state = RatchetState::init_sender(shared_secret, PublicKey::from(remote_pub)); + let state = RatchetState::init_sender(shared_secret, PublicKey32::from(remote_pub)); Box::new(FFIRatchetState(state)).into() } diff --git a/double-ratchets/src/keypair.rs b/double-ratchets/src/keypair.rs index 7943646..4f3a6ac 100644 --- a/double-ratchets/src/keypair.rs +++ b/double-ratchets/src/keypair.rs @@ -1,27 +1,27 @@ +use crypto::{PrivateKey32, PublicKey32}; use rand_core::OsRng; -use x25519_dalek::{PublicKey, StaticSecret}; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::types::SharedSecret; #[derive(Clone, Zeroize, ZeroizeOnDrop)] pub struct InstallationKeyPair { - secret: StaticSecret, - public: PublicKey, + secret: PrivateKey32, + public: PublicKey32, } impl InstallationKeyPair { pub fn generate() -> Self { - let secret = StaticSecret::random_from_rng(OsRng); - let public = PublicKey::from(&secret); + let secret = PrivateKey32::random_from_rng(OsRng); + let public = PublicKey32::from(&secret); Self { secret, public } } - pub fn dh(&self, their_public: &PublicKey) -> SharedSecret { + pub fn dh(&self, their_public: &PublicKey32) -> SharedSecret { self.secret.diffie_hellman(their_public).to_bytes() } - pub fn public(&self) -> &PublicKey { + pub fn public(&self) -> &PublicKey32 { &self.public } @@ -32,8 +32,8 @@ impl InstallationKeyPair { /// Import the secret key from raw bytes. pub fn from_secret_bytes(bytes: [u8; 32]) -> Self { - let secret = StaticSecret::from(bytes); - let public = PublicKey::from(&secret); + let secret = PrivateKey32::from(bytes); + let public = PublicKey32::from(&secret); Self { secret, public } } } diff --git a/double-ratchets/src/state.rs b/double-ratchets/src/state.rs index 48ad359..ea1b2a3 100644 --- a/double-ratchets/src/state.rs +++ b/double-ratchets/src/state.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, marker::PhantomData}; +use crypto::PublicKey32; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError}; -use x25519_dalek::PublicKey; use zeroize::{Zeroize, Zeroizing}; use crate::{ @@ -29,13 +29,13 @@ pub struct RatchetState { pub receiving_chain: Option, pub dh_self: InstallationKeyPair, - pub dh_remote: Option, + pub dh_remote: Option, pub msg_send: u32, pub msg_recv: u32, pub prev_chain_len: u32, - pub skipped_keys: HashMap<(PublicKey, u32), MessageKey>, + pub skipped_keys: HashMap<(PublicKey32, u32), MessageKey>, pub(crate) _domain: PhantomData, } @@ -144,7 +144,7 @@ impl RatchetState { let dh_self = InstallationKeyPair::from_secret_bytes(dh_self_bytes); dh_self_bytes.zeroize(); - let dh_remote = reader.read_option()?.map(PublicKey::from); + let dh_remote = reader.read_option()?.map(PublicKey32::from); let msg_send = reader.read_u32()?; let msg_recv = reader.read_u32()?; @@ -153,7 +153,7 @@ impl RatchetState { let skipped_count = reader.read_u32()? as usize; let mut skipped_keys = HashMap::with_capacity(skipped_count); for _ in 0..skipped_count { - let pk = PublicKey::from(reader.read_array::<32>()?); + let pk = PublicKey32::from(reader.read_array::<32>()?); let msg_num = reader.read_u32()?; let mk: MessageKey = reader.read_array()?; skipped_keys.insert((pk, msg_num), mk); @@ -198,7 +198,7 @@ impl<'de, D: HkdfInfo> Deserialize<'de> for RatchetState { /// Public header attached to every encrypted message (unencrypted but authenticated). #[derive(Clone, Debug)] pub struct Header { - pub dh_pub: PublicKey, + pub dh_pub: PublicKey32, pub msg_num: u32, pub prev_chain_len: u32, } @@ -233,7 +233,7 @@ impl RatchetState { /// # Returns /// /// A new `RatchetState` ready to send the first message. - pub fn init_sender(shared_secret: SharedSecret, remote_pub: PublicKey) -> Self { + pub fn init_sender(shared_secret: SharedSecret, remote_pub: PublicKey32) -> Self { let dh_self = InstallationKeyPair::generate(); // Initial DH @@ -296,7 +296,7 @@ impl RatchetState { /// # Arguments /// /// * `remote_pub` - The new DH public key from the sender. - pub fn dh_ratchet_receive(&mut self, remote_pub: PublicKey) { + pub fn dh_ratchet_receive(&mut self, remote_pub: PublicKey32) { let dh_out = self.dh_self.dh(&remote_pub); let (new_root, recv_chain) = kdf_root::(&self.root_key, &dh_out); @@ -605,7 +605,7 @@ mod tests { // Tamper with header (change DH pub byte) let mut tampered_pub_bytes = header.dh_pub.to_bytes(); tampered_pub_bytes[0] ^= 0xff; - header.dh_pub = PublicKey::from(tampered_pub_bytes); + header.dh_pub = PublicKey32::from(tampered_pub_bytes); let result = bob.decrypt_message(&ct, header); assert!(result.is_err()); diff --git a/double-ratchets/src/storage/session.rs b/double-ratchets/src/storage/session.rs index 399af8d..387a41c 100644 --- a/double-ratchets/src/storage/session.rs +++ b/double-ratchets/src/storage/session.rs @@ -1,4 +1,4 @@ -use x25519_dalek::PublicKey; +use crypto::PublicKey32; use crate::{ InstallationKeyPair, @@ -82,7 +82,7 @@ impl<'a, D: HkdfInfo + Clone> RatchetSession<'a, D> { storage: &'a mut SqliteStorage, conversation_id: impl Into, shared_secret: SharedSecret, - remote_pub: PublicKey, + remote_pub: PublicKey32, ) -> Result { let state = RatchetState::::init_sender(shared_secret, remote_pub); Self::create(storage, conversation_id, state) diff --git a/double-ratchets/src/storage/types.rs b/double-ratchets/src/storage/types.rs index 6a1cd80..8d3f9de 100644 --- a/double-ratchets/src/storage/types.rs +++ b/double-ratchets/src/storage/types.rs @@ -3,8 +3,8 @@ use crate::{ state::{RatchetState, SkippedKey}, types::MessageKey, }; +use crypto::PublicKey32; use thiserror::Error; -use x25519_dalek::PublicKey; #[derive(Debug, Error)] pub enum StorageError { @@ -58,11 +58,16 @@ impl RatchetStateRecord { use std::marker::PhantomData; let dh_self = InstallationKeyPair::from_secret_bytes(self.dh_self_secret); - let dh_remote = self.dh_remote.map(PublicKey::from); + let dh_remote = self.dh_remote.map(PublicKey32::from); - let skipped: HashMap<(PublicKey, u32), MessageKey> = skipped_keys + let skipped: HashMap<(PublicKey32, u32), MessageKey> = skipped_keys .into_iter() - .map(|sk| ((PublicKey::from(sk.public_key), sk.msg_num), sk.message_key)) + .map(|sk| { + ( + (PublicKey32::from(sk.public_key), sk.msg_num), + sk.message_key, + ) + }) .collect(); RatchetState {