use std::collections::HashMap;
use std::marker::PhantomData;
use p2panda_core::cbor::{DecodeError, EncodeError, decode_cbor, encode_cbor};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::crypto::hpke::{HpkeCiphertext, HpkeError, hpke_open, hpke_seal};
use crate::crypto::x25519::{PublicKey, SecretKey, X25519Error};
use crate::crypto::{Rng, RngError};
use crate::key_bundle::{LongTermKeyBundle, OneTimeKeyBundle, PreKeyId};
use crate::key_manager::KeyManager;
use crate::traits::{IdentityManager, KeyBundle, PreKeyManager};
use crate::two_party::{X3dhCiphertext, X3dhError, x3dh_decrypt, x3dh_encrypt};
pub struct TwoParty<KMG, KB> {
_marker: PhantomData<(KMG, KB)>,
}
pub type OneTimeTwoParty = TwoParty<KeyManager, OneTimeKeyBundle>;
pub type LongTermTwoParty = TwoParty<KeyManager, LongTermKeyBundle>;
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
pub struct TwoPartyState<KB: KeyBundle> {
our_next_key_index: u64,
our_min_key_index: u64,
our_secret_keys: HashMap<u64, SecretKey>,
our_received_secret_key: Option<SecretKey>,
their_next_key_used: KeyUsed,
their_identity_key: PublicKey,
their_prekey_bundle: Option<KB>,
their_public_key: Option<PublicKey>,
}
impl<KMG, KB> TwoParty<KMG, KB>
where
KMG: IdentityManager<KMG::State> + PreKeyManager,
KB: KeyBundle,
{
pub fn init(their_prekey_bundle: KB) -> TwoPartyState<KB> {
TwoPartyState {
our_next_key_index: 1,
our_min_key_index: 1,
our_secret_keys: HashMap::new(),
our_received_secret_key: None,
their_identity_key: *their_prekey_bundle.identity_key(),
their_public_key: None,
their_next_key_used: KeyUsed::PreKey,
their_prekey_bundle: Some(their_prekey_bundle),
}
}
pub fn send(
y: TwoPartyState<KB>,
y_manager: &KMG::State,
plaintext: &[u8],
rng: &Rng,
) -> TwoPartyResult<(TwoPartyState<KB>, TwoPartyMessage)> {
let (for_us, for_them) = Self::generate_keys(rng)?;
let plaintext_message = TwoPartyPlaintext {
plaintext: plaintext.to_vec(),
receiver_new_secret: for_them.their_new_secret.clone(),
sender_new_public_key: for_them.our_new_public_key,
sender_next_index: y.our_next_key_index,
};
let plaintext_bytes = plaintext_message.to_bytes()?;
let (mut y_i, ciphertext) = Self::encrypt(y, y_manager, &plaintext_bytes, rng)?;
let message = TwoPartyMessage {
ciphertext,
key_used: y_i.their_next_key_used,
};
y_i.our_secret_keys
.insert(y_i.our_next_key_index, for_us.our_new_secret);
y_i.our_next_key_index += 1;
y_i.their_public_key = Some(for_us.their_new_public_key);
y_i.their_next_key_used = KeyUsed::ReceivedKey;
Ok((y_i, message))
}
pub fn receive(
y: TwoPartyState<KB>,
y_manager: KMG::State,
message: TwoPartyMessage,
) -> TwoPartyResult<(TwoPartyState<KB>, KMG::State, Vec<u8>)> {
let (mut y_i, y_manager_i, plaintext_bytes) =
Self::decrypt(y, y_manager, message.ciphertext, message.key_used)?;
let plaintext_message = TwoPartyPlaintext::from_bytes(&plaintext_bytes)?;
y_i.their_public_key = Some(plaintext_message.sender_new_public_key);
y_i.their_next_key_used = KeyUsed::OwnKey(plaintext_message.sender_next_index);
y_i.our_received_secret_key = Some(plaintext_message.receiver_new_secret);
Ok((y_i, y_manager_i, plaintext_message.plaintext))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[allow(clippy::enum_variant_names)]
pub enum KeyUsed {
PreKey,
ReceivedKey,
OwnKey(u64),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct TwoPartyMessage {
ciphertext: TwoPartyCiphertext,
key_used: KeyUsed,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum TwoPartyCiphertext {
PreKey(X3dhCiphertext),
Hpke(HpkeCiphertext),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct TwoPartyPlaintext {
plaintext: Vec<u8>,
receiver_new_secret: SecretKey,
sender_new_public_key: PublicKey,
sender_next_index: u64,
}
impl TwoPartyPlaintext {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
decode_cbor(bytes)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, EncodeError> {
encode_cbor(&self)
}
}
impl<KMG, KB> TwoParty<KMG, KB>
where
KMG: IdentityManager<KMG::State> + PreKeyManager,
KB: KeyBundle,
{
fn encrypt(
mut y: TwoPartyState<KB>,
y_manager: &KMG::State,
plaintext: &[u8],
rng: &Rng,
) -> TwoPartyResult<(TwoPartyState<KB>, TwoPartyCiphertext)> {
let ciphertext = match &y.their_public_key {
None => {
let their_prekey_bundle = y
.their_prekey_bundle
.take()
.ok_or(TwoPartyError::PreKeyReuse)?;
let ciphertext = x3dh_encrypt(
plaintext,
KMG::identity_secret(y_manager),
&their_prekey_bundle,
rng,
)?;
TwoPartyCiphertext::PreKey(ciphertext)
}
Some(their_public_key) => {
let ciphertext = hpke_seal(their_public_key, None, None, plaintext)?;
TwoPartyCiphertext::Hpke(ciphertext)
}
};
Ok((y, ciphertext))
}
fn decrypt(
mut y: TwoPartyState<KB>,
y_manager: KMG::State,
ciphertext: TwoPartyCiphertext,
key_used: KeyUsed,
) -> TwoPartyResult<(TwoPartyState<KB>, KMG::State, Vec<u8>)> {
let (y_manager_i, plaintext) = match key_used {
KeyUsed::PreKey => {
let TwoPartyCiphertext::PreKey(ciphertext) = ciphertext else {
return Err(TwoPartyError::InvalidCiphertextType);
};
let (y_manager_i, onetime_secret) = match ciphertext.onetime_prekey_id {
Some(onetime_prekey_id) => {
let (y_manager_i, onetime_secret) =
KMG::use_onetime_secret(y_manager, onetime_prekey_id)
.map_err(|_| TwoPartyError::PreKeyReuse)?;
(y_manager_i, onetime_secret)
}
None => (y_manager, None),
};
let plaintext = x3dh_decrypt(
&ciphertext,
KMG::identity_secret(&y_manager_i),
KMG::prekey_secret(&y_manager_i, &ciphertext.prekey_id)
.map_err(|_| TwoPartyError::UnknownPreKeyUsed(ciphertext.prekey_id))?,
onetime_secret.as_ref(),
)?;
(y_manager_i, plaintext)
}
KeyUsed::ReceivedKey => {
let TwoPartyCiphertext::Hpke(ciphertext) = ciphertext else {
return Err(TwoPartyError::InvalidCiphertextType);
};
let Some(our_received_secret_key) = &y.our_received_secret_key else {
return Err(TwoPartyError::UnknownSecretUsed(0));
};
let plaintext = hpke_open(&ciphertext, our_received_secret_key, None, None)?;
(y_manager, plaintext)
}
KeyUsed::OwnKey(index) => {
let TwoPartyCiphertext::Hpke(ciphertext) = ciphertext else {
return Err(TwoPartyError::InvalidCiphertextType);
};
let plaintext = match y.our_secret_keys.get(&index) {
Some(secret) => hpke_open(&ciphertext, secret, None, None)?,
None => return Err(TwoPartyError::UnknownSecretUsed(index)),
};
for i in y.our_min_key_index..index + 1 {
y.our_secret_keys.remove(&i);
}
y.our_min_key_index = index + 1;
(y_manager, plaintext)
}
};
Ok((y, y_manager_i, plaintext))
}
}
impl<KMG, KB> TwoParty<KMG, KB> {
fn generate_keys(rng: &Rng) -> TwoPartyResult<(NewKeysForUs, NewKeysForThem)> {
let our_new_secret = SecretKey::from_bytes(rng.random_array()?);
let our_new_public_key = our_new_secret.public_key()?;
let their_new_secret = SecretKey::from_bytes(rng.random_array()?);
let their_new_public_key = their_new_secret.public_key()?;
Ok((
NewKeysForUs {
our_new_secret,
their_new_public_key,
},
NewKeysForThem {
our_new_public_key,
their_new_secret,
},
))
}
}
struct NewKeysForUs {
our_new_secret: SecretKey,
their_new_public_key: PublicKey,
}
struct NewKeysForThem {
our_new_public_key: PublicKey,
their_new_secret: SecretKey,
}
pub type TwoPartyResult<T> = Result<T, TwoPartyError>;
#[derive(Debug, Error)]
pub enum TwoPartyError {
#[error(transparent)]
Hpke(#[from] HpkeError),
#[error(transparent)]
X3dh(#[from] X3dhError),
#[error(transparent)]
Rng(#[from] RngError),
#[error(transparent)]
Encode(#[from] EncodeError),
#[error(transparent)]
Decode(#[from] DecodeError),
#[error(transparent)]
X25519(#[from] X25519Error),
#[error("prekeys have already been used")]
PreKeyReuse,
#[error("tried to decrypt with unknown 2SM secret at index {0}")]
UnknownSecretUsed(u64),
#[error("tried to decrypt with unknown initial X3DH pre-key {0}")]
UnknownPreKeyUsed(PreKeyId),
#[error("invalid ciphertext for message type")]
InvalidCiphertextType,
}
#[cfg(test)]
mod tests {
use crate::crypto::Rng;
use crate::crypto::x25519::SecretKey;
use crate::key_bundle::Lifetime;
use crate::key_manager::KeyManager;
use crate::traits::PreKeyManager;
use super::{KeyUsed, LongTermTwoParty, OneTimeTwoParty, TwoPartyError};
#[test]
fn two_party_secret_messaging_protocol() {
let rng = Rng::from_seed([1; 32]);
let alice_identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let alice_manager =
KeyManager::init_and_generate_prekey(&alice_identity_secret, Lifetime::default(), &rng)
.unwrap();
let (alice_manager, alice_prekey_bundle) =
KeyManager::generate_onetime_bundle(alice_manager, &rng).unwrap();
let bob_identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let bob_manager =
KeyManager::init_and_generate_prekey(&bob_identity_secret, Lifetime::default(), &rng)
.unwrap();
let (bob_manager, bob_prekey_bundle) =
KeyManager::generate_onetime_bundle(bob_manager, &rng).unwrap();
let alice_2sm = OneTimeTwoParty::init(bob_prekey_bundle);
assert_eq!(alice_2sm.our_secret_keys.len(), 0);
assert_eq!(alice_2sm.our_min_key_index, 1);
assert_eq!(alice_2sm.our_next_key_index, 1);
assert_eq!(alice_2sm.their_next_key_used, KeyUsed::PreKey);
let bob_2sm = OneTimeTwoParty::init(alice_prekey_bundle);
assert_eq!(bob_2sm.our_secret_keys.len(), 0);
assert_eq!(bob_2sm.our_min_key_index, 1);
assert_eq!(bob_2sm.our_next_key_index, 1);
assert_eq!(bob_2sm.their_next_key_used, KeyUsed::PreKey);
let (alice_2sm, message_1) =
OneTimeTwoParty::send(alice_2sm, &alice_manager, b"Hello, Bob!", &rng).unwrap();
assert_eq!(alice_2sm.our_secret_keys.len(), 1);
assert_eq!(alice_2sm.our_min_key_index, 1);
assert_eq!(alice_2sm.our_next_key_index, 2);
assert!(alice_2sm.their_public_key.is_some());
assert!(alice_2sm.our_received_secret_key.is_none());
assert_eq!(alice_2sm.their_next_key_used, KeyUsed::ReceivedKey);
assert!(alice_2sm.their_prekey_bundle.is_none());
let (bob_2sm, bob_manager, receive_1) =
OneTimeTwoParty::receive(bob_2sm, bob_manager, message_1).unwrap();
assert_eq!(bob_2sm.our_secret_keys.len(), 0);
assert_eq!(bob_2sm.our_min_key_index, 1);
assert_eq!(bob_2sm.our_next_key_index, 1);
assert_eq!(
bob_2sm
.their_public_key
.expect("bob learned about public key of alice"),
alice_2sm
.our_secret_keys
.get(&1)
.expect("alice has one secret key")
.public_key()
.unwrap()
);
assert!(bob_2sm.our_received_secret_key.is_some());
assert_eq!(bob_2sm.their_next_key_used, KeyUsed::OwnKey(1));
assert!(bob_2sm.their_prekey_bundle.is_some());
let (alice_2sm, message_2) =
OneTimeTwoParty::send(alice_2sm, &alice_manager, b"How are you doing?", &rng).unwrap();
let (bob_2sm, bob_manager, receive_2) =
OneTimeTwoParty::receive(bob_2sm, bob_manager, message_2).unwrap();
assert_eq!(alice_2sm.our_secret_keys.len(), 2);
assert_eq!(alice_2sm.our_min_key_index, 1);
assert_eq!(alice_2sm.our_next_key_index, 3);
assert_ne!(
alice_2sm.our_secret_keys.get(&1).unwrap(),
alice_2sm.our_secret_keys.get(&2).unwrap(),
);
assert_eq!(
bob_2sm
.their_public_key
.expect("bob learned about public key of alice"),
alice_2sm
.our_secret_keys
.get(&2)
.expect("alice has one secret key")
.public_key()
.unwrap()
);
let (bob_2sm, message_3) =
OneTimeTwoParty::send(bob_2sm, &bob_manager, b"I'm alright. Thank you!", &rng).unwrap();
assert_eq!(message_3.key_used, KeyUsed::OwnKey(2));
assert_eq!(bob_2sm.our_secret_keys.len(), 1);
assert_eq!(bob_2sm.our_min_key_index, 1);
assert_eq!(bob_2sm.our_next_key_index, 2);
assert_eq!(bob_2sm.their_next_key_used, KeyUsed::ReceivedKey);
let (alice_2sm, alice_manager, receive_3) =
OneTimeTwoParty::receive(alice_2sm, alice_manager, message_3).unwrap();
assert_eq!(alice_2sm.our_secret_keys.len(), 0);
assert_eq!(alice_2sm.our_min_key_index, 3);
assert_eq!(alice_2sm.our_next_key_index, 3);
let (bob_2sm, message_4) =
OneTimeTwoParty::send(bob_2sm, &bob_manager, b"How are you?", &rng).unwrap();
let (alice_2sm, alice_manager, receive_4) =
OneTimeTwoParty::receive(alice_2sm, alice_manager, message_4).unwrap();
let (alice_2sm, message_5) =
OneTimeTwoParty::send(alice_2sm, &alice_manager, b"I'm bored.", &rng).unwrap();
let (bob_2sm, bob_manager, receive_5) =
OneTimeTwoParty::receive(bob_2sm, bob_manager, message_5).unwrap();
assert_eq!(receive_1, b"Hello, Bob!");
assert_eq!(receive_2, b"How are you doing?");
assert_eq!(receive_3, b"I'm alright. Thank you!");
assert_eq!(receive_4, b"How are you?");
assert_eq!(receive_5, b"I'm bored.");
let (bob_2sm, message_6) =
OneTimeTwoParty::send(bob_2sm, &bob_manager, b":-(", &rng).unwrap();
let (alice_2sm, message_7) =
OneTimeTwoParty::send(alice_2sm, &alice_manager, b"Oh wait.", &rng).unwrap();
let (alice_2sm, _, receive_6) =
OneTimeTwoParty::receive(alice_2sm, alice_manager, message_6).unwrap();
let (bob_2sm, _, receive_7) =
OneTimeTwoParty::receive(bob_2sm, bob_manager, message_7).unwrap();
assert_eq!(receive_6, b":-(");
assert_eq!(receive_7, b"Oh wait.");
assert_eq!(alice_2sm.our_secret_keys.len(), 1);
assert_eq!(bob_2sm.our_secret_keys.len(), 1);
}
#[test]
fn long_term_prekeys() {
let rng = Rng::from_seed([1; 32]);
let alice_identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let alice_manager =
KeyManager::init_and_generate_prekey(&alice_identity_secret, Lifetime::default(), &rng)
.unwrap();
let alice_prekey_bundle = KeyManager::prekey_bundle(&alice_manager).unwrap();
let bob_identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let bob_manager =
KeyManager::init_and_generate_prekey(&bob_identity_secret, Lifetime::default(), &rng)
.unwrap();
let bob_prekey_bundle = KeyManager::prekey_bundle(&bob_manager).unwrap();
let alice_2sm_a = LongTermTwoParty::init(bob_prekey_bundle.clone());
let bob_2sm_a = LongTermTwoParty::init(alice_prekey_bundle.clone());
let (alice_2sm_a, message_1) =
LongTermTwoParty::send(alice_2sm_a, &alice_manager, b"Hello, Bob!", &rng).unwrap();
let bob_public_key_1 = alice_2sm_a.their_public_key;
let (bob_2sm_a, bob_manager, receive_1) =
LongTermTwoParty::receive(bob_2sm_a, bob_manager, message_1).unwrap();
let (_bob_2sm_a, message_2) =
LongTermTwoParty::send(bob_2sm_a, &bob_manager, b"Hello, Alice!", &rng).unwrap();
let (_alice_2sm_a, alice_manager, receive_2) =
LongTermTwoParty::receive(alice_2sm_a, alice_manager, message_2).unwrap();
assert_eq!(receive_1, b"Hello, Bob!");
assert_eq!(receive_2, b"Hello, Alice!");
let alice_2sm_b = LongTermTwoParty::init(bob_prekey_bundle);
let bob_2sm_b = LongTermTwoParty::init(alice_prekey_bundle);
let (alice_2sm_b, message_1) =
LongTermTwoParty::send(alice_2sm_b, &alice_manager, b"Hello, again, Bob!", &rng)
.unwrap();
let bob_public_key_2 = alice_2sm_b.their_public_key;
let (bob_2sm_b, bob_manager, receive_1) =
LongTermTwoParty::receive(bob_2sm_b, bob_manager, message_1).unwrap();
let (_bob_2sm_b, message_2) =
LongTermTwoParty::send(bob_2sm_b, &bob_manager, b"Hello, again, Alice!", &rng).unwrap();
let (_alice_2sm_b, _alice_manager, receive_2) =
LongTermTwoParty::receive(alice_2sm_b, alice_manager, message_2).unwrap();
assert_eq!(receive_1, b"Hello, again, Bob!");
assert_eq!(receive_2, b"Hello, again, Alice!");
assert_ne!(bob_public_key_1, bob_public_key_2);
}
#[test]
fn invalid_replayed_messages() {
let rng = Rng::from_seed([1; 32]);
let alice_identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let alice_manager =
KeyManager::init_and_generate_prekey(&alice_identity_secret, Lifetime::default(), &rng)
.unwrap();
let (alice_manager, alice_prekey_bundle) =
KeyManager::generate_onetime_bundle(alice_manager, &rng).unwrap();
let bob_identity_secret = SecretKey::from_bytes(rng.random_array().unwrap());
let bob_manager =
KeyManager::init_and_generate_prekey(&bob_identity_secret, Lifetime::default(), &rng)
.unwrap();
let (bob_manager, bob_prekey_bundle) =
KeyManager::generate_onetime_bundle(bob_manager, &rng).unwrap();
let alice_2sm = OneTimeTwoParty::init(bob_prekey_bundle);
let bob_2sm = OneTimeTwoParty::init(alice_prekey_bundle);
let (alice_2sm, message_1) =
OneTimeTwoParty::send(alice_2sm, &alice_manager, b"Hello, Bob!", &rng).unwrap();
let (bob_2sm, bob_manager, _receive_1) =
OneTimeTwoParty::receive(bob_2sm, bob_manager, message_1.clone()).unwrap();
let result = OneTimeTwoParty::receive(bob_2sm.clone(), bob_manager.clone(), message_1);
assert!(matches!(result, Err(TwoPartyError::PreKeyReuse)));
let (_alice_2sm, message_2) =
OneTimeTwoParty::send(alice_2sm, &alice_manager, b"Hello, again, Bob!", &rng).unwrap();
let (bob_2sm, bob_manager, _receive_2) =
OneTimeTwoParty::receive(bob_2sm, bob_manager, message_2.clone()).unwrap();
let result = OneTimeTwoParty::receive(bob_2sm, bob_manager, message_2);
assert!(matches!(result, Err(TwoPartyError::Hpke(_))));
}
}