1use crate::errors::{BottleError, Result};
38use const_oid::{db::rfc5912, ObjectIdentifier};
39use der::{Decode, Encode};
40use pkcs8::{AlgorithmIdentifierRef, PrivateKeyInfo};
41use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum KeyType {
46 EcdsaP256,
48 EcdsaP384,
50 EcdsaP521,
52 Ed25519,
54 X25519,
56 Rsa,
58 #[cfg(feature = "ml-kem")]
60 MlKem768,
61 #[cfg(feature = "ml-kem")]
63 MlKem1024,
64 #[cfg(feature = "post-quantum")]
66 MlDsa44,
67 #[cfg(feature = "post-quantum")]
69 MlDsa65,
70 #[cfg(feature = "post-quantum")]
72 MlDsa87,
73 #[cfg(feature = "post-quantum")]
75 SlhDsa128s,
76 #[cfg(feature = "post-quantum")]
78 SlhDsa192s,
79 #[cfg(feature = "post-quantum")]
81 SlhDsa256s,
82}
83
84impl KeyType {
85 fn oid(&self) -> ObjectIdentifier {
87 match self {
88 KeyType::EcdsaP256 => rfc5912::ID_EC_PUBLIC_KEY, KeyType::EcdsaP384 => rfc5912::ID_EC_PUBLIC_KEY, KeyType::EcdsaP521 => rfc5912::ID_EC_PUBLIC_KEY, KeyType::Ed25519 => ObjectIdentifier::new("1.3.101.112").expect("Invalid Ed25519 OID"), KeyType::X25519 => ObjectIdentifier::new("1.3.101.110").expect("Invalid X25519 OID"), KeyType::Rsa => rfc5912::RSA_ENCRYPTION, #[cfg(feature = "ml-kem")]
95 KeyType::MlKem768 | KeyType::MlKem1024 => {
96 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 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 ObjectIdentifier::new("1.3.6.1.4.1.2.267.1.16.7").expect("Invalid SLH-DSA OID")
112 }
113 }
114 }
115
116 #[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]), KeyType::EcdsaP384 => Some(&[1, 3, 132, 0, 34]), KeyType::EcdsaP521 => Some(&[1, 3, 132, 0, 35]), _ => None,
124 }
125 }
126}
127
128pub fn marshal_pkix_public_key(public_key_bytes: &[u8]) -> Result<Vec<u8>> {
155 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
160pub 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
195pub 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
223pub 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 Ok(spki.subject_public_key.raw_bytes().to_vec())
261}
262
263pub 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
279pub 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
326pub 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
361pub 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 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 Err(BottleError::Deserialization(
419 "RSA PKCS#8 deserialization not yet implemented. Use RsaKey directly.".to_string(),
420 ))
421 }
422 _ => {
423 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
433pub 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
450fn 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 if public_key_bytes.len() != 32 {
521 return Err(BottleError::Serialization(
522 "Invalid X25519 public key length".to_string(),
523 ));
524 }
525
526 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 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 if private_key_bytes.len() != 32 {
553 return Err(BottleError::Serialization(
554 "Invalid X25519 private key length".to_string(),
555 ));
556 }
557
558 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 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 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
724fn detect_key_type_from_public_key(public_key_bytes: &[u8]) -> Result<KeyType> {
726 match public_key_bytes.len() {
727 32 => {
728 #[cfg(feature = "post-quantum")]
731 {
732 Ok(KeyType::Ed25519)
734 }
735 #[cfg(not(feature = "post-quantum"))]
736 {
737 Ok(KeyType::Ed25519)
738 }
739 }
740 65 => {
741 if public_key_bytes[0] == 0x04 {
743 Ok(KeyType::EcdsaP256)
744 } else {
745 Err(BottleError::InvalidKeyType)
746 }
747 }
748 97 => {
749 if public_key_bytes[0] == 0x04 {
751 Ok(KeyType::EcdsaP384)
752 } else {
753 Err(BottleError::InvalidKeyType)
754 }
755 }
756 133 => {
757 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}