1use std::collections::HashMap;
4use std::convert::TryFrom;
5use std::error;
6
7use base64::STANDARD;
8use serde::{Deserialize, Serialize};
9use serde_repr::{Deserialize_repr, Serialize_repr};
10use serde_with::skip_serializing_none;
11
12base64_serde_type!(Base64Standard, STANDARD);
13
14#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
15#[serde(rename_all = "kebab-case")]
16pub enum Luks2ConfigFlag {
17 AllowDiscards,
18 SameCpuCrypt,
19 SubmitFromCryptCpus,
20 NoJournal,
21 #[serde(other, skip_serializing)]
22 Unknown,
23}
24
25#[skip_serializing_none]
26#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
27pub struct Luks2Config {
28 #[serde(with = "serde_with::rust::display_fromstr")]
29 pub json_size: u64,
30 #[serde(with = "serde_with::rust::display_fromstr")]
31 pub keyslots_size: u64,
32 pub flags: Option<Vec<Luks2ConfigFlag>>,
33 pub requirements: Option<Vec<String>>,
34}
35
36#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
37#[serde(rename_all = "snake_case")]
38pub enum Luks2DigestType {
39 Pbkdf2,
40}
41
42#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
43pub struct Luks2Digest {
44 #[serde(rename = "type")]
45 pub type_: Luks2DigestType,
46 pub keyslots: Vec<String>,
47 pub segments: Vec<String>,
48 #[serde(with = "Base64Standard")]
49 pub salt: Vec<u8>,
50 #[serde(with = "Base64Standard")]
51 pub digest: Vec<u8>,
52 pub hash: String,
54 pub iterations: u64,
55}
56
57#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
58#[serde(rename_all = "snake_case")]
59pub enum Luks2KeyslotType {
60 Luks2,
61}
62
63#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
64#[serde(rename_all = "snake_case")]
65pub enum Luks2KeyslotAfType {
66 Luks1,
67}
68
69#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
70pub struct Luks2KeyslotAf {
71 #[serde(rename = "type")]
72 pub type_: Luks2KeyslotAfType,
73 pub stripes: u32,
74 pub hash: String,
75}
76
77#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
78#[serde(rename_all = "snake_case")]
79pub enum Luks2KeyslotAreaType {
80 Raw,
81}
82
83#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
84pub struct Luks2KeyslotArea {
85 #[serde(rename = "type")]
86 pub type_: Luks2KeyslotAreaType,
87 #[serde(with = "serde_with::rust::display_fromstr")]
88 pub offset: u64,
89 #[serde(with = "serde_with::rust::display_fromstr")]
90 pub size: u64,
91 pub encryption: String,
92 pub key_size: u16,
93}
94
95#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
96#[serde(tag = "type", rename_all = "snake_case")]
97pub enum Luks2KeyslotKdf {
98 Pbkdf2 {
99 #[serde(with = "Base64Standard")]
100 salt: Vec<u8>,
101 hash: String,
102 iterations: u32,
103 },
104 Argon2i {
105 #[serde(with = "Base64Standard")]
106 salt: Vec<u8>,
107 time: u32,
108 memory: u32,
109 cpus: u32,
110 },
111 Argon2id {
112 #[serde(with = "Base64Standard")]
113 salt: Vec<u8>,
114 time: u32,
115 memory: u32,
116 cpus: u32,
117 },
118}
119
120#[derive(Serialize_repr, Deserialize_repr, Debug, PartialEq, Clone)]
121#[repr(u8)]
122pub enum Luks2KeyslotPriority {
123 Ignore = 0,
124 Normal = 1,
125 High = 2,
126}
127
128#[skip_serializing_none]
129#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
130pub struct Luks2Keyslot {
131 #[serde(rename = "type")]
132 pub type_: Luks2KeyslotType,
133 pub key_size: u32,
134 pub area: Luks2KeyslotArea,
135 pub kdf: Luks2KeyslotKdf,
136 pub af: Luks2KeyslotAf,
137 pub priority: Option<Luks2KeyslotPriority>,
138}
139
140#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
141#[serde(rename_all = "snake_case")]
142pub enum Luks2SegmentType {
143 Crypt,
144}
145
146#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
147pub struct Luks2SegmentIntegrity {
148 #[serde(rename = "type")]
149 pub type_: String,
150 pub journal_encryption: String,
151 pub journal_integrity: String,
152}
153
154#[skip_serializing_none]
155#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
156pub struct Luks2Segment {
157 #[serde(rename = "type")]
158 pub type_: Luks2SegmentType,
159 #[serde(with = "serde_with::rust::display_fromstr")]
160 pub offset: u64,
161 pub size: String, #[serde(with = "serde_with::rust::display_fromstr")]
163 pub iv_tweak: u64,
164 pub encryption: String,
165 pub sector_size: u32,
166 pub integrity: Option<Luks2SegmentIntegrity>,
167 pub flags: Option<Vec<String>>,
168}
169
170#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
171pub struct Luks2Token {
172 #[serde(rename = "type")]
173 pub type_: String,
174 pub keyslots: Vec<String>,
175 #[serde(flatten)]
176 pub other: serde_json::Map<String, serde_json::Value>,
177}
178
179impl TryFrom<&str> for Luks2Token {
180 type Error = Box<dyn error::Error>;
181
182 fn try_from(value: &str) -> Result<Self, Self::Error> {
183 let res = serde_json::from_str(value)?;
184 Ok(res)
185 }
186}
187
188impl TryFrom<&Luks2Token> for String {
189 type Error = Box<dyn error::Error>;
190
191 fn try_from(value: &Luks2Token) -> Result<Self, Self::Error> {
192 let res = serde_json::to_string(value)?;
193 Ok(res)
194 }
195}
196
197#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
198pub struct Luks2Metadata {
199 pub config: Luks2Config,
200 pub digests: HashMap<u16, Luks2Digest>,
201 pub keyslots: HashMap<u16, Luks2Keyslot>,
202 pub segments: HashMap<u16, Luks2Segment>,
203 pub tokens: HashMap<u16, Luks2Token>,
204}
205
206impl TryFrom<&str> for Luks2Metadata {
207 type Error = Box<dyn error::Error>;
208
209 fn try_from(value: &str) -> Result<Self, Self::Error> {
210 let res = serde_json::from_str(value)?;
211 Ok(res)
212 }
213}
214
215impl TryFrom<&Luks2Metadata> for String {
216 type Error = Box<dyn error::Error>;
217
218 fn try_from(m: &Luks2Metadata) -> Result<Self, Self::Error> {
219 let res = serde_json::to_string(m)?;
220 Ok(res)
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_json_config_flag_from() {
230 assert_eq!(
231 serde_json::from_str::<Luks2ConfigFlag>(r#""allow-discards""#).unwrap(),
232 Luks2ConfigFlag::AllowDiscards
233 );
234 assert_eq!(
235 serde_json::from_str::<Luks2ConfigFlag>(r#""same-cpu-crypt""#).unwrap(),
236 Luks2ConfigFlag::SameCpuCrypt
237 );
238 assert_eq!(
239 serde_json::from_str::<Luks2ConfigFlag>(r#""submit-from-crypt-cpus""#).unwrap(),
240 Luks2ConfigFlag::SubmitFromCryptCpus
241 );
242 assert_eq!(
243 serde_json::from_str::<Luks2ConfigFlag>(r#""no-journal""#).unwrap(),
244 Luks2ConfigFlag::NoJournal
245 );
246 assert_eq!(
247 serde_json::from_str::<Luks2ConfigFlag>(r#""random-flag""#).unwrap(),
248 Luks2ConfigFlag::Unknown
249 );
250 }
251
252 #[test]
253 fn test_json_config_flag_to() {
254 assert_eq!(
255 r#""allow-discards""#,
256 serde_json::to_string(&Luks2ConfigFlag::AllowDiscards).unwrap()
257 );
258 assert_eq!(
259 r#""same-cpu-crypt""#,
260 serde_json::to_string(&Luks2ConfigFlag::SameCpuCrypt).unwrap()
261 );
262 assert_eq!(
263 r#""submit-from-crypt-cpus""#,
264 serde_json::to_string(&Luks2ConfigFlag::SubmitFromCryptCpus).unwrap()
265 );
266 assert_eq!(
267 r#""no-journal""#,
268 serde_json::to_string(&Luks2ConfigFlag::NoJournal).unwrap()
269 );
270 assert_eq!(true, serde_json::to_string(&Luks2ConfigFlag::Unknown).is_err());
271 }
272
273 #[test]
274 fn test_json_config() {
275 let js = r#"{"json_size":"12288","keyslots_size":"16744448"}"#;
276 let c: Luks2Config = serde_json::from_str(js).unwrap();
277 assert_eq!(c.json_size, 12288);
278 assert_eq!(c.keyslots_size, 16744448);
279 assert_eq!(c.flags, None);
280 assert_eq!(c.requirements, None);
281
282 let to_js = serde_json::to_string(&c).unwrap();
283 assert_eq!(to_js, js);
284 }
285
286 #[test]
287 fn test_json_digests() {
288 let js = r#"{"type":"pbkdf2","keyslots":["0"],"segments":["0"],"salt":"WYkbZOppCHRvwDvrVIbxKimZ4qjXDSizlcMRvyE7EM0=","digest":"SH2Ks6EOcW9r8Q82mLQG8+5H3TvAYLdLw8VuP7Vo5eM=","hash":"sha256","iterations":223672}"#;
289 let d: Luks2Digest = serde_json::from_str(js).unwrap();
290
291 assert_eq!(d.type_, Luks2DigestType::Pbkdf2);
292 assert_eq!(d.keyslots, vec!("0"));
293 assert_eq!(d.segments, vec!("0"));
294 assert_eq!(d.hash, "sha256");
295 assert_eq!(d.iterations, 223672);
296 assert_eq!(
297 d.salt,
298 [
299 89u8, 137, 27, 100, 234, 105, 8, 116, 111, 192, 59, 235, 84, 134, 241, 42, 41, 153, 226, 168, 215, 13,
300 40, 179, 149, 195, 17, 191, 33, 59, 16, 205
301 ]
302 );
303 assert_eq!(
304 d.digest,
305 [
306 72u8, 125, 138, 179, 161, 14, 113, 111, 107, 241, 15, 54, 152, 180, 6, 243, 238, 71, 221, 59, 192, 96,
307 183, 75, 195, 197, 110, 63, 181, 104, 229, 227
308 ]
309 );
310
311 let to_js = serde_json::to_string(&d).unwrap();
312 assert_eq!(to_js, js);
313 }
314
315 #[test]
316 fn test_json_keyslot_af() {
317 let js = r#"{"type":"luks1","stripes":4000,"hash":"sha256"}"#;
318 let a: Luks2KeyslotAf = serde_json::from_str(js).unwrap();
319
320 assert_eq!(a.type_, Luks2KeyslotAfType::Luks1);
321 assert_eq!(a.stripes, 4000);
322 assert_eq!(a.hash, "sha256");
323
324 let to_js = serde_json::to_string(&a).unwrap();
325 assert_eq!(to_js, js);
326 }
327
328 #[test]
329 fn test_json_keyslot_area() {
330 let js = r#"{"type":"raw","offset":"32768","size":"258048","encryption":"aes-xts-plain64","key_size":64}"#;
331 let a: Luks2KeyslotArea = serde_json::from_str(js).unwrap();
332
333 assert_eq!(a.type_, Luks2KeyslotAreaType::Raw);
334 assert_eq!(a.offset, 32768);
335 assert_eq!(a.size, 258048);
336 assert_eq!(a.encryption, "aes-xts-plain64");
337 assert_eq!(a.key_size, 64);
338
339 let to_js = serde_json::to_string(&a).unwrap();
340 assert_eq!(to_js, js);
341 }
342
343 #[test]
344 fn test_json_keyslot_kdf_pbkdf2() {
345 let js = r#"{"type":"pbkdf2","salt":"SH2Ks6EOcW9r8Q82mLQG8+5H3TvAYLdLw8VuP7Vo5eM=","hash":"sha256","iterations":1234}"#;
346 let k: Luks2KeyslotKdf = serde_json::from_str(js).unwrap();
347
348 match &k {
349 Luks2KeyslotKdf::Pbkdf2 { salt, hash, iterations } => {
350 assert_eq!(
351 salt[..],
352 [
353 72u8, 125, 138, 179, 161, 14, 113, 111, 107, 241, 15, 54, 152, 180, 6, 243, 238, 71, 221, 59,
354 192, 96, 183, 75, 195, 197, 110, 63, 181, 104, 229, 227
355 ]
356 );
357 assert_eq!(hash, "sha256");
358 assert_eq!(*iterations, 1234);
359 }
360 _ => assert!(false, "expected pbkdf2"),
361 }
362
363 let to_js = serde_json::to_string(&k).unwrap();
364 assert_eq!(to_js, js);
365 }
366
367 #[test]
368 fn test_json_keyslot_kdf_argon2i() {
369 let js = r#"{"type":"argon2i","salt":"cNqP5YVtK2DRlLvTPZU8LXy4jWi1+QJPH+Gz3WouBTI=","time":8,"memory":1048576,"cpus":4}"#;
370 let k: Luks2KeyslotKdf = serde_json::from_str(js).unwrap();
371
372 match &k {
373 Luks2KeyslotKdf::Argon2i {
374 salt,
375 time,
376 memory,
377 cpus,
378 } => {
379 assert_eq!(
380 salt[..],
381 [
382 112u8, 218, 143, 229, 133, 109, 43, 96, 209, 148, 187, 211, 61, 149, 60, 45, 124, 184, 141,
383 104, 181, 249, 2, 79, 31, 225, 179, 221, 106, 46, 5, 50
384 ]
385 );
386 assert_eq!(*time, 8);
387 assert_eq!(*memory, 1048576);
388 assert_eq!(*cpus, 4);
389 }
390 _ => assert!(false, "expected argon2i"),
391 }
392
393 let to_js = serde_json::to_string(&k).unwrap();
394 assert_eq!(to_js, js);
395 }
396
397 #[test]
398 fn test_json_keyslot_kdf_argon2id() {
399 let js = r#"{"type":"argon2id","salt":"cNqP5YVtK2DRlLvTPZU8LXy4jWi1+QJPH+Gz3WouBTI=","time":8,"memory":1048576,"cpus":4}"#;
400 let k: Luks2KeyslotKdf = serde_json::from_str(js).unwrap();
401
402 match &k {
403 Luks2KeyslotKdf::Argon2id {
404 salt,
405 time,
406 memory,
407 cpus,
408 } => {
409 assert_eq!(
410 salt[..],
411 [
412 112u8, 218, 143, 229, 133, 109, 43, 96, 209, 148, 187, 211, 61, 149, 60, 45, 124, 184, 141,
413 104, 181, 249, 2, 79, 31, 225, 179, 221, 106, 46, 5, 50
414 ]
415 );
416 assert_eq!(*time, 8);
417 assert_eq!(*memory, 1048576);
418 assert_eq!(*cpus, 4);
419 }
420 _ => assert!(false, "expected argon2id"),
421 }
422
423 let to_js = serde_json::to_string(&k).unwrap();
424 assert_eq!(to_js, js);
425 }
426
427 #[test]
428 fn test_json_segment() {
429 let js = r#"{"type":"crypt","offset":"16777216","size":"dynamic","iv_tweak":"0","encryption":"aes-xts-plain64","sector_size":512}"#;
430 let s: Luks2Segment = serde_json::from_str(js).unwrap();
431
432 assert_eq!(s.type_, Luks2SegmentType::Crypt);
433 assert_eq!(s.offset, 16777216);
434 assert_eq!(s.size, "dynamic");
435 assert_eq!(s.iv_tweak, 0);
436 assert_eq!(s.encryption, "aes-xts-plain64");
437 assert_eq!(s.sector_size, 512);
438
439 let to_js = serde_json::to_string(&s).unwrap();
440 assert_eq!(to_js, js);
441 }
442
443 #[test]
444 fn test_json_token() {
445 let js = r#"{"type":"luks2-keyring","keyslots":["0","1"],"key_description":"my:key"}"#;
446 let t: Luks2Token = serde_json::from_str(js).unwrap();
447
448 let mut m = serde_json::Map::new();
449 let _ = m.insert(
450 "key_description".to_owned(),
451 serde_json::Value::String("my:key".to_owned()),
452 );
453
454 assert_eq!(t.type_, "luks2-keyring");
455 assert_eq!(t.keyslots, ["0", "1"]);
456 assert_eq!(t.other, m);
457
458 let to_js = serde_json::to_string(&t).unwrap();
459 assert_eq!(to_js, js);
460 }
461
462 #[test]
463 fn test_json_metadata_simple() {
464 let js = r#"{"config":{"json_size":"12288","keyslots_size":"16744448"},"digests":{"0":{"type":"pbkdf2","keyslots":["0"],"segments":["0"],"salt":"WYkbZOppCHRvwDvrVIbxKimZ4qjXDSizlcMRvyE7EM0=","digest":"SH2Ks6EOcW9r8Q82mLQG8+5H3TvAYLdLw8VuP7Vo5eM=","hash":"sha256","iterations":223672}},"keyslots":{"0":{"type":"luks2","key_size":64,"area":{"type":"raw","offset":"32768","size":"258048","encryption":"aes-xts-plain64","key_size":64},"kdf":{"type":"argon2i","salt":"cNqP5YVtK2DRlLvTPZU8LXy4jWi1+QJPH+Gz3WouBTI=","time":8,"memory":1048576,"cpus":4},"af":{"type":"luks1","stripes":4000,"hash":"sha256"}}},"segments":{"0":{"type":"crypt","offset":"16777216","size":"dynamic","iv_tweak":"0","encryption":"aes-xts-plain64","sector_size":512}},"tokens":{}}"#;
465 let m: Luks2Metadata = serde_json::from_str(js).unwrap();
466
467 let expected = Luks2Metadata {
468 config: Luks2Config {
469 json_size: 12288,
470 keyslots_size: 16744448,
471 flags: None,
472 requirements: None,
473 },
474 digests: [(
475 0u16,
476 Luks2Digest {
477 type_: Luks2DigestType::Pbkdf2,
478 keyslots: vec!["0".to_owned()],
479 segments: vec!["0".to_owned()],
480 salt: vec![
481 89, 137, 27, 100, 234, 105, 8, 116, 111, 192, 59, 235, 84, 134, 241, 42, 41, 153, 226, 168,
482 215, 13, 40, 179, 149, 195, 17, 191, 33, 59, 16, 205,
483 ],
484 digest: vec![
485 72, 125, 138, 179, 161, 14, 113, 111, 107, 241, 15, 54, 152, 180, 6, 243, 238, 71, 221, 59,
486 192, 96, 183, 75, 195, 197, 110, 63, 181, 104, 229, 227,
487 ],
488 hash: "sha256".to_string(),
489 iterations: 223672,
490 },
491 )]
492 .iter()
493 .cloned()
494 .collect(),
495 keyslots: [(
496 0u16,
497 Luks2Keyslot {
498 type_: Luks2KeyslotType::Luks2,
499 key_size: 64,
500 area: Luks2KeyslotArea {
501 type_: Luks2KeyslotAreaType::Raw,
502 offset: 32768,
503 size: 258048,
504 encryption: "aes-xts-plain64".to_string(),
505 key_size: 64,
506 },
507 kdf: Luks2KeyslotKdf::Argon2i {
508 salt: vec![
509 112, 218, 143, 229, 133, 109, 43, 96, 209, 148, 187, 211, 61, 149, 60, 45, 124, 184, 141,
510 104, 181, 249, 2, 79, 31, 225, 179, 221, 106, 46, 5, 50,
511 ],
512 time: 8,
513 memory: 1048576,
514 cpus: 4,
515 },
516 af: Luks2KeyslotAf {
517 type_: Luks2KeyslotAfType::Luks1,
518 stripes: 4000,
519 hash: "sha256".to_string(),
520 },
521 priority: None,
522 },
523 )]
524 .iter()
525 .cloned()
526 .collect(),
527 segments: [(
528 0u16,
529 Luks2Segment {
530 type_: Luks2SegmentType::Crypt,
531 offset: 16777216,
532 size: "dynamic".to_string(),
533 iv_tweak: 0,
534 encryption: "aes-xts-plain64".to_string(),
535 sector_size: 512,
536 integrity: None,
537 flags: None,
538 },
539 )]
540 .iter()
541 .cloned()
542 .collect(),
543 tokens: HashMap::new(),
544 };
545
546 assert_eq!(m, expected);
547
548 let to_js = serde_json::to_string(&m).unwrap();
549 assert_eq!(to_js, js);
550 }
551
552 #[test]
553 fn test_json_metadata_example() {
554 let js = r#"{
555 "keyslots":{
556 "0":{
557 "type":"luks2",
558 "key_size":32,
559 "af":{
560 "type":"luks1",
561 "stripes":4000,
562 "hash":"sha256"
563 },
564 "area":{
565 "type":"raw",
566 "encryption":"aes-xts-plain64",
567 "key_size":32,
568 "offset":"32768",
569 "size":"131072"
570 },
571 "kdf":{
572 "type":"argon2i",
573 "time":4,
574 "memory":235980,
575 "cpus":2,
576 "salt":"z6vz4xK7cjan92rDA5JF8O6Jk2HouV0O8DMB6GlztVk="
577 }
578 },
579 "1":{
580 "type":"luks2",
581 "key_size":32,
582 "af":{
583 "type":"luks1",
584 "stripes":4000,
585 "hash":"sha256"
586 },
587 "area":{
588 "type":"raw",
589 "encryption":"aes-xts-plain64",
590 "key_size":32,
591 "offset":"163840",
592 "size":"131072"
593 },
594 "kdf":{
595 "type":"pbkdf2",
596 "hash":"sha256",
597 "iterations":1774240,
598 "salt":"vWcwY3rx2fKpXW2Q6oSCNf8j5bvdJyEzB6BNXECGDsI="
599 }
600 }
601 },
602 "tokens":{
603 "0":{
604 "type":"luks2-keyring",
605 "keyslots":[
606 "1"
607 ],
608 "key_description":"MyKeyringKeyID"
609 }
610 },
611 "segments":{
612 "0":{
613 "type":"crypt",
614 "offset":"4194304",
615 "iv_tweak":"0",
616 "size":"dynamic",
617 "encryption":"aes-xts-plain64",
618 "sector_size":512
619 }
620 },
621 "digests":{
622 "0":{
623 "type":"pbkdf2",
624 "keyslots":[
625 "0",
626 "1"
627 ],
628 "segments":[
629 "0"
630 ],
631 "hash":"sha256",
632 "iterations":110890,
633 "salt":"G8gqtKhS96IbogHyJLO+t9kmjLkx+DM3HHJqQtgc2Dk=",
634 "digest":"C9JWko5m+oYmjg6R0t/98cGGzLr/4UaG3hImSJMivfc="
635 }
636 },
637 "config":{
638 "json_size":"12288",
639 "keyslots_size":"4161536",
640 "flags":[
641 "allow-discards"
642 ]
643 }
644 }
645 "#;
646 let m: Luks2Metadata = serde_json::from_str(js).unwrap();
647
648 assert_eq!(m.config.flags, Some(vec!(Luks2ConfigFlag::AllowDiscards)));
650 }
651}