feat(mesh): add KeyPackage distribution over mesh
Implements announce-based KeyPackage distribution for serverless MLS: - MeshAnnounce now includes optional `keypackage_hash` field (8 bytes) - CAP_MLS_READY capability flag for nodes with KeyPackages - KeyPackageCache for storing received KeyPackages: - Indexed by mesh address - Multiple per address (for rotation) - TTL-based expiry - Capacity-bounded with LRU eviction - Mesh protocol messages: - KeyPackageRequest (request by address or hash) - KeyPackageResponse (KeyPackage + hash) - KeyPackageUnavailable (negative response) Protocol flow: 1. Bob announces with keypackage_hash 2. Alice requests KeyPackage via mesh 3. Bob (or relay) responds with full KeyPackage 4. Alice creates MLS Welcome, sends to Bob via mesh
This commit is contained in:
@@ -17,6 +17,8 @@ pub const CAP_STORE: u16 = 0x0002;
|
||||
pub const CAP_GATEWAY: u16 = 0x0004;
|
||||
/// Capability flag: node is on a low-bandwidth transport only.
|
||||
pub const CAP_CONSTRAINED: u16 = 0x0008;
|
||||
/// Capability flag: node has KeyPackages available for MLS group invites.
|
||||
pub const CAP_MLS_READY: u16 = 0x0010;
|
||||
|
||||
/// A signed mesh node announcement.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@@ -37,6 +39,10 @@ pub struct MeshAnnounce {
|
||||
pub hop_count: u8,
|
||||
/// Maximum propagation hops.
|
||||
pub max_hops: u8,
|
||||
/// Optional hash of current KeyPackage (SHA-256, truncated to 8 bytes).
|
||||
/// Present when CAP_MLS_READY is set. Peers can request the full KeyPackage.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub keypackage_hash: Option<[u8; 8]>,
|
||||
/// Ed25519 signature over all fields except signature and hop_count.
|
||||
pub signature: Vec<u8>,
|
||||
}
|
||||
@@ -51,6 +57,16 @@ pub fn compute_address(identity_key: &[u8]) -> [u8; 16] {
|
||||
addr
|
||||
}
|
||||
|
||||
/// Compute the 8-byte truncated hash of a KeyPackage for announce inclusion.
|
||||
///
|
||||
/// This hash is used to identify which KeyPackage version a node has available.
|
||||
pub fn compute_keypackage_hash(keypackage_bytes: &[u8]) -> [u8; 8] {
|
||||
let hash = Sha256::digest(keypackage_bytes);
|
||||
let mut kp_hash = [0u8; 8];
|
||||
kp_hash.copy_from_slice(&hash[..8]);
|
||||
kp_hash
|
||||
}
|
||||
|
||||
impl MeshAnnounce {
|
||||
/// Create and sign a new mesh announcement.
|
||||
pub fn new(
|
||||
@@ -58,6 +74,17 @@ impl MeshAnnounce {
|
||||
capabilities: u16,
|
||||
reachable_via: Vec<(String, Vec<u8>)>,
|
||||
max_hops: u8,
|
||||
) -> Self {
|
||||
Self::with_keypackage(identity, capabilities, reachable_via, max_hops, None)
|
||||
}
|
||||
|
||||
/// Create announcement with an optional KeyPackage hash.
|
||||
pub fn with_keypackage(
|
||||
identity: &MeshIdentity,
|
||||
capabilities: u16,
|
||||
reachable_via: Vec<(String, Vec<u8>)>,
|
||||
max_hops: u8,
|
||||
keypackage_hash: Option<[u8; 8]>,
|
||||
) -> Self {
|
||||
let identity_key = identity.public_key().to_vec();
|
||||
let address = compute_address(&identity_key);
|
||||
@@ -75,6 +102,7 @@ impl MeshAnnounce {
|
||||
reachable_via,
|
||||
hop_count: 0,
|
||||
max_hops,
|
||||
keypackage_hash,
|
||||
signature: Vec::new(),
|
||||
};
|
||||
|
||||
@@ -105,7 +133,7 @@ impl MeshAnnounce {
|
||||
/// hop_count without re-signing (same design as [`MeshEnvelope`]).
|
||||
fn signable_bytes(&self) -> Vec<u8> {
|
||||
let mut buf = Vec::with_capacity(
|
||||
self.identity_key.len() + 16 + 2 + 8 + 8 + self.reachable_via.len() * 32 + 1,
|
||||
self.identity_key.len() + 16 + 2 + 8 + 8 + self.reachable_via.len() * 32 + 1 + 9,
|
||||
);
|
||||
buf.extend_from_slice(&self.identity_key);
|
||||
buf.extend_from_slice(&self.address);
|
||||
@@ -117,6 +145,13 @@ impl MeshAnnounce {
|
||||
buf.extend_from_slice(addr);
|
||||
}
|
||||
buf.push(self.max_hops);
|
||||
// Include keypackage_hash in signature if present
|
||||
if let Some(kp_hash) = &self.keypackage_hash {
|
||||
buf.push(1); // presence marker
|
||||
buf.extend_from_slice(kp_hash);
|
||||
} else {
|
||||
buf.push(0); // absence marker
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user