1use alloc::boxed::Box;
4use alloc::vec::Vec;
5
6use lib_q_core::{
7 Aead,
8 AeadDecryptSemantic,
9 AeadKey,
10 Algorithm,
11 DecryptSemanticOutcome,
12 Nonce,
13 Result,
14};
15
16use crate::metadata::{
17 AeadMetadata,
18 AeadWithMetadata,
19};
20
21pub struct RomulusMAead {
23 metadata: &'static AeadMetadata,
24 inner: lib_q_romulus::RomulusMAead,
25}
26
27impl RomulusMAead {
28 pub fn new() -> Self {
29 Self {
30 metadata: crate::metadata::get_metadata(Algorithm::RomulusM)
31 .expect("Romulus-M metadata"),
32 inner: lib_q_romulus::RomulusMAead::new(),
33 }
34 }
35}
36
37impl Aead for RomulusMAead {
38 fn encrypt(
39 &self,
40 key: &AeadKey,
41 nonce: &Nonce,
42 plaintext: &[u8],
43 associated_data: Option<&[u8]>,
44 ) -> Result<Vec<u8>> {
45 self.validate_key(key)?;
46 self.validate_nonce(nonce)?;
47 crate::security::validation::validate_plaintext(plaintext)?;
48 let ad = associated_data.unwrap_or(&[]);
49 crate::security::validation::validate_associated_data(ad)?;
50 self.inner.encrypt(key, nonce, plaintext, Some(ad))
51 }
52
53 fn decrypt(
54 &self,
55 key: &AeadKey,
56 nonce: &Nonce,
57 ciphertext: &[u8],
58 associated_data: Option<&[u8]>,
59 ) -> Result<Vec<u8>> {
60 self.validate_key(key)?;
61 self.validate_nonce(nonce)?;
62 self.validate_ciphertext_size(ciphertext.len())?;
63 crate::security::validation::validate_ciphertext(ciphertext)?;
64 let ad = associated_data.unwrap_or(&[]);
65 crate::security::validation::validate_associated_data(ad)?;
66 self.inner.decrypt(key, nonce, ciphertext, Some(ad))
67 }
68}
69
70impl AeadDecryptSemantic for RomulusMAead {
71 fn decrypt_semantic(
72 &self,
73 key: &AeadKey,
74 nonce: &Nonce,
75 ciphertext: &[u8],
76 associated_data: Option<&[u8]>,
77 ) -> Result<DecryptSemanticOutcome> {
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 let ad = associated_data.unwrap_or(&[]);
83 crate::security::validation::validate_associated_data(ad)?;
84 self.inner
85 .decrypt_semantic(key, nonce, ciphertext, Some(ad))
86 }
87}
88
89impl AeadWithMetadata for RomulusMAead {
90 fn metadata(&self) -> &'static AeadMetadata {
91 self.metadata
92 }
93}
94
95impl Default for RomulusMAead {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101impl crate::plugin::AeadPlugin for RomulusMAead {
102 fn algorithm(&self) -> Algorithm {
103 Algorithm::RomulusM
104 }
105
106 fn create(&self) -> Result<Box<dyn AeadWithMetadata>> {
107 Ok(Box::new(Self::new()))
108 }
109
110 fn metadata(&self) -> &'static AeadMetadata {
111 crate::metadata::get_metadata(Algorithm::RomulusM).expect("Romulus-M metadata")
112 }
113
114 fn name(&self) -> &'static str {
115 "Romulus-M"
116 }
117
118 fn version(&self) -> &'static str {
119 "1.3.0"
120 }
121
122 fn description(&self) -> &'static str {
123 "Romulus-M misuse-resistant AEAD (SKINNY-128-384+), 128-bit key/nonce/tag"
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn romulus_m_metadata_matches() {
133 let a = RomulusMAead::new();
134 assert_eq!(a.algorithm(), Algorithm::RomulusM);
135 assert_eq!(a.key_size(), 16);
136 assert_eq!(a.nonce_size(), 16);
137 assert_eq!(a.tag_size(), 16);
138 }
139
140 #[test]
141 fn romulus_m_roundtrip_registry() {
142 let a = RomulusMAead::new();
143 let key = AeadKey::new([7u8; 16].to_vec());
144 let nonce = Nonce::new([8u8; 16].to_vec());
145 let pt = b"hello-romulus-m";
146 let ad = b"ad-bytes";
147 let ct = a
148 .encrypt(&key, &nonce, pt.as_slice(), Some(ad.as_slice()))
149 .expect("encrypt");
150 let out = a
151 .decrypt(&key, &nonce, ct.as_slice(), Some(ad.as_slice()))
152 .expect("decrypt");
153 assert_eq!(out.as_slice(), pt.as_slice());
154 }
155}