rust_crypto_utils/
signatures.rs

1//! Digital signatures using Ed25519 and HMAC
2
3use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
4use hmac::{Hmac, Mac};
5use rand::rngs::OsRng;
6use sha2::Sha256;
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9type HmacSha256 = Hmac<Sha256>;
10
11/// Ed25519 signing key pair
12#[derive(ZeroizeOnDrop)]
13pub struct Ed25519KeyPair {
14    signing_key: SigningKey,
15}
16
17impl Ed25519KeyPair {
18    /// Generate a new random key pair
19    pub fn generate() -> Self {
20        use rand::RngCore;
21        let mut seed = [0u8; 32];
22        OsRng.fill_bytes(&mut seed);
23        let signing_key = SigningKey::from_bytes(&seed);
24        Self { signing_key }
25    }
26
27    /// Create from seed bytes (32 bytes)
28    pub fn from_seed(seed: &[u8; 32]) -> Self {
29        let signing_key = SigningKey::from_bytes(seed);
30        Self { signing_key }
31    }
32
33    /// Sign a message
34    pub fn sign(&self, message: &[u8]) -> Vec<u8> {
35        let signature = self.signing_key.sign(message);
36        signature.to_bytes().to_vec()
37    }
38
39    /// Get public key
40    pub fn public_key(&self) -> Ed25519PublicKey {
41        Ed25519PublicKey {
42            verifying_key: self.signing_key.verifying_key(),
43        }
44    }
45
46    /// Export signing key bytes (use with extreme caution)
47    pub fn to_bytes(&self) -> [u8; 32] {
48        self.signing_key.to_bytes()
49    }
50}
51
52/// Ed25519 public key for verification
53#[derive(Clone)]
54pub struct Ed25519PublicKey {
55    verifying_key: VerifyingKey,
56}
57
58impl Ed25519PublicKey {
59    /// Create from bytes
60    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, String> {
61        VerifyingKey::from_bytes(bytes)
62            .map(|verifying_key| Self { verifying_key })
63            .map_err(|e| format!("Invalid public key: {}", e))
64    }
65
66    /// Verify a signature
67    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), String> {
68        if signature.len() != 64 {
69            return Err("Invalid signature length".to_string());
70        }
71
72        let sig_array: [u8; 64] = signature.try_into()
73            .map_err(|_| "Failed to convert signature")?;
74        let signature = Signature::from_bytes(&sig_array);
75
76        self.verifying_key
77            .verify(message, &signature)
78            .map_err(|e| format!("Verification failed: {}", e))
79    }
80
81    /// Export public key bytes
82    pub fn to_bytes(&self) -> [u8; 32] {
83        self.verifying_key.to_bytes()
84    }
85}
86
87/// HMAC-based message authentication
88#[derive(Zeroize, ZeroizeOnDrop)]
89pub struct HmacKey {
90    key: Vec<u8>,
91}
92
93impl HmacKey {
94    /// Create HMAC key from bytes
95    pub fn new(key: Vec<u8>) -> Self {
96        Self { key }
97    }
98
99    /// Generate random HMAC key (32 bytes)
100    pub fn generate() -> Self {
101        let mut key = vec![0u8; 32];
102        rand::RngCore::fill_bytes(&mut OsRng, &mut key);
103        Self { key }
104    }
105
106    /// Generate HMAC tag for message
107    pub fn sign(&self, message: &[u8]) -> Vec<u8> {
108        let mut mac = HmacSha256::new_from_slice(&self.key)
109            .expect("HMAC key length error");
110        mac.update(message);
111        mac.finalize().into_bytes().to_vec()
112    }
113
114    /// Verify HMAC tag
115    pub fn verify(&self, message: &[u8], tag: &[u8]) -> Result<(), String> {
116        let mut mac = HmacSha256::new_from_slice(&self.key)
117            .map_err(|e| format!("HMAC error: {}", e))?;
118        mac.update(message);
119        mac.verify_slice(tag)
120            .map_err(|_| "HMAC verification failed".to_string())
121    }
122
123    /// Get key bytes
124    pub fn as_bytes(&self) -> &[u8] {
125        &self.key
126    }
127}
128
129/// Digital signature suite for multi-purpose signing
130pub struct SignatureSuite {
131    ed25519_keypair: Option<Ed25519KeyPair>,
132    hmac_key: Option<HmacKey>,
133}
134
135impl SignatureSuite {
136    /// Create new suite with Ed25519
137    pub fn new_ed25519() -> Self {
138        Self {
139            ed25519_keypair: Some(Ed25519KeyPair::generate()),
140            hmac_key: None,
141        }
142    }
143
144    /// Create new suite with HMAC
145    pub fn new_hmac() -> Self {
146        Self {
147            ed25519_keypair: None,
148            hmac_key: Some(HmacKey::generate()),
149        }
150    }
151
152    /// Create suite with both Ed25519 and HMAC
153    pub fn new_combined() -> Self {
154        Self {
155            ed25519_keypair: Some(Ed25519KeyPair::generate()),
156            hmac_key: Some(HmacKey::generate()),
157        }
158    }
159
160    /// Sign with Ed25519 (returns None if not configured)
161    pub fn sign_ed25519(&self, message: &[u8]) -> Option<Vec<u8>> {
162        self.ed25519_keypair.as_ref().map(|kp| kp.sign(message))
163    }
164
165    /// Sign with HMAC (returns None if not configured)
166    pub fn sign_hmac(&self, message: &[u8]) -> Option<Vec<u8>> {
167        self.hmac_key.as_ref().map(|key| key.sign(message))
168    }
169
170    /// Get Ed25519 public key
171    pub fn ed25519_public_key(&self) -> Option<Ed25519PublicKey> {
172        self.ed25519_keypair.as_ref().map(|kp| kp.public_key())
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn test_ed25519_sign_verify() {
182        let keypair = Ed25519KeyPair::generate();
183        let message = b"Test message for signing";
184
185        let signature = keypair.sign(message);
186        assert_eq!(signature.len(), 64);
187
188        let public_key = keypair.public_key();
189        assert!(public_key.verify(message, &signature).is_ok());
190    }
191
192    #[test]
193    fn test_ed25519_verify_failure() {
194        let keypair = Ed25519KeyPair::generate();
195        let message = b"Original message";
196        let tampered = b"Tampered message";
197
198        let signature = keypair.sign(message);
199        let public_key = keypair.public_key();
200
201        assert!(public_key.verify(tampered, &signature).is_err());
202    }
203
204    #[test]
205    fn test_ed25519_from_seed() {
206        let seed = [42u8; 32];
207        let keypair1 = Ed25519KeyPair::from_seed(&seed);
208        let keypair2 = Ed25519KeyPair::from_seed(&seed);
209
210        let message = b"Test";
211        let sig1 = keypair1.sign(message);
212        let sig2 = keypair2.sign(message);
213
214        assert_eq!(sig1, sig2);
215    }
216
217    #[test]
218    fn test_hmac_sign_verify() {
219        let key = HmacKey::generate();
220        let message = b"Test message";
221
222        let tag = key.sign(message);
223        assert!(key.verify(message, &tag).is_ok());
224    }
225
226    #[test]
227    fn test_hmac_verify_failure() {
228        let key = HmacKey::generate();
229        let message = b"Original message";
230        let tampered = b"Tampered message";
231
232        let tag = key.sign(message);
233        assert!(key.verify(tampered, &tag).is_err());
234    }
235
236    #[test]
237    fn test_hmac_different_keys() {
238        let key1 = HmacKey::generate();
239        let key2 = HmacKey::generate();
240        let message = b"Test";
241
242        let tag1 = key1.sign(message);
243        assert!(key2.verify(message, &tag1).is_err());
244    }
245
246    #[test]
247    fn test_signature_suite_ed25519() {
248        let suite = SignatureSuite::new_ed25519();
249        let message = b"Test message";
250
251        let signature = suite.sign_ed25519(message).expect("Signing failed");
252        let public_key = suite.ed25519_public_key().expect("No public key");
253
254        assert!(public_key.verify(message, &signature).is_ok());
255    }
256
257    #[test]
258    fn test_signature_suite_hmac() {
259        let suite = SignatureSuite::new_hmac();
260        let message = b"Test message";
261
262        let tag = suite.sign_hmac(message).expect("HMAC signing failed");
263        assert!(!tag.is_empty());
264    }
265
266    #[test]
267    fn test_signature_suite_combined() {
268        let suite = SignatureSuite::new_combined();
269        let message = b"Test message";
270
271        assert!(suite.sign_ed25519(message).is_some());
272        assert!(suite.sign_hmac(message).is_some());
273    }
274
275    #[test]
276    fn test_ed25519_public_key_serialization() {
277        let keypair = Ed25519KeyPair::generate();
278        let public_key = keypair.public_key();
279
280        let bytes = public_key.to_bytes();
281        let restored = Ed25519PublicKey::from_bytes(&bytes).unwrap();
282
283        let message = b"Test";
284        let signature = keypair.sign(message);
285
286        assert!(restored.verify(message, &signature).is_ok());
287    }
288}