diff options
| author | salmonik <raisersoft@proton.me> | 2026-02-28 19:19:28 +0100 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-02-28 19:19:28 +0100 |
| commit | 4732867ffa8041a3406dade3ae3ad32cf95f0a0c (patch) | |
| tree | cd64723174b55b3459f9da05f883743da811f26b /src/bin/client/database.rs | |
| parent | better raw mode for windows (diff) | |
| download | zagan-bet-4732867ffa8041a3406dade3ae3ad32cf95f0a0c.tar.gz zagan-bet-4732867ffa8041a3406dade3ae3ad32cf95f0a0c.zip | |
add authentication barebone
Diffstat (limited to 'src/bin/client/database.rs')
| -rw-r--r-- | src/bin/client/database.rs | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/src/bin/client/database.rs b/src/bin/client/database.rs index bc8f6f8..03a6c45 100644 --- a/src/bin/client/database.rs +++ b/src/bin/client/database.rs | |||
| @@ -1,4 +1,10 @@ | |||
| 1 | use rusqlite::Connection; | 1 | use rusqlite::Connection; |
| 2 | use aes_gcm::{Aes256Gcm, Key, Nonce}; | ||
| 3 | use aes_gcm::aead::{Aead, KeyInit}; | ||
| 4 | use pbkdf2::pbkdf2; | ||
| 5 | use hmac::Hmac; | ||
| 6 | use sha2::Sha256; | ||
| 7 | use rand::Rng; | ||
| 2 | 8 | ||
| 3 | pub fn init_db() -> Connection { | 9 | pub fn init_db() -> Connection { |
| 4 | let conn = Connection::open("client.db").unwrap(); | 10 | let conn = Connection::open("client.db").unwrap(); |
| @@ -9,6 +15,14 @@ pub fn init_db() -> Connection { | |||
| 9 | )", | 15 | )", |
| 10 | [], | 16 | [], |
| 11 | ).unwrap(); | 17 | ).unwrap(); |
| 18 | conn.execute( | ||
| 19 | "CREATE TABLE IF NOT EXISTS credentials ( | ||
| 20 | host TEXT PRIMARY KEY, | ||
| 21 | username TEXT NOT NULL, | ||
| 22 | encrypted_password BLOB NOT NULL | ||
| 23 | )", | ||
| 24 | [], | ||
| 25 | ).unwrap(); | ||
| 12 | conn | 26 | conn |
| 13 | } | 27 | } |
| 14 | 28 | ||
| @@ -28,3 +42,53 @@ pub fn save_host(conn: &Connection, host: &str) { | |||
| 28 | [host], | 42 | [host], |
| 29 | ).unwrap(); | 43 | ).unwrap(); |
| 30 | } | 44 | } |
| 45 | |||
| 46 | pub fn save_credentials(conn: &Connection, host: &str, username: &str, password: &str) -> Result<(), aes_gcm::Error> { | ||
| 47 | let encrypted = encrypt_password(password)?; | ||
| 48 | conn.execute( | ||
| 49 | "INSERT OR REPLACE INTO credentials (host, username, encrypted_password) VALUES (?1, ?2, ?3)", | ||
| 50 | (host, username, &encrypted), | ||
| 51 | ).unwrap(); | ||
| 52 | Ok(()) | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn get_credentials(conn: &Connection, host: &str) -> Option<(String, String)> { | ||
| 56 | let mut stmt = conn.prepare("SELECT username, encrypted_password FROM credentials WHERE host = ?1").unwrap(); | ||
| 57 | let mut rows = stmt.query([host]).unwrap(); | ||
| 58 | if let Ok(Some(row)) = rows.next() { | ||
| 59 | let username: String = row.get(0).unwrap(); | ||
| 60 | let encrypted: Vec<u8> = row.get(1).unwrap(); | ||
| 61 | decrypt_password(&encrypted).map(|password| (username, password)) | ||
| 62 | } else { | ||
| 63 | None | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | fn derive_key() -> Key<Aes256Gcm> { | ||
| 68 | let mut key = [0u8; 32]; | ||
| 69 | let salt = b"client_salt"; // Fixed salt for simplicity | ||
| 70 | pbkdf2::<Hmac<Sha256>>(b"client_key", salt, 10000, &mut key).expect("error deriving key"); | ||
| 71 | key.into() | ||
| 72 | } | ||
| 73 | |||
| 74 | fn encrypt_password(password: &str) -> Result<Vec<u8>, aes_gcm::Error> { | ||
| 75 | let key = derive_key(); | ||
| 76 | let cipher = Aes256Gcm::new(&key); | ||
| 77 | let mut nonce_bytes = [0u8; 12]; | ||
| 78 | rand::thread_rng().fill(&mut nonce_bytes); | ||
| 79 | let nonce = Nonce::from_slice(&nonce_bytes); | ||
| 80 | let ciphertext = cipher.encrypt(nonce, password.as_bytes())?; | ||
| 81 | let mut result = nonce_bytes.to_vec(); | ||
| 82 | result.extend(ciphertext); | ||
| 83 | Ok(result) | ||
| 84 | } | ||
| 85 | |||
| 86 | fn decrypt_password(encrypted: &[u8]) -> Option<String> { | ||
| 87 | if encrypted.len() < 12 { | ||
| 88 | return None; | ||
| 89 | } | ||
| 90 | let key = derive_key(); | ||
| 91 | let cipher = Aes256Gcm::new(&key); | ||
| 92 | let nonce = Nonce::from_slice(&encrypted[..12]); | ||
| 93 | cipher.decrypt(nonce, &encrypted[12..]).ok().and_then(|plaintext| String::from_utf8(plaintext).ok()) | ||
| 94 | } | ||
