Skip to main content

alimentar/format/
signing.rs

1//! Digital signing support for .ald format (ยง5.2)
2//!
3//! Provides Ed25519 signatures for dataset integrity and authenticity
4//! verification.
5
6use crate::error::{Error, Result};
7
8/// Ed25519 public key size (32 bytes)
9pub const PUBLIC_KEY_SIZE: usize = 32;
10
11/// Ed25519 signature size (64 bytes)
12pub const SIGNATURE_SIZE: usize = 64;
13
14/// Signing key pair for creating signatures
15#[cfg(feature = "format-signing")]
16#[derive(Clone)]
17pub struct SigningKeyPair {
18    signing_key: ed25519_dalek::SigningKey,
19}
20
21#[cfg(feature = "format-signing")]
22impl std::fmt::Debug for SigningKeyPair {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        // Show first 8 bytes of public key as hex for identification
25        let pk = self.public_key_bytes();
26        write!(
27            f,
28            "SigningKeyPair {{ public_key: {:02x}{:02x}{:02x}{:02x}..., secret_key: [REDACTED] }}",
29            pk[0], pk[1], pk[2], pk[3]
30        )
31    }
32}
33
34#[cfg(feature = "format-signing")]
35impl SigningKeyPair {
36    /// Generate a new random signing key pair
37    ///
38    /// # Errors
39    ///
40    /// Returns error if random number generation fails.
41    pub fn generate() -> Result<Self> {
42        let mut key_bytes = [0u8; 32];
43        getrandom::getrandom(&mut key_bytes)
44            .map_err(|e| Error::Format(format!("RNG error: {e}")))?;
45        let signing_key = ed25519_dalek::SigningKey::from_bytes(&key_bytes);
46        Ok(Self { signing_key })
47    }
48
49    /// Create a signing key pair from existing secret key bytes
50    #[must_use]
51    pub fn from_bytes(secret_key: [u8; 32]) -> Self {
52        Self {
53            signing_key: ed25519_dalek::SigningKey::from_bytes(&secret_key),
54        }
55    }
56
57    /// Get the public key bytes for this key pair
58    #[must_use]
59    pub fn public_key_bytes(&self) -> [u8; 32] {
60        self.signing_key.verifying_key().to_bytes()
61    }
62
63    /// Get the secret key bytes
64    #[must_use]
65    pub fn secret_key_bytes(&self) -> [u8; 32] {
66        self.signing_key.to_bytes()
67    }
68
69    /// Sign a message
70    ///
71    /// Returns a 64-byte Ed25519 signature.
72    #[must_use]
73    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
74        use ed25519_dalek::Signer;
75        self.signing_key.sign(message).to_bytes()
76    }
77}
78
79/// Verify an Ed25519 signature
80///
81/// # Arguments
82///
83/// * `message` - The signed message
84/// * `signature` - The 64-byte signature
85/// * `public_key` - The 32-byte public key
86///
87/// # Errors
88///
89/// Returns error if the signature is invalid.
90#[cfg(feature = "format-signing")]
91pub fn verify(message: &[u8], signature: &[u8; 64], public_key: &[u8; 32]) -> Result<()> {
92    use ed25519_dalek::{Signature, Verifier, VerifyingKey};
93
94    let verifying_key = VerifyingKey::from_bytes(public_key)
95        .map_err(|e| Error::Format(format!("Invalid public key: {e}")))?;
96
97    let sig = Signature::from_bytes(signature);
98
99    verifying_key
100        .verify(message, &sig)
101        .map_err(|_| Error::Format("Signature verification failed".to_string()))
102}
103
104/// Dataset signature block
105#[derive(Debug, Clone)]
106pub struct SignatureBlock {
107    /// The Ed25519 signature (64 bytes)
108    pub signature: [u8; 64],
109    /// The signer's public key (32 bytes)
110    pub public_key: [u8; 32],
111}
112
113impl SignatureBlock {
114    /// Size of the signature block in bytes (64 + 32 = 96)
115    pub const SIZE: usize = SIGNATURE_SIZE + PUBLIC_KEY_SIZE;
116
117    /// Create a new signature block by signing data
118    #[cfg(feature = "format-signing")]
119    #[must_use]
120    pub fn sign(data: &[u8], key_pair: &SigningKeyPair) -> Self {
121        Self {
122            signature: key_pair.sign(data),
123            public_key: key_pair.public_key_bytes(),
124        }
125    }
126
127    /// Verify the signature against the given data
128    ///
129    /// # Errors
130    ///
131    /// Returns error if signature verification fails.
132    #[cfg(feature = "format-signing")]
133    pub fn verify(&self, data: &[u8]) -> Result<()> {
134        verify(data, &self.signature, &self.public_key)
135    }
136
137    /// Serialize to bytes (96 bytes: signature + public_key)
138    #[must_use]
139    pub fn to_bytes(&self) -> [u8; Self::SIZE] {
140        let mut buf = [0u8; Self::SIZE];
141        buf[..SIGNATURE_SIZE].copy_from_slice(&self.signature);
142        buf[SIGNATURE_SIZE..].copy_from_slice(&self.public_key);
143        buf
144    }
145
146    /// Deserialize from bytes
147    ///
148    /// # Errors
149    ///
150    /// Returns error if buffer is too small.
151    pub fn from_bytes(buf: &[u8]) -> Result<Self> {
152        if buf.len() < Self::SIZE {
153            return Err(Error::Format(format!(
154                "Signature block too small: {} bytes, expected {}",
155                buf.len(),
156                Self::SIZE
157            )));
158        }
159
160        let mut signature = [0u8; SIGNATURE_SIZE];
161        let mut public_key = [0u8; PUBLIC_KEY_SIZE];
162        signature.copy_from_slice(&buf[..SIGNATURE_SIZE]);
163        public_key.copy_from_slice(&buf[SIGNATURE_SIZE..Self::SIZE]);
164
165        Ok(Self {
166            signature,
167            public_key,
168        })
169    }
170}
171
172#[cfg(all(test, feature = "format-signing"))]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn test_sign_verify_roundtrip() {
178        let key_pair = SigningKeyPair::generate().expect("keygen failed");
179        let message = b"Hello, World! This is a test message.";
180
181        let signature = key_pair.sign(message);
182        let public_key = key_pair.public_key_bytes();
183
184        verify(message, &signature, &public_key).expect("verification failed");
185    }
186
187    #[test]
188    fn test_wrong_message_fails() {
189        let key_pair = SigningKeyPair::generate().expect("keygen failed");
190        let message = b"Original message";
191        let wrong_message = b"Wrong message";
192
193        let signature = key_pair.sign(message);
194        let public_key = key_pair.public_key_bytes();
195
196        let result = verify(wrong_message, &signature, &public_key);
197        assert!(result.is_err());
198    }
199
200    #[test]
201    fn test_wrong_key_fails() {
202        let key_pair1 = SigningKeyPair::generate().expect("keygen 1 failed");
203        let key_pair2 = SigningKeyPair::generate().expect("keygen 2 failed");
204        let message = b"Test message";
205
206        let signature = key_pair1.sign(message);
207        let wrong_public_key = key_pair2.public_key_bytes();
208
209        let result = verify(message, &signature, &wrong_public_key);
210        assert!(result.is_err());
211    }
212
213    #[test]
214    fn test_signature_block_roundtrip() {
215        let key_pair = SigningKeyPair::generate().expect("keygen failed");
216        let data = b"Dataset content to sign";
217
218        let block = SignatureBlock::sign(data, &key_pair);
219        block.verify(data).expect("verify failed");
220
221        // Test serialization
222        let bytes = block.to_bytes();
223        assert_eq!(bytes.len(), SignatureBlock::SIZE);
224
225        let restored = SignatureBlock::from_bytes(&bytes).expect("parse failed");
226        restored.verify(data).expect("restored verify failed");
227    }
228
229    #[test]
230    fn test_from_bytes_keypair() {
231        let key_pair1 = SigningKeyPair::generate().expect("keygen failed");
232        let secret_bytes = key_pair1.secret_key_bytes();
233
234        let key_pair2 = SigningKeyPair::from_bytes(secret_bytes);
235        assert_eq!(key_pair1.public_key_bytes(), key_pair2.public_key_bytes());
236
237        // Both should produce identical signatures
238        let message = b"Test determinism";
239        assert_eq!(key_pair1.sign(message), key_pair2.sign(message));
240    }
241
242    #[test]
243    fn test_different_keys_produce_different_signatures() {
244        let key_pair1 = SigningKeyPair::generate().expect("keygen 1 failed");
245        let key_pair2 = SigningKeyPair::generate().expect("keygen 2 failed");
246        let message = b"Same message";
247
248        let sig1 = key_pair1.sign(message);
249        let sig2 = key_pair2.sign(message);
250
251        assert_ne!(sig1, sig2);
252    }
253
254    #[test]
255    fn test_signature_block_from_bytes_too_small() {
256        let buf = [0u8; 10]; // Too small
257        let result = SignatureBlock::from_bytes(&buf);
258        assert!(result.is_err());
259        let err_msg = format!("{}", result.unwrap_err());
260        assert!(err_msg.contains("too small"));
261    }
262
263    #[test]
264    fn test_constants() {
265        assert_eq!(PUBLIC_KEY_SIZE, 32);
266        assert_eq!(SIGNATURE_SIZE, 64);
267        assert_eq!(SignatureBlock::SIZE, 96);
268    }
269
270    #[test]
271    fn test_signing_key_pair_clone() {
272        let key_pair = SigningKeyPair::generate().expect("keygen failed");
273        let cloned = key_pair.clone();
274        assert_eq!(key_pair.public_key_bytes(), cloned.public_key_bytes());
275        assert_eq!(key_pair.secret_key_bytes(), cloned.secret_key_bytes());
276    }
277
278    #[test]
279    fn test_signing_key_pair_debug() {
280        let key_pair = SigningKeyPair::generate().expect("keygen failed");
281        let debug = format!("{:?}", key_pair);
282        assert!(debug.contains("SigningKeyPair"));
283        assert!(debug.contains("REDACTED"));
284    }
285
286    #[test]
287    fn test_signature_block_clone() {
288        let key_pair = SigningKeyPair::generate().expect("keygen failed");
289        let data = b"Test data";
290        let block = SignatureBlock::sign(data, &key_pair);
291        let cloned = block.clone();
292        assert_eq!(cloned.signature, block.signature);
293        assert_eq!(cloned.public_key, block.public_key);
294    }
295
296    #[test]
297    fn test_signature_block_debug() {
298        let key_pair = SigningKeyPair::generate().expect("keygen failed");
299        let block = SignatureBlock::sign(b"test", &key_pair);
300        let debug = format!("{:?}", block);
301        assert!(debug.contains("SignatureBlock"));
302    }
303
304    #[test]
305    fn test_verify_invalid_public_key() {
306        let message = b"Test message";
307        let signature = [0u8; 64];
308        // All zeros is not a valid public key for Ed25519
309        let invalid_pk = [0u8; 32];
310        let result = verify(message, &signature, &invalid_pk);
311        assert!(result.is_err());
312    }
313
314    #[test]
315    fn test_empty_message_signing() {
316        let key_pair = SigningKeyPair::generate().expect("keygen failed");
317        let message = b"";
318        let signature = key_pair.sign(message);
319        let public_key = key_pair.public_key_bytes();
320        verify(message, &signature, &public_key).expect("empty message verify failed");
321    }
322
323    #[test]
324    fn test_large_message_signing() {
325        let key_pair = SigningKeyPair::generate().expect("keygen failed");
326        let message = vec![0xABu8; 1024 * 1024]; // 1MB
327        let signature = key_pair.sign(&message);
328        let public_key = key_pair.public_key_bytes();
329        verify(&message, &signature, &public_key).expect("large message verify failed");
330    }
331}