From 23a6e82d5bd08c95d0ca741345a84eab02c619dc Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Wed, 6 Dec 2023 17:05:11 +0000 Subject: [PATCH 01/11] Implemented a buf reader implementation for reading `/proc/crypto`. Where possible appropriate types have been used. Tests have been implemented for a few cases but not all. --- procfs-core/src/crypto.rs | 532 ++++++++++++++++++++++++++++++++++++++ procfs-core/src/lib.rs | 3 + 2 files changed, 535 insertions(+) create mode 100644 procfs-core/src/crypto.rs diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs new file mode 100644 index 0000000..5347d73 --- /dev/null +++ b/procfs-core/src/crypto.rs @@ -0,0 +1,532 @@ +use crate::{expect, FromBufRead, ProcError, ProcResult}; + +#[cfg(feature = "serde1")] +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + convert::TryFrom, + io::BufRead, + iter::{once, Peekable}, + str::FromStr, +}; + +/// Represents the data from `/proc/crypto`. +/// +/// Each block represents a cryptographic implementation that has been registered with the kernel. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct CryptoTable { + pub crypto_blocks: HashMap, +} + +/// Format of a crypto implementation represented in /proc/crypto. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct CryptoBlock { + pub name: String, + pub driver: String, + pub module: String, + pub priority: isize, + pub ref_count: isize, + pub self_test: SelfTest, + pub internal: bool, + pub fips_enabled: bool, + pub crypto_type: Type, +} + +impl FromBufRead for CryptoTable { + fn from_buf_read(r: R) -> ProcResult { + let mut lines = r.lines().flatten().peekable(); + let mut crypto_blocks = HashMap::new(); + loop { + let line = match lines.next() { + Some(line) => line, + // We got to the end of the file + None => break, + }; + // Just skip empty lines + if !line.is_empty() { + let mut split = line.split(":"); + let name = expect!(split.next()); + if name.trim() == "name" { + let name = expect!(split.next()).to_string(); + let block = CryptoBlock::from_iter(&mut lines, name.as_str())?; + crypto_blocks.insert(name, block); + } + } + } + + Ok(CryptoTable { crypto_blocks }) + } +} + +impl CryptoBlock { + fn from_iter>(iter: &mut Peekable, name: &str) -> ProcResult { + let driver = parse_line(iter, "driver", &name)?; + let module = parse_line(iter, "module", &name)?; + let priority = from_str!(isize, &parse_line(iter, "priority", &name)?); + let ref_count = from_str!(isize, &parse_line(iter, "refcnt", &name)?); + let self_test = SelfTest::try_from(parse_line(iter, "selftest", &name)?.as_str())?; + let internal = parse_bool(iter, "internal", name)?; + let fips_enabled = parse_fips(iter, name)?; + let crypto_type = Type::from_iter(iter, name)?; + Ok(CryptoBlock { + name: name.to_string(), + driver, + module, + priority, + ref_count, + self_test, + internal, + fips_enabled, + crypto_type, + }) + } +} + +/// Potential results for selftest. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub enum SelfTest { + Passed, + Unknown, +} + +impl TryFrom<&str> for SelfTest { + type Error = ProcError; + + fn try_from(value: &str) -> Result { + Ok(match value { + "passed" => Self::Passed, + "unknown" => Self::Unknown, + _ => { + return Err(build_internal_error!(format!( + "Could not recognise self test string {value}" + ))) + } + }) + } +} + +/// Enumeration of potential types and their associated data. Unknown at end to catch unrecognised types. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub enum Type { + /// Symmetric Key Cipher + Skcipher(Skcipher), + /// Single Block Cipher + Cipher(Cipher), + /// Syncronous Hash + Shash(Shash), + /// Asyncronous Hash + Ahash(Ahash), + /// Authenticated Encryption with Associated Data + Aead(Aead), + /// Random Number Generator + Rng(Rng), + /// Test algorithm + Larval(Larval), + /// Synchronous Compression + Scomp, + /// General Compression + Compression, + /// Asymmetric Cipher + AkCipher, + /// Key-agreement Protocol Primitive + Kpp, + /// Signature + Sig, + /// Unrecognised type, associated data collected in to a hash map + Unknown(Unknown), +} + +impl Type { + fn from_iter>(iter: &mut Peekable, name: &str) -> ProcResult { + let type_name = parse_line(iter, "type", name)?; + Ok(match type_name.as_str() { + "skcipher" => Self::Skcipher(Skcipher::parse(iter, name)?), + "cipher" => Self::Cipher(Cipher::parse(iter, name)?), + "shash" => Self::Shash(Shash::parse(iter, name)?), + "scomp" => Self::Scomp, + "compression" => Self::Compression, + "akcipher" => Self::AkCipher, + "kpp" => Self::Kpp, + "ahash" => Self::Ahash(Ahash::parse(iter, name)?), + "aead" => Self::Aead(Aead::parse(iter, name)?), + "rng" => Self::Rng(Rng::parse(iter, name)?), + "larval" => Self::Larval(Larval::parse(iter, name)?), + "sig" => Self::Sig, + unknown_name => Self::Unknown(Unknown::parse(iter, unknown_name, name)?), + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Skcipher { + r#async: bool, + block_size: usize, + min_key_size: usize, + max_key_size: usize, + iv_size: usize, + chunk_size: usize, + walk_size: usize, +} + +impl Skcipher { + fn parse>(iter: &mut T, name: &str) -> ProcResult { + let r#async = parse_bool(iter, "async", name)?; + let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); + let min_key_size = from_str!(usize, &parse_line(iter, "min keysize", name)?); + let max_key_size = from_str!(usize, &parse_line(iter, "max keysize", name)?); + let iv_size = from_str!(usize, &parse_line(iter, "ivsize", name)?); + let chunk_size = from_str!(usize, &parse_line(iter, "chunksize", name)?); + let walk_size = from_str!(usize, &parse_line(iter, "walksize", name)?); + Ok(Self { + r#async, + block_size, + min_key_size, + max_key_size, + iv_size, + chunk_size, + walk_size, + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Cipher { + block_size: usize, + min_key_size: usize, + max_key_size: usize, +} + +impl Cipher { + fn parse>(iter: &mut T, name: &str) -> ProcResult { + let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); + let min_key_size = from_str!(usize, &parse_line(iter, "min keysize", name)?); + let max_key_size = from_str!(usize, &parse_line(iter, "max keysize", name)?); + Ok(Self { + block_size, + min_key_size, + max_key_size, + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Shash { + block_size: usize, + digest_size: usize, +} + +impl Shash { + fn parse>(iter: &mut T, name: &str) -> ProcResult { + let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); + let digest_size = from_str!(usize, &parse_line(iter, "digestsize", name)?); + Ok(Self { + block_size, + digest_size, + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Ahash { + r#async: bool, + block_size: usize, + digest_size: usize, +} + +impl Ahash { + fn parse>(iter: &mut T, name: &str) -> ProcResult { + let r#async = parse_bool(iter, "async", name)?; + let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); + let digest_size = from_str!(usize, &parse_line(iter, "digestsize", name)?); + Ok(Self { + r#async, + block_size, + digest_size, + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Aead { + r#async: bool, + block_size: usize, + iv_size: usize, + max_auth_size: usize, + gen_iv: Option, +} + +impl Aead { + fn parse>(iter: &mut Peekable, name: &str) -> ProcResult { + let r#async = parse_bool(iter, "async", name)?; + let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); + let iv_size = from_str!(usize, &parse_line(iter, "ivsize", name)?); + let max_auth_size = from_str!(usize, &parse_line(iter, "maxauthsize", name)?); + let gen_iv = parse_gen_iv(iter, name)?; + Ok(Self { + r#async, + block_size, + iv_size, + max_auth_size, + gen_iv, + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Rng { + seed_size: usize, +} + +impl Rng { + fn parse>(iter: &mut T, name: &str) -> ProcResult { + let seed_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); + Ok(Self { seed_size }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Larval { + flags: u32, +} + +impl Larval { + fn parse>(iter: &mut T, name: &str) -> ProcResult { + let flags = from_str!(u32, &parse_line(iter, "flags", name)?); + Ok(Self { flags }) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] +pub struct Unknown { + fields: HashMap, +} + +impl Unknown { + fn parse>(iter: &mut T, unknown_name: &str, _name: &str) -> ProcResult { + let fields = iter + .map_while(|line| { + (!line.is_empty()).then(|| { + line.split_once(":") + .map(|(k, v)| (k.trim().to_string(), v.trim().to_string())) + }) + }) + .flatten() + .chain(once((String::from("name"), unknown_name.to_string()))) + .collect(); + Ok(Self { fields }) + } +} + +fn parse_line>(iter: &mut T, to_find: &str, name: &str) -> ProcResult { + let line = expect!(iter.next()); + let (key, val) = expect!(line.split_once(":")); + if key.trim() != to_find { + return Err(build_internal_error!(format!( + "could not locate {to_find} in /proc/crypto, block {name}" + ))); + } + Ok(val.trim().to_string()) +} + +fn parse_fips>(iter: &mut Peekable, name: &str) -> ProcResult { + if iter.peek().map(|line| line.contains("fips")).unwrap_or(false) { + let fips = parse_line(iter, "fips", name)?; + if fips == "yes" { + return Ok(true); + } + } + Ok(false) +} + +fn parse_bool>(iter: &mut T, to_find: &str, name: &str) -> ProcResult { + match parse_line(iter, to_find, name)?.as_str() { + "yes" => Ok(true), + "no" => Ok(false), + _ => Err(build_internal_error!(format!( + "{to_find} for {name} was unrecognised term" + ))), + } +} + +fn parse_gen_iv>(iter: &mut Peekable, name: &str) -> ProcResult> { + if iter.peek().map(|line| line.contains("geniv")).unwrap_or(false) { + let val = parse_line(iter, "geniv", name)?; + if val != "" { + return Ok(Some(expect!(usize::from_str(&val)))); + } + } + Ok(None) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn parse_line_correct() { + let line = "name : ghash".to_string(); + let mut iter = std::iter::once(line); + let val = match parse_line(&mut iter, "name", "parse_line_correct") { + Ok(val) => val, + Err(e) => panic!("{}", e), + }; + assert_eq!("ghash", val); + } + + #[test] + fn parse_line_incorrect() { + let line = "name : ghash".to_string(); + let mut iter = std::iter::once(line); + let val = match parse_line(&mut iter, "name", "parse_line_incorrect") { + Ok(val) => val, + Err(e) => panic!("{}", e), + }; + assert_ne!("hash", val); + } + + #[test] + fn parse_block() { + let block = r#"driver : deflate-generic +module : kernel +priority : 0 +refcnt : 2 +selftest : passed +internal : no +type : compression"#; + let mut iter = block.lines().map(|s| s.to_string()).peekable(); + let block = CryptoBlock::from_iter(&mut iter, "deflate"); + assert!(block.is_ok()); + let block = block.unwrap(); + assert_eq!(block.name, "deflate"); + assert_eq!(block.driver, "deflate-generic"); + assert_eq!(block.module, "kernel"); + assert_eq!(block.priority, 0); + assert_eq!(block.ref_count, 2); + assert_eq!(block.self_test, SelfTest::Passed); + assert_eq!(block.internal, false); + assert_eq!(block.crypto_type, Type::Compression); + } + + #[test] + fn parse_bad_block() { + let block = r#"driver : deflate-generic +module : kernel +priority : 0 +refcnt : 2 +selftest : passed +internal : no +type : aead"#; + let mut iter = block.lines().map(|s| s.to_string()).peekable(); + let block = CryptoBlock::from_iter(&mut iter, "deflate"); + assert!(block.is_err()); + } + + #[test] + fn parse_two() { + let block = r#"name : ccm(aes) +driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni)) +module : ccm +priority : 300 +refcnt : 4 +selftest : passed +internal : no +type : aead +async : no +blocksize : 1 +ivsize : 16 +maxauthsize : 16 +geniv : + +name : ctr(aes) +driver : ctr(aes-aesni) +module : kernel +priority : 300 +refcnt : 4 +selftest : passed +internal : no +type : skcipher +async : no +blocksize : 1 +min keysize : 16 +max keysize : 32 +ivsize : 16 +chunksize : 16 +walksize : 16 + +"#; + let blocks = CryptoTable::from_buf_read(block.as_bytes()); + assert!(blocks.is_ok()); + let blocks = blocks.unwrap(); + assert_eq!(blocks.crypto_blocks.len(), 2); + } + + #[test] + fn parse_unknown() { + let block = r#"driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni)) +module : ccm +priority : 300 +refcnt : 4 +selftest : passed +internal : no +type : unknown +key : val +key2 : val2 +"#; + let mut iter = block.lines().map(|s| s.to_string()).peekable(); + let block = CryptoBlock::from_iter(&mut iter, "ccm(aes)"); + assert!(block.is_ok()); + let block = block.unwrap(); + let mut compare = HashMap::new(); + compare.insert(String::from("key"), String::from("val")); + compare.insert(String::from("key2"), String::from("val2")); + compare.insert(String::from("name"), String::from("unknown")); + assert_eq!(block.crypto_type, Type::Unknown(Unknown { fields: compare })); + } + + #[test] + fn parse_unknown_top() { + let block = r#"name : ccm(aes) +driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni)) +module : ccm +priority : 300 +refcnt : 4 +selftest : passed +internal : no +type : unknown +key : val +key2 : val2 + +name : ctr(aes) +driver : ctr(aes-aesni) +module : kernel +priority : 300 +refcnt : 4 +selftest : passed +internal : no +type : skcipher +async : no +blocksize : 1 +min keysize : 16 +max keysize : 32 +ivsize : 16 +chunksize : 16 +walksize : 16 +"#; + let blocks = CryptoTable::from_buf_read(block.as_bytes()); + assert!(blocks.is_ok()); + let blocks = blocks.unwrap(); + assert_eq!(blocks.crypto_blocks.len(), 2); + } +} diff --git a/procfs-core/src/lib.rs b/procfs-core/src/lib.rs index a71ed34..ff743b1 100644 --- a/procfs-core/src/lib.rs +++ b/procfs-core/src/lib.rs @@ -351,6 +351,9 @@ pub use cgroups::*; mod cpuinfo; pub use cpuinfo::*; +mod crypto; +pub use crypto::*; + mod diskstats; pub use diskstats::*; From 5d105fadb9a199fb56ace05c56f2a328504fcab0 Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Wed, 6 Dec 2023 17:18:48 +0000 Subject: [PATCH 02/11] Fixed dumb typo in rng parsing. --- procfs-core/src/crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 5347d73..1b86c2a 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -289,7 +289,7 @@ pub struct Rng { impl Rng { fn parse>(iter: &mut T, name: &str) -> ProcResult { - let seed_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); + let seed_size = from_str!(usize, &parse_line(iter, "seedsize", name)?); Ok(Self { seed_size }) } } From 6039b900eb3045abe1d223de46784deee377fa3e Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Wed, 6 Dec 2023 17:23:00 +0000 Subject: [PATCH 03/11] Ensured that whitespace isn't left on name. --- procfs-core/src/crypto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 1b86c2a..74aafa1 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -49,7 +49,7 @@ impl FromBufRead for CryptoTable { let mut split = line.split(":"); let name = expect!(split.next()); if name.trim() == "name" { - let name = expect!(split.next()).to_string(); + let name = expect!(split.next()).trim().to_string(); let block = CryptoBlock::from_iter(&mut lines, name.as_str())?; crypto_blocks.insert(name, block); } From 7507faed9e41055c343295aed88689dbace2dd72 Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Wed, 6 Dec 2023 17:33:23 +0000 Subject: [PATCH 04/11] Moved the get for the inner hash table of CryptoTable out to CryptoTable. --- procfs-core/src/crypto.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 74aafa1..90132ad 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -3,8 +3,10 @@ use crate::{expect, FromBufRead, ProcError, ProcResult}; #[cfg(feature = "serde1")] use serde::{Deserialize, Serialize}; use std::{ + borrow::Borrow, collections::HashMap, convert::TryFrom, + hash::Hash, io::BufRead, iter::{once, Peekable}, str::FromStr, @@ -60,6 +62,13 @@ impl FromBufRead for CryptoTable { } } +impl CryptoTable { + pub fn get>(&self, target: &T) -> Option<&CryptoBlock> + { + self.crypto_blocks.get(target.as_ref()) + } +} + impl CryptoBlock { fn from_iter>(iter: &mut Peekable, name: &str) -> ProcResult { let driver = parse_line(iter, "driver", &name)?; From a2d77572e5e6292d99afc1a61d1ecef5babf60f6 Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Fri, 16 Feb 2024 11:52:45 +0000 Subject: [PATCH 05/11] Changed to use lines().peekable() to avoid removing error details. Changed to using a while let loop instead of a fixed loop. --- procfs-core/src/crypto.rs | 99 +++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 90132ad..145535b 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -3,10 +3,8 @@ use crate::{expect, FromBufRead, ProcError, ProcResult}; #[cfg(feature = "serde1")] use serde::{Deserialize, Serialize}; use std::{ - borrow::Borrow, collections::HashMap, convert::TryFrom, - hash::Hash, io::BufRead, iter::{once, Peekable}, str::FromStr, @@ -38,14 +36,10 @@ pub struct CryptoBlock { impl FromBufRead for CryptoTable { fn from_buf_read(r: R) -> ProcResult { - let mut lines = r.lines().flatten().peekable(); + let mut lines = r.lines().peekable(); let mut crypto_blocks = HashMap::new(); - loop { - let line = match lines.next() { - Some(line) => line, - // We got to the end of the file - None => break, - }; + while let Some(line) = lines.next() { + let line = line?; // Just skip empty lines if !line.is_empty() { let mut split = line.split(":"); @@ -63,14 +57,16 @@ impl FromBufRead for CryptoTable { } impl CryptoTable { - pub fn get>(&self, target: &T) -> Option<&CryptoBlock> - { + pub fn get>(&self, target: &T) -> Option<&CryptoBlock> { self.crypto_blocks.get(target.as_ref()) } } impl CryptoBlock { - fn from_iter>(iter: &mut Peekable, name: &str) -> ProcResult { + fn from_iter>>( + iter: &mut Peekable, + name: &str, + ) -> ProcResult { let driver = parse_line(iter, "driver", &name)?; let module = parse_line(iter, "module", &name)?; let priority = from_str!(isize, &parse_line(iter, "priority", &name)?); @@ -150,7 +146,10 @@ pub enum Type { } impl Type { - fn from_iter>(iter: &mut Peekable, name: &str) -> ProcResult { + fn from_iter>>( + iter: &mut Peekable, + name: &str, + ) -> ProcResult { let type_name = parse_line(iter, "type", name)?; Ok(match type_name.as_str() { "skcipher" => Self::Skcipher(Skcipher::parse(iter, name)?), @@ -165,7 +164,7 @@ impl Type { "rng" => Self::Rng(Rng::parse(iter, name)?), "larval" => Self::Larval(Larval::parse(iter, name)?), "sig" => Self::Sig, - unknown_name => Self::Unknown(Unknown::parse(iter, unknown_name, name)?), + unknown_name => Self::Unknown(Unknown::parse(iter, unknown_name)), }) } } @@ -183,7 +182,7 @@ pub struct Skcipher { } impl Skcipher { - fn parse>(iter: &mut T, name: &str) -> ProcResult { + fn parse>>(iter: &mut T, name: &str) -> ProcResult { let r#async = parse_bool(iter, "async", name)?; let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let min_key_size = from_str!(usize, &parse_line(iter, "min keysize", name)?); @@ -212,7 +211,7 @@ pub struct Cipher { } impl Cipher { - fn parse>(iter: &mut T, name: &str) -> ProcResult { + fn parse>>(iter: &mut T, name: &str) -> ProcResult { let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let min_key_size = from_str!(usize, &parse_line(iter, "min keysize", name)?); let max_key_size = from_str!(usize, &parse_line(iter, "max keysize", name)?); @@ -232,7 +231,7 @@ pub struct Shash { } impl Shash { - fn parse>(iter: &mut T, name: &str) -> ProcResult { + fn parse>>(iter: &mut T, name: &str) -> ProcResult { let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let digest_size = from_str!(usize, &parse_line(iter, "digestsize", name)?); Ok(Self { @@ -251,7 +250,7 @@ pub struct Ahash { } impl Ahash { - fn parse>(iter: &mut T, name: &str) -> ProcResult { + fn parse>>(iter: &mut T, name: &str) -> ProcResult { let r#async = parse_bool(iter, "async", name)?; let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let digest_size = from_str!(usize, &parse_line(iter, "digestsize", name)?); @@ -274,7 +273,10 @@ pub struct Aead { } impl Aead { - fn parse>(iter: &mut Peekable, name: &str) -> ProcResult { + fn parse>>( + iter: &mut Peekable, + name: &str, + ) -> ProcResult { let r#async = parse_bool(iter, "async", name)?; let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let iv_size = from_str!(usize, &parse_line(iter, "ivsize", name)?); @@ -297,7 +299,7 @@ pub struct Rng { } impl Rng { - fn parse>(iter: &mut T, name: &str) -> ProcResult { + fn parse>>(iter: &mut T, name: &str) -> ProcResult { let seed_size = from_str!(usize, &parse_line(iter, "seedsize", name)?); Ok(Self { seed_size }) } @@ -310,7 +312,7 @@ pub struct Larval { } impl Larval { - fn parse>(iter: &mut T, name: &str) -> ProcResult { + fn parse>>(iter: &mut T, name: &str) -> ProcResult { let flags = from_str!(u32, &parse_line(iter, "flags", name)?); Ok(Self { flags }) } @@ -323,9 +325,13 @@ pub struct Unknown { } impl Unknown { - fn parse>(iter: &mut T, unknown_name: &str, _name: &str) -> ProcResult { + fn parse>>(iter: &mut T, unknown_name: &str) -> Self { let fields = iter .map_while(|line| { + let line = match line { + Ok(line) => line, + Err(_) => return None, + }; (!line.is_empty()).then(|| { line.split_once(":") .map(|(k, v)| (k.trim().to_string(), v.trim().to_string())) @@ -334,12 +340,16 @@ impl Unknown { .flatten() .chain(once((String::from("name"), unknown_name.to_string()))) .collect(); - Ok(Self { fields }) + Self { fields } } } -fn parse_line>(iter: &mut T, to_find: &str, name: &str) -> ProcResult { - let line = expect!(iter.next()); +fn parse_line>>( + iter: &mut T, + to_find: &str, + name: &str, +) -> ProcResult { + let line = expect!(iter.next())?; let (key, val) = expect!(line.split_once(":")); if key.trim() != to_find { return Err(build_internal_error!(format!( @@ -349,8 +359,15 @@ fn parse_line>(iter: &mut T, to_find: &str, name: &st Ok(val.trim().to_string()) } -fn parse_fips>(iter: &mut Peekable, name: &str) -> ProcResult { - if iter.peek().map(|line| line.contains("fips")).unwrap_or(false) { +fn parse_fips>>( + iter: &mut Peekable, + name: &str, +) -> ProcResult { + if iter + .peek() + .map(|line| line.as_ref().is_ok_and(|line| line.contains("fips"))) + .unwrap_or(false) + { let fips = parse_line(iter, "fips", name)?; if fips == "yes" { return Ok(true); @@ -359,7 +376,11 @@ fn parse_fips>(iter: &mut Peekable, name: &str) -> Ok(false) } -fn parse_bool>(iter: &mut T, to_find: &str, name: &str) -> ProcResult { +fn parse_bool>>( + iter: &mut T, + to_find: &str, + name: &str, +) -> ProcResult { match parse_line(iter, to_find, name)?.as_str() { "yes" => Ok(true), "no" => Ok(false), @@ -369,8 +390,15 @@ fn parse_bool>(iter: &mut T, to_find: &str, name: &st } } -fn parse_gen_iv>(iter: &mut Peekable, name: &str) -> ProcResult> { - if iter.peek().map(|line| line.contains("geniv")).unwrap_or(false) { +fn parse_gen_iv>>( + iter: &mut Peekable, + name: &str, +) -> ProcResult> { + if iter + .peek() + .map(|line| line.as_ref().is_ok_and(|line| line.contains("geniv"))) + .unwrap_or(false) + { let val = parse_line(iter, "geniv", name)?; if val != "" { return Ok(Some(expect!(usize::from_str(&val)))); @@ -385,7 +413,7 @@ mod test { #[test] fn parse_line_correct() { - let line = "name : ghash".to_string(); + let line = Ok("name : ghash".to_string()); let mut iter = std::iter::once(line); let val = match parse_line(&mut iter, "name", "parse_line_correct") { Ok(val) => val, @@ -396,7 +424,7 @@ mod test { #[test] fn parse_line_incorrect() { - let line = "name : ghash".to_string(); + let line = Ok("name : ghash".to_string()); let mut iter = std::iter::once(line); let val = match parse_line(&mut iter, "name", "parse_line_incorrect") { Ok(val) => val, @@ -414,7 +442,7 @@ refcnt : 2 selftest : passed internal : no type : compression"#; - let mut iter = block.lines().map(|s| s.to_string()).peekable(); + let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable(); let block = CryptoBlock::from_iter(&mut iter, "deflate"); assert!(block.is_ok()); let block = block.unwrap(); @@ -437,8 +465,9 @@ refcnt : 2 selftest : passed internal : no type : aead"#; - let mut iter = block.lines().map(|s| s.to_string()).peekable(); + let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable(); let block = CryptoBlock::from_iter(&mut iter, "deflate"); + eprintln!("{block:?}"); assert!(block.is_err()); } @@ -493,7 +522,7 @@ type : unknown key : val key2 : val2 "#; - let mut iter = block.lines().map(|s| s.to_string()).peekable(); + let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable(); let block = CryptoBlock::from_iter(&mut iter, "ccm(aes)"); assert!(block.is_ok()); let block = block.unwrap(); From 9020b55c205e8e8d6f8ac026b4197a56afc01dcd Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Fri, 16 Feb 2024 12:04:11 +0000 Subject: [PATCH 06/11] Made structs fields public and renamed async to async_capable --- procfs-core/src/crypto.rs | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 145535b..5fff847 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -172,18 +172,18 @@ impl Type { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Skcipher { - r#async: bool, - block_size: usize, - min_key_size: usize, - max_key_size: usize, - iv_size: usize, - chunk_size: usize, - walk_size: usize, + pub async_capable : bool, + pub block_size: usize, + pub min_key_size: usize, + pub max_key_size: usize, + pub iv_size: usize, + pub chunk_size: usize, + pub walk_size: usize, } impl Skcipher { fn parse>>(iter: &mut T, name: &str) -> ProcResult { - let r#async = parse_bool(iter, "async", name)?; + let async_capable = parse_bool(iter, "async", name)?; let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let min_key_size = from_str!(usize, &parse_line(iter, "min keysize", name)?); let max_key_size = from_str!(usize, &parse_line(iter, "max keysize", name)?); @@ -191,7 +191,7 @@ impl Skcipher { let chunk_size = from_str!(usize, &parse_line(iter, "chunksize", name)?); let walk_size = from_str!(usize, &parse_line(iter, "walksize", name)?); Ok(Self { - r#async, + async_capable, block_size, min_key_size, max_key_size, @@ -205,9 +205,9 @@ impl Skcipher { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Cipher { - block_size: usize, - min_key_size: usize, - max_key_size: usize, + pub block_size: usize, + pub min_key_size: usize, + pub max_key_size: usize, } impl Cipher { @@ -226,8 +226,8 @@ impl Cipher { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Shash { - block_size: usize, - digest_size: usize, + pub block_size: usize, + pub digest_size: usize, } impl Shash { @@ -244,18 +244,18 @@ impl Shash { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Ahash { - r#async: bool, - block_size: usize, - digest_size: usize, + pub async_capable: bool, + pub block_size: usize, + pub digest_size: usize, } impl Ahash { fn parse>>(iter: &mut T, name: &str) -> ProcResult { - let r#async = parse_bool(iter, "async", name)?; + let async_capable = parse_bool(iter, "async", name)?; let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let digest_size = from_str!(usize, &parse_line(iter, "digestsize", name)?); Ok(Self { - r#async, + async_capable, block_size, digest_size, }) @@ -265,11 +265,11 @@ impl Ahash { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Aead { - r#async: bool, - block_size: usize, - iv_size: usize, - max_auth_size: usize, - gen_iv: Option, + pub async_capable: bool, + pub block_size: usize, + pub iv_size: usize, + pub max_auth_size: usize, + pub gen_iv: Option, } impl Aead { @@ -277,13 +277,13 @@ impl Aead { iter: &mut Peekable, name: &str, ) -> ProcResult { - let r#async = parse_bool(iter, "async", name)?; + let async_capable = parse_bool(iter, "async", name)?; let block_size = from_str!(usize, &parse_line(iter, "blocksize", name)?); let iv_size = from_str!(usize, &parse_line(iter, "ivsize", name)?); let max_auth_size = from_str!(usize, &parse_line(iter, "maxauthsize", name)?); let gen_iv = parse_gen_iv(iter, name)?; Ok(Self { - r#async, + async_capable, block_size, iv_size, max_auth_size, @@ -295,7 +295,7 @@ impl Aead { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Rng { - seed_size: usize, + pub seed_size: usize, } impl Rng { @@ -308,7 +308,7 @@ impl Rng { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Larval { - flags: u32, + pub flags: u32, } impl Larval { @@ -321,7 +321,7 @@ impl Larval { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Unknown { - fields: HashMap, + pub fields: HashMap, } impl Unknown { From 0d1b94405b3164380ed382b58d41cadbe31c9a7c Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Fri, 16 Feb 2024 12:19:51 +0000 Subject: [PATCH 07/11] Implemented current for CryptoTable and implemented a test of reading the local file. --- procfs/src/crypto.rs | 27 +++++++++++++++++++++++++++ procfs/src/lib.rs | 3 +++ 2 files changed, 30 insertions(+) create mode 100644 procfs/src/crypto.rs diff --git a/procfs/src/crypto.rs b/procfs/src/crypto.rs new file mode 100644 index 0000000..69dcce0 --- /dev/null +++ b/procfs/src/crypto.rs @@ -0,0 +1,27 @@ + +use procfs_core::ProcResult; +pub use procfs_core::CryptoTable; + +use crate::Current; + +impl Current for CryptoTable { + const PATH: &'static str = "/proc/crypto"; +} + +pub fn crypto() -> ProcResult { + CryptoTable::current() +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn read_crypto() { + let table = crypto(); + assert!(table.is_ok(), "Couldn't read local /proc/crypto"); + let table = table.unwrap(); + assert!(!table.crypto_blocks.is_empty(), "Crypto table was empty"); + } +} \ No newline at end of file diff --git a/procfs/src/lib.rs b/procfs/src/lib.rs index 068b38c..6a2d89e 100644 --- a/procfs/src/lib.rs +++ b/procfs/src/lib.rs @@ -182,6 +182,9 @@ pub(crate) fn write_value, T: fmt::Display>(path: P, value: T) -> mod cgroups; pub use crate::cgroups::*; +mod crypto; +pub use crate::crypto::*; + pub mod keyring; mod iomem; From e396b3ca809bbb731c4ffe44610219b6b7828958 Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Fri, 16 Feb 2024 12:24:38 +0000 Subject: [PATCH 08/11] Corrected clippy lints in crypto files. --- procfs-core/src/crypto.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 5fff847..32b17d1 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -42,7 +42,7 @@ impl FromBufRead for CryptoTable { let line = line?; // Just skip empty lines if !line.is_empty() { - let mut split = line.split(":"); + let mut split = line.split(':'); let name = expect!(split.next()); if name.trim() == "name" { let name = expect!(split.next()).trim().to_string(); @@ -67,11 +67,11 @@ impl CryptoBlock { iter: &mut Peekable, name: &str, ) -> ProcResult { - let driver = parse_line(iter, "driver", &name)?; - let module = parse_line(iter, "module", &name)?; - let priority = from_str!(isize, &parse_line(iter, "priority", &name)?); - let ref_count = from_str!(isize, &parse_line(iter, "refcnt", &name)?); - let self_test = SelfTest::try_from(parse_line(iter, "selftest", &name)?.as_str())?; + let driver = parse_line(iter, "driver", name)?; + let module = parse_line(iter, "module", name)?; + let priority = from_str!(isize, &parse_line(iter, "priority", name)?); + let ref_count = from_str!(isize, &parse_line(iter, "refcnt", name)?); + let self_test = SelfTest::try_from(parse_line(iter, "selftest", name)?.as_str())?; let internal = parse_bool(iter, "internal", name)?; let fips_enabled = parse_fips(iter, name)?; let crypto_type = Type::from_iter(iter, name)?; @@ -333,7 +333,7 @@ impl Unknown { Err(_) => return None, }; (!line.is_empty()).then(|| { - line.split_once(":") + line.split_once(':') .map(|(k, v)| (k.trim().to_string(), v.trim().to_string())) }) }) @@ -350,7 +350,7 @@ fn parse_line>>( name: &str, ) -> ProcResult { let line = expect!(iter.next())?; - let (key, val) = expect!(line.split_once(":")); + let (key, val) = expect!(line.split_once(':')); if key.trim() != to_find { return Err(build_internal_error!(format!( "could not locate {to_find} in /proc/crypto, block {name}" From 41c174e191ccdb298e23ae4f5995d4d4fbad940a Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Sat, 17 Feb 2024 13:21:29 +0000 Subject: [PATCH 09/11] Modify assert -> unwrap to be an expect, to carry error data in an easier to read manner. --- procfs/src/crypto.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/procfs/src/crypto.rs b/procfs/src/crypto.rs index 69dcce0..d552484 100644 --- a/procfs/src/crypto.rs +++ b/procfs/src/crypto.rs @@ -20,8 +20,7 @@ mod tests { #[test] fn read_crypto() { let table = crypto(); - assert!(table.is_ok(), "Couldn't read local /proc/crypto"); - let table = table.unwrap(); + let table = table.expect("CrytoTable should have been read"); assert!(!table.crypto_blocks.is_empty(), "Crypto table was empty"); } } \ No newline at end of file From 865a6c7b7e8437bcbeaa0ddaff8c3fe9ed89771c Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Thu, 22 Feb 2024 11:55:13 +0000 Subject: [PATCH 10/11] Changed CryptoTable implementation to being `Hashmap>` to account for duplicate names. Minor changes to tests, added test for two blocks that share a name but are distinct drivers. --- procfs-core/src/crypto.rs | 56 ++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 32b17d1..16276bf 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -16,7 +16,7 @@ use std::{ #[derive(Debug, Clone)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct CryptoTable { - pub crypto_blocks: HashMap, + pub crypto_blocks: HashMap>, } /// Format of a crypto implementation represented in /proc/crypto. @@ -37,7 +37,7 @@ pub struct CryptoBlock { impl FromBufRead for CryptoTable { fn from_buf_read(r: R) -> ProcResult { let mut lines = r.lines().peekable(); - let mut crypto_blocks = HashMap::new(); + let mut crypto_blocks: HashMap> = HashMap::new(); while let Some(line) = lines.next() { let line = line?; // Just skip empty lines @@ -47,7 +47,11 @@ impl FromBufRead for CryptoTable { if name.trim() == "name" { let name = expect!(split.next()).trim().to_string(); let block = CryptoBlock::from_iter(&mut lines, name.as_str())?; - crypto_blocks.insert(name, block); + if let Some(v) = crypto_blocks.get_mut(&name) { + v.push(block); + } else { + crypto_blocks.insert(name, vec![block]); + } } } } @@ -57,7 +61,7 @@ impl FromBufRead for CryptoTable { } impl CryptoTable { - pub fn get>(&self, target: &T) -> Option<&CryptoBlock> { + pub fn get>(&self, target: T) -> Option<&Vec> { self.crypto_blocks.get(target.as_ref()) } } @@ -172,7 +176,7 @@ impl Type { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Skcipher { - pub async_capable : bool, + pub async_capable: bool, pub block_size: usize, pub min_key_size: usize, pub max_key_size: usize, @@ -444,8 +448,7 @@ internal : no type : compression"#; let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable(); let block = CryptoBlock::from_iter(&mut iter, "deflate"); - assert!(block.is_ok()); - let block = block.unwrap(); + let block = block.expect("Should be have read one block"); assert_eq!(block.name, "deflate"); assert_eq!(block.driver, "deflate-generic"); assert_eq!(block.module, "kernel"); @@ -505,11 +508,40 @@ walksize : 16 "#; let blocks = CryptoTable::from_buf_read(block.as_bytes()); - assert!(blocks.is_ok()); - let blocks = blocks.unwrap(); + let blocks = blocks.expect("Should be have read two blocks"); assert_eq!(blocks.crypto_blocks.len(), 2); } + #[test] + fn parse_duplicate_name() { + let block = r#"name : deflate +driver : deflate-generic +module : kernel +priority : 0 +refcnt : 2 +selftest : passed +internal : no +type : compression + +name : deflate +driver : deflate-non-generic +module : kernel +priority : 0 +refcnt : 2 +selftest : passed +internal : no +type : compression +"#; + let blocks = CryptoTable::from_buf_read(block.as_bytes()); + let blocks = blocks.expect("Should be have read two blocks"); + assert_eq!(blocks.crypto_blocks.len(), 1); + let deflate_vec = blocks + .crypto_blocks + .get("deflate") + .expect("Should have created a vec of deflates"); + assert_eq!(deflate_vec.len(), 2); + } + #[test] fn parse_unknown() { let block = r#"driver : ccm_base(ctr(aes-aesni),cbcmac(aes-aesni)) @@ -524,8 +556,7 @@ key2 : val2 "#; let mut iter = block.lines().map(|s| Ok(s.to_string())).peekable(); let block = CryptoBlock::from_iter(&mut iter, "ccm(aes)"); - assert!(block.is_ok()); - let block = block.unwrap(); + let block = block.expect("Should be have read one block"); let mut compare = HashMap::new(); compare.insert(String::from("key"), String::from("val")); compare.insert(String::from("key2"), String::from("val2")); @@ -563,8 +594,7 @@ chunksize : 16 walksize : 16 "#; let blocks = CryptoTable::from_buf_read(block.as_bytes()); - assert!(blocks.is_ok()); - let blocks = blocks.unwrap(); + let blocks = blocks.expect("Should be have read one block"); assert_eq!(blocks.crypto_blocks.len(), 2); } } From 5ac611a928b384f2030312a2853219f5853cacf0 Mon Sep 17 00:00:00 2001 From: Chris Bury Date: Fri, 1 Mar 2024 15:53:49 +0000 Subject: [PATCH 11/11] Refactored to use the entry api for the crypto_blocks map. --- procfs-core/src/crypto.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/procfs-core/src/crypto.rs b/procfs-core/src/crypto.rs index 16276bf..5e7869f 100644 --- a/procfs-core/src/crypto.rs +++ b/procfs-core/src/crypto.rs @@ -47,11 +47,8 @@ impl FromBufRead for CryptoTable { if name.trim() == "name" { let name = expect!(split.next()).trim().to_string(); let block = CryptoBlock::from_iter(&mut lines, name.as_str())?; - if let Some(v) = crypto_blocks.get_mut(&name) { - v.push(block); - } else { - crypto_blocks.insert(name, vec![block]); - } + let blocks = crypto_blocks.entry(name).or_insert(Vec::new()); + blocks.push(block); } } }