implement better hashing
- use Blake2b128 for the CLI arguments hash
This commit is contained in:
parent
ece742a1e3
commit
6e553cc185
5 changed files with 109 additions and 32 deletions
63
Cargo.lock
generated
63
Cargo.lock
generated
|
|
@ -73,12 +73,36 @@ version = "0.22.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.1"
|
version = "2.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.17.0"
|
version = "3.17.0"
|
||||||
|
|
@ -209,6 +233,16 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctrlc"
|
name = "ctrlc"
|
||||||
version = "3.4.7"
|
version = "3.4.7"
|
||||||
|
|
@ -239,6 +273,17 @@ dependencies = [
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-next"
|
name = "dirs-next"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
|
@ -340,6 +385,16 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
@ -846,6 +901,8 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
name = "shrupl"
|
name = "shrupl"
|
||||||
version = "0.1.0-alpha"
|
version = "0.1.0-alpha"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"blake2",
|
||||||
"clap",
|
"clap",
|
||||||
"console",
|
"console",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
|
|
@ -988,6 +1045,12 @@ dependencies = [
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ edition = "2024"
|
||||||
description = "ShrUpl is a tool to upload files to a Sharry Instance through a public Alias, leveraging the tus protocol"
|
description = "ShrUpl is a tool to upload files to a Sharry Instance through a public Alias, leveraging the tus protocol"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
base64ct = { version = "1.8.0", default-features = false, features = ["alloc"] }
|
||||||
|
blake2 = { version = "0.10.6", default-features = false }
|
||||||
clap = { version = "4.5.38", features = ["derive"] }
|
clap = { version = "4.5.38", features = ["derive"] }
|
||||||
console = { version = "0.15.11", default-features = false }
|
console = { version = "0.15.11", default-features = false }
|
||||||
ctrlc = { version = "3.4.7", features = ["termination"] }
|
ctrlc = { version = "3.4.7", features = ["termination"] }
|
||||||
|
|
|
||||||
40
src/cli.rs
40
src/cli.rs
|
|
@ -1,11 +1,7 @@
|
||||||
use std::{
|
use std::{convert::Infallible, fmt, io, time::Duration};
|
||||||
convert::Infallible,
|
|
||||||
fmt,
|
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
|
||||||
io,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use base64ct::{Base64UrlUnpadded, Encoding};
|
||||||
|
use blake2::{Blake2b, Digest, digest::consts::U16};
|
||||||
use clap::{
|
use clap::{
|
||||||
Parser,
|
Parser,
|
||||||
builder::{PossibleValuesParser, TypedValueParser},
|
builder::{PossibleValuesParser, TypedValueParser},
|
||||||
|
|
@ -18,7 +14,7 @@ use crate::{
|
||||||
sharry::{NewShareRequest, Uri},
|
sharry::{NewShareRequest, Uri},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Parser, Hash)]
|
#[derive(Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
/// Timeout in seconds for HTTP actions (set 0 or invalid to disable)
|
/// Timeout in seconds for HTTP actions (set 0 or invalid to disable)
|
||||||
|
|
@ -100,6 +96,18 @@ fn parse_sharry_file(data: &str) -> io::Result<Checked> {
|
||||||
Checked::new(data)
|
Checked::new(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Blake2b128 = Blake2b<U16>;
|
||||||
|
|
||||||
|
fn sorted<T>(values: &[T]) -> Vec<&T>
|
||||||
|
where
|
||||||
|
T: Ord,
|
||||||
|
{
|
||||||
|
let mut refs: Vec<_> = values.iter().collect();
|
||||||
|
refs.sort_unstable();
|
||||||
|
|
||||||
|
refs
|
||||||
|
}
|
||||||
|
|
||||||
impl Cli {
|
impl Cli {
|
||||||
pub fn get_timeout(&self) -> Option<Duration> {
|
pub fn get_timeout(&self) -> Option<Duration> {
|
||||||
(!self.timeout.is_zero()).then_some(self.timeout)
|
(!self.timeout.is_zero()).then_some(self.timeout)
|
||||||
|
|
@ -135,16 +143,14 @@ impl Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hash(&self) -> String {
|
pub fn get_hash(&self) -> String {
|
||||||
let file_refs = {
|
let mut hasher = Blake2b128::new();
|
||||||
let mut refs: Vec<_> = self.files.iter().collect();
|
hasher.update(self.get_uri());
|
||||||
refs.sort_unstable();
|
hasher.update(&self.alias);
|
||||||
|
|
||||||
refs
|
for chk in sorted(&self.files) {
|
||||||
};
|
hasher.update(chk);
|
||||||
|
}
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
Base64UrlUnpadded::encode_string(&hasher.finalize())
|
||||||
(self.get_uri(), &self.alias, file_refs).hash(&mut hasher);
|
|
||||||
|
|
||||||
format!("{:x}", hasher.finish())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ use super::{FileTrait, Uploading};
|
||||||
|
|
||||||
/// Description of an existing, regular file
|
/// Description of an existing, regular file
|
||||||
///
|
///
|
||||||
/// - impl Debug, Clone, Hash for `clap` compatibility
|
/// - impl Clone for `clap` compatibility
|
||||||
/// - impl serde for appstate caching
|
/// - impl serde for appstate caching
|
||||||
/// - impl Ord to handle multiple files given
|
/// - impl PartialEq..Ord to handle multiple files given
|
||||||
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Checked {
|
pub struct Checked {
|
||||||
/// canonical path to a regular file
|
/// canonical path to a regular file
|
||||||
pub(super) path: PathBuf,
|
pub(super) path: PathBuf,
|
||||||
|
|
@ -22,6 +22,12 @@ pub struct Checked {
|
||||||
pub(super) size: u64,
|
pub(super) size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for Checked {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
self.path.as_os_str().as_encoded_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Checked {
|
impl Checked {
|
||||||
/// create a new checked file from some path reference
|
/// create a new checked file from some path reference
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,28 @@ use std::fmt;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Hash)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Uri {
|
pub struct Uri(String);
|
||||||
protocol: String,
|
|
||||||
base_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Uri {
|
impl fmt::Display for Uri {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}://{}", self.protocol, self.base_url)
|
f.write_str(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for Uri {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
self.0.as_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Uri {
|
impl Uri {
|
||||||
pub fn new(protocol: impl Into<String>, base_url: impl Into<String>) -> Self {
|
pub fn new(protocol: impl fmt::Display, base_url: impl fmt::Display) -> Self {
|
||||||
Self {
|
Self(format!("{}://{}", protocol, base_url))
|
||||||
protocol: protocol.into(),
|
|
||||||
base_url: base_url.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endpoint(&self, path: fmt::Arguments) -> String {
|
fn endpoint(&self, path: fmt::Arguments) -> String {
|
||||||
let uri = format!("{}://{}/api/v2/{path}", self.protocol, self.base_url);
|
let uri = format!("{}/api/v2/{path}", self.0);
|
||||||
trace!("endpoint: {uri:?}");
|
trace!("endpoint: {uri:?}");
|
||||||
uri
|
uri
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue