diff --git a/Cargo.toml b/Cargo.toml index 13ba6269..a64eedfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,6 +109,7 @@ peripherals = { path = "modules/peripherals" } bootsplash = { path = "modules/bootsplash" } internationalization = { path = "modules/internationalization" } testing = { path = "modules/testing" } +device = { path = "modules/device" } # - Drivers drivers_core = { path = "drivers/core" } @@ -199,6 +200,7 @@ members = [ "modules/internationalization/macros", "modules/graphics/fonts_generator", "modules/testing", + "modules/device", ] [patch.crates-io] diff --git a/drivers/shared/Cargo.toml b/drivers/shared/Cargo.toml index 604b5605..93a8b25d 100644 --- a/drivers/shared/Cargo.toml +++ b/drivers/shared/Cargo.toml @@ -7,7 +7,10 @@ edition = "2024" file_system = { workspace = true } log = { workspace = true } shared = { workspace = true } +device = { workspace = true } + getrandom = { version = "0.3.3" } +sha2 = { version = "0.10", features = ["asm"] } [dev-dependencies] critical-section = { workspace = true, features = ["std"] } diff --git a/drivers/shared/src/devices/hash.rs b/drivers/shared/src/devices/hash.rs new file mode 100644 index 00000000..d7fdbaaa --- /dev/null +++ b/drivers/shared/src/devices/hash.rs @@ -0,0 +1,107 @@ +use device::hash::{self, HashAlgorithm}; +use file_system::{ + BaseOperations, CharacterDevice, Context, ControlArgument, ControlCommand, Error, + MountOperations, Result, Size, +}; +use sha2::{Digest, Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256, digest::DynDigest}; + +#[derive(Clone)] +struct HashDeviceContext { + hasher: Box, +} + +unsafe impl Send for HashDeviceContext {} +unsafe impl Sync for HashDeviceContext {} + +impl HashDeviceContext { + fn new(algorithm: HashAlgorithm) -> Result { + let hasher: Box = match algorithm { + HashAlgorithm::Sha224 => Box::new(Sha224::new()), + HashAlgorithm::Sha256 => Box::new(Sha256::new()), + HashAlgorithm::Sha384 => Box::new(Sha384::new()), + HashAlgorithm::Sha512 => Box::new(Sha512::new()), + HashAlgorithm::Sha512_224 => Box::new(Sha512_224::new()), + HashAlgorithm::Sha512_256 => Box::new(Sha512_256::new()), + _ => return Err(Error::InvalidParameter), + }; + + Ok(Self { hasher }) + } +} + +pub struct HashDevice; + +impl BaseOperations for HashDevice { + fn open(&self, context: &mut Context) -> Result<()> { + context.set_private_data(Box::new(HashDeviceContext::new(HashAlgorithm::Sha256)?)); + Ok(()) + } + + fn close(&self, context: &mut Context) -> Result<()> { + context.take_private_data_of_type::(); + Ok(()) + } + + fn read(&self, context: &mut Context, buffer: &mut [u8], _: Size) -> Result { + let hash_context = context + .get_private_data_mutable_of_type::() + .ok_or_else(|| file_system::Error::InvalidParameter)?; + + if buffer.len() < hash_context.hasher.output_size() { + return Err(Error::InvalidParameter); + } + + let result = hash_context.hasher.clone().finalize(); + let length = result.len(); + buffer[..length].copy_from_slice(&result); + Ok(length) + } + + fn write(&self, context: &mut Context, buffer: &[u8], _: Size) -> Result { + let hash_context = context + .get_private_data_mutable_of_type::() + .ok_or_else(|| file_system::Error::InvalidParameter)?; + + hash_context.hasher.update(buffer); + Ok(buffer.len()) + } + + fn control( + &self, + context: &mut Context, + command: ControlCommand, + argument: &mut ControlArgument, + ) -> Result<()> { + let hash_context = context + .get_private_data_mutable_of_type::() + .ok_or_else(|| file_system::Error::InvalidParameter)?; + + match command { + hash::RESET => { + hash_context.hasher.reset(); + Ok(()) + } + hash::SET_ALGORITHM => { + let algorithm = argument + .cast::() + .ok_or_else(|| file_system::Error::InvalidParameter)?; + + *hash_context = HashDeviceContext::new(*algorithm)?; + Ok(()) + } + _ => Err(file_system::Error::UnsupportedOperation), + } + } + + fn clone_context(&self, context: &Context) -> Result { + let hash_context = context + .get_private_data_of_type::() + .ok_or_else(|| file_system::Error::InvalidParameter)?; + + Ok(Context::new(Some(hash_context.clone()))) + } +} + +impl MountOperations for HashDevice {} + +impl CharacterDevice for HashDevice {} diff --git a/drivers/shared/src/devices/mod.rs b/drivers/shared/src/devices/mod.rs index 713b5fbc..076f0a95 100644 --- a/drivers/shared/src/devices/mod.rs +++ b/drivers/shared/src/devices/mod.rs @@ -1,3 +1,5 @@ +mod hash; mod random; +pub use hash::*; pub use random::*; diff --git a/examples/native/src/main.rs b/examples/native/src/main.rs index 5c4ce5f4..8c041389 100644 --- a/examples/native/src/main.rs +++ b/examples/native/src/main.rs @@ -155,7 +155,12 @@ async fn main() { CharacterDevice, drivers_shared::devices::RandomDevice ), - (&"/devices/null", CharacterDevice, drivers_core::NullDevice) + (&"/devices/null", CharacterDevice, drivers_core::NullDevice), + ( + &"/devices/hasher", + CharacterDevice, + drivers_shared::devices::HashDevice + ), ] ) .await diff --git a/examples/wasm/src/main.rs b/examples/wasm/src/main.rs index 31440646..2cbfe67f 100644 --- a/examples/wasm/src/main.rs +++ b/examples/wasm/src/main.rs @@ -147,6 +147,11 @@ async fn main() { CharacterDevice, drivers_wasm::devices::HttpClientDevice ), + ( + &"/devices/hasher", + CharacterDevice, + drivers_shared::devices::HashDevice + ), ] ) .await diff --git a/modules/authentication/Cargo.toml b/modules/authentication/Cargo.toml index 2234c28f..063ec099 100644 --- a/modules/authentication/Cargo.toml +++ b/modules/authentication/Cargo.toml @@ -15,7 +15,6 @@ virtual_file_system = { workspace = true } users = { workspace = true } task = { workspace = true } file_system = { workspace = true } +device = { workspace = true } -# External dependencies -sha2 = { version = "0.10", features = ["asm"] } # SHA-512 cryptographic hashing miniserde = { workspace = true } diff --git a/modules/authentication/src/error.rs b/modules/authentication/src/error.rs index 24ee4b61..d409c260 100644 --- a/modules/authentication/src/error.rs +++ b/modules/authentication/src/error.rs @@ -73,6 +73,8 @@ pub enum Error { FailedToGetUserIdentifier(users::Error), /// Failed to close a file FailedToCloseFile(virtual_file_system::Error), + /// Failed to hash + FailedToHashPassword(virtual_file_system::Error), } impl Display for Error { @@ -160,6 +162,9 @@ impl Display for Error { Self::FailedToCloseFile(error) => { write!(formatter, "Failed to close file: {error}") } + Self::FailedToHashPassword(error) => { + write!(formatter, "Failed to hash password: {error}") + } } } } diff --git a/modules/authentication/src/hash.rs b/modules/authentication/src/hash.rs index 237adb5f..236067c3 100644 --- a/modules/authentication/src/hash.rs +++ b/modules/authentication/src/hash.rs @@ -16,7 +16,10 @@ use alloc::{ format, string::{String, ToString}, }; -use virtual_file_system::File; +use device::hash::{HashAlgorithm, SET_ALGORITHM}; +use file_system::AccessFlags; +use task::TaskIdentifier; +use virtual_file_system::{File, VirtualFileSystem}; use crate::{Error, RANDOM_DEVICE_PATH, Result}; @@ -40,11 +43,10 @@ use crate::{Error, RANDOM_DEVICE_PATH, Result}; /// /// The salt generation converts random bytes to lowercase letters (a-z) /// for readability while maintaining sufficient entropy for security. -pub async fn generate_salt() -> Result { - let virtual_file_system = virtual_file_system::get_instance(); - - let task = task::get_instance().get_current_task_identifier().await; - +pub async fn generate_salt( + virtual_file_system: &VirtualFileSystem<'_>, + task: TaskIdentifier, +) -> Result { let mut buffer = [0_u8; 16]; File::read_slice_from_path(virtual_file_system, task, RANDOM_DEVICE_PATH, &mut buffer) @@ -78,15 +80,49 @@ pub async fn generate_salt() -> Result { /// This function uses SHA-512, which is cryptographically secure and resistant /// to collision attacks. The salt prevents rainbow table attacks and ensures /// that identical passwords have different hashes. -pub fn hash_password(password: &str, salt: &str) -> String { - use sha2::Digest; +pub async fn hash_password( + virtual_file_system: &VirtualFileSystem<'_>, + task: TaskIdentifier, + password: &str, + salt: &str, +) -> Result { + let mut file = File::open( + virtual_file_system, + task, + "/devices/hasher", + AccessFlags::READ_WRITE.into(), + ) + .await + .map_err(Error::FailedToHashPassword)?; + + let mut algorithm = HashAlgorithm::Sha512; + + file.control(SET_ALGORITHM, &mut algorithm) + .await + .map_err(Error::FailedToHashPassword)?; + + file.write(password.as_bytes()) + .await + .map_err(Error::FailedToHashPassword)?; + + file.write(salt.as_bytes()) + .await + .map_err(Error::FailedToHashPassword)?; - let mut hasher = sha2::Sha512::new(); + let mut hash_buffer = [0_u8; 512 / 8]; - hasher.update(password.as_bytes()); - hasher.update(salt.as_bytes()); + file.read(&mut hash_buffer) + .await + .map_err(Error::FailedToHashPassword)?; + + file.close(virtual_file_system) + .await + .map_err(Error::FailedToCloseFile)?; - let hash = hasher.finalize(); + let hash = hash_buffer + .iter() + .map(|byte| format!("{:02x}", byte)) + .collect::(); - format!("{hash:x}") + Ok(hash) } diff --git a/modules/authentication/src/user.rs b/modules/authentication/src/user.rs index 459b950d..619893d6 100644 --- a/modules/authentication/src/user.rs +++ b/modules/authentication/src/user.rs @@ -204,14 +204,11 @@ pub async fn authenticate_user<'a>( ) -> Result { let path = get_user_file_path(user_name)?; - let mut user_file = File::open( - virtual_file_system, - task::get_instance().get_current_task_identifier().await, - path, - AccessFlags::Read.into(), - ) - .await - .map_err(Error::FailedToOpenUserFile)?; + let task = task::get_instance().get_current_task_identifier().await; + + let mut user_file = File::open(virtual_file_system, task, path, AccessFlags::Read.into()) + .await + .map_err(Error::FailedToOpenUserFile)?; let mut buffer = Vec::new(); @@ -228,7 +225,10 @@ pub async fn authenticate_user<'a>( let user: User = miniserde::json::from_str(core::str::from_utf8(&buffer).unwrap()) .map_err(Error::FailedToParseUserFile)?; - if hash_password(password, user.get_salt()) == user.get_hash() { + let hashed_password = + hash_password(virtual_file_system, task, password, user.get_salt()).await?; + + if hashed_password == user.get_hash() { Ok(user.get_identifier()) } else { Err(Error::InvalidPassword) @@ -289,9 +289,11 @@ pub async fn create_user<'a>( .map_err(Error::FailedToCreateUser)?; // - Hash password. - let salt = generate_salt().await?; + let task = task::get_instance().get_current_task_identifier().await; + + let salt = generate_salt(virtual_file_system, task).await?; - let hash = hash_password(password, &salt); + let hash = hash_password(virtual_file_system, task, password, &salt).await?; // - Write user file. let user = User::new( @@ -360,9 +362,9 @@ pub async fn change_user_password<'a>( ) -> Result<()> { let task = task::get_instance().get_current_task_identifier().await; - let salt = generate_salt().await?; + let salt = generate_salt(virtual_file_system, task).await?; - let hash = hash_password(new_password, &salt); + let hash = hash_password(virtual_file_system, task, new_password, &salt).await?; let user_file_path = Path::new(USERS_FOLDER_PATH) .to_owned() diff --git a/modules/device/Cargo.toml b/modules/device/Cargo.toml new file mode 100644 index 00000000..38471181 --- /dev/null +++ b/modules/device/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "device" +version = "0.1.0" +edition = "2024" + +[dependencies] +file_system = { workspace = true } diff --git a/modules/device/src/hash.rs b/modules/device/src/hash.rs new file mode 100644 index 00000000..fd3c9ee2 --- /dev/null +++ b/modules/device/src/hash.rs @@ -0,0 +1,18 @@ +use file_system::{ControlCommand, ControlDirectionFlags}; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum HashAlgorithm { + Md5 = 0, + Sha1 = 1, + Sha224 = 2, + Sha256 = 3, + Sha384 = 4, + Sha512 = 5, + Sha512_224 = 6, + Sha512_256 = 7, +} + +pub const RESET: ControlCommand = ControlCommand::new::<()>(ControlDirectionFlags::Write, b'H', 0); +pub const SET_ALGORITHM: ControlCommand = + ControlCommand::new::(ControlDirectionFlags::Write, b'H', 1); diff --git a/modules/device/src/lib.rs b/modules/device/src/lib.rs new file mode 100644 index 00000000..ec5d33c1 --- /dev/null +++ b/modules/device/src/lib.rs @@ -0,0 +1 @@ +pub mod hash; diff --git a/modules/testing/src/lib.rs b/modules/testing/src/lib.rs index 024d7f33..95909715 100644 --- a/modules/testing/src/lib.rs +++ b/modules/testing/src/lib.rs @@ -138,7 +138,12 @@ pub async fn initialize(graphics_enabled: bool) -> Standard { CharacterDevice, drivers_std::devices::TimeDevice ), - (&"/devices/null", CharacterDevice, drivers_core::NullDevice) + (&"/devices/null", CharacterDevice, drivers_core::NullDevice), + ( + &"/devices/hasher", + CharacterDevice, + drivers_shared::devices::HashDevice + ), ] ) .await