rust_bottle/membership.rs
1use crate::errors::{BottleError, Result};
2use crate::idcard::IDCard;
3use crate::signing::Sign;
4use rand::RngCore;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// A Membership provides cryptographically signed group affiliations.
9///
10/// Memberships link a member (via their IDCard) to a group (via the group's
11/// public key). They can contain additional information like roles and are
12/// cryptographically signed by the group owner to prove authenticity.
13///
14/// # Example
15///
16/// ```rust
17/// use rust_bottle::*;
18/// use rand::rngs::OsRng;
19///
20/// let rng = &mut OsRng;
21/// let member_key = Ed25519Key::generate(rng);
22/// let member_idcard = IDCard::new(&member_key.public_key_bytes());
23///
24/// let group_key = Ed25519Key::generate(rng);
25/// let mut membership = Membership::new(&member_idcard, &group_key.public_key_bytes());
26/// membership.set_info("role", "admin");
27///
28/// let signed = membership.sign(rng, &group_key).unwrap();
29/// ```
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Membership {
32 /// Member's IDCard (serialized)
33 member_idcard: Vec<u8>, // Serialized IDCard
34 /// Group's public key (identifies the group)
35 group_public_key: Vec<u8>,
36 /// Additional information (e.g., role, department)
37 info: HashMap<String, String>,
38 /// Cryptographic signature (if signed)
39 signature: Option<Vec<u8>>,
40}
41
42impl Membership {
43 /// Create a new membership linking a member to a group.
44 ///
45 /// # Arguments
46 ///
47 /// * `member_idcard` - The member's IDCard
48 /// * `group_public_key` - The group's public key
49 ///
50 /// # Returns
51 ///
52 /// A new `Membership` instance (not yet signed)
53 ///
54 /// # Example
55 ///
56 /// ```rust
57 /// use rust_bottle::*;
58 /// use rand::rngs::OsRng;
59 ///
60 /// let rng = &mut OsRng;
61 /// let member_key = Ed25519Key::generate(rng);
62 /// let member_idcard = IDCard::new(&member_key.public_key_bytes());
63 ///
64 /// let group_key = Ed25519Key::generate(rng);
65 /// let membership = Membership::new(&member_idcard, &group_key.public_key_bytes());
66 /// ```
67 pub fn new(member_idcard: &IDCard, group_public_key: &[u8]) -> Self {
68 Self {
69 member_idcard: member_idcard.to_bytes().unwrap_or_default(), // Should handle error properly
70 group_public_key: group_public_key.to_vec(),
71 info: HashMap::new(),
72 signature: None,
73 }
74 }
75
76 /// Set information key-value pair.
77 ///
78 /// Information fields can store application-specific data like roles,
79 /// departments, or other metadata about the membership.
80 ///
81 /// # Arguments
82 ///
83 /// * `key` - Information key
84 /// * `value` - Information value
85 ///
86 /// # Example
87 ///
88 /// ```rust
89 /// use rust_bottle::*;
90 /// use rand::rngs::OsRng;
91 ///
92 /// let rng = &mut OsRng;
93 /// let member_key = Ed25519Key::generate(rng);
94 /// let member_idcard = IDCard::new(&member_key.public_key_bytes());
95 /// let group_key = Ed25519Key::generate(rng);
96 ///
97 /// let mut membership = Membership::new(&member_idcard, &group_key.public_key_bytes());
98 /// membership.set_info("role", "admin");
99 /// membership.set_info("department", "Engineering");
100 /// ```
101 pub fn set_info(&mut self, key: &str, value: &str) {
102 self.info.insert(key.to_string(), value.to_string());
103 }
104
105 /// Get information value by key.
106 ///
107 /// # Arguments
108 ///
109 /// * `key` - Information key to look up
110 ///
111 /// # Returns
112 ///
113 /// * `Some(&str)` if the key exists
114 /// * `None` if the key is not found
115 pub fn info(&self, key: &str) -> Option<&str> {
116 self.info.get(key).map(|s| s.as_str())
117 }
118
119 /// Sign the membership with a private key.
120 ///
121 /// This creates a cryptographic signature of the membership (excluding
122 /// the signature field itself) and stores it. The signed membership is
123 /// then serialized and returned.
124 ///
125 /// # Arguments
126 ///
127 /// * `rng` - A random number generator
128 /// * `signer` - A signer implementing the `Sign` trait (typically the group owner's key)
129 ///
130 /// # Returns
131 ///
132 /// * `Ok(Vec<u8>)` - Serialized signed membership
133 /// * `Err(BottleError::Serialization)` - If serialization fails
134 /// * `Err(BottleError::VerifyFailed)` - If signing fails
135 ///
136 /// # Example
137 ///
138 /// ```rust
139 /// use rust_bottle::*;
140 /// use rand::rngs::OsRng;
141 ///
142 /// let rng = &mut OsRng;
143 /// let member_key = Ed25519Key::generate(rng);
144 /// let member_idcard = IDCard::new(&member_key.public_key_bytes());
145 /// let group_key = Ed25519Key::generate(rng);
146 ///
147 /// let mut membership = Membership::new(&member_idcard, &group_key.public_key_bytes());
148 /// let signed = membership.sign(rng, &group_key).unwrap();
149 /// ```
150 pub fn sign<R: RngCore>(&mut self, rng: &mut R, signer: &dyn Sign) -> Result<Vec<u8>> {
151 let data_to_sign = self.create_signing_data()?;
152 let signature = signer.sign(rng, &data_to_sign)?;
153 self.signature = Some(signature.clone());
154
155 // Return serialized membership
156 self.to_bytes()
157 }
158
159 /// Verify the membership signature.
160 ///
161 /// # Note
162 ///
163 /// This is a simplified implementation that only checks for the presence
164 /// of a signature. Full verification would require extracting the signing
165 /// key from the group's IDCard and verifying the signature cryptographically.
166 ///
167 /// # Arguments
168 ///
169 /// * `_group_idcard` - The group's IDCard (currently not used)
170 ///
171 /// # Returns
172 ///
173 /// * `Ok(())` - If signature exists
174 /// * `Err(BottleError::VerifyFailed)` - If signature is missing
175 pub fn verify(&self, _group_idcard: &IDCard) -> Result<()> {
176 // Verify signature using group's public key
177 // This is a simplified version - in practice, we'd extract the signing key from the IDCard
178 if self.signature.is_none() {
179 return Err(BottleError::VerifyFailed);
180 }
181
182 // For now, just check that signature exists
183 // Full verification would require the group's private key or a verifier
184 Ok(())
185 }
186
187 /// Create data to sign (everything except the signature field).
188 ///
189 /// This serializes the membership with the signature field set to None,
190 /// which is what gets signed.
191 ///
192 /// # Returns
193 ///
194 /// Serialized membership bytes without the signature
195 fn create_signing_data(&self) -> Result<Vec<u8>> {
196 let mut membership = self.clone();
197 membership.signature = None;
198 bincode::serialize(&membership).map_err(|e| {
199 BottleError::Serialization(format!("Failed to serialize membership: {}", e))
200 })
201 }
202
203 /// Serialize the membership to bytes using bincode.
204 ///
205 /// # Returns
206 ///
207 /// * `Ok(Vec<u8>)` - Serialized membership bytes
208 /// * `Err(BottleError::Serialization)` - If serialization fails
209 pub fn to_bytes(&self) -> Result<Vec<u8>> {
210 bincode::serialize(self).map_err(|e| {
211 BottleError::Serialization(format!("Failed to serialize membership: {}", e))
212 })
213 }
214
215 /// Deserialize a membership from bytes.
216 ///
217 /// # Arguments
218 ///
219 /// * `data` - Serialized membership bytes (from `to_bytes`)
220 ///
221 /// # Returns
222 ///
223 /// * `Ok(Membership)` - Deserialized membership
224 /// * `Err(BottleError::Deserialization)` - If deserialization fails
225 pub fn from_bytes(data: &[u8]) -> Result<Self> {
226 bincode::deserialize(data).map_err(|e| {
227 BottleError::Deserialization(format!("Failed to deserialize membership: {}", e))
228 })
229 }
230}