1use crate::constants::*;
4use crate::error::{Error, Result};
5use crate::wire::{read_header, take, write_header};
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct Envelope {
21 pub(crate) epk_x25519: [u8; X25519_PK_LEN],
23 pub(crate) ct_mlkem: Box<[u8; MLKEM1024_CT_LEN]>,
25 pub(crate) nonce: [u8; NONCE_LEN],
27 pub(crate) ciphertext: Vec<u8>,
29}
30
31impl Envelope {
32 pub fn to_bytes(&self) -> Vec<u8> {
34 let mut out = Vec::with_capacity(ENVELOPE_AAD_LEN + self.ciphertext.len());
35 self.write_aad(&mut out);
36 out.extend_from_slice(&self.ciphertext);
37 out
38 }
39
40 pub(crate) fn write_aad(&self, out: &mut Vec<u8>) {
42 let start = out.len();
43 write_header(out, MAGIC_ENVELOPE);
44 out.extend_from_slice(&self.epk_x25519);
45 out.extend_from_slice(self.ct_mlkem.as_ref());
46 out.extend_from_slice(&self.nonce);
47 debug_assert_eq!(out.len() - start, ENVELOPE_AAD_LEN);
48 }
49
50 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
58 let mut rest = read_header(bytes, MAGIC_ENVELOPE, Error::InvalidEnvelope)?;
59 let epk_x25519 = take(&mut rest, Error::InvalidEnvelope)?;
60 let ct_mlkem: [u8; MLKEM1024_CT_LEN] = take(&mut rest, Error::InvalidEnvelope)?;
61 let nonce = take(&mut rest, Error::InvalidEnvelope)?;
62 if rest.len() < TAG_LEN {
63 return Err(Error::InvalidEnvelope);
64 }
65 Ok(Self {
66 epk_x25519,
67 ct_mlkem: Box::new(ct_mlkem),
68 nonce,
69 ciphertext: rest.to_vec(),
70 })
71 }
72}
73
74#[derive(Clone, Debug, PartialEq, Eq)]
85pub struct HybridSignature {
86 pub(crate) ed25519: [u8; ED25519_SIG_LEN],
88 pub(crate) mldsa: Box<[u8; MLDSA87_SIG_LEN]>,
90}
91
92impl HybridSignature {
93 pub fn to_bytes(&self) -> Vec<u8> {
95 let mut out = Vec::with_capacity(SIGNATURE_LEN);
96 write_header(&mut out, MAGIC_SIGNATURE);
97 out.extend_from_slice(&self.ed25519);
98 out.extend_from_slice(self.mldsa.as_ref());
99 debug_assert_eq!(out.len(), SIGNATURE_LEN);
100 out
101 }
102
103 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
111 let mut rest = read_header(bytes, MAGIC_SIGNATURE, Error::InvalidSignature)?;
112 let ed25519 = take(&mut rest, Error::InvalidSignature)?;
113 let mldsa: [u8; MLDSA87_SIG_LEN] = take(&mut rest, Error::InvalidSignature)?;
114 if !rest.is_empty() {
115 return Err(Error::InvalidSignature);
116 }
117 Ok(Self {
118 ed25519,
119 mldsa: Box::new(mldsa),
120 })
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 fn dummy_envelope() -> Envelope {
129 Envelope {
130 epk_x25519: [1; X25519_PK_LEN],
131 ct_mlkem: Box::new([2; MLKEM1024_CT_LEN]),
132 nonce: [3; NONCE_LEN],
133 ciphertext: vec![4; 40],
134 }
135 }
136
137 #[test]
138 fn envelope_roundtrip() {
139 let env = dummy_envelope();
140 let bytes = env.to_bytes();
141 assert_eq!(bytes.len(), ENVELOPE_AAD_LEN + 40);
142 assert_eq!(Envelope::from_bytes(&bytes).unwrap(), env);
143 }
144
145 #[test]
146 fn envelope_rejects_truncation() {
147 let bytes = dummy_envelope().to_bytes();
148 for len in [
150 0,
151 5,
152 HEADER_LEN,
153 ENVELOPE_AAD_LEN,
154 ENVELOPE_AAD_LEN + TAG_LEN - 1,
155 ] {
156 assert!(Envelope::from_bytes(&bytes[..len]).is_err(), "len={len}");
157 }
158 }
159
160 #[test]
161 fn signature_roundtrip() {
162 let sig = HybridSignature {
163 ed25519: [5; ED25519_SIG_LEN],
164 mldsa: Box::new([6; MLDSA87_SIG_LEN]),
165 };
166 let bytes = sig.to_bytes();
167 assert_eq!(bytes.len(), SIGNATURE_LEN);
168 assert_eq!(HybridSignature::from_bytes(&bytes).unwrap(), sig);
169 }
170
171 #[test]
172 fn signature_rejects_wrong_length() {
173 let sig = HybridSignature {
174 ed25519: [5; ED25519_SIG_LEN],
175 mldsa: Box::new([6; MLDSA87_SIG_LEN]),
176 };
177 let bytes = sig.to_bytes();
178 assert_eq!(
179 HybridSignature::from_bytes(&bytes[..bytes.len() - 1]).unwrap_err(),
180 Error::InvalidSignature
181 );
182 let mut long = bytes.clone();
183 long.push(0);
184 assert_eq!(
185 HybridSignature::from_bytes(&long).unwrap_err(),
186 Error::InvalidSignature
187 );
188 }
189}