1use crate::{Hash, KeyId};
69use hex;
70use libcrux_ml_dsa::{
71 ml_dsa_44,
72 ml_dsa_44::{MLDSA44KeyPair, MLDSA44Signature, MLDSA44SigningKey, MLDSA44VerificationKey},
73 ml_dsa_65,
74 ml_dsa_65::{MLDSA65KeyPair, MLDSA65Signature, MLDSA65SigningKey, MLDSA65VerificationKey},
75 ml_dsa_87,
76 ml_dsa_87::{MLDSA87KeyPair, MLDSA87Signature, MLDSA87SigningKey, MLDSA87VerificationKey},
77 KEY_GENERATION_RANDOMNESS_SIZE, SIGNING_RANDOMNESS_SIZE,
78};
79use pem::{encode, parse_many, Pem};
80use rand::RngCore;
81use serde::{Deserialize, Serialize};
82use signature::{Signer, Verifier};
83use std::fmt;
84
85#[derive(Debug, thiserror::Error)]
87pub enum KeyPairError {
88 #[error("Failed to serialize KeyPair: {0}")]
89 Serialization(#[from] postcard::Error),
90 #[error("Failed to deserialize KeyPair: {0}")]
91 Deserialization(postcard::Error),
92 #[error("Failed to decode base64: {0}")]
93 Base64Decode(#[from] base64::DecodeError),
94 #[error("Invalid key data: {0}")]
95 InvalidKeyData(String),
96}
97
98#[derive(Debug, thiserror::Error)]
100pub enum VerifyingKeyError {
101 #[error("Failed to serialize VerifyingKey: {0}")]
102 SerializationError(String),
103 #[error("Failed to deserialize VerifyingKey: {0}")]
104 DeserializationError(String),
105 #[error("Failed to parse PEM: {0}")]
106 PemParseError(String),
107 #[error("Invalid PEM label: expected 'ZOE PUBLIC KEY', got '{0}'")]
108 InvalidPemLabel(String),
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
113pub enum Algorithm {
114 Ed25519,
116 MlDsa44,
118 MlDsa65,
120 MlDsa87,
122}
123
124impl fmt::Display for Algorithm {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match self {
127 Algorithm::Ed25519 => write!(f, "Ed25519"),
128 Algorithm::MlDsa44 => write!(f, "ML-DSA-44"),
129 Algorithm::MlDsa65 => write!(f, "ML-DSA-65"),
130 Algorithm::MlDsa87 => write!(f, "ML-DSA-87"),
131 }
132 }
133}
134
135#[derive(Clone, Serialize, Deserialize)]
168pub enum VerifyingKey {
169 Ed25519(Box<ed25519_dalek::VerifyingKey>),
171 #[serde(with = "serde_helpers::VerifyingKeyDef44")]
173 MlDsa44((Box<MLDSA44VerificationKey>, Hash)),
174 #[serde(with = "serde_helpers::VerifyingKeyDef65")]
176 MlDsa65((Box<MLDSA65VerificationKey>, Hash)),
177 #[serde(with = "serde_helpers::VerifyingKeyDef87")]
179 MlDsa87((Box<MLDSA87VerificationKey>, Hash)),
180}
181
182impl std::fmt::Debug for VerifyingKey {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 match self {
185 VerifyingKey::Ed25519(key) => f.debug_tuple("Ed25519").field(key).finish(),
186 VerifyingKey::MlDsa44((_, hash)) => f
187 .debug_tuple("MlDsa44")
188 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
189 .finish(),
190 VerifyingKey::MlDsa65((_, hash)) => f
191 .debug_tuple("MlDsa65")
192 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
193 .finish(),
194 VerifyingKey::MlDsa87((_, hash)) => f
195 .debug_tuple("MlDsa87")
196 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
197 .finish(),
198 }
199 }
200}
201
202impl From<MLDSA44VerificationKey> for VerifyingKey {
203 fn from(key: MLDSA44VerificationKey) -> Self {
204 let hash = blake3::hash(key.as_slice());
205 VerifyingKey::MlDsa44((Box::new(key), hash))
206 }
207}
208
209impl From<MLDSA65VerificationKey> for VerifyingKey {
210 fn from(key: MLDSA65VerificationKey) -> Self {
211 let hash = blake3::hash(key.as_slice());
212 VerifyingKey::MlDsa65((Box::new(key), hash))
213 }
214}
215
216impl From<MLDSA87VerificationKey> for VerifyingKey {
217 fn from(key: MLDSA87VerificationKey) -> Self {
218 let hash = blake3::hash(key.as_slice());
219 VerifyingKey::MlDsa87((Box::new(key), hash))
220 }
221}
222
223impl PartialEq for VerifyingKey {
224 fn eq(&self, other: &Self) -> bool {
225 self.id() == other.id()
226 }
227}
228
229impl Eq for VerifyingKey {}
230
231impl PartialOrd for VerifyingKey {
232 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
233 Some(self.cmp(other))
234 }
235}
236
237impl Ord for VerifyingKey {
238 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
239 let my_key = match self {
241 VerifyingKey::Ed25519(_) => 0,
242 VerifyingKey::MlDsa44(..) => 1,
243 VerifyingKey::MlDsa65(..) => 2,
244 VerifyingKey::MlDsa87(..) => 3,
245 };
246 let other_key_idx = match other {
247 VerifyingKey::Ed25519(_) => 0,
248 VerifyingKey::MlDsa44(..) => 1,
249 VerifyingKey::MlDsa65(..) => 2,
250 VerifyingKey::MlDsa87(..) => 3,
251 };
252 if my_key < other_key_idx {
253 return std::cmp::Ordering::Less;
254 } else if my_key > other_key_idx {
255 return std::cmp::Ordering::Greater;
256 }
257 self.id().cmp(&other.id())
259 }
260}
261
262impl std::hash::Hash for VerifyingKey {
263 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
264 self.id().hash(state);
265 }
266}
267
268impl TryFrom<&[u8]> for VerifyingKey {
269 type Error = postcard::Error;
270 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
271 let key: VerifyingKey = postcard::from_bytes(value)?;
272 Ok(key)
273 }
274}
275
276#[derive(Debug, thiserror::Error)]
277pub enum VerifyError {
278 #[error("Invalid key")]
279 InvalidKey,
280 #[error("Ed25519 signature error")]
281 Ed25519SignatureError(ed25519_dalek::SignatureError),
282 #[error("ML-DSA verify error")]
283 MlDsaVerifyError(libcrux_ml_dsa::VerificationError),
284}
285
286impl VerifyingKey {
288 pub fn algorithm(&self) -> Algorithm {
291 match self {
292 Self::Ed25519(_) => Algorithm::Ed25519,
293 Self::MlDsa44(..) => Algorithm::MlDsa44,
294 Self::MlDsa65(..) => Algorithm::MlDsa65,
295 Self::MlDsa87(..) => Algorithm::MlDsa87,
296 }
297 }
298
299 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), VerifyError> {
333 match (self, signature) {
334 (VerifyingKey::Ed25519(key), Signature::Ed25519(sig)) => key
335 .verify(message, sig)
336 .map_err(VerifyError::Ed25519SignatureError),
337 (VerifyingKey::MlDsa44((key, _hash)), Signature::MlDsa44((sig, _hash2))) => {
338 ml_dsa_44::portable::verify(key, message, &[], sig)
339 .map_err(VerifyError::MlDsaVerifyError)
340 }
341 (VerifyingKey::MlDsa65((key, _hash)), Signature::MlDsa65((sig, _hash2))) => {
342 ml_dsa_65::portable::verify(key, message, &[], sig)
343 .map_err(VerifyError::MlDsaVerifyError)
344 }
345 (VerifyingKey::MlDsa87((key, _hash)), Signature::MlDsa87((sig, _hash2))) => {
346 ml_dsa_87::portable::verify(key, message, &[], sig)
347 .map_err(VerifyError::MlDsaVerifyError)
348 }
349 _ => Err(VerifyError::InvalidKey), }
351 }
352
353 pub fn encode(&self) -> Vec<u8> {
380 postcard::to_stdvec(self).expect("Failed to serialize VerifyingKey")
381 }
382
383 pub fn id(&self) -> KeyId {
384 match self {
385 VerifyingKey::Ed25519(key) => KeyId::from_bytes(*key.as_bytes()),
386 VerifyingKey::MlDsa44((_key, hash)) => KeyId::from(*hash),
387 VerifyingKey::MlDsa65((_key, hash)) => KeyId::from(*hash),
388 VerifyingKey::MlDsa87((_key, hash)) => KeyId::from(*hash),
389 }
390 }
391
392 pub fn to_bytes(&self) -> Result<Vec<u8>, postcard::Error> {
393 postcard::to_stdvec(self)
394 }
395
396 pub fn from_hex(hex: String) -> Result<Self, String> {
397 let bytes = hex::decode(hex).map_err(|e| format!("Invalid hex: {e}"))?;
398 let key: VerifyingKey =
399 postcard::from_bytes(&bytes).map_err(|e| format!("Invalid key data: {e}"))?;
400 Ok(key)
401 }
402
403 pub fn to_pem(&self) -> Result<String, VerifyingKeyError> {
425 let key_bytes = postcard::to_stdvec(self)
426 .map_err(|e| VerifyingKeyError::SerializationError(e.to_string()))?;
427
428 let pem = Pem::new("ZOE PUBLIC KEY", key_bytes);
429 Ok(encode(&pem))
430 }
431
432 pub fn from_pem(pem_string: &str) -> Result<VerifyingKey, VerifyingKeyError> {
459 let pem =
460 pem::parse(pem_string).map_err(|e| VerifyingKeyError::PemParseError(e.to_string()))?;
461
462 if pem.tag() != "ZOE PUBLIC KEY" {
463 return Err(VerifyingKeyError::InvalidPemLabel(pem.tag().to_string()));
464 }
465
466 let key = postcard::from_bytes(pem.contents())
467 .map_err(|e| VerifyingKeyError::DeserializationError(e.to_string()))?;
468
469 Ok(key)
470 }
471}
472
473#[derive(Clone)]
506pub enum SigningKey {
507 Ed25519(Box<ed25519_dalek::SigningKey>),
509 MlDsa44((Box<MLDSA44SigningKey>, Hash)),
511 MlDsa65((Box<MLDSA65SigningKey>, Hash)),
513 MlDsa87((Box<MLDSA87SigningKey>, Hash)),
515}
516
517impl std::fmt::Debug for SigningKey {
518 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
519 match self {
520 SigningKey::Ed25519(_) => f.debug_tuple("Ed25519").field(&"<private_key>").finish(),
521 SigningKey::MlDsa44((_, hash)) => f
522 .debug_tuple("MlDsa44")
523 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
524 .finish(),
525 SigningKey::MlDsa65((_, hash)) => f
526 .debug_tuple("MlDsa65")
527 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
528 .finish(),
529 SigningKey::MlDsa87((_, hash)) => f
530 .debug_tuple("MlDsa87")
531 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
532 .finish(),
533 }
534 }
535}
536
537impl PartialEq for SigningKey {
538 fn eq(&self, other: &Self) -> bool {
539 match (self, other) {
540 (SigningKey::Ed25519(a), SigningKey::Ed25519(b)) => a == b,
541 (SigningKey::MlDsa44((_, hash_a)), SigningKey::MlDsa44((_, hash_b))) => {
542 hash_a == hash_b
543 }
544 (SigningKey::MlDsa65((_, hash_a)), SigningKey::MlDsa65((_, hash_b))) => {
545 hash_a == hash_b
546 }
547 (SigningKey::MlDsa87((_, hash_a)), SigningKey::MlDsa87((_, hash_b))) => {
548 hash_a == hash_b
549 }
550 _ => false,
551 }
552 }
553}
554
555impl SigningKey {
556 pub fn sign(&self, message: &[u8]) -> Signature {
558 match self {
559 SigningKey::Ed25519(key) => Signature::Ed25519(Box::new(key.sign(message))),
560 SigningKey::MlDsa44((key, _)) => {
561 let mut randomness = [0u8; SIGNING_RANDOMNESS_SIZE];
562 rand::thread_rng().fill_bytes(&mut randomness);
563 let signature = ml_dsa_44::portable::sign(key, message, &[], randomness)
564 .expect("ML-DSA signing should not fail");
565 signature.into()
566 }
567 SigningKey::MlDsa65((key, _)) => {
568 let mut randomness = [0u8; SIGNING_RANDOMNESS_SIZE];
569 rand::thread_rng().fill_bytes(&mut randomness);
570 let signature = ml_dsa_65::portable::sign(key, message, &[], randomness)
571 .expect("ML-DSA signing should not fail");
572 signature.into()
573 }
574 SigningKey::MlDsa87((key, _)) => {
575 let mut randomness = [0u8; SIGNING_RANDOMNESS_SIZE];
576 rand::thread_rng().fill_bytes(&mut randomness);
577 let signature = ml_dsa_87::portable::sign(key, message, &[], randomness)
578 .expect("ML-DSA signing should not fail");
579 signature.into()
580 }
581 }
582 }
583}
584
585impl Signature {
586 pub fn encode(&self) -> Vec<u8> {
588 postcard::to_stdvec(self).expect("Failed to serialize Signature")
589 }
590}
591
592#[derive(Clone, Serialize, Deserialize)]
593pub enum Signature {
594 Ed25519(Box<ed25519_dalek::Signature>),
595 #[serde(with = "serde_helpers::SignatureDef44")]
596 MlDsa44((Box<MLDSA44Signature>, Hash)),
597 #[serde(with = "serde_helpers::SignatureDef65")]
598 MlDsa65((Box<MLDSA65Signature>, Hash)),
599 #[serde(with = "serde_helpers::SignatureDef87")]
600 MlDsa87((Box<MLDSA87Signature>, Hash)),
601}
602
603impl std::fmt::Debug for Signature {
604 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
605 match self {
606 Signature::Ed25519(sig) => f.debug_tuple("Ed25519").field(sig).finish(),
607 Signature::MlDsa44((_, hash)) => f
608 .debug_tuple("MlDsa44")
609 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
610 .finish(),
611 Signature::MlDsa65((_, hash)) => f
612 .debug_tuple("MlDsa65")
613 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
614 .finish(),
615 Signature::MlDsa87((_, hash)) => f
616 .debug_tuple("MlDsa87")
617 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
618 .finish(),
619 }
620 }
621}
622
623impl From<MLDSA44Signature> for Signature {
624 fn from(sig: MLDSA44Signature) -> Self {
625 let hash = blake3::hash(sig.as_slice());
626 Signature::MlDsa44((Box::new(sig), hash))
627 }
628}
629
630impl From<MLDSA65Signature> for Signature {
631 fn from(sig: MLDSA65Signature) -> Self {
632 let hash = blake3::hash(sig.as_slice());
633 Signature::MlDsa65((Box::new(sig), hash))
634 }
635}
636
637impl From<MLDSA87Signature> for Signature {
638 fn from(sig: MLDSA87Signature) -> Self {
639 let hash = blake3::hash(sig.as_slice());
640 Signature::MlDsa87((Box::new(sig), hash))
641 }
642}
643
644impl Signature {
645 pub fn id(&self) -> KeyId {
646 match self {
647 Signature::Ed25519(sig) => KeyId::from_bytes(*sig.s_bytes()),
648 Signature::MlDsa44((_sig, hash)) => KeyId::from(*hash),
649 Signature::MlDsa65((_sig, hash)) => KeyId::from(*hash),
650 Signature::MlDsa87((_sig, hash)) => KeyId::from(*hash),
651 }
652 }
653}
654
655impl PartialEq for Signature {
656 fn eq(&self, other: &Self) -> bool {
657 self.partial_cmp(other) == Some(std::cmp::Ordering::Equal)
658 }
659}
660
661impl PartialOrd for Signature {
666 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
667 let my_key = match self {
669 Signature::Ed25519(_) => 0,
670 Signature::MlDsa44(..) => 1,
671 Signature::MlDsa65(..) => 2,
672 Signature::MlDsa87(..) => 3,
673 };
674 let other_key_idx = match other {
675 Signature::Ed25519(_) => 0,
676 Signature::MlDsa44(..) => 1,
677 Signature::MlDsa65(..) => 2,
678 Signature::MlDsa87(..) => 3,
679 };
680 if my_key < other_key_idx {
681 return Some(std::cmp::Ordering::Less);
682 } else if my_key > other_key_idx {
683 return Some(std::cmp::Ordering::Greater);
684 }
685 self.id().partial_cmp(&other.id())
686 }
687}
688
689pub enum KeyPair {
690 Ed25519(Box<ed25519_dalek::SigningKey>),
691 MlDsa44(Box<MLDSA44KeyPair>, Hash),
692 MlDsa65(Box<MLDSA65KeyPair>, Hash),
693 MlDsa87(Box<MLDSA87KeyPair>, Hash),
694}
695
696impl std::fmt::Debug for KeyPair {
697 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
698 match self {
699 KeyPair::Ed25519(_) => f.debug_tuple("Ed25519").field(&"<keypair>").finish(),
700 KeyPair::MlDsa44(_, hash) => f
701 .debug_tuple("MlDsa44")
702 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
703 .finish(),
704 KeyPair::MlDsa65(_, hash) => f
705 .debug_tuple("MlDsa65")
706 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
707 .finish(),
708 KeyPair::MlDsa87(_, hash) => f
709 .debug_tuple("MlDsa87")
710 .field(&format!("hash:{}", hex::encode(hash.as_bytes())))
711 .finish(),
712 }
713 }
714}
715
716impl KeyPair {
717 pub fn id(&self) -> KeyId {
718 match self {
719 KeyPair::Ed25519(key) => KeyId::from_bytes(*key.as_bytes()),
720 KeyPair::MlDsa44(_key, hash) => KeyId::from(*hash),
721 KeyPair::MlDsa65(_key, hash) => KeyId::from(*hash),
722 KeyPair::MlDsa87(_key, hash) => KeyId::from(*hash),
723 }
724 }
725
726 pub fn public_key(&self) -> VerifyingKey {
727 self.into()
728 }
729
730 pub fn algorithm(&self) -> Algorithm {
732 match self {
733 Self::Ed25519(_) => Algorithm::Ed25519,
734 Self::MlDsa44(..) => Algorithm::MlDsa44,
735 Self::MlDsa65(..) => Algorithm::MlDsa65,
736 Self::MlDsa87(..) => Algorithm::MlDsa87,
737 }
738 }
739}
740
741impl PartialEq for KeyPair {
742 fn eq(&self, other: &Self) -> bool {
743 match (self, other) {
744 (KeyPair::Ed25519(a), KeyPair::Ed25519(b)) => a.to_bytes() == b.to_bytes(),
745 (KeyPair::MlDsa44(_, hash), KeyPair::MlDsa44(_, hash_other)) => hash == hash_other,
746 (KeyPair::MlDsa65(_, hash), KeyPair::MlDsa65(_, hash_other)) => hash == hash_other,
747 (KeyPair::MlDsa87(_, hash), KeyPair::MlDsa87(_, hash_other)) => hash == hash_other,
748 _ => false,
749 }
750 }
751}
752
753impl From<&KeyPair> for VerifyingKey {
754 fn from(val: &KeyPair) -> Self {
755 match val {
756 KeyPair::Ed25519(a) => VerifyingKey::Ed25519(Box::new(a.verifying_key())),
757 KeyPair::MlDsa44(a, hash) => {
758 let key = a.verification_key.clone();
760 VerifyingKey::MlDsa44((Box::new(key), *hash))
761 }
762 KeyPair::MlDsa65(a, hash) => {
763 let key = a.verification_key.clone();
764 VerifyingKey::MlDsa65((Box::new(key), *hash))
765 }
766 KeyPair::MlDsa87(a, hash) => {
767 let key = a.verification_key.clone();
768 VerifyingKey::MlDsa87((Box::new(key), *hash))
769 }
770 }
771 }
772}
773
774impl KeyPair {
775 pub fn sign(&self, message: &[u8]) -> Signature {
776 match self {
777 KeyPair::Ed25519(a) => Signature::Ed25519(Box::new(a.sign(message))),
778 KeyPair::MlDsa44(a, _) => {
779 let mut randomness = [0u8; SIGNING_RANDOMNESS_SIZE];
780 rand::thread_rng().fill_bytes(&mut randomness);
781 let signature = ml_dsa_44::portable::sign(&a.signing_key, message, &[], randomness)
782 .expect("ML-DSA signing should not fail");
783 let hash = blake3::hash(signature.as_slice());
784 Signature::MlDsa44((Box::new(signature), hash))
785 }
786 KeyPair::MlDsa65(a, _) => {
787 let mut randomness = [0u8; SIGNING_RANDOMNESS_SIZE];
788 rand::thread_rng().fill_bytes(&mut randomness);
789 let signature = ml_dsa_65::portable::sign(&a.signing_key, message, &[], randomness)
790 .expect("ML-DSA signing should not fail");
791 let hash = blake3::hash(signature.as_slice());
792 Signature::MlDsa65((Box::new(signature), hash))
793 }
794 KeyPair::MlDsa87(a, _) => {
795 let mut randomness = [0u8; SIGNING_RANDOMNESS_SIZE];
796 rand::thread_rng().fill_bytes(&mut randomness);
797 let signature = ml_dsa_87::portable::sign(&a.signing_key, message, &[], randomness)
798 .expect("ML-DSA signing should not fail");
799 let hash = blake3::hash(signature.as_slice());
800 Signature::MlDsa87((Box::new(signature), hash))
801 }
802 }
803 }
804
805 pub fn generate_ml_dsa44<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> KeyPair {
806 let mut randomness = [0u8; KEY_GENERATION_RANDOMNESS_SIZE];
807 rng.fill_bytes(&mut randomness);
808 let key = ml_dsa_44::portable::generate_key_pair(randomness);
809 let hash = blake3::hash(key.verification_key.as_slice());
810 KeyPair::MlDsa44(Box::new(key), hash)
811 }
812
813 pub fn generate_ml_dsa65<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> KeyPair {
814 let mut randomness = [0u8; KEY_GENERATION_RANDOMNESS_SIZE];
815 rng.fill_bytes(&mut randomness);
816 let key = ml_dsa_65::portable::generate_key_pair(randomness);
817 let hash = blake3::hash(key.verification_key.as_slice());
818 KeyPair::MlDsa65(Box::new(key), hash)
819 }
820
821 pub fn generate_ml_dsa87<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> KeyPair {
822 let mut randomness = [0u8; KEY_GENERATION_RANDOMNESS_SIZE];
823 rng.fill_bytes(&mut randomness);
824 let key = ml_dsa_87::portable::generate_key_pair(randomness);
825 let hash = blake3::hash(key.verification_key.as_slice());
826 KeyPair::MlDsa87(Box::new(key), hash)
827 }
828
829 pub fn generate<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> KeyPair {
830 KeyPair::generate_ml_dsa65(rng)
831 }
832
833 pub fn generate_for_algorithm<R: rand::CryptoRng + rand::RngCore>(
834 algorithm: Algorithm,
835 rng: &mut R,
836 ) -> KeyPair {
837 match algorithm {
838 Algorithm::Ed25519 => KeyPair::generate_ed25519(rng),
839 Algorithm::MlDsa44 => KeyPair::generate_ml_dsa44(rng),
840 Algorithm::MlDsa65 => KeyPair::generate_ml_dsa65(rng),
841 Algorithm::MlDsa87 => KeyPair::generate_ml_dsa87(rng),
842 }
843 }
844
845 pub fn generate_ed25519<R: rand::CryptoRng + rand::RngCore>(rng: &mut R) -> KeyPair {
846 KeyPair::Ed25519(Box::new(ed25519_dalek::SigningKey::generate(rng)))
847 }
848
849 pub fn to_pem(&self) -> Result<String, KeyPairError> {
874 match self {
875 KeyPair::Ed25519(signing_key) => {
876 let private_key_bytes = signing_key.to_bytes();
878 let pem = Pem::new("ZOE ED25519 PRIVATE KEY", private_key_bytes.to_vec());
879 Ok(encode(&pem))
880 }
881 KeyPair::MlDsa44(keypair, _hash) => {
882 let private_pem = Pem::new(
884 "ZOE ML-DSA-44 PRIVATE KEY",
885 keypair.signing_key.as_slice().to_vec(),
886 );
887 let public_pem = Pem::new(
888 "ZOE ML-DSA-44 PUBLIC KEY",
889 keypair.verification_key.as_slice().to_vec(),
890 );
891
892 Ok(format!("{}\n{}", encode(&private_pem), encode(&public_pem)))
893 }
894 KeyPair::MlDsa65(keypair, _hash) => {
895 let private_pem = Pem::new(
897 "ZOE ML-DSA-65 PRIVATE KEY",
898 keypair.signing_key.as_slice().to_vec(),
899 );
900 let public_pem = Pem::new(
901 "ZOE ML-DSA-65 PUBLIC KEY",
902 keypair.verification_key.as_slice().to_vec(),
903 );
904
905 Ok(format!("{}\n{}", encode(&private_pem), encode(&public_pem)))
906 }
907 KeyPair::MlDsa87(keypair, _hash) => {
908 let private_pem = Pem::new(
910 "ZOE ML-DSA-87 PRIVATE KEY",
911 keypair.signing_key.as_slice().to_vec(),
912 );
913 let public_pem = Pem::new(
914 "ZOE ML-DSA-87 PUBLIC KEY",
915 keypair.verification_key.as_slice().to_vec(),
916 );
917
918 Ok(format!("{}\n{}", encode(&private_pem), encode(&public_pem)))
919 }
920 }
921 }
922
923 pub fn from_pem(pem_string: &str) -> Result<KeyPair, KeyPairError> {
950 let pems = parse_many(pem_string)
951 .map_err(|e| KeyPairError::InvalidKeyData(format!("Invalid PEM format: {e}")))?;
952
953 if pems.is_empty() {
954 return Err(KeyPairError::InvalidKeyData(
955 "No PEM blocks found".to_string(),
956 ));
957 }
958
959 if pems.len() == 1 && pems[0].tag() == "ZOE ED25519 PRIVATE KEY" {
961 if pems[0].contents().len() != 32 {
962 return Err(KeyPairError::InvalidKeyData(
963 "Invalid Ed25519 private key length".to_string(),
964 ));
965 }
966 let mut key_bytes = [0u8; 32];
967 key_bytes.copy_from_slice(pems[0].contents());
968 let signing_key = ed25519_dalek::SigningKey::from_bytes(&key_bytes);
969 return Ok(KeyPair::Ed25519(Box::new(signing_key)));
970 }
971
972 if pems.len() == 2 {
974 let mut private_key_bytes: Option<Vec<u8>> = None;
975 let mut public_key_bytes: Option<Vec<u8>> = None;
976 let mut algorithm: Option<&str> = None;
977
978 for pem in &pems {
979 match pem.tag() {
980 "ZOE ML-DSA-44 PRIVATE KEY" => {
981 private_key_bytes = Some(pem.contents().to_vec());
982 algorithm = Some("ML-DSA-44");
983 }
984 "ZOE ML-DSA-44 PUBLIC KEY" => {
985 public_key_bytes = Some(pem.contents().to_vec());
986 }
987 "ZOE ML-DSA-65 PRIVATE KEY" => {
988 private_key_bytes = Some(pem.contents().to_vec());
989 algorithm = Some("ML-DSA-65");
990 }
991 "ZOE ML-DSA-65 PUBLIC KEY" => {
992 public_key_bytes = Some(pem.contents().to_vec());
993 }
994 "ZOE ML-DSA-87 PRIVATE KEY" => {
995 private_key_bytes = Some(pem.contents().to_vec());
996 algorithm = Some("ML-DSA-87");
997 }
998 "ZOE ML-DSA-87 PUBLIC KEY" => {
999 public_key_bytes = Some(pem.contents().to_vec());
1000 }
1001 _ => {
1002 return Err(KeyPairError::InvalidKeyData(format!(
1003 "Unsupported PEM key type: {}",
1004 pem.tag()
1005 )));
1006 }
1007 }
1008 }
1009
1010 match (algorithm, private_key_bytes, public_key_bytes) {
1012 (Some("ML-DSA-44"), Some(private_bytes), Some(public_bytes)) => {
1013 let signing_key =
1014 MLDSA44SigningKey::new(private_bytes.try_into().map_err(|_| {
1015 KeyPairError::InvalidKeyData(
1016 "Invalid ML-DSA-44 signing key size".to_string(),
1017 )
1018 })?);
1019 let verification_key =
1020 MLDSA44VerificationKey::new(public_bytes.try_into().map_err(|_| {
1021 KeyPairError::InvalidKeyData(
1022 "Invalid ML-DSA-44 verification key size".to_string(),
1023 )
1024 })?);
1025 let keypair = MLDSA44KeyPair {
1026 signing_key,
1027 verification_key,
1028 };
1029 let hash = blake3::hash(keypair.verification_key.as_slice());
1031 Ok(KeyPair::MlDsa44(Box::new(keypair), hash))
1032 }
1033 (Some("ML-DSA-65"), Some(private_bytes), Some(public_bytes)) => {
1034 let signing_key =
1035 MLDSA65SigningKey::new(private_bytes.try_into().map_err(|_| {
1036 KeyPairError::InvalidKeyData(
1037 "Invalid ML-DSA-65 signing key size".to_string(),
1038 )
1039 })?);
1040 let verification_key =
1041 MLDSA65VerificationKey::new(public_bytes.try_into().map_err(|_| {
1042 KeyPairError::InvalidKeyData(
1043 "Invalid ML-DSA-65 verification key size".to_string(),
1044 )
1045 })?);
1046 let keypair = MLDSA65KeyPair {
1047 signing_key,
1048 verification_key,
1049 };
1050 let hash = blake3::hash(keypair.verification_key.as_slice());
1052 Ok(KeyPair::MlDsa65(Box::new(keypair), hash))
1053 }
1054 (Some("ML-DSA-87"), Some(private_bytes), Some(public_bytes)) => {
1055 let signing_key =
1056 MLDSA87SigningKey::new(private_bytes.try_into().map_err(|_| {
1057 KeyPairError::InvalidKeyData(
1058 "Invalid ML-DSA-87 signing key size".to_string(),
1059 )
1060 })?);
1061 let verification_key =
1062 MLDSA87VerificationKey::new(public_bytes.try_into().map_err(|_| {
1063 KeyPairError::InvalidKeyData(
1064 "Invalid ML-DSA-87 verification key size".to_string(),
1065 )
1066 })?);
1067 let keypair = MLDSA87KeyPair {
1068 signing_key,
1069 verification_key,
1070 };
1071 let hash = blake3::hash(keypair.verification_key.as_slice());
1073 Ok(KeyPair::MlDsa87(Box::new(keypair), hash))
1074 }
1075 _ => Err(KeyPairError::InvalidKeyData(
1076 "Incomplete or mismatched ML-DSA key blocks".to_string(),
1077 )),
1078 }
1079 } else {
1080 Err(KeyPairError::InvalidKeyData(format!(
1081 "Expected 1 PEM block for Ed25519 or 2 blocks for ML-DSA, found {}",
1082 pems.len()
1083 )))
1084 }
1085 }
1086}
1087
1088impl fmt::Display for VerifyingKey {
1089 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1090 match self {
1091 Self::Ed25519(key) => {
1092 write!(f, "Ed25519({})", hex::encode(key.to_bytes()))
1093 }
1094 Self::MlDsa44((_key, hash)) => {
1095 write!(f, "ML-DSA-44({})", hex::encode(hash.as_bytes()))
1096 }
1097 Self::MlDsa65((_key, hash)) => {
1098 write!(f, "ML-DSA-65({})", hex::encode(hash.as_bytes()))
1099 }
1100 Self::MlDsa87((_key, hash)) => {
1101 write!(f, "ML-DSA-87({})", hex::encode(hash.as_bytes()))
1102 }
1103 }
1104 }
1105}
1106
1107impl fmt::Display for KeyPair {
1108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1109 match self {
1110 Self::Ed25519(key) => {
1111 write!(
1112 f,
1113 "Ed25519({})",
1114 hex::encode(key.verifying_key().to_bytes())
1115 )
1116 }
1117 Self::MlDsa44(_keypair, hash) => {
1118 write!(f, "ML-DSA-44({})", hex::encode(hash.as_bytes()))
1119 }
1120 Self::MlDsa65(_keypair, hash) => {
1121 write!(f, "ML-DSA-65({})", hex::encode(hash.as_bytes()))
1122 }
1123 Self::MlDsa87(_keypair, hash) => {
1124 write!(f, "ML-DSA-87({})", hex::encode(hash.as_bytes()))
1125 }
1126 }
1127 }
1128}
1129
1130mod serde_helpers {
1131 #![allow(clippy::borrowed_box)]
1132
1133 use crate::Hash;
1134 use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
1135 use libcrux_ml_dsa::{
1136 ml_dsa_44::{MLDSA44Signature, MLDSA44VerificationKey},
1137 ml_dsa_65::{MLDSA65Signature, MLDSA65VerificationKey},
1138 ml_dsa_87::{MLDSA87Signature, MLDSA87VerificationKey},
1139 };
1140 use serde_bytes::ByteArray;
1141
1142 const ML_DSA_44_VERIFICATION_KEY_SIZE: usize = 1312;
1144 const ML_DSA_44_SIGNATURE_SIZE: usize = 2420;
1145 const ML_DSA_65_VERIFICATION_KEY_SIZE: usize = 1952;
1146 const ML_DSA_65_SIGNATURE_SIZE: usize = 3309;
1147 const ML_DSA_87_VERIFICATION_KEY_SIZE: usize = 2592;
1148 const ML_DSA_87_SIGNATURE_SIZE: usize = 4627;
1149
1150 pub struct VerifyingKeyDef44;
1153
1154 impl VerifyingKeyDef44 {
1155 pub fn serialize<S>(
1156 key: &(Box<MLDSA44VerificationKey>, Hash),
1157 serializer: S,
1158 ) -> Result<S::Ok, S::Error>
1159 where
1160 S: Serializer,
1161 {
1162 let key_bytes: &[u8; ML_DSA_44_VERIFICATION_KEY_SIZE] = key
1164 .0
1165 .as_slice()
1166 .try_into()
1167 .map_err(|_| serde::ser::Error::custom("ML-DSA-44 key has incorrect size"))?;
1168 let byte_array = ByteArray::new(*key_bytes);
1169 byte_array.serialize(serializer)
1170 }
1171
1172 pub fn deserialize<'de, D>(
1173 deserializer: D,
1174 ) -> Result<(Box<MLDSA44VerificationKey>, Hash), D::Error>
1175 where
1176 D: Deserializer<'de>,
1177 {
1178 let byte_array =
1180 ByteArray::<ML_DSA_44_VERIFICATION_KEY_SIZE>::deserialize(deserializer)?;
1181 let key_bytes: [u8; ML_DSA_44_VERIFICATION_KEY_SIZE] = byte_array.into_array();
1182 let key = MLDSA44VerificationKey::new(key_bytes);
1183 let hash = blake3::hash(&key_bytes);
1184 Ok((Box::new(key), hash))
1185 }
1186 }
1187
1188 pub struct VerifyingKeyDef65;
1191
1192 impl VerifyingKeyDef65 {
1193 pub fn serialize<S>(
1194 key: &(Box<MLDSA65VerificationKey>, Hash),
1195 serializer: S,
1196 ) -> Result<S::Ok, S::Error>
1197 where
1198 S: Serializer,
1199 {
1200 let key_bytes: &[u8; ML_DSA_65_VERIFICATION_KEY_SIZE] = key
1202 .0
1203 .as_slice()
1204 .try_into()
1205 .map_err(|_| serde::ser::Error::custom("ML-DSA-65 key has incorrect size"))?;
1206 let byte_array = ByteArray::new(*key_bytes);
1207 byte_array.serialize(serializer)
1208 }
1209
1210 pub fn deserialize<'de, D>(
1211 deserializer: D,
1212 ) -> Result<(Box<MLDSA65VerificationKey>, Hash), D::Error>
1213 where
1214 D: Deserializer<'de>,
1215 {
1216 let byte_array =
1218 ByteArray::<ML_DSA_65_VERIFICATION_KEY_SIZE>::deserialize(deserializer)?;
1219 let key_bytes: [u8; ML_DSA_65_VERIFICATION_KEY_SIZE] = byte_array.into_array();
1220 let key = MLDSA65VerificationKey::new(key_bytes);
1221 let hash = blake3::hash(&key_bytes);
1222 Ok((Box::new(key), hash))
1223 }
1224 }
1225
1226 pub struct VerifyingKeyDef87;
1229
1230 impl VerifyingKeyDef87 {
1231 pub fn serialize<S>(
1232 key: &(Box<MLDSA87VerificationKey>, Hash),
1233 serializer: S,
1234 ) -> Result<S::Ok, S::Error>
1235 where
1236 S: Serializer,
1237 {
1238 let key_bytes: &[u8; ML_DSA_87_VERIFICATION_KEY_SIZE] = key
1240 .0
1241 .as_slice()
1242 .try_into()
1243 .map_err(|_| serde::ser::Error::custom("ML-DSA-87 key has incorrect size"))?;
1244 let byte_array = ByteArray::new(*key_bytes);
1245 byte_array.serialize(serializer)
1246 }
1247
1248 pub fn deserialize<'de, D>(
1249 deserializer: D,
1250 ) -> Result<(Box<MLDSA87VerificationKey>, Hash), D::Error>
1251 where
1252 D: Deserializer<'de>,
1253 {
1254 let byte_array =
1256 ByteArray::<ML_DSA_87_VERIFICATION_KEY_SIZE>::deserialize(deserializer)?;
1257 let key_bytes: [u8; ML_DSA_87_VERIFICATION_KEY_SIZE] = byte_array.into_array();
1258 let key = MLDSA87VerificationKey::new(key_bytes);
1259 let hash = blake3::hash(&key_bytes);
1260 Ok((Box::new(key), hash))
1261 }
1262 }
1263
1264 pub struct SignatureDef44;
1267
1268 impl SignatureDef44 {
1269 pub fn serialize<S>(
1270 sig: &(Box<MLDSA44Signature>, Hash),
1271 serializer: S,
1272 ) -> Result<S::Ok, S::Error>
1273 where
1274 S: Serializer,
1275 {
1276 let sig_bytes: &[u8; ML_DSA_44_SIGNATURE_SIZE] =
1278 sig.0.as_slice().try_into().map_err(|_| {
1279 serde::ser::Error::custom("ML-DSA-44 signature has incorrect size")
1280 })?;
1281 let byte_array = ByteArray::new(*sig_bytes);
1282 byte_array.serialize(serializer)
1283 }
1284
1285 pub fn deserialize<'de, D>(
1286 deserializer: D,
1287 ) -> Result<(Box<MLDSA44Signature>, Hash), D::Error>
1288 where
1289 D: Deserializer<'de>,
1290 {
1291 let byte_array = ByteArray::<ML_DSA_44_SIGNATURE_SIZE>::deserialize(deserializer)?;
1293 let sig_bytes: [u8; ML_DSA_44_SIGNATURE_SIZE] = byte_array.into_array();
1294 let sig = MLDSA44Signature::new(sig_bytes);
1295 let hash = blake3::hash(&sig_bytes);
1296 Ok((Box::new(sig), hash))
1297 }
1298 }
1299
1300 pub struct SignatureDef65;
1303
1304 impl SignatureDef65 {
1305 pub fn serialize<S>(
1306 sig: &(Box<MLDSA65Signature>, Hash),
1307 serializer: S,
1308 ) -> Result<S::Ok, S::Error>
1309 where
1310 S: Serializer,
1311 {
1312 let sig_bytes: &[u8; ML_DSA_65_SIGNATURE_SIZE] =
1314 sig.0.as_slice().try_into().map_err(|_| {
1315 serde::ser::Error::custom("ML-DSA-65 signature has incorrect size")
1316 })?;
1317 let byte_array = ByteArray::new(*sig_bytes);
1318 byte_array.serialize(serializer)
1319 }
1320
1321 pub fn deserialize<'de, D>(
1322 deserializer: D,
1323 ) -> Result<(Box<MLDSA65Signature>, Hash), D::Error>
1324 where
1325 D: Deserializer<'de>,
1326 {
1327 let byte_array = ByteArray::<ML_DSA_65_SIGNATURE_SIZE>::deserialize(deserializer)?;
1329 let sig_bytes: [u8; ML_DSA_65_SIGNATURE_SIZE] = byte_array.into_array();
1330 let sig = MLDSA65Signature::new(sig_bytes);
1331 let hash = blake3::hash(&sig_bytes);
1332 Ok((Box::new(sig), hash))
1333 }
1334 }
1335
1336 pub struct SignatureDef87;
1339
1340 impl SignatureDef87 {
1341 pub fn serialize<S>(
1342 sig: &(Box<MLDSA87Signature>, Hash),
1343 serializer: S,
1344 ) -> Result<S::Ok, S::Error>
1345 where
1346 S: Serializer,
1347 {
1348 let sig_bytes: &[u8; ML_DSA_87_SIGNATURE_SIZE] =
1350 sig.0.as_slice().try_into().map_err(|_| {
1351 serde::ser::Error::custom("ML-DSA-87 signature has incorrect size")
1352 })?;
1353 let byte_array = ByteArray::new(*sig_bytes);
1354 byte_array.serialize(serializer)
1355 }
1356
1357 pub fn deserialize<'de, D>(
1358 deserializer: D,
1359 ) -> Result<(Box<MLDSA87Signature>, Hash), D::Error>
1360 where
1361 D: Deserializer<'de>,
1362 {
1363 let byte_array = ByteArray::<ML_DSA_87_SIGNATURE_SIZE>::deserialize(deserializer)?;
1365 let sig_bytes: [u8; ML_DSA_87_SIGNATURE_SIZE] = byte_array.into_array();
1366 let sig = MLDSA87Signature::new(sig_bytes);
1367 let hash = blake3::hash(&sig_bytes);
1368 Ok((Box::new(sig), hash))
1369 }
1370 }
1371}
1372
1373#[cfg(test)]
1374mod tests {
1375 use super::*;
1376 use rand::rngs::OsRng;
1377 use std::collections::{BTreeSet, HashSet};
1378
1379 fn create_test_keypairs() -> Vec<KeyPair> {
1381 let mut rng = OsRng;
1382 vec![
1383 KeyPair::generate_ed25519(&mut rng),
1384 KeyPair::generate_ml_dsa44(&mut rng),
1385 KeyPair::generate_ml_dsa65(&mut rng),
1386 KeyPair::generate_ml_dsa87(&mut rng),
1387 ]
1388 }
1389
1390 fn create_test_verifying_keys() -> Vec<VerifyingKey> {
1392 create_test_keypairs()
1393 .iter()
1394 .map(|kp| kp.public_key())
1395 .collect()
1396 }
1397
1398 #[test]
1399 fn test_verifying_key_equality_and_id_consistency() {
1400 let keypairs = create_test_keypairs();
1401
1402 for keypair in &keypairs {
1403 let key1 = keypair.public_key();
1404 let key2 = keypair.public_key();
1405
1406 assert_eq!(
1408 key1, key2,
1409 "VerifyingKeys from same KeyPair should be equal"
1410 );
1411
1412 assert_eq!(
1414 key1.id(),
1415 key2.id(),
1416 "IDs should be identical for equal keys"
1417 );
1418
1419 assert_eq!(key1, key1, "VerifyingKey should equal itself");
1421 }
1422
1423 let keys = create_test_verifying_keys();
1425 for (i, key1) in keys.iter().enumerate() {
1426 for (j, key2) in keys.iter().enumerate() {
1427 if i != j {
1428 assert_ne!(
1429 key1, key2,
1430 "Different keypairs should produce different VerifyingKeys"
1431 );
1432 assert_ne!(
1433 key1.id(),
1434 key2.id(),
1435 "Different keys should have different IDs"
1436 );
1437 }
1438 }
1439 }
1440 }
1441
1442 #[test]
1443 fn test_verifying_key_ordering() {
1444 let keys = create_test_verifying_keys();
1445
1446 for key1 in &keys {
1448 for key2 in &keys {
1449 let cmp1 = key1.cmp(key2);
1450 let cmp2 = key2.cmp(key1);
1451
1452 match cmp1 {
1454 std::cmp::Ordering::Less => assert_eq!(cmp2, std::cmp::Ordering::Greater),
1455 std::cmp::Ordering::Greater => assert_eq!(cmp2, std::cmp::Ordering::Less),
1456 std::cmp::Ordering::Equal => assert_eq!(cmp2, std::cmp::Ordering::Equal),
1457 }
1458
1459 assert_eq!(key1.partial_cmp(key2), Some(cmp1));
1461 }
1462 }
1463
1464 let mut sorted_keys = keys.clone();
1466 sorted_keys.sort();
1467
1468 for i in 0..sorted_keys.len() {
1470 for j in i + 1..sorted_keys.len() {
1471 assert!(
1472 sorted_keys[i] <= sorted_keys[j],
1473 "Sort order should be maintained"
1474 );
1475 }
1476 }
1477 }
1478
1479 #[test]
1480 fn test_verifying_key_hash_consistency() {
1481 let keys = create_test_verifying_keys();
1482
1483 for key in &keys {
1484 let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
1485 let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
1486
1487 std::hash::Hash::hash(key, &mut hasher1);
1488 std::hash::Hash::hash(key, &mut hasher2);
1489
1490 let hash1 = std::hash::Hasher::finish(&hasher1);
1491 let hash2 = std::hash::Hasher::finish(&hasher2);
1492
1493 assert_eq!(hash1, hash2, "Hash should be consistent for same key");
1494 }
1495
1496 let keypair = &create_test_keypairs()[0];
1498 let key1 = keypair.public_key();
1499 let key2 = keypair.public_key();
1500
1501 let mut hasher1 = std::collections::hash_map::DefaultHasher::new();
1502 let mut hasher2 = std::collections::hash_map::DefaultHasher::new();
1503
1504 std::hash::Hash::hash(&key1, &mut hasher1);
1505 std::hash::Hash::hash(&key2, &mut hasher2);
1506
1507 assert_eq!(
1508 std::hash::Hasher::finish(&hasher1),
1509 std::hash::Hasher::finish(&hasher2)
1510 );
1511
1512 let mut hash_set = HashSet::new();
1514 let mut btree_set = BTreeSet::new();
1515
1516 for key in &keys {
1517 hash_set.insert(key.clone());
1518 btree_set.insert(key.clone());
1519 }
1520
1521 assert_eq!(
1522 hash_set.len(),
1523 keys.len(),
1524 "All keys should be unique in HashSet"
1525 );
1526 assert_eq!(
1527 btree_set.len(),
1528 keys.len(),
1529 "All keys should be unique in BTreeSet"
1530 );
1531 }
1532
1533 #[test]
1534 fn test_verifying_key_serialization_round_trip() {
1535 let keys = create_test_verifying_keys();
1536
1537 for original_key in &keys {
1538 let encoded = original_key.encode();
1540 let decoded: VerifyingKey = postcard::from_bytes(&encoded)
1541 .expect("Should successfully deserialize VerifyingKey");
1542
1543 assert_eq!(
1544 *original_key, decoded,
1545 "Round-trip serialization should preserve equality"
1546 );
1547 assert_eq!(
1548 original_key.id(),
1549 decoded.id(),
1550 "Round-trip should preserve ID"
1551 );
1552
1553 let encoded2 = decoded.encode();
1555 assert_eq!(encoded, encoded2, "Encoding should be deterministic");
1556
1557 let bytes = original_key.to_bytes().expect("Should serialize to bytes");
1559 let restored = VerifyingKey::try_from(bytes.as_slice())
1560 .expect("Should successfully restore from bytes");
1561
1562 assert_eq!(
1563 *original_key, restored,
1564 "Alternative serialization should work"
1565 );
1566 assert_eq!(
1567 original_key.id(),
1568 restored.id(),
1569 "Alternative serialization should preserve ID"
1570 );
1571 }
1572 }
1573
1574 #[test]
1575 fn test_signing_key_equality() {
1576 let mut rng = OsRng;
1577
1578 let ed25519_bytes = [42u8; 32]; let ed25519_key1 = ed25519_dalek::SigningKey::from_bytes(&ed25519_bytes);
1581 let ed25519_key2 = ed25519_dalek::SigningKey::from_bytes(&ed25519_bytes);
1582
1583 let signing_key1 = SigningKey::Ed25519(Box::new(ed25519_key1));
1584 let signing_key2 = SigningKey::Ed25519(Box::new(ed25519_key2));
1585
1586 assert_eq!(
1587 signing_key1, signing_key2,
1588 "SigningKeys from same bytes should be equal"
1589 );
1590
1591 let different_ed25519 = KeyPair::generate_ed25519(&mut rng);
1593 let different_ml_dsa = KeyPair::generate_ml_dsa65(&mut rng);
1594
1595 let ed25519_signing = match different_ed25519 {
1596 KeyPair::Ed25519(ref key) => SigningKey::Ed25519(key.clone()),
1597 _ => panic!("Expected Ed25519 keypair"),
1598 };
1599
1600 let ml_dsa_signing = match different_ml_dsa {
1601 KeyPair::MlDsa65(ref keypair, hash) => {
1602 SigningKey::MlDsa65((Box::new(keypair.signing_key.clone()), hash))
1603 }
1604 _ => panic!("Expected ML-DSA-65 keypair"),
1605 };
1606
1607 assert_ne!(
1608 ed25519_signing, ml_dsa_signing,
1609 "Different key types should not be equal"
1610 );
1611 }
1612
1613 #[test]
1614 fn test_signing_key_functionality() {
1615 let keypairs = create_test_keypairs();
1616 let message = b"test message for signing";
1617
1618 for keypair in &keypairs {
1619 let signature = keypair.sign(message);
1620 let verifying_key = keypair.public_key();
1621
1622 verifying_key.verify(message, &signature).unwrap();
1624 let wrong_message = b"different message";
1626 let is_invalid = verifying_key.verify(wrong_message, &signature);
1627 assert!(
1628 is_invalid.is_err(),
1629 "Signature should be invalid for wrong message"
1630 );
1631 }
1632 }
1633
1634 #[test]
1635 fn test_signature_equality_and_ordering() {
1636 let keypairs = create_test_keypairs();
1637 let message = b"test message";
1638
1639 let mut signatures = Vec::new();
1640 for keypair in &keypairs {
1641 signatures.push(keypair.sign(message));
1642 }
1643
1644 for (i, sig1) in signatures.iter().enumerate() {
1646 for (j, sig2) in signatures.iter().enumerate() {
1647 if i == j {
1648 assert_eq!(sig1, sig2, "Signature should equal itself");
1649 } else {
1650 assert_ne!(sig1, sig2, "Different signatures should not be equal");
1652 }
1653 }
1654 }
1655
1656 for sig1 in &signatures {
1658 for sig2 in &signatures {
1659 let cmp = sig1.partial_cmp(sig2);
1660 assert!(cmp.is_some(), "Signatures should always be comparable");
1661
1662 let reverse_cmp = sig2.partial_cmp(sig1);
1664 match cmp.unwrap() {
1665 std::cmp::Ordering::Less => {
1666 assert_eq!(reverse_cmp, Some(std::cmp::Ordering::Greater))
1667 }
1668 std::cmp::Ordering::Greater => {
1669 assert_eq!(reverse_cmp, Some(std::cmp::Ordering::Less))
1670 }
1671 std::cmp::Ordering::Equal => {
1672 assert_eq!(reverse_cmp, Some(std::cmp::Ordering::Equal))
1673 }
1674 }
1675 }
1676 }
1677
1678 let mut sorted_signatures = signatures.clone();
1680 sorted_signatures.sort_by(|a, b| a.partial_cmp(b).unwrap());
1681
1682 for i in 0..sorted_signatures.len() {
1684 for j in i + 1..sorted_signatures.len() {
1685 assert!(
1686 sorted_signatures[i].partial_cmp(&sorted_signatures[j])
1687 != Some(std::cmp::Ordering::Greater)
1688 );
1689 }
1690 }
1691 }
1692
1693 #[test]
1694 fn test_signature_id_consistency() {
1695 let keypairs = create_test_keypairs();
1696 let message = b"test message";
1697
1698 for keypair in &keypairs {
1699 let sig1 = keypair.sign(message);
1700 let sig2 = keypair.sign(message);
1701
1702 assert_eq!(sig1.id(), sig1.id(), "Signature ID should be consistent");
1705 assert_eq!(sig2.id(), sig2.id(), "Signature ID should be consistent");
1706 }
1707 }
1708
1709 #[test]
1710 fn test_signature_serialization_round_trip() {
1711 let keypairs = create_test_keypairs();
1712 let message = b"test message";
1713
1714 for keypair in &keypairs {
1715 let original_signature = keypair.sign(message);
1716
1717 let encoded = original_signature.encode();
1719 let decoded: Signature =
1720 postcard::from_bytes(&encoded).expect("Should successfully deserialize Signature");
1721
1722 assert_eq!(
1723 original_signature, decoded,
1724 "Round-trip serialization should preserve equality"
1725 );
1726 assert_eq!(
1727 original_signature.id(),
1728 decoded.id(),
1729 "Round-trip should preserve ID"
1730 );
1731
1732 let encoded2 = decoded.encode();
1734 assert_eq!(encoded, encoded2, "Encoding should be deterministic");
1735 }
1736 }
1737
1738 #[test]
1739 fn test_keypair_equality_and_id_consistency() {
1740 let _rng = OsRng;
1741
1742 let ed25519_bytes = [42u8; 32];
1744 let ed25519_key1 = ed25519_dalek::SigningKey::from_bytes(&ed25519_bytes);
1745 let ed25519_key2 = ed25519_dalek::SigningKey::from_bytes(&ed25519_bytes);
1746
1747 let keypair1 = KeyPair::Ed25519(Box::new(ed25519_key1));
1748 let keypair2 = KeyPair::Ed25519(Box::new(ed25519_key2));
1749
1750 assert_eq!(
1751 keypair1, keypair2,
1752 "KeyPairs from same bytes should be equal"
1753 );
1754 assert_eq!(
1755 keypair1.id(),
1756 keypair2.id(),
1757 "Equal KeyPairs should have same ID"
1758 );
1759
1760 let different_keypairs = create_test_keypairs();
1762 for (i, kp1) in different_keypairs.iter().enumerate() {
1763 for (j, kp2) in different_keypairs.iter().enumerate() {
1764 if i != j {
1765 assert_ne!(kp1, kp2, "Different KeyPairs should not be equal");
1766 assert_ne!(
1767 kp1.id(),
1768 kp2.id(),
1769 "Different KeyPairs should have different IDs"
1770 );
1771 }
1772 }
1773 }
1774 }
1775
1776 #[test]
1777 fn test_cross_algorithm_verification_rejection() {
1778 let keypairs = create_test_keypairs();
1779 let message = b"test message";
1780
1781 let mut signatures = Vec::new();
1783 let mut verifying_keys = Vec::new();
1784
1785 for keypair in &keypairs {
1786 signatures.push(keypair.sign(message));
1787 verifying_keys.push(keypair.public_key());
1788 }
1789
1790 for (key, sig) in verifying_keys.iter().zip(signatures.iter()) {
1792 key.verify(message, sig).unwrap();
1793 }
1794
1795 for (i, key) in verifying_keys.iter().enumerate() {
1797 for (j, sig) in signatures.iter().enumerate() {
1798 if i != j {
1799 let is_valid = key.verify(message, sig);
1800 assert!(
1801 is_valid.is_err(),
1802 "Mismatched key and signature should fail verification"
1803 );
1804 }
1805 }
1806 }
1807 }
1808
1809 #[test]
1810 fn test_verifying_key_pem_round_trip() {
1811 let keys = create_test_verifying_keys();
1812
1813 for original_key in &keys {
1814 let pem_string = original_key.to_pem().expect("Should encode to PEM");
1816
1817 assert!(
1819 pem_string.contains("-----BEGIN ZOE PUBLIC KEY-----"),
1820 "Should contain PEM begin marker with correct label"
1821 );
1822 assert!(
1823 pem_string.contains("-----END ZOE PUBLIC KEY-----"),
1824 "Should contain PEM end marker with correct label"
1825 );
1826
1827 assert!(
1829 pem_string.lines().count() >= 3,
1830 "PEM should have at least begin, content, and end lines"
1831 );
1832
1833 let restored_key = VerifyingKey::from_pem(&pem_string).expect("Should decode from PEM");
1834
1835 assert_eq!(
1837 original_key.encode(),
1838 restored_key.encode(),
1839 "Keys should be identical after PEM round trip"
1840 );
1841
1842 assert_eq!(
1843 original_key.algorithm(),
1844 restored_key.algorithm(),
1845 "Algorithms should be identical after PEM round trip"
1846 );
1847
1848 assert_eq!(
1849 original_key.id(),
1850 restored_key.id(),
1851 "Key IDs should be identical after PEM round trip"
1852 );
1853
1854 let pem_string2 = restored_key.to_pem().expect("Should encode to PEM again");
1856 assert_eq!(
1857 pem_string, pem_string2,
1858 "Multiple PEM encodings should be identical"
1859 );
1860 }
1861 }
1862
1863 #[test]
1864 fn test_verifying_key_pem_error_cases() {
1865 let invalid_pem = "not a pem file";
1867 let result = VerifyingKey::from_pem(invalid_pem);
1868 assert!(result.is_err(), "Should fail to parse invalid PEM format");
1869 assert!(
1870 matches!(result.unwrap_err(), VerifyingKeyError::PemParseError(_)),
1871 "Should return PemParseError for invalid format"
1872 );
1873
1874 let wrong_label_pem = "-----BEGIN WRONG LABEL-----\nZGF0YQ==\n-----END WRONG LABEL-----";
1876 let result = VerifyingKey::from_pem(wrong_label_pem);
1877 assert!(result.is_err(), "Should fail with wrong PEM label");
1878 assert!(
1879 matches!(result.unwrap_err(), VerifyingKeyError::InvalidPemLabel(_)),
1880 "Should return InvalidPemLabel error"
1881 );
1882
1883 let invalid_data_pem =
1885 "-----BEGIN ZOE PUBLIC KEY-----\ninvalid_base64_data!\n-----END ZOE PUBLIC KEY-----";
1886 let result = VerifyingKey::from_pem(invalid_data_pem);
1887 assert!(result.is_err(), "Should fail with invalid key data");
1888 }
1890
1891 #[test]
1892 fn test_verifying_key_pem_compatibility_with_different_algorithms() {
1893 let keys = create_test_verifying_keys();
1894
1895 for key in &keys {
1897 let pem = key.to_pem().expect("Should encode to PEM");
1898 let restored = VerifyingKey::from_pem(&pem).expect("Should decode from PEM");
1899
1900 match (key, &restored) {
1902 (VerifyingKey::Ed25519(_), VerifyingKey::Ed25519(_)) => {
1903 assert_eq!(key.encode(), restored.encode(), "Ed25519 keys should match");
1904 }
1905 (VerifyingKey::MlDsa44(_), VerifyingKey::MlDsa44(_)) => {
1906 assert_eq!(
1907 key.encode(),
1908 restored.encode(),
1909 "ML-DSA-44 keys should match"
1910 );
1911 }
1912 (VerifyingKey::MlDsa65(_), VerifyingKey::MlDsa65(_)) => {
1913 assert_eq!(
1914 key.encode(),
1915 restored.encode(),
1916 "ML-DSA-65 keys should match"
1917 );
1918 }
1919 (VerifyingKey::MlDsa87(_), VerifyingKey::MlDsa87(_)) => {
1920 assert_eq!(
1921 key.encode(),
1922 restored.encode(),
1923 "ML-DSA-87 keys should match"
1924 );
1925 }
1926 _ => panic!("Algorithm type changed during PEM round trip"),
1927 }
1928 }
1929 }
1930
1931 #[test]
1932 fn test_verifying_key_pem_whitespace_handling() {
1933 let key = create_test_verifying_keys()[0].clone();
1934 let pem = key.to_pem().expect("Should encode to PEM");
1935
1936 let pem_with_whitespace = format!(" \n\t{pem}\n \t");
1938 let restored = VerifyingKey::from_pem(&pem_with_whitespace)
1939 .expect("Should handle whitespace gracefully");
1940
1941 assert_eq!(
1942 key.encode(),
1943 restored.encode(),
1944 "Should handle extra whitespace in PEM"
1945 );
1946
1947 let pem_crlf = pem.replace('\n', "\r\n");
1949 let restored_crlf =
1950 VerifyingKey::from_pem(&pem_crlf).expect("Should handle CRLF line endings");
1951
1952 assert_eq!(
1953 key.encode(),
1954 restored_crlf.encode(),
1955 "Should handle CRLF line endings"
1956 );
1957 }
1958
1959 #[test]
1960 fn test_verifying_key_pem_consistency_with_keypair_pem() {
1961 let keypairs = create_test_keypairs();
1962
1963 for keypair in &keypairs {
1964 let verifying_key = keypair.public_key();
1965 let verifying_key_pem = verifying_key
1966 .to_pem()
1967 .expect("Should encode VerifyingKey to PEM");
1968
1969 assert!(
1971 verifying_key_pem.contains("ZOE PUBLIC KEY"),
1972 "VerifyingKey PEM should use consistent ZOE label format"
1973 );
1974
1975 let restored_key = VerifyingKey::from_pem(&verifying_key_pem)
1977 .expect("Should decode VerifyingKey from PEM");
1978
1979 assert_eq!(
1980 verifying_key.encode(),
1981 restored_key.encode(),
1982 "VerifyingKey PEM round trip should preserve key data"
1983 );
1984
1985 let message = b"test message for PEM verification";
1987 let signature = keypair.sign(message);
1988
1989 restored_key
1990 .verify(message, &signature)
1991 .expect("Restored key should be able to verify signatures");
1992 }
1993 }
1994
1995 #[test]
1996 fn test_algorithm_ordering_consistency() {
1997 let mut rng = OsRng;
1998
1999 let ed25519_key = KeyPair::generate_ed25519(&mut rng).public_key();
2001 let ml_dsa44_key = KeyPair::generate_ml_dsa44(&mut rng).public_key();
2002 let ml_dsa65_key = KeyPair::generate_ml_dsa65(&mut rng).public_key();
2003 let ml_dsa87_key = KeyPair::generate_ml_dsa87(&mut rng).public_key();
2004
2005 assert!(
2007 ed25519_key < ml_dsa44_key,
2008 "Ed25519 should be less than ML-DSA-44"
2009 );
2010 assert!(
2011 ml_dsa44_key < ml_dsa65_key,
2012 "ML-DSA-44 should be less than ML-DSA-65"
2013 );
2014 assert!(
2015 ml_dsa65_key < ml_dsa87_key,
2016 "ML-DSA-65 should be less than ML-DSA-87"
2017 );
2018
2019 let message = b"test message";
2021 let ed25519_sig = KeyPair::generate_ed25519(&mut rng).sign(message);
2022 let ml_dsa44_sig = KeyPair::generate_ml_dsa44(&mut rng).sign(message);
2023 let ml_dsa65_sig = KeyPair::generate_ml_dsa65(&mut rng).sign(message);
2024 let ml_dsa87_sig = KeyPair::generate_ml_dsa87(&mut rng).sign(message);
2025
2026 assert!(
2027 ed25519_sig < ml_dsa44_sig,
2028 "Ed25519 sig should be less than ML-DSA-44 sig"
2029 );
2030 assert!(
2031 ml_dsa44_sig < ml_dsa65_sig,
2032 "ML-DSA-44 sig should be less than ML-DSA-65 sig"
2033 );
2034 assert!(
2035 ml_dsa65_sig < ml_dsa87_sig,
2036 "ML-DSA-65 sig should be less than ML-DSA-87 sig"
2037 );
2038 }
2039
2040 #[test]
2041 fn test_id_across_operations() {
2042 let keypairs = create_test_keypairs();
2043
2044 for keypair in &keypairs {
2045 let public_key = keypair.public_key();
2046 let public_key_id = public_key.id();
2047 assert_eq!(
2048 public_key_id,
2049 keypair.public_key().id(),
2050 "Multiple public key extractions should have same ID"
2051 );
2052
2053 let encoded = postcard::to_stdvec(&public_key).expect("Should serialize");
2055 let decoded: VerifyingKey = postcard::from_bytes(&encoded).expect("Should deserialize");
2056
2057 assert_eq!(
2058 public_key_id,
2059 decoded.id(), "ID should be stable across serialization"
2061 );
2062
2063 let signed = keypair.sign(b"test message");
2064 let signature_id = signed.id();
2065
2066 let encoded = postcard::to_stdvec(&signed).expect("Should serialize");
2068 let decoded: Signature = postcard::from_bytes(&encoded).expect("Should deserialize");
2069
2070 assert_eq!(
2071 signature_id,
2072 decoded.id(), "signature ID should be stable across serialization"
2074 );
2075 }
2076 }
2077
2078 #[test]
2079 fn test_hash_based_ids_for_ml_dsa() {
2080 let mut rng = OsRng;
2081
2082 let ml_dsa65_keypair = KeyPair::generate_ml_dsa65(&mut rng);
2084 let verifying_key = ml_dsa65_keypair.public_key();
2085
2086 match verifying_key {
2087 VerifyingKey::MlDsa65((ref key, ref stored_hash)) => {
2088 let computed_hash = blake3::hash(key.as_slice());
2089 assert_eq!(
2090 stored_hash.as_bytes(),
2091 computed_hash.as_bytes(),
2092 "Stored hash should match computed hash of encoded key"
2093 );
2094 assert_eq!(
2095 verifying_key.id().as_bytes(),
2096 computed_hash.as_bytes(),
2097 "ID should be the blake3 hash of encoded key"
2098 );
2099 }
2100 _ => panic!("Expected ML-DSA-65 key"),
2101 }
2102 }
2103
2104 #[test]
2105 fn test_ed25519_id_is_key_bytes() {
2106 let mut rng = OsRng;
2107 let ed25519_keypair = KeyPair::generate_ed25519(&mut rng);
2108 let verifying_key = ed25519_keypair.public_key();
2109
2110 match verifying_key {
2111 VerifyingKey::Ed25519(ref key) => {
2112 assert_eq!(
2113 verifying_key.id().as_bytes(),
2114 key.as_bytes(),
2115 "Ed25519 ID should be the raw key bytes"
2116 );
2117 }
2118 _ => panic!("Expected Ed25519 key"),
2119 }
2120 }
2121
2122 #[test]
2123 fn test_keypair_generate_defaults_to_ml_dsa65() {
2124 let mut rng = OsRng;
2125 let default_keypair = KeyPair::generate(&mut rng);
2126
2127 match default_keypair {
2128 KeyPair::MlDsa65(..) => {
2129 }
2131 _ => panic!("KeyPair::generate should default to ML-DSA-65"),
2132 }
2133 }
2134
2135 #[test]
2136 fn test_signature_id_implementation() {
2137 let mut rng = OsRng;
2138 let message = b"test message";
2139
2140 let ed25519_keypair = KeyPair::generate_ed25519(&mut rng);
2142 let ed25519_sig = ed25519_keypair.sign(message);
2143
2144 match ed25519_sig {
2145 Signature::Ed25519(ref sig) => {
2146 assert_eq!(
2147 ed25519_sig.id().as_bytes(),
2148 sig.s_bytes(),
2149 "Ed25519 signature ID should be s_bytes"
2150 );
2151 }
2152 _ => panic!("Expected Ed25519 signature"),
2153 }
2154
2155 let ml_dsa_keypair = KeyPair::generate_ml_dsa65(&mut rng);
2157 let ml_dsa_sig = ml_dsa_keypair.sign(message);
2158
2159 match ml_dsa_sig {
2160 Signature::MlDsa65((ref sig, ref stored_hash)) => {
2161 let computed_hash = blake3::hash(sig.as_slice());
2162 assert_eq!(
2163 stored_hash.as_bytes(),
2164 computed_hash.as_bytes(),
2165 "Stored hash should match computed hash"
2166 );
2167 assert_eq!(
2168 ml_dsa_sig.id().as_bytes(),
2169 computed_hash.as_bytes(),
2170 "ML-DSA signature ID should be blake3 hash"
2171 );
2172 }
2173 _ => panic!("Expected ML-DSA-65 signature"),
2174 }
2175 }
2176
2177 #[test]
2178 fn test_deterministic_encoding() {
2179 let keypairs = create_test_keypairs();
2180
2181 for keypair in &keypairs {
2182 let key = keypair.public_key();
2183
2184 let encoded1 = key.encode();
2186 let encoded2 = key.encode();
2187 let encoded3 = postcard::to_stdvec(&key).expect("Should serialize");
2188
2189 assert_eq!(encoded1, encoded2, "Multiple encodings should be identical");
2190 assert_eq!(
2191 encoded2, encoded3,
2192 "Different encoding methods should produce same result"
2193 );
2194 }
2195 }
2196
2197 #[test]
2198 fn test_keypair_pem_round_trip() {
2199 let keypairs = create_test_keypairs();
2200
2201 for original_keypair in keypairs {
2202 let pem_string = original_keypair.to_pem().expect("Should encode to PEM");
2204
2205 assert!(
2207 pem_string.contains("-----BEGIN"),
2208 "Should contain PEM begin marker"
2209 );
2210 assert!(
2211 pem_string.contains("-----END"),
2212 "Should contain PEM end marker"
2213 );
2214
2215 let restored_keypair = KeyPair::from_pem(&pem_string).expect("Should decode from PEM");
2216
2217 assert_eq!(
2219 original_keypair.public_key(),
2220 restored_keypair.public_key(),
2221 "Public keys should be identical after PEM round trip"
2222 );
2223
2224 assert_eq!(
2225 original_keypair.algorithm(),
2226 restored_keypair.algorithm(),
2227 "Algorithms should be identical after PEM round trip"
2228 );
2229
2230 let message = b"test message for PEM round trip";
2232 let original_signature = original_keypair.sign(message);
2233 let restored_signature = restored_keypair.sign(message);
2234
2235 let public_key = original_keypair.public_key();
2237 public_key.verify(message, &original_signature).unwrap();
2238 public_key.verify(message, &restored_signature).unwrap();
2239 }
2240 }
2241
2242 #[test]
2243 fn test_keypair_pem_environment_variable_simulation() {
2244 let keypairs = create_test_keypairs();
2245
2246 for (i, keypair) in keypairs.iter().enumerate() {
2247 let env_value = keypair
2249 .to_pem()
2250 .expect("Should encode for environment variable");
2251
2252 let restored_keypair =
2254 KeyPair::from_pem(&env_value).expect("Should decode from environment variable");
2255
2256 assert_eq!(
2258 keypair.public_key(),
2259 restored_keypair.public_key(),
2260 "Keypair {i} should survive environment variable round trip"
2261 );
2262
2263 let message = format!("Environment variable test message {i}");
2265 let signature = restored_keypair.sign(message.as_bytes());
2266 restored_keypair
2267 .public_key()
2268 .verify(message.as_bytes(), &signature)
2269 .unwrap();
2270 }
2271 }
2272
2273 #[test]
2274 fn test_keypair_pem_deterministic() {
2275 let mut rng = OsRng;
2276 let keypair = KeyPair::generate_ed25519(&mut rng);
2277
2278 let pem_1 = keypair.to_pem().unwrap();
2280 let pem_2 = keypair.to_pem().unwrap();
2281 let pem_3 = keypair.to_pem().unwrap();
2282
2283 assert_eq!(pem_1, pem_2, "PEM serialization should be deterministic");
2284 assert_eq!(pem_2, pem_3, "PEM serialization should be deterministic");
2285 }
2286
2287 #[test]
2288 fn test_keypair_pem_error_handling() {
2289 let invalid_pem = "not a valid PEM file";
2291 let result = KeyPair::from_pem(invalid_pem);
2292 assert!(result.is_err(), "Should fail on invalid PEM format");
2293 assert!(matches!(
2294 result.unwrap_err(),
2295 KeyPairError::InvalidKeyData(_)
2296 ));
2297
2298 let unsupported_pem =
2300 "-----BEGIN CERTIFICATE-----\nVGVzdCBjZXJ0aWZpY2F0ZQ==\n-----END CERTIFICATE-----";
2301 let result = KeyPair::from_pem(unsupported_pem);
2302 assert!(result.is_err(), "Should fail on unsupported PEM type");
2303 assert!(matches!(
2304 result.unwrap_err(),
2305 KeyPairError::InvalidKeyData(_)
2306 ));
2307
2308 let wrong_length_pem = "-----BEGIN PRIVATE KEY-----\nVGVzdA==\n-----END PRIVATE KEY-----";
2310 let result = KeyPair::from_pem(wrong_length_pem);
2311 assert!(result.is_err(), "Should fail on wrong key length");
2312 assert!(matches!(
2313 result.unwrap_err(),
2314 KeyPairError::InvalidKeyData(_)
2315 ));
2316 }
2317
2318 #[test]
2319 fn test_all_keypair_types_pem_compatibility() {
2320 let mut rng = OsRng;
2321
2322 let test_cases = vec![
2323 ("Ed25519", KeyPair::generate_ed25519(&mut rng)),
2324 ("ML-DSA-44", KeyPair::generate_ml_dsa44(&mut rng)),
2325 ("ML-DSA-65", KeyPair::generate_ml_dsa65(&mut rng)),
2326 ("ML-DSA-87", KeyPair::generate_ml_dsa87(&mut rng)),
2327 ];
2328
2329 for (name, keypair) in test_cases {
2330 let pem_string = keypair
2332 .to_pem()
2333 .unwrap_or_else(|_| panic!("{name} should encode to PEM"));
2334 let restored = KeyPair::from_pem(&pem_string)
2335 .unwrap_or_else(|_| panic!("{name} should decode from PEM"));
2336
2337 assert_eq!(
2338 keypair.public_key(),
2339 restored.public_key(),
2340 "{name} public key should match after PEM round trip"
2341 );
2342
2343 let message = format!("Test message for {name}");
2345 let signature = restored.sign(message.as_bytes());
2346 restored
2347 .public_key()
2348 .verify(message.as_bytes(), &signature)
2349 .unwrap();
2350
2351 match name {
2353 "Ed25519" => assert!(pem_string.contains("ZOE ED25519 PRIVATE KEY")),
2354 "ML-DSA-44" => {
2355 assert!(pem_string.contains("ZOE ML-DSA-44 PRIVATE KEY"));
2356 assert!(pem_string.contains("ZOE ML-DSA-44 PUBLIC KEY"));
2357 }
2358 "ML-DSA-65" => {
2359 assert!(pem_string.contains("ZOE ML-DSA-65 PRIVATE KEY"));
2360 assert!(pem_string.contains("ZOE ML-DSA-65 PUBLIC KEY"));
2361 }
2362 "ML-DSA-87" => {
2363 assert!(pem_string.contains("ZOE ML-DSA-87 PRIVATE KEY"));
2364 assert!(pem_string.contains("ZOE ML-DSA-87 PUBLIC KEY"));
2365 }
2366 _ => panic!("Unexpected key type"),
2367 }
2368 }
2369 }
2370
2371 #[test]
2372 fn test_pem_format_structure() {
2373 let mut rng = OsRng;
2374
2375 let ed25519_keypair = KeyPair::generate_ed25519(&mut rng);
2377 let ed25519_pem = ed25519_keypair.to_pem().unwrap();
2378
2379 assert!(ed25519_pem.contains("-----BEGIN ZOE ED25519 PRIVATE KEY-----"));
2380 assert!(ed25519_pem.contains("-----END ZOE ED25519 PRIVATE KEY-----"));
2381
2382 let ml_dsa_keypair = KeyPair::generate_ml_dsa65(&mut rng);
2384 let ml_dsa_pem = ml_dsa_keypair.to_pem().unwrap();
2385
2386 assert!(ml_dsa_pem.contains("-----BEGIN ZOE ML-DSA-65 PRIVATE KEY-----"));
2387 assert!(ml_dsa_pem.contains("-----END ZOE ML-DSA-65 PRIVATE KEY-----"));
2388 assert!(ml_dsa_pem.contains("-----BEGIN ZOE ML-DSA-65 PUBLIC KEY-----"));
2389 assert!(ml_dsa_pem.contains("-----END ZOE ML-DSA-65 PUBLIC KEY-----"));
2390
2391 assert!(
2393 ed25519_pem.len() < ml_dsa_pem.len(),
2394 "Ed25519 PEM should be smaller than ML-DSA PEM"
2395 );
2396 }
2397}