Add password hashing

This commit is contained in:
Vincent Ollivier 2020-01-03 22:01:48 +01:00
parent e87b485a86
commit 0b59c0d310
11 changed files with 243 additions and 14 deletions

106
Cargo.lock generated
View File

@ -20,6 +20,12 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "base64"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "bit_field"
version = "0.9.0"
@ -32,12 +38,39 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array 0.12.3",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "bootloader"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d596849a47f28abdea62d7a6a25c4f6e69c3d9b09b0a2877db6e9cda004ca993"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.3.2"
@ -59,6 +92,31 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa"
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
dependencies = [
"generic-array 0.12.3",
"subtle",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.3",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "generic-array"
version = "0.12.3"
@ -97,6 +155,16 @@ dependencies = [
"hash32",
]
[[package]]
name = "hmac"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
dependencies = [
"crypto-mac",
"digest",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -110,11 +178,15 @@ dependencies = [
name = "moros"
version = "0.1.0"
dependencies = [
"base64",
"bootloader",
"heapless",
"hmac",
"lazy_static",
"pbkdf2",
"pc-keyboard",
"pic8259_simple",
"sha2",
"spin",
"volatile",
"x86_64",
@ -126,6 +198,22 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "pbkdf2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9"
dependencies = [
"byteorder",
"crypto-mac",
]
[[package]]
name = "pc-keyboard"
version = "0.5.0"
@ -164,6 +252,18 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "sha2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
dependencies = [
"block-buffer",
"digest",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "spin"
version = "0.5.2"
@ -176,6 +276,12 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "typenum"
version = "1.11.2"

View File

@ -17,3 +17,7 @@ pic8259_simple = "0.1.1"
pc-keyboard = { version = "0.5.0", git = "https://github.com/vinc/pc-keyboard", branch = "feature/add-dvorak-layout" }
heapless = "0.5.1"
lazy_static = { version = "1.0", features = ["spin_no_std"] }
base64 = { version = "0.11", default-features = false }
pbkdf2 = { version = "0.3", default-features = false }
sha2 = { version = "0.8", default-features = false }
hmac = { version = "0.7", default-features = false }

1
dsk/cfg/passwords.csv Normal file
View File

@ -0,0 +1 @@
root,1$AAAQAA$nvQAFYGhN1a4NmHS8ooWrA$7zOWQLxpmRF4AC3G3+F2OVJ9IejzdH52TXQFlbRAmLg
1 root 1$AAAQAA$nvQAFYGhN1a4NmHS8ooWrA$7zOWQLxpmRF4AC3G3+F2OVJ9IejzdH52TXQFlbRAmLg

View File

@ -4,5 +4,6 @@ pub mod console;
pub mod fs;
pub mod gdt;
pub mod interrupts;
pub mod random;
pub mod sleep;
pub mod vga;

8
src/kernel/random.rs Normal file
View File

@ -0,0 +1,8 @@
use x86_64::instructions::random::RdRand;
pub fn rand64() -> Option<u64> {
match RdRand::new() {
Some(rand) => rand.get_u64(),
None => None
}
}

View File

@ -11,6 +11,7 @@ fn main(_boot_info: &'static BootInfo) -> ! {
moros::init();
include_file("/cfg/boot.sh", include_str!("../dsk/cfg/boot.sh"));
include_file("/cfg/banner.txt", include_str!("../dsk/cfg/banner.txt"));
include_file("/cfg/passwords.csv", include_str!("../dsk/cfg/passwords.csv"));
loop {
user::shell::main(&["shell", "/cfg/boot.sh"]);
}

30
src/user/base64.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::{print, user};
use heapless::{String, Vec};
use heapless::consts::*;
pub fn main(args: &[&str]) -> user::shell::ExitCode {
if args.len() != 2 {
user::shell::ExitCode::CommandError
} else {
let buf = encode(args[1].as_bytes());
let encoded = String::from_utf8(buf).unwrap();
print!("{}\n", encoded);
user::shell::ExitCode::CommandSuccessful
}
}
pub fn encode(s: &[u8]) -> Vec<u8, U1024> {
let mut buf = Vec::<u8, U1024>::new();
buf.resize(s.len() * 4 / 3 + 4, 0).unwrap(); // Resize to base64 + padding
let bytes_written = base64::encode_config_slice(s, base64::STANDARD_NO_PAD, &mut buf);
buf.resize(bytes_written, 0).unwrap(); // Resize back to actual size
buf
}
pub fn decode(s: &[u8]) -> Vec<u8, U1024> {
let mut buf = Vec::<u8, U1024>::new();
buf.resize(s.len(), 0).unwrap();
let bytes_written = base64::decode_config_slice(s, base64::STANDARD_NO_PAD, &mut buf).unwrap();
buf.resize(bytes_written, 0).unwrap();
buf
}

View File

@ -1,4 +1,10 @@
use crate::{print, kernel, user};
use heapless::{String, FnvIndexMap, Vec};
use heapless::consts::*;
use core::convert::TryInto;
use core::str;
use hmac::Hmac;
use sha2::Sha256;
pub fn main(_args: &[&str]) -> user::shell::ExitCode {
login()
@ -6,21 +12,91 @@ pub fn main(_args: &[&str]) -> user::shell::ExitCode {
// TODO: Add max number of attempts
pub fn login() -> user::shell::ExitCode {
print!("\nUsername: ");
let username = kernel::console::get_line();
if username != "root\n" {
kernel::sleep::sleep(1.0);
return login();
let mut hashed_passwords: FnvIndexMap<String<U256>, String<U1024>, U256> = FnvIndexMap::new();
if let Some(file) = kernel::fs::File::open("/cfg/passwords.csv") {
for line in file.read().split("\n") {
let mut rows = line.split(",");
if let Some(username) = rows.next() {
if let Some(hashed_password) = rows.next() {
hashed_passwords.insert(username.into(), hashed_password.into()).unwrap();
}
}
}
}
print!("Password: ");
kernel::console::disable_echo();
let password = kernel::console::get_line();
kernel::console::enable_echo();
print!("\n");
if password != "root\n" {
kernel::sleep::sleep(1.0);
return login();
print!("\nUsername: ");
let mut username = kernel::console::get_line();
username.pop(); // Trim end of string
match hashed_passwords.get(&username) {
None => {
kernel::sleep::sleep(1.0);
return login();
},
Some(hashed_password) => {
print!("Password: ");
kernel::console::disable_echo();
let mut password = kernel::console::get_line();
kernel::console::enable_echo();
print!("\n");
password.pop();
if !check(&password, hashed_password) {
kernel::sleep::sleep(1.0);
return login();
}
}
}
user::shell::ExitCode::CommandSuccessful
}
pub fn check(password: &str, hashed_password: &str) -> bool {
let fields: Vec<_, U4> = hashed_password.split('$').collect();
if fields.len() != 4 || fields[0] != "1" {
return false;
}
let decoded_field = user::base64::decode(&fields[1].as_bytes());
let c = u32::from_be_bytes(decoded_field[0..4].try_into().unwrap());
let decoded_field = user::base64::decode(&fields[2].as_bytes());
let salt: [u8; 16] = decoded_field[0..16].try_into().unwrap();
let mut hash = [0u8; 32];
pbkdf2::pbkdf2::<Hmac<Sha256>>(password.as_bytes(), &salt, c as usize, &mut hash);
let encoded_hash = String::from_utf8(user::base64::encode(&hash)).unwrap();
encoded_hash == fields[3]
}
// Password hashing version 1 => PBKDF2-HMAC-SHA256 + BASE64
// Fields: "<version>$<c>$<salt>$<hash>"
// Example: "1$AAAQAA$PDkXP0I8O7SxNOxvUKmHHQ$BwIUWBxKs50BTpH6i4ImF3SZOxADv7dh4xtu3IKc3o8"
pub fn hash(password: &str) -> String<U1024> {
let v = "1"; // Password hashing version
let c = 4096u32; // Number of iterations
let mut salt = [0u8; 16];
let mut hash = [0u8; 32];
// Generating salt
for i in 0..2 {
let num = kernel::random::rand64().unwrap();
let buf = num.to_be_bytes();
let n = buf.len();
for j in 0..n {
salt[i * n + j] = buf[j];
}
}
// Hashing password with PBKDF2-HMAC-SHA256
pbkdf2::pbkdf2::<Hmac<Sha256>>(password.as_bytes(), &salt, c as usize, &mut hash);
// Encoding in Base64 standard without padding
let c = c.to_be_bytes();
let mut res: String<U1024> = String::from(v);
res.push('$').unwrap();
res.push_str(&String::from_utf8(user::base64::encode(&c)).unwrap()).unwrap();
res.push('$').unwrap();
res.push_str(&String::from_utf8(user::base64::encode(&salt)).unwrap()).unwrap();
res.push('$').unwrap();
res.push_str(&String::from_utf8(user::base64::encode(&hash)).unwrap()).unwrap();
res
}

View File

@ -1,3 +1,4 @@
pub mod base64;
pub mod clear;
pub mod date;
pub mod editor;

View File

@ -141,6 +141,7 @@ impl Shell {
"sleep" => user::sleep::main(&args),
"clear" => user::clear::main(&args),
"login" => user::login::main(&args),
"base64" => user::base64::main(&args),
_ => ExitCode::CommandUnknown,
}
}

View File

@ -12,4 +12,4 @@
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
}