1use ed25519_dalek::{Signer as Ed25519Signer, SigningKey, VerifyingKey};
11use ml_dsa::{
12 Keypair, MlDsa65, Signature, SigningKey as MlDsaSigningKey, Verifier,
13 VerifyingKey as MlDsaVerifyingKey,
14};
15
16use crate::{EndpointKey, Error};
17
18pub const ML_DSA_65_PUBLIC_KEY_BYTES: usize = 1_952;
20pub const ML_DSA_65_SIGNATURE_BYTES: usize = 3_309;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct EndpointIdentity {
30 pub classical: EndpointKey,
32 pub post_quantum: [u8; ML_DSA_65_PUBLIC_KEY_BYTES],
34}
35
36impl EndpointIdentity {
37 #[must_use]
39 pub const fn new(
40 classical: EndpointKey,
41 post_quantum: [u8; ML_DSA_65_PUBLIC_KEY_BYTES],
42 ) -> Self {
43 Self {
44 classical,
45 post_quantum,
46 }
47 }
48
49 pub fn verify_classical(&self, message: &[u8], signature: &[u8; 64]) -> Result<(), Error> {
51 let key =
52 VerifyingKey::from_bytes(self.classical.as_bytes()).map_err(|_| Error::BadSenderKey)?;
53 key.verify_strict(message, &ed25519_dalek::Signature::from_bytes(signature))
54 .map_err(|_| Error::BadSignature)
55 }
56
57 pub fn verify_post_quantum(
59 &self,
60 message: &[u8],
61 signature: &[u8; ML_DSA_65_SIGNATURE_BYTES],
62 ) -> Result<(), Error> {
63 let key_bytes =
64 ml_dsa::EncodedVerifyingKey::<MlDsa65>::try_from(self.post_quantum.as_slice())
65 .map_err(|_| Error::BadPostQuantumKey)?;
66 let key = MlDsaVerifyingKey::<MlDsa65>::decode(&key_bytes);
67 let signature = Signature::<MlDsa65>::try_from(signature.as_slice())
68 .map_err(|_| Error::BadPostQuantumSignatureEncoding)?;
69 key.verify(message, &signature)
70 .map_err(|_| Error::BadPostQuantumSignature)
71 }
72}
73
74pub struct SigningIdentity {
80 classical: SigningKey,
81 post_quantum: MlDsaSigningKey<MlDsa65>,
82}
83
84impl SigningIdentity {
85 #[must_use]
87 pub fn new(classical: SigningKey, post_quantum: MlDsaSigningKey<MlDsa65>) -> Self {
88 Self {
89 classical,
90 post_quantum,
91 }
92 }
93
94 #[must_use]
96 pub fn endpoint(&self) -> EndpointIdentity {
97 let encoded = self.post_quantum.verifying_key().encode();
98 let mut post_quantum = [0_u8; ML_DSA_65_PUBLIC_KEY_BYTES];
99 post_quantum.copy_from_slice(encoded.as_ref());
100 EndpointIdentity::new(
101 EndpointKey::from_bytes(self.classical.verifying_key().to_bytes()),
102 post_quantum,
103 )
104 }
105}
106
107pub trait EndpointSigner {
109 fn endpoint(&self) -> EndpointIdentity;
111 fn sign_classical(&self, message: &[u8]) -> [u8; 64];
113 fn sign_post_quantum(&self, message: &[u8]) -> [u8; ML_DSA_65_SIGNATURE_BYTES];
115}
116
117impl EndpointSigner for SigningIdentity {
118 fn endpoint(&self) -> EndpointIdentity {
119 self.endpoint()
120 }
121
122 fn sign_classical(&self, message: &[u8]) -> [u8; 64] {
123 Ed25519Signer::sign(&self.classical, message).to_bytes()
124 }
125
126 fn sign_post_quantum(&self, message: &[u8]) -> [u8; ML_DSA_65_SIGNATURE_BYTES] {
127 let encoded = self.post_quantum.sign(message).encode();
128 let mut signature = [0_u8; ML_DSA_65_SIGNATURE_BYTES];
129 signature.copy_from_slice(encoded.as_ref());
130 signature
131 }
132}