diff --git a/openpgp/gitprotect.sh b/openpgp/gitprotect.sh index 22dfc1f67726110794dcfe66173ef3e67445d27b..672c50860b31af79ba1f11c8733f86f1766550ce 100755 --- a/openpgp/gitprotect.sh +++ b/openpgp/gitprotect.sh @@ -34,6 +34,10 @@ for storing the passwords centrally, allowing small geographically disperse teams to exchange them in a secure manner. The utility relies on using GnuPG utility for performing all tasks related to encryption and decryption. +The script works on induvidual directories by keeping the GnuPG keyring used for +encryption in the subdirectory .gnupg, and using a sub-directory 'decrypted' for +storing unencrypted content. + The following commands are provided for managing the repository/directories: init @@ -58,6 +62,17 @@ The following commands are provided for managing the repository/directories: Lists the keys from the git repository GnuPG keyring. + encrypt + + Encrypts all the files from the 'decrypted' sub-directory, and stores them + in the initialised directory. The encrypted files will have the .gpg + extension. + + decrypt + + Decrypts all the files from the current directory, storing them in the + 'decrypted' sub-directory. Only the files with extension .gpg are decrypted. + $program accepts the following options: -v show script version and licensing information @@ -140,6 +155,9 @@ function gitprotectConfigured() { # ERR_NOTINGIT=10 ERR_NOCONFIG=11 +ERR_NOKEYARG=12 +ERR_NODECRYPTDIR=13 +ERR_NORECIPIENTS=14 # If no arguments were given, just show usage help. if [[ -z $1 ]]; then @@ -173,30 +191,39 @@ gnupgHome="$(pwd)/.gnupg" if [[ $command == "init" ]]; then if [[ -d $gnupgHome ]]; then - echo "Directory already set-up." + echo "Directory already set-up." >&2 exit 0 fi - # Created the local .gnupg directory. + # Create the local .gnupg directory. mkdir "$gnupgHome" chmod 700 "$gnupgHome" - gpg2 --homedir "$gnupgHome" --list-keys 2>/dev/null + + # Initialise the GnuPG files in local directory. + gpg2 --batch --homedir "$gnupgHome" --list-keys 2>/dev/null elif [[ $command == "addkey" ]]; then gitprotectConfigured || exit "$ERR_NOCONFIG" + # At least one key has to be provided. + if [[ "${#@}" == 0 ]]; then + echo "ERROR: At least one key file or identifier must be specified" >&2 + exit "$ERR_NOKEYARG" + fi + # Process all the keys specified. for key in "$@"; do # First try accessing a file by the given key name. Otherwise treat it # as key identifier. if [[ -f $key ]]; then - if ! gpg2 --homedir "$gnupgHome" --import "$key"; then + if ! gpg2 --batch --homedir "$gnupgHome" --import "$key"; then echo "ERROR: Failed to add key from file '$key'." >&2 fi else - if ! gpg2 --list-keys "$key" >/dev/null 2>&1; then + if ! gpg2 --batch --list-keys "$key" >/dev/null 2>&1; then echo "WARN: Key with identifier '$key' not found in user's GnuPG keyring. Skipping." >&2 else - if ! gpg2 --armor --export "$key" | gpg2 --homedir "$gnupgHome" --import; then + ! gpg2 --batch --armor --export "$key" | gpg2 --batch --homedir "$gnupgHome" --import + if [[ ${PIPESTATUS[0]} != 0 ]]; then echo "ERROR: Failed to add key with identifier '$key')." >&2 fi fi @@ -205,19 +232,69 @@ elif [[ $command == "addkey" ]]; then elif [[ $command = "rmkey" ]]; then gitprotectConfigured || exit "$ERR_NOCONFIG" + # At least one key has to be provided. + if [[ "${#@}" == 0 ]]; then + echo "ERROR: At least one key file or identifier must be specified" >&2 + exit "$ERR_NOKEYARG" + fi + # Process all the keys specified. for key in "$@"; do - if ! gpg2 --homedir "$gnupgHome" --list-key "$key" 2>/dev/null; then + if ! gpg2 --batch --homedir "$gnupgHome" --list-key "$key" 2>/dev/null; then echo "WARN: Key with identifier '$key' not found in git repository directory's GnuPG keyring. Skipping" >&2 - else - if ! gpg2 --homedir "$gnupgHome" --yes --delete-key "$key"; then - echo "ERROR: Failed to remove the key with identifier '$key'." >&2 - fi + elif ! gpg2 --batch --homedir "$gnupgHome" --yes --delete-key "$key"; then + echo "ERROR: Failed to remove the key with identifier '$key'." >&2 fi done elif [[ $command = "listkeys" ]]; then gitprotectConfigured || exit "$ERR_NOCONFIG" - gpg2 --fingerprint --homedir "$gnupgHome" --list-public-keys --keyid-format long + gpg2 --batch --homedir "$gnupgHome" --list-public-keys --keyid-format long +elif [[ $command = "encrypt" ]]; then + gitprotectConfigured || exit "$ERR_NOCONFIG" + + # Verify that the directory with unencrypted files exists. + if [[ ! -d "decrypted/" ]]; then + echo "ERROR: Nothing to encrypt. sub-directory 'decrypted' does not exist." + exit "$ERR_NODECRYPTDIR" + fi + + # Set-up the list of recipients. Read the information about each public + # sub-key from the local keyring. + while read key_validity key_id key_capabilities; do + # Only use non-expired sub-keys that have encryption capability. + if [[ $key_validity != e && $key_capabilities =~ .*e.* ]]; then + recipients+=("-r" "$key_id") + fi + done < <(gpg2 --homedir "$gnupgHome" --list-public-keys --with-colons | grep '^sub' | awk 'BEGIN { FS = ":" } ; { print $2, $5, $12 }') + + # Make sure that we have at least a single recipient. + if [[ "${#recipients[@]}" == 0 ]]; then + echo "ERROR: No suitable recipients were found in the keyring." >&2 + exit "$ERR_NORECIPIENTS" + fi + + # Encrypt every file from the decrypted sub-directory. + while read filePath; do + filename=$(basename "$filePath") + cat "$filePath" | gpg2 --trust-model always --batch --homedir "$gnupgHome" \ + --armor "${recipients[@]}" --encrypt > "${filename}.gpg" + done < <(find "decrypted/" -maxdepth 1 -type f) +elif [[ $command = "decrypt" ]]; then + gitprotectConfigured || exit "$ERR_NOCONFIG" + + # Create the sub-directory that will contain the decrypted data. + mkdir -p "decrypted/" + + # Process each GnuPG-encrypted file. + while read filePath; do + filename=$(basename "$filePath" ".gpg") + + # Remove the "decrypted" file if decryption had failed. + if ! gpg2 --decrypt "$filePath" > "decrypted/$filename"; then + echo "ERROR: Failed to decrypt file '$filepath'. No private key available." >&2 + rm "decrypted/$filename" + fi + done < <(find . -maxdepth 1 -name '*.gpg') else echo "ERROR: Unsupported command '$command'" >&2 fi