Skip to main content

moloch_core/
crypto.rs

1//! Cryptographic primitives for Moloch, powered by Arcanum.
2//!
3//! We use Arcanum's optimized implementations:
4//! - BLAKE3 for content hashing (SIMD-accelerated, fast, secure)
5//! - Ed25519 for signatures (fast verification, small signatures)
6//! - Batch verification for 3-8x faster block validation
7
8use std::fmt;
9
10use arcanum_hash::prelude::{Blake3, Hasher as ArcanumHasher};
11use arcanum_signatures::ed25519::Ed25519BatchVerifier;
12use arcanum_signatures::prelude::{
13    Ed25519Signature, Ed25519SigningKey, Ed25519VerifyingKey, Signature as ArcanumSignature,
14    SigningKey as ArcanumSigningKey, VerifyingKey as ArcanumVerifyingKey,
15};
16use arcanum_signatures::BatchVerifier;
17use serde::{Deserialize, Serialize};
18
19use crate::error::{Error, Result};
20
21/// A 32-byte hash value.
22#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
23pub struct Hash([u8; 32]);
24
25impl Hash {
26    /// The zero hash (used as a sentinel).
27    pub const ZERO: Self = Self([0u8; 32]);
28
29    /// Create a hash from raw bytes.
30    pub fn from_bytes(bytes: [u8; 32]) -> Self {
31        Self(bytes)
32    }
33
34    /// Get the raw bytes.
35    pub fn as_bytes(&self) -> &[u8; 32] {
36        &self.0
37    }
38
39    /// Create from hex string.
40    pub fn from_hex(s: &str) -> Result<Self> {
41        let bytes = hex::decode(s)?;
42        if bytes.len() != 32 {
43            return Err(Error::invalid_hash(format!(
44                "expected 32 bytes, got {}",
45                bytes.len()
46            )));
47        }
48        let mut arr = [0u8; 32];
49        arr.copy_from_slice(&bytes);
50        Ok(Self(arr))
51    }
52
53    /// Convert to hex string.
54    pub fn to_hex(&self) -> String {
55        hex::encode(self.0)
56    }
57
58    /// Check if this is the zero hash.
59    pub fn is_zero(&self) -> bool {
60        self == &Self::ZERO
61    }
62}
63
64impl fmt::Debug for Hash {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        write!(f, "Hash({})", &self.to_hex()[..16])
67    }
68}
69
70impl fmt::Display for Hash {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        write!(f, "{}", self.to_hex())
73    }
74}
75
76impl AsRef<[u8]> for Hash {
77    fn as_ref(&self) -> &[u8] {
78        &self.0
79    }
80}
81
82/// Hash arbitrary data using Arcanum BLAKE3 (SIMD-accelerated).
83pub fn hash(data: &[u8]) -> Hash {
84    let output = Blake3::hash(data);
85    let bytes: [u8; 32] = output.to_array().expect("BLAKE3 always outputs 32 bytes");
86    Hash(bytes)
87}
88
89/// Hash two child hashes to produce a parent hash.
90/// Used in merkle tree construction.
91pub fn hash_pair(left: Hash, right: Hash) -> Hash {
92    let mut hasher = Blake3::new();
93    hasher.update(left.as_bytes());
94    hasher.update(right.as_bytes());
95    let output = hasher.finalize();
96    let bytes: [u8; 32] = output.to_array().expect("BLAKE3 always outputs 32 bytes");
97    Hash(bytes)
98}
99
100/// Hash multiple items by concatenating their hashes.
101pub fn hash_all<T: AsRef<[u8]>>(items: &[T]) -> Hash {
102    let mut hasher = Blake3::new();
103    for item in items {
104        hasher.update(item.as_ref());
105    }
106    let output = hasher.finalize();
107    let bytes: [u8; 32] = output.to_array().expect("BLAKE3 always outputs 32 bytes");
108    Hash(bytes)
109}
110
111/// A public key for verifying signatures.
112#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
113pub struct PublicKey(#[serde(with = "public_key_serde")] Ed25519VerifyingKey);
114
115mod public_key_serde {
116    use super::*;
117    use serde::{Deserializer, Serializer};
118
119    pub fn serialize<S: Serializer>(
120        key: &Ed25519VerifyingKey,
121        s: S,
122    ) -> std::result::Result<S::Ok, S::Error> {
123        // Serialize as fixed-size array for bincode compatibility
124        let bytes = ArcanumVerifyingKey::to_bytes(key);
125        let arr: [u8; 32] = bytes
126            .try_into()
127            .map_err(|_| serde::ser::Error::custom("invalid key length"))?;
128        arr.serialize(s)
129    }
130
131    pub fn deserialize<'de, D: Deserializer<'de>>(
132        d: D,
133    ) -> std::result::Result<Ed25519VerifyingKey, D::Error> {
134        let bytes: [u8; 32] = Deserialize::deserialize(d)?;
135        Ed25519VerifyingKey::from_bytes(&bytes).map_err(serde::de::Error::custom)
136    }
137}
138
139impl PublicKey {
140    /// Create from raw bytes.
141    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self> {
142        let key = Ed25519VerifyingKey::from_bytes(bytes)
143            .map_err(|e| Error::invalid_key(e.to_string()))?;
144        Ok(Self(key))
145    }
146
147    /// Get the raw bytes.
148    pub fn as_bytes(&self) -> [u8; 32] {
149        let bytes = ArcanumVerifyingKey::to_bytes(&self.0);
150        bytes
151            .try_into()
152            .expect("Ed25519 public key is always 32 bytes")
153    }
154
155    /// Derive a unique identifier from this key.
156    pub fn id(&self) -> Hash {
157        hash(&self.as_bytes())
158    }
159
160    /// Verify a signature.
161    pub fn verify(&self, message: &[u8], signature: &Sig) -> Result<()> {
162        ArcanumVerifyingKey::verify(&self.0, message, &signature.0)
163            .map_err(|_| Error::invalid_signature())
164    }
165
166    /// Get the inner Ed25519 verifying key for batch operations.
167    pub(crate) fn inner(&self) -> &Ed25519VerifyingKey {
168        &self.0
169    }
170}
171
172impl fmt::Debug for PublicKey {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        write!(f, "PublicKey({})", &hex::encode(&self.as_bytes()[..8]))
175    }
176}
177
178impl std::hash::Hash for PublicKey {
179    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
180        self.as_bytes().hash(state);
181    }
182}
183
184/// A secret key for signing.
185#[derive(Clone)]
186pub struct SecretKey(Ed25519SigningKey);
187
188impl SecretKey {
189    /// Generate a new random key pair.
190    pub fn generate() -> Self {
191        Self(Ed25519SigningKey::generate())
192    }
193
194    /// Create from raw bytes.
195    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self> {
196        let key =
197            Ed25519SigningKey::from_bytes(bytes).map_err(|e| Error::invalid_key(e.to_string()))?;
198        Ok(Self(key))
199    }
200
201    /// Get the raw bytes.
202    pub fn as_bytes(&self) -> [u8; 32] {
203        let bytes = ArcanumSigningKey::to_bytes(&self.0);
204        bytes
205            .try_into()
206            .expect("Ed25519 secret key is always 32 bytes")
207    }
208
209    /// Get the corresponding public key.
210    pub fn public_key(&self) -> PublicKey {
211        PublicKey(ArcanumSigningKey::verifying_key(&self.0))
212    }
213
214    /// Sign a message.
215    pub fn sign(&self, message: &[u8]) -> Sig {
216        Sig(ArcanumSigningKey::sign(&self.0, message))
217    }
218}
219
220impl fmt::Debug for SecretKey {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        write!(f, "SecretKey([redacted])")
223    }
224}
225
226/// A digital signature.
227#[derive(Clone, Serialize, Deserialize)]
228pub struct Sig(#[serde(with = "sig_serde")] Ed25519Signature);
229
230impl PartialEq for Sig {
231    fn eq(&self, other: &Self) -> bool {
232        self.to_bytes() == other.to_bytes()
233    }
234}
235
236impl Eq for Sig {}
237
238mod sig_serde {
239    use super::*;
240    use serde::{Deserializer, Serializer};
241
242    pub fn serialize<S: Serializer>(
243        sig: &Ed25519Signature,
244        s: S,
245    ) -> std::result::Result<S::Ok, S::Error> {
246        // Serialize as two 32-byte arrays for bincode compatibility
247        // (serde only implements for arrays up to 32 elements)
248        let bytes = ArcanumSignature::to_bytes(sig);
249        let (first, second) = bytes.split_at(32);
250        let first: [u8; 32] = first
251            .try_into()
252            .map_err(|_| serde::ser::Error::custom("invalid signature length"))?;
253        let second: [u8; 32] = second
254            .try_into()
255            .map_err(|_| serde::ser::Error::custom("invalid signature length"))?;
256        (first, second).serialize(s)
257    }
258
259    pub fn deserialize<'de, D: Deserializer<'de>>(
260        d: D,
261    ) -> std::result::Result<Ed25519Signature, D::Error> {
262        let (first, second): ([u8; 32], [u8; 32]) = Deserialize::deserialize(d)?;
263        let mut bytes = [0u8; 64];
264        bytes[..32].copy_from_slice(&first);
265        bytes[32..].copy_from_slice(&second);
266        Ed25519Signature::from_bytes(&bytes).map_err(serde::de::Error::custom)
267    }
268}
269
270impl Sig {
271    /// Create an empty/placeholder signature.
272    pub fn empty() -> Self {
273        Self(Ed25519Signature::from_bytes(&[0u8; 64]).expect("zero bytes is valid"))
274    }
275
276    /// Create from raw bytes.
277    pub fn from_bytes(bytes: &[u8; 64]) -> Result<Self> {
278        Ok(Self(
279            Ed25519Signature::from_bytes(bytes).map_err(|_| Error::invalid_signature())?,
280        ))
281    }
282
283    /// Get the raw bytes.
284    pub fn to_bytes(&self) -> [u8; 64] {
285        ArcanumSignature::to_bytes(&self.0)
286            .try_into()
287            .expect("Ed25519 signature is always 64 bytes")
288    }
289
290    /// Check if this is an empty signature.
291    pub fn is_empty(&self) -> bool {
292        self.to_bytes() == [0u8; 64]
293    }
294
295    /// Get the inner Ed25519 signature for batch operations.
296    pub(crate) fn inner(&self) -> &Ed25519Signature {
297        &self.0
298    }
299}
300
301/// Batch-verify multiple signatures at once.
302///
303/// This is 3-8x faster than verifying signatures individually for large batches.
304/// Uses Arcanum's optimized Ed25519 batch verifier with Straus' multi-scalar
305/// multiplication algorithm.
306///
307/// # Arguments
308/// * `items` - Slice of (public_key, message, signature) tuples to verify
309///
310/// # Returns
311/// * `Ok(())` if all signatures are valid
312/// * `Err` if any signature is invalid (does not identify which one)
313///
314/// # Example
315/// ```ignore
316/// let items = events.iter()
317///     .map(|e| (e.attester(), e.canonical_bytes(), e.signature()))
318///     .collect::<Vec<_>>();
319/// batch_verify(&items)?;
320/// ```
321pub fn batch_verify(items: &[(&PublicKey, &[u8], &Sig)]) -> Result<()> {
322    if items.is_empty() {
323        return Ok(());
324    }
325
326    // Convert to Arcanum's expected format
327    let arcanum_items: Vec<(&Ed25519VerifyingKey, &[u8], &Ed25519Signature)> = items
328        .iter()
329        .map(|(pk, msg, sig)| (pk.inner(), *msg, sig.inner()))
330        .collect();
331
332    Ed25519BatchVerifier::verify_batch(&arcanum_items).map_err(|_| Error::invalid_signature())
333}
334
335/// Result of batch verification with identification of invalid signatures.
336#[derive(Debug, Clone)]
337pub struct BatchVerifyResult {
338    /// Indices of items that failed verification.
339    pub invalid_indices: Vec<usize>,
340}
341
342impl BatchVerifyResult {
343    /// Check if all signatures were valid.
344    pub fn all_valid(&self) -> bool {
345        self.invalid_indices.is_empty()
346    }
347}
348
349/// Batch-verify with fallback to identify invalid signatures.
350///
351/// If batch verification fails, falls back to individual verification
352/// to identify which signatures are invalid.
353///
354/// # Performance
355/// - Fast path (all valid): O(n) with batch optimization
356/// - Slow path (some invalid): O(n) sequential verification
357pub fn batch_verify_with_fallback(items: &[(&PublicKey, &[u8], &Sig)]) -> BatchVerifyResult {
358    // Try batch first
359    if batch_verify(items).is_ok() {
360        return BatchVerifyResult {
361            invalid_indices: Vec::new(),
362        };
363    }
364
365    // Fallback: identify which ones are invalid
366    let invalid_indices = items
367        .iter()
368        .enumerate()
369        .filter_map(|(i, (pk, msg, sig))| {
370            if pk.verify(msg, sig).is_err() {
371                Some(i)
372            } else {
373                None
374            }
375        })
376        .collect();
377
378    BatchVerifyResult { invalid_indices }
379}
380
381impl fmt::Debug for Sig {
382    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383        write!(f, "Sig({})", &hex::encode(&self.to_bytes()[..8]))
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::*;
390
391    #[test]
392    fn test_sig_bincode_roundtrip() {
393        let key = SecretKey::generate();
394        let sig = key.sign(b"test message");
395
396        // Serialize
397        let bytes = bincode::serialize(&sig).expect("serialize should work");
398        println!("Serialized sig size: {} bytes", bytes.len());
399
400        // Deserialize
401        let restored: Sig = bincode::deserialize(&bytes).expect("deserialize should work");
402
403        assert_eq!(sig.to_bytes(), restored.to_bytes());
404    }
405
406    #[test]
407    fn test_pubkey_bincode_roundtrip() {
408        let key = SecretKey::generate();
409        let pk = key.public_key();
410
411        // Serialize
412        let bytes = bincode::serialize(&pk).expect("serialize should work");
413        println!("Serialized pubkey size: {} bytes", bytes.len());
414
415        // Deserialize
416        let restored: PublicKey = bincode::deserialize(&bytes).expect("deserialize should work");
417
418        assert_eq!(pk.as_bytes(), restored.as_bytes());
419    }
420
421    #[test]
422    fn test_hash_basic() {
423        let h1 = hash(b"hello");
424        let h2 = hash(b"hello");
425        let h3 = hash(b"world");
426
427        assert_eq!(h1, h2);
428        assert_ne!(h1, h3);
429        assert!(!h1.is_zero());
430        assert!(Hash::ZERO.is_zero());
431    }
432
433    #[test]
434    fn test_hash_matches_blake3() {
435        // Verify we get the same output as direct blake3
436        let h = hash(b"hello");
437        // BLAKE3 hash of "hello" is known
438        assert_eq!(
439            h.to_hex(),
440            "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f"
441        );
442    }
443
444    #[test]
445    fn test_hash_hex_roundtrip() {
446        let h = hash(b"test data");
447        let hex_str = h.to_hex();
448        let h2 = Hash::from_hex(&hex_str).unwrap();
449        assert_eq!(h, h2);
450    }
451
452    #[test]
453    fn test_hash_pair_order_matters() {
454        let a = hash(b"a");
455        let b = hash(b"b");
456
457        let ab = hash_pair(a, b);
458        let ba = hash_pair(b, a);
459
460        assert_ne!(ab, ba);
461    }
462
463    #[test]
464    fn test_sign_verify() {
465        let sk = SecretKey::generate();
466        let pk = sk.public_key();
467
468        let message = b"audit event data";
469        let sig = sk.sign(message);
470
471        assert!(pk.verify(message, &sig).is_ok());
472        assert!(pk.verify(b"wrong message", &sig).is_err());
473    }
474
475    #[test]
476    fn test_key_id_deterministic() {
477        let sk = SecretKey::generate();
478        let pk = sk.public_key();
479
480        let id1 = pk.id();
481        let id2 = pk.id();
482
483        assert_eq!(id1, id2);
484    }
485
486    #[test]
487    fn test_secret_key_roundtrip() {
488        let sk = SecretKey::generate();
489        let bytes = sk.as_bytes();
490        let restored = SecretKey::from_bytes(&bytes).unwrap();
491
492        // Verify they produce same public key
493        assert_eq!(sk.public_key().as_bytes(), restored.public_key().as_bytes());
494    }
495}