feat: demls sender credential

This commit is contained in:
kaichaosun 2026-06-18 15:57:50 +08:00
parent a5d760468a
commit 53c3a89c08
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
4 changed files with 48 additions and 18 deletions

2
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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<SenderCredential> {
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)]

View File

@ -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!(