rust_bottle/
pkix.rs

1//! PKIX/PKCS#8 Key Serialization
2//!
3//! This module provides functions to marshal and unmarshal cryptographic keys
4//! in standard PKIX (SubjectPublicKeyInfo) and PKCS#8 formats. These formats
5//! enable interoperability with other cryptographic tools and libraries.
6//!
7//! # Supported Formats
8//!
9//! - **PKCS#8**: Private key format (RFC 5208)
10//! - **PKIX/SPKI**: Public key format (RFC 5280)
11//! - **PEM**: Base64-encoded DER with headers/footers
12//! - **DER**: Binary ASN.1 encoding
13//!
14//! # Example
15//!
16//! ```rust
17//! use rust_bottle::keys::EcdsaP256Key;
18//! use rust_bottle::pkix;
19//! use rand::rngs::OsRng;
20//!
21//! let rng = &mut OsRng;
22//! let key = EcdsaP256Key::generate(rng);
23//!
24//! // Marshal public key to PKIX format
25//! let pkix_der = pkix::marshal_pkix_public_key(&key.public_key_bytes()).unwrap();
26//! let pkix_pem = pkix::marshal_pkix_public_key_pem(&key.public_key_bytes()).unwrap();
27//!
28//! // Marshal private key to PKCS#8 format
29//! let pkcs8_der = pkix::marshal_pkcs8_private_key(&key.private_key_bytes(), pkix::KeyType::EcdsaP256).unwrap();
30//! let pkcs8_pem = pkix::marshal_pkcs8_private_key_pem(&key.private_key_bytes(), pkix::KeyType::EcdsaP256).unwrap();
31//!
32//! // Unmarshal keys
33//! let pub_key = pkix::parse_pkix_public_key(&pkix_der).unwrap();
34//! let priv_key = pkix::parse_pkcs8_private_key(&pkcs8_der, pkix::KeyType::EcdsaP256).unwrap();
35//! ```
36
37use crate::errors::{BottleError, Result};
38use const_oid::{db::rfc5912, ObjectIdentifier};
39use der::{Decode, Encode};
40use pkcs8::{AlgorithmIdentifierRef, PrivateKeyInfo};
41use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};
42
43/// Key type identifier for PKCS#8/PKIX serialization
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum KeyType {
46    /// ECDSA P-256 (secp256r1)
47    EcdsaP256,
48    /// ECDSA P-384 (secp384r1)
49    EcdsaP384,
50    /// ECDSA P-521 (secp521r1)
51    EcdsaP521,
52    /// Ed25519
53    Ed25519,
54    /// X25519
55    X25519,
56    /// RSA (PKCS#1)
57    Rsa,
58    /// ML-KEM-768 (requires `ml-kem` feature)
59    #[cfg(feature = "ml-kem")]
60    MlKem768,
61    /// ML-KEM-1024 (requires `ml-kem` feature)
62    #[cfg(feature = "ml-kem")]
63    MlKem1024,
64    /// ML-DSA-44 (requires `post-quantum` feature)
65    #[cfg(feature = "post-quantum")]
66    MlDsa44,
67    /// ML-DSA-65 (requires `post-quantum` feature)
68    #[cfg(feature = "post-quantum")]
69    MlDsa65,
70    /// ML-DSA-87 (requires `post-quantum` feature)
71    #[cfg(feature = "post-quantum")]
72    MlDsa87,
73    /// SLH-DSA-128s (requires `post-quantum` feature)
74    #[cfg(feature = "post-quantum")]
75    SlhDsa128s,
76    /// SLH-DSA-128f (requires `post-quantum` feature)
77    #[cfg(feature = "post-quantum")]
78    SlhDsa128f,
79    /// SLH-DSA-192s (requires `post-quantum` feature)
80    #[cfg(feature = "post-quantum")]
81    SlhDsa192s,
82    /// SLH-DSA-192f (requires `post-quantum` feature)
83    #[cfg(feature = "post-quantum")]
84    SlhDsa192f,
85    /// SLH-DSA-256s (requires `post-quantum` feature)
86    #[cfg(feature = "post-quantum")]
87    SlhDsa256s,
88    /// SLH-DSA-256f (requires `post-quantum` feature)
89    #[cfg(feature = "post-quantum")]
90    SlhDsa256f,
91    /// SLH-DSA-SHA2-128s (requires `post-quantum` feature)
92    #[cfg(feature = "post-quantum")]
93    SlhDsaSha2_128s,
94    /// SLH-DSA-SHA2-128f (requires `post-quantum` feature)
95    #[cfg(feature = "post-quantum")]
96    SlhDsaSha2_128f,
97    /// SLH-DSA-SHA2-192s (requires `post-quantum` feature)
98    #[cfg(feature = "post-quantum")]
99    SlhDsaSha2_192s,
100    /// SLH-DSA-SHA2-192f (requires `post-quantum` feature)
101    #[cfg(feature = "post-quantum")]
102    SlhDsaSha2_192f,
103    /// SLH-DSA-SHA2-256s (requires `post-quantum` feature)
104    #[cfg(feature = "post-quantum")]
105    SlhDsaSha2_256s,
106    /// SLH-DSA-SHA2-256f (requires `post-quantum` feature)
107    #[cfg(feature = "post-quantum")]
108    SlhDsaSha2_256f,
109}
110
111impl KeyType {
112    /// Get the OID (Object Identifier) for this key type
113    fn oid(&self) -> ObjectIdentifier {
114        match self {
115            KeyType::EcdsaP256 => rfc5912::ID_EC_PUBLIC_KEY, // ecPublicKey
116            KeyType::EcdsaP384 => rfc5912::ID_EC_PUBLIC_KEY, // ecPublicKey
117            KeyType::EcdsaP521 => rfc5912::ID_EC_PUBLIC_KEY, // ecPublicKey
118            KeyType::Ed25519 => ObjectIdentifier::new("1.3.101.112").expect("Invalid Ed25519 OID"), // Ed25519
119            KeyType::X25519 => ObjectIdentifier::new("1.3.101.110").expect("Invalid X25519 OID"), // X25519
120            KeyType::Rsa => rfc5912::RSA_ENCRYPTION, // rsaEncryption
121            #[cfg(feature = "ml-kem")]
122            KeyType::MlKem768 | KeyType::MlKem1024 => {
123                // ML-KEM OID (NIST standard) - placeholder, actual OID may differ
124                // Using a temporary OID structure - update with official OID when available
125                // Note: This is a placeholder OID - update when NIST publishes official OIDs
126                ObjectIdentifier::new("1.3.6.1.4.1.2.267.7.6.5").expect("Invalid ML-KEM OID")
127            }
128            #[cfg(feature = "post-quantum")]
129            KeyType::MlDsa44 | KeyType::MlDsa65 | KeyType::MlDsa87 => {
130                // ML-DSA OID (NIST standard) - placeholder, actual OID may differ
131                // Note: This is a placeholder OID - update when NIST publishes official OIDs
132                ObjectIdentifier::new("1.3.6.1.4.1.2.267.7.4.4").expect("Invalid ML-DSA OID")
133            }
134            #[cfg(feature = "post-quantum")]
135            KeyType::SlhDsa128s | KeyType::SlhDsa128f | KeyType::SlhDsa192s | KeyType::SlhDsa192f | KeyType::SlhDsa256s | KeyType::SlhDsa256f
136            | KeyType::SlhDsaSha2_128s | KeyType::SlhDsaSha2_128f | KeyType::SlhDsaSha2_192s | KeyType::SlhDsaSha2_192f | KeyType::SlhDsaSha2_256s | KeyType::SlhDsaSha2_256f => {
137                // SLH-DSA OID (NIST standard) - placeholder, actual OID may differ
138                // Note: This is a placeholder OID - update when NIST publishes official OIDs
139                ObjectIdentifier::new("1.3.6.1.4.1.2.267.1.16.7").expect("Invalid SLH-DSA OID")
140            }
141        }
142    }
143
144    /// Get the curve OID for ECDSA keys
145    #[allow(dead_code)]
146    fn curve_oid(&self) -> Option<&'static [u32]> {
147        match self {
148            KeyType::EcdsaP256 => Some(&[1, 2, 840, 10045, 3, 1, 7]), // prime256v1
149            KeyType::EcdsaP384 => Some(&[1, 3, 132, 0, 34]),          // secp384r1
150            KeyType::EcdsaP521 => Some(&[1, 3, 132, 0, 35]),          // secp521r1
151            _ => None,
152        }
153    }
154}
155
156/// Marshal a public key to PKIX (SubjectPublicKeyInfo) format in DER encoding.
157///
158/// This function encodes a public key in the standard PKIX format, which is
159/// compatible with OpenSSL, other cryptographic libraries, and tools.
160///
161/// # Arguments
162///
163/// * `public_key_bytes` - Raw public key bytes (format depends on key type)
164/// * `key_type` - The type of key being marshaled
165///
166/// # Returns
167///
168/// * `Ok(Vec<u8>)` - DER-encoded PKIX public key
169/// * `Err(BottleError)` - If encoding fails
170///
171/// # Example
172///
173/// ```rust
174/// use rust_bottle::keys::EcdsaP256Key;
175/// use rust_bottle::pkix;
176/// use rand::rngs::OsRng;
177///
178/// let rng = &mut OsRng;
179/// let key = EcdsaP256Key::generate(rng);
180/// let pkix_der = pkix::marshal_pkix_public_key(&key.public_key_bytes()).unwrap();
181/// ```
182pub fn marshal_pkix_public_key(public_key_bytes: &[u8]) -> Result<Vec<u8>> {
183    // Try to detect key type from the bytes
184    let key_type = detect_key_type_from_public_key(public_key_bytes)?;
185    marshal_pkix_public_key_with_type(public_key_bytes, key_type)
186}
187
188/// Marshal a public key to PKIX format with explicit key type.
189///
190/// # Arguments
191///
192/// * `public_key_bytes` - Raw public key bytes
193/// * `key_type` - The type of key being marshaled
194///
195/// # Returns
196///
197/// * `Ok(Vec<u8>)` - DER-encoded PKIX public key
198/// * `Err(BottleError)` - If encoding fails
199pub fn marshal_pkix_public_key_with_type(
200    public_key_bytes: &[u8],
201    key_type: KeyType,
202) -> Result<Vec<u8>> {
203    match key_type {
204        KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => {
205            marshal_ecdsa_pkix(public_key_bytes, key_type)
206        }
207        KeyType::Ed25519 => marshal_ed25519_pkix(public_key_bytes),
208        KeyType::X25519 => marshal_x25519_pkix(public_key_bytes),
209        KeyType::Rsa => marshal_rsa_pkix(public_key_bytes),
210        #[cfg(feature = "ml-kem")]
211        KeyType::MlKem768 | KeyType::MlKem1024 => marshal_mlkem_pkix(public_key_bytes, key_type),
212        #[cfg(feature = "post-quantum")]
213        KeyType::MlDsa44 | KeyType::MlDsa65 | KeyType::MlDsa87 => {
214            marshal_mldsa_pkix(public_key_bytes, key_type)
215        }
216        #[cfg(feature = "post-quantum")]
217        KeyType::SlhDsa128s | KeyType::SlhDsa128f | KeyType::SlhDsa192s | KeyType::SlhDsa192f | KeyType::SlhDsa256s | KeyType::SlhDsa256f
218        | KeyType::SlhDsaSha2_128s | KeyType::SlhDsaSha2_128f | KeyType::SlhDsaSha2_192s | KeyType::SlhDsaSha2_192f | KeyType::SlhDsaSha2_256s | KeyType::SlhDsaSha2_256f => {
219            marshal_slhdsa_pkix(public_key_bytes, key_type)
220        }
221    }
222}
223
224/// Marshal a public key to PKIX format in PEM encoding.
225///
226/// # Arguments
227///
228/// * `public_key_bytes` - Raw public key bytes
229///
230/// # Returns
231///
232/// * `Ok(String)` - PEM-encoded PKIX public key
233/// * `Err(BottleError)` - If encoding fails
234///
235/// # Example
236///
237/// ```rust
238/// use rust_bottle::keys::EcdsaP256Key;
239/// use rust_bottle::pkix;
240/// use rand::rngs::OsRng;
241///
242/// let rng = &mut OsRng;
243/// let key = EcdsaP256Key::generate(rng);
244/// let pkix_pem = pkix::marshal_pkix_public_key_pem(&key.public_key_bytes()).unwrap();
245/// ```
246pub fn marshal_pkix_public_key_pem(public_key_bytes: &[u8]) -> Result<String> {
247    let der = marshal_pkix_public_key(public_key_bytes)?;
248    let pem = pem::encode(&pem::Pem::new("PUBLIC KEY", der));
249    Ok(pem)
250}
251
252/// Parse a PKIX (SubjectPublicKeyInfo) public key from DER encoding.
253///
254/// # Arguments
255///
256/// * `der_bytes` - DER-encoded PKIX public key
257/// * `key_type` - Expected key type (for validation)
258///
259/// # Returns
260///
261/// * `Ok(Vec<u8>)` - Raw public key bytes
262/// * `Err(BottleError)` - If parsing fails
263///
264/// # Example
265///
266/// ```rust
267/// use rust_bottle::keys::EcdsaP256Key;
268/// use rust_bottle::pkix;
269/// use rand::rngs::OsRng;
270///
271/// let rng = &mut OsRng;
272/// let key = EcdsaP256Key::generate(rng);
273/// let pkix_der = pkix::marshal_pkix_public_key(&key.public_key_bytes()).unwrap();
274/// let pub_key = pkix::parse_pkix_public_key(&pkix_der).unwrap();
275/// assert_eq!(pub_key, key.public_key_bytes());
276/// ```
277pub fn parse_pkix_public_key(der_bytes: &[u8]) -> Result<Vec<u8>> {
278    use der::asn1::AnyRef;
279    use der::asn1::BitString;
280    let spki: SubjectPublicKeyInfo<AnyRef, BitString> = SubjectPublicKeyInfo::from_der(der_bytes)
281        .map_err(|e| {
282        BottleError::Deserialization(format!("Failed to parse PKIX public key: {}", e))
283    })?;
284
285    // Extract the raw key bytes from the SPKI structure
286    // The algorithm identifier tells us the key type
287    // For now, return the raw subjectPublicKey bytes
288    // In a full implementation, we'd parse based on the algorithm OID
289    Ok(spki.subject_public_key.raw_bytes().to_vec())
290}
291
292/// Parse a PKIX public key from PEM encoding.
293///
294/// # Arguments
295///
296/// * `pem_str` - PEM-encoded PKIX public key
297///
298/// # Returns
299///
300/// * `Ok(Vec<u8>)` - Raw public key bytes
301/// * `Err(BottleError)` - If parsing fails
302pub fn parse_pkix_public_key_pem(pem_str: &str) -> Result<Vec<u8>> {
303    let pem = pem::parse(pem_str)
304        .map_err(|e| BottleError::Deserialization(format!("Failed to parse PEM: {}", e)))?;
305    parse_pkix_public_key(pem.contents())
306}
307
308/// Marshal a private key to PKCS#8 format in DER encoding.
309///
310/// # Arguments
311///
312/// * `private_key_bytes` - Raw private key bytes
313/// * `key_type` - The type of key being marshaled
314///
315/// # Returns
316///
317/// * `Ok(Vec<u8>)` - DER-encoded PKCS#8 private key
318/// * `Err(BottleError)` - If encoding fails
319///
320/// # Example
321///
322/// ```rust
323/// use rust_bottle::keys::EcdsaP256Key;
324/// use rust_bottle::pkix;
325/// use rand::rngs::OsRng;
326///
327/// let rng = &mut OsRng;
328/// let key = EcdsaP256Key::generate(rng);
329/// let pkcs8_der = pkix::marshal_pkcs8_private_key(
330///     &key.private_key_bytes(),
331///     pkix::KeyType::EcdsaP256
332/// ).unwrap();
333/// ```
334pub fn marshal_pkcs8_private_key(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
335    match key_type {
336        KeyType::EcdsaP256 | KeyType::EcdsaP384 | KeyType::EcdsaP521 => {
337            marshal_ecdsa_pkcs8(private_key_bytes, key_type)
338        }
339        KeyType::Ed25519 => marshal_ed25519_pkcs8(private_key_bytes),
340        KeyType::X25519 => marshal_x25519_pkcs8(private_key_bytes),
341        KeyType::Rsa => marshal_rsa_pkcs8(private_key_bytes),
342        #[cfg(feature = "ml-kem")]
343        KeyType::MlKem768 | KeyType::MlKem1024 => marshal_mlkem_pkcs8(private_key_bytes, key_type),
344        #[cfg(feature = "post-quantum")]
345        KeyType::MlDsa44 | KeyType::MlDsa65 | KeyType::MlDsa87 => {
346            marshal_mldsa_pkcs8(private_key_bytes, key_type)
347        }
348        #[cfg(feature = "post-quantum")]
349        KeyType::SlhDsa128s | KeyType::SlhDsa128f | KeyType::SlhDsa192s | KeyType::SlhDsa192f | KeyType::SlhDsa256s | KeyType::SlhDsa256f
350        | KeyType::SlhDsaSha2_128s | KeyType::SlhDsaSha2_128f | KeyType::SlhDsaSha2_192s | KeyType::SlhDsaSha2_192f | KeyType::SlhDsaSha2_256s | KeyType::SlhDsaSha2_256f => {
351            marshal_slhdsa_pkcs8(private_key_bytes, key_type)
352        }
353    }
354}
355
356/// Marshal a private key to PKCS#8 format in PEM encoding.
357///
358/// # Arguments
359///
360/// * `private_key_bytes` - Raw private key bytes
361/// * `key_type` - The type of key being marshaled
362///
363/// # Returns
364///
365/// * `Ok(String)` - PEM-encoded PKCS#8 private key
366/// * `Err(BottleError)` - If encoding fails
367///
368/// # Example
369///
370/// ```rust
371/// use rust_bottle::keys::EcdsaP256Key;
372/// use rust_bottle::pkix;
373/// use rand::rngs::OsRng;
374///
375/// let rng = &mut OsRng;
376/// let key = EcdsaP256Key::generate(rng);
377/// let pkcs8_pem = pkix::marshal_pkcs8_private_key_pem(
378///     &key.private_key_bytes(),
379///     pkix::KeyType::EcdsaP256
380/// ).unwrap();
381/// ```
382pub fn marshal_pkcs8_private_key_pem(
383    private_key_bytes: &[u8],
384    key_type: KeyType,
385) -> Result<String> {
386    let der = marshal_pkcs8_private_key(private_key_bytes, key_type)?;
387    let pem = pem::encode(&pem::Pem::new("PRIVATE KEY", der));
388    Ok(pem)
389}
390
391/// Parse a PKCS#8 private key from DER encoding.
392///
393/// # Arguments
394///
395/// * `der_bytes` - DER-encoded PKCS#8 private key
396/// * `key_type` - Expected key type (for validation)
397///
398/// # Returns
399///
400/// * `Ok(Vec<u8>)` - Raw private key bytes
401/// * `Err(BottleError)` - If parsing fails
402///
403/// # Example
404///
405/// ```rust
406/// use rust_bottle::keys::EcdsaP256Key;
407/// use rust_bottle::pkix;
408/// use rand::rngs::OsRng;
409///
410/// let rng = &mut OsRng;
411/// let key = EcdsaP256Key::generate(rng);
412/// let pkcs8_der = pkix::marshal_pkcs8_private_key(
413///     &key.private_key_bytes(),
414///     pkix::KeyType::EcdsaP256
415/// ).unwrap();
416/// let priv_key = pkix::parse_pkcs8_private_key(&pkcs8_der, pkix::KeyType::EcdsaP256).unwrap();
417/// assert_eq!(priv_key, key.private_key_bytes());
418/// ```
419pub fn parse_pkcs8_private_key(der_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
420    match key_type {
421        KeyType::EcdsaP256 => {
422            use p256::ecdsa::SigningKey;
423            use p256::pkcs8::DecodePrivateKey;
424            let signing_key = SigningKey::from_pkcs8_der(der_bytes).map_err(|e| {
425                BottleError::Deserialization(format!("Failed to parse P-256 PKCS#8: {}", e))
426            })?;
427            Ok(signing_key.to_bytes().to_vec())
428        }
429        KeyType::Ed25519 => {
430            use ed25519_dalek::pkcs8::DecodePrivateKey;
431            use ed25519_dalek::SigningKey;
432            let signing_key = SigningKey::from_pkcs8_der(der_bytes).map_err(|e| {
433                BottleError::Deserialization(format!("Failed to parse Ed25519 PKCS#8: {}", e))
434            })?;
435            Ok(signing_key.to_bytes().to_vec())
436        }
437        KeyType::X25519 => {
438            // X25519 private keys are stored directly as raw bytes in PKCS#8
439            let pkcs8 = PrivateKeyInfo::from_der(der_bytes).map_err(|e| {
440                BottleError::Deserialization(format!("Failed to parse PKCS#8 private key: {}", e))
441            })?;
442            Ok(pkcs8.private_key.to_vec())
443        }
444        KeyType::Rsa => {
445            // RSA private keys in PKCS#8 contain RSAPrivateKey structure
446            // For now, return error - proper implementation requires RsaPrivateKey parsing
447            // TODO: Implement proper RSA PKCS#8 deserialization
448            Err(BottleError::Deserialization(
449                "RSA PKCS#8 deserialization not yet implemented. Use RsaKey directly.".to_string(),
450            ))
451        }
452        _ => {
453            // For other key types, return the raw private key bytes
454            // (they may need special handling in the future)
455            let pkcs8 = PrivateKeyInfo::from_der(der_bytes).map_err(|e| {
456                BottleError::Deserialization(format!("Failed to parse PKCS#8 private key: {}", e))
457            })?;
458            Ok(pkcs8.private_key.to_vec())
459        }
460    }
461}
462
463/// Parse a PKCS#8 private key from PEM encoding.
464///
465/// # Arguments
466///
467/// * `pem_str` - PEM-encoded PKCS#8 private key
468/// * `key_type` - Expected key type (for validation)
469///
470/// # Returns
471///
472/// * `Ok(Vec<u8>)` - Raw private key bytes
473/// * `Err(BottleError)` - If parsing fails
474pub fn parse_pkcs8_private_key_pem(pem_str: &str, key_type: KeyType) -> Result<Vec<u8>> {
475    let pem = pem::parse(pem_str)
476        .map_err(|e| BottleError::Deserialization(format!("Failed to parse PEM: {}", e)))?;
477    parse_pkcs8_private_key(pem.contents(), key_type)
478}
479
480// Helper functions for specific key types
481
482fn marshal_ecdsa_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
483    match key_type {
484        KeyType::EcdsaP256 => {
485            use p256::pkcs8::EncodePublicKey;
486            let pub_key = p256::PublicKey::from_sec1_bytes(public_key_bytes).map_err(|e| {
487                BottleError::Serialization(format!("Failed to create P-256 public key: {}", e))
488            })?;
489            pub_key
490                .to_public_key_der()
491                .map(|doc| doc.as_bytes().to_vec())
492                .map_err(|e| {
493                    BottleError::Serialization(format!("Failed to encode P-256 PKIX: {}", e))
494                })
495        }
496        _ => Err(BottleError::UnsupportedAlgorithm),
497    }
498}
499
500fn marshal_ecdsa_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
501    match key_type {
502        KeyType::EcdsaP256 => {
503            use p256::ecdsa::SigningKey;
504            use p256::pkcs8::EncodePrivateKey;
505            let signing_key = SigningKey::from_bytes(private_key_bytes.into()).map_err(|e| {
506                BottleError::Serialization(format!("Invalid P-256 private key: {}", e))
507            })?;
508            signing_key
509                .to_pkcs8_der()
510                .map(|doc| doc.as_bytes().to_vec())
511                .map_err(|e| {
512                    BottleError::Serialization(format!("Failed to encode P-256 PKCS#8: {}", e))
513                })
514        }
515        _ => Err(BottleError::UnsupportedAlgorithm),
516    }
517}
518
519fn marshal_ed25519_pkix(public_key_bytes: &[u8]) -> Result<Vec<u8>> {
520    use ed25519_dalek::pkcs8::EncodePublicKey;
521    use ed25519_dalek::VerifyingKey;
522
523    let verifying_key = VerifyingKey::from_bytes(public_key_bytes.try_into().map_err(|_| {
524        BottleError::Serialization("Invalid Ed25519 public key length".to_string())
525    })?)
526    .map_err(|e| BottleError::Serialization(format!("Invalid Ed25519 public key: {}", e)))?;
527
528    verifying_key
529        .to_public_key_der()
530        .map(|doc| doc.as_bytes().to_vec())
531        .map_err(|e| BottleError::Serialization(format!("Failed to encode Ed25519 PKIX: {}", e)))
532}
533
534fn marshal_ed25519_pkcs8(private_key_bytes: &[u8]) -> Result<Vec<u8>> {
535    use ed25519_dalek::pkcs8::EncodePrivateKey;
536    use ed25519_dalek::SigningKey;
537
538    let signing_key = SigningKey::from_bytes(private_key_bytes.try_into().map_err(|_| {
539        BottleError::Serialization("Invalid Ed25519 private key length".to_string())
540    })?);
541
542    signing_key
543        .to_pkcs8_der()
544        .map(|doc| doc.as_bytes().to_vec())
545        .map_err(|e| BottleError::Serialization(format!("Failed to encode Ed25519 PKCS#8: {}", e)))
546}
547
548fn marshal_x25519_pkix(public_key_bytes: &[u8]) -> Result<Vec<u8>> {
549    // X25519 public keys are 32 bytes
550    if public_key_bytes.len() != 32 {
551        return Err(BottleError::Serialization(
552            "Invalid X25519 public key length".to_string(),
553        ));
554    }
555
556    // X25519 uses a simple octet string encoding
557    // This is a simplified implementation
558    use der::asn1::OctetString;
559    let key_octets = OctetString::new(public_key_bytes).map_err(|e| {
560        BottleError::Serialization(format!("Failed to create X25519 octet string: {}", e))
561    })?;
562
563    // Create SPKI structure
564    // X25519 uses no parameters per RFC 8410
565    use der::asn1::AnyRef;
566    let algorithm = AlgorithmIdentifier {
567        oid: KeyType::X25519.oid(),
568        parameters: None::<AnyRef>,
569    };
570
571    let spki = SubjectPublicKeyInfo {
572        algorithm,
573        subject_public_key: key_octets,
574    };
575
576    spki.to_der()
577        .map_err(|e| BottleError::Serialization(format!("Failed to encode X25519 PKIX: {}", e)))
578}
579
580fn marshal_x25519_pkcs8(private_key_bytes: &[u8]) -> Result<Vec<u8>> {
581    // X25519 private keys are 32 bytes
582    if private_key_bytes.len() != 32 {
583        return Err(BottleError::Serialization(
584            "Invalid X25519 private key length".to_string(),
585        ));
586    }
587
588    // Create PKCS#8 structure
589    // X25519 uses no parameters per RFC 8410
590    let algorithm = AlgorithmIdentifierRef {
591        oid: KeyType::X25519.oid(),
592        parameters: None,
593    };
594
595    let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
596
597    pkcs8
598        .to_der()
599        .map_err(|e| BottleError::Serialization(format!("Failed to encode X25519 PKCS#8: {}", e)))
600}
601
602fn marshal_rsa_pkix(_public_key_bytes: &[u8]) -> Result<Vec<u8>> {
603    // Note: RSA public key bytes from RsaKey::public_key_bytes() are currently empty
604    // This is a placeholder - proper implementation requires RsaPublicKey reference
605    // For now, return an error indicating PKCS#8 serialization should be used
606    // TODO: Implement proper RSA PKIX serialization using RsaPublicKey
607    Err(BottleError::Serialization(
608        "RSA PKIX serialization requires RsaPublicKey reference. Use PKCS#8 serialization or provide RsaPublicKey directly.".to_string()
609    ))
610}
611
612fn marshal_rsa_pkcs8(_private_key_bytes: &[u8]) -> Result<Vec<u8>> {
613    // Note: RSA private key bytes from RsaKey::private_key_bytes() are currently empty
614    // This is a placeholder - proper implementation requires RsaPrivateKey reference
615    // For now, return an error indicating direct RsaKey should be used
616    // TODO: Implement proper RSA PKCS#8 serialization using RsaPrivateKey
617    Err(BottleError::Serialization(
618        "RSA PKCS#8 serialization requires RsaPrivateKey reference. Use RsaKey directly or provide RsaPrivateKey.".to_string()
619    ))
620}
621
622#[cfg(feature = "ml-kem")]
623fn marshal_mlkem_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
624    use der::asn1::BitString;
625
626    let key_bits = BitString::from_bytes(public_key_bytes).map_err(|e| {
627        BottleError::Serialization(format!("Failed to create ML-KEM bit string: {}", e))
628    })?;
629
630    use der::asn1::AnyRef;
631    let algorithm = AlgorithmIdentifier {
632        oid: key_type.oid(),
633        parameters: Some(AnyRef::NULL),
634    };
635
636    let spki = SubjectPublicKeyInfo {
637        algorithm,
638        subject_public_key: key_bits,
639    };
640
641    spki.to_der()
642        .map_err(|e| BottleError::Serialization(format!("Failed to encode ML-KEM PKIX: {}", e)))
643}
644
645#[cfg(feature = "ml-kem")]
646fn marshal_mlkem_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
647    use der::asn1::OctetString;
648
649    let _key_octets = OctetString::new(private_key_bytes).map_err(|e| {
650        BottleError::Serialization(format!("Failed to create ML-KEM octet string: {}", e))
651    })?;
652
653    use der::asn1::AnyRef;
654    let algorithm = AlgorithmIdentifierRef {
655        oid: key_type.oid(),
656        parameters: Some(AnyRef::NULL),
657    };
658
659    let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
660
661    pkcs8
662        .to_der()
663        .map_err(|e| BottleError::Serialization(format!("Failed to encode ML-KEM PKCS#8: {}", e)))
664}
665
666#[cfg(feature = "post-quantum")]
667fn marshal_mldsa_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
668    use der::asn1::BitString;
669
670    let key_bits = BitString::from_bytes(public_key_bytes).map_err(|e| {
671        BottleError::Serialization(format!("Failed to create ML-DSA bit string: {}", e))
672    })?;
673
674    use der::asn1::AnyRef;
675    let algorithm = AlgorithmIdentifier {
676        oid: key_type.oid(),
677        parameters: Some(AnyRef::NULL),
678    };
679
680    let spki = SubjectPublicKeyInfo {
681        algorithm,
682        subject_public_key: key_bits,
683    };
684
685    spki.to_der()
686        .map_err(|e| BottleError::Serialization(format!("Failed to encode ML-DSA PKIX: {}", e)))
687}
688
689#[cfg(feature = "post-quantum")]
690fn marshal_mldsa_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
691    use der::asn1::OctetString;
692
693    let _key_octets = OctetString::new(private_key_bytes).map_err(|e| {
694        BottleError::Serialization(format!("Failed to create ML-DSA octet string: {}", e))
695    })?;
696
697    use der::asn1::AnyRef;
698    let algorithm = AlgorithmIdentifierRef {
699        oid: key_type.oid(),
700        parameters: Some(AnyRef::NULL),
701    };
702
703    let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
704
705    pkcs8
706        .to_der()
707        .map_err(|e| BottleError::Serialization(format!("Failed to encode ML-DSA PKCS#8: {}", e)))
708}
709
710#[cfg(feature = "post-quantum")]
711fn marshal_slhdsa_pkix(public_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
712    use der::asn1::BitString;
713
714    let key_bits = BitString::from_bytes(public_key_bytes).map_err(|e| {
715        BottleError::Serialization(format!("Failed to create SLH-DSA bit string: {}", e))
716    })?;
717
718    use der::asn1::AnyRef;
719    let algorithm = AlgorithmIdentifier {
720        oid: key_type.oid(),
721        parameters: Some(AnyRef::NULL),
722    };
723
724    let spki = SubjectPublicKeyInfo {
725        algorithm,
726        subject_public_key: key_bits,
727    };
728
729    spki.to_der()
730        .map_err(|e| BottleError::Serialization(format!("Failed to encode SLH-DSA PKIX: {}", e)))
731}
732
733#[cfg(feature = "post-quantum")]
734fn marshal_slhdsa_pkcs8(private_key_bytes: &[u8], key_type: KeyType) -> Result<Vec<u8>> {
735    use der::asn1::OctetString;
736
737    let _key_octets = OctetString::new(private_key_bytes).map_err(|e| {
738        BottleError::Serialization(format!("Failed to create SLH-DSA octet string: {}", e))
739    })?;
740
741    use der::asn1::AnyRef;
742    let algorithm = AlgorithmIdentifierRef {
743        oid: key_type.oid(),
744        parameters: Some(AnyRef::NULL),
745    };
746
747    let pkcs8 = PrivateKeyInfo::new(algorithm, private_key_bytes);
748
749    pkcs8
750        .to_der()
751        .map_err(|e| BottleError::Serialization(format!("Failed to encode SLH-DSA PKCS#8: {}", e)))
752}
753
754/// Detect key type from public key bytes
755fn detect_key_type_from_public_key(public_key_bytes: &[u8]) -> Result<KeyType> {
756    match public_key_bytes.len() {
757        32 => {
758            // Could be Ed25519, X25519, or SLH-DSA-128s - try Ed25519 first (more common for signing)
759            // In practice, you'd need context or try both
760            #[cfg(feature = "post-quantum")]
761            {
762                // If post-quantum is enabled, could be SLH-DSA-128s, but default to Ed25519
763                Ok(KeyType::Ed25519)
764            }
765            #[cfg(not(feature = "post-quantum"))]
766            {
767                Ok(KeyType::Ed25519)
768            }
769        }
770        65 => {
771            // SEC1 uncompressed format (0x04 prefix) - likely ECDSA P-256
772            if public_key_bytes[0] == 0x04 {
773                Ok(KeyType::EcdsaP256)
774            } else {
775                Err(BottleError::InvalidKeyType)
776            }
777        }
778        97 => {
779            // SEC1 uncompressed format for P-384
780            if public_key_bytes[0] == 0x04 {
781                Ok(KeyType::EcdsaP384)
782            } else {
783                Err(BottleError::InvalidKeyType)
784            }
785        }
786        133 => {
787            // SEC1 uncompressed format for P-521
788            if public_key_bytes[0] == 0x04 {
789                Ok(KeyType::EcdsaP521)
790            } else {
791                Err(BottleError::InvalidKeyType)
792            }
793        }
794        1184 => {
795            #[cfg(feature = "ml-kem")]
796            {
797                Ok(KeyType::MlKem768)
798            }
799            #[cfg(not(feature = "ml-kem"))]
800            {
801                Err(BottleError::InvalidKeyType)
802            }
803        }
804        1568 => {
805            #[cfg(feature = "ml-kem")]
806            {
807                Ok(KeyType::MlKem1024)
808            }
809            #[cfg(not(feature = "ml-kem"))]
810            {
811                Err(BottleError::InvalidKeyType)
812            }
813        }
814        1312 => {
815            #[cfg(feature = "post-quantum")]
816            {
817                Ok(KeyType::MlDsa44)
818            }
819            #[cfg(not(feature = "post-quantum"))]
820            {
821                Err(BottleError::InvalidKeyType)
822            }
823        }
824        1952 => {
825            #[cfg(feature = "post-quantum")]
826            {
827                Ok(KeyType::MlDsa65)
828            }
829            #[cfg(not(feature = "post-quantum"))]
830            {
831                Err(BottleError::InvalidKeyType)
832            }
833        }
834        2592 => {
835            #[cfg(feature = "post-quantum")]
836            {
837                Ok(KeyType::MlDsa87)
838            }
839            #[cfg(not(feature = "post-quantum"))]
840            {
841                Err(BottleError::InvalidKeyType)
842            }
843        }
844        48 => {
845            #[cfg(feature = "post-quantum")]
846            {
847                Ok(KeyType::SlhDsa192s)
848            }
849            #[cfg(not(feature = "post-quantum"))]
850            {
851                Err(BottleError::InvalidKeyType)
852            }
853        }
854        64 => {
855            #[cfg(feature = "post-quantum")]
856            {
857                Ok(KeyType::SlhDsa256s)
858            }
859            #[cfg(not(feature = "post-quantum"))]
860            {
861                Err(BottleError::InvalidKeyType)
862            }
863        }
864        _ => Err(BottleError::InvalidKeyType),
865    }
866}