1use crate::{expect, FromBufRead, ProcError, ProcResult};
2
3#[cfg(feature = "serde1")]
4use serde::{Deserialize, Serialize};
5use std::{
6 collections::HashMap,
7 convert::TryFrom,
8 io::BufRead,
9 iter::{once, Peekable},
10 str::FromStr,
11};
12
13#[derive(Debug, Clone)]
17#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
18pub struct CryptoTable {
19 pub crypto_blocks: HashMap<String, Vec<CryptoBlock>>,
20}
21
22#[derive(Debug, Clone)]
24#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
25pub struct CryptoBlock {
26 pub name: String,
27 pub driver: String,
28 pub module: String,
29 pub priority: isize,
30 pub ref_count: isize,
31 pub self_test: SelfTest,
32 pub internal: bool,
33 pub fips_enabled: bool,
34 pub crypto_type: Type,
35}
36
37impl FromBufRead for CryptoTable {
38 fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
39 let mut lines = r.lines().peekable();
40 let mut crypto_blocks: HashMap<String, Vec<CryptoBlock>> = HashMap::new();
41 while let Some(line) = lines.next() {
42 let line = line?;
43 if !line.is_empty() {
45 let mut split = line.split(':');
46 let name = expect!(split.next());
47 if name.trim() == "name" {
48 let name = expect!(split.next()).trim().to_string();
49 let block = CryptoBlock::from_iter(&mut lines, name.as_str())?;
50 let blocks = crypto_blocks.entry(name).or_insert(Vec::new());
51 blocks.push(block);
52 }
53 }
54 }
55
56 Ok(CryptoTable { crypto_blocks })
57 }
58}
59
60impl CryptoTable {
61 pub fn get<T: AsRef<str>>(&self, target: T) -> Option<&Vec<CryptoBlock>> {
62 self.crypto_blocks.get(target.as_ref())
63 }
64}
65
66impl CryptoBlock {
67 fn from_iter<T: Iterator<Item = Result<String, std::io::Error>>>(
68 iter: &mut Peekable<T>,
69 name: &str,
70 ) -> ProcResult<Self> {
71 let driver = parse_line(iter, "driver", name)?;
72 let module = parse_line(iter, "module", name)?;
73 let priority = from_str!(isize, &parse_line(iter, "priority", name)?);
74 let ref_count = from_str!(isize, &parse_line(iter, "refcnt", name)?);
75 let self_test = SelfTest::try_from(parse_line(iter, "selftest", name)?.as_str())?;
76 let internal = parse_bool(iter, "internal", name)?;
77 let fips_enabled = parse_fips(iter, name)?;
78 let crypto_type = Type::from_iter(iter, name)?;
79 Ok(CryptoBlock {
80 name: name.to_string(),
81 driver,
82 module,
83 priority,
84 ref_count,
85 self_test,
86 internal,
87 fips_enabled,
88 crypto_type,
89 })
90 }
91}
92
93#[derive(Debug, Clone, PartialEq)]
95#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
96pub enum SelfTest {
97 Passed,
98 Unknown,
99}
100
101impl TryFrom<&str> for SelfTest {
102 type Error = ProcError;
103
104 fn try_from(value: &str) -> Result<Self, ProcError> {
105 Ok(match value {
106 "passed" => Self::Passed,
107 "unknown" => Self::Unknown,
108 _ => {
109 return Err(build_internal_error!(format!(
110 "Could not recognise self test string {value}"
111 )))
112 }
113 })
114 }
115}
116
117#[derive(Debug, Clone, PartialEq)]
119#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
120pub enum Type {
121 Skcipher(Skcipher),
123 Cipher(Cipher),
125 Shash(Shash),
127 Ahash(Ahash),
129 Aead(Aead),
131 Rng(Rng),
133 Larval(Larval),
135 Scomp,
137 Compression,
139 AkCipher,
141 Kpp,
143 Sig,
145 Unknown(Unknown),
147}
148
149impl Type {
150 fn from_iter<T: Iterator<Item = Result<String, std::io::Error>>>(
151 iter: &mut Peekable<T>,
152 name: &str,
153 ) -> ProcResult<Self> {
154 let type_name = parse_line(iter, "type", name)?;
155 Ok(match type_name.as_str() {
156 "skcipher" => Self::Skcipher(Skcipher::parse(iter, name)?),
157 "cipher" => Self::Cipher(Cipher::parse(iter, name)?),
158 "shash" => Self::Shash(Shash::parse(iter, name)?),
159 "scomp" => Self::Scomp,
160 "compression" => Self::Compression,
161 "akcipher" => Self::AkCipher,
162 "kpp" => Self::Kpp,
163 "ahash" => Self::Ahash(Ahash::parse(iter, name)?),
164 "aead" => Self::Aead(Aead::parse(iter, name)?),
165 "rng" => Self::Rng(Rng::parse(iter, name)?),
166 "larval" => Self::Larval(Larval::parse(iter, name)?),
167 "sig" => Self::Sig,
168 unknown_name => Self::Unknown(Unknown::parse(iter, unknown_name)),
169 })
170 }
171}
172
173#[derive(Debug, Clone, PartialEq)]
174#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
175pub struct Skcipher {
176 pub async_capable: bool,
177 pub block_size: usize,
178 pub min_key_size: usize,
179 pub max_key_size: usize,
180 pub iv_size: usize,
181 pub chunk_size: usize,
182 pub walk_size: usize,
183}
184
185impl Skcipher {
186 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(iter: &mut T, name: &str) -> ProcResult<Self> {
187 let async_capable = parse_bool(iter, "async", name)?;
188 let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?);
189 let min_key_size = from_str!(usize, &parse_line(iter, "min keysize", name)?);
190 let max_key_size = from_str!(usize, &parse_line(iter, "max keysize", name)?);
191 let iv_size = from_str!(usize, &parse_line(iter, "ivsize", name)?);
192 let chunk_size = from_str!(usize, &parse_line(iter, "chunksize", name)?);
193 let walk_size = from_str!(usize, &parse_line(iter, "walksize", name)?);
194 Ok(Self {
195 async_capable,
196 block_size,
197 min_key_size,
198 max_key_size,
199 iv_size,
200 chunk_size,
201 walk_size,
202 })
203 }
204}
205
206#[derive(Debug, Clone, PartialEq)]
207#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
208pub struct Cipher {
209 pub block_size: usize,
210 pub min_key_size: usize,
211 pub max_key_size: usize,
212}
213
214impl Cipher {
215 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(iter: &mut T, name: &str) -> ProcResult<Self> {
216 let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?);
217 let min_key_size = from_str!(usize, &parse_line(iter, "min keysize", name)?);
218 let max_key_size = from_str!(usize, &parse_line(iter, "max keysize", name)?);
219 Ok(Self {
220 block_size,
221 min_key_size,
222 max_key_size,
223 })
224 }
225}
226
227#[derive(Debug, Clone, PartialEq)]
228#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
229pub struct Shash {
230 pub block_size: usize,
231 pub digest_size: usize,
232}
233
234impl Shash {
235 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(iter: &mut T, name: &str) -> ProcResult<Self> {
236 let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?);
237 let digest_size = from_str!(usize, &parse_line(iter, "digestsize", name)?);
238 Ok(Self {
239 block_size,
240 digest_size,
241 })
242 }
243}
244
245#[derive(Debug, Clone, PartialEq)]
246#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
247pub struct Ahash {
248 pub async_capable: bool,
249 pub block_size: usize,
250 pub digest_size: usize,
251}
252
253impl Ahash {
254 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(iter: &mut T, name: &str) -> ProcResult<Self> {
255 let async_capable = parse_bool(iter, "async", name)?;
256 let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?);
257 let digest_size = from_str!(usize, &parse_line(iter, "digestsize", name)?);
258 Ok(Self {
259 async_capable,
260 block_size,
261 digest_size,
262 })
263 }
264}
265
266#[derive(Debug, Clone, PartialEq)]
267#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
268pub struct Aead {
269 pub async_capable: bool,
270 pub block_size: usize,
271 pub iv_size: usize,
272 pub max_auth_size: usize,
273 pub gen_iv: Option<usize>,
274}
275
276impl Aead {
277 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(
278 iter: &mut Peekable<T>,
279 name: &str,
280 ) -> ProcResult<Self> {
281 let async_capable = parse_bool(iter, "async", name)?;
282 let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?);
283 let iv_size = from_str!(usize, &parse_line(iter, "ivsize", name)?);
284 let max_auth_size = from_str!(usize, &parse_line(iter, "maxauthsize", name)?);
285 let gen_iv = parse_gen_iv(iter, name)?;
286 Ok(Self {
287 async_capable,
288 block_size,
289 iv_size,
290 max_auth_size,
291 gen_iv,
292 })
293 }
294}
295
296#[derive(Debug, Clone, PartialEq)]
297#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
298pub struct Rng {
299 pub seed_size: usize,
300}
301
302impl Rng {
303 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(iter: &mut T, name: &str) -> ProcResult<Self> {
304 let seed_size = from_str!(usize, &parse_line(iter, "seedsize", name)?);
305 Ok(Self { seed_size })
306 }
307}
308
309#[derive(Debug, Clone, PartialEq)]
310#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
311pub struct Larval {
312 pub flags: u32,
313}
314
315impl Larval {
316 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(iter: &mut T, name: &str) -> ProcResult<Self> {
317 let flags = from_str!(u32, &parse_line(iter, "flags", name)?);
318 Ok(Self { flags })
319 }
320}
321
322#[derive(Debug, Clone, PartialEq)]
323#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
324pub struct Unknown {
325 pub fields: HashMap<String, String>,
326}
327
328impl Unknown {
329 fn parse<T: Iterator<Item = Result<String, std::io::Error>>>(iter: &mut T, unknown_name: &str) -> Self {
330 let fields = iter
331 .map_while(|line| {
332 let line = match line {
333 Ok(line) => line,
334 Err(_) => return None,
335 };
336 (!line.is_empty()).then(|| {
337 line.split_once(':')
338 .map(|(k, v)| (k.trim().to_string(), v.trim().to_string()))
339 })
340 })
341 .flatten()
342 .chain(once((String::from("name"), unknown_name.to_string())))
343 .collect();
344 Self { fields }
345 }
346}
347
348fn parse_line<T: Iterator<Item = Result<String, std::io::Error>>>(
349 iter: &mut T,
350 to_find: &str,
351 name: &str,
352) -> ProcResult<String> {
353 let line = expect!(iter.next())?;
354 let (key, val) = expect!(line.split_once(':'));
355 if key.trim() != to_find {
356 return Err(build_internal_error!(format!(
357 "could not locate {to_find} in /proc/crypto, block {name}"
358 )));
359 }
360 Ok(val.trim().to_string())
361}
362
363fn parse_fips<T: Iterator<Item = Result<String, std::io::Error>>>(
364 iter: &mut Peekable<T>,
365 name: &str,
366) -> ProcResult<bool> {
367 if iter
368 .peek()
369 .map(|line| line.as_ref().is_ok_and(|line| line.contains("fips")))
370 .unwrap_or(false)
371 {
372 let fips = parse_line(iter, "fips", name)?;
373 if fips == "yes" {
374 return Ok(true);
375 }
376 }
377 Ok(false)
378}
379
380fn parse_bool<T: Iterator<Item = Result<String, std::io::Error>>>(
381 iter: &mut T,
382 to_find: &str,
383 name: &str,
384) -> ProcResult<bool> {
385 match parse_line(iter, to_find, name)?.as_str() {
386 "yes" => Ok(true),
387 "no" => Ok(false),
388 _ => Err(build_internal_error!(format!(
389 "{to_find} for {name} was unrecognised term"
390 ))),
391 }
392}
393
394fn parse_gen_iv<T: Iterator<Item = Result<String, std::io::Error>>>(
395 iter: &mut Peekable<T>,
396 name: &str,
397) -> ProcResult<Option<usize>> {
398 if iter
399 .peek()
400 .map(|line| line.as_ref().is_ok_and(|line| line.contains("geniv")))
401 .unwrap_or(false)
402 {
403 let val = parse_line(iter, "geniv", name)?;
404 if val != "<none>" {
405 return Ok(Some(expect!(usize::from_str(&val))));
406 }
407 }
408 Ok(None)
409}
410
411#[cfg(test)]
412mod test {
413 use super::*;
414
415 #[test]
416 fn parse_line_correct() {
417 let line = Ok("name : ghash".to_string());
418 let mut iter = std::iter::once(line);
419 let val = match parse_line(&mut iter, "name", "parse_line_correct") {
420 Ok(val) => val,
421 Err(e) => panic!("{}", e),
422 };
423 assert_eq!("ghash", val);
424 }
425
426 #[test]
427 fn parse_line_incorrect() {
428 let line = Ok("name : ghash".to_string());
429 let mut iter = std::iter::once(line);
430 let val = match parse_line(&mut iter, "name", "parse_line_incorrect") {
431 Ok(val) => val,
432 Err(e) => panic!("{}", e),
433 };
434 assert_ne!("hash", val);
435 }
436
437 #[test]
438 fn parse_block() {
439 let block = r#"driver : deflate-generic
440module : kernel
441priority : 0
442refcnt : 2
443selftest : passed
444internal : no
445type : compression"#;
446 let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable();
447 let block = CryptoBlock::from_iter(&mut iter, "deflate");
448 let block = block.expect("Should be have read one block");
449 assert_eq!(block.name, "deflate");
450 assert_eq!(block.driver, "deflate-generic");
451 assert_eq!(block.module, "kernel");
452 assert_eq!(block.priority, 0);
453 assert_eq!(block.ref_count, 2);
454 assert_eq!(block.self_test, SelfTest::Passed);
455 assert_eq!(block.internal, false);
456 assert_eq!(block.crypto_type, Type::Compression);
457 }
458
459 #[test]
460 fn parse_bad_block() {
461 let block = r#"driver : deflate-generic
462module : kernel
463priority : 0
464refcnt : 2
465selftest : passed
466internal : no
467type : aead"#;
468 let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable();
469 let block = CryptoBlock::from_iter(&mut iter, "deflate");
470 eprintln!("{block:?}");
471 assert!(block.is_err());
472 }
473
474 #[test]
475 fn parse_two() {
476 let block = r#"name : ccm(aes)
477driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni))
478module : ccm
479priority : 300
480refcnt : 4
481selftest : passed
482internal : no
483type : aead
484async : no
485blocksize : 1
486ivsize : 16
487maxauthsize : 16
488geniv : <none>
489
490name : ctr(aes)
491driver : ctr(aes-aesni)
492module : kernel
493priority : 300
494refcnt : 4
495selftest : passed
496internal : no
497type : skcipher
498async : no
499blocksize : 1
500min keysize : 16
501max keysize : 32
502ivsize : 16
503chunksize : 16
504walksize : 16
505
506"#;
507 let blocks = CryptoTable::from_buf_read(block.as_bytes());
508 let blocks = blocks.expect("Should be have read two blocks");
509 assert_eq!(blocks.crypto_blocks.len(), 2);
510 }
511
512 #[test]
513 fn parse_duplicate_name() {
514 let block = r#"name : deflate
515driver : deflate-generic
516module : kernel
517priority : 0
518refcnt : 2
519selftest : passed
520internal : no
521type : compression
522
523name : deflate
524driver : deflate-non-generic
525module : kernel
526priority : 0
527refcnt : 2
528selftest : passed
529internal : no
530type : compression
531"#;
532 let blocks = CryptoTable::from_buf_read(block.as_bytes());
533 let blocks = blocks.expect("Should be have read two blocks");
534 assert_eq!(blocks.crypto_blocks.len(), 1);
535 let deflate_vec = blocks
536 .crypto_blocks
537 .get("deflate")
538 .expect("Should have created a vec of deflates");
539 assert_eq!(deflate_vec.len(), 2);
540 }
541
542 #[test]
543 fn parse_unknown() {
544 let block = r#"driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni))
545module : ccm
546priority : 300
547refcnt : 4
548selftest : passed
549internal : no
550type : unknown
551key : val
552key2 : val2
553"#;
554 let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable();
555 let block = CryptoBlock::from_iter(&mut iter, "ccm(aes)");
556 let block = block.expect("Should be have read one block");
557 let mut compare = HashMap::new();
558 compare.insert(String::from("key"), String::from("val"));
559 compare.insert(String::from("key2"), String::from("val2"));
560 compare.insert(String::from("name"), String::from("unknown"));
561 assert_eq!(block.crypto_type, Type::Unknown(Unknown { fields: compare }));
562 }
563
564 #[test]
565 fn parse_unknown_top() {
566 let block = r#"name : ccm(aes)
567driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni))
568module : ccm
569priority : 300
570refcnt : 4
571selftest : passed
572internal : no
573type : unknown
574key : val
575key2 : val2
576
577name : ctr(aes)
578driver : ctr(aes-aesni)
579module : kernel
580priority : 300
581refcnt : 4
582selftest : passed
583internal : no
584type : skcipher
585async : no
586blocksize : 1
587min keysize : 16
588max keysize : 32
589ivsize : 16
590chunksize : 16
591walksize : 16
592"#;
593 let blocks = CryptoTable::from_buf_read(block.as_bytes());
594 let blocks = blocks.expect("Should be have read one block");
595 assert_eq!(blocks.crypto_blocks.len(), 2);
596 }
597}