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}