mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
992 lines
36 KiB
Rust
992 lines
36 KiB
Rust
use base64::prelude::*;
|
|
use std::collections::HashMap;
|
|
|
|
use ed448_goldilocks_plus::{elliptic_curve::group::GroupEncoding, EdwardsPoint, Scalar};
|
|
use protocols::{doubleratchet::{DoubleRatchetParticipant, P2PChannelEnvelope}, tripleratchet::{PeerInfo, TripleRatchetParticipant}};
|
|
|
|
pub(crate) mod protocols;
|
|
|
|
pub struct DoubleRatchetStateAndEnvelope {
|
|
pub ratchet_state: String,
|
|
pub envelope: String,
|
|
}
|
|
|
|
pub struct DoubleRatchetStateAndMessage {
|
|
pub ratchet_state: String,
|
|
pub message: Vec<u8>,
|
|
}
|
|
|
|
pub struct TripleRatchetStateAndMetadata {
|
|
pub ratchet_state: String,
|
|
pub metadata: HashMap<String, String>,
|
|
}
|
|
|
|
pub struct TripleRatchetStateAndEnvelope {
|
|
pub ratchet_state: String,
|
|
pub envelope: String,
|
|
}
|
|
|
|
pub struct TripleRatchetStateAndMessage {
|
|
pub ratchet_state: String,
|
|
pub message: Vec<u8>,
|
|
}
|
|
|
|
pub fn new_double_ratchet(session_key: &Vec<u8>, sending_header_key: &Vec<u8>, next_receiving_header_key: &Vec<u8>, is_sender: bool, sending_ephemeral_private_key: &Vec<u8>, receiving_ephemeral_key: &Vec<u8>) -> String {
|
|
if sending_ephemeral_private_key.len() != 56 {
|
|
return "".to_string();
|
|
}
|
|
|
|
if receiving_ephemeral_key.len() != 57 {
|
|
return "".to_string();
|
|
}
|
|
|
|
let mut sending_ephemeral_private_key_bytes = [0u8; 56];
|
|
sending_ephemeral_private_key_bytes.copy_from_slice(&sending_ephemeral_private_key);
|
|
|
|
let mut receiving_ephemeral_key_bytes = [0u8; 57];
|
|
receiving_ephemeral_key_bytes.copy_from_slice(&receiving_ephemeral_key);
|
|
|
|
let sending_key = Scalar::from_bytes(&sending_ephemeral_private_key_bytes.into());
|
|
let receiving_key = EdwardsPoint::from_bytes(&receiving_ephemeral_key_bytes.into()).into_option();
|
|
if receiving_key.is_none() {
|
|
return "".to_string();
|
|
}
|
|
|
|
let participant = DoubleRatchetParticipant::new(
|
|
&session_key,
|
|
&sending_header_key,
|
|
&next_receiving_header_key,
|
|
true,
|
|
sending_key,
|
|
receiving_key.unwrap(),
|
|
);
|
|
|
|
if participant.is_err() {
|
|
return "".to_string();
|
|
}
|
|
|
|
let json = participant.unwrap().to_json();
|
|
if json.is_err() {
|
|
return "".to_string();
|
|
}
|
|
|
|
return json.unwrap();
|
|
}
|
|
|
|
pub fn double_ratchet_encrypt(ratchet_state_and_message: DoubleRatchetStateAndMessage) -> DoubleRatchetStateAndEnvelope {
|
|
let ratchet_state = ratchet_state_and_message.ratchet_state.clone();
|
|
let participant = DoubleRatchetParticipant::from_json(ratchet_state.clone());
|
|
|
|
if participant.is_err() {
|
|
return DoubleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
let mut dr = participant.unwrap();
|
|
let envelope = dr.ratchet_encrypt(&ratchet_state_and_message.message);
|
|
|
|
if envelope.is_err() {
|
|
return DoubleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
|
|
let participant_json = dr.to_json();
|
|
if participant_json.is_err() {
|
|
return DoubleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
let envelope_json = envelope.unwrap().to_json();
|
|
if envelope_json.is_err() {
|
|
return DoubleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
return DoubleRatchetStateAndEnvelope{
|
|
ratchet_state: participant_json.unwrap(),
|
|
envelope: envelope_json.unwrap(),
|
|
};
|
|
}
|
|
|
|
pub fn double_ratchet_decrypt(ratchet_state_and_envelope: DoubleRatchetStateAndEnvelope) -> DoubleRatchetStateAndMessage {
|
|
let ratchet_state = ratchet_state_and_envelope.ratchet_state.clone();
|
|
let participant = DoubleRatchetParticipant::from_json(ratchet_state.clone());
|
|
let envelope = P2PChannelEnvelope::from_json(ratchet_state_and_envelope.envelope);
|
|
|
|
if participant.is_err() || envelope.is_err() {
|
|
return DoubleRatchetStateAndMessage{
|
|
ratchet_state: ratchet_state,
|
|
message: vec![],
|
|
};
|
|
}
|
|
|
|
let mut dr = participant.unwrap();
|
|
let message = dr.ratchet_decrypt(&envelope.unwrap());
|
|
|
|
if message.is_err() {
|
|
return DoubleRatchetStateAndMessage{
|
|
ratchet_state: ratchet_state,
|
|
message: vec![],
|
|
};
|
|
}
|
|
|
|
let participant_json = dr.to_json();
|
|
if participant_json.is_err() {
|
|
return DoubleRatchetStateAndMessage{
|
|
ratchet_state: ratchet_state,
|
|
message: vec![],
|
|
};
|
|
}
|
|
|
|
return DoubleRatchetStateAndMessage{
|
|
ratchet_state: participant_json.unwrap(),
|
|
message: message.unwrap(),
|
|
};
|
|
}
|
|
|
|
pub fn new_triple_ratchet(peers: &Vec<Vec<u8>>, peer_key: &Vec<u8>, identity_key: &Vec<u8>, signed_pre_key: &Vec<u8>, threshold: u64, async_dkg_ratchet: bool) -> TripleRatchetStateAndMetadata {
|
|
if peer_key.len() != 56 {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
if identity_key.len() != 56 {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
if signed_pre_key.len() != 56 {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
if peers.len() < 3 {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
if threshold > peers.len() as u64 {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let mut peer_key_bytes = [0u8; 56];
|
|
peer_key_bytes.copy_from_slice(&peer_key);
|
|
|
|
let mut identity_key_bytes = [0u8; 56];
|
|
identity_key_bytes.copy_from_slice(&identity_key);
|
|
|
|
let mut signed_pre_key_bytes = [0u8; 56];
|
|
signed_pre_key_bytes.copy_from_slice(&signed_pre_key);
|
|
|
|
let peer_key_scalar = Scalar::from_bytes(&peer_key_bytes.into());
|
|
let identity_key_scalar = Scalar::from_bytes(&identity_key_bytes.into());
|
|
let signed_pre_key_scalar = Scalar::from_bytes(&signed_pre_key_bytes.into());
|
|
let mut peerinfos = Vec::<PeerInfo>::new();
|
|
for pk in peers.iter() {
|
|
if pk.len() != 171 {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
peerinfos.push(PeerInfo{
|
|
public_key: pk[..57].into(),
|
|
identity_public_key: pk[57..114].into(),
|
|
signed_pre_public_key: pk[114..].into(),
|
|
});
|
|
}
|
|
|
|
let participant = TripleRatchetParticipant::new(
|
|
&peerinfos,
|
|
peer_key_scalar,
|
|
identity_key_scalar,
|
|
signed_pre_key_scalar,
|
|
threshold as usize,
|
|
async_dkg_ratchet,
|
|
);
|
|
|
|
if participant.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let (tr, metadata) = participant.unwrap();
|
|
|
|
let participant_json = tr.to_json();
|
|
|
|
if participant_json.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: "".to_string(),
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let metadata_json = match metadata_to_json(&String::from(""), metadata) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: participant_json.unwrap(),
|
|
metadata: metadata_json,
|
|
};
|
|
}
|
|
|
|
fn metadata_to_json(ratchet_state: &String, metadata: HashMap<Vec<u8>, P2PChannelEnvelope>) -> Result<HashMap<String, String>, TripleRatchetStateAndMetadata> {
|
|
let mut metadata_json = HashMap::<String, String>::new();
|
|
for (k,v) in metadata {
|
|
let env = v.to_json();
|
|
if env.is_err() {
|
|
return Err(TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state.to_string(),
|
|
metadata: HashMap::new(),
|
|
});
|
|
}
|
|
|
|
metadata_json.insert(BASE64_STANDARD.encode(k), env.unwrap());
|
|
}
|
|
Ok(metadata_json)
|
|
}
|
|
|
|
fn json_to_metadata(ratchet_state_and_metadata: TripleRatchetStateAndMetadata, ratchet_state: &String) -> Result<HashMap<Vec<u8>, P2PChannelEnvelope>, TripleRatchetStateAndMetadata> {
|
|
let mut metadata = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
|
for (k,v) in ratchet_state_and_metadata.metadata {
|
|
let env = P2PChannelEnvelope::from_json(v);
|
|
let kb = BASE64_STANDARD.decode(k);
|
|
if env.is_err() || kb.is_err() {
|
|
return Err(TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state.clone(),
|
|
metadata: HashMap::new(),
|
|
});
|
|
}
|
|
|
|
metadata.insert(kb.unwrap(), env.unwrap());
|
|
}
|
|
Ok(metadata)
|
|
}
|
|
|
|
pub fn triple_ratchet_init_round_1(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
|
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
|
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
|
if tr.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let mut trp = tr.unwrap();
|
|
let result = trp.initialize(&metadata);
|
|
if result.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let metadata = result.unwrap();
|
|
let metadata_json = match metadata_to_json(&ratchet_state, metadata) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let json = trp.to_json();
|
|
if json.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: json.unwrap(),
|
|
metadata: metadata_json,
|
|
};
|
|
}
|
|
|
|
pub fn triple_ratchet_init_round_2(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
|
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
|
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
|
if tr.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let mut trp = tr.unwrap();
|
|
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
|
for (k, v) in metadata {
|
|
let r = trp.receive_poly_frag(&k, &v);
|
|
if r.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let opt = r.unwrap();
|
|
if opt.is_some() {
|
|
result = opt.unwrap();
|
|
}
|
|
}
|
|
|
|
let metadata_json = match metadata_to_json(&ratchet_state, result) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let json = trp.to_json();
|
|
if json.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: json.unwrap(),
|
|
metadata: metadata_json,
|
|
};
|
|
}
|
|
|
|
pub fn triple_ratchet_init_round_3(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
|
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
|
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
|
if tr.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let mut trp = tr.unwrap();
|
|
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
|
for (k, v) in metadata {
|
|
let r = trp.receive_commitment(&k, &v);
|
|
if r.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let opt = r.unwrap();
|
|
if opt.is_some() {
|
|
result = opt.unwrap();
|
|
}
|
|
}
|
|
|
|
let metadata_json = match metadata_to_json(&ratchet_state, result) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let json = trp.to_json();
|
|
if json.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: json.unwrap(),
|
|
metadata: metadata_json,
|
|
};
|
|
}
|
|
|
|
pub fn triple_ratchet_init_round_4(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
|
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
|
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
|
if tr.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let mut trp = tr.unwrap();
|
|
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
|
for (k, v) in metadata {
|
|
let r = trp.recombine(&k, &v);
|
|
if r.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
}
|
|
|
|
let metadata_json = match metadata_to_json(&ratchet_state, result) {
|
|
Ok(value) => value,
|
|
Err(value) => return value,
|
|
};
|
|
|
|
let json = trp.to_json();
|
|
if json.is_err() {
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: ratchet_state,
|
|
metadata: HashMap::new(),
|
|
};
|
|
}
|
|
|
|
return TripleRatchetStateAndMetadata{
|
|
ratchet_state: json.unwrap(),
|
|
metadata: metadata_json,
|
|
};
|
|
}
|
|
|
|
pub fn triple_ratchet_encrypt(ratchet_state_and_message: TripleRatchetStateAndMessage) -> TripleRatchetStateAndEnvelope {
|
|
let ratchet_state = ratchet_state_and_message.ratchet_state.clone();
|
|
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
|
if tr.is_err() {
|
|
return TripleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
let mut trp = tr.unwrap();
|
|
let result = trp.ratchet_encrypt(&ratchet_state_and_message.message);
|
|
|
|
if result.is_err() {
|
|
return TripleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
let envelope = result.unwrap();
|
|
let envelope_json = envelope.to_json();
|
|
|
|
if envelope_json.is_err() {
|
|
return TripleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
let json = trp.to_json();
|
|
if json.is_err() {
|
|
return TripleRatchetStateAndEnvelope{
|
|
ratchet_state: ratchet_state,
|
|
envelope: "".to_string(),
|
|
};
|
|
}
|
|
|
|
return TripleRatchetStateAndEnvelope{
|
|
ratchet_state: json.unwrap(),
|
|
envelope: envelope_json.unwrap(),
|
|
};
|
|
}
|
|
|
|
pub fn triple_ratchet_decrypt(ratchet_state_and_envelope: TripleRatchetStateAndEnvelope) -> TripleRatchetStateAndMessage {
|
|
let ratchet_state = ratchet_state_and_envelope.ratchet_state.clone();
|
|
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
|
if tr.is_err() {
|
|
return TripleRatchetStateAndMessage{
|
|
ratchet_state: ratchet_state,
|
|
message: vec![],
|
|
};
|
|
}
|
|
|
|
let mut trp = tr.unwrap();
|
|
let env = P2PChannelEnvelope::from_json(ratchet_state_and_envelope.envelope);
|
|
if env.is_err() {
|
|
return TripleRatchetStateAndMessage{
|
|
ratchet_state: ratchet_state,
|
|
message: vec![],
|
|
};
|
|
}
|
|
|
|
let result = trp.ratchet_decrypt(&env.unwrap());
|
|
|
|
if result.is_err() {
|
|
return TripleRatchetStateAndMessage{
|
|
ratchet_state: ratchet_state,
|
|
message: vec![],
|
|
};
|
|
}
|
|
|
|
let message = result.unwrap().0;
|
|
|
|
let json = trp.to_json();
|
|
if json.is_err() {
|
|
return TripleRatchetStateAndMessage{
|
|
ratchet_state: ratchet_state,
|
|
message: vec![],
|
|
};
|
|
}
|
|
|
|
return TripleRatchetStateAndMessage{
|
|
ratchet_state: json.unwrap(),
|
|
message: message,
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::collections::HashMap;
|
|
|
|
use super::*;
|
|
use ed448_goldilocks_plus::{Scalar, elliptic_curve::Group, EdwardsPoint};
|
|
use protocols::{doubleratchet::P2PChannelEnvelope, tripleratchet::{PeerInfo, TripleRatchetParticipant}};
|
|
|
|
#[test]
|
|
fn test_four_party_triple_ratchet_communication() {
|
|
let mut rng = rand::thread_rng();
|
|
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
|
|
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
|
.collect();
|
|
|
|
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
|
|
|
|
let mut peer_infos: Vec<PeerInfo> = keys
|
|
.iter()
|
|
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
|
|
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
})
|
|
.collect();
|
|
|
|
// mirror the internal order so we can use by index:
|
|
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
|
|
|
|
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
|
|
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
|
|
for i in 0..4 {
|
|
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
}
|
|
|
|
for i in 0..4 {
|
|
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
|
|
.filter(|&(j, _)| j != i)
|
|
.map(|(_, peer)| peer.clone())
|
|
.collect();
|
|
|
|
let (participant, init_msg) = TripleRatchetParticipant::new(
|
|
&other_peers,
|
|
keys[i].0.clone(),
|
|
keys[i].1.clone(),
|
|
keys[i].2.clone(),
|
|
3,
|
|
false,
|
|
).unwrap();
|
|
|
|
participants.push(participant);
|
|
|
|
for (j, env) in init_msg.iter() {
|
|
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
|
|
// Exchange initial messages and get frags:
|
|
for i in 0..4 {
|
|
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
|
|
for (j, env) in result.iter() {
|
|
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
|
|
// Exchange frags and receive commitments once all frags have been distributed:
|
|
for i in 0..4 {
|
|
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
|
|
for (j, env) in out.iter() {
|
|
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Exchange commitments and produce reveals:
|
|
for i in 0..4 {
|
|
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
|
|
for (j, env) in reveal_msg.iter() {
|
|
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Collect reveals and confirm zkpoks are valid, produce group key:
|
|
for i in 0..4 {
|
|
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
participants[i].recombine(j, &env.clone()).unwrap();
|
|
}
|
|
}
|
|
|
|
// Test sending and receiving messages
|
|
let test_messages = [
|
|
"hello there",
|
|
"general kenobi",
|
|
"you are a bold one",
|
|
"*mechanical laughter*",
|
|
];
|
|
|
|
for (i, message) in test_messages.iter().enumerate() {
|
|
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
|
|
for j in 0..4 {
|
|
if i != j {
|
|
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
|
|
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
|
|
}
|
|
}
|
|
}
|
|
|
|
for _ in 0..5 {
|
|
for i in 0..4 {
|
|
let message1 = format!("test 1 {}", i + 1);
|
|
let message2 = format!("test 2 {}", i + 1);
|
|
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
|
|
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
|
|
|
|
for j in 0..4 {
|
|
if i != j {
|
|
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
|
|
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
|
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
|
|
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn test_four_party_triple_ratchet_communication_with_serialization_each_step() {
|
|
let mut rng = rand::thread_rng();
|
|
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
|
|
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
|
.collect();
|
|
|
|
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
|
|
|
|
let mut peer_infos: Vec<PeerInfo> = keys
|
|
.iter()
|
|
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
|
|
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
})
|
|
.collect();
|
|
|
|
// mirror the internal order so we can use by index:
|
|
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
|
|
|
|
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
|
|
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
|
|
for i in 0..4 {
|
|
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
}
|
|
|
|
for i in 0..4 {
|
|
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
|
|
.filter(|&(j, _)| j != i)
|
|
.map(|(_, peer)| peer.clone())
|
|
.collect();
|
|
|
|
let (participant, init_msg) = TripleRatchetParticipant::new(
|
|
&other_peers,
|
|
keys[i].0.clone(),
|
|
keys[i].1.clone(),
|
|
keys[i].2.clone(),
|
|
3,
|
|
false,
|
|
).unwrap();
|
|
|
|
for (j, env) in init_msg.iter() {
|
|
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
|
|
let participant_json = participant.to_json();
|
|
if participant_json.is_err() {
|
|
panic!("bad json");
|
|
}
|
|
participants.push(TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap());
|
|
}
|
|
|
|
// Exchange initial messages and get frags:
|
|
for i in 0..4 {
|
|
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
|
|
for (j, env) in result.iter() {
|
|
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
|
|
let participant_json = participants[i].to_json();
|
|
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
|
}
|
|
|
|
// Exchange frags and receive commitments once all frags have been distributed:
|
|
for i in 0..4 {
|
|
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
|
|
for (j, env) in out.iter() {
|
|
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
let participant_json = participants[i].to_json();
|
|
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
|
}
|
|
|
|
// Exchange commitments and produce reveals:
|
|
for i in 0..4 {
|
|
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
|
|
for (j, env) in reveal_msg.iter() {
|
|
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
let participant_json = participants[i].to_json();
|
|
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
|
}
|
|
|
|
// Collect reveals and confirm zkpoks are valid, produce group key:
|
|
for i in 0..4 {
|
|
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
participants[i].recombine(j, &env.clone()).unwrap();
|
|
|
|
let participant_json = participants[i].to_json();
|
|
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
|
}
|
|
}
|
|
|
|
// Test sending and receiving messages
|
|
let test_messages = [
|
|
"hello there",
|
|
"general kenobi",
|
|
"you are a bold one",
|
|
"*mechanical laughter*",
|
|
];
|
|
|
|
for (i, message) in test_messages.iter().enumerate() {
|
|
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
|
|
for j in 0..4 {
|
|
if i != j {
|
|
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
|
|
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
|
|
}
|
|
}
|
|
|
|
let participant_json = participants[i].to_json();
|
|
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
|
}
|
|
|
|
for _ in 0..5 {
|
|
for i in 0..4 {
|
|
let message1 = format!("test 1 {}", i + 1);
|
|
let message2 = format!("test 2 {}", i + 1);
|
|
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
|
|
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
|
|
|
|
for j in 0..4 {
|
|
if i != j {
|
|
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
|
|
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
|
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
|
|
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
|
}
|
|
}
|
|
|
|
let participant_json = participants[i].to_json();
|
|
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_four_party_async_triple_ratchet_communication() {
|
|
let mut rng = rand::thread_rng();
|
|
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
|
|
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
|
.collect();
|
|
|
|
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
|
|
|
|
let mut peer_infos: Vec<PeerInfo> = keys
|
|
.iter()
|
|
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
|
|
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
|
})
|
|
.collect();
|
|
|
|
// mirror the internal order so we can use by index:
|
|
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
|
|
|
|
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
|
|
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
|
|
|
for i in 0..4 {
|
|
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
|
}
|
|
|
|
for i in 0..4 {
|
|
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
|
|
.filter(|&(j, _)| j != i)
|
|
.map(|(_, peer)| peer.clone())
|
|
.collect();
|
|
|
|
let (participant, init_msg) = TripleRatchetParticipant::new(
|
|
&other_peers,
|
|
keys[i].0.clone(),
|
|
keys[i].1.clone(),
|
|
keys[i].2.clone(),
|
|
2,
|
|
true,
|
|
).unwrap();
|
|
|
|
participants.push(participant);
|
|
|
|
for (j, env) in init_msg.iter() {
|
|
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
|
|
// Exchange initial messages and get frags:
|
|
for i in 0..4 {
|
|
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
|
|
for (j, env) in result.iter() {
|
|
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
|
|
// Exchange frags and receive commitments once all frags have been distributed:
|
|
for i in 0..4 {
|
|
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
|
|
for (j, env) in out.iter() {
|
|
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Exchange commitments and produce reveals:
|
|
for i in 0..4 {
|
|
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
|
|
for (j, env) in reveal_msg.iter() {
|
|
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Collect reveals and confirm zkpoks are valid, produce group key:
|
|
for i in 0..4 {
|
|
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
|
|
participants[i].recombine(j, &env.clone()).unwrap();
|
|
}
|
|
}
|
|
|
|
// Test sending and receiving messages
|
|
let test_messages = [
|
|
"hello there",
|
|
"general kenobi",
|
|
"you are a bold one",
|
|
"*mechanical laughter*",
|
|
];
|
|
|
|
for (i, message) in test_messages.iter().enumerate() {
|
|
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
|
|
for j in 0..4 {
|
|
if i != j {
|
|
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
|
|
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
|
|
}
|
|
}
|
|
}
|
|
|
|
for _ in 0..5 {
|
|
for i in 0..4 {
|
|
let message1 = format!("test 1 {}", i + 1);
|
|
let message2 = format!("test 2 {}", i + 1);
|
|
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
|
|
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
|
|
|
|
for j in 0..4 {
|
|
if i != j {
|
|
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
|
|
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
|
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
|
|
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|