From 503ba812f231fe34b35a16aebf98059c615606c0 Mon Sep 17 00:00:00 2001 From: shockrah Date: Mon, 10 Mar 2025 23:20:56 -0700 Subject: [PATCH] Adding framework for salt & hashing passwords before creating admin users, still requires integration at this stage --- Cargo.lock | 39 +++++++++++++++++++++++++++++++++++++++ admin-cli/Cargo.toml | 1 + admin-cli/src/main.rs | 13 ++++++++++++- db/setup-tables.sql | 4 +++- dev.py | 7 +++++++ 5 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6dc2a3..8572480 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,7 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" name = "admin-cli" version = "0.1.0" dependencies = [ + "argon2", "base64", "clap", "postgres", @@ -96,6 +97,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -171,6 +184,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97d56060ee67d285efb8001fec9d2a4c710c32efd2e14b5cbb5ba71930fc2d" + [[package]] name = "binascii" version = "0.1.4" @@ -183,6 +202,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -906,6 +934,17 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "pear" version = "0.2.9" diff --git a/admin-cli/Cargo.toml b/admin-cli/Cargo.toml index f0dcf16..e6cf7d8 100644 --- a/admin-cli/Cargo.toml +++ b/admin-cli/Cargo.toml @@ -9,3 +9,4 @@ postgres = "0.19.9" base64 = "0.22.1" serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.133" +argon2 = "0.5.3" diff --git a/admin-cli/src/main.rs b/admin-cli/src/main.rs index 73a779e..98bdc26 100644 --- a/admin-cli/src/main.rs +++ b/admin-cli/src/main.rs @@ -5,6 +5,7 @@ use clap::Parser; use postgres::{Client, NoTls}; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; use serde::Serialize; +use argon2::{Argon2, PasswordHasher, password_hash::Salt, PasswordHash}; const PASSWORD_LENGTH: usize = 64; @@ -32,14 +33,24 @@ struct Config { bubble: Admin } + fn random_string(size: usize) -> String { - // Next we'll generate a bunch of random numbers + // Generates a URL safe string of random text of a given size + // Internally uses /dev/urandom to generate that string let mut buffer = vec![0; size]; let mut f = std::fs::File::open("/dev/urandom").unwrap(); f.read_exact(&mut buffer).unwrap(); URL_SAFE_NO_PAD.encode(buffer) } +fn salt_and_hash(password: &str) -> String { + let salt = random_string(8); + let salt: Salt = salt.as_str().try_into().unwrap(); + let a2 = Argon2::default(); + let hash = a2.hash_password(password.as_bytes(), salt).unwrap(); + hash.to_string() +} + fn admin(username: &str, password_size: usize) -> Admin { Admin { username: username.to_string(), diff --git a/db/setup-tables.sql b/db/setup-tables.sql index e4c7dec..92be434 100644 --- a/db/setup-tables.sql +++ b/db/setup-tables.sql @@ -3,7 +3,9 @@ CREATE TABLE IF NOT EXISTS users ( id UUID, /* Acts as a kind of nick name per instance as it assumes no uniqueness */ username VARCHAR(256), - /* Basic salted+hashed password */ + /* Salt to be generated everytime password is (re)created */ + salt VARCHAR(256), + /* Basic hashed password */ password VARCHAR(256), PRIMARY KEY (id) ); diff --git a/dev.py b/dev.py index 5023903..dcd26e6 100755 --- a/dev.py +++ b/dev.py @@ -18,6 +18,11 @@ if __name__ == '__main__': help='Sets the database URL to use for connecting to postgres', default='postgres://psql:example@localhost:5432' ) + parser.add_argument( + '-c', + '--check-container', + help='Execs into the given container with bash for debugging' + ) args = parser.parse_args() os.environ['DB_CONNECTION_STRING'] = args.db_url if args.init_db: @@ -26,3 +31,5 @@ if __name__ == '__main__': env=os.environ, shell=True ) + if args.check_container: + run(f'docker exec -it {args.check_container} bash'.split())