225 lines
11 KiB
Bash
225 lines
11 KiB
Bash
#!/bin/bash
|
|
# packages: cracklib-runtime, openssh/openssh-client/openssh-clients/openssh-keygen/ssh-keygen
|
|
# set -x # debug mode
|
|
set -Eeuo pipefail # safety shit. fail to trap on any error, undeclared var, pipe filure
|
|
IFS=$'\n\t' # safer handlin of filenames and shit with spaces, prevents splitting
|
|
trap 'error_handler $?' ERR # prettyful trap for error handlin :3
|
|
|
|
# these can be adjusted interactively
|
|
default_email="anonymous@anonymous.com"
|
|
default_algo="ed25519"
|
|
default_working_dir="$PWD"
|
|
default_key_name="Signing_Key_$default_algo"
|
|
default_public_files_dir_name="public_files"
|
|
# set here not thrugh interactive
|
|
txt_help_file_name="How_to_Verify_Signature.txt"
|
|
|
|
# my silly pretty lil error handler :3
|
|
error_handler() {
|
|
local retcode="$1" # get dat retcode
|
|
# color da error output red :3
|
|
local RED='\033[0;31m' # red color
|
|
local NO_COLOR='\033[0m' # reset to no color
|
|
echo -e "\n\n${RED}ERROR: Failure With Return Code: $retcode on Line: $BASH_LINENO at Second: $SECONDS Timestamp: $(date)${NO_COLOR}"
|
|
echo -e "${RED}Command:\n\t$BASH_COMMAND\n\n${NO_COLOR}"
|
|
}
|
|
|
|
### x todo: error trap and handle
|
|
### todo: work in mktemp -d dir?
|
|
### todo: handle fix perms shit pls
|
|
### todo: handle file paths better
|
|
### todo: cleanup output
|
|
#### spacing
|
|
#### better feedback for success/fail of shit
|
|
#### clean shit uuuup
|
|
### todo: allow file input from cli
|
|
### todo: cli options for generate/sign/verify/all
|
|
### todo: auto-compress pub files? (cleartext zip, encrypted 7z?)
|
|
### x todo: messagging to properly handle dat damn key now lol
|
|
### x todo: sha256+sha512 checksums (straight from the file)
|
|
#### x PLUS sha256+sha512 checksums WITH INDEPENDANT SALT/PASSPPHRASE (+ the file)
|
|
#### x in txt files, both signed.
|
|
#### x meaning dat i can verify my identity not only with my private key, but ALSO independently via revealing the checksum salt/passphrase, without harming the cryptographic security or anonymity of either.
|
|
#### x prompt twice for SOLID AS SHIT passphrase, like >50 random chars hardcore shit
|
|
#### x verify it doesnt match key passphrase, that it passes dat normal pass check, and dat the two imputs match
|
|
#### x "sha256sum file: "+sha256sum(file) > file.checksums, "sha512sum file: "+sha512sum(file) >> file.checksums
|
|
#### x "salted sha256sum file: "+sha256sum(file+passphrase) >> file.checksums, "salted sha512sum file: "+sha512sum(file+passphrase) >> file.checksums
|
|
#### x sign file.checksums, adding file.checksums.sig
|
|
#### x add checksum salt verify method to help txt
|
|
#### x add normal checksum verify method to help txt
|
|
#### x verify normal and salted checksums
|
|
#### x prompt once again for passphrase for salted mode
|
|
#### x verify checksums file sig
|
|
#### todo: maybe figure out how to unify these sigs and files and shit?
|
|
|
|
read -p "Enter path to file to sign: " file_to_sign
|
|
read -p "Enter Private 50+ Character Passphrase For Checksum Verification: " checksum_passphrase
|
|
read -p "Enter Checksum Verification Passphrase Again: " checksum_passphrase_check
|
|
checksum_passphrase_len=${#checksum_passphrase} # get length of provided checksum passphrase
|
|
# check for match
|
|
if [[ "$checksum_passphrase" == "$checksum_passphrase_check" ]]; then
|
|
echo "OK: Checksum Verification Passphrases Match"
|
|
# check length greateer than or equal to 50
|
|
if [ $checksum_passphrase_len -ge 50 ]; then
|
|
echo "OK: Checksum Verification Passphrase is greater or equal to 50 chars long"
|
|
# check for pass security using cracklib-check from cracklib-runtime package
|
|
cracklib_check=$(echo "$checksum_passphrase" | cracklib-check)
|
|
if [[ ! "$cracklib_check" =~ OK$ ]]; then # check if it ends with anythign other than OK
|
|
echo -e "\n\nFAIL: Checksum Verification Passphrase NOT Secure Enough!\nReported: $cracklib_check${NO_COLOR}\n\n"
|
|
exit 1
|
|
else
|
|
echo "OK: Cracklib Reports Password is Secure"
|
|
fi
|
|
else
|
|
echo -e "\n\n${RED}FAIL: Checksum Verification Passphrase NOT Longer Than the Minimum 50 Chars${NO_COLOR}\n\n"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo -e "\n\n${RED}FAIL: Checksum Verification Passphrases DO NOT MATCH${NO_COLOR}\n\n"
|
|
exit 1
|
|
fi
|
|
read -p "Enter working directory (empty for $default_working_dir): " working_dir
|
|
read -p "Enter email to use (empty for $default_email): " email
|
|
read -p "Enter public key comment (empty for $default_email): " comment
|
|
read -p "Enter algo (empty for $default_algo): " algo
|
|
read -p "Enter new key name (Empty for $default_key_name): " key_name ### todo: test/handle
|
|
read -p "Enter public files output dir name (Empty for $default_public_files_dir_name): " public_files_dir_name
|
|
|
|
# handle defaults
|
|
## handle default working dir
|
|
### todo: check for dir existance first
|
|
### todo: check for dir rw perms first
|
|
if [ -z "$working_dir" ]; then
|
|
echo "Changing directory to $default_working_dir"
|
|
working_dir="$default_working_dir"
|
|
cd "$working_dir"
|
|
else
|
|
echo "Changing directory to $working_dir"
|
|
cd "$working_dir"
|
|
fi
|
|
## handle ddefault email
|
|
if [ -z "$email" ]; then
|
|
email="$default_email"
|
|
fi
|
|
## handle ddefault pub key comment
|
|
### todo: handle empty comment?
|
|
if [ -z "$comment" ]; then
|
|
comment="$default_email"
|
|
fi
|
|
## handle ddefault algo
|
|
### todo: check against ssh-keygen
|
|
if [ -z "$algo" ]; then
|
|
algo="$default_algo"
|
|
fi
|
|
## handle default key name
|
|
if [ -z "$key_name" ]; then
|
|
key_priv_path="$working_dir/$default_key_name"
|
|
else
|
|
key_priv_path="$working_dir/$key_name"
|
|
fi
|
|
## handle default publid files dir name/path
|
|
if [ -z "$public_files_dir_name" ]; then
|
|
public_files_dir_path="$working_dir/$default_public_files_dir_name"
|
|
else
|
|
public_files_dir_path="$working_dir/$public_files_dir_name"
|
|
fi
|
|
|
|
# environment checks
|
|
## check if file exists and is regular file
|
|
## todo: make this use absolute paths
|
|
if [ ! -f "$file_to_sign" ]; then
|
|
echo "FAIL! $file_to_sign not found or not regular file! Exititng!"
|
|
exit 1
|
|
fi
|
|
|
|
# sum housekeeping, makin file pathssss and shit
|
|
sig_file_path="$file_to_sign.sig" # path to detached signature file
|
|
key_pub_path="$key_priv_path.pub" # path to pub key file
|
|
allowed_signers_path="$working_dir/allowed_signers" # path to allowed_signers file
|
|
file_to_sign_checksums_path="$file_to_sign.checksums" # checksums file
|
|
file_to_sign_checksums_sig_path="$file_to_sign_checksums_path.sig" # checksums file sig file
|
|
file_to_sign_basename="$(basename $file_to_sign)" # file_to_sign name only
|
|
file_to_sign_contents="$(cat $file_to_sign)" # file_to_sign file contents
|
|
|
|
# generate key pair
|
|
## tell user to be sure to use a sdolid passphrase and wait for enter to continue
|
|
echo "BE SURE TO SAVE THIS KEY WITH A SINGLE USE DURABLE STRONG PASSPHRASE WHEB PROMPTED"
|
|
read -p "enter to continue" # wait for enter to contintue, nothign is read
|
|
echo "Generating new key pair $key_priv_path and $key_pub_path with algo $algo with comment/identity $email"
|
|
## -t is algo, -C is comment in pubkey -f is path to key file name
|
|
ssh-keygen -t "$algo" -C "$comment" -f "$key_priv_path"
|
|
## fix perms and pwnership on keyfilez
|
|
echo "Changing perms on dis dir and files to user/group $USER"
|
|
chown $USER:$USER -R "$working_dir"
|
|
find "$working_dir" -type d -exec chmod 700 {} \;
|
|
find "$working_dir" -type f -exec chmod 600 {} \;
|
|
|
|
# create a local allowed_signers file
|
|
echo "Creating alllowed_singers file at $allowed_signers_path with $key_pub_path and identity $email"
|
|
## format: <email> <pub key>
|
|
pub_key_text="$(cat $key_pub_path)" # text of public key
|
|
echo "$email $pub_key_text" > "$allowed_signers_path" # dont append, just one off here
|
|
|
|
# generate checksums
|
|
## normal ones sha256 and sha512
|
|
### initialize the checksums file new with normal sha256 and sha512 digests
|
|
echo -e "sha256 $(sha256sum $file_to_sign)\nsha512 $(sha512sum $file_to_sign)" > "$file_to_sign_checksums_path"
|
|
## salted hashes
|
|
### salted sha256
|
|
#### normal append first part of line
|
|
echo "sha256 (private salt+\`cat file\`): " >> "$file_to_sign_checksums_path"
|
|
#### no trailing newline echo to stdin sha256sum, clean up with awk, append to checksums file line
|
|
echo -n "${checksum_passphrase}${file_to_sign_contents}" | sha256sum | awk '{print $1}' >> "$file_to_sign_checksums_path"
|
|
#### no trailing newline echo append to checksums file, completing the sha256 salted line
|
|
echo -n "$file_to_sign_basename" >> "$file_to_sign_checksums_path"
|
|
### salted sha512
|
|
#### normal append first part of salted sha512 line
|
|
echo "sha512 (private salt+\`cat file\`): " >> "$file_to_sign_checksums_path"
|
|
#### no trailing newline echo to stdin sha512sum, clean up with awk, append to checksums file line
|
|
echo -n -e "${checksum_passphrase}${file_to_sign_contents}" | sha512sum | awk '{print $1}' >> "$file_to_sign_checksums_path"
|
|
#### no trailing newline echo append to checksums file, completing the sha512 salted line and completing the file
|
|
echo -n "$file_to_sign_basename" >> "$file_to_sign_checksums_path"
|
|
|
|
# sign the files
|
|
## sign da file to sign
|
|
echo "Signing $file_to_sign with detached signature file $sig_file_path"
|
|
### -Y mode (sign) -f private key path -n file (fuile mode) file.txt (file to sign)
|
|
ssh-keygen -Y sign -f "$key_priv_path" -n file "$file_to_sign"
|
|
## sign checksums file
|
|
echo "Signing $file_to_sign_checksums_path with detached signature file $file_to_sign_checksums_sig_path"
|
|
### -Y mode (sign) -f private key path -n file (fuile mode) file.txt (file to sign)
|
|
ssh-keygen -Y sign -f "$key_priv_path" -n file "$file_to_sign_checksums_path"
|
|
|
|
# verify sigs
|
|
## file to sign
|
|
echo "Verifying signature of $file_to_sign with signature file $sig_file_path for public key $key_pub_path and identitiy $email"
|
|
### -Y mode (verify) -f path to allowed_signers file -I email specified in allowed_signers file -n file (file mode) -s path to sig file < file.txt input file to verify
|
|
ssh-keygen -Y verify -f "$allowed_signers_path" -I "$email" -n file -s "$sig_file_path" < "$file_to_sign"
|
|
## checksums file
|
|
echo "Verifying signature of $file_to_sign_checksums_path with signature file $file_to_sign_checksums_sig_path for public key $key_pub_path and identitiy $email"
|
|
### -Y mode (verify) -f path to allowed_signers file -I email specified in allowed_signers file -n file (file mode) -s path to sig file < file.txt input file to verify
|
|
ssh-keygen -Y verify -f "$allowed_signers_path" -I "$email" -n file -s "$file_to_sign_checksums_sig_path" < "$file_to_sign_checksums_path"
|
|
|
|
# make public files copy
|
|
## create public files dir or fail if exists
|
|
### todo: use -p for ez reuse?
|
|
echo "Creating public files output dir at $public_files_dir_path"
|
|
mkdir "$public_files_dir_path"
|
|
## copy the pub files there
|
|
echo "Copying publc key, local allowed_signatures file, signed file, and detached signature file to $public_files_dir_path"
|
|
cp "$key_pub_path" "$public_files_dir_path" # public key
|
|
cp "$allowed_signers_path" "$public_files_dir_path" # allowed signers file
|
|
cp "$file_to_sign" "$public_files_dir_path" # signed file
|
|
cp "$sig_file_path" "$public_files_dir_path" # sig file
|
|
## generate the help txt file and put it in place
|
|
### get filenames only from the paths via basename
|
|
allowed_signers_path_basename="$(basename $allowed_signers_path)"
|
|
sig_file_path_basename="$(basename $sig_file_path)"
|
|
### format help text
|
|
help_txt="To Verify (Linux)\n\tssh-keygen -Y verify -f \"$allowed_signers_path_basename\" -I \"$email\" -n file -s \"$sig_file_path_basename\" < \"$file_to_sign_basename\""
|
|
### maek da file
|
|
echo "Creating custom help txt file in $public_files_dir_path/$txt_help_file_name"
|
|
echo -e "$help_txt" > "$public_files_dir_path/$txt_help_file_name"
|
|
|
|
# declare victory :3
|
|
echo -e "\nall donesies :3\n" |