From c5ed7b895caad6dc4e6d3398fbe3c154a102959c Mon Sep 17 00:00:00 2001
From: KunoiSayami <46131041+KunoiSayami@users.noreply.github.com>
Date: Mon, 10 May 2021 12:58:30 +0800
Subject: refactor: Use argon2 instead of sha256 to store password * style:
Update html file style
---
src/datastructures.rs | 195 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 184 insertions(+), 11 deletions(-)
(limited to 'src/datastructures.rs')
diff --git a/src/datastructures.rs b/src/datastructures.rs
index 2a19087..bb91cfb 100644
--- a/src/datastructures.rs
+++ b/src/datastructures.rs
@@ -19,16 +19,56 @@
*/
use anyhow::Result;
-use sha2::Digest;
use std::borrow::Cow;
use std::fs::read_to_string;
use std::path::Path;
use url::form_urlencoded;
+use std::fmt::Formatter;
+use rand::Rng;
+use serde::{Serialize, Deserialize};
+use argon2::{
+ password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
+ Argon2
+};
+use rand_core::OsRng;
const DEFAULT_CONFIG_LOCATION: &str = "/etc/cgitrc";
const DEFAULT_COOKIE_TTL: u64 = 1200;
const DEFAULT_DATABASE_LOCATION: &str = "/etc/cgit/auth.db";
pub const CACHE_DIR: &str = "/var/cache/cgit";
+pub type RandIntType = u32;
+pub const MINIMUM_SECRET_LENGTH: usize = 8;
+
+pub fn get_current_timestamp() -> u64 {
+ let start = std::time::SystemTime::now();
+ let since_the_epoch = start
+ .duration_since(std::time::UNIX_EPOCH)
+ .expect("Time went backwards");
+ since_the_epoch.as_secs()
+}
+
+pub fn rand_int() -> RandIntType {
+ let mut rng = rand::thread_rng();
+ rng.gen()
+}
+
+pub fn rand_str(len: usize) -> String {
+ const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+ abcdefghijklmnopqrstuvwxyz\
+ 0123456789";
+ let mut rng = rand::thread_rng();
+
+ let password: String = (0..len)
+ .map(|_| {
+ let idx = rng.gen_range(0, CHARSET.len());
+ CHARSET[idx] as char
+ })
+ .collect();
+
+ password
+}
+
+
#[derive(Debug, Clone)]
pub struct Config {
@@ -36,6 +76,7 @@ pub struct Config {
database: String,
//access_node: hashmap,
pub bypass_root: bool,
+ //secret: String,
}
impl Default for Config {
@@ -44,6 +85,7 @@ impl Default for Config {
cookie_ttl: DEFAULT_COOKIE_TTL,
database: DEFAULT_DATABASE_LOCATION.to_string(),
bypass_root: false,
+ //secret: Default::default(),
}
}
}
@@ -58,6 +100,7 @@ impl Config {
let mut cookie_ttl: u64 = DEFAULT_COOKIE_TTL;
let mut database: &str = "/etc/cgit/auth.db";
let mut bypass_root: bool = false;
+ let mut secret: &str = "";
for line in file.lines() {
let line = line.trim();
if !line.contains('=') || !line.starts_with("cgit-simple-auth-") {
@@ -74,6 +117,7 @@ impl Config {
"cookie-ttl" => cookie_ttl = value.parse().unwrap_or(DEFAULT_COOKIE_TTL),
"database" => database = value,
"bypass-root" => bypass_root = value.to_lowercase().eq("true"),
+ "secret" => secret = value,
_ => {}
}
}
@@ -81,12 +125,23 @@ impl Config {
cookie_ttl,
database: database.to_string(),
bypass_root,
+ //secret: secret.to_string(),
}
}
pub fn get_database_location(&self) -> &str {
self.database.as_str()
}
+
+/* pub fn get_secret_warning(&self) -> &str {
+ if self.secret.is_empty() {
+ r#"Warning: You should specify secret in your cgitrc file."#
+ } else if self.secret.len() < MINIMUM_SECRET_LENGTH {
+ r#"Warning: You should set key length more than MINIMUM_SECRET_LENGTH."#
+ } else {
+ ""
+ }
+ }*/
}
#[derive(Debug, Clone, Default)]
@@ -103,10 +158,13 @@ impl FormData {
}
}
- pub fn get_string_sha256_value(s: &str) -> Result {
- let mut hasher = sha2::Sha256::new();
- hasher.update(s.as_bytes());
- Ok(format!("{:x}", hasher.finalize()))
+ pub fn get_string_argon2_hash(s: &str) -> Result {
+ let passwd = s.as_bytes();
+ let salt = SaltString::generate(&mut OsRng);
+
+ let argon2_alg = Argon2::default();
+
+ Ok(argon2_alg.hash_password_simple(passwd, salt.as_ref()).unwrap().to_string())
}
pub fn set_password(&mut self, password: String) {
@@ -114,6 +172,11 @@ impl FormData {
self.hash = Default::default();
}
+ pub fn verify_password(&self, password_hash: &PasswordHash) -> bool {
+ let argon2_alg = Argon2::default();
+ argon2_alg.verify_password(self.password.as_bytes(), password_hash).is_ok()
+ }
+
pub fn set_user(&mut self, user: String) {
self.user = user
}
@@ -122,20 +185,20 @@ impl FormData {
&self.user
}
- pub fn get_password_sha256(&self) -> Result {
- Self::get_string_sha256_value(&self.password)
+ pub fn get_password_argon2(&self) -> Result {
+ Self::get_string_argon2_hash(&self.password)
}
#[allow(dead_code)]
- pub fn get_password_sha256_cache(&mut self) -> Result {
+ pub fn get_password_argon2_cache(&mut self) -> Result {
if self.hash.is_empty() {
- self.hash = self.get_password_sha256()?;
+ self.hash = self.get_password_argon2()?;
}
Ok(self.hash.clone())
}
#[allow(dead_code)]
- pub fn get_sha256_without_calc(&self) -> &String {
+ pub fn get_argon2_without_calc(&self) -> &String {
&self.hash
}
}
@@ -151,7 +214,6 @@ impl From<&[u8]> for FormData {
}
Cow::Borrowed("password") => {
data.set_password(f.1.to_string());
- data.get_password_sha256_cache().unwrap();
}
_ => {}
}
@@ -171,3 +233,114 @@ impl From for FormData {
Self::from(&s)
}
}
+
+#[derive(Serialize, Deserialize)]
+struct IvFile {
+ iv: String,
+ timestamp: u64,
+}
+
+pub struct Cookie {
+ timestamp: u64,
+ randint: RandIntType,
+ body: String,
+}
+
+impl Cookie {
+ fn new(randint: RandIntType, body: &String) -> Self {
+ Self {
+ timestamp: get_current_timestamp(),
+ randint,
+ body: body.clone()
+ }
+ }
+
+ pub fn load_from_request(cookies: &str) -> Result