@@ -268,92 +268,68 @@ stage_rekeyed_files() {
268268 fi
269269}
270270
271- # save helper scripts under the repository's git directory
272- save_helper_scripts () {
273- mkdir -p " ${GIT_DIR} /crypt"
274-
275- # The `decryption -> encryption` process on an unchanged file must be
276- # deterministic for everything to work transparently. To do that, the same
277- # salt must be used each time we encrypt the same file. An HMAC has been
278- # proven to be a PRF, so we generate an HMAC-SHA256 for each decrypted file
279- # (keyed with a combination of the filename and transcrypt password), and
280- # then use the last 16 bytes of that HMAC for the file's unique salt.
281-
282- cat << -'EOF ' >"${GIT_DIR}/crypt/clean"
283- #!/usr/bin/env bash
284- filename=$1
285- # ignore empty files
286- if [[ -s $filename ]]; then
287- # cache STDIN to test if it's already encrypted
288- tempfile=$(mktemp 2>/dev/null || mktemp -t tmp)
289- trap 'rm -f "$tempfile"' EXIT
290- tee "$tempfile" &>/dev/null
291- # the first bytes of an encrypted file are always "Salted" in Base64
292- read -n 8 firstbytes <"$tempfile"
293- if [[ $firstbytes == "U2FsdGVk" ]]; then
294- cat "$tempfile"
295- else
296- cipher=$(git config --get --local transcrypt.cipher)
297- password=$(git config --get --local transcrypt.password)
298- salt=$(openssl dgst -hmac "${filename}:${password}" -sha256 "$filename" | tr -d '\r\n' | tail -c 16)
299- ENC_PASS=$password openssl enc -$cipher -md MD5 -pass env:ENC_PASS -e -a -S "$salt" -in "$tempfile"
300- fi
301- fi
302- EOF
303-
304- cat << -'EOF ' >"${GIT_DIR}/crypt/smudge"
305- #!/usr/bin/env bash
271+ # The `decryption -> encryption` process on an unchanged file must be
272+ # deterministic for everything to work transparently. To do that, the same
273+ # salt must be used each time we encrypt the same file. An HMAC has been
274+ # proven to be a PRF, so we generate an HMAC-SHA256 for each decrypted file
275+ # (keyed with a combination of the filename and transcrypt password), and
276+ # then use the last 16 bytes of that HMAC for the file's unique salt.
277+ crypt_clean () {
278+ filename=$1
279+ # ignore empty files
280+ if [[ -s $filename ]]; then
281+ # cache STDIN to test if it's already encrypted
306282 tempfile=$( mktemp 2> /dev/null || mktemp -t tmp)
307283 trap ' rm -f "$tempfile"' EXIT
308- cipher=$(git config --get --local transcrypt.cipher)
309- password=$(git config --get --local transcrypt.password)
310- tee "$tempfile" | ENC_PASS=$password openssl enc -$cipher -md MD5 -pass env:ENC_PASS -d -a 2>/dev/null || cat "$tempfile"
311- EOF
312-
313- cat << -'EOF ' >"${GIT_DIR}/crypt/textconv"
314- #!/usr/bin/env bash
315- filename=$1
316- # ignore empty files
317- if [[ -s $filename ]]; then
318- cipher=$(git config --get --local transcrypt.cipher)
319- password=$(git config --get --local transcrypt.password)
320- ENC_PASS=$password openssl enc -$cipher -md MD5 -pass env:ENC_PASS -d -a -in "$filename" 2>/dev/null || cat "$filename"
284+ tee " $tempfile " & > /dev/null
285+ # the first bytes of an encrypted file are always "Salted" in Base64
286+ read -r -n 8 firstbytes < " $tempfile "
287+ if [[ $firstbytes == " U2FsdGVk" ]]; then
288+ cat " $tempfile "
289+ else
290+ cipher=$( git config --get --local transcrypt.cipher)
291+ password=$( git config --get --local transcrypt.password)
292+ salt=$( openssl dgst -hmac " ${filename} :${password} " -sha256 " $filename " | tr -d ' \r\n' | tail -c 16)
293+ ENC_PASS=$password openssl enc " -${cipher} " -md MD5 -pass env:ENC_PASS -e -a -S " $salt " -in " $tempfile "
321294 fi
322- EOF
295+ fi
296+ }
323297
324- # make scripts executable
325- for script in {clean,smudge,textconv}; do
326- chmod 0755 " ${GIT_DIR} /crypt/${script} "
327- done
298+ crypt_smudge () {
299+ tempfile=$( mktemp 2> /dev/null || mktemp -t tmp)
300+ trap ' rm -f "$tempfile"' EXIT
301+ cipher=$( git config --get --local transcrypt.cipher)
302+ password=$( git config --get --local transcrypt.password)
303+ tee " $tempfile " | ENC_PASS=$password openssl enc " -${cipher} " -md MD5 -pass env:ENC_PASS -d -a 2> /dev/null || cat " $tempfile "
304+ }
305+
306+ crypt_textconv () {
307+ filename=$1
308+ # ignore empty files
309+ if [[ -s $filename ]]; then
310+ cipher=$( git config --get --local transcrypt.cipher)
311+ password=$( git config --get --local transcrypt.password)
312+ ENC_PASS=$password openssl enc " -${cipher} " -md MD5 -pass env:ENC_PASS -d -a -in " $filename " 2> /dev/null || cat " $filename "
313+ fi
328314}
329315
330316# write the configuration to the repository's git config
331317save_configuration () {
332- save_helper_scripts
318+ # This directory is used by transcrypt as a working directory.
319+ mkdir -p " ${GIT_DIR} /crypt"
333320
334321 # write the encryption info
335322 git config transcrypt.version " $VERSION "
336323 git config transcrypt.cipher " $cipher "
337324 git config transcrypt.password " $password "
338325
339326 # write the filter settings
340- if [[ -d $( git rev-parse --git-common-dir) ]]; then
341- # this allows us to support multiple working trees via git-worktree
342- # ...but the --git-common-dir flag was only added in November 2014
343- # shellcheck disable=SC2016
344- git config filter.crypt.clean ' "$(git rev-parse --git-common-dir)"/crypt/clean %f'
345- # shellcheck disable=SC2016
346- git config filter.crypt.smudge ' "$(git rev-parse --git-common-dir)"/crypt/smudge'
347- # shellcheck disable=SC2016
348- git config diff.crypt.textconv ' "$(git rev-parse --git-common-dir)"/crypt/textconv'
349- else
350- # shellcheck disable=SC2016
351- git config filter.crypt.clean ' "$(git rev-parse --git-dir)"/crypt/clean %f'
352- # shellcheck disable=SC2016
353- git config filter.crypt.smudge ' "$(git rev-parse --git-dir)"/crypt/smudge'
354- # shellcheck disable=SC2016
355- git config diff.crypt.textconv ' "$(git rev-parse --git-dir)"/crypt/textconv'
356- fi
327+ # shellcheck disable=SC2016
328+ git config filter.crypt.clean " $0 --crypt-clean %f"
329+ # shellcheck disable=SC2016
330+ git config filter.crypt.smudge " $0 --crypt-smudge"
331+ # shellcheck disable=SC2016
332+ git config diff.crypt.textconv " $0 --crypt-textconv"
357333 git config filter.crypt.required ' true'
358334 git config diff.crypt.cachetextconv ' true'
359335 git config diff.crypt.binary ' true'
@@ -466,6 +442,8 @@ uninstall_transcrypt() {
466442 clean_gitconfig
467443
468444 # remove helper scripts
445+ # This is obsolete, but we should keep it to clean up these
446+ # scripts from old versions of transcrypt.
469447 for script in {clean,smudge,textconv}; do
470448 [[ ! -f " ${GIT_DIR} /crypt/${script} " ]] || rm " ${GIT_DIR} /crypt/${script} "
471449 done
@@ -800,6 +778,21 @@ while [[ "${1:-}" != '' ]]; do
800778 --import-gpg=* )
801779 gpg_import_file=${1#* =}
802780 ;;
781+ --crypt-clean)
782+ shift
783+ crypt_clean " $1 "
784+ exit 0
785+ ;;
786+ --crypt-smudge)
787+ shift
788+ crypt_smudge
789+ exit 0
790+ ;;
791+ --crypt-textconv)
792+ shift
793+ crypt_textconv " $1 "
794+ exit 0
795+ ;;
803796 -v | --version)
804797 printf ' transcrypt %s\n' " $VERSION "
805798 exit 0
0 commit comments