#!/usr/bin/env nix-shell #! nix-shell -i bash --pure #! nix-shell -p bash openssl git unixtools.column set -euo pipefail # # transcrypt - https://github.com/elasticdog/transcrypt # # A script to configure transparent encryption of sensitive files stored in # a Git repository. It utilizes OpenSSL's symmetric cipher routines and follows # the gitattributes(5) man page regarding the use of filters. # # Copyright (c) 2014-2019 Aaron Bull Schaefer # This source code is provided under the terms of the MIT License # that can be be found in the LICENSE file. # ##### CONSTANTS # the release version of this script readonly VERSION='2.0.0' # the default cipher to utilize readonly DEFAULT_CIPHER='aes-256-ctr' # the openssl options to encrypt/decrypt the files # shellcheck disable=SC2016 readonly ENCRYPT_OPTIONS='-$cipher -pbkdf2 -iter 200000' # regular expression used to test user input readonly YES_REGEX='^[Yy]$' ## Repository Metadata # whether or not transcrypt is already configured readonly CONFIGURED=$(git config --get --local transcrypt.version 2>/dev/null) # the current git repository's top-level directory readonly REPO=$(git rev-parse --show-toplevel 2>/dev/null) # whether or not a HEAD revision exists readonly HEAD_EXISTS=$(git rev-parse --verify --quiet HEAD 2>/dev/null) # https://github.com/RichiH/vcsh # whether or not the git repository is running under vcsh readonly IS_VCSH=$(git config --get --local --bool vcsh.vcsh 2>/dev/null) # whether or not the git repository is bare readonly IS_BARE=$(git rev-parse --is-bare-repository 2>/dev/null) ## Git Directory Handling # print a canonicalized absolute pathname realpath() { local path=$1 # make path absolute local abspath=$path if [[ -n ${abspath##/*} ]]; then abspath=$(pwd -P)/$abspath fi # canonicalize path local dirname= if [[ -d $abspath ]]; then dirname=$(cd "$abspath" && pwd -P) abspath=$dirname elif [[ -e $abspath ]]; then dirname=$(cd "${abspath%/*}/" 2>/dev/null && pwd -P) abspath=$dirname/${abspath##*/} fi if [[ -d $dirname && -e $abspath ]]; then printf '%s\n' "$abspath" else printf 'invalid path: %s\n' "$path" >&2 exit 1 fi } # the current git repository's .git directory RELATIVE_GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) readonly GIT_DIR=$(realpath "$RELATIVE_GIT_DIR" 2>/dev/null) # the current git repository's gitattributes file readonly CORE_ATTRIBUTES=$(git config --get --local --path core.attributesFile) if [[ $CORE_ATTRIBUTES ]]; then readonly GIT_ATTRIBUTES=$CORE_ATTRIBUTES elif [[ $IS_BARE == 'true' ]] || [[ $IS_VCSH == 'true' ]]; then readonly GIT_ATTRIBUTES="${GIT_DIR}/info/attributes" else readonly GIT_ATTRIBUTES="${REPO}/.gitattributes" fi ##### FUNCTIONS # print a message to stderr warn() { local fmt="$1" shift # shellcheck disable=SC2059 printf "transcrypt: $fmt\n" "$@" >&2 } # print a message to stderr and exit with either # the given status or that of the most recent command die() { local st="$?" if [[ "$1" != *[^0-9]* ]]; then st="$1" shift fi warn "$@" exit "$st" } # verify that all requirements have been met run_safety_checks() { # validate that we're in a git repository [[ $GIT_DIR ]] || die 'you are not currently in a git repository; did you forget to run "git init"?' # exit if transcrypt is not in the required state if [[ $requires_existing_config ]] && [[ ! $CONFIGURED ]]; then die 1 'the current repository is not configured' elif [[ ! $requires_existing_config ]] && [[ $CONFIGURED ]]; then die 1 'the current repository is already configured; see --display' fi # check for dependencies for cmd in {column,grep,mktemp,openssl,sed,tee}; do command -v $cmd >/dev/null || die 'required command "%s" was not found' "$cmd" done # ensure the repository is clean (if it has a HEAD revision) so we can force # checkout files without the destruction of uncommitted changes if [[ $requires_clean_repo ]] && [[ $HEAD_EXISTS ]] && [[ $IS_BARE == 'false' ]]; then # check if the repo is dirty if ! git diff-index --quiet HEAD --; then die 1 'the repo is dirty; commit or stash your changes before running transcrypt' fi fi } # unset the cipher variable if it is not supported by openssl validate_cipher() { local list_cipher_commands list_cipher_commands='openssl enc -ciphers' remove_dash() { sed 's#\(^\| \)-#\1#g' } local supported supported=$($list_cipher_commands | remove_dash | tr -s ' ' '\n' | grep --line-regexp "$cipher") || true if [[ ! $supported ]]; then if [[ $interactive ]]; then printf '"%s" is not a valid cipher; choose one of the following:\n\n' "$cipher" $list_cipher_commands | remove_dash | column -c 80 printf '\n' cipher='' else # shellcheck disable=SC2016 die 1 '"%s" is not a valid cipher; see `%s`' "$cipher" "$($list_cipher_commands | remove_dash)" fi fi } # ensure we have a cipher to encrypt with get_cipher() { while [[ ! $cipher ]]; do local answer= if [[ $interactive ]]; then printf 'Encrypt using which cipher? [%s] ' "$DEFAULT_CIPHER" read -r answer fi # use the default cipher if the user gave no answer; # otherwise verify the given cipher is supported by openssl if [[ ! $answer ]]; then cipher=$DEFAULT_CIPHER else cipher=$answer validate_cipher fi done } # ensure we have a password to encrypt with get_password() { while [[ ! $password ]]; do local answer= if [[ $interactive ]]; then printf 'Generate a random password? [Y/n] ' read -r -n 1 -s answer printf '\n' fi # generate a random password if the user answered yes; # otherwise prompt the user for a password if [[ $answer =~ $YES_REGEX ]] || [[ ! $answer ]]; then local password_length=30 local random_base64 random_base64=$(openssl rand -base64 $password_length) password=$random_base64 else printf 'Password: ' read -r password [[ $password ]] || printf 'no password was specified\n' fi done } # confirm the transcrypt configuration confirm_configuration() { local answer= printf '\nRepository metadata:\n\n' [[ ! $REPO ]] || printf ' GIT_WORK_TREE: %s\n' "$REPO" printf ' GIT_DIR: %s\n' "$GIT_DIR" printf ' GIT_ATTRIBUTES: %s\n\n' "$GIT_ATTRIBUTES" printf 'The following configuration will be saved:\n\n' printf ' CIPHER: %s\n' "$cipher" printf ' PASSWORD: %s\n\n' "$password" printf 'Does this look correct? [Y/n] ' read -r -n 1 -s answer # exit if the user did not confirm if [[ $answer =~ $YES_REGEX ]] || [[ ! $answer ]]; then printf '\n\n' else printf '\n' die 1 'configuration has been aborted' fi } # confirm the rekey configuration confirm_rekey() { local answer= printf '\nRepository metadata:\n\n' [[ ! $REPO ]] || printf ' GIT_WORK_TREE: %s\n' "$REPO" printf ' GIT_DIR: %s\n' "$GIT_DIR" printf ' GIT_ATTRIBUTES: %s\n\n' "$GIT_ATTRIBUTES" printf 'The following configuration will be saved:\n\n' printf ' CIPHER: %s\n' "$cipher" printf ' PASSWORD: %s\n\n' "$password" printf 'You are about to re-encrypt all encrypted files using new credentials.\n' printf 'Once you do this, their historical diffs will no longer display in plain text.\n\n' printf 'Proceed with rekey? [y/N] ' read -r answer # only rekey if the user explicitly confirmed if [[ $answer =~ $YES_REGEX ]]; then printf '\n' else die 1 'rekeying has been aborted' fi } # automatically stage rekeyed files in preparation for the user to commit them stage_rekeyed_files() { local encrypted_files encrypted_files=$(git ls-crypt) if [[ $encrypted_files ]] && [[ $IS_BARE == 'false' ]]; then # touch all encrypted files to prevent stale stat info cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO" # shellcheck disable=SC2086 touch $encrypted_files # shellcheck disable=SC2086 git update-index --add -- $encrypted_files printf '*** rekeyed files have been staged ***\n' printf '*** COMMIT THESE CHANGES RIGHT AWAY! ***\n\n' fi } # save helper scripts under the repository's git directory save_helper_scripts() { mkdir -p "${GIT_DIR}/crypt" openssl_command="openssl enc $ENCRYPT_OPTIONS -pass env:ENC_PASS" # The `decryption -> encryption` process on an unchanged file must be # deterministic for everything to work transparently. To do that, the same # salt must be used each time we encrypt the same file. An HMAC has been # proven to be a PRF, so we generate an HMAC-SHA256 for each decrypted file # (keyed with a combination of the filename and transcrypt password), and # then use the last 16 bytes of that HMAC for the file's unique salt. cat <<-'EOF' >"${GIT_DIR}/crypt/clean" #!/usr/bin/env bash filename=$1 # ignore empty files if [[ -s $filename ]]; then # cache STDIN to test if it's already encrypted tempfile=$(mktemp 2>/dev/null || mktemp -t tmp) trap 'rm -f "$tempfile"' EXIT tee "$tempfile" &>/dev/null # the first bytes of an encrypted file are always "Salted" in Base64 read -n 8 firstbytes <"$tempfile" if [[ $firstbytes == "U2FsdGVk" ]]; then cat "$tempfile" else cipher=$(git config --get --local transcrypt.cipher) password=$(git config --get --local transcrypt.password) salt=$(openssl dgst -hmac "${filename}:${password}" -sha256 "$filename" | tr -d '\r\n' | tail -c 16) ENC_PASS=$password @openssl_command@ -e -a -S "$salt" -in "$tempfile" fi fi EOF cat <<-'EOF' >"${GIT_DIR}/crypt/smudge" #!/usr/bin/env bash tempfile=$(mktemp 2>/dev/null || mktemp -t tmp) trap 'rm -f "$tempfile"' EXIT cipher=$(git config --get --local transcrypt.cipher) password=$(git config --get --local transcrypt.password) tee "$tempfile" | ENC_PASS=$password @openssl_command@ -d -a 2>/dev/null || cat "$tempfile" EOF cat <<-'EOF' >"${GIT_DIR}/crypt/textconv" #!/usr/bin/env bash filename=$1 # ignore empty files if [[ -s $filename ]]; then cipher=$(git config --get --local transcrypt.cipher) password=$(git config --get --local transcrypt.password) ENC_PASS=$password @openssl_command@ -d -a -in "$filename" 2>/dev/null || cat "$filename" fi EOF # make scripts executable for script in {clean,smudge,textconv}; do chmod 0755 "${GIT_DIR}/crypt/${script}" sed "s/@openssl_command@/$openssl_command/" -i "${GIT_DIR}/crypt/${script}" done } # write the configuration to the repository's git config save_configuration() { save_helper_scripts # write the encryption info git config transcrypt.version "$VERSION" git config transcrypt.cipher "$cipher" git config transcrypt.password "$password" # write the filter settings if [[ -d $(git rev-parse --git-common-dir) ]]; then # this allows us to support multiple working trees via git-worktree # ...but the --git-common-dir flag was only added in November 2014 # shellcheck disable=SC2016 git config filter.crypt.clean '"$(git rev-parse --git-common-dir)"/crypt/clean %f' # shellcheck disable=SC2016 git config filter.crypt.smudge '"$(git rev-parse --git-common-dir)"/crypt/smudge' # shellcheck disable=SC2016 git config diff.crypt.textconv '"$(git rev-parse --git-common-dir)"/crypt/textconv' else # shellcheck disable=SC2016 git config filter.crypt.clean '"$(git rev-parse --git-dir)"/crypt/clean %f' # shellcheck disable=SC2016 git config filter.crypt.smudge '"$(git rev-parse --git-dir)"/crypt/smudge' # shellcheck disable=SC2016 git config diff.crypt.textconv '"$(git rev-parse --git-dir)"/crypt/textconv' fi git config filter.crypt.required 'true' git config diff.crypt.cachetextconv 'true' git config diff.crypt.binary 'true' git config merge.renormalize 'true' # add a git alias for listing encrypted files git config alias.ls-crypt "!git ls-files | git check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; /crypt$/{ print \$1 }'" } # display the current configuration settings display_configuration() { local current_cipher current_cipher=$(git config --get --local transcrypt.cipher) local current_password current_password=$(git config --get --local transcrypt.password) local escaped_password=${current_password//\'/\'\\\'\'} printf 'The current repository was configured using transcrypt version %s\n' "$CONFIGURED" printf 'and has the following configuration:\n\n' [[ ! $REPO ]] || printf ' GIT_WORK_TREE: %s\n' "$REPO" printf ' GIT_DIR: %s\n' "$GIT_DIR" printf ' GIT_ATTRIBUTES: %s\n\n' "$GIT_ATTRIBUTES" printf ' CIPHER: %s\n' "$current_cipher" printf ' PASSWORD: %s\n\n' "$current_password" printf 'Copy and paste the following command to initialize a cloned repository:\n\n' printf " transcrypt -c %s -p '%s'\n" "$current_cipher" "$escaped_password" } # remove transcrypt-related settings from the repository's git config clean_gitconfig() { git config --remove-section transcrypt 2>/dev/null || true git config --remove-section filter.crypt 2>/dev/null || true git config --remove-section diff.crypt 2>/dev/null || true git config --unset merge.renormalize # remove the merge section if it's now empty local merge_values merge_values=$(git config --get-regex --local 'merge\..*') || true if [[ ! $merge_values ]]; then git config --remove-section merge 2>/dev/null || true fi } # force the checkout of any files with the crypt filter applied to them; # this will decrypt existing encrypted files if you've just cloned a repository, # or it will encrypt locally decrypted files if you've just flushed the credentials force_checkout() { # make sure a HEAD revision exists if [[ $HEAD_EXISTS ]] && [[ $IS_BARE == 'false' ]]; then # this would normally delete uncommitted changes in the working directory, # but we already made sure the repo was clean during the safety checks local encrypted_files encrypted_files=$(git ls-crypt) cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO" IFS=$'\n' for file in $encrypted_files; do rm "$file" git checkout --force HEAD -- "$file" >/dev/null done unset IFS fi } # remove the locally cached encryption credentials and # re-encrypt any files that had been previously decrypted flush_credentials() { local answer= if [[ $interactive ]]; then printf 'You are about to flush the local credentials; make sure you have saved them elsewhere.\n' printf 'All previously decrypted files will revert to their encrypted form.\n\n' printf 'Proceed with credential flush? [y/N] ' read -r answer printf '\n' else # although destructive, we should support the --yes option answer='y' fi # only flush if the user explicitly confirmed if [[ $answer =~ $YES_REGEX ]]; then clean_gitconfig # re-encrypt any files that had been previously decrypted force_checkout printf 'The local transcrypt credentials have been successfully flushed.\n' else die 1 'flushing of credentials has been aborted' fi } # remove all transcrypt configuration from the repository uninstall_transcrypt() { local answer= if [[ $interactive ]]; then printf 'You are about to remove all transcrypt configuration from your repository.\n' printf 'All previously encrypted files will remain decrypted in this working copy.\n\n' printf 'Proceed with uninstall? [y/N] ' read -r answer printf '\n' else # although destructive, we should support the --yes option answer='y' fi # only uninstall if the user explicitly confirmed if [[ $answer =~ $YES_REGEX ]]; then clean_gitconfig # remove helper scripts for script in {clean,smudge,textconv}; do [[ ! -f "${GIT_DIR}/crypt/${script}" ]] || rm "${GIT_DIR}/crypt/${script}" done [[ ! -d "${GIT_DIR}/crypt" ]] || rmdir "${GIT_DIR}/crypt" # touch all encrypted files to prevent stale stat info local encrypted_files encrypted_files=$(git ls-crypt) if [[ $encrypted_files ]] && [[ $IS_BARE == 'false' ]]; then cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO" # shellcheck disable=SC2086 touch $encrypted_files fi # remove the `git ls-crypt` alias git config --unset alias.ls-crypt # remove the alias section if it's now empty local alias_values alias_values=$(git config --get-regex --local 'alias\..*') || true if [[ ! $alias_values ]]; then git config --remove-section alias 2>/dev/null || true fi # remove any defined crypt patterns in gitattributes case $OSTYPE in darwin*) /usr/bin/sed -i '' '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES" ;; linux*) sed -i '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES" ;; esac printf 'The transcrypt configuration has been completely removed from the repository.\n' else die 1 'uninstallation has been aborted' fi } # list all of the currently encrypted files in the repository list_files() { if [[ $IS_BARE == 'false' ]]; then cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO" git ls-files | git check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }' fi } # show the raw file as stored in the git commit object show_raw_file() { if [[ -f $show_file ]]; then # ensure the file is currently being tracked local escaped_file=${show_file//\//\\\/} if git ls-files --others -- "$show_file" | awk "/${escaped_file}/{ exit 1 }"; then file_paths=$(git ls-tree --name-only --full-name HEAD "$show_file") else die 1 'the file "%s" is not currently being tracked by git' "$show_file" fi elif [[ $show_file == '*' ]]; then file_paths=$(git ls-crypt) else die 1 'the file "%s" does not exist' "$show_file" fi IFS=$'\n' for file in $file_paths; do printf '==> %s <==\n' "$file" >&2 git --no-pager show HEAD:"$file" --no-textconv printf '\n' >&2 done unset IFS } # export password and cipher to a gpg encrypted file export_gpg() { # check for dependencies command -v gpg >/dev/null || die 'required command "gpg" was not found' # ensure the recipient key exists if ! gpg --list-keys "$gpg_recipient" 2>/dev/null; then die 1 'GPG recipient key "%s" does not exist' "$gpg_recipient" fi local current_cipher current_cipher=$(git config --get --local transcrypt.cipher) local current_password current_password=$(git config --get --local transcrypt.password) mkdir -p "${GIT_DIR}/crypt" local gpg_encrypt_cmd="gpg --batch --recipient $gpg_recipient --trust-model always --yes --armor --quiet --encrypt -" printf 'password=%s\ncipher=%s\n' "$current_password" "$current_cipher" | $gpg_encrypt_cmd >"${GIT_DIR}/crypt/${gpg_recipient}.asc" printf "The transcrypt configuration has been encrypted and exported to:\n%s/crypt/%s.asc\n" "$GIT_DIR" "$gpg_recipient" } # import password and cipher from a gpg encrypted file import_gpg() { # check for dependencies command -v gpg >/dev/null || die 'required command "gpg" was not found' local path if [[ -f "${GIT_DIR}/crypt/${gpg_import_file}" ]]; then path="${GIT_DIR}/crypt/${gpg_import_file}" elif [[ -f "${GIT_DIR}/crypt/${gpg_import_file}.asc" ]]; then path="${GIT_DIR}/crypt/${gpg_import_file}.asc" elif [[ ! -f $gpg_import_file ]]; then die 1 'the file "%s" does not exist' "$gpg_import_file" else path="$gpg_import_file" fi local configuration='' local safety_counter=0 # fix for intermittent 'no secret key' decryption failures while [[ ! $configuration ]]; do configuration=$(gpg --batch --quiet --decrypt "$path") safety_counter=$((safety_counter + 1)) if [[ $safety_counter -eq 3 ]]; then die 1 'unable to decrypt the file "%s"' "$path" fi done cipher=$(printf '%s' "$configuration" | grep '^cipher' | cut -d'=' -f 2-) password=$(printf '%s' "$configuration" | grep '^password' | cut -d'=' -f 2-) } # print this script's usage message to stderr usage() { cat <<-EOF >&2 usage: transcrypt [-c CIPHER] [-p PASSWORD] [-h] EOF } # print this script's help message to stdout help() { cat <<-EOF NAME transcrypt -- transparently encrypt files within a git repository SYNOPSIS transcrypt [options...] DESCRIPTION transcrypt will configure a Git repository to support the transparent encryption/decryption of files by utilizing OpenSSL's symmetric cipher routines and Git's built-in clean/smudge filters. It will also add a Git alias "ls-crypt" to list all transparently encrypted files within the repository. The transcrypt source code and full documentation may be downloaded from https://github.com/elasticdog/transcrypt. OPTIONS -c, --cipher=CIPHER the symmetric cipher to utilize for encryption; defaults to aes-256-cbc -p, --password=PASSWORD the password to derive the key from; defaults to 30 random base64 characters -y, --yes assume yes and accept defaults for non-specified options -d, --display display the current repository's cipher and password -r, --rekey re-encrypt all encrypted files using new credentials -f, --flush-credentials remove the locally cached encryption credentials and re-encrypt any files that had been previously decrypted -F, --force ignore whether the git directory is clean, proceed with the possibility that uncommitted changes are overwritten -u, --uninstall remove all transcrypt configuration from the repository and leave files in the current working copy decrypted -l, --list list all of the transparently encrypted files in the repository, relative to the top-level directory -s, --show-raw=FILE show the raw file as stored in the git commit object; use this to check if files are encrypted as expected -e, --export-gpg=RECIPIENT export the repository's cipher and password to a file encrypted for a gpg recipient -i, --import-gpg=FILE import the password and cipher from a gpg encrypted file -v, --version print the version information -h, --help view this help message EXAMPLES To initialize a Git repository to support transparent encryption, just change into the repo and run the transcrypt script. transcrypt will prompt you interactively for all required information if the corre- sponding option flags were not given. $ cd / $ transcrypt Once a repository has been configured with transcrypt, you can trans- parently encrypt files by applying the "crypt" filter and diff to a pattern in the top-level .gitattributes config. If that pattern matches a file in your repository, the file will be transparently encrypted once you stage and commit it: $ echo 'sensitive_file filter=crypt diff=crypt' >> .gitattributes $ git add .gitattributes sensitive_file $ git commit -m 'Add encrypted version of a sensitive file' See the gitattributes(5) man page for more information. If you have just cloned a repository containing files that are encrypted, you'll want to configure transcrypt with the same cipher and password as the origin repository. Once transcrypt has stored the matching credentials, it will force a checkout of any existing encrypted files in order to decrypt them. If the origin repository has just rekeyed, all clones should flush their transcrypt credentials, fetch and merge the new encrypted files via Git, and then re-configure transcrypt with the new credentials. AUTHOR Aaron Bull Schaefer SEE ALSO enc(1), gitattributes(5) EOF } ##### MAIN # reset all variables that might be set cipher='' password='' interactive='true' display_config='' rekey='' flush_creds='' uninstall='' show_file='' gpg_recipient='' gpg_import_file='' # used to bypass certain safety checks requires_existing_config='' requires_clean_repo='true' # parse command line options while [[ "${1:-}" != '' ]]; do case $1 in -c | --cipher) cipher=$2 shift ;; --cipher=*) cipher=${1#*=} ;; -p | --password) password=$2 shift ;; --password=*) password=${1#*=} ;; -y | --yes) interactive='' ;; -d | --display) display_config='true' requires_existing_config='true' requires_clean_repo='' ;; -r | --rekey) rekey='true' requires_existing_config='true' ;; -f | --flush-credentials) flush_creds='true' requires_existing_config='true' ;; -F | --force) requires_clean_repo='' ;; -u | --uninstall) uninstall='true' requires_existing_config='true' requires_clean_repo='' ;; -l | --list) list_files exit 0 ;; -s | --show-raw) show_file=$2 show_raw_file exit 0 ;; --show-raw=*) show_file=${1#*=} show_raw_file exit 0 ;; -e | --export-gpg) gpg_recipient=$2 requires_existing_config='true' requires_clean_repo='' shift ;; --export-gpg=*) gpg_recipient=${1#*=} requires_existing_config='true' requires_clean_repo='' ;; -i | --import-gpg) gpg_import_file=$2 shift ;; --import-gpg=*) gpg_import_file=${1#*=} ;; -v | --version) printf 'transcrypt %s\n' "$VERSION" exit 0 ;; -h | --help | -\?) help exit 0 ;; --*) warn 'unknown option -- %s' "${1#--}" usage exit 1 ;; *) warn 'unknown option -- %s' "${1#-}" usage exit 1 ;; esac shift done # always run our safety checks run_safety_checks # in order to keep behavior consistent no matter what order the options were # specified in, we must run these here rather than in the case statement above if [[ $uninstall ]]; then uninstall_transcrypt exit 0 elif [[ $display_config ]] && [[ $flush_creds ]]; then display_configuration printf '\n' flush_credentials exit 0 elif [[ $display_config ]]; then display_configuration exit 0 elif [[ $flush_creds ]]; then flush_credentials exit 0 elif [[ $gpg_recipient ]]; then export_gpg exit 0 elif [[ $gpg_import_file ]]; then import_gpg elif [[ $cipher ]]; then validate_cipher fi # perform function calls to configure transcrypt get_cipher get_password if [[ $rekey ]] && [[ $interactive ]]; then confirm_rekey elif [[ $interactive ]]; then confirm_configuration fi save_configuration if [[ $rekey ]]; then stage_rekeyed_files else force_checkout fi # ensure the git attributes file exists if [[ ! -f $GIT_ATTRIBUTES ]]; then mkdir -p "${GIT_ATTRIBUTES%/*}" printf '#pattern filter=crypt diff=crypt\n' >"$GIT_ATTRIBUTES" fi printf 'The repository has been successfully configured by transcrypt.\n' exit 0