1use crate::{error::CryptoError, Result};
4use base64::{engine::general_purpose::STANDARD, Engine};
5use serde::{Deserialize, Serialize};
6use serde_json::{Map, Value};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct PqcMetadata {
14 pub kem_params: Option<KemParameters>,
16 pub sig_params: Option<SigParameters>,
18 pub enc_params: EncParameters,
20 pub compression_params: Option<CompressionParameters>,
22 pub custom: HashMap<String, Vec<u8>>,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub struct KemParameters {
31 pub public_key: Vec<u8>,
33 pub ciphertext: Vec<u8>,
35 pub params: HashMap<String, Vec<u8>>,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct SigParameters {
44 pub public_key: Vec<u8>,
46 pub signature: Vec<u8>,
48 pub params: HashMap<String, Vec<u8>>,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57pub struct EncParameters {
58 pub iv: Vec<u8>,
60 pub tag: Vec<u8>,
62 pub params: HashMap<String, Vec<u8>>,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70pub struct CompressionParameters {
71 pub algorithm: String,
73 pub level: u8,
75 pub original_size: u64,
77 pub params: HashMap<String, Vec<u8>>,
79}
80
81impl PqcMetadata {
82 #[must_use]
94 pub fn new() -> Self {
95 Self {
96 kem_params: None,
97 sig_params: None,
98 enc_params: EncParameters {
99 iv: Vec::new(),
100 tag: Vec::new(),
101 params: HashMap::new(),
102 },
103 compression_params: None,
104 custom: HashMap::new(),
105 }
106 }
107
108 pub fn validate(&self) -> Result<()> {
112 if self.enc_params.iv.is_empty() {
114 return Err(CryptoError::MetadataError(
115 "Encryption IV cannot be empty".to_string(),
116 ));
117 }
118
119 Ok(())
121 }
122
123 pub fn add_custom(&mut self, key: String, value: Vec<u8>) {
134 self.custom.insert(key, value);
135 }
136
137 #[must_use]
149 pub fn get_custom(&self, key: &str) -> Option<&[u8]> {
150 self.custom.get(key).map(Vec::as_slice)
151 }
152
153 #[must_use]
161 pub fn to_json_bytes(&self) -> Vec<u8> {
162 let mut root = Map::new();
163
164 let mut enc = Map::new();
165 enc.insert(
166 "iv".into(),
167 Value::String(STANDARD.encode(&self.enc_params.iv)),
168 );
169 enc.insert(
170 "tag".into(),
171 Value::String(STANDARD.encode(&self.enc_params.tag)),
172 );
173 enc.insert("params".into(), bytes_map_to_json(&self.enc_params.params));
174 root.insert("encryption_params".into(), Value::Object(enc));
175
176 if let Some(ref kem) = self.kem_params {
177 let mut m = Map::new();
178 m.insert(
179 "public_key".into(),
180 Value::String(STANDARD.encode(&kem.public_key)),
181 );
182 m.insert(
183 "ciphertext".into(),
184 Value::String(STANDARD.encode(&kem.ciphertext)),
185 );
186 m.insert("params".into(), bytes_map_to_json(&kem.params));
187 root.insert("kem_params".into(), Value::Object(m));
188 }
189
190 if let Some(ref sig) = self.sig_params {
191 let mut m = Map::new();
192 m.insert(
193 "public_key".into(),
194 Value::String(STANDARD.encode(&sig.public_key)),
195 );
196 m.insert(
197 "signature".into(),
198 Value::String(STANDARD.encode(&sig.signature)),
199 );
200 m.insert("params".into(), bytes_map_to_json(&sig.params));
201 root.insert("signature_params".into(), Value::Object(m));
202 }
203
204 if let Some(ref comp) = self.compression_params {
205 let mut m = Map::new();
206 m.insert("algorithm".into(), Value::String(comp.algorithm.clone()));
207 m.insert("level".into(), Value::Number(comp.level.into()));
208 m.insert(
209 "original_size".into(),
210 Value::Number(comp.original_size.into()),
211 );
212 m.insert("params".into(), bytes_map_to_json(&comp.params));
213 root.insert("compression_params".into(), Value::Object(m));
214 }
215
216 if !self.custom.is_empty() {
217 root.insert("custom".into(), bytes_map_to_json(&self.custom));
218 }
219
220 serde_json::to_vec(&Value::Object(root)).unwrap_or_default()
223 }
224
225 pub fn from_json_bytes(bytes: &[u8]) -> Result<Self> {
234 let root: Value = serde_json::from_slice(bytes)
235 .map_err(|e| CryptoError::MetadataError(format!("Invalid metadata JSON: {e}")))?;
236 let obj = root
237 .as_object()
238 .ok_or_else(|| CryptoError::MetadataError("Metadata must be a JSON object".into()))?;
239
240 let enc_obj = obj
241 .get("encryption_params")
242 .and_then(Value::as_object)
243 .ok_or_else(|| CryptoError::MetadataError("Missing encryption_params".into()))?;
244 let enc_params = EncParameters {
245 iv: decode_field(enc_obj, "iv")?,
246 tag: decode_field(enc_obj, "tag")?,
247 params: json_to_bytes_map(enc_obj.get("params"))?,
248 };
249
250 let kem_params = match obj.get("kem_params").and_then(Value::as_object) {
251 Some(m) => Some(KemParameters {
252 public_key: decode_field(m, "public_key")?,
253 ciphertext: decode_field(m, "ciphertext")?,
254 params: json_to_bytes_map(m.get("params"))?,
255 }),
256 None => None,
257 };
258
259 let sig_params = match obj.get("signature_params").and_then(Value::as_object) {
260 Some(m) => Some(SigParameters {
261 public_key: decode_field(m, "public_key")?,
262 signature: decode_field(m, "signature")?,
263 params: json_to_bytes_map(m.get("params"))?,
264 }),
265 None => None,
266 };
267
268 let compression_params = match obj.get("compression_params").and_then(Value::as_object) {
269 Some(m) => Some(CompressionParameters {
270 algorithm: m
271 .get("algorithm")
272 .and_then(Value::as_str)
273 .unwrap_or_default()
274 .to_string(),
275 level: m
276 .get("level")
277 .and_then(Value::as_u64)
278 .and_then(|n| u8::try_from(n).ok())
279 .unwrap_or(0),
280 original_size: m.get("original_size").and_then(Value::as_u64).unwrap_or(0),
281 params: json_to_bytes_map(m.get("params"))?,
282 }),
283 None => None,
284 };
285
286 Ok(Self {
287 kem_params,
288 sig_params,
289 enc_params,
290 compression_params,
291 custom: json_to_bytes_map(obj.get("custom"))?,
292 })
293 }
294}
295
296fn bytes_map_to_json(map: &HashMap<String, Vec<u8>>) -> Value {
298 let mut out = Map::new();
299 for (k, v) in map {
300 out.insert(k.clone(), Value::String(STANDARD.encode(v)));
301 }
302 Value::Object(out)
303}
304
305fn json_to_bytes_map(value: Option<&Value>) -> Result<HashMap<String, Vec<u8>>> {
307 let mut out = HashMap::new();
308 if let Some(Value::Object(m)) = value {
309 for (k, v) in m {
310 let s = v.as_str().ok_or_else(|| {
311 CryptoError::MetadataError(format!("Param '{k}' is not a string"))
312 })?;
313 let decoded = STANDARD
314 .decode(s)
315 .map_err(|e| CryptoError::MetadataError(format!("Param '{k}' base64: {e}")))?;
316 out.insert(k.clone(), decoded);
317 }
318 }
319 Ok(out)
320}
321
322fn decode_field(obj: &Map<String, Value>, key: &str) -> Result<Vec<u8>> {
324 let s = obj
325 .get(key)
326 .and_then(Value::as_str)
327 .ok_or_else(|| CryptoError::MetadataError(format!("Missing metadata field '{key}'")))?;
328 STANDARD
329 .decode(s)
330 .map_err(|e| CryptoError::MetadataError(format!("Field '{key}' base64: {e}")))
331}
332
333impl Default for PqcMetadata {
334 fn default() -> Self {
335 Self::new()
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn test_metadata_validation() {
345 let mut metadata = PqcMetadata::new();
346
347 assert!(metadata.validate().is_err());
349
350 metadata.enc_params.iv = vec![1; 12];
352 assert!(metadata.validate().is_ok());
353 }
354
355 #[test]
356 fn test_custom_parameters() {
357 let mut metadata = PqcMetadata::new();
358 metadata.add_custom("test".to_string(), vec![1, 2, 3]);
359
360 assert_eq!(metadata.get_custom("test"), Some(&[1, 2, 3][..]));
361 assert_eq!(metadata.get_custom("missing"), None);
362 }
363}