#!/bin/bash # packages: cracklib-runtime, openssh-client(s) # 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_Files.txt" RED='\033[0;31m' # red color for errorz NO_COLOR='\033[0m' # reset to no color after red # my silly pretty lil error handler :3 error_handler() { local retcode="$1" # get dat retcode # color da error output red :3 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}" } ### todo: environment check/package installa ### todo: make input of both passphrases only happen twice, compared against teach other, and tested ### todo: handle fix perms shit by narrowly changing perms of da private key fiel ### todo: handle file paths better ### todo: cleanup output #### spacing #### y 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?) ### 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: 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, detached signature file, checksums file, and detached checksums 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" # main file sig file cp "$file_to_sign_checksums_path" "$public_files_dir_path" # checksums file cp "$file_to_sign_checksums_sig_path" "$public_files_dir_path" # checksums 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)" checksums_sig_file_basename="$(basename $file_to_sign_checksums_sig_path)" checksums_file_basename="$(basename $file_to_sign_checksums_path)" ### format help text help_txt="To Verify Signatures: (Linux)\n\tssh-keygen -Y verify -f \"$allowed_signers_path_basename\" -I \"$email\" -n file -s \"$sig_file_path_basename\" < \"$file_to_sign_basename\"\n\tssh-keygen -Y verify -f \"$allowed_signers_path_basename\" -I \"$email\" -n file -s \"$checksums_sig_file_basename\" < \"$checksums_file_basename\"\n\nTo Verify Normal Checksums (Linux):\n\tsha256sum $file_to_sign_basename\n\tsha256sum $file_to_sign_basename\n\t\tThen compare to values in $checksums_file_basename\n\nTo Verify Authenticated Checksums Via Private Salt (Linux):\n\tprivate_salt="\"\n\techo -n -e \"\${private_salt}\$(cat $file_to_sign_basename)\" | sha256sum\n\techo -n -e \"\${private_salt}\$(cat $file_to_sign_basename)\" | sha512sum\n\t\tThen compare to values in $checksums_file_basename\n" ### 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"