chie_crypto/
ring.rs

1//! Ring signatures for anonymous signing within a group.
2//!
3//! Ring signatures allow a member of a group to sign a message anonymously
4//! without revealing which member signed it. This is useful for privacy-preserving
5//! content distribution in P2P networks.
6//!
7//! This implementation uses a commitment-based ring signature scheme that provides
8//! computational anonymity within the ring.
9//!
10//! # Example
11//!
12//! ```
13//! use chie_crypto::{KeyPair, ring::{RingSignature, sign_ring, verify_ring}};
14//!
15//! // Create a ring of public keys
16//! let keypair1 = KeyPair::generate();
17//! let keypair2 = KeyPair::generate();
18//! let keypair3 = KeyPair::generate();
19//!
20//! let ring = vec![
21//!     keypair1.public_key(),
22//!     keypair2.public_key(),
23//!     keypair3.public_key(),
24//! ];
25//!
26//! // Signer uses their secret key (keypair2) to sign
27//! let message = b"Anonymous content distribution";
28//! let signature = sign_ring(&keypair2, &ring, message).unwrap();
29//!
30//! // Anyone can verify, but cannot determine which key signed
31//! assert!(verify_ring(&ring, message, &signature).unwrap());
32//! ```
33
34use crate::signing::{KeyPair, PublicKey, verify as verify_signature};
35use blake3;
36use serde::{Deserialize, Serialize};
37use thiserror::Error;
38
39/// Error types for ring signature operations.
40#[derive(Debug, Error)]
41pub enum RingError {
42    #[error("Ring must contain at least 2 public keys")]
43    RingTooSmall,
44
45    #[error("Signer not found in ring")]
46    SignerNotInRing,
47
48    #[error("Invalid ring signature")]
49    InvalidSignature,
50
51    #[error("Serialization error: {0}")]
52    SerializationError(String),
53
54    #[error("Ring size mismatch")]
55    RingSizeMismatch,
56}
57
58pub type RingResult<T> = Result<T, RingError>;
59
60/// A ring signature that proves a message was signed by one member of a group.
61///
62/// This uses a commitment-based approach where each ring member gets a commitment
63/// and the signer proves knowledge of one secret key via a signature.
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct RingSignature {
66    /// Commitments for each ring member
67    commitments: Vec<[u8; 32]>,
68    /// The actual signature (serialized as Vec for serde compatibility)
69    #[serde(with = "serde_signature")]
70    signature: [u8; 64],
71    /// Index hint (optional, can be randomized for additional privacy)
72    hint: u32,
73}
74
75// Serde helper for [u8; 64] signature
76mod serde_signature {
77    use serde::{Deserialize, Deserializer, Serializer};
78
79    pub fn serialize<S>(bytes: &[u8; 64], serializer: S) -> Result<S::Ok, S::Error>
80    where
81        S: Serializer,
82    {
83        serializer.serialize_bytes(bytes)
84    }
85
86    pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 64], D::Error>
87    where
88        D: Deserializer<'de>,
89    {
90        let bytes: Vec<u8> = Deserialize::deserialize(deserializer)?;
91        if bytes.len() != 64 {
92            return Err(serde::de::Error::custom("invalid signature length"));
93        }
94        let mut array = [0u8; 64];
95        array.copy_from_slice(&bytes);
96        Ok(array)
97    }
98}
99
100impl RingSignature {
101    /// Serialize the ring signature to bytes.
102    pub fn to_bytes(&self) -> RingResult<Vec<u8>> {
103        crate::codec::encode(self).map_err(|e| RingError::SerializationError(e.to_string()))
104    }
105
106    /// Deserialize a ring signature from bytes.
107    pub fn from_bytes(bytes: &[u8]) -> RingResult<Self> {
108        crate::codec::decode(bytes).map_err(|e| RingError::SerializationError(e.to_string()))
109    }
110
111    /// Get the number of ring members this signature covers.
112    pub fn ring_size(&self) -> usize {
113        self.commitments.len()
114    }
115}
116
117/// Sign a message with a ring signature.
118///
119/// The signer must be a member of the ring. The resulting signature proves
120/// that one of the ring members signed the message, but doesn't reveal which one.
121///
122/// # Security
123///
124/// This uses a commitment-based ring signature approach. The anonymity holds
125/// computationally assuming the hardness of finding hash collisions.
126pub fn sign_ring(
127    signer: &KeyPair,
128    ring: &[PublicKey],
129    message: &[u8],
130) -> RingResult<RingSignature> {
131    if ring.len() < 2 {
132        return Err(RingError::RingTooSmall);
133    }
134
135    // Find signer's position in ring
136    let signer_pubkey = signer.public_key();
137    let _signer_index = ring
138        .iter()
139        .position(|pk| pk == &signer_pubkey)
140        .ok_or(RingError::SignerNotInRing)?;
141
142    // Create commitments for each ring member
143    let mut commitments = Vec::with_capacity(ring.len());
144
145    for (i, pk) in ring.iter().enumerate() {
146        // Create a commitment that binds the message and ring position
147        let mut hasher = blake3::Hasher::new();
148        hasher.update(b"CHIE-RING-SIG-V1");
149        hasher.update(message);
150        hasher.update(pk);
151        hasher.update(&i.to_le_bytes());
152
153        // Add all ring members for context
154        for ring_pk in ring {
155            hasher.update(ring_pk);
156        }
157
158        commitments.push(*hasher.finalize().as_bytes());
159    }
160
161    // Create the actual signature using signer's key
162    // We sign the concatenation of message and all commitments
163    let mut sig_message = Vec::new();
164    sig_message.extend_from_slice(message);
165    for commitment in &commitments {
166        sig_message.extend_from_slice(commitment);
167    }
168
169    let signature = signer.sign(&sig_message);
170
171    // Use a random hint to avoid leaking signer index
172    let hint = blake3::hash(message).as_bytes()[0] as u32;
173
174    Ok(RingSignature {
175        commitments,
176        signature,
177        hint,
178    })
179}
180
181/// Verify a ring signature.
182///
183/// Returns `Ok(true)` if the signature is valid, meaning it was created by
184/// one of the ring members. Returns `Err` if the signature is invalid or
185/// malformed.
186pub fn verify_ring(
187    ring: &[PublicKey],
188    message: &[u8],
189    signature: &RingSignature,
190) -> RingResult<bool> {
191    if ring.len() < 2 {
192        return Err(RingError::RingTooSmall);
193    }
194
195    if ring.len() != signature.commitments.len() {
196        return Err(RingError::RingSizeMismatch);
197    }
198
199    // Verify commitments match the ring
200    for (i, pk) in ring.iter().enumerate() {
201        let mut hasher = blake3::Hasher::new();
202        hasher.update(b"CHIE-RING-SIG-V1");
203        hasher.update(message);
204        hasher.update(pk);
205        hasher.update(&i.to_le_bytes());
206
207        for ring_pk in ring {
208            hasher.update(ring_pk);
209        }
210
211        let expected_commitment = hasher.finalize();
212        if expected_commitment.as_bytes() != &signature.commitments[i] {
213            return Ok(false);
214        }
215    }
216
217    // Build verification message
218    let mut sig_message = Vec::new();
219    sig_message.extend_from_slice(message);
220    for commitment in &signature.commitments {
221        sig_message.extend_from_slice(commitment);
222    }
223
224    // Try to verify signature with each ring member
225    for pk in ring {
226        if verify_signature(pk, &sig_message, &signature.signature).is_ok() {
227            return Ok(true);
228        }
229    }
230
231    Ok(false)
232}
233
234/// A builder for creating ring signatures with additional context.
235pub struct RingSignatureBuilder {
236    ring: Vec<PublicKey>,
237    context: Vec<u8>,
238}
239
240impl RingSignatureBuilder {
241    /// Create a new ring signature builder.
242    pub fn new() -> Self {
243        Self {
244            ring: Vec::new(),
245            context: Vec::new(),
246        }
247    }
248
249    /// Add a public key to the ring.
250    pub fn add_member(mut self, pubkey: PublicKey) -> Self {
251        self.ring.push(pubkey);
252        self
253    }
254
255    /// Add multiple public keys to the ring.
256    pub fn add_members(mut self, pubkeys: &[PublicKey]) -> Self {
257        self.ring.extend_from_slice(pubkeys);
258        self
259    }
260
261    /// Set application-specific context for domain separation.
262    pub fn with_context(mut self, context: &[u8]) -> Self {
263        self.context = context.to_vec();
264        self
265    }
266
267    /// Sign a message with the configured ring.
268    pub fn sign(self, signer: &KeyPair, message: &[u8]) -> RingResult<RingSignature> {
269        let mut combined_message = self.context;
270        combined_message.extend_from_slice(message);
271        sign_ring(signer, &self.ring, &combined_message)
272    }
273
274    /// Verify a signature with the configured ring.
275    pub fn verify(self, message: &[u8], signature: &RingSignature) -> RingResult<bool> {
276        let mut combined_message = self.context;
277        combined_message.extend_from_slice(message);
278        verify_ring(&self.ring, &combined_message, signature)
279    }
280}
281
282impl Default for RingSignatureBuilder {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291    use crate::signing::KeyPair;
292
293    #[test]
294    fn test_ring_signature_basic() {
295        let keypair1 = KeyPair::generate();
296        let keypair2 = KeyPair::generate();
297        let keypair3 = KeyPair::generate();
298
299        let ring = vec![
300            keypair1.public_key(),
301            keypair2.public_key(),
302            keypair3.public_key(),
303        ];
304
305        let message = b"Test message for ring signature";
306
307        // Sign with keypair2
308        let signature = sign_ring(&keypair2, &ring, message).unwrap();
309
310        // Verify
311        assert!(verify_ring(&ring, message, &signature).unwrap());
312    }
313
314    #[test]
315    fn test_ring_signature_wrong_message() {
316        let keypair1 = KeyPair::generate();
317        let keypair2 = KeyPair::generate();
318        let keypair3 = KeyPair::generate();
319
320        let ring = vec![
321            keypair1.public_key(),
322            keypair2.public_key(),
323            keypair3.public_key(),
324        ];
325
326        let message = b"Original message";
327        let wrong_message = b"Wrong message";
328
329        let signature = sign_ring(&keypair2, &ring, message).unwrap();
330
331        // Should fail with wrong message
332        assert!(!verify_ring(&ring, wrong_message, &signature).unwrap());
333    }
334
335    #[test]
336    fn test_ring_too_small() {
337        let keypair1 = KeyPair::generate();
338        let ring = vec![keypair1.public_key()];
339        let message = b"Test";
340
341        let result = sign_ring(&keypair1, &ring, message);
342        assert!(matches!(result, Err(RingError::RingTooSmall)));
343    }
344
345    #[test]
346    fn test_signer_not_in_ring() {
347        let keypair1 = KeyPair::generate();
348        let keypair2 = KeyPair::generate();
349        let keypair3 = KeyPair::generate();
350        let outsider = KeyPair::generate();
351
352        let ring = vec![
353            keypair1.public_key(),
354            keypair2.public_key(),
355            keypair3.public_key(),
356        ];
357
358        let message = b"Test";
359        let result = sign_ring(&outsider, &ring, message);
360        assert!(matches!(result, Err(RingError::SignerNotInRing)));
361    }
362
363    #[test]
364    fn test_ring_signature_serialization() {
365        let keypair1 = KeyPair::generate();
366        let keypair2 = KeyPair::generate();
367        let keypair3 = KeyPair::generate();
368
369        let ring = vec![
370            keypair1.public_key(),
371            keypair2.public_key(),
372            keypair3.public_key(),
373        ];
374
375        let message = b"Test serialization";
376        let signature = sign_ring(&keypair2, &ring, message).unwrap();
377
378        // Serialize and deserialize
379        let bytes = signature.to_bytes().unwrap();
380        let deserialized = RingSignature::from_bytes(&bytes).unwrap();
381
382        // Verify deserialized signature
383        assert!(verify_ring(&ring, message, &deserialized).unwrap());
384    }
385
386    #[test]
387    fn test_ring_signature_builder() {
388        let keypair1 = KeyPair::generate();
389        let keypair2 = KeyPair::generate();
390        let keypair3 = KeyPair::generate();
391
392        let message = b"Builder test";
393
394        let signature = RingSignatureBuilder::new()
395            .add_member(keypair1.public_key())
396            .add_member(keypair2.public_key())
397            .add_member(keypair3.public_key())
398            .with_context(b"CHIE-PROTOCOL-V1")
399            .sign(&keypair2, message)
400            .unwrap();
401
402        let valid = RingSignatureBuilder::new()
403            .add_member(keypair1.public_key())
404            .add_member(keypair2.public_key())
405            .add_member(keypair3.public_key())
406            .with_context(b"CHIE-PROTOCOL-V1")
407            .verify(message, &signature)
408            .unwrap();
409
410        assert!(valid);
411    }
412
413    #[test]
414    fn test_large_ring() {
415        // Test with larger ring (10 members)
416        let keypairs: Vec<KeyPair> = (0..10).map(|_| KeyPair::generate()).collect();
417        let ring: Vec<PublicKey> = keypairs.iter().map(|kp| kp.public_key()).collect();
418
419        let message = b"Large ring test";
420        let signer = &keypairs[5];
421
422        let signature = sign_ring(signer, &ring, message).unwrap();
423        assert!(verify_ring(&ring, message, &signature).unwrap());
424    }
425
426    #[test]
427    fn test_ring_anonymity() {
428        // Test that different signers in the same ring produce different signatures
429        let keypair1 = KeyPair::generate();
430        let keypair2 = KeyPair::generate();
431        let keypair3 = KeyPair::generate();
432
433        let ring = vec![
434            keypair1.public_key(),
435            keypair2.public_key(),
436            keypair3.public_key(),
437        ];
438
439        let message = b"Anonymity test";
440
441        let sig1 = sign_ring(&keypair1, &ring, message).unwrap();
442        let sig2 = sign_ring(&keypair2, &ring, message).unwrap();
443
444        // Both signatures should verify
445        assert!(verify_ring(&ring, message, &sig1).unwrap());
446        assert!(verify_ring(&ring, message, &sig2).unwrap());
447
448        // Signatures should be different (high probability)
449        assert_ne!(sig1.signature, sig2.signature);
450    }
451}