Initial version of whck
This commit is contained in:
commit
510a0b13b9
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.*.sw*
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "spec"]
|
||||
path = spec
|
||||
url = https://tildegit.org/forge/endpoints
|
|
@ -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",
|
||||
]
|
|
@ -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"
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 7eb833a59e7987bc73d303a1a261a0a664c791cc
|
|
@ -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;
|
||||
})
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -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"
|
Loading…
Reference in New Issue