Skip to content

Add support for pbkdf2 key derivation with iterations, and make this the default #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,12 @@ by running the `--display` command line option:
The current repository was configured using transcrypt v0.2.0
and has the following configuration:

CIPHER: aes-256-cbc
CIPHER: aes-256-cbc:pbkdf2:1024
PASSWORD: correct horse battery staple

Copy and paste the following command to initialize a cloned repository:

transcrypt -c aes-256-cbc -p 'correct horse battery staple'
transcrypt -c aes-256-cbc:pbkdf2:1024 -p 'correct horse battery staple'

Once transcrypt has stored the matching credentials, it will force a checkout of
any exising encrypted files in order to decrypt them.
Expand All @@ -184,7 +184,7 @@ re-configure transcrypt with the new credentials.
$ transcrypt --flush-credentials
$ git fetch origin
$ git merge origin/main
$ transcrypt -c aes-256-cbc -p 'the-new-password'
$ transcrypt -c aes-256-cbcaes-256-cbc:pbkdf2:1024 -p 'the-new-password'

### Command Line Options

Expand All @@ -194,8 +194,10 @@ directory.
transcrypt [option...]

-c, --cipher=CIPHER
the symmetric cipher to utilize for encryption;
defaults to aes-256-cbc
the symmetric cipher to utilize for encryption, can
include cipher, key derivation, and iteration components
separated by ':';
defaults to aes-256-cbc:pbkdf2:1024

-p, --password=PASSWORD
the password to derive the key from;
Expand Down
80 changes: 40 additions & 40 deletions sensitive_file
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
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
L/aeTVBw2BnDs2K48meuFw==
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
Ax3RhJMLF5+uXPoslVOEIQ==
49 changes: 37 additions & 12 deletions transcrypt
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ set -euo pipefail
##### CONSTANTS

# the release version of this script
readonly VERSION='2.2.0-pre'
readonly VERSION='3.0-pre'

# the default cipher to utilize
readonly DEFAULT_CIPHER='aes-256-cbc'
readonly DEFAULT_CIPHER='aes-256-cbc:pbkdf2:1024'

##### FUNCTIONS

Expand Down Expand Up @@ -52,6 +52,7 @@ realpath() {
}

# establish repository metadata and directory handling
# shellcheck disable=SC2155
gather_repo_metadata() {
# whether or not transcrypt is already configured
readonly CONFIGURED=$(git config --get --local transcrypt.version 2>/dev/null)
Expand Down Expand Up @@ -130,21 +131,35 @@ git_clean() {
if [[ $firstbytes == "U2FsdGVk" ]]; then
cat "$tempfile"
else
cipher=$(git config --get --local transcrypt.cipher)
cipher_key_deriv_iter=$(git config --get --local transcrypt.cipher)
cipher=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $1 }')
key_deriv=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $2 }')
iter=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $3 }')
password=$(git config --get --local transcrypt.password)
openssl_path=$(git config --get --local transcrypt.openssl-path)
salt=$("${openssl_path}" dgst -hmac "${filename}:${password}" -sha256 "$tempfile" | tr -d '\r\n' | tail -c16)
ENC_PASS=$password "$openssl_path" enc "-${cipher}" -md MD5 -pass env:ENC_PASS -e -a -S "$salt" -in "$tempfile"
if [[ -z "$key_deriv" ]]; then
ENC_PASS=$password "$openssl_path" enc "-${cipher}" -md MD5 -pass env:ENC_PASS -e -a -S "$salt" -in "$tempfile"
else
ENC_PASS=$password "$openssl_path" enc "-${cipher}" "-${key_deriv}" -iter "${iter}" -md SHA512 -pass env:ENC_PASS -e -a -S "$salt" -in "$tempfile"
fi
fi
}

git_smudge() {
tempfile=$(mktemp 2>/dev/null || mktemp -t tmp)
trap 'rm -f "$tempfile"' EXIT
cipher=$(git config --get --local transcrypt.cipher)
cipher_key_deriv_iter=$(git config --get --local transcrypt.cipher)
cipher=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $1 }')
key_deriv=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $2 }')
iter=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $3 }')
password=$(git config --get --local transcrypt.password)
openssl_path=$(git config --get --local transcrypt.openssl-path)
tee "$tempfile" | ENC_PASS=$password "$openssl_path" enc "-${cipher}" -md MD5 -pass env:ENC_PASS -d -a 2>/dev/null || cat "$tempfile"
if [[ -z "$key_deriv" ]]; then
tee "$tempfile" | ENC_PASS=$password "$openssl_path" enc "-${cipher}" -md MD5 -pass env:ENC_PASS -d -a 2>/dev/null || cat "$tempfile"
else
tee "$tempfile" | ENC_PASS=$password "$openssl_path" enc "-${cipher}" "-${key_deriv}" -iter "${iter}" -md SHA512 -pass env:ENC_PASS -d -a 2>/dev/null || cat "$tempfile"
fi
}

git_textconv() {
Expand All @@ -153,10 +168,17 @@ git_textconv() {
if [[ ! -s $filename ]]; then
return
fi
cipher=$(git config --get --local transcrypt.cipher)
cipher_key_deriv_iter=$(git config --get --local transcrypt.cipher)
cipher=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $1 }')
key_deriv=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $2 }')
iter=$(echo "$cipher_key_deriv_iter" | awk 'BEGIN { FS = ":" }; { print $3 }')
password=$(git config --get --local transcrypt.password)
openssl_path=$(git config --get --local transcrypt.openssl-path)
ENC_PASS=$password "$openssl_path" enc "-${cipher}" -md MD5 -pass env:ENC_PASS -d -a -in "$filename" 2>/dev/null || cat "$filename"
if [[ -z "$key_deriv" ]]; then
ENC_PASS=$password "$openssl_path" enc "-${cipher}" -md MD5 -pass env:ENC_PASS -d -a -in "$filename" 2>/dev/null || cat "$filename"
else
ENC_PASS=$password "$openssl_path" enc "-${cipher}" "-${key_deriv}" -iter "${iter}" -md SHA512 -pass env:ENC_PASS -d -a -in "$filename" 2>/dev/null || cat "$filename"
fi
}

# shellcheck disable=SC2005,SC2002,SC2181
Expand Down Expand Up @@ -239,17 +261,20 @@ validate_cipher() {
list_cipher_commands="${openssl_path} list -cipher-commands"
fi

local cipher_only
cipher_only=$(echo "$cipher" | cut -d: -f1)

local supported
supported=$($list_cipher_commands | tr -s ' ' '\n' | grep -Fx "$cipher") || true
supported=$($list_cipher_commands | tr -s ' ' '\n' | grep -Fx "$cipher_only") || true
if [[ ! $supported ]]; then
if [[ $interactive ]]; then
printf '"%s" is not a valid cipher; choose one of the following:\n\n' "$cipher"
printf '"%s" is not a valid cipher; choose one of the following:\n\n' "$cipher_only"
$list_cipher_commands | column -c 80
printf '\n'
cipher=''
else
# shellcheck disable=SC2016
die 1 '"%s" is not a valid cipher; see `%s`' "$cipher" "$list_cipher_commands"
die 1 '"%s" is not a valid cipher; see `%s`' "$cipher_only" "$list_cipher_commands"
fi
fi
}
Expand Down Expand Up @@ -999,7 +1024,7 @@ rekey=''
show_file=''
uninstall=''
upgrade=''
openssl_path='openssl'
openssl_path=$(git config --get --local transcrypt.openssl-path || echo 'openssl')

# used to bypass certain safety checks
requires_existing_config=''
Expand Down