Skip to main content

libgrite_core/
signing.rs

1//! Ed25519 signing and verification for events
2//!
3//! Signatures are detached - they sign the 32-byte event_id, not the full event.
4//! This allows verification independent of serialization format.
5
6use ed25519_dalek::{SigningKey, VerifyingKey, Signature, Signer, Verifier};
7use rand::rngs::OsRng;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11use crate::types::ids::EventId;
12use crate::types::event::Event;
13
14/// Ed25519 signing key pair
15pub struct SigningKeyPair {
16    signing_key: SigningKey,
17}
18
19impl SigningKeyPair {
20    /// Generate a new random Ed25519 key pair
21    pub fn generate() -> Self {
22        let signing_key = SigningKey::generate(&mut OsRng);
23        Self { signing_key }
24    }
25
26    /// Create from a 32-byte seed (hex-encoded)
27    pub fn from_seed_hex(seed_hex: &str) -> Result<Self, SigningError> {
28        let seed_bytes = hex::decode(seed_hex)
29            .map_err(|e| SigningError::KeyParseError(e.to_string()))?;
30
31        if seed_bytes.len() != 32 {
32            return Err(SigningError::KeyParseError(
33                format!("Seed must be 32 bytes, got {}", seed_bytes.len())
34            ));
35        }
36
37        let mut seed_array = [0u8; 32];
38        seed_array.copy_from_slice(&seed_bytes);
39
40        let signing_key = SigningKey::from_bytes(&seed_array);
41        Ok(Self { signing_key })
42    }
43
44    /// Get the seed as hex (for storage)
45    pub fn seed_hex(&self) -> String {
46        hex::encode(self.signing_key.to_bytes())
47    }
48
49    /// Get the public key as hex
50    pub fn public_key_hex(&self) -> String {
51        hex::encode(self.signing_key.verifying_key().to_bytes())
52    }
53
54    /// Get the verifying key
55    pub fn verifying_key(&self) -> VerifyingKey {
56        self.signing_key.verifying_key()
57    }
58
59    /// Sign an event ID (32 bytes)
60    pub fn sign(&self, event_id: &EventId) -> Vec<u8> {
61        let signature = self.signing_key.sign(event_id);
62        signature.to_bytes().to_vec()
63    }
64
65    /// Sign an event, returning the signature
66    pub fn sign_event(&self, event: &Event) -> Vec<u8> {
67        self.sign(&event.event_id)
68    }
69}
70
71/// Signature verification policy
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
73#[serde(rename_all = "lowercase")]
74pub enum VerificationPolicy {
75    /// No signature verification
76    #[default]
77    Off,
78    /// Warn on missing or invalid signatures but continue
79    Warn,
80    /// Require valid signatures on all events
81    Require,
82}
83
84impl VerificationPolicy {
85    /// Parse from string
86    pub fn from_str(s: &str) -> Option<Self> {
87        match s.to_lowercase().as_str() {
88            "off" => Some(VerificationPolicy::Off),
89            "warn" => Some(VerificationPolicy::Warn),
90            "require" => Some(VerificationPolicy::Require),
91            _ => None,
92        }
93    }
94
95    /// Convert to string
96    pub fn as_str(&self) -> &'static str {
97        match self {
98            VerificationPolicy::Off => "off",
99            VerificationPolicy::Warn => "warn",
100            VerificationPolicy::Require => "require",
101        }
102    }
103}
104
105/// Errors that can occur during signing or verification
106#[derive(Debug, Error)]
107pub enum SigningError {
108    #[error("signature missing")]
109    SignatureMissing,
110
111    #[error("invalid signature")]
112    InvalidSignature,
113
114    #[error("public key not found for actor {0}")]
115    PublicKeyNotFound(String),
116
117    #[error("key parse error: {0}")]
118    KeyParseError(String),
119
120    #[error("signature parse error: {0}")]
121    SignatureParseError(String),
122}
123
124/// Verify an event signature against a public key
125pub fn verify_signature(event: &Event, public_key_hex: &str) -> Result<(), SigningError> {
126    // Get signature from event
127    let sig_bytes = event.sig.as_ref()
128        .ok_or(SigningError::SignatureMissing)?;
129
130    // Parse public key
131    let pk_bytes = hex::decode(public_key_hex)
132        .map_err(|e| SigningError::KeyParseError(e.to_string()))?;
133
134    if pk_bytes.len() != 32 {
135        return Err(SigningError::KeyParseError(
136            format!("Public key must be 32 bytes, got {}", pk_bytes.len())
137        ));
138    }
139
140    let mut pk_array = [0u8; 32];
141    pk_array.copy_from_slice(&pk_bytes);
142
143    let verifying_key = VerifyingKey::from_bytes(&pk_array)
144        .map_err(|e| SigningError::KeyParseError(e.to_string()))?;
145
146    // Parse signature
147    if sig_bytes.len() != 64 {
148        return Err(SigningError::SignatureParseError(
149            format!("Signature must be 64 bytes, got {}", sig_bytes.len())
150        ));
151    }
152
153    let mut sig_array = [0u8; 64];
154    sig_array.copy_from_slice(sig_bytes);
155
156    let signature = Signature::from_bytes(&sig_array);
157
158    // Verify
159    verifying_key.verify(&event.event_id, &signature)
160        .map_err(|_| SigningError::InvalidSignature)
161}
162
163/// Verify a raw signature against event_id and public key
164pub fn verify_raw(
165    event_id: &EventId,
166    signature: &[u8],
167    public_key_hex: &str,
168) -> Result<(), SigningError> {
169    // Parse public key
170    let pk_bytes = hex::decode(public_key_hex)
171        .map_err(|e| SigningError::KeyParseError(e.to_string()))?;
172
173    if pk_bytes.len() != 32 {
174        return Err(SigningError::KeyParseError(
175            format!("Public key must be 32 bytes, got {}", pk_bytes.len())
176        ));
177    }
178
179    let mut pk_array = [0u8; 32];
180    pk_array.copy_from_slice(&pk_bytes);
181
182    let verifying_key = VerifyingKey::from_bytes(&pk_array)
183        .map_err(|e| SigningError::KeyParseError(e.to_string()))?;
184
185    // Parse signature
186    if signature.len() != 64 {
187        return Err(SigningError::SignatureParseError(
188            format!("Signature must be 64 bytes, got {}", signature.len())
189        ));
190    }
191
192    let mut sig_array = [0u8; 64];
193    sig_array.copy_from_slice(signature);
194
195    let sig = Signature::from_bytes(&sig_array);
196
197    // Verify
198    verifying_key.verify(event_id, &sig)
199        .map_err(|_| SigningError::InvalidSignature)
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use crate::types::event::EventKind;
206
207    #[test]
208    fn test_keypair_generation() {
209        let keypair = SigningKeyPair::generate();
210        let seed = keypair.seed_hex();
211        let pk = keypair.public_key_hex();
212
213        // Seed should be 64 hex chars (32 bytes)
214        assert_eq!(seed.len(), 64);
215        // Public key should be 64 hex chars (32 bytes)
216        assert_eq!(pk.len(), 64);
217    }
218
219    #[test]
220    fn test_keypair_from_seed() {
221        let keypair1 = SigningKeyPair::generate();
222        let seed = keypair1.seed_hex();
223
224        let keypair2 = SigningKeyPair::from_seed_hex(&seed).unwrap();
225
226        // Same seed should produce same public key
227        assert_eq!(keypair1.public_key_hex(), keypair2.public_key_hex());
228    }
229
230    #[test]
231    fn test_sign_and_verify() {
232        let keypair = SigningKeyPair::generate();
233        let event_id: EventId = [42u8; 32];
234
235        let signature = keypair.sign(&event_id);
236
237        // Signature should be 64 bytes
238        assert_eq!(signature.len(), 64);
239
240        // Verification should succeed
241        let result = verify_raw(&event_id, &signature, &keypair.public_key_hex());
242        assert!(result.is_ok());
243    }
244
245    #[test]
246    fn test_sign_event() {
247        let keypair = SigningKeyPair::generate();
248
249        let mut event = Event::new(
250            [1u8; 32],
251            [2u8; 16],
252            [3u8; 16],
253            1700000000000,
254            None,
255            EventKind::IssueCreated {
256                title: "Test".to_string(),
257                body: "Body".to_string(),
258                labels: vec![],
259            },
260        );
261
262        event.sig = Some(keypair.sign_event(&event));
263
264        // Verification should succeed
265        let result = verify_signature(&event, &keypair.public_key_hex());
266        assert!(result.is_ok());
267    }
268
269    #[test]
270    fn test_verify_missing_signature() {
271        let keypair = SigningKeyPair::generate();
272
273        let event = Event::new(
274            [1u8; 32],
275            [2u8; 16],
276            [3u8; 16],
277            1700000000000,
278            None,
279            EventKind::CommentAdded { body: "test".to_string() },
280        );
281
282        let result = verify_signature(&event, &keypair.public_key_hex());
283        assert!(matches!(result, Err(SigningError::SignatureMissing)));
284    }
285
286    #[test]
287    fn test_verify_invalid_signature() {
288        let keypair = SigningKeyPair::generate();
289
290        let mut event = Event::new(
291            [1u8; 32],
292            [2u8; 16],
293            [3u8; 16],
294            1700000000000,
295            None,
296            EventKind::CommentAdded { body: "test".to_string() },
297        );
298
299        // Set invalid signature (wrong bytes)
300        event.sig = Some(vec![0u8; 64]);
301
302        let result = verify_signature(&event, &keypair.public_key_hex());
303        assert!(matches!(result, Err(SigningError::InvalidSignature)));
304    }
305
306    #[test]
307    fn test_verify_wrong_public_key() {
308        let keypair1 = SigningKeyPair::generate();
309        let keypair2 = SigningKeyPair::generate();
310
311        let mut event = Event::new(
312            [1u8; 32],
313            [2u8; 16],
314            [3u8; 16],
315            1700000000000,
316            None,
317            EventKind::CommentAdded { body: "test".to_string() },
318        );
319
320        // Sign with keypair1
321        event.sig = Some(keypair1.sign_event(&event));
322
323        // Verify with keypair2's public key - should fail
324        let result = verify_signature(&event, &keypair2.public_key_hex());
325        assert!(matches!(result, Err(SigningError::InvalidSignature)));
326    }
327
328    #[test]
329    fn test_verification_policy_parse() {
330        assert_eq!(VerificationPolicy::from_str("off"), Some(VerificationPolicy::Off));
331        assert_eq!(VerificationPolicy::from_str("warn"), Some(VerificationPolicy::Warn));
332        assert_eq!(VerificationPolicy::from_str("require"), Some(VerificationPolicy::Require));
333        assert_eq!(VerificationPolicy::from_str("REQUIRE"), Some(VerificationPolicy::Require));
334        assert_eq!(VerificationPolicy::from_str("invalid"), None);
335    }
336}