Skip to main content

ave_identity/keys/
keypair.rs

1//! Algorithm-agnostic key pair wrapper.
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::CryptoError;
6use std::fmt;
7
8use super::{DSA, DSAlgorithm, Ed25519Signer, PublicKey, SignatureIdentifier};
9
10/// Key pair algorithms supported by this crate.
11#[derive(
12    Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default,
13)]
14pub enum KeyPairAlgorithm {
15    /// Ed25519 elliptic curve signature scheme.
16    #[default]
17    Ed25519,
18}
19
20impl From<DSAlgorithm> for KeyPairAlgorithm {
21    fn from(algo: DSAlgorithm) -> Self {
22        match algo {
23            DSAlgorithm::Ed25519 => Self::Ed25519,
24        }
25    }
26}
27
28impl From<KeyPairAlgorithm> for DSAlgorithm {
29    fn from(kp_type: KeyPairAlgorithm) -> Self {
30        match kp_type {
31            KeyPairAlgorithm::Ed25519 => Self::Ed25519,
32        }
33    }
34}
35
36impl KeyPairAlgorithm {
37    /// Generates a random key pair for this algorithm.
38    pub fn generate_keypair(&self) -> Result<KeyPair, CryptoError> {
39        KeyPair::generate(*self)
40    }
41}
42
43impl fmt::Display for KeyPairAlgorithm {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        match self {
46            Self::Ed25519 => write!(f, "Ed25519"),
47        }
48    }
49}
50
51/// Owns a signing key and exposes a stable high-level API.
52///
53/// Use this type when the caller should not depend on a concrete algorithm.
54#[derive(Clone)]
55pub enum KeyPair {
56    Ed25519(Ed25519Signer),
57}
58
59impl KeyPair {
60    /// Generates a random key pair for `key_type`.
61    pub fn generate(key_type: KeyPairAlgorithm) -> Result<Self, CryptoError> {
62        match key_type {
63            KeyPairAlgorithm::Ed25519 => {
64                Ed25519Signer::generate().map(KeyPair::Ed25519)
65            }
66        }
67    }
68
69    /// Imports a key pair from PKCS#8 DER bytes.
70    ///
71    /// The algorithm is detected from the OID stored in the DER structure.
72    pub fn from_secret_der(der: &[u8]) -> Result<Self, CryptoError> {
73        use pkcs8::{ObjectIdentifier, PrivateKeyInfo};
74
75        // Parse the DER structure
76        let private_key_info = PrivateKeyInfo::try_from(der)
77            .map_err(|e| CryptoError::InvalidDerFormat(e.to_string()))?;
78
79        // Get the algorithm OID
80        let oid = private_key_info.algorithm.oid;
81
82        // Ed25519 OID: 1.3.101.112
83        const ED25519_OID: ObjectIdentifier =
84            ObjectIdentifier::new_unwrap("1.3.101.112");
85
86        // Match OID to algorithm
87        if oid == ED25519_OID {
88            // Extract the secret key bytes from the OCTET STRING
89            let secret_key = private_key_info.private_key;
90
91            // Ed25519 keys in PKCS#8 are wrapped in an OCTET STRING
92            // The first byte should be 0x04 (OCTET STRING tag), followed by length
93            if secret_key.len() < 2 || secret_key[0] != 0x04 {
94                return Err(CryptoError::InvalidSecretKey(
95                    "Invalid Ed25519 key encoding in DER".to_string(),
96                ));
97            }
98
99            let key_length = secret_key[1] as usize;
100            if secret_key.len() < 2 + key_length {
101                return Err(CryptoError::InvalidSecretKey(
102                    "Truncated Ed25519 key in DER".to_string(),
103                ));
104            }
105
106            let actual_key = &secret_key[2..2 + key_length];
107            Ed25519Signer::from_secret_key(actual_key).map(KeyPair::Ed25519)
108        } else {
109            Err(CryptoError::UnsupportedAlgorithm(format!(
110                "Algorithm with OID {} is not supported",
111                oid
112            )))
113        }
114    }
115
116    /// Builds a key pair from an exact 32-byte seed.
117    pub fn from_seed(
118        key_type: KeyPairAlgorithm,
119        seed: &[u8; 32],
120    ) -> Result<Self, CryptoError> {
121        match key_type {
122            KeyPairAlgorithm::Ed25519 => {
123                Ed25519Signer::from_seed(seed).map(KeyPair::Ed25519)
124            }
125        }
126    }
127
128    /// Derives a deterministic key pair from arbitrary bytes.
129    pub fn derive_from_data(
130        key_type: KeyPairAlgorithm,
131        data: &[u8],
132    ) -> Result<Self, CryptoError> {
133        match key_type {
134            KeyPairAlgorithm::Ed25519 => {
135                Ed25519Signer::derive_from_data(data).map(KeyPair::Ed25519)
136            }
137        }
138    }
139
140    /// Imports a key pair from raw secret key bytes.
141    ///
142    /// For explicit algorithm selection, use [`Self::from_secret_key_with_type`].
143    pub fn from_secret_key(secret_key: &[u8]) -> Result<Self, CryptoError> {
144        // Try to detect algorithm from key length
145        match secret_key.len() {
146            32 | 64 => {
147                Ed25519Signer::from_secret_key(secret_key).map(KeyPair::Ed25519)
148            }
149            _ => Err(CryptoError::InvalidSecretKey(format!(
150                "Unsupported key length: {} bytes",
151                secret_key.len()
152            ))),
153        }
154    }
155
156    /// Imports a key pair from raw secret key bytes and an explicit algorithm.
157    pub fn from_secret_key_with_type(
158        key_type: KeyPairAlgorithm,
159        secret_key: &[u8],
160    ) -> Result<Self, CryptoError> {
161        match key_type {
162            KeyPairAlgorithm::Ed25519 => {
163                Ed25519Signer::from_secret_key(secret_key).map(KeyPair::Ed25519)
164            }
165        }
166    }
167
168    /// Returns the key pair algorithm.
169    #[inline]
170    pub const fn key_type(&self) -> KeyPairAlgorithm {
171        match self {
172            Self::Ed25519(_) => KeyPairAlgorithm::Ed25519,
173        }
174    }
175
176    /// Signs `message`.
177    #[inline]
178    pub fn sign(
179        &self,
180        message: &[u8],
181    ) -> Result<SignatureIdentifier, CryptoError> {
182        match self {
183            Self::Ed25519(signer) => signer.sign(message),
184        }
185    }
186
187    /// Returns the signature algorithm used by this key pair.
188    #[inline]
189    pub fn algorithm(&self) -> DSAlgorithm {
190        match self {
191            Self::Ed25519(signer) => signer.algorithm(),
192        }
193    }
194
195    /// Returns the one-byte identifier for the algorithm.
196    #[inline]
197    pub fn algorithm_id(&self) -> u8 {
198        match self {
199            Self::Ed25519(signer) => signer.algorithm_id(),
200        }
201    }
202
203    /// Returns the raw public key bytes.
204    #[inline]
205    pub fn public_key_bytes(&self) -> Vec<u8> {
206        match self {
207            Self::Ed25519(signer) => signer.public_key_bytes(),
208        }
209    }
210
211    /// Returns the public key wrapper.
212    #[inline]
213    pub fn public_key(&self) -> PublicKey {
214        PublicKey::new(self.algorithm(), self.public_key_bytes())
215            .expect("KeyPair should always have valid public key")
216    }
217
218    /// Returns the raw secret key bytes.
219    #[inline]
220    pub fn secret_key_bytes(&self) -> Result<Vec<u8>, CryptoError> {
221        match self {
222            Self::Ed25519(signer) => signer.secret_key_bytes(),
223        }
224    }
225
226    /// Serializes the key pair as `identifier || secret_key`.
227    ///
228    /// This exposes the secret key material.
229    pub fn to_bytes(&self) -> Result<Vec<u8>, CryptoError> {
230        let secret = self.secret_key_bytes()?;
231        let mut result = Vec::with_capacity(1 + secret.len());
232        result.push(self.algorithm_id());
233        result.extend_from_slice(&secret);
234        Ok(result)
235    }
236
237    /// Deserializes a key pair from `identifier || secret_key`.
238    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
239        if bytes.is_empty() {
240            return Err(CryptoError::InvalidSecretKey(
241                "Data too short to contain algorithm identifier".to_string(),
242            ));
243        }
244
245        let id = bytes[0];
246        let algorithm = DSAlgorithm::from_identifier(id)?;
247        let key_type = KeyPairAlgorithm::from(algorithm);
248        let secret_key = &bytes[1..];
249
250        Self::from_secret_key_with_type(key_type, secret_key)
251    }
252
253    /// Exports the secret key as PKCS#8 DER.
254    pub fn to_secret_der(&self) -> Result<Vec<u8>, CryptoError> {
255        use pkcs8::{ObjectIdentifier, PrivateKeyInfo, der::Encode};
256
257        const ED25519_OID: ObjectIdentifier =
258            ObjectIdentifier::new_unwrap("1.3.101.112");
259
260        let secret_key_bytes = self.secret_key_bytes()?;
261
262        // Wrap the key in an OCTET STRING (0x04 tag)
263        let mut wrapped_key = Vec::with_capacity(2 + secret_key_bytes.len());
264        wrapped_key.push(0x04); // OCTET STRING tag
265        wrapped_key.push(secret_key_bytes.len() as u8); // length
266        wrapped_key.extend_from_slice(&secret_key_bytes);
267
268        let algorithm_identifier = pkcs8::AlgorithmIdentifierRef {
269            oid: ED25519_OID,
270            parameters: None,
271        };
272
273        let private_key_info = PrivateKeyInfo {
274            algorithm: algorithm_identifier,
275            private_key: &wrapped_key,
276            public_key: None,
277        };
278
279        private_key_info.to_der().map_err(|e| {
280            CryptoError::InvalidSecretKey(format!("DER encoding failed: {}", e))
281        })
282    }
283}
284
285impl Default for KeyPair {
286    fn default() -> Self {
287        Self::Ed25519(Ed25519Signer::default())
288    }
289}
290
291impl fmt::Debug for KeyPair {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        use crate::common::base64_encoding;
294        f.debug_struct("KeyPair")
295            .field("type", &self.key_type())
296            .field("algorithm", &self.algorithm())
297            .field(
298                "public_key",
299                &base64_encoding::encode(&self.public_key_bytes()),
300            )
301            .finish_non_exhaustive()
302    }
303}
304
305impl fmt::Display for KeyPair {
306    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307        write!(f, "{:?} KeyPair", self.key_type())
308    }
309}
310
311// Implement DSA trait for KeyPair to make it fully interchangeable
312impl DSA for KeyPair {
313    #[inline]
314    fn algorithm_id(&self) -> u8 {
315        Self::algorithm_id(self)
316    }
317
318    #[inline]
319    fn signature_length(&self) -> usize {
320        match self {
321            Self::Ed25519(signer) => signer.signature_length(),
322        }
323    }
324
325    #[inline]
326    fn sign(&self, message: &[u8]) -> Result<SignatureIdentifier, CryptoError> {
327        Self::sign(self, message)
328    }
329
330    #[inline]
331    fn algorithm(&self) -> DSAlgorithm {
332        Self::algorithm(self)
333    }
334
335    #[inline]
336    fn public_key_bytes(&self) -> Vec<u8> {
337        Self::public_key_bytes(self)
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use super::*;
344
345    #[test]
346    fn test_keypair_generate() {
347        let keypair = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
348        assert_eq!(keypair.algorithm(), DSAlgorithm::Ed25519);
349        assert_eq!(keypair.key_type(), KeyPairAlgorithm::Ed25519);
350        assert_eq!(keypair.public_key_bytes().len(), 32);
351    }
352
353    #[test]
354    fn test_keypair_sign_verify() {
355        let keypair = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
356        let message = b"Test message";
357
358        let signature = keypair.sign(message).unwrap();
359        let public_key = keypair.public_key();
360
361        assert!(public_key.verify(message, &signature).is_ok());
362        assert!(public_key.verify(b"Wrong message", &signature).is_err());
363    }
364
365    #[test]
366    fn test_keypair_from_seed() {
367        let seed = [42u8; 32];
368        let keypair1 =
369            KeyPair::from_seed(KeyPairAlgorithm::Ed25519, &seed).unwrap();
370        let keypair2 =
371            KeyPair::from_seed(KeyPairAlgorithm::Ed25519, &seed).unwrap();
372
373        // Same seed should produce same keys
374        assert_eq!(keypair1.public_key_bytes(), keypair2.public_key_bytes());
375    }
376
377    #[test]
378    fn test_keypair_derive_from_data() {
379        let data = b"my passphrase";
380        let keypair1 =
381            KeyPair::derive_from_data(KeyPairAlgorithm::Ed25519, data).unwrap();
382        let keypair2 =
383            KeyPair::derive_from_data(KeyPairAlgorithm::Ed25519, data).unwrap();
384
385        // Same data should produce same keys
386        assert_eq!(keypair1.public_key_bytes(), keypair2.public_key_bytes());
387
388        // Different data should produce different keys
389        let keypair3 =
390            KeyPair::derive_from_data(KeyPairAlgorithm::Ed25519, b"different")
391                .unwrap();
392        assert_ne!(keypair1.public_key_bytes(), keypair3.public_key_bytes());
393    }
394
395    #[test]
396    fn test_keypair_serialization() {
397        let keypair = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
398        let message = b"Test message";
399
400        // Serialize
401        let bytes = keypair.to_bytes().unwrap();
402        assert_eq!(bytes[0], b'E'); // Ed25519 identifier
403
404        // Deserialize
405        let keypair2 = KeyPair::from_bytes(&bytes).unwrap();
406
407        // Should produce same signatures
408        let sig1 = keypair.sign(message).unwrap();
409        let sig2 = keypair2.sign(message).unwrap();
410
411        // Both should verify correctly
412        let public_key = keypair.public_key();
413        assert!(public_key.verify(message, &sig1).is_ok());
414        assert!(public_key.verify(message, &sig2).is_ok());
415    }
416
417    #[test]
418    fn test_keypair_dsa_trait() {
419        let keypair = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
420        let message = b"Test message";
421
422        // Use DSA trait methods
423        let signature = DSA::sign(&keypair, message).unwrap();
424        assert_eq!(DSA::algorithm(&keypair), DSAlgorithm::Ed25519);
425        assert_eq!(DSA::algorithm_id(&keypair), b'E');
426
427        // Verify
428        let public_key = keypair.public_key();
429        assert!(public_key.verify(message, &signature).is_ok());
430    }
431
432    #[test]
433    fn test_keypair_public_key_wrapper() {
434        let keypair = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
435        let public_key = keypair.public_key();
436
437        assert_eq!(public_key.algorithm(), keypair.algorithm());
438        assert_eq!(public_key.as_bytes(), &keypair.public_key_bytes()[..]);
439    }
440
441    #[test]
442    fn test_keypair_from_secret_key_autodetect() {
443        let keypair1 = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
444        let secret_bytes = keypair1.secret_key_bytes().unwrap();
445
446        // Auto-detect should work
447        let keypair2 = KeyPair::from_secret_key(&secret_bytes).unwrap();
448
449        assert_eq!(keypair1.public_key_bytes(), keypair2.public_key_bytes());
450    }
451
452    #[test]
453    fn test_keypair_type_conversion() {
454        let kp_type = KeyPairAlgorithm::Ed25519;
455        let algo: DSAlgorithm = kp_type.into();
456        assert_eq!(algo, DSAlgorithm::Ed25519);
457
458        let kp_type2: KeyPairAlgorithm = algo.into();
459        assert_eq!(kp_type, kp_type2);
460    }
461
462    #[test]
463    fn test_keypair_algorithm_generate() {
464        let algorithm = KeyPairAlgorithm::Ed25519;
465        let keypair = algorithm.generate_keypair().unwrap();
466
467        assert_eq!(keypair.key_type(), KeyPairAlgorithm::Ed25519);
468        assert_eq!(keypair.algorithm(), DSAlgorithm::Ed25519);
469
470        // Should be able to sign
471        let message = b"test";
472        let signature = keypair.sign(message).unwrap();
473        let public_key = keypair.public_key();
474        assert!(public_key.verify(message, &signature).is_ok());
475    }
476
477    #[test]
478    fn test_keypair_algorithm_display() {
479        let algorithm = KeyPairAlgorithm::Ed25519;
480        assert_eq!(algorithm.to_string(), "Ed25519");
481    }
482
483    #[test]
484    fn test_default_keypair() {
485        let keypair = KeyPair::default();
486        assert_eq!(keypair.key_type(), KeyPairAlgorithm::Ed25519);
487    }
488
489    #[test]
490    fn test_keypair_clone() {
491        // Test that cloning works correctly
492        let keypair = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
493        let keypair_clone = keypair.clone();
494
495        // Both should have the same public key
496        assert_eq!(
497            keypair.public_key_bytes(),
498            keypair_clone.public_key_bytes()
499        );
500
501        // Both should sign the same way
502        let message = b"test message";
503        let sig1 = keypair.sign(message).unwrap();
504        let sig2 = keypair_clone.sign(message).unwrap();
505
506        // Signatures should be identical (deterministic)
507        assert_eq!(sig1, sig2);
508
509        // Both signatures should verify
510        let public_key = keypair.public_key();
511        assert!(public_key.verify(message, &sig1).is_ok());
512        assert!(public_key.verify(message, &sig2).is_ok());
513    }
514
515    #[test]
516    fn test_keypair_der_roundtrip() {
517        // Test DER serialization and deserialization
518        let keypair1 = KeyPair::generate(KeyPairAlgorithm::Ed25519).unwrap();
519        let message = b"Test message for DER roundtrip";
520
521        // Serialize to DER
522        let der_bytes = keypair1.to_secret_der().unwrap();
523
524        // Verify it starts with DER SEQUENCE tag
525        assert_eq!(der_bytes[0], 0x30); // SEQUENCE tag
526
527        // Deserialize from DER
528        let keypair2 = KeyPair::from_secret_der(&der_bytes).unwrap();
529
530        // Should have the same public key
531        assert_eq!(keypair1.public_key_bytes(), keypair2.public_key_bytes());
532
533        // Should produce verifiable signatures
534        let sig1 = keypair1.sign(message).unwrap();
535        let sig2 = keypair2.sign(message).unwrap();
536
537        let public_key = keypair1.public_key();
538        assert!(public_key.verify(message, &sig1).is_ok());
539        assert!(public_key.verify(message, &sig2).is_ok());
540    }
541
542    #[test]
543    fn test_keypair_from_der_invalid() {
544        // Test error handling for invalid DER data
545        let invalid_der = vec![0x00, 0x01, 0x02];
546        let result = KeyPair::from_secret_der(&invalid_der);
547        assert!(result.is_err());
548        assert!(matches!(
549            result.unwrap_err(),
550            CryptoError::InvalidDerFormat(_)
551        ));
552    }
553
554    #[test]
555    fn test_keypair_from_der_unsupported_algorithm() {
556        // Create a valid DER structure but with an unsupported OID
557        use pkcs8::{ObjectIdentifier, PrivateKeyInfo, der::Encode};
558
559        // Use a different OID (e.g., secp256k1: 1.3.132.0.10)
560        let unsupported_oid = ObjectIdentifier::new_unwrap("1.3.132.0.10");
561
562        let fake_key = vec![0x04, 0x20]; // OCTET STRING tag + length
563        let fake_key = [&fake_key[..], &[0u8; 32]].concat();
564
565        let algorithm_identifier = pkcs8::AlgorithmIdentifierRef {
566            oid: unsupported_oid,
567            parameters: None,
568        };
569
570        let private_key_info = PrivateKeyInfo {
571            algorithm: algorithm_identifier,
572            private_key: &fake_key,
573            public_key: None,
574        };
575
576        let der_bytes = private_key_info.to_der().unwrap();
577
578        let result = KeyPair::from_secret_der(&der_bytes);
579        assert!(result.is_err());
580        assert!(matches!(
581            result.unwrap_err(),
582            CryptoError::UnsupportedAlgorithm(_)
583        ));
584    }
585}