diff --git a/Cargo.lock b/Cargo.lock index e3d992c..e45b763 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1814,7 +1814,7 @@ dependencies = [ [[package]] name = "de-mls" version = "3.0.0" -source = "git+https://github.com/vacp2p/de-mls?branch=develop#d838e832994fd1d14f624783741bc60b31510fa0" +source = "git+https://github.com/vacp2p/de-mls?rev=7ae3f03c8dc20711e5ddb712d535dd4b9de74cce#7ae3f03c8dc20711e5ddb712d535dd4b9de74cce" dependencies = [ "hashgraph-like-consensus", "indexmap 2.14.0", diff --git a/core/conversations/Cargo.toml b/core/conversations/Cargo.toml index 3ca2a1f..ef5691d 100644 --- a/core/conversations/Cargo.toml +++ b/core/conversations/Cargo.toml @@ -19,7 +19,7 @@ storage = { workspace = true } alloy = "2.0" base64 = "0.22" chat-proto = { git = "https://github.com/logos-messaging/chat_proto", rev = "37ec98a151f6d50aab2905802ac0a896477e62ea" } -de-mls = { git = "https://github.com/vacp2p/de-mls", branch = "develop" } +de-mls = { git = "https://github.com/vacp2p/de-mls", rev = "7ae3f03c8dc20711e5ddb712d535dd4b9de74cce" } double-ratchets = { path = "../double-ratchets" } hashgraph-like-consensus = "0.5.1" hex = "0.4.3" diff --git a/core/conversations/src/conversation/group_v2.rs b/core/conversations/src/conversation/group_v2.rs index 0fb5043..d25b760 100644 --- a/core/conversations/src/conversation/group_v2.rs +++ b/core/conversations/src/conversation/group_v2.rs @@ -17,7 +17,8 @@ use de_mls::defaults::{ use de_mls::member_id::MemberId; use de_mls::mls_crypto::MlsCredentials; use de_mls::protos::de_mls::messages::v1::{ - AppMessage as AppMessageProto, MemberWelcome, app_message, + AppMessage as AppMessageProto, ConversationMessage as ConversationMessageProto, MemberWelcome, + app_message, }; use de_mls::session::{Conversation, ConversationConfig, ConversationDeps}; use hashgraph_like_consensus::signing::EthereumConsensusSigner; @@ -461,23 +462,36 @@ impl GroupV2Convo { content: Some(Content { bytes: cm.message.clone(), }), - // de-mls carries only the sender's member-id (the LocalIdentity - // display string); it does not yet expose an account-bound - // credential the way GroupV1 does. Surface the LocalIdentity and - // claim it as its own Account (the testnet 1:1 case). This claim - // won't validate against the account directory, so the client - // reports it unverified. - // TODO: surface a real credential once de-mls exposes the sender. - credential: Some(SenderCredential { - account: IdentId::new(cm.sender.clone()), - local_identity: IdentId::new(cm.sender.clone()), - }), + // Use the MLS-authenticated sender de-mls stamps on inbound (the + // verified leaf credential content), not the self-declared + // `sender` string. Today the de-mls member-id is the identity + // *name*, so account == local identity (the single-key testnet + // case) and it won't validate against the account directory (a + // name isn't an account key) — the client reports it unverified. + // TODO: once the member-id carries an account key, this becomes a + // directory-validated credential like GroupV1. + credential: sender_credential(cm), }), _ => None, }) } } +/// Build a [`SenderCredential`] from de-mls's MLS-authenticated `sender_credential` +/// (the verified member-id bytes stamped on inbound). Returns `None` when de-mls +/// didn't stamp one (e.g. system messages). The member-id is the identity name +/// today, so account and local identity are the same. +fn sender_credential(cm: &ConversationMessageProto) -> Option { + if cm.sender_credential.is_empty() { + return None; + } + let id = IdentId::new(String::from_utf8_lossy(&cm.sender_credential).into_owned()); + Some(SenderCredential { + account: id.clone(), + local_identity: id, + }) +} + use prost::{Oneof, bytes::Bytes}; #[derive(Clone, PartialEq, Message)] diff --git a/core/integration_tests_core/tests/mls_integration.rs b/core/integration_tests_core/tests/mls_integration.rs index ac2bc62..ff2d687 100644 --- a/core/integration_tests_core/tests/mls_integration.rs +++ b/core/integration_tests_core/tests/mls_integration.rs @@ -61,17 +61,33 @@ fn create_group() { .saro() .check(&convo_id, M_R1, Some(raya_sender.clone())) ); - assert!(harness.saro().check(&convo_id, M_P1, Some(pax_sender.clone()))); + assert!( + harness + .saro() + .check(&convo_id, M_P1, Some(pax_sender.clone())) + ); assert!( !harness .raya() .check(&convo_id, M_R1, Some(raya_sender.clone())) ); - assert!(harness.raya().check(&convo_id, M_P1, Some(pax_sender.clone()))); + assert!( + harness + .raya() + .check(&convo_id, M_P1, Some(pax_sender.clone())) + ); - assert!(!harness.pax().check(&convo_id, M_R1, Some(raya_sender.clone()))); - assert!(!harness.pax().check(&convo_id, M_P1, Some(pax_sender.clone()))); + assert!( + !harness + .pax() + .check(&convo_id, M_R1, Some(raya_sender.clone())) + ); + assert!( + !harness + .pax() + .check(&convo_id, M_P1, Some(pax_sender.clone())) + ); // Single-key testnet account: account and local identity are the same key. assert_eq!(