1#[cfg(feature = "alloc")]
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8
9use lib_q_core::{
10 Aead,
11 AeadDecryptSemantic,
12 AeadKey,
13 Algorithm,
14 DecryptSemanticOutcome,
15 Nonce,
16 Result,
17};
18
19use crate::metadata::{
21 AeadMetadata,
22 AeadWithMetadata,
23};
24
25pub struct RoccaSAead {
27 metadata: &'static AeadMetadata,
28 inner: lib_q_rocca_s::RoccaSAead,
29}
30
31impl RoccaSAead {
32 pub fn new() -> Self {
34 Self {
35 metadata: crate::metadata::get_metadata(Algorithm::RoccaS)
36 .expect("Rocca-S metadata not found"),
37 inner: lib_q_rocca_s::RoccaSAead::new(),
38 }
39 }
40}
41
42impl Aead for RoccaSAead {
43 fn encrypt(
44 &self,
45 key: &AeadKey,
46 nonce: &Nonce,
47 plaintext: &[u8],
48 associated_data: Option<&[u8]>,
49 ) -> Result<Vec<u8>> {
50 self.validate_key(key)?;
51 self.validate_nonce(nonce)?;
52 crate::security::validation::validate_plaintext(plaintext)?;
53
54 let associated_data = associated_data.unwrap_or(&[]);
55 crate::security::validation::validate_associated_data(associated_data)?;
56
57 #[cfg(feature = "rocca-s")]
58 {
59 self.inner
60 .encrypt(key, nonce, plaintext, Some(associated_data))
61 }
62
63 #[cfg(not(feature = "rocca-s"))]
64 {
65 Err(lib_q_core::Error::NotImplemented {
66 feature: "Rocca-S AEAD implementation requires 'rocca-s' feature",
67 })
68 }
69 }
70
71 fn decrypt(
72 &self,
73 key: &AeadKey,
74 nonce: &Nonce,
75 ciphertext: &[u8],
76 associated_data: Option<&[u8]>,
77 ) -> Result<Vec<u8>> {
78 self.validate_key(key)?;
79 self.validate_nonce(nonce)?;
80 self.validate_ciphertext_size(ciphertext.len())?;
81 crate::security::validation::validate_ciphertext(ciphertext)?;
82
83 let associated_data = associated_data.unwrap_or(&[]);
84 crate::security::validation::validate_associated_data(associated_data)?;
85
86 #[cfg(feature = "rocca-s")]
87 {
88 self.inner
89 .decrypt(key, nonce, ciphertext, Some(associated_data))
90 }
91
92 #[cfg(not(feature = "rocca-s"))]
93 {
94 Err(lib_q_core::Error::NotImplemented {
95 feature: "Rocca-S AEAD implementation requires 'rocca-s' feature",
96 })
97 }
98 }
99}
100
101impl AeadDecryptSemantic for RoccaSAead {
102 fn decrypt_semantic(
103 &self,
104 key: &AeadKey,
105 nonce: &Nonce,
106 ciphertext: &[u8],
107 associated_data: Option<&[u8]>,
108 ) -> Result<DecryptSemanticOutcome> {
109 self.validate_key(key)?;
110 self.validate_nonce(nonce)?;
111 self.validate_ciphertext_size(ciphertext.len())?;
112 crate::security::validation::validate_ciphertext(ciphertext)?;
113
114 let associated_data = associated_data.unwrap_or(&[]);
115 crate::security::validation::validate_associated_data(associated_data)?;
116
117 #[cfg(feature = "rocca-s")]
118 {
119 self.inner
120 .decrypt_semantic(key, nonce, ciphertext, Some(associated_data))
121 }
122
123 #[cfg(not(feature = "rocca-s"))]
124 {
125 Err(lib_q_core::Error::NotImplemented {
126 feature: "Rocca-S AEAD implementation requires 'rocca-s' feature",
127 })
128 }
129 }
130}
131
132impl AeadWithMetadata for RoccaSAead {
133 fn metadata(&self) -> &'static AeadMetadata {
134 self.metadata
135 }
136}
137
138impl Default for RoccaSAead {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144impl crate::plugin::AeadPlugin for RoccaSAead {
146 fn algorithm(&self) -> Algorithm {
147 Algorithm::RoccaS
148 }
149
150 fn create(&self) -> Result<Box<dyn AeadWithMetadata>> {
151 Ok(Box::new(Self::new()))
152 }
153
154 fn metadata(&self) -> &'static AeadMetadata {
155 crate::metadata::get_metadata(Algorithm::RoccaS).expect("Metadata not found for algorithm")
156 }
157
158 fn name(&self) -> &'static str {
159 "Rocca-S AEAD"
160 }
161
162 fn version(&self) -> &'static str {
163 "1.0.0"
164 }
165
166 fn description(&self) -> &'static str {
167 "High-throughput AES-round AEAD (IETF draft-nakano-rocca-s)"
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_rocca_s_creation() {
177 let aead = RoccaSAead::new();
178 assert_eq!(aead.algorithm(), Algorithm::RoccaS);
179 assert_eq!(aead.key_size(), 32);
180 assert_eq!(aead.nonce_size(), 16);
181 assert_eq!(aead.tag_size(), 32);
182 assert_eq!(aead.security_level(), 1);
183 }
184
185 #[test]
186 fn test_rocca_s_metadata() {
187 let aead = RoccaSAead::new();
188 let metadata = aead.metadata();
189 assert_eq!(metadata.algorithm, Algorithm::RoccaS);
190 assert_eq!(metadata.name, "Rocca-S");
191 assert_eq!(metadata.key_size, 32);
192 assert_eq!(metadata.nonce_size, 16);
193 assert_eq!(metadata.tag_size, 32);
194 assert_eq!(metadata.security_level, 1);
195 }
196
197 #[test]
198 fn test_rocca_s_validation() {
199 let aead = RoccaSAead::new();
200 let key = AeadKey::new(vec![0u8; 32]);
201 assert!(aead.validate_key(&key).is_ok());
202 let invalid_key = AeadKey::new(vec![0u8; 16]);
203 assert!(aead.validate_key(&invalid_key).is_err());
204 let nonce = Nonce::new(vec![0u8; 16]);
205 assert!(aead.validate_nonce(&nonce).is_ok());
206 let invalid_nonce = Nonce::new(vec![0u8; 12]);
207 assert!(aead.validate_nonce(&invalid_nonce).is_err());
208 }
209
210 #[cfg(feature = "rocca-s")]
211 #[test]
212 fn test_rocca_s_encrypt_decrypt() {
213 let aead = RoccaSAead::new();
214 let key = AeadKey::new(vec![0u8; 32]);
215 let nonce = Nonce::new(vec![0u8; 16]);
216 let plaintext = b"Hello, World!";
217 let ad = b"metadata";
218
219 let ciphertext = aead
220 .encrypt(&key, &nonce, plaintext, Some(ad.as_slice()))
221 .unwrap();
222 assert_eq!(ciphertext.len(), plaintext.len() + aead.tag_size());
223
224 let decrypted = aead
225 .decrypt(&key, &nonce, &ciphertext, Some(ad.as_slice()))
226 .unwrap();
227 assert_eq!(decrypted, plaintext);
228 }
229
230 #[cfg(feature = "rocca-s")]
231 #[test]
232 fn test_rocca_s_authentication_failure() {
233 let aead = RoccaSAead::new();
234 let key = AeadKey::new(vec![0u8; 32]);
235 let nonce = Nonce::new(vec![0u8; 16]);
236 let ciphertext = aead.encrypt(&key, &nonce, b"Hello, World!", None).unwrap();
237 let mut tampered = ciphertext.clone();
238 tampered[0] ^= 0xFF;
239 let result = aead.decrypt(&key, &nonce, &tampered, None);
240 assert!(matches!(
241 result,
242 Err(lib_q_core::Error::VerificationFailed { .. })
243 ));
244 }
245
246 #[cfg(feature = "rocca-s")]
247 #[test]
248 fn test_rocca_s_official_kat() {
249 let aead = RoccaSAead::new();
251 let key = AeadKey::new(vec![0u8; 32]);
252 let nonce = Nonce::new(vec![0u8; 16]);
253 let ad = vec![0u8; 32];
254 let pt = vec![0u8; 64];
255 let ct = aead.encrypt(&key, &nonce, &pt, Some(&ad)).unwrap();
256 let tag = &ct[64..];
257 let expected_tag = [
258 0x8D, 0xF9, 0x34, 0xD1, 0x48, 0x37, 0x10, 0xC9, 0x41, 0x0F, 0x6A, 0x08, 0x9C, 0x4C,
259 0xED, 0x97, 0x91, 0x90, 0x1B, 0x7E, 0x2E, 0x66, 0x12, 0x06, 0x20, 0x2D, 0xB2, 0xCC,
260 0x7A, 0x24, 0xA3, 0x86,
261 ];
262 assert_eq!(tag, expected_tag);
263 }
264}