1use spin::RwLock;
23
24use crate::{
25 crypto::hash::{sha256, Hash256},
26 error::KernelError,
27 sync::once_lock::OnceLock,
28};
29
30pub type UserId = u32;
32
33const MAX_ACCOUNTS: usize = 16;
40
41const MAX_PASSWORD_HISTORY: usize = 5;
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum AuthResult {
51 Success,
52 InvalidCredentials,
53 AccountLocked,
54 PasswordExpired,
55 MfaRequired,
56 AccountExpired,
57 Denied,
58}
59
60#[derive(Debug, Clone, Copy)]
66pub struct PasswordPolicy {
67 pub min_length: usize,
69 pub require_uppercase: bool,
71 pub require_lowercase: bool,
73 pub require_digit: bool,
75 pub require_special: bool,
77 pub history_size: usize,
79}
80
81impl PasswordPolicy {
82 pub const fn default_policy() -> Self {
84 Self {
85 min_length: 8,
86 require_uppercase: true,
87 require_lowercase: true,
88 require_digit: true,
89 require_special: false,
90 history_size: 5,
91 }
92 }
93
94 pub const fn relaxed() -> Self {
96 Self {
97 min_length: 1,
98 require_uppercase: false,
99 require_lowercase: false,
100 require_digit: false,
101 require_special: false,
102 history_size: 0,
103 }
104 }
105
106 pub fn validate_password(&self, password: &str) -> Result<(), KernelError> {
111 if password.len() < self.min_length {
112 return Err(KernelError::InvalidArgument {
113 name: "password",
114 value: "too short",
115 });
116 }
117
118 if self.require_uppercase && !password.bytes().any(|b| b.is_ascii_uppercase()) {
119 return Err(KernelError::InvalidArgument {
120 name: "password",
121 value: "must contain an uppercase letter",
122 });
123 }
124
125 if self.require_lowercase && !password.bytes().any(|b| b.is_ascii_lowercase()) {
126 return Err(KernelError::InvalidArgument {
127 name: "password",
128 value: "must contain a lowercase letter",
129 });
130 }
131
132 if self.require_digit && !password.bytes().any(|b| b.is_ascii_digit()) {
133 return Err(KernelError::InvalidArgument {
134 name: "password",
135 value: "must contain a digit",
136 });
137 }
138
139 if self.require_special
140 && !password
141 .bytes()
142 .any(|b| b.is_ascii_punctuation() || b == b' ')
143 {
144 return Err(KernelError::InvalidArgument {
145 name: "password",
146 value: "must contain a special character",
147 });
148 }
149
150 Ok(())
151 }
152}
153
154const BLOCK_SIZE: usize = 64;
160
161const HMAC_INNER_BUF_SIZE: usize = 192;
172
173fn hmac_sha256(key: &[u8], message: &[u8]) -> Hash256 {
183 const IPAD: u8 = 0x36;
184 const OPAD: u8 = 0x5c;
185
186 let max_msg = HMAC_INNER_BUF_SIZE - BLOCK_SIZE;
187 assert!(
188 message.len() <= max_msg,
189 "hmac_sha256: message too large for stack buffer"
190 );
191
192 let key_hash;
194 let actual_key = if key.len() > BLOCK_SIZE {
195 key_hash = sha256(key);
196 key_hash.as_bytes().as_slice()
197 } else {
198 key
199 };
200
201 let mut padded_key = [0u8; BLOCK_SIZE];
203 padded_key[..actual_key.len()].copy_from_slice(actual_key);
204
205 let mut inner_buf = [0u8; HMAC_INNER_BUF_SIZE];
207 for (i, byte) in padded_key.iter().enumerate() {
208 inner_buf[i] = byte ^ IPAD;
209 }
210 let inner_len = BLOCK_SIZE + message.len();
211 inner_buf[BLOCK_SIZE..inner_len].copy_from_slice(message);
212 let inner_hash = sha256(&inner_buf[..inner_len]);
213
214 let mut outer_buf = [0u8; BLOCK_SIZE + 32];
216 for (i, byte) in padded_key.iter().enumerate() {
217 outer_buf[i] = byte ^ OPAD;
218 }
219 outer_buf[BLOCK_SIZE..BLOCK_SIZE + 32].copy_from_slice(inner_hash.as_bytes());
220
221 sha256(&outer_buf[..BLOCK_SIZE + 32])
222}
223
224fn pbkdf2_hmac_sha256(password: &[u8], salt: &[u8], iterations: u32) -> Hash256 {
233 let mut salt_with_counter = [0u8; 128 + 4];
241 assert!(salt.len() <= 128, "pbkdf2: salt too large for stack buffer");
242 salt_with_counter[..salt.len()].copy_from_slice(salt);
243 salt_with_counter[salt.len()..salt.len() + 4].copy_from_slice(&1u32.to_be_bytes());
244 let salt_counter_len = salt.len() + 4;
245
246 let u1 = hmac_sha256(password, &salt_with_counter[..salt_counter_len]);
247 let mut result = *u1.as_bytes();
248 let mut prev = u1;
249
250 for _ in 1..iterations {
251 let u_next = hmac_sha256(password, prev.as_bytes());
252 for (r, u) in result.iter_mut().zip(u_next.as_bytes().iter()) {
254 *r ^= u;
255 }
256 prev = u_next;
257 }
258
259 Hash256(result)
260}
261
262#[derive(Debug, Clone)]
268#[allow(dead_code)]
269pub struct Credential {
270 pub username: &'static str,
271 pub(crate) password_hash: Hash256,
272 pub(crate) salt: [u8; 32],
273}
274
275#[derive(Debug, Clone)]
281pub struct UserAccount {
282 pub user_id: UserId,
283 pub username: &'static str,
284 pub(crate) password_hash: Hash256,
285 pub salt: [u8; 32],
286 pub locked: bool,
287 pub failed_attempts: u32,
288 pub mfa_enabled: bool,
289 pub mfa_secret: Option<[u8; 32]>,
290 pub expires_at: Option<u64>,
293 pub(crate) password_history: [Option<(Hash256, [u8; 32])>; MAX_PASSWORD_HISTORY],
295 pub password_history_len: usize,
297}
298
299impl UserAccount {
300 #[cfg(debug_assertions)]
303 const PBKDF2_ITERATIONS: u32 = 10;
304 #[cfg(not(debug_assertions))]
305 const PBKDF2_ITERATIONS: u32 = 10_000;
306
307 pub fn new(user_id: UserId, username: &'static str, password: &str) -> Self {
309 let (password_hash, salt) = Self::hash_password(password);
310
311 Self {
312 user_id,
313 username,
314 password_hash,
315 salt,
316 locked: false,
317 failed_attempts: 0,
318 mfa_enabled: false,
319 mfa_secret: None,
320 expires_at: None,
321 password_history: [None; MAX_PASSWORD_HISTORY],
322 password_history_len: 0,
323 }
324 }
325
326 fn hash_password(password: &str) -> (Hash256, [u8; 32]) {
328 use crate::crypto::random::get_random;
329
330 let rng = get_random();
331 let mut salt = [0u8; 32];
332 if let Err(_e) = rng.fill_bytes(&mut salt) {
333 crate::kprintln!(
334 "[AUTH] Warning: RNG fill_bytes failed for password salt, using zeroed salt"
335 );
336 }
337
338 let hash = pbkdf2_hmac_sha256(password.as_bytes(), &salt, Self::PBKDF2_ITERATIONS);
339
340 (hash, salt)
341 }
342
343 fn hash_password_with_salt(password: &str, salt: &[u8; 32]) -> Hash256 {
345 pbkdf2_hmac_sha256(password.as_bytes(), salt, Self::PBKDF2_ITERATIONS)
346 }
347
348 pub fn verify_password(&self, password: &str) -> bool {
350 let computed = Self::hash_password_with_salt(password, &self.salt);
351 computed == self.password_hash
352 }
353
354 pub fn is_expired(&self) -> bool {
356 if let Some(expires_at) = self.expires_at {
357 let now = crate::arch::timer::get_timestamp_secs();
358 now >= expires_at
359 } else {
360 false
361 }
362 }
363
364 pub fn set_expiration(&mut self, expires_at: Option<u64>) {
366 self.expires_at = expires_at;
367 }
368
369 pub fn change_password(
375 &mut self,
376 new_password: &str,
377 history_size: usize,
378 ) -> Result<(), KernelError> {
379 if self.verify_password(new_password) {
381 return Err(KernelError::InvalidArgument {
382 name: "password",
383 value: "must differ from current password",
384 });
385 }
386
387 let reused = self.password_history[..self.password_history_len]
389 .iter()
390 .any(|entry| {
391 if let Some((old_hash, old_salt)) = entry {
392 let candidate = Self::hash_password_with_salt(new_password, old_salt);
393 crate::crypto::constant_time::ct_eq_bytes(
394 candidate.as_bytes(),
395 old_hash.as_bytes(),
396 ) == 1
397 } else {
398 false
399 }
400 });
401
402 if reused {
403 return Err(KernelError::InvalidArgument {
404 name: "password",
405 value: "matches a recent password in history",
406 });
407 }
408
409 let effective_size = history_size.min(MAX_PASSWORD_HISTORY);
411 if effective_size > 0 {
412 if self.password_history_len >= effective_size {
413 for i in 0..effective_size - 1 {
415 self.password_history[i] = self.password_history[i + 1];
416 }
417 self.password_history[effective_size - 1] = Some((self.password_hash, self.salt));
418 self.password_history_len = effective_size;
419 } else {
420 self.password_history[self.password_history_len] =
421 Some((self.password_hash, self.salt));
422 self.password_history_len += 1;
423 }
424 }
425
426 let (new_hash, new_salt) = Self::hash_password(new_password);
428 self.password_hash = new_hash;
429 self.salt = new_salt;
430
431 Ok(())
432 }
433
434 pub fn enable_mfa(&mut self) -> [u8; 32] {
436 use crate::crypto::random::get_random;
437
438 let rng = get_random();
439 let mut secret = [0u8; 32];
440 if let Err(_e) = rng.fill_bytes(&mut secret) {
441 crate::kprintln!("[AUTH] Warning: RNG fill_bytes failed for MFA secret");
442 }
443
444 self.mfa_secret = Some(secret);
445 self.mfa_enabled = true;
446
447 secret
448 }
449
450 pub fn verify_mfa_token(&self, token: u32) -> bool {
452 if !self.mfa_enabled {
453 return true; }
455
456 if let Some(secret) = self.mfa_secret {
457 let time_step = 30; let current_time = crate::arch::timer::get_timestamp_secs();
460
461 let time_counter = current_time / time_step;
462
463 for offset in [0u64, 1u64] {
465 let counter = if offset == 0 {
466 time_counter
467 } else {
468 if self.check_totp_window(&secret, time_counter.wrapping_add(1), token) {
470 return true;
471 }
472 time_counter.wrapping_sub(1)
473 };
474
475 if self.check_totp_window(&secret, counter, token) {
476 return true;
477 }
478 }
479
480 false
481 } else {
482 false
483 }
484 }
485
486 fn check_totp_window(&self, secret: &[u8; 32], time_counter: u64, token: u32) -> bool {
488 let counter_bytes = time_counter.to_be_bytes();
490 let hash = hmac_sha256(secret, &counter_bytes);
491 let expected_token = u32::from_be_bytes([
492 hash.as_bytes()[0],
493 hash.as_bytes()[1],
494 hash.as_bytes()[2],
495 hash.as_bytes()[3],
496 ]) % 1_000_000; token == expected_token
499 }
500}
501
502struct AccountDatabase {
512 entries: [Option<UserAccount>; MAX_ACCOUNTS],
513 count: usize,
514}
515
516impl AccountDatabase {
517 const fn new() -> Self {
519 const NONE: Option<UserAccount> = None;
523 Self {
524 entries: [NONE; MAX_ACCOUNTS],
525 count: 0,
526 }
527 }
528
529 fn get(&self, username: &str) -> Option<&UserAccount> {
531 self.entries[..self.count]
532 .iter()
533 .flatten()
534 .find(|account| account.username == username)
535 }
536
537 fn get_mut(&mut self, username: &str) -> Option<&mut UserAccount> {
539 self.entries[..self.count]
540 .iter_mut()
541 .flatten()
542 .find(|account| account.username == username)
543 }
544
545 fn contains_key(&self, username: &str) -> bool {
547 self.get(username).is_some()
548 }
549
550 fn insert(&mut self, account: UserAccount) -> Result<(), KernelError> {
552 if self.count >= MAX_ACCOUNTS {
553 return Err(KernelError::ResourceExhausted {
554 resource: "account_database",
555 });
556 }
557
558 for entry in &mut self.entries {
561 if entry.is_none() {
562 *entry = Some(account);
563 self.count += 1;
564 return Ok(());
565 }
566 }
567
568 Err(KernelError::ResourceExhausted {
570 resource: "account_database",
571 })
572 }
573
574 fn remove(&mut self, username: &str) -> Option<UserAccount> {
576 for entry in &mut self.entries {
577 if let Some(account) = entry {
578 if account.username == username {
579 let removed = entry.take();
580 self.count -= 1;
581 return removed;
582 }
583 }
584 }
585 None
586 }
587
588 fn iter(&self) -> impl Iterator<Item = &UserAccount> {
590 self.entries.iter().filter_map(|e| e.as_ref())
591 }
592}
593
594pub struct AuthManager {
600 accounts: RwLock<AccountDatabase>,
601 next_user_id: RwLock<u32>,
602 max_failed_attempts: u32,
603 password_policy: RwLock<PasswordPolicy>,
604}
605
606impl AuthManager {
607 pub fn new() -> Self {
609 Self {
610 accounts: RwLock::new(AccountDatabase::new()),
611 next_user_id: RwLock::new(1000), max_failed_attempts: 5,
613 password_policy: RwLock::new(PasswordPolicy::relaxed()),
614 }
615 }
616
617 pub fn with_policy(policy: PasswordPolicy) -> Self {
619 Self {
620 accounts: RwLock::new(AccountDatabase::new()),
621 next_user_id: RwLock::new(1000),
622 max_failed_attempts: 5,
623 password_policy: RwLock::new(policy),
624 }
625 }
626
627 pub fn set_password_policy(&self, policy: PasswordPolicy) {
629 *self.password_policy.write() = policy;
630 }
631
632 pub fn get_password_policy(&self) -> PasswordPolicy {
634 *self.password_policy.read()
635 }
636
637 pub fn create_user(
642 &self,
643 username: &'static str,
644 password: &str,
645 ) -> Result<UserId, KernelError> {
646 let policy = *self.password_policy.read();
648 policy.validate_password(password)?;
649
650 let mut accounts = self.accounts.write();
651
652 if accounts.contains_key(username) {
654 return Err(KernelError::AlreadyExists {
655 resource: "user",
656 id: 0, });
658 }
659
660 let user_id = {
662 let mut next_id = self.next_user_id.write();
663 let id = *next_id;
664 *next_id += 1;
665 id
666 };
667
668 let account = UserAccount::new(user_id, username, password);
670
671 accounts.insert(account)?;
672
673 Ok(user_id)
674 }
675
676 pub fn authenticate(&self, username: &str, password: &str) -> AuthResult {
680 let mut accounts = self.accounts.write();
681
682 if let Some(account) = accounts.get_mut(username) {
683 if account.locked {
685 crate::security::audit::log_auth_attempt(0, account.user_id, username, false);
687 return AuthResult::AccountLocked;
688 }
689
690 if account.is_expired() {
692 crate::security::audit::log_auth_attempt(0, account.user_id, username, false);
693 return AuthResult::AccountExpired;
694 }
695
696 if account.verify_password(password) {
698 account.failed_attempts = 0;
700
701 if account.mfa_enabled {
703 return AuthResult::MfaRequired;
704 }
705
706 crate::security::audit::log_auth_attempt(0, account.user_id, username, true);
708
709 return AuthResult::Success;
710 } else {
711 account.failed_attempts += 1;
713
714 crate::security::audit::log_auth_attempt(0, account.user_id, username, false);
716
717 if account.failed_attempts >= self.max_failed_attempts {
719 account.locked = true;
720 return AuthResult::AccountLocked;
721 }
722
723 return AuthResult::InvalidCredentials;
724 }
725 }
726
727 AuthResult::InvalidCredentials
728 }
729
730 pub fn authenticate_mfa(&self, username: &str, password: &str, mfa_token: u32) -> AuthResult {
732 let result = self.authenticate(username, password);
734
735 if result != AuthResult::MfaRequired {
736 return result;
737 }
738
739 let accounts = self.accounts.read();
741 if let Some(account) = accounts.get(username) {
742 if account.verify_mfa_token(mfa_token) {
743 crate::security::audit::log_auth_attempt(0, account.user_id, username, true);
744 return AuthResult::Success;
745 }
746 }
747
748 AuthResult::InvalidCredentials
749 }
750
751 pub fn change_password(
756 &self,
757 username: &str,
758 old_password: &str,
759 new_password: &str,
760 ) -> Result<(), KernelError> {
761 let policy = *self.password_policy.read();
762
763 policy.validate_password(new_password)?;
765
766 let mut accounts = self.accounts.write();
767
768 if let Some(account) = accounts.get_mut(username) {
769 if !account.verify_password(old_password) {
771 return Err(KernelError::PermissionDenied {
772 operation: "change_password",
773 });
774 }
775
776 account.change_password(new_password, policy.history_size)?;
778
779 Ok(())
780 } else {
781 Err(KernelError::NotFound {
782 resource: "user",
783 id: 0,
784 })
785 }
786 }
787
788 pub fn set_account_expiration(
790 &self,
791 username: &str,
792 expires_at: Option<u64>,
793 ) -> Result<(), KernelError> {
794 let mut accounts = self.accounts.write();
795
796 if let Some(account) = accounts.get_mut(username) {
797 account.set_expiration(expires_at);
798 Ok(())
799 } else {
800 Err(KernelError::NotFound {
801 resource: "user",
802 id: 0,
803 })
804 }
805 }
806
807 pub fn enable_mfa(&self, username: &str) -> Result<[u8; 32], KernelError> {
809 let mut accounts = self.accounts.write();
810
811 if let Some(account) = accounts.get_mut(username) {
812 Ok(account.enable_mfa())
813 } else {
814 Err(KernelError::NotFound {
815 resource: "user",
816 id: 0, })
818 }
819 }
820
821 pub fn unlock_account(&self, username: &str) -> Result<(), KernelError> {
823 let mut accounts = self.accounts.write();
824
825 if let Some(account) = accounts.get_mut(username) {
826 account.locked = false;
827 account.failed_attempts = 0;
828 Ok(())
829 } else {
830 Err(KernelError::NotFound {
831 resource: "user",
832 id: 0, })
834 }
835 }
836
837 pub fn delete_user(&self, username: &str) -> Result<(), KernelError> {
839 let mut accounts = self.accounts.write();
840
841 accounts
842 .remove(username)
843 .map(|_| ())
844 .ok_or(KernelError::NotFound {
845 resource: "user",
846 id: 0, })
848 }
849
850 pub fn list_usernames(&self, buf: &mut [Option<&str>]) -> usize {
855 let accounts = self.accounts.read();
856 let mut i = 0;
857 for account in accounts.iter() {
858 if i >= buf.len() {
859 break;
860 }
861 buf[i] = Some(account.username);
862 i += 1;
863 }
864 i
865 }
866
867 pub fn get_user_by_id(&self, user_id: UserId) -> Option<&'static str> {
869 let accounts = self.accounts.read();
870
871 for account in accounts.iter() {
872 if account.user_id == user_id {
873 return Some(account.username);
874 }
875 }
876
877 None
878 }
879}
880
881impl Default for AuthManager {
882 fn default() -> Self {
883 Self::new()
884 }
885}
886
887static AUTH_MANAGER: OnceLock<AuthManager> = OnceLock::new();
893
894pub fn init() -> Result<(), KernelError> {
896 AUTH_MANAGER
897 .set(AuthManager::new())
898 .map_err(|_| KernelError::AlreadyExists {
899 resource: "auth_manager",
900 id: 0,
901 })?;
902
903 let auth_manager = get_auth_manager();
905 if let Err(_e) = auth_manager.create_user("root", "veridian") {
906 crate::kprintln!("[AUTH] Warning: Failed to create default root account");
907 }
908
909 crate::println!("[AUTH] Authentication framework initialized");
910 crate::println!("[AUTH] Default root user created");
911 crate::println!(
912 "[AUTH] PBKDF2-HMAC-SHA256 with {} iterations",
913 UserAccount::PBKDF2_ITERATIONS
914 );
915
916 Ok(())
917}
918
919pub fn get_auth_manager() -> &'static AuthManager {
921 AUTH_MANAGER.get().expect("Auth manager not initialized")
922}
923
924pub fn validate_password(password: &str) -> Result<(), KernelError> {
926 PasswordPolicy::default_policy().validate_password(password)
927}
928
929#[cfg(test)]
934mod tests {
935 use super::*;
936
937 #[test]
938 fn test_password_hashing() {
939 let account = UserAccount::new(1000, "test", "password123");
940
941 assert!(account.verify_password("password123"));
942 assert!(!account.verify_password("wrongpassword"));
943 }
944
945 #[test]
946 fn test_authentication() {
947 let auth = AuthManager::new();
948
949 let _ = auth.create_user("alice", "secret");
950
951 assert_eq!(auth.authenticate("alice", "secret"), AuthResult::Success);
952 assert_eq!(
953 auth.authenticate("alice", "wrong"),
954 AuthResult::InvalidCredentials
955 );
956 assert_eq!(
957 auth.authenticate("bob", "secret"),
958 AuthResult::InvalidCredentials
959 );
960 }
961
962 #[test]
963 fn test_account_locking() {
964 let auth = AuthManager::new();
965
966 let _ = auth.create_user("bob", "password");
967
968 for _ in 0..5 {
970 let _ = auth.authenticate("bob", "wrong");
971 }
972
973 assert_eq!(
975 auth.authenticate("bob", "password"),
976 AuthResult::AccountLocked
977 );
978 }
979
980 #[test]
981 fn test_pbkdf2_hmac_sha256() {
982 let salt = [0x42u8; 32];
984 let hash1 = pbkdf2_hmac_sha256(b"test_password", &salt, 10);
985 let hash2 = pbkdf2_hmac_sha256(b"test_password", &salt, 10);
986 assert_eq!(hash1, hash2);
987
988 let hash3 = pbkdf2_hmac_sha256(b"different_password", &salt, 10);
990 assert_ne!(hash1, hash3);
991 }
992
993 #[test]
994 fn test_hmac_sha256() {
995 let key = b"secret_key";
997 let msg = b"hello world";
998 let h1 = hmac_sha256(key, msg);
999 let h2 = hmac_sha256(key, msg);
1000 assert_eq!(h1, h2);
1001
1002 let h3 = hmac_sha256(key, b"different message");
1004 assert_ne!(h1, h3);
1005 }
1006
1007 #[test]
1008 fn test_password_policy_validation() {
1009 let policy = PasswordPolicy::default_policy();
1010
1011 assert!(policy.validate_password("Ab1").is_err());
1013
1014 assert!(policy.validate_password("abcdefg1").is_err());
1016
1017 assert!(policy.validate_password("ABCDEFG1").is_err());
1019
1020 assert!(policy.validate_password("Abcdefgh").is_err());
1022
1023 assert!(policy.validate_password("Abcdefg1").is_ok());
1025 }
1026
1027 #[test]
1028 fn test_account_expiration() {
1029 let mut account = UserAccount::new(1000, "exptest", "password");
1030
1031 assert!(!account.is_expired());
1033
1034 account.set_expiration(Some(0));
1037 assert_eq!(account.expires_at, Some(0));
1040 }
1041
1042 #[test]
1043 fn test_password_change_reuse() {
1044 let mut account = UserAccount::new(1000, "chgtest", "original");
1045
1046 let result = account.change_password("original", 5);
1048 assert!(result.is_err());
1049
1050 let result = account.change_password("newpassword", 5);
1052 assert!(result.is_ok());
1053
1054 assert_eq!(account.password_history_len, 1);
1056
1057 assert!(account.verify_password("newpassword"));
1059 assert!(!account.verify_password("original"));
1060 }
1061}