apparently the gen_or_get_key logic is for string only keys

changed that and also added an implementation for the trait
This commit is contained in:
2026-07-13 19:19:29 -05:00
parent 6a8abec015
commit 29033259df
5 changed files with 39 additions and 25 deletions
Generated
+7
View File
@@ -245,6 +245,12 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "2.13.0"
@@ -436,6 +442,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
name = "cli"
version = "1.0.0"
dependencies = [
"base64",
"chrono",
"clap",
"colored",
+1
View File
@@ -17,6 +17,7 @@ tokio = { version = "1.52.3", features = ["full"] }
crypto = { path = "../crypto" }
keyring = "4.1.4"
colored = "3.1.1"
base64 = "0.22.1"
chrono = "0.4.45"
dotenvy = "0.15"
uuid = "1.23.5"
+15 -24
View File
@@ -1,8 +1,10 @@
use base64::{Engine, engine::general_purpose::STANDARD};
use clap::{Args, Parser, Subcommand};
use crypto::helper::gen_key;
use keyring::Entry;
use std::error::Error;
mod db;
mod store;
#[derive(Parser)]
#[clap(
name = "Harbor",
@@ -58,38 +60,27 @@ pub enum ConfigCommands {
Delete { project: String, name: String },
}
pub fn gen_or_get_key() -> Result<crypto::Key, Box<dyn Error>> {
fn gen_or_get_key() -> Result<crypto::Key, Box<dyn Error>> {
let entry = Entry::new("harbor", "encryption-key")?;
match entry.get_password() {
Ok(hex_string) => {
let key_bytes = hex::decode(hex_string)?;
Ok(encoded) => {
let bytes = STANDARD.decode(encoded)?;
if key_bytes.len() != 32 {
return Err("Retrieved key length is invalid (must be 32 bytes)".into());
}
let key = crypto::helper::key_from(bytes)?;
Ok(key_bytes
.as_slice()
.try_into()
.map_err(|_| "Invalid key length")?)
Ok(key)
}
Err(err) => {
if is_key_not_found(&err) {
let key = crypto::helper::gen_key();
Err(keyring::Error::NoEntry) => {
let key = gen_key();
let hex_string = hex::encode(key.as_slice());
entry.set_password(&hex_string)?;
let encoded = STANDARD.encode(key);
entry.set_password(&encoded)?;
Ok(key)
} else {
Err(Box::new(err))
}
Ok(key)
}
Err(err) => Err(Box::new(err)),
}
}
fn is_key_not_found(err: &keyring::Error) -> bool {
matches!(err, keyring::Error::NoEntry)
}
+7
View File
@@ -0,0 +1,7 @@
// This trait will replace the current implementation.
// Right now, the implementation is locked on only SQLite.
// With this trait, we can implement other savers like Postgres, MySQL, etc.
// Or even third party services like Hashicorp Vault, AWS Secrets Manager, etc.
// For now, I'll just get an MVP in place.
pub trait Store {}
+9 -1
View File
@@ -9,7 +9,7 @@ pub type Key = chacha20poly1305::Key;
pub type Nonce = chacha20poly1305::Nonce;
pub mod helper {
use super::{Generate, Key, Nonce};
use super::{Generate, Key, Nonce, Result};
pub fn gen_nonce() -> Nonce {
Nonce::generate()
}
@@ -17,6 +17,14 @@ pub mod helper {
pub fn gen_key() -> Key {
Key::generate()
}
pub fn key_from(bytes: Vec<u8>) -> Result<Key> {
let array: [u8; 32] = bytes
.try_into()
.map_err(|_| "key must be exactly 32 bytes".to_string())?;
Ok(Key::from(array))
}
}
pub fn encrypt(key: &Key, plaintext: Vec<u8>) -> Result<(Nonce, Vec<u8>)> {