Cursor: Apply local changes for cloud agent
This commit is contained in:
@@ -7,7 +7,7 @@ use opaque_ke::{
|
||||
};
|
||||
use quicnprotochat_core::{
|
||||
generate_key_package, hybrid_decrypt, hybrid_encrypt, opaque_auth::OpaqueSuite,
|
||||
GroupMember, HybridKeypair, IdentityKeypair,
|
||||
HybridKeypair, IdentityKeypair,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -16,7 +16,10 @@ use super::{
|
||||
connect_node, current_timestamp_ms, enqueue, fetch_all, fetch_hybrid_key,
|
||||
fetch_key_package, fetch_wait, try_hybrid_decrypt, upload_hybrid_key, upload_key_package,
|
||||
},
|
||||
state::{decode_identity_key, load_existing_state, load_or_init_state, save_state, sha256},
|
||||
state::{
|
||||
decode_identity_key, load_existing_state, load_or_init_state, save_state, sha256,
|
||||
MemberBackend,
|
||||
},
|
||||
};
|
||||
|
||||
/// Print local identity information from the state file (no server connection).
|
||||
@@ -45,6 +48,14 @@ pub fn cmd_whoami(state_path: &Path, password: Option<&str>) -> anyhow::Result<(
|
||||
"none"
|
||||
}
|
||||
);
|
||||
println!(
|
||||
"pq_backend : {}",
|
||||
if state.use_pq_backend {
|
||||
"yes (MLS HPKE: X25519 + ML-KEM-768)"
|
||||
} else {
|
||||
"no (classical)"
|
||||
}
|
||||
);
|
||||
println!("state_file : {}", state_path.display());
|
||||
|
||||
Ok(())
|
||||
@@ -365,7 +376,7 @@ async fn do_upload_keypackage(
|
||||
ca_cert: &Path,
|
||||
server_name: &str,
|
||||
password: Option<&str>,
|
||||
member: &mut GroupMember,
|
||||
member: &mut MemberBackend,
|
||||
hybrid_kp: Option<&HybridKeypair>,
|
||||
) -> anyhow::Result<()> {
|
||||
let tls_bytes = member
|
||||
@@ -428,8 +439,9 @@ pub async fn cmd_register_state(
|
||||
ca_cert: &Path,
|
||||
server_name: &str,
|
||||
password: Option<&str>,
|
||||
use_pq_backend: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let state = load_or_init_state(state_path, password)?;
|
||||
let state = load_or_init_state(state_path, password, use_pq_backend)?;
|
||||
let (mut member, hybrid_kp) = state.into_parts(state_path)?;
|
||||
do_upload_keypackage(
|
||||
state_path,
|
||||
@@ -522,15 +534,37 @@ pub async fn cmd_fetch_key(
|
||||
}
|
||||
|
||||
/// Run a two-party MLS demo against the unified server.
|
||||
pub async fn cmd_demo_group(server: &str, ca_cert: &Path, server_name: &str) -> anyhow::Result<()> {
|
||||
pub async fn cmd_demo_group(
|
||||
server: &str,
|
||||
ca_cert: &Path,
|
||||
server_name: &str,
|
||||
use_pq_backend: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
|
||||
let creator_state_path = PathBuf::from("quicnprotochat-demo-creator.bin");
|
||||
let joiner_state_path = PathBuf::from("quicnprotochat-demo-joiner.bin");
|
||||
|
||||
let (mut creator, creator_hybrid_opt) =
|
||||
load_or_init_state(&creator_state_path, None)?.into_parts(&creator_state_path)?;
|
||||
let (mut joiner, joiner_hybrid_opt) =
|
||||
load_or_init_state(&joiner_state_path, None)?.into_parts(&joiner_state_path)?;
|
||||
let pb = ProgressBar::new(5);
|
||||
pb.set_style(
|
||||
ProgressStyle::with_template("{spinner:.green} [{bar:40.cyan/blue}] {pos}/{len} {msg}")
|
||||
.expect("demo progress template is valid")
|
||||
.tick_chars("\u{2801}\u{2802}\u{2804}\u{2840}\u{2820}\u{2810}\u{2808} ")
|
||||
.progress_chars("=>-"),
|
||||
);
|
||||
pb.enable_steady_tick(std::time::Duration::from_millis(80));
|
||||
|
||||
pb.set_message("Generating Alice keys\u{2026}");
|
||||
let (mut creator, creator_hybrid_opt) =
|
||||
load_or_init_state(&creator_state_path, None, use_pq_backend)?.into_parts(&creator_state_path)?;
|
||||
pb.inc(1);
|
||||
|
||||
pb.set_message("Generating Bob keys\u{2026}");
|
||||
let (mut joiner, joiner_hybrid_opt) =
|
||||
load_or_init_state(&joiner_state_path, None, use_pq_backend)?.into_parts(&joiner_state_path)?;
|
||||
pb.inc(1);
|
||||
|
||||
pb.set_message("Creating group\u{2026}");
|
||||
let creator_hybrid = creator_hybrid_opt.unwrap_or_else(HybridKeypair::generate);
|
||||
let joiner_hybrid = joiner_hybrid_opt.unwrap_or_else(HybridKeypair::generate);
|
||||
|
||||
@@ -552,8 +586,6 @@ pub async fn cmd_demo_group(server: &str, ca_cert: &Path, server_name: &str) ->
|
||||
upload_hybrid_key(&creator_node, &creator_identity, &creator_hybrid.public_key()).await?;
|
||||
upload_hybrid_key(&joiner_node, &joiner_identity, &joiner_hybrid.public_key()).await?;
|
||||
|
||||
println!("hybrid public keys uploaded for creator and joiner");
|
||||
|
||||
let fetched_joiner_kp = fetch_key_package(&creator_node, &joiner_identity).await?;
|
||||
anyhow::ensure!(
|
||||
!fetched_joiner_kp.is_empty(),
|
||||
@@ -566,7 +598,9 @@ pub async fn cmd_demo_group(server: &str, ca_cert: &Path, server_name: &str) ->
|
||||
let (_commit, welcome) = creator
|
||||
.add_member(&fetched_joiner_kp)
|
||||
.context("add_member failed")?;
|
||||
pb.inc(1);
|
||||
|
||||
pb.set_message("Encrypting\u{2026}");
|
||||
let creator_ds = creator_node.clone();
|
||||
let joiner_ds = joiner_node.clone();
|
||||
|
||||
@@ -576,7 +610,9 @@ pub async fn cmd_demo_group(server: &str, ca_cert: &Path, server_name: &str) ->
|
||||
let wrapped_welcome =
|
||||
hybrid_encrypt(&joiner_hybrid_pk, &welcome).context("hybrid encrypt welcome")?;
|
||||
enqueue(&creator_ds, &joiner_identity, &wrapped_welcome).await?;
|
||||
pb.inc(1);
|
||||
|
||||
pb.set_message("Delivering\u{2026}");
|
||||
let welcome_payloads = fetch_all(&joiner_ds, &joiner_identity).await?;
|
||||
let raw_welcome = welcome_payloads
|
||||
.first()
|
||||
@@ -605,10 +641,6 @@ pub async fn cmd_demo_group(server: &str, ca_cert: &Path, server_name: &str) ->
|
||||
let plaintext_creator_joiner = joiner
|
||||
.receive_message(&inner_creator_joiner)?
|
||||
.context("expected application message")?;
|
||||
println!(
|
||||
"creator -> joiner plaintext: {}",
|
||||
String::from_utf8_lossy(&plaintext_creator_joiner)
|
||||
);
|
||||
|
||||
let creator_hybrid_pk = fetch_hybrid_key(&joiner_node, &creator_identity)
|
||||
.await?
|
||||
@@ -629,11 +661,17 @@ pub async fn cmd_demo_group(server: &str, ca_cert: &Path, server_name: &str) ->
|
||||
let plaintext_joiner_creator = creator
|
||||
.receive_message(&inner_joiner_creator)?
|
||||
.context("expected application message")?;
|
||||
pb.inc(1);
|
||||
|
||||
pb.finish_and_clear();
|
||||
println!(
|
||||
"joiner -> creator plaintext: {}",
|
||||
"creator -> joiner: {}",
|
||||
String::from_utf8_lossy(&plaintext_creator_joiner)
|
||||
);
|
||||
println!(
|
||||
"joiner -> creator: {}",
|
||||
String::from_utf8_lossy(&plaintext_joiner_creator)
|
||||
);
|
||||
|
||||
println!("demo-group complete (hybrid PQ envelope active)");
|
||||
|
||||
Ok(())
|
||||
@@ -645,8 +683,9 @@ pub async fn cmd_create_group(
|
||||
_server: &str,
|
||||
group_id: &str,
|
||||
password: Option<&str>,
|
||||
use_pq_backend: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let state = load_or_init_state(state_path, password)?;
|
||||
let state = load_or_init_state(state_path, password, use_pq_backend)?;
|
||||
let (mut member, hybrid_kp) = state.into_parts(state_path)?;
|
||||
|
||||
anyhow::ensure!(
|
||||
@@ -850,11 +889,29 @@ pub async fn cmd_recv(
|
||||
stream: bool,
|
||||
password: Option<&str>,
|
||||
) -> anyhow::Result<()> {
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
|
||||
let state = load_existing_state(state_path, password)?;
|
||||
let (mut member, hybrid_kp) = state.into_parts(state_path)?;
|
||||
|
||||
let client = connect_node(server, ca_cert, server_name).await?;
|
||||
|
||||
let stream_pb: Option<ProgressBar> = if stream {
|
||||
let pb = ProgressBar::new_spinner();
|
||||
pb.set_style(
|
||||
ProgressStyle::with_template("{spinner:.green} {msg}")
|
||||
.expect("recv progress template is valid")
|
||||
.tick_chars("\u{2801}\u{2802}\u{2804}\u{2840}\u{2820}\u{2810}\u{2808} "),
|
||||
);
|
||||
pb.set_message("Listening for messages (0 received)\u{2026}");
|
||||
pb.enable_steady_tick(std::time::Duration::from_millis(100));
|
||||
Some(pb)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut total_received: usize = 0;
|
||||
|
||||
loop {
|
||||
let mut payloads =
|
||||
fetch_wait(&client, &member.identity().public_key_bytes(), wait_ms).await?;
|
||||
@@ -876,13 +933,29 @@ pub async fn cmd_recv(
|
||||
let mls_payload = match try_hybrid_decrypt(hybrid_kp.as_ref(), payload) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
println!("[{idx}] decrypt error: {e}");
|
||||
match &stream_pb {
|
||||
Some(pb) => pb.println(format!("[{idx}] decrypt error: {e}")),
|
||||
None => println!("[{idx}] decrypt error: {e}"),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
match member.receive_message(&mls_payload) {
|
||||
Ok(Some(pt)) => println!("[{idx}] plaintext: {}", String::from_utf8_lossy(&pt)),
|
||||
Ok(None) => println!("[{idx}] commit applied"),
|
||||
Ok(Some(pt)) => {
|
||||
total_received += 1;
|
||||
let line = format!("[{idx}] plaintext: {}", String::from_utf8_lossy(&pt));
|
||||
match &stream_pb {
|
||||
Some(pb) => pb.println(line),
|
||||
None => println!("{line}"),
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
let line = format!("[{idx}] commit applied");
|
||||
match &stream_pb {
|
||||
Some(pb) => pb.println(line),
|
||||
None => println!("{line}"),
|
||||
}
|
||||
}
|
||||
Err(_) => retry_mls.push(mls_payload),
|
||||
}
|
||||
}
|
||||
@@ -890,14 +963,33 @@ pub async fn cmd_recv(
|
||||
// epoch was not yet advanced until a commit earlier in the batch was applied).
|
||||
for mls_payload in &retry_mls {
|
||||
match member.receive_message(mls_payload) {
|
||||
Ok(Some(pt)) => println!("[retry] plaintext: {}", String::from_utf8_lossy(&pt)),
|
||||
Ok(Some(pt)) => {
|
||||
total_received += 1;
|
||||
let line = format!("[retry] plaintext: {}", String::from_utf8_lossy(&pt));
|
||||
match &stream_pb {
|
||||
Some(pb) => pb.println(line),
|
||||
None => println!("{line}"),
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(e) => println!("[retry] error: {e}"),
|
||||
Err(e) => {
|
||||
let line = format!("[retry] error: {e}");
|
||||
match &stream_pb {
|
||||
Some(pb) => pb.println(line),
|
||||
None => println!("{line}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
save_state(state_path, &member, hybrid_kp.as_ref(), password)?;
|
||||
|
||||
if let Some(ref pb) = stream_pb {
|
||||
pb.set_message(format!(
|
||||
"Listening for messages ({total_received} received)\u{2026}"
|
||||
));
|
||||
}
|
||||
|
||||
if !stream {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user