From 9ee0a1550e98d25cfc09f268230b269a0c117ed1 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Mon, 30 Jan 2017 20:57:19 +0000 Subject: [PATCH 01/25] Add -r option to enable GPG key integration. The flag -r (for recipient like in GPG itself) takes a mandatory argument, the GPG key ID. --- tomb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) mode change 100755 => 100644 tomb diff --git a/tomb b/tomb old mode 100755 new mode 100644 index 1a1a632..e168b65 --- a/tomb +++ b/tomb @@ -2563,15 +2563,15 @@ main() { main_opts=(q -quiet=q D -debug=D h -help=h v -version=v f -force=f -tmp: U: G: T: -no-color -unsafe) subcommands_opts[__default]="" # -o in open and mount is used to pass alternate mount options - subcommands_opts[open]="n -nohook=n k: -kdf: o: -ignore-swap -tomb-pwd: " + subcommands_opts[open]="n -nohook=n k: -kdf: o: -ignore-swap -tomb-pwd: r: " subcommands_opts[mount]=${subcommands_opts[open]} subcommands_opts[create]="" # deprecated, will issue warning # -o in forge and lock is used to pass an alternate cipher. - subcommands_opts[forge]="-ignore-swap k: -kdf: o: -tomb-pwd: -use-urandom " + subcommands_opts[forge]="-ignore-swap k: -kdf: o: -tomb-pwd: -use-urandom r: " subcommands_opts[dig]="-ignore-swap s: -size=s " - subcommands_opts[lock]="-ignore-swap k: -kdf: o: -tomb-pwd: " + subcommands_opts[lock]="-ignore-swap k: -kdf: o: -tomb-pwd: r: " subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: " subcommands_opts[engrave]="k: " From 6c0d89cab1ad970f3ff0848990332a4ae5044430 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Mon, 30 Jan 2017 21:01:27 +0000 Subject: [PATCH 02/25] Use -r option to shortcut interactive tomb password popup --- tomb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) mode change 100644 => 100755 tomb diff --git a/tomb b/tomb old mode 100644 new mode 100755 index e168b65..4ade4f1 --- a/tomb +++ b/tomb @@ -889,7 +889,12 @@ _load_key() { [[ -z $keyfile ]] && { _failure "This operation requires a key file to be specified using the -k option." } - if [[ $keyfile == "-" ]]; then + if option_is_set -r; then + _verbose "load_key delegating password handling to GnuPG" + _message "Waiting for GnuPG to handle password authentication... " + TOMBKEYFILE=$keyfile + TOMBKEY="${mapfile[$TOMBKEYFILE]}" + elif [[ $keyfile == "-" ]]; then _verbose "load_key reading from stdin." _message "Waiting for the key to be piped from stdin... " TOMBKEYFILE=stdin @@ -1024,6 +1029,12 @@ ask_key_password() { _verbose "no password needed, using secret bytes from stdin" return 0 } + if option_is_set -r; then + _verbose "no password needed, using GPG key" + get_lukskey + return $? + fi + _message "A password is required to use key ::1 key::" $TOMBKEYFILE passok=0 tombpass="" From e78af47c56715c92a732d140bcee5d1f7ee34df1 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Mon, 30 Jan 2017 21:10:20 +0000 Subject: [PATCH 03/25] Add a GPG database in 'extras/test/gnupg' for test suite purpose The GPG Key are unencrypted. Do not use them for an other purpose than a test suite. --- extras/test/.gitignore | 1 + extras/test/gnupg/pubring.gpg | Bin 0 -> 2429 bytes extras/test/gnupg/secring.gpg | Bin 0 -> 5033 bytes extras/test/gnupg/trustdb.gpg | Bin 0 -> 1360 bytes 4 files changed, 1 insertion(+) create mode 100644 extras/test/.gitignore create mode 100644 extras/test/gnupg/pubring.gpg create mode 100644 extras/test/gnupg/secring.gpg create mode 100644 extras/test/gnupg/trustdb.gpg diff --git a/extras/test/.gitignore b/extras/test/.gitignore new file mode 100644 index 0000000..c6bc574 --- /dev/null +++ b/extras/test/.gitignore @@ -0,0 +1 @@ +gnupg/ diff --git a/extras/test/gnupg/pubring.gpg b/extras/test/gnupg/pubring.gpg new file mode 100644 index 0000000000000000000000000000000000000000..9217774a9d3a5a9e895a87bd4abe4cf7024957c9 GIT binary patch literal 2429 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 literal 0 HcmV?d00001 diff --git a/extras/test/gnupg/trustdb.gpg b/extras/test/gnupg/trustdb.gpg new file mode 100644 index 0000000000000000000000000000000000000000..3b760ba3b9be4fd326705e27b7344d39faf49826 GIT binary patch literal 1360 zcmZQfFGy!*W@Ke#Vql1<-X+g~9WZiX7sn7qQKx_*4HrOB2N%U;jH<&76OhPgxKJ@% zi?O(qR$eS UUcJS1 Date: Mon, 30 Jan 2017 21:19:43 +0000 Subject: [PATCH 04/25] Fix test suite error in the return code: GLOBAL_RESULT were always true. --- extras/test/runtests | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/extras/test/runtests b/extras/test/runtests index b664e9a..66b79d1 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -389,11 +389,9 @@ notice "Test results summary" print "${#startloops} loop devices busy at start" for t in $tests; do - echo "$t\t${results[$t]:-FAIL}" -done - -for r in ${(v)results}; do - [[ "$r" == "SUCCESS" ]] || GLOBAL_RESULT=1 + res=${results[$t]:-FAIL} + [[ "$res" == "SUCCESS" ]] || GLOBAL_RESULT=1 + echo "$t\t$res" done print "${#endloops} loop devices busy at end" From db7109da4a838dffe6e5da3abf344a5bb1642a9c Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Mon, 30 Jan 2017 21:45:02 +0000 Subject: [PATCH 05/25] Add tests for GPG recipient support in tomb --- extras/test/runtests | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/extras/test/runtests b/extras/test/runtests index 66b79d1..9269d67 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -56,12 +56,20 @@ command -v qrencode > /dev/null || QRENCODE=0 typeset -A results -tests=(dig forge lock badpass open close passwd chksum bind setkey) +tests=(dig forge lock badpass open close passwd chksum bind setkey +recip-dig recip-forge recip-lock recip-open recip-close) { test $RESIZER = 1 } && { tests+=(resize) } { test $KDF = 1 } && { tests+=(kdforge kdfpass kdflock kdfopen) } { test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl) } { test $QRENCODE = 1 } && { tests+=(qrenc) } +# GnuPG Conf. +# Note: the assumption is the test keys are unencrypted. +export GNUPGHOME="gnupg/" +chmod 700 "$GNUPGHOME" +gpgid_1="A4857CD176B31435F9709D25F0E573B8289439CD" +gpgid_2="0B2235E660753AB0475FB3E23DC836481F44B31E" + notice "Loading test suite" # functions that can be called singularly @@ -100,6 +108,35 @@ test-tomb-create() { { test $? = 0 } && { results+=(lock SUCCESS) } } +test-tomb-recip() { + + notice "wiping all recip.tomb* in /tmp" + local tomb=/tmp/recip.tomb + local tomb_key=/tmp/recip.tomb.key + sudo rm -f "$tomb" "$tomb_key" + + notice "Testing tomb with recipient creation: dig" + tt dig -s 20 $tomb + { test $? = 0 } && { results+=(recip-dig SUCCESS) } + + notice "Testing tomb with recipient creation: forge" + tt forge $tomb_key -r $gpgid_1 --ignore-swap --unsafe --use-urandom + { test $? = 0 } && { results+=(recip-forge SUCCESS) } + + notice "Testing tomb with recipient creation: lock" + tt lock $tomb -k $tomb_key -r $gpgid_1 --ignore-swap --unsafe + { test $? = 0 } && { results+=(recip-lock SUCCESS) } + + notice "Testing tomb with recipient opening: open" + tt open $tomb -k $tomb_key -r $gpgid_1 + { test $? = 0 } && { results+=(recip-open SUCCESS) } + + notice "Testing tomb with recipient closing: close" + tt close recip + { test $? = 0 } && { results+=(recip-close SUCCESS) } + +} + test-bind-hooks() { notice "Testing bind hooks" @@ -208,6 +245,7 @@ startloops=(`sudo losetup -a |cut -d: -f1`) # isolated function (also called with source) test-tomb-create +test-tomb-recip notice "Testing open with wrong password" From 902860fd9fd12daa6088ce8f7949f7c2c61b9d2e Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Tue, 31 Jan 2017 13:45:11 +0000 Subject: [PATCH 06/25] Add GPG recipient support when generating a new tomb key --- tomb | 123 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 65 insertions(+), 58 deletions(-) diff --git a/tomb b/tomb index 4ade4f1..a481c9a 100755 --- a/tomb +++ b/tomb @@ -1139,72 +1139,79 @@ gen_key() { tombpass="" tombpasstmp="" - if [ "$1" = "" ]; then - while true; do - # 3 tries to write two times a matching password - tombpass=`ask_password "Type the new password to secure your key"` - if [[ $? != 0 ]]; then - _failure "User aborted." - fi - if [ -z $tombpass ]; then - _failure "You set empty password, which is not possible." - fi - tombpasstmp=$tombpass - tombpass=`ask_password "Type the new password to secure your key (again)"` - if [[ $? != 0 ]]; then - _failure "User aborted." - fi - if [ "$tombpasstmp" = "$tombpass" ]; then - break; - fi - unset tombpasstmp - unset tombpass - done - else - tombpass="$1" - _verbose "gen_key takes tombpass from CLI argument: ::1 tomb pass::" $tombpass - fi - - header="" - [[ $KDF == 1 ]] && { - { option_is_set --kdf } && { - # KDF is a new key strenghtening technique against brute forcing - # see: https://github.com/dyne/Tomb/issues/82 - itertime="`option_value --kdf`" - # removing support of floating points because they can't be type checked well - if [[ "$itertime" != <-> ]]; then - unset tombpass + { ! option_is_set -r } && { + if [ "$1" = "" ]; then + while true; do + # 3 tries to write two times a matching password + tombpass=`ask_password "Type the new password to secure your key"` + if [[ $? != 0 ]]; then + _failure "User aborted." + fi + if [ -z $tombpass ]; then + _failure "You set empty password, which is not possible." + fi + tombpasstmp=$tombpass + tombpass=`ask_password "Type the new password to secure your key (again)"` + if [[ $? != 0 ]]; then + _failure "User aborted." + fi + if [ "$tombpasstmp" = "$tombpass" ]; then + break; + fi unset tombpasstmp - _warning "Wrong argument for --kdf: must be an integer number (iteration seconds)." - _failure "Depending on the speed of machines using this tomb, use 1 to 10, or more" - return 1 - fi - # --kdf takes one parameter: iter time (on present machine) in seconds - local -i microseconds - microseconds=$(( itertime * 1000000 )) - _success "Using KDF, iteration time: ::1 microseconds::" $microseconds - _message "generating salt" - pbkdf2_salt=`tomb-kdb-pbkdf2-gensalt` - _message "calculating iterations" - pbkdf2_iter=`tomb-kdb-pbkdf2-getiter $microseconds` - _message "encoding the password" - # We use a length of 64bytes = 512bits (more than needed!?) - tombpass=`tomb-kdb-pbkdf2 $pbkdf2_salt $pbkdf2_iter 64 <<<"${tombpass}"` + unset tombpass + done + else + tombpass="$1" + _verbose "gen_key takes tombpass from CLI argument: ::1 tomb pass::" $tombpass + fi - header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n" + header="" + [[ $KDF == 1 ]] && { + { option_is_set --kdf } && { + # KDF is a new key strenghtening technique against brute forcing + # see: https://github.com/dyne/Tomb/issues/82 + itertime="`option_value --kdf`" + # removing support of floating points because they can't be type checked well + if [[ "$itertime" != <-> ]]; then + unset tombpass + unset tombpasstmp + _warning "Wrong argument for --kdf: must be an integer number (iteration seconds)." + _failure "Depending on the speed of machines using this tomb, use 1 to 10, or more" + return 1 + fi + # --kdf takes one parameter: iter time (on present machine) in seconds + local -i microseconds + microseconds=$(( itertime * 1000000 )) + _success "Using KDF, iteration time: ::1 microseconds::" $microseconds + _message "generating salt" + pbkdf2_salt=`tomb-kdb-pbkdf2-gensalt` + _message "calculating iterations" + pbkdf2_iter=`tomb-kdb-pbkdf2-getiter $microseconds` + _message "encoding the password" + # We use a length of 64bytes = 512bits (more than needed!?) + tombpass=`tomb-kdb-pbkdf2 $pbkdf2_salt $pbkdf2_iter 64 <<<"${tombpass}"` + + header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n" + } } + print $header } - - print $header - _tmp_create local tmpres=$TOMBTMP - print -n - "${tombpass}\n$TOMBSECRET" \ - | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ - --no-options --no-tty --passphrase-fd 0 \ - --status-fd 2 -o - -c -a 2> $tmpres + if option_is_set -r; then + print -n - "${tombpass}\n$TOMBSECRET" \ + | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ + --no-options --no-tty --recipient `option_value -r` \ + --status-fd 2 -o - --encrypt --armor 2> $tmpres + else + print -n - "${tombpass}\n$TOMBSECRET" \ + | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ + --no-options --no-tty --passphrase-fd 0 \ + --status-fd 2 -o - --symmetric --armor 2> $tmpres + fi # check result of gpg operation for i in ${(f)"$(cat $tmpres)"}; do _verbose "$i" From 2d516cbaed37c2c1cc85c6bd77c8685e90517433 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Tue, 31 Jan 2017 15:11:50 +0000 Subject: [PATCH 07/25] Check if the GPG key given is a valid. Add the function 'is_valid_recipients' A key is valid if both public and private keys are present in the GPG database --- tomb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tomb b/tomb index a481c9a..4f734ca 100755 --- a/tomb +++ b/tomb @@ -825,6 +825,15 @@ _ensure_dependencies() { # {{{ Key operations +# $1 is the key ID we are checking +is_valid_recipients() { + local gpg_id="$1" # Unique argument is the GPG ID to test + _verbose "is_valid_recipients" + + gpg --list-secret-keys "$gpg_id" &> /dev/null + return $? +} + # $1 is the encrypted key contents we are checking is_valid_key() { local key="$1" # Unique argument is an encrypted key to test @@ -1202,10 +1211,14 @@ gen_key() { local tmpres=$TOMBTMP if option_is_set -r; then + local gpgkey="`option_value -r`" + { is_valid_recipients "$gpgkey" } || { + _failure "You set an invalid GPG ID." + } print -n - "${tombpass}\n$TOMBSECRET" \ | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ - --no-options --no-tty --recipient `option_value -r` \ - --status-fd 2 -o - --encrypt --armor 2> $tmpres + --no-options --no-tty --recipient "$gpgkey" \ + --status-fd 2 -o - --encrypt --armor 2> $tmpres else print -n - "${tombpass}\n$TOMBSECRET" \ | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ From d1b016b3c1b31811c3254fa4def3193cc21b8811 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Tue, 31 Jan 2017 15:49:39 +0000 Subject: [PATCH 08/25] Add GPG recipient support for steganography function (bury and exhume) The tomb policy is to use the same password to encrypt the key and to bury it. However, steganography cannot be done with GPG key. Therefore, we check the user can decrypt the tomb with its GPG key and we ask for a steganography password. Having different method is a technical requirement and should enhance security. --- tomb | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/tomb b/tomb index 4f734ca..a1463dd 100755 --- a/tomb +++ b/tomb @@ -1014,9 +1014,18 @@ get_lukskey() { # key needs to be exhumed from an image elif [[ -r $TOMBKEYFILE && $(file $TOMBKEYFILE) =~ "JP.G" ]]; then - + # When using a GPG key, the tomb key is buried using a steganography password + if option_is_set -r; then + _password=$(ask_password "Insert password to exhume key from $imagefile") + [[ $? != 0 ]] && { + _warning "User aborted password dialog." + return 1 + } + fi exhume_key $TOMBKEYFILE "$_password" - + if option_is_set -r; then + unset _password + fi fi gpg_decrypt "$_password" # Save decrypted contents into $TOMBSECRET @@ -1269,13 +1278,18 @@ bury_key() { } _success "Encoding key ::1 tomb key:: inside image ::2 image file::" $TOMBKEY $imagefile - _message "Please confirm the key password for the encoding" + { option_is_set -r } || { + _message "Please confirm the key password for the encoding" } + # We ask the password and test if it is the same encoding the # base key, to insure that the same password is used for the # encryption and the steganography. This is a standard enforced # by Tomb, but it isn't strictly necessary (and having different # password would enhance security). Nevertheless here we prefer # usability. + # However, steganography cannot be done with GPG key. Therefore, + # if using a GPG key, we test if the user can decrypt the tomb + # with its key and we ask for a the steganography password. { option_is_set --tomb-pwd } && { local tombpwd="`option_value --tomb-pwd`" @@ -1288,6 +1302,32 @@ bury_key() { _warning "Wrong password supplied." _failure "You shall not bury a key whose password is unknown to you." } + if option_is_set -r; then + tombpass="" + tombpasstmp="" + while true; do + # 3 tries to write two times a matching password + tombpass=`ask_password "Type a password to bury your key"` + if [[ $? != 0 ]]; then + _failure "User aborted." + fi + if [ -z $tombpass ]; then + _failure "You set empty password, which is not possible." + fi + tombpasstmp=$tombpass + tombpass=`ask_password "Type a password to bury your key (again)"` + if [[ $? != 0 ]]; then + _failure "User aborted." + fi + if [ "$tombpasstmp" = "$tombpass" ]; then + break; + fi + unset tombpasstmp + unset tombpass + done + TOMBPASSWORD="$tombpass" + fi + # We omit armor strings since having them as constants can give # ground to effective attacks on steganography print - "$TOMBKEY" | awk ' @@ -2616,8 +2656,8 @@ main() { subcommands_opts[search]="" subcommands_opts[help]="" - subcommands_opts[bury]="k: -tomb-pwd: " - subcommands_opts[exhume]="k: -tomb-pwd: " + subcommands_opts[bury]="k: -tomb-pwd: r: " + subcommands_opts[exhume]="k: -tomb-pwd: r: " # subcommands_opts[decompose]="" # subcommands_opts[recompose]="" # subcommands_opts[install]="" From e2fe8e508e49b270131ebfe1077bb6862203b3b2 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Tue, 31 Jan 2017 16:01:42 +0000 Subject: [PATCH 09/25] Add unit tests for steganography feature using GPG key --- extras/test/runtests | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/extras/test/runtests b/extras/test/runtests index 9269d67..cdf9e18 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -60,7 +60,8 @@ tests=(dig forge lock badpass open close passwd chksum bind setkey recip-dig recip-forge recip-lock recip-open recip-close) { test $RESIZER = 1 } && { tests+=(resize) } { test $KDF = 1 } && { tests+=(kdforge kdfpass kdflock kdfopen) } -{ test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl) } +{ test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl +recip-stgin recip-stgout recip-stgopen recip-stgimpl) } { test $QRENCODE = 1 } && { tests+=(qrenc) } # GnuPG Conf. @@ -135,6 +136,33 @@ test-tomb-recip() { tt close recip { test $? = 0 } && { results+=(recip-close SUCCESS) } + { test $STEGHIDE = 1 } && { + notice "Testing tomb with recipient steganographic hiding of keys" + + cp -f arditi.jpg /tmp/recip.jpg + sudo rm -f /tmp/recip.steg.key + + # The implementation does not support "--unsafe --tomb-pwd ${dummypass}" yet + tt bury -k /tmp/recip.tomb.key \ + /tmp/recip.jpg -r "$gpgid_1" + { test $? = 0 } && { results+=(recip-stgin SUCCESS) } + + tt exhume -k /tmp/recip.steg.key \ + /tmp/recip.jpg + { test $? = 0 } && { results+=(recip-stgout SUCCESS) } + + tt open -k /tmp/recip.steg.key \ + /tmp/recip.tomb -r "$gpgid_1" + { test $? = 0 } && { results+=(recip-stgopen SUCCESS) } + ${T} close recip + + notice "test using open -k image.jpeg" + tt open -k /tmp/recip.jpg \ + /tmp/recip.tomb -r "$gpgid_1" + { test $? = 0 } && { results+=(recip-stgimpl SUCCESS) } + tt close recip + } + } test-bind-hooks() { From b23e9aa0280b6e27ccf1b4daf4467d39bfe46a56 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Tue, 31 Jan 2017 16:23:29 +0000 Subject: [PATCH 10/25] Add --tomb-pwd support for GPG key on steganography functions --- extras/test/runtests | 11 +++++------ tomb | 16 +++++++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/extras/test/runtests b/extras/test/runtests index cdf9e18..3d2ff22 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -142,22 +142,21 @@ test-tomb-recip() { cp -f arditi.jpg /tmp/recip.jpg sudo rm -f /tmp/recip.steg.key - # The implementation does not support "--unsafe --tomb-pwd ${dummypass}" yet - tt bury -k /tmp/recip.tomb.key \ + tt --unsafe --tomb-pwd ${dummypass} bury -k /tmp/recip.tomb.key \ /tmp/recip.jpg -r "$gpgid_1" { test $? = 0 } && { results+=(recip-stgin SUCCESS) } - tt exhume -k /tmp/recip.steg.key \ - /tmp/recip.jpg + tt --unsafe --tomb-pwd ${dummypass} exhume -k /tmp/recip.steg.key \ + /tmp/recip.jpg { test $? = 0 } && { results+=(recip-stgout SUCCESS) } - tt open -k /tmp/recip.steg.key \ + tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/recip.steg.key \ /tmp/recip.tomb -r "$gpgid_1" { test $? = 0 } && { results+=(recip-stgopen SUCCESS) } ${T} close recip notice "test using open -k image.jpeg" - tt open -k /tmp/recip.jpg \ + tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/recip.jpg \ /tmp/recip.tomb -r "$gpgid_1" { test $? = 0 } && { results+=(recip-stgimpl SUCCESS) } tt close recip diff --git a/tomb b/tomb index a1463dd..cd104bf 100755 --- a/tomb +++ b/tomb @@ -1015,7 +1015,10 @@ get_lukskey() { # key needs to be exhumed from an image elif [[ -r $TOMBKEYFILE && $(file $TOMBKEYFILE) =~ "JP.G" ]]; then # When using a GPG key, the tomb key is buried using a steganography password - if option_is_set -r; then + if option_is_set -r && option_is_set --tomb-pwd; then + _password="`option_value --tomb-pwd`" + _verbose "tomb-pwd = ::1 tomb pass::" $_password + elif option_is_set -r; then _password=$(ask_password "Insert password to exhume key from $imagefile") [[ $? != 0 ]] && { _warning "User aborted password dialog." @@ -1289,9 +1292,9 @@ bury_key() { # usability. # However, steganography cannot be done with GPG key. Therefore, # if using a GPG key, we test if the user can decrypt the tomb - # with its key and we ask for a the steganography password. + # with its key and we ask for a steganography password. - { option_is_set --tomb-pwd } && { + { option_is_set --tomb-pwd } && { ! option_is_set -r } && { local tombpwd="`option_value --tomb-pwd`" _verbose "tomb-pwd = ::1 tomb pass::" $tombpwd ask_key_password "$tombpwd" @@ -1302,7 +1305,10 @@ bury_key() { _warning "Wrong password supplied." _failure "You shall not bury a key whose password is unknown to you." } - if option_is_set -r; then + if option_is_set -r && option_is_set --tomb-pwd; then + TOMBPASSWORD="`option_value --tomb-pwd`" + _verbose "tomb-pwd = ::1 tomb pass::" $TOMBPASSWORD + elif option_is_set -r; then tombpass="" tombpasstmp="" while true; do @@ -1862,7 +1868,7 @@ mount_tomb() { # take the name only, strip extensions _verbose "Tomb name: ::1 tomb name:: (to be engraved)" $TOMBNAME - { option_is_set --tomb-pwd } && { + { option_is_set --tomb-pwd } && { ! option_is_set -r } && { tomb_pwd="`option_value --tomb-pwd`" _verbose "tomb-pwd = ::1 tomb pass::" $tomb_pwd ask_key_password "$tomb_pwd" From 8f8dc0a0d4a088d77159d791d85e3cd8df434ec2 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Tue, 31 Jan 2017 23:10:39 +0000 Subject: [PATCH 11/25] Improve exhumation of key when opening a tomb --- tomb | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tomb b/tomb index cd104bf..52dd73c 100755 --- a/tomb +++ b/tomb @@ -1014,20 +1014,22 @@ get_lukskey() { # key needs to be exhumed from an image elif [[ -r $TOMBKEYFILE && $(file $TOMBKEYFILE) =~ "JP.G" ]]; then - # When using a GPG key, the tomb key is buried using a steganography password - if option_is_set -r && option_is_set --tomb-pwd; then - _password="`option_value --tomb-pwd`" - _verbose "tomb-pwd = ::1 tomb pass::" $_password - elif option_is_set -r; then - _password=$(ask_password "Insert password to exhume key from $imagefile") - [[ $? != 0 ]] && { - _warning "User aborted password dialog." - return 1 - } - fi - exhume_key $TOMBKEYFILE "$_password" if option_is_set -r; then + # When using a GPG key, the tomb key is buried using a steganography password + if option_is_set --tomb-pwd; then + _password="`option_value --tomb-pwd`" + _verbose "tomb-pwd = ::1 tomb pass::" $_password + else + _password=$(ask_password "Insert password to exhume key from $imagefile") + [[ $? != 0 ]] && { + _warning "User aborted password dialog." + return 1 + } + fi + exhume_key $TOMBKEYFILE "$_password" unset _password + else + exhume_key $TOMBKEYFILE "$_password" fi fi From 5a35ab9668e5613f731baaaf089a002ddd9f4db7 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 2 Feb 2017 23:24:45 +0000 Subject: [PATCH 12/25] Improve key encryption/decryption using GPG key. Decryption/Encryption works without these improvment, however, there are needed in order to have clean key (without empty line). Moreover, tests showed not doing cause troubles when changing the GPG key used to encrypt a tomb key. --- tomb | 67 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/tomb b/tomb index 52dd73c..da2c70b 100755 --- a/tomb +++ b/tomb @@ -947,14 +947,24 @@ gpg_decrypt() { # fix for gpg 1.4.11 where the --status-* options don't work ;^/ local gpgver=$(gpg --version --no-permission-warning | awk '/^gpg/ {print $3}') local gpgpass="$1\n$TOMBKEY" - local gpgstatus - local tmpres + local tmpres ret + typeset -a gpgopt + gpgpopt=(--passphrase-fd 0) + { option_is_set -r } && { + local gpgkey=`option_value -r` + _verbose "using $gpgkey to decrypt a tomb key" + { ! is_valid_recipients "$gpgkey" } && { + _failure "You set an invalid GPG ID." + } + gpgpass="$TOMBKEY" + gpgpopt=() + } + [[ $gpgver == "1.4.11" ]] && { _verbose "GnuPG is version 1.4.11 - adopting status fix." - TOMBSECRET=`print - "$gpgpass" | \ - gpg --batch --passphrase-fd 0 --no-tty --no-options` + gpg --batch ${gpgpopt[@]} --no-tty --no-options` ret=$? unset gpgpass return $ret @@ -963,12 +973,10 @@ gpg_decrypt() { _tmp_create tmpres=$TOMBTMP TOMBSECRET=`print - "$gpgpass" | \ - gpg --batch --passphrase-fd 0 --no-tty --no-options \ + gpg --batch ${gpgpopt[@]} --no-tty --no-options \ --status-fd 2 --no-mdc-warning --no-permission-warning \ --no-secmem-warning 2> $tmpres` - unset gpgpass - ret=1 for i in ${(f)"$(cat $tmpres)"}; do _verbose "$i" @@ -1158,11 +1166,24 @@ gen_key() { # -o is the --cipher-algo to use (string taken by GnuPG) local algopt="`option_value -o`" local algo="${algopt:-AES256}" + local gpgpass opt + typeset -a gpgopt # here user is prompted for key password tombpass="" tombpasstmp="" - { ! option_is_set -r } && { + { option_is_set -r } && { + local gpgkey=`option_value -r` + _verbose "using $gpgkey to encrypt a tomb key" + { is_valid_recipients "$gpgkey" } || { + _failure "You set an invalid GPG ID." + } + + # Set gpg inputs and options + gpgpass="$TOMBSECRET" + gpgopt=(--encrypt --recipient "$gpgkey") + opt='' + } || { if [ "$1" = "" ]; then while true; do # 3 tries to write two times a matching password @@ -1219,26 +1240,20 @@ gen_key() { } } print $header + + # Set gpg inputs and options + gpgpass="${tombpass}\n$TOMBSECRET" + gpgopt=(--passphrase-fd 0 --symmetric) + opt='-n' } - _tmp_create - local tmpres=$TOMBTMP - - if option_is_set -r; then - local gpgkey="`option_value -r`" - { is_valid_recipients "$gpgkey" } || { - _failure "You set an invalid GPG ID." - } - print -n - "${tombpass}\n$TOMBSECRET" \ - | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ - --no-options --no-tty --recipient "$gpgkey" \ - --status-fd 2 -o - --encrypt --armor 2> $tmpres - else - print -n - "${tombpass}\n$TOMBSECRET" \ - | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ - --no-options --no-tty --passphrase-fd 0 \ - --status-fd 2 -o - --symmetric --armor 2> $tmpres - fi + _tmp_create + local tmpres=$TOMBTMP + print $opt - "$gpgpass" \ + | gpg --openpgp --force-mdc --cipher-algo ${algo} --batch \ + --no-options --no-tty ${gpgopt[@]} \ + --status-fd 2 -o - --armor 2> $tmpres + unset gpgpass # check result of gpg operation for i in ${(f)"$(cat $tmpres)"}; do _verbose "$i" From 47ddeebbc48720310616a909c7f12637c855552b Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 2 Feb 2017 23:41:09 +0000 Subject: [PATCH 13/25] Add support to change the GPG key used to encrypt a tomb key. (tomb passwd) --- extras/test/runtests | 12 +++++++++++- tomb | 9 +++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/extras/test/runtests b/extras/test/runtests index 3d2ff22..9152acc 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -57,7 +57,7 @@ command -v qrencode > /dev/null || QRENCODE=0 typeset -A results tests=(dig forge lock badpass open close passwd chksum bind setkey -recip-dig recip-forge recip-lock recip-open recip-close) +recip-dig recip-forge recip-lock recip-open recip-close recip-passwd) { test $RESIZER = 1 } && { tests+=(resize) } { test $KDF = 1 } && { tests+=(kdforge kdfpass kdflock kdfopen) } { test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl @@ -162,6 +162,16 @@ test-tomb-recip() { tt close recip } + notice "Testing tomb with recipient changing gpg key: passwd" + res=0 + tt passwd -k $tomb_key -r $gpgid_1 -R $gpgid_2 + { test $? = 0 } || { res=1 } + tt open $tomb -k $tomb_key -r $gpgid_2 + { test $? = 0 } || { res=1 } + tt close recip + { test $? = 0 } || { res=1 } + { test $res = 0 } && { results+=(recip-passwd SUCCESS) } + } test-bind-hooks() { diff --git a/tomb b/tomb index da2c70b..4e5bd96 100755 --- a/tomb +++ b/tomb @@ -1173,7 +1173,12 @@ gen_key() { tombpasstmp="" { option_is_set -r } && { - local gpgkey=`option_value -r` + { option_is_set -R } && { + local gpgkey=`option_value -R` + } || { + local gpgkey=`option_value -r` + } + _verbose "using $gpgkey to encrypt a tomb key" { is_valid_recipients "$gpgkey" } || { _failure "You set an invalid GPG ID." @@ -2669,7 +2674,7 @@ main() { subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: " subcommands_opts[engrave]="k: " - subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: " + subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: R: " subcommands_opts[close]="" subcommands_opts[help]="" subcommands_opts[slam]="" From a200448de2a3c51305cf039381866c278468b20d Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 00:01:02 +0000 Subject: [PATCH 14/25] Add tomb resize support for GPG key --- extras/test/runtests | 6 +++++- tomb | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/extras/test/runtests b/extras/test/runtests index 9152acc..ae271c4 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -57,7 +57,7 @@ command -v qrencode > /dev/null || QRENCODE=0 typeset -A results tests=(dig forge lock badpass open close passwd chksum bind setkey -recip-dig recip-forge recip-lock recip-open recip-close recip-passwd) +recip-dig recip-forge recip-lock recip-open recip-close recip-passwd recip-resize) { test $RESIZER = 1 } && { tests+=(resize) } { test $KDF = 1 } && { tests+=(kdforge kdfpass kdflock kdfopen) } { test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl @@ -172,6 +172,10 @@ test-tomb-recip() { { test $? = 0 } || { res=1 } { test $res = 0 } && { results+=(recip-passwd SUCCESS) } + notice "Testing tomb with recipient resizing a tomb: resize" + tt resize -s 30 $tomb -k $tomb_key -r $gpgid_2 + { test $? = 0 } && { results+=(recip-resize SUCCESS) } + } test-bind-hooks() { diff --git a/tomb b/tomb index 4e5bd96..be7aa9b 100755 --- a/tomb +++ b/tomb @@ -2691,7 +2691,7 @@ main() { # subcommands_opts[install]="" subcommands_opts[askpass]="" subcommands_opts[source]="" - subcommands_opts[resize]="-ignore-swap s: -size=s k: -tomb-pwd: " + subcommands_opts[resize]="-ignore-swap s: -size=s k: -tomb-pwd: r: " subcommands_opts[check]="-ignore-swap " # subcommands_opts[translate]="" From 53b7460274e8478ed955aa6b3ccd95fd502ac417 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 00:16:29 +0000 Subject: [PATCH 15/25] Add tomb setkey support for GPG key --- extras/test/runtests | 17 +++++++++++++++-- tomb | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/extras/test/runtests b/extras/test/runtests index ae271c4..a89c591 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -56,8 +56,9 @@ command -v qrencode > /dev/null || QRENCODE=0 typeset -A results -tests=(dig forge lock badpass open close passwd chksum bind setkey -recip-dig recip-forge recip-lock recip-open recip-close recip-passwd recip-resize) +tests=(dig forge lock badpass open close passwd chksum bind setkey recip-dig +recip-forge recip-lock recip-open recip-close recip-passwd recip-resize +recip-setkey) { test $RESIZER = 1 } && { tests+=(resize) } { test $KDF = 1 } && { tests+=(kdforge kdfpass kdflock kdfopen) } { test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl @@ -176,6 +177,18 @@ test-tomb-recip() { tt resize -s 30 $tomb -k $tomb_key -r $gpgid_2 { test $? = 0 } && { results+=(recip-resize SUCCESS) } + notice "Testing tomb with recipient setting a new key: setkey" + sudo rm -f /tmp/new.recip.tomb.key + res=0 + tt forge /tmp/new.recip.tomb.key -r $gpgid_2 \ + --ignore-swap --unsafe --use-urandom + { test $? = 0 } || { res=1 } + tt setkey -k /tmp/new.recip.tomb.key $tomb_key $tomb -r $gpgid_2 + { test $? = 0 } || { res=1 } + tt open -k /tmp/new.recip.tomb.key $tomb -r $gpgid_2 + { test $? = 0 } || { res=1 } + { test $res = 0 } && { results+=(recip-setkey SUCCESS) } + tt close recip } test-bind-hooks() { diff --git a/tomb b/tomb index be7aa9b..fc3f7c6 100755 --- a/tomb +++ b/tomb @@ -2671,7 +2671,7 @@ main() { subcommands_opts[forge]="-ignore-swap k: -kdf: o: -tomb-pwd: -use-urandom r: " subcommands_opts[dig]="-ignore-swap s: -size=s " subcommands_opts[lock]="-ignore-swap k: -kdf: o: -tomb-pwd: r: " - subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: " + subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: " subcommands_opts[engrave]="k: " subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: R: " From 15164f5578a34a85a5821a331045cd2c11d369bd Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 17:28:56 +0000 Subject: [PATCH 16/25] Add sharing support for tomb key. A tomb key can be encrypted with more than one recipient. Therefore, a tomb can be shared between different user. The multiple recipients are given using the -r (or/and -R) option and must be separated by ','. Multiple recipients can be given for the commands: forge, setket and passwd --- extras/test/runtests | 41 ++++++++++++++++++++++++++++++++- tomb | 54 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/extras/test/runtests b/extras/test/runtests index a89c591..e3af55a 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -58,7 +58,7 @@ command -v qrencode > /dev/null || QRENCODE=0 typeset -A results tests=(dig forge lock badpass open close passwd chksum bind setkey recip-dig recip-forge recip-lock recip-open recip-close recip-passwd recip-resize -recip-setkey) +recip-setkey shared shared-passwd shared-setkey) { test $RESIZER = 1 } && { tests+=(resize) } { test $KDF = 1 } && { tests+=(kdforge kdfpass kdflock kdfopen) } { test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl @@ -191,6 +191,44 @@ test-tomb-recip() { tt close recip } +test-tomb-shared() { + + notice "wiping all shared.tomb* in /tmp" + rm -f /tmp/shared.tomb /tmp/shared.tomb.key + + notice "Testing sharing a tomb" + res=0 + tt dig -s 20 /tmp/shared.tomb + { test $? = 0 } || { res=1 } + tt forge /tmp/shared.tomb.key -r $gpgid_1,$gpgid_2 \ + --ignore-swap --unsafe --use-urandom + { test $? = 0 } || { res=1 } + tt lock /tmp/shared.tomb -k /tmp/shared.tomb.key \ + --ignore-swap --unsafe -r $gpgid_1 + { test $? = 0 } || { res=1 } + tt open /tmp/shared.tomb -k /tmp/shared.tomb.key -r $gpgid_1 + { test $? = 0 } || { res=1 } + tt close shared + { test $? = 0 } || { res=1 } + { test $res = 0 } && { results+=(shared SUCCESS) } + + notice "Testing changing recipients on a shared Tomb" + tt passwd -k /tmp/shared.tomb.key -r $gpgid_1,$gpgid_2 \ + -R $gpgid_2,$gpgid_1 + { test $? = 0 } && { results+=(shared-passwd SUCCESS) } + + notice "Testing setkey on a shared Tomb" + rm -f /tmp/new.shared.tomb.key + res=0 + tt forge /tmp/new.shared.tomb.key -r $gpgid_1,$gpgid_2 \ + --ignore-swap --unsafe --use-urandom + { test $? = 0 } || { res=1 } + tt setkey -k /tmp/new.shared.tomb.key /tmp/shared.tomb.key /tmp/shared.tomb \ + -r $gpgid_2,$gpgid_1 + { test $? = 0 } || { res=1 } + { test $res = 0 } && { results+=(shared-setkey SUCCESS) } +} + test-bind-hooks() { notice "Testing bind hooks" @@ -300,6 +338,7 @@ startloops=(`sudo losetup -a |cut -d: -f1`) # isolated function (also called with source) test-tomb-create test-tomb-recip +test-tomb-shared notice "Testing open with wrong password" diff --git a/tomb b/tomb index fc3f7c6..d7ad4b5 100755 --- a/tomb +++ b/tomb @@ -825,13 +825,43 @@ _ensure_dependencies() { # {{{ Key operations -# $1 is the key ID we are checking +# $@ is the list of all the recipient used to encrypt a tomb key is_valid_recipients() { - local gpg_id="$1" # Unique argument is the GPG ID to test + typeset -a recipients + recipients=($@) + _verbose "is_valid_recipients" - gpg --list-secret-keys "$gpg_id" &> /dev/null - return $? + # All the keys ID must be valid (the public keys must be present in the database) + for gpg_id in ${recipients[@]}; do + gpg --list-keys "$gpg_id" &> /dev/null + [[ $? != 0 ]] && { + _warning "$gpg_id is not a valid key ID." + return 1 + } + done + + # At least one private key must be present + for gpg_id in $recipients; do + gpg --list-secret-keys "$gpg_id" &> /dev/null + [[ $? = 0 ]] && { + return 0 + } + done + + return 1 +} + +# $@ is the list of all the recipient used to encrypt a tomb key +# Print the recipient arg to be used in gpg. +_recipients_arg() { + typeset -a recipients + recipients=($@) + + for gpg_id in ${recipients[@]}; do + print -R -n "--recipient $gpg_id " + done + return 0 } # $1 is the encrypted key contents we are checking @@ -952,9 +982,9 @@ gpg_decrypt() { gpgpopt=(--passphrase-fd 0) { option_is_set -r } && { - local gpgkey=`option_value -r` - _verbose "using $gpgkey to decrypt a tomb key" - { ! is_valid_recipients "$gpgkey" } && { + typeset -a recipients + recipients=(${(s:,:)$(option_value -r)}) + { is_valid_recipients $recipients } || { _failure "You set an invalid GPG ID." } gpgpass="$TOMBKEY" @@ -1173,20 +1203,20 @@ gen_key() { tombpasstmp="" { option_is_set -r } && { + typeset -a recipients { option_is_set -R } && { - local gpgkey=`option_value -R` + recipients=(${(s:,:)$(option_value -R)}) } || { - local gpgkey=`option_value -r` + recipients=(${(s:,:)$(option_value -r)}) } - _verbose "using $gpgkey to encrypt a tomb key" - { is_valid_recipients "$gpgkey" } || { + { is_valid_recipients $recipients } || { _failure "You set an invalid GPG ID." } # Set gpg inputs and options gpgpass="$TOMBSECRET" - gpgopt=(--encrypt --recipient "$gpgkey") + gpgopt=(--encrypt `_recipients_arg $recipients`) opt='' } || { if [ "$1" = "" ]; then From 6f89dbd2fec992e6b386fc048e7a0fe89dccc38a Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 17:55:06 +0000 Subject: [PATCH 17/25] Add '--shared' in order to activate sharing support. Sharing feature is a very sensitive action, the user needs to trust the GPG public key it is going to share its tomb. This is why this feature needs to be explicitly activated using in more the flag --shared on the key encryption commands. --- extras/test/runtests | 8 ++++---- tomb | 27 ++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/extras/test/runtests b/extras/test/runtests index e3af55a..c25ec23 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -200,7 +200,7 @@ test-tomb-shared() { res=0 tt dig -s 20 /tmp/shared.tomb { test $? = 0 } || { res=1 } - tt forge /tmp/shared.tomb.key -r $gpgid_1,$gpgid_2 \ + tt forge /tmp/shared.tomb.key -r $gpgid_1,$gpgid_2 --shared \ --ignore-swap --unsafe --use-urandom { test $? = 0 } || { res=1 } tt lock /tmp/shared.tomb -k /tmp/shared.tomb.key \ @@ -214,17 +214,17 @@ test-tomb-shared() { notice "Testing changing recipients on a shared Tomb" tt passwd -k /tmp/shared.tomb.key -r $gpgid_1,$gpgid_2 \ - -R $gpgid_2,$gpgid_1 + -R $gpgid_2,$gpgid_1 --shared { test $? = 0 } && { results+=(shared-passwd SUCCESS) } notice "Testing setkey on a shared Tomb" rm -f /tmp/new.shared.tomb.key res=0 - tt forge /tmp/new.shared.tomb.key -r $gpgid_1,$gpgid_2 \ + tt forge /tmp/new.shared.tomb.key -r $gpgid_1,$gpgid_2 --shared\ --ignore-swap --unsafe --use-urandom { test $? = 0 } || { res=1 } tt setkey -k /tmp/new.shared.tomb.key /tmp/shared.tomb.key /tmp/shared.tomb \ - -r $gpgid_2,$gpgid_1 + -r $gpgid_2,$gpgid_1 --shared { test $? = 0 } || { res=1 } { test $res = 0 } && { results+=(shared-setkey SUCCESS) } } diff --git a/tomb b/tomb index d7ad4b5..15ff175 100755 --- a/tomb +++ b/tomb @@ -864,6 +864,14 @@ _recipients_arg() { return 0 } +# $1 is a GPG key recipient +# Print the fingerprint of the GPG key +_fingerprint() { + local recipient="$1" + gpg --with-colons --fingerprint "$recipient" | grep fpr | head -1 | cut -d ':' -f 10 | sed 's/.\{4\}/& /g' +} + + # $1 is the encrypted key contents we are checking is_valid_key() { local key="$1" # Unique argument is an encrypted key to test @@ -1210,6 +1218,19 @@ gen_key() { recipients=(${(s:,:)$(option_value -r)}) } + [ "${#recipients}" -gt 1 ] && { + if option_is_set --shared; then + _warning "You are going to encrypt a tomb key with ${#recipients} recipients." + _warning "It is your responsibility to check the fingerprint of these recipients." + _warning "The fingerprints are:" + for gpg_id in ${recipients[@]}; do + _warning " `_fingerprint "$gpg_id"`" + done + else + _failure "You need to use the option '--shared' to enable sharing support" + fi + } + { is_valid_recipients $recipients } || { _failure "You set an invalid GPG ID." } @@ -2698,13 +2719,13 @@ main() { subcommands_opts[create]="" # deprecated, will issue warning # -o in forge and lock is used to pass an alternate cipher. - subcommands_opts[forge]="-ignore-swap k: -kdf: o: -tomb-pwd: -use-urandom r: " + subcommands_opts[forge]="-ignore-swap k: -kdf: o: -tomb-pwd: -use-urandom r: -shared " subcommands_opts[dig]="-ignore-swap s: -size=s " subcommands_opts[lock]="-ignore-swap k: -kdf: o: -tomb-pwd: r: " - subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: " + subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: -shared " subcommands_opts[engrave]="k: " - subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: R: " + subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: R: -shared " subcommands_opts[close]="" subcommands_opts[help]="" subcommands_opts[slam]="" From e8384ec7ac2b86cce3c509a1ac35efa2b7bfb380 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 18:06:57 +0000 Subject: [PATCH 18/25] Allow opening a tomb without giving a valid recipient. The -r option always requires an arguments. However GPG does not need any recipient when decrypting a key. In order to be able to open a tomb without writing (the long) recipient, the user can use the -f option to short-cut the valid recipient checking. A dummy recipient is still required. --- tomb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tomb b/tomb index 15ff175..5a76b78 100755 --- a/tomb +++ b/tomb @@ -992,7 +992,7 @@ gpg_decrypt() { { option_is_set -r } && { typeset -a recipients recipients=(${(s:,:)$(option_value -r)}) - { is_valid_recipients $recipients } || { + { ! is_valid_recipients $recipients } && { ! option_is_set -f } && { _failure "You set an invalid GPG ID." } gpgpass="$TOMBKEY" From dfc593f9d6e301d8b46b999a732640038deaa0f1 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 18:33:28 +0000 Subject: [PATCH 19/25] Add support for GPG key in the tomb outputs. --- tomb | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/tomb b/tomb index 5a76b78..cfe07af 100755 --- a/tomb +++ b/tomb @@ -937,8 +937,8 @@ _load_key() { _failure "This operation requires a key file to be specified using the -k option." } if option_is_set -r; then - _verbose "load_key delegating password handling to GnuPG" - _message "Waiting for GnuPG to handle password authentication... " + _verbose "load_key key encrypted with a GnuPG Key" + _message "Key encrypted with a GnuPG Key" TOMBKEYFILE=$keyfile TOMBKEY="${mapfile[$TOMBKEYFILE]}" elif [[ $keyfile == "-" ]]; then @@ -1160,7 +1160,11 @@ change_passwd() { _check_swap # Ensure swap is secure, if any _load_key # Try loading key from option -k and set TOMBKEYFILE - _message "Commanded to change password for tomb key ::1 key::" $TOMBKEYFILE + { option_is_set -r } && { + _message "Commanded to change GnuPG key for tomb key ::1 key::" $TOMBKEYFILE + } || { + _message "Commanded to change password for tomb key ::1 key::" $TOMBKEYFILE + } _tmp_create tmpnewkey=$TOMBTMP @@ -1174,7 +1178,11 @@ change_passwd() { fi [[ $? == 0 ]] || _failure "No valid password supplied." - _success "Changing password for ::1 key file::" $TOMBKEYFILE + { option_is_set -r } && { + _success "Changing GnuPG key for ::1 key file::" $TOMBKEYFILE + } || { + _success "Changing password for ::1 key file::" $TOMBKEYFILE + } # Here $TOMBSECRET contains the key material in clear @@ -1191,7 +1199,11 @@ change_passwd() { # Copy the new key as the original keyfile name cp -f "${tmpnewkey}" $TOMBKEYFILE - _success "Your passphrase was successfully updated." + { option_is_set -r } && { + _success "Your GnuPG key was successfully changed" + } || { + _success "Your passphrase was successfully updated." + } return 0 } @@ -1354,8 +1366,11 @@ bury_key() { } _success "Encoding key ::1 tomb key:: inside image ::2 image file::" $TOMBKEY $imagefile - { option_is_set -r } || { - _message "Please confirm the key password for the encoding" } + { option_is_set -r } && { + _message "Using GnuPG Key ID" + } || { + _message "Please confirm the key password for the encoding" + } # We ask the password and test if it is the same encoding the # base key, to insure that the same password is used for the @@ -1375,7 +1390,7 @@ bury_key() { ask_key_password } [[ $? != 0 ]] && { - _warning "Wrong password supplied." + _warning "Wrong password/GnuPG ID supplied." _failure "You shall not bury a key whose password is unknown to you." } if option_is_set -r && option_is_set --tomb-pwd; then @@ -1642,7 +1657,7 @@ forge_key() { _message "Commanded to forge key ::1 key:: with cipher algorithm ::2 algorithm::" \ $destkey $algo - [[ $KDF == 1 ]] && { + [[ $KDF == 1 ]] && { ! option_is_set -r } && { _message "Using KDF to protect the key password (`option_value --kdf` rounds)" } @@ -1665,7 +1680,15 @@ forge_key() { # Here the global variable TOMBSECRET contains the naked secret - _success "Choose the password of your key: ::1 tomb key::" $TOMBKEYFILE + { option_is_set -r } && { + { option_is_set --shared } && { + _success "Using GnuPG keys to encrypt and share your key: ::1 tomb key::" $TOMBKEYFILE + } || { + _success "Using the GnuPG key ::1:: to encrypt the key: ::2 tomb key::" `option_value -r` $TOMBKEYFILE + } + } || { + _success "Choose the password of your key: ::1 tomb key::" $TOMBKEYFILE + } _message "(You can also change it later using 'tomb passwd'.)" # _user_file $TOMBKEYFILE From 6cfffef13760ae1c76bf6fa3bf55167d7d742308 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 18:53:31 +0000 Subject: [PATCH 20/25] Update function comments & description with GPG recipient support. --- tomb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tomb b/tomb index cfe07af..d305b64 100755 --- a/tomb +++ b/tomb @@ -981,6 +981,7 @@ _load_key() { # takes two args just like get_lukskey # prints out the decrypted content # contains tweaks for different gpg versions +# support both symmetric and asymmetric encryption gpg_decrypt() { # fix for gpg 1.4.11 where the --status-* options don't work ;^/ local gpgver=$(gpg --version --no-permission-warning | awk '/^gpg/ {print $3}') @@ -1210,7 +1211,7 @@ change_passwd() { # takes care to encrypt a key -# honored options: --kdf --tomb-pwd -o +# honored options: --kdf --tomb-pwd -o -r -R gen_key() { # $1 the password to use; if not set ask user # -o is the --cipher-algo to use (string taken by GnuPG) @@ -1614,11 +1615,13 @@ dig_tomb() { # Step two -- Create a detached key to lock a tomb with # -# Synopsis: forge_key [destkey|-k destkey] [-o cipher] +# Synopsis: forge_key [destkey|-k destkey] [-o cipher] [-r gpgid] [--shared] # # Arguments: # -k path to destination keyfile # -o Use an alternate algorithm +# -r GPG recipients to be used +# --shared Activate sharing capability # forge_key() { # can be specified both as simple argument or using -k @@ -1723,7 +1726,7 @@ forge_key() { # Step three -- Lock tomb # -# Synopsis: tomb_lock file.tomb file.tomb.key [-o cipher] +# Synopsis: tomb_lock file.tomb file.tomb.key [-o cipher] [-r gpgid] # # Lock the given tomb with the given key file, in fact formatting the # loopback volume as a LUKS device. From f27130053d607a52d12f8a73ea082d0f0b81555c Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 18:56:13 +0000 Subject: [PATCH 21/25] Add new options description in tomb -h --- tomb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tomb b/tomb index d305b64..1732ab1 100755 --- a/tomb +++ b/tomb @@ -651,6 +651,9 @@ usage() { _print " -n don't process the hooks found in tomb" _print " -o options passed to commands: open, lock, forge (see man)" _print " -f force operation (i.e. even if swap is active)" + _print " -r GnuPG users IDs to encrypt a tomb key (separated by ,)" + _print " -R new recipients to use (separated by ,)" + _print " --shared active sharing feature" [[ $KDF == 1 ]] && { _print " --kdf forge keys armored against dictionary attacks" } From bfe5bb97073dc6f4f0d7f10235c2082808714ccc Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Fri, 3 Feb 2017 20:07:21 +0000 Subject: [PATCH 22/25] Update the man page with GPG key support --- doc/tomb.1 | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/doc/tomb.1 b/doc/tomb.1 index 775bc19..0e91112 100644 --- a/doc/tomb.1 +++ b/doc/tomb.1 @@ -46,7 +46,8 @@ supported ciphers use \fI-v\fR. For additional protection against dictionary attacks on keys, the (experimental) \fI--kdf\fR option can be used when forging a key, making sure that the \fItomb-kdb-pbkdf2\fR binaries in \fIextras/kdf\fR were compiled and installed on the -system. +system. Use the \fI-r\fR option to encrypt the key with a GPG key +instead of a password. .B .IP "lock" @@ -60,7 +61,8 @@ option can be used to specify the cipher specification: default is If you are looking for something exotic, also try "serpent-xts-plain64". More options may be found in cryptsetup(8) and Linux documentation. This operation requires root privileges to loopback mount, format the tomb (using -LUKS and Ext4), then set the key in its first LUKS slot. +LUKS and Ext4), then set the key in its first LUKS slot. Use the \fI-r\fR +option to lock the tomb using a GPG key. .B .IP "open" @@ -70,7 +72,8 @@ which can also be an \fIjpeg image\fR (see indicate the \fImountpoint\fR where the tomb should be made accessible, else the tomb is mounted in a directory inside /media (if not available it uses /run/media/$USER). The option \fI-o\fR can be -used to pass mount(8) options (default: rw,noatime,nodev). +used to pass mount(8) options (default: rw,noatime,nodev). Use the +\fI-r\fR option to open the tomb using a GPG key. .B .IP "list" @@ -123,7 +126,8 @@ Changes the password protecting a key file specified using its content will be decoded and reencoded using the new one. This action can't be forced if the current password is not known. If the key file is broken (missing headers) this function also attempts its -recovery. +recovery. Use the \fI-r\fR option to unlock the tomb using your old +GPG key and the \fI-R\fR option to provide the new GPG key. .B .IP "setkey" @@ -131,7 +135,8 @@ Changes the key file that locks a tomb, substituting the old one with a new one. Both the old and the new key files are needed for this operation and their passwords must be known. The new key must be specified using the \fI-k\fR option, the first argument should be the old -key and the second and last argument the tomb file. +key and the second and last argument the tomb file. Use the \fI-r\fR +option to unlock the tomb with a GPG key. .B .IP "resize" @@ -158,7 +163,8 @@ Hides a tomb key (\fI-k\fR) inside a \fIjpeg image\fR (first argument) using \fIsteganography\fR: the image will change in a way that cannot be noticed by human eye and hardly detected by data analysis. This option is useful to backup tomb keys in unsuspected places; it depends -from the availability of \fIsteghide\fR. +from the availability of \fIsteghide\fR. Use the \fI-r\fR +option to unlock the tomb with a GPG key. .B .IP "exhume" @@ -200,6 +206,21 @@ what you are doing if you force an operation. When digging or resizing a tomb, this option must be used to specify the \fIsize\fR of the new file to be created. Units are megabytes (MiB). .B +.IP "-r \fI[,]\fR" +Tell tomb to use a asymmetric GnuPG key instead of a passphrase to +encrypt a tomb key. \fIgpg_id\fR is the key recipient in your GPG +database, you must hold both the public and the private key. If more +than one recipient is present the --shared flag must be present. +The recipients are separed by a ','. +.B +.IP "-R \fI[,]\fR" +Provide a new set of recipient to encrypt a tomb key. This option is +only used in the \fIpasswd\fR command. +.B +.IP "--shared" +Activate the capability to share a tomb. This flag must be enabled +when using the \fI-r\fR option with more than one recipient. +.B .IP "--kdf \fI\fR" Activate the KDF feature against dictionary attacks when creating a key: forces a delay of \fI\fR times every time this key is @@ -357,6 +378,16 @@ eval $(gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info") In the future it may become mandatory to run gpg-agent when using tomb. +.SH SHARE A TOMB +A tomb key can be encrypted with more than one recipient. Therefore, +a tomb can be shared between different user. The multiple recipients +are given using the \fI-r\fR (or/and \fI-R\fR) option and must be +separated by a coma: \fI,\fR. It is a very sensitive action, and the user +needs to trust all the GPG public keys it is going to share its tomb. +This is why this feature needs to be explicitly activated using in +more the flag \fI--shared\fR. The \fI--shared\fR option can be used +in the tomb commands: \fIforge\fR \fIsetkey\fR and \fIpasswd\fR. + .SH EXAMPLES .IP \(bu From 528140738a26991b55186c856cea22cf580df38a Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 9 Feb 2017 18:57:34 +0000 Subject: [PATCH 23/25] Add -g/--gpgkey option to tell tomb to use GPG key to encrypt a tomb key Option -r is now only used to provide the recipient Option -R removed, the new recipient can be given by the -r option. --- extras/test/runtests | 37 ++++++++++++++--------------- tomb | 56 ++++++++++++++++++++------------------------ 2 files changed, 43 insertions(+), 50 deletions(-) mode change 100755 => 100644 tomb diff --git a/extras/test/runtests b/extras/test/runtests index c25ec23..4847f45 100755 --- a/extras/test/runtests +++ b/extras/test/runtests @@ -122,15 +122,15 @@ test-tomb-recip() { { test $? = 0 } && { results+=(recip-dig SUCCESS) } notice "Testing tomb with recipient creation: forge" - tt forge $tomb_key -r $gpgid_1 --ignore-swap --unsafe --use-urandom + tt forge $tomb_key -g -r $gpgid_1 --ignore-swap --unsafe --use-urandom { test $? = 0 } && { results+=(recip-forge SUCCESS) } notice "Testing tomb with recipient creation: lock" - tt lock $tomb -k $tomb_key -r $gpgid_1 --ignore-swap --unsafe + tt lock $tomb -k $tomb_key -g -r $gpgid_1 --ignore-swap --unsafe { test $? = 0 } && { results+=(recip-lock SUCCESS) } notice "Testing tomb with recipient opening: open" - tt open $tomb -k $tomb_key -r $gpgid_1 + tt open $tomb -k $tomb_key -g { test $? = 0 } && { results+=(recip-open SUCCESS) } notice "Testing tomb with recipient closing: close" @@ -144,7 +144,7 @@ test-tomb-recip() { sudo rm -f /tmp/recip.steg.key tt --unsafe --tomb-pwd ${dummypass} bury -k /tmp/recip.tomb.key \ - /tmp/recip.jpg -r "$gpgid_1" + /tmp/recip.jpg -g -r "$gpgid_1" { test $? = 0 } && { results+=(recip-stgin SUCCESS) } tt --unsafe --tomb-pwd ${dummypass} exhume -k /tmp/recip.steg.key \ @@ -152,40 +152,40 @@ test-tomb-recip() { { test $? = 0 } && { results+=(recip-stgout SUCCESS) } tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/recip.steg.key \ - /tmp/recip.tomb -r "$gpgid_1" + /tmp/recip.tomb -g { test $? = 0 } && { results+=(recip-stgopen SUCCESS) } ${T} close recip notice "test using open -k image.jpeg" tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/recip.jpg \ - /tmp/recip.tomb -r "$gpgid_1" + /tmp/recip.tomb -g { test $? = 0 } && { results+=(recip-stgimpl SUCCESS) } tt close recip } notice "Testing tomb with recipient changing gpg key: passwd" res=0 - tt passwd -k $tomb_key -r $gpgid_1 -R $gpgid_2 + tt passwd -k $tomb_key -g -r $gpgid_2 { test $? = 0 } || { res=1 } - tt open $tomb -k $tomb_key -r $gpgid_2 + tt open $tomb -k $tomb_key -g { test $? = 0 } || { res=1 } tt close recip { test $? = 0 } || { res=1 } { test $res = 0 } && { results+=(recip-passwd SUCCESS) } notice "Testing tomb with recipient resizing a tomb: resize" - tt resize -s 30 $tomb -k $tomb_key -r $gpgid_2 + tt resize -s 30 $tomb -k $tomb_key -g -r $gpgid_2 { test $? = 0 } && { results+=(recip-resize SUCCESS) } notice "Testing tomb with recipient setting a new key: setkey" sudo rm -f /tmp/new.recip.tomb.key res=0 - tt forge /tmp/new.recip.tomb.key -r $gpgid_2 \ + tt forge /tmp/new.recip.tomb.key -g -r $gpgid_2 \ --ignore-swap --unsafe --use-urandom { test $? = 0 } || { res=1 } - tt setkey -k /tmp/new.recip.tomb.key $tomb_key $tomb -r $gpgid_2 + tt setkey -k /tmp/new.recip.tomb.key $tomb_key $tomb -g -r $gpgid_2 { test $? = 0 } || { res=1 } - tt open -k /tmp/new.recip.tomb.key $tomb -r $gpgid_2 + tt open -k /tmp/new.recip.tomb.key $tomb -g { test $? = 0 } || { res=1 } { test $res = 0 } && { results+=(recip-setkey SUCCESS) } tt close recip @@ -200,31 +200,30 @@ test-tomb-shared() { res=0 tt dig -s 20 /tmp/shared.tomb { test $? = 0 } || { res=1 } - tt forge /tmp/shared.tomb.key -r $gpgid_1,$gpgid_2 --shared \ + tt forge /tmp/shared.tomb.key -g -r $gpgid_1,$gpgid_2 --shared \ --ignore-swap --unsafe --use-urandom { test $? = 0 } || { res=1 } tt lock /tmp/shared.tomb -k /tmp/shared.tomb.key \ - --ignore-swap --unsafe -r $gpgid_1 + --ignore-swap --unsafe -g -r $gpgid_1 { test $? = 0 } || { res=1 } - tt open /tmp/shared.tomb -k /tmp/shared.tomb.key -r $gpgid_1 + tt open /tmp/shared.tomb -k /tmp/shared.tomb.key -g { test $? = 0 } || { res=1 } tt close shared { test $? = 0 } || { res=1 } { test $res = 0 } && { results+=(shared SUCCESS) } notice "Testing changing recipients on a shared Tomb" - tt passwd -k /tmp/shared.tomb.key -r $gpgid_1,$gpgid_2 \ - -R $gpgid_2,$gpgid_1 --shared + tt passwd -k /tmp/shared.tomb.key -g -r $gpgid_2,$gpgid_1 --shared { test $? = 0 } && { results+=(shared-passwd SUCCESS) } notice "Testing setkey on a shared Tomb" rm -f /tmp/new.shared.tomb.key res=0 - tt forge /tmp/new.shared.tomb.key -r $gpgid_1,$gpgid_2 --shared\ + tt forge /tmp/new.shared.tomb.key -g -r $gpgid_1,$gpgid_2 --shared\ --ignore-swap --unsafe --use-urandom { test $? = 0 } || { res=1 } tt setkey -k /tmp/new.shared.tomb.key /tmp/shared.tomb.key /tmp/shared.tomb \ - -r $gpgid_2,$gpgid_1 --shared + -g -r $gpgid_2,$gpgid_1 --shared { test $? = 0 } || { res=1 } { test $res = 0 } && { results+=(shared-setkey SUCCESS) } } diff --git a/tomb b/tomb old mode 100755 new mode 100644 index 1732ab1..bb7b111 --- a/tomb +++ b/tomb @@ -651,8 +651,8 @@ usage() { _print " -n don't process the hooks found in tomb" _print " -o options passed to commands: open, lock, forge (see man)" _print " -f force operation (i.e. even if swap is active)" - _print " -r GnuPG users IDs to encrypt a tomb key (separated by ,)" - _print " -R new recipients to use (separated by ,)" + _print " -g use a GnuPG key to encrypt a tomb key" + _print " -r provide GnuPG recipients (separated by coma)" _print " --shared active sharing feature" [[ $KDF == 1 ]] && { _print " --kdf forge keys armored against dictionary attacks" @@ -939,7 +939,7 @@ _load_key() { [[ -z $keyfile ]] && { _failure "This operation requires a key file to be specified using the -k option." } - if option_is_set -r; then + if option_is_set -g; then _verbose "load_key key encrypted with a GnuPG Key" _message "Key encrypted with a GnuPG Key" TOMBKEYFILE=$keyfile @@ -993,12 +993,7 @@ gpg_decrypt() { typeset -a gpgopt gpgpopt=(--passphrase-fd 0) - { option_is_set -r } && { - typeset -a recipients - recipients=(${(s:,:)$(option_value -r)}) - { ! is_valid_recipients $recipients } && { ! option_is_set -f } && { - _failure "You set an invalid GPG ID." - } + { option_is_set -g } && { gpgpass="$TOMBKEY" gpgpopt=() } @@ -1064,7 +1059,7 @@ get_lukskey() { # key needs to be exhumed from an image elif [[ -r $TOMBKEYFILE && $(file $TOMBKEYFILE) =~ "JP.G" ]]; then - if option_is_set -r; then + if option_is_set -g; then # When using a GPG key, the tomb key is buried using a steganography password if option_is_set --tomb-pwd; then _password="`option_value --tomb-pwd`" @@ -1102,7 +1097,7 @@ ask_key_password() { _verbose "no password needed, using secret bytes from stdin" return 0 } - if option_is_set -r; then + if option_is_set -g; then _verbose "no password needed, using GPG key" get_lukskey return $? @@ -1164,7 +1159,7 @@ change_passwd() { _check_swap # Ensure swap is secure, if any _load_key # Try loading key from option -k and set TOMBKEYFILE - { option_is_set -r } && { + { option_is_set -g } && { _message "Commanded to change GnuPG key for tomb key ::1 key::" $TOMBKEYFILE } || { _message "Commanded to change password for tomb key ::1 key::" $TOMBKEYFILE @@ -1182,7 +1177,7 @@ change_passwd() { fi [[ $? == 0 ]] || _failure "No valid password supplied." - { option_is_set -r } && { + { option_is_set -g } && { _success "Changing GnuPG key for ::1 key file::" $TOMBKEYFILE } || { _success "Changing password for ::1 key file::" $TOMBKEYFILE @@ -1203,7 +1198,7 @@ change_passwd() { # Copy the new key as the original keyfile name cp -f "${tmpnewkey}" $TOMBKEYFILE - { option_is_set -r } && { + { option_is_set -g } && { _success "Your GnuPG key was successfully changed" } || { _success "Your passphrase was successfully updated." @@ -1226,14 +1221,13 @@ gen_key() { tombpass="" tombpasstmp="" - { option_is_set -r } && { - typeset -a recipients - { option_is_set -R } && { - recipients=(${(s:,:)$(option_value -R)}) - } || { - recipients=(${(s:,:)$(option_value -r)}) + { option_is_set -g } && { + { option_is_set -r } || { + _failure "A GPG recipient needs to be specified using -r." } - + + typeset -a recipients + recipients=(${(s:,:)$(option_value -r)}) [ "${#recipients}" -gt 1 ] && { if option_is_set --shared; then _warning "You are going to encrypt a tomb key with ${#recipients} recipients." @@ -1370,7 +1364,7 @@ bury_key() { } _success "Encoding key ::1 tomb key:: inside image ::2 image file::" $TOMBKEY $imagefile - { option_is_set -r } && { + { option_is_set -g } && { _message "Using GnuPG Key ID" } || { _message "Please confirm the key password for the encoding" @@ -1386,7 +1380,7 @@ bury_key() { # if using a GPG key, we test if the user can decrypt the tomb # with its key and we ask for a steganography password. - { option_is_set --tomb-pwd } && { ! option_is_set -r } && { + { option_is_set --tomb-pwd } && { ! option_is_set -g } && { local tombpwd="`option_value --tomb-pwd`" _verbose "tomb-pwd = ::1 tomb pass::" $tombpwd ask_key_password "$tombpwd" @@ -1397,10 +1391,10 @@ bury_key() { _warning "Wrong password/GnuPG ID supplied." _failure "You shall not bury a key whose password is unknown to you." } - if option_is_set -r && option_is_set --tomb-pwd; then + if option_is_set -g && option_is_set --tomb-pwd; then TOMBPASSWORD="`option_value --tomb-pwd`" _verbose "tomb-pwd = ::1 tomb pass::" $TOMBPASSWORD - elif option_is_set -r; then + elif option_is_set -g; then tombpass="" tombpasstmp="" while true; do @@ -1663,7 +1657,7 @@ forge_key() { _message "Commanded to forge key ::1 key:: with cipher algorithm ::2 algorithm::" \ $destkey $algo - [[ $KDF == 1 ]] && { ! option_is_set -r } && { + [[ $KDF == 1 ]] && { ! option_is_set -g } && { _message "Using KDF to protect the key password (`option_value --kdf` rounds)" } @@ -1686,7 +1680,7 @@ forge_key() { # Here the global variable TOMBSECRET contains the naked secret - { option_is_set -r } && { + { option_is_set -g } && { { option_is_set --shared } && { _success "Using GnuPG keys to encrypt and share your key: ::1 tomb key::" $TOMBKEYFILE } || { @@ -1970,7 +1964,7 @@ mount_tomb() { # take the name only, strip extensions _verbose "Tomb name: ::1 tomb name:: (to be engraved)" $TOMBNAME - { option_is_set --tomb-pwd } && { ! option_is_set -r } && { + { option_is_set --tomb-pwd } && { ! option_is_set -g } && { tomb_pwd="`option_value --tomb-pwd`" _verbose "tomb-pwd = ::1 tomb pass::" $tomb_pwd ask_key_password "$tomb_pwd" @@ -2739,10 +2733,10 @@ main() { # can only use the non-abbreviated long-option version like: # -force and NOT -f # - main_opts=(q -quiet=q D -debug=D h -help=h v -version=v f -force=f -tmp: U: G: T: -no-color -unsafe) + main_opts=(q -quiet=q D -debug=D h -help=h v -version=v f -force=f -tmp: U: G: T: -no-color -unsafe g -gpgkey=g) subcommands_opts[__default]="" # -o in open and mount is used to pass alternate mount options - subcommands_opts[open]="n -nohook=n k: -kdf: o: -ignore-swap -tomb-pwd: r: " + subcommands_opts[open]="n -nohook=n k: -kdf: o: -ignore-swap -tomb-pwd: " subcommands_opts[mount]=${subcommands_opts[open]} subcommands_opts[create]="" # deprecated, will issue warning @@ -2754,7 +2748,7 @@ main() { subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: -shared " subcommands_opts[engrave]="k: " - subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: R: -shared " + subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: -shared " subcommands_opts[close]="" subcommands_opts[help]="" subcommands_opts[slam]="" From c63fcf2730507ad38f0fcb557cace64099310c17 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 9 Feb 2017 19:18:02 +0000 Subject: [PATCH 24/25] Fix is_valid_recipients private key detection --- tomb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tomb b/tomb index bb7b111..ac920be 100644 --- a/tomb +++ b/tomb @@ -845,7 +845,7 @@ is_valid_recipients() { done # At least one private key must be present - for gpg_id in $recipients; do + for gpg_id in ${recipients[@]}; do gpg --list-secret-keys "$gpg_id" &> /dev/null [[ $? = 0 ]] && { return 0 From 4a7019715f119d0a1ec2c79e13c548b9244988d7 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 9 Feb 2017 20:59:10 +0000 Subject: [PATCH 25/25] Use --hidden-recipient by default instead of --recipient. Due to the hidden-recipient, GPG will try all the available keys. User can speed up this process providing the recipent using the -r option. Therefore, 'tomb open' optionaly support the -r option. --- tomb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) mode change 100644 => 100755 tomb diff --git a/tomb b/tomb old mode 100644 new mode 100755 index ac920be..8f171d5 --- a/tomb +++ b/tomb @@ -858,11 +858,12 @@ is_valid_recipients() { # $@ is the list of all the recipient used to encrypt a tomb key # Print the recipient arg to be used in gpg. _recipients_arg() { + local arg="$1"; shift typeset -a recipients recipients=($@) for gpg_id in ${recipients[@]}; do - print -R -n "--recipient $gpg_id " + print -R -n "$arg $gpg_id " done return 0 } @@ -996,6 +997,16 @@ gpg_decrypt() { { option_is_set -g } && { gpgpass="$TOMBKEY" gpgpopt=() + + # GPG option '--try-secret-key' exist since GPG 2.1 + { option_is_set -r } && [[ $gpgver =~ "2.1." ]] && { + typeset -a recipients + recipients=(${(s:,:)$(option_value -r)}) + { ! is_valid_recipients $recipients } && { + _failure "You set an invalid GPG ID." + } + gpgpopt=(`_recipients_arg "--try-secret-key" $recipients`) + } } [[ $gpgver == "1.4.11" ]] && { @@ -1209,7 +1220,7 @@ change_passwd() { # takes care to encrypt a key -# honored options: --kdf --tomb-pwd -o -r -R +# honored options: --kdf --tomb-pwd -o -g -r gen_key() { # $1 the password to use; if not set ask user # -o is the --cipher-algo to use (string taken by GnuPG) @@ -1247,7 +1258,7 @@ gen_key() { # Set gpg inputs and options gpgpass="$TOMBSECRET" - gpgopt=(--encrypt `_recipients_arg $recipients`) + gpgopt=(--encrypt `_recipients_arg "--hidden-recipient" $recipients`) opt='' } || { if [ "$1" = "" ]; then @@ -2736,7 +2747,7 @@ main() { main_opts=(q -quiet=q D -debug=D h -help=h v -version=v f -force=f -tmp: U: G: T: -no-color -unsafe g -gpgkey=g) subcommands_opts[__default]="" # -o in open and mount is used to pass alternate mount options - subcommands_opts[open]="n -nohook=n k: -kdf: o: -ignore-swap -tomb-pwd: " + subcommands_opts[open]="n -nohook=n k: -kdf: o: -ignore-swap -tomb-pwd: r: " subcommands_opts[mount]=${subcommands_opts[open]} subcommands_opts[create]="" # deprecated, will issue warning