Initial version of whck

This commit is contained in:
southerntofu 2022-02-19 00:04:09 +01:00
commit 510a0b13b9
8 changed files with 331 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
.*.sw*

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "spec"]
path = spec
url = https://tildegit.org/forge/endpoints

117
Cargo.lock generated Normal file
View File

@ -0,0 +1,117 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cpufeatures"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "libc"
version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94"
[[package]]
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "whck"
version = "0.1.0"
dependencies = [
"hex",
"hmac",
"sha2",
]

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "whck"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hmac = "0.12"
sha2 = "0.10"
hex = "0.4"

1
spec Submodule

@ -0,0 +1 @@
Subproject commit 7eb833a59e7987bc73d303a1a261a0a664c791cc

51
src/helpers.rs Normal file
View File

@ -0,0 +1,51 @@
use std::env::var as env_var;
use std::path::PathBuf;
pub fn whck_dir() -> PathBuf {
whck_dir_from_env()
.unwrap_or(
whck_dir_from_xdg_config()
.unwrap_or(
whck_dir_from_home()
.unwrap_or(
whck_dir_from_exe()
.expect("Exhausted all methods to find ~/.config/whck/")
)
)
)
}
pub fn whck_dir_from_env() -> Option<PathBuf> {
env_var("WHCK_DIR").ok().map(|x| PathBuf::from(x))
}
pub fn whck_dir_from_xdg_config() -> Option<PathBuf> {
env_var("XDG_CONFIG_HOME").ok().map(|x| PathBuf::from(x).join("whck"))
}
pub fn whck_dir_from_home() -> Option<PathBuf> {
env_var("HOME").ok().map(|x| PathBuf::from(x).join(".config/whck"))
}
pub fn whck_dir_from_exe() -> Option<PathBuf> {
//std::env::current_exe().ok().and_then(|x| {
println!("{:?}", std::env::current_exe());
std::env::current_exe().ok().and_then(|x| {
// Canonicalize (resolve symlinks) in case we have a symlink from webdir
let basepath = x.canonicalize().unwrap();
let mut components = basepath.components();
println!("{:?}", components);
// We have canonicalized so component 0 is "RootDir"
if let Some(dir) = components.nth(1) {
if dir.as_os_str().to_str().unwrap() == "home" {
if let Some(user) = components.next() {
return Some(PathBuf::from("/home")
.join(user)
.join(".config/whck"));
} else {
println!("no user");
}
} else {
println!("Not home");
}
}
return None;
})
}

122
src/main.rs Normal file
View File

@ -0,0 +1,122 @@
use std::convert::From;
use std::env::args;
use std::fs::read_to_string;
use std::io::{Read, stdin};
use std::str::FromStr;
use std::process::exit;
use sha2::Sha256;
use hmac::{Hmac, Mac};
type HmacSha256 = Hmac<Sha256>;
mod helpers;
#[derive(Debug)]
pub enum Error {
WrongKind(String),
WrongClaim(String),
IOError(std::io::Error)
}
impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Error {
Error::IOError(error)
}
}
pub enum SigType {
SHA256HMAC,
}
pub enum Kind {
Sig(String, SigType),
Token,
}
impl FromStr for Kind {
type Err = Error;
fn from_str(s: &str) -> Result<Kind, Self::Err> {
match s {
"hmac-sha256" => {
let mut body = String::new();
let mut input = stdin();
input.read_to_string(&mut body)?;
println!("Body size: {}", body.len());
Ok(Kind::Sig(body, SigType::SHA256HMAC))
},
"token" => {
Ok(Kind::Token)
},
"help" | "-h" | "--help" => {
help();
exit(0);
},
_ => {
Err(Error::WrongKind(s.to_string()))
}
}
}
}
impl Kind {
// Returns true if the check succeeded, false otherwise
pub fn check(&self, secret: &str, claim: &str) -> bool {
match self {
Kind::Sig(body, sigtype) => {
println!("|{}|", &body);
match sigtype {
SigType::SHA256HMAC => {
let mut mac = HmacSha256::new_from_slice(secret.as_bytes()).unwrap();
mac.update(body.as_bytes());
let computed_sig = hex::encode(mac.finalize().into_bytes());
println!("Computed: {}", &computed_sig);
return computed_sig == claim;
},
}
},
Kind::Token => {
return secret == claim;
},
}
}
}
fn help() {
println!("whck KIND IDENTIFIER CLAIM BODY");
println!(" KIND: sig/token");
println!(" IDENTIFIER: the unique identifier for the stored secret in $XDG_CONFIG_DIR/whck/ folder");
println!(" CLAIM: the claimed sig/token to verify");
println!("");
println!("If you're checking a signature, don't forget to pass the body as STDIN");
println!("NOTE: If you're protecting secrets from other users (chmod -R 700 $XDG_CONFIG_HOME/whck),");
println!("don't forget to set the suid bit on the program (chmod u+s $(which whck)).")
}
// Panic on malformed $XDG_CONFIG_DIR
fn read_secret(id: &str) -> String {
if id.starts_with("/") || id.contains("/../") {
println!("Malformed identifier");
exit(1);
}
// $WHCK_DIR is used for tests
// Otherwise, try $XDG_CONFIG_HOME/whck or $HOME/.config/whck
// When home isn't defined try to check if $0 starts with /home and if so assume $HOME is there
let mut secret_path = helpers::whck_dir();
secret_path.push(id);
let secret = read_to_string(&secret_path).expect(&format!("Failed to read secret from: {}", &secret_path.to_str().unwrap()));
return secret;
}
fn main() -> Result<(), Error> {
let mut cli_args = args();
let arg_kind = cli_args.nth(1).expect("Missing argument: KIND");
let kind = Kind::from_str(&arg_kind)?;
let id = cli_args.nth(0).expect("Missing argument: IDENTIFIER");
let claim = cli_args.nth(0).expect("Missing argument: CLAIM");
let secret = read_secret(&id);
if ! kind.check(&secret, &claim) {
return Err(Error::WrongClaim(claim.clone()))
}
Ok(())
}

24
test.sh Executable file
View File

@ -0,0 +1,24 @@
#! /usr/bin/env bash
SCRIPTDIR="$(dirname "$0")"
ORIGDIR="$(pwd)"
if [ ! -f "$SCRIPTDIR"/spec/test_cli.sh ]; then
echo "Submodule not cloned yet. Doing it now"
cd $SCRIPTDIR
git submodule init
if ! git submodule update; then
echo "Failed to download submodules. Are you connected to the internet?"
exit 1
fi
fi
if [ -f "$SCRIPTDIR"/target/release/whck ]; then
cargo build --release
"$SCRIPTDIR"/spec/test_cli.sh "$SCRIPTDIR"/target/release/whck
else
cargo build
"$SCRIPTDIR"/spec/test_cli.sh "$SCRIPTDIR"/target/debug/whck
fi
cd "$ORIGDIR"