dls: add password hash function

This commit is contained in:
Mikaël Cluseau
2026-03-16 11:06:19 +01:00
parent 4b1edb2a55
commit 4619899e65
6 changed files with 216 additions and 647 deletions

804
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,10 +28,12 @@ hex = "0.4.3"
human-units = "0.5.3" human-units = "0.5.3"
log = "0.4.27" log = "0.4.27"
lz4 = "1.28.1" lz4 = "1.28.1"
nix = { version = "0.30.1", features = ["user"] } nix = { version = "0.31.2", features = ["user"] }
openssl = "0.10.73" openssl = "0.10.73"
page_size = "0.6.0" page_size = "0.6.0"
reqwest = { version = "0.13.1", features = ["json", "stream", "native-tls", "socks"] } reqwest = { version = "0.13.1", features = ["json", "stream", "native-tls", "socks"], default-features = false }
rpassword = "7.4.0"
rust-argon2 = "3.0.0"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140" serde_json = "1.0.140"
serde_yaml = "0.9.34" serde_yaml = "0.9.34"

View File

@@ -36,6 +36,10 @@ enum Command {
}, },
#[command(subcommand)] #[command(subcommand)]
DlSet(DlSet), DlSet(DlSet),
/// hash a password
Hash {
salt: String,
},
} }
#[derive(Subcommand)] #[derive(Subcommand)]
@@ -103,14 +107,16 @@ async fn main() -> eyre::Result<()> {
.parse_default_env() .parse_default_env()
.init(); .init();
let token = std::env::var("DLS_TOKEN").map_err(|_| format_err!("DLS_TOKEN should be set"))?; let dls = || {
let token = std::env::var("DLS_TOKEN").expect("DLS_TOKEN should be set");
let dls = dls::Client::new(cli.dls, token); dls::Client::new(cli.dls, token)
};
use Command as C; use Command as C;
match cli.command { match cli.command {
C::Clusters => write_json(&dls.clusters().await?), C::Clusters => write_json(&dls().clusters().await?),
C::Cluster { cluster, command } => { C::Cluster { cluster, command } => {
let dls = dls();
let cluster = dls.cluster(cluster); let cluster = dls.cluster(cluster);
use ClusterCommand as CC; use ClusterCommand as CC;
@@ -155,8 +161,9 @@ async fn main() -> eyre::Result<()> {
} }
} }
} }
C::Hosts => write_json(&dls.hosts().await?), C::Hosts => write_json(&dls().hosts().await?),
C::Host { out, host, asset } => { C::Host { out, host, asset } => {
let dls = dls();
let host_name = host.clone(); let host_name = host.clone();
let host = dls.host(host); let host = dls.host(host);
match asset { match asset {
@@ -171,7 +178,7 @@ async fn main() -> eyre::Result<()> {
C::DlSet(set) => match set { C::DlSet(set) => match set {
DlSet::Sign { expiry, items } => { DlSet::Sign { expiry, items } => {
let req = dls::DownloadSetReq { expiry, items }; let req = dls::DownloadSetReq { expiry, items };
let signed = dls.sign_dl_set(&req).await?; let signed = dls().sign_dl_set(&req).await?;
println!("{signed}"); println!("{signed}");
} }
DlSet::Show { signed_set } => { DlSet::Show { signed_set } => {
@@ -211,11 +218,19 @@ async fn main() -> eyre::Result<()> {
name, name,
asset, asset,
} => { } => {
let dls = dls();
let stream = dls.fetch_dl_set(&signed_set, &kind, &name, &asset).await?; let stream = dls.fetch_dl_set(&signed_set, &kind, &name, &asset).await?;
let mut out = create_asset_file(out, &kind, &name, &asset).await?; let mut out = create_asset_file(out, &kind, &name, &asset).await?;
copy_stream(stream, &mut out).await?; copy_stream(stream, &mut out).await?;
} }
}, },
C::Hash { salt } => {
let salt = dkl::base64_decode(&salt)?;
let passphrase = rpassword::prompt_password("password to hash: ")?;
let hash = dls::store::hash_password(&salt, &passphrase)?;
println!("hash (hex): {}", hex::encode(&hash));
println!("hash (base64): {}", dkl::base64_encode(&hash));
}
}; };
Ok(()) Ok(())

View File

@@ -6,6 +6,8 @@ use std::collections::BTreeMap as Map;
use std::fmt::Display; use std::fmt::Display;
use std::net::IpAddr; use std::net::IpAddr;
pub mod store;
pub struct Client { pub struct Client {
base_url: String, base_url: String,
token: String, token: String,

17
src/dls/store.rs Normal file
View File

@@ -0,0 +1,17 @@
pub fn hash_password(salt: &[u8], passphrase: &str) -> argon2::Result<[u8; 32]> {
let hash = argon2::hash_raw(
passphrase.as_bytes(),
salt,
&argon2::Config {
variant: argon2::Variant::Argon2id,
hash_length: 32,
time_cost: 1,
mem_cost: 65536,
thread_mode: argon2::ThreadMode::Parallel,
lanes: 4,
..Default::default()
},
)?;
unsafe { Ok(hash.try_into().unwrap_unchecked()) }
}

View File

@@ -83,6 +83,11 @@ impl Config {
} }
pub fn base64_decode(s: &str) -> Result<Vec<u8>, base64::DecodeError> { pub fn base64_decode(s: &str) -> Result<Vec<u8>, base64::DecodeError> {
use base64::{Engine, prelude::BASE64_STANDARD_NO_PAD as B64}; use base64::{prelude::BASE64_STANDARD_NO_PAD as B64, Engine as _};
B64.decode(s.trim_end_matches('=')) B64.decode(s.trim_end_matches('='))
} }
pub fn base64_encode(b: &[u8]) -> String {
use base64::{prelude::BASE64_STANDARD as B64, Engine as _};
B64.encode(b)
}