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