From c9c4f168601b1f87b2a01ec337f69aedddfbb956 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 10 Oct 2022 14:50:06 -0400 Subject: [PATCH 1/7] Add `crypt` subcommand to migrate from outside the subdirectory --- gocrypt.bash | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gocrypt.bash b/gocrypt.bash index 1cc40ef..cdfa00c 100755 --- a/gocrypt.bash +++ b/gocrypt.bash @@ -79,6 +79,14 @@ gocrypt_delegate() { pass git commit -m "Encrypted pass operation inside gocrypt" "$gocrypt_dir" || echo "No git commit created" } +gocrypt_crypt() { + gocrypt_open_check + [ ! -f "$1.gpg" ] && gocrypt_die "Not found: $1" + cmd_show "$1" | EDITOR=tee gocrypt_delegate edit "$1" + cmd_delete -f "$1" + echo "Moved $1 into encrypted storage" +} + gocrypt_help() { printf "%s" "\ $PROGRAM gocrypt - hide part of the password store in a subdirectory encrypted with gocryptfs @@ -100,6 +108,11 @@ usage $PROGRAM gocrypt close Unmount the encrypted subtree, if it was opened before. + $PROGRAM gocrypt crypt + Move a password from the original password store to the encrypted subdirectory. Note + that if you use git, this will still leave a record in the git repository of the original password + store. + $PROGRAM gocrypt help Print this help message. @@ -145,6 +158,10 @@ case "$1" in shift gocrypt_open $@ ;; + crypt) + shift + gocrypt_crypt $@ + ;; close) shift gocrypt_close $@ From 1457458ab8b7a6db77e0f8f82870561f4f57a590 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 10 Oct 2022 14:53:26 -0400 Subject: [PATCH 2/7] Quote $@ in bash scripts --- gocrypt.bash | 12 ++++++------ pass-test | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gocrypt.bash b/gocrypt.bash index cdfa00c..eeff7bc 100755 --- a/gocrypt.bash +++ b/gocrypt.bash @@ -73,7 +73,7 @@ gocrypt_close() { gocrypt_delegate() { gocrypt_open_check # Delegate command to another `pass` instance that manages what is inside of the mountpoint - PASSWORD_STORE_DIR="$PWD/$gocrypt_dec_dir" pass $@ + PASSWORD_STORE_DIR="$PWD/$gocrypt_dec_dir" pass "$@" # Commit if there has been changes due to this operation pass git add "$gocrypt_dir" pass git commit -m "Encrypted pass operation inside gocrypt" "$gocrypt_dir" || echo "No git commit created" @@ -152,23 +152,23 @@ case "$1" in ;; init) shift - gocrypt_init $@ + gocrypt_init "$@" ;; open) shift - gocrypt_open $@ + gocrypt_open "$@" ;; crypt) shift - gocrypt_crypt $@ + gocrypt_crypt "$@" ;; close) shift - gocrypt_close $@ + gocrypt_close "$@" ;; ls|list|grep|find|search|show|insert|add|edit|generate|rm|remove|delete|mv|rename|cp|copy|git) # No shift here since we need to delegate these commands to another pass instance - gocrypt_delegate $@ + gocrypt_delegate "$@" ;; *) gocrypt_die "Unknown command $1 for gocrypt" diff --git a/pass-test b/pass-test index 22def5a..31d0a3c 100755 --- a/pass-test +++ b/pass-test @@ -1,3 +1,3 @@ #!/usr/bin/env sh -PASSWORD_STORE_DIR=$PWD/pass PASSWORD_STORE_EXTENSIONS_DIR=$PWD PASSWORD_STORE_ENABLE_EXTENSIONS=true pass $@ +PASSWORD_STORE_DIR=$PWD/pass PASSWORD_STORE_EXTENSIONS_DIR=$PWD PASSWORD_STORE_ENABLE_EXTENSIONS=true pass "$@" From 780e62a71369a3caa39e02d024f450a35476bc2d Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 10 Oct 2022 14:55:42 -0400 Subject: [PATCH 3/7] gocrypt: Use lazy unmount when absolutely necessary --- gocrypt.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gocrypt.bash b/gocrypt.bash index eeff7bc..03a69b4 100755 --- a/gocrypt.bash +++ b/gocrypt.bash @@ -67,7 +67,7 @@ gocrypt_open() { gocrypt_close() { gocrypt_open_check - fusermount -u "$gocrypt_dec_dir" + fusermount -u "$gocrypt_dec_dir" || fusermount -u -z "$gocrypt_dec_dir" } gocrypt_delegate() { From 9ae556286960c178e3ab3f2dbc1e8911be59fc56 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 10 Oct 2022 15:06:37 -0400 Subject: [PATCH 4/7] Switch to using internal functions fron pass instead of using pass directly --- gocrypt.bash | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gocrypt.bash b/gocrypt.bash index 03a69b4..d6a9f9a 100755 --- a/gocrypt.bash +++ b/gocrypt.bash @@ -36,11 +36,11 @@ gocrypt_init() { gocrypt_die "gocrypt plugin already initialized for your password store" fi - pass generate "$gocrypt_passwd_file" 32 + cmd_generate "$gocrypt_passwd_file" 32 # Initialize gocryptfs mkdir "$gocrypt_dir" - gocryptfs -passfile /dev/stdin -init "$gocrypt_dir" <<< "$(pass show "$gocrypt_passwd_file")" + gocryptfs -passfile /dev/stdin -init "$gocrypt_dir" <<< "$(cmd_show "$gocrypt_passwd_file")" # Mount the gocryptfs subdirectory and initialze what is inside of it gocrypt_open @@ -53,16 +53,16 @@ gocrypt_init() { echo "# Gocrypt" >> .gitignore echo "gocrypt" >> .gitignore - pass git add .gitignore - pass git add "$gocrypt_dir" - pass git commit -m "Initialized encrypted storage for gocrypt plugin" + cmd_git add .gitignore + cmd_git add "$gocrypt_dir" + cmd_git commit -m "Initialized encrypted storage for gocrypt plugin" } gocrypt_open() { gocrypt_close_check mkdir -p "$gocrypt_dec_dir" - gocryptfs -passfile /dev/stdin "$gocrypt_dir" "$gocrypt_dec_dir" <<< "$(pass show "$gocrypt_passwd_file")" + gocryptfs -passfile /dev/stdin "$gocrypt_dir" "$gocrypt_dec_dir" <<< "$(cmd_show "$gocrypt_passwd_file")" } gocrypt_close() { @@ -75,8 +75,8 @@ gocrypt_delegate() { # Delegate command to another `pass` instance that manages what is inside of the mountpoint PASSWORD_STORE_DIR="$PWD/$gocrypt_dec_dir" pass "$@" # Commit if there has been changes due to this operation - pass git add "$gocrypt_dir" - pass git commit -m "Encrypted pass operation inside gocrypt" "$gocrypt_dir" || echo "No git commit created" + cmd_git add "$gocrypt_dir" + cmd_git commit -m "Encrypted pass operation inside gocrypt" "$gocrypt_dir" || echo "No git commit created" } gocrypt_crypt() { From cf670956d84c5566e851150fb7a59214c386abb6 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 10 Oct 2022 15:08:28 -0400 Subject: [PATCH 5/7] Use $PROGRAM when delegating pass commands --- gocrypt.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gocrypt.bash b/gocrypt.bash index d6a9f9a..858b398 100755 --- a/gocrypt.bash +++ b/gocrypt.bash @@ -73,7 +73,7 @@ gocrypt_close() { gocrypt_delegate() { gocrypt_open_check # Delegate command to another `pass` instance that manages what is inside of the mountpoint - PASSWORD_STORE_DIR="$PWD/$gocrypt_dec_dir" pass "$@" + PASSWORD_STORE_DIR="$PWD/$gocrypt_dec_dir" "$PROGRAM" "$@" # Commit if there has been changes due to this operation cmd_git add "$gocrypt_dir" cmd_git commit -m "Encrypted pass operation inside gocrypt" "$gocrypt_dir" || echo "No git commit created" From fe28c7e3dfb876f86e278216b5d5367b47ab1325 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 10 Oct 2022 15:36:37 -0400 Subject: [PATCH 6/7] Check extraneous arguments on each subcommand --- gocrypt.bash | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gocrypt.bash b/gocrypt.bash index 858b398..4a98efc 100755 --- a/gocrypt.bash +++ b/gocrypt.bash @@ -31,6 +31,7 @@ gocrypt_die() { } gocrypt_init() { + [ $# -eq 0 ] || gocrypt_die "Unexpected argument" gocrypt_sys_check if [ -d "$gocrypt_dir" ] || [ -f "$gocrypt_dir" ]; then gocrypt_die "gocrypt plugin already initialized for your password store" @@ -59,6 +60,7 @@ gocrypt_init() { } gocrypt_open() { + [ $# -eq 0 ] || gocrypt_die "Unexpected argument" gocrypt_close_check mkdir -p "$gocrypt_dec_dir" @@ -66,6 +68,7 @@ gocrypt_open() { } gocrypt_close() { + [ $# -eq 0 ] || gocrypt_die "Unexpected argument" gocrypt_open_check fusermount -u "$gocrypt_dec_dir" || fusermount -u -z "$gocrypt_dec_dir" } @@ -80,6 +83,7 @@ gocrypt_delegate() { } gocrypt_crypt() { + [ $# -eq 1 ] || gocrypt_die "Unexpected argument" gocrypt_open_check [ ! -f "$1.gpg" ] && gocrypt_die "Not found: $1" cmd_show "$1" | EDITOR=tee gocrypt_delegate edit "$1" From f9902f8989f105c3838e9918b34bb3a44c174052 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 10 Oct 2022 16:08:44 -0400 Subject: [PATCH 7/7] Support an extra symmetric passphrase for protecting the crypt subtree This can help protect against gpg vulnerabilities (e.g. quantum computing) and/or leakage. --- gocrypt.bash | 71 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/gocrypt.bash b/gocrypt.bash index 4a98efc..37cf4ae 100755 --- a/gocrypt.bash +++ b/gocrypt.bash @@ -5,9 +5,11 @@ readonly gocrypt_dir=".gocrypt" readonly gocrypt_dec_dir="gocrypt" readonly gocrypt_passwd_file="gocrypt-passwd" +readonly gocrypt_needs_passphrase_marker=".gocrypt-needs-passphrase" gocrypt_sys_check() { which gocryptfs > /dev/null || gocrypt_die "gocryptfs not found in PATH" + which perl > /dev/null || gocrypt_die "perl not found in PATH" } gocrypt_env_check() { @@ -30,21 +32,59 @@ gocrypt_die() { exit 1 } +gocrypt_derive_password() { + local data="$1" + local key="$2" + + perl <<< "use Digest::SHA qw(hmac_sha256_hex);\$digest=hmac_sha256_hex(\"$data\n\", \"$key\");print(\$digest);" +} + gocrypt_init() { + local needs_passphrase=false + local passphrase="" + while [ $# -gt 0 ]; do + case "$1" in + -p|--passphrase) + needs_passphrase=true + ;; + *) + gocrypt_die "Unexpected argument: $1" + ;; + esac + shift + done + [ $# -eq 0 ] || gocrypt_die "Unexpected argument" gocrypt_sys_check if [ -d "$gocrypt_dir" ] || [ -f "$gocrypt_dir" ]; then gocrypt_die "gocrypt plugin already initialized for your password store" fi + if $needs_passphrase; then + echo -n "Enter passphrase: " + read -s passphrase + local passphrase_confirm="" + echo + echo -n "Confirm passphrase: " + read -s passphrase_confirm + echo + + [ "$passphrase" == "$passphrase_confirm" ] || gocrypt_die "Passphrase mismatch" + fi + cmd_generate "$gocrypt_passwd_file" 32 + local gocrypt_passwd="$(cmd_show "$gocrypt_passwd_file")" # Initialize gocryptfs mkdir "$gocrypt_dir" - gocryptfs -passfile /dev/stdin -init "$gocrypt_dir" <<< "$(cmd_show "$gocrypt_passwd_file")" + if $needs_passphrase; then + touch "$gocrypt_needs_passphrase_marker" + gocrypt_passwd="$(gocrypt_derive_password "$gocrypt_passwd" "$passphrase")" + fi + gocryptfs -passfile /dev/stdin -init "$gocrypt_dir" <<< "$gocrypt_passwd" || gocrypt_die "Unable to initialize gocryptfs" # Mount the gocryptfs subdirectory and initialze what is inside of it - gocrypt_open + _gocrypt_passwd="$gocrypt_passwd" gocrypt_open || gocrypt_die "Cannot open the gocryptfs we just initialized" touch "$gocrypt_dec_dir"/.pass-gocrypt # By default, we use the same gpg-id inside, but the user can decide to use a different one later by doing it manually ln -s ../.gpg-id "$gocrypt_dec_dir"/.gpg-id @@ -55,6 +95,7 @@ gocrypt_init() { echo "gocrypt" >> .gitignore cmd_git add .gitignore + $needs_passphrase && cmd_git add "$gocrypt_needs_passphrase_marker" cmd_git add "$gocrypt_dir" cmd_git commit -m "Initialized encrypted storage for gocrypt plugin" } @@ -64,7 +105,22 @@ gocrypt_open() { gocrypt_close_check mkdir -p "$gocrypt_dec_dir" - gocryptfs -passfile /dev/stdin "$gocrypt_dir" "$gocrypt_dec_dir" <<< "$(cmd_show "$gocrypt_passwd_file")" + local gocrypt_passwd="" + + if [ ! -z "$_gocrypt_passwd" ]; then + gocrypt_passwd="$_gocrypt_passwd" + else + gocrypt_passwd="$(cmd_show "$gocrypt_passwd_file")" + + if [ -f "$gocrypt_needs_passphrase_marker" ]; then + local passphrase="" + echo -n "Enter passphrase: " + read -s passphrase + gocrypt_passwd="$(gocrypt_derive_password "$gocrypt_passwd" "$passphrase")" + fi + fi + + gocryptfs -passfile /dev/stdin "$gocrypt_dir" "$gocrypt_dec_dir" <<< "$gocrypt_passwd" } gocrypt_close() { @@ -96,7 +152,7 @@ gocrypt_help() { $PROGRAM gocrypt - hide part of the password store in a subdirectory encrypted with gocryptfs usage - $PROGRAM gocrypt init + $PROGRAM gocrypt init [-p|--passphrase] Initialize a encrypted subdirectory at \$PASSWORD_STORE_DIR/$gocrypt_dir. The password used by gocryptfs will be generated by pass and stored at \$PASSWORD_STORE_DIR/$gocrypt_passwd_file.gpg. The encrypted subdirectory, along with the generated (encrypted) password, will be committed to @@ -106,6 +162,13 @@ usage subtree. You can change this manually by mounting (opening) the directory and replacing this symlink with a custom one. + You can optionally use an extra piece of symmetric passphrase to encrypt the subdirectory, by + passing the argument -p or --passphrase when invoking this command to initialize. In this case, + the passphrase you input will be used along with the generated password to derive a new master + password for gocryptfs. This second piece of passphrase will not be stored in the password store, + and you will be asked for it every time you invoke \`$PROGRAM gocrypt open\`. This mode adds an + extra layer of protection in case the gpg-encrypted master password is somehow compromised. + $PROGRAM gocrypt open Mount the encrypted subdirectory to \$PASSWORD_STORE_DIR/$gocrypt_dec_dir.