Merge pull request #244 from roddhjav/gnupg-key-support

GnuPG Key Support
This commit is contained in:
Jaromil 2017-02-20 22:09:48 +01:00 committed by GitHub
commit e37982d114
7 changed files with 454 additions and 108 deletions

View File

@ -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<gpg_id>[,<gpg_id2>]\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<gpg_id>[,<gpg_id2>]\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<itertime>\fR"
Activate the KDF feature against dictionary attacks when creating a
key: forces a delay of \fI<itertime>\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

1
extras/test/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
gnupg/

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -56,12 +56,24 @@ command -v qrencode > /dev/null || QRENCODE=0
typeset -A results
tests=(dig forge lock badpass open close openro 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 recip-passwd recip-resize
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) }
{ test $STEGHIDE = 1 } && { tests+=(stgin stgout stgopen stgpipe stgimpl
recip-stgin recip-stgout recip-stgopen recip-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 +112,124 @@ 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 -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 -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 -g
{ test $? = 0 } && { results+=(recip-open SUCCESS) }
notice "Testing tomb with recipient closing: close"
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
tt --unsafe --tomb-pwd ${dummypass} bury -k /tmp/recip.tomb.key \
/tmp/recip.jpg -g -r "$gpgid_1"
{ test $? = 0 } && { results+=(recip-stgin SUCCESS) }
tt --unsafe --tomb-pwd ${dummypass} exhume -k /tmp/recip.steg.key \
/tmp/recip.jpg
{ test $? = 0 } && { results+=(recip-stgout SUCCESS) }
tt --unsafe --tomb-pwd ${dummypass} open -k /tmp/recip.steg.key \
/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 -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 -g -r $gpgid_2
{ test $? = 0 } || { res=1 }
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 -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 -g -r $gpgid_2 \
--ignore-swap --unsafe --use-urandom
{ test $? = 0 } || { res=1 }
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 -g
{ test $? = 0 } || { res=1 }
{ test $res = 0 } && { results+=(recip-setkey SUCCESS) }
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 -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 -g -r $gpgid_1
{ test $? = 0 } || { res=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 -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 -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 \
-g -r $gpgid_2,$gpgid_1 --shared
{ test $? = 0 } || { res=1 }
{ test $res = 0 } && { results+=(shared-setkey SUCCESS) }
}
test-bind-hooks() {
notice "Testing bind hooks"
@ -233,6 +363,8 @@ 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"
@ -414,11 +546,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"

374
tomb
View File

@ -652,6 +652,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 " -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"
}
@ -828,6 +831,54 @@ _ensure_dependencies() {
# {{{ Key operations
# $@ is the list of all the recipient used to encrypt a tomb key
is_valid_recipients() {
typeset -a recipients
recipients=($@)
_verbose "is_valid_recipients"
# 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() {
local arg="$1"; shift
typeset -a recipients
recipients=($@)
for gpg_id in ${recipients[@]}; do
print -R -n "$arg $gpg_id "
done
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
@ -892,7 +943,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 -g; then
_verbose "load_key key encrypted with a GnuPG Key"
_message "Key encrypted with a GnuPG Key"
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
@ -932,18 +988,34 @@ _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}')
local gpgpass="$1\n$TOMBKEY"
local gpgstatus
local tmpres
local tmpres ret
typeset -a gpgopt
gpgpopt=(--passphrase-fd 0)
{ 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" ]] && {
_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
@ -952,12 +1024,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"
@ -1003,9 +1073,23 @@ get_lukskey() {
# key needs to be exhumed from an image
elif [[ -r $TOMBKEYFILE && $(file $TOMBKEYFILE) =~ "JP.G" ]]; then
exhume_key $TOMBKEYFILE "$_password"
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`"
_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
gpg_decrypt "$_password" # Save decrypted contents into $TOMBSECRET
@ -1027,6 +1111,12 @@ ask_key_password() {
_verbose "no password needed, using secret bytes from stdin"
return 0 }
if option_is_set -g; 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=""
@ -1083,7 +1173,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 -g } && {
_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
@ -1097,7 +1191,11 @@ change_passwd() {
fi
[[ $? == 0 ]] || _failure "No valid password supplied."
_success "Changing password for ::1 key file::" $TOMBKEYFILE
{ option_is_set -g } && {
_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
@ -1114,89 +1212,128 @@ change_passwd() {
# Copy the new key as the original keyfile name
cp -f "${tmpnewkey}" $TOMBKEYFILE
_success "Your passphrase was successfully updated."
{ option_is_set -g } && {
_success "Your GnuPG key was successfully changed"
} || {
_success "Your passphrase was successfully updated."
}
return 0
}
# takes care to encrypt a key
# honored options: --kdf --tomb-pwd -o
# 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)
local algopt="`option_value -o`"
local algo="${algopt:-AES256}"
local gpgpass opt
typeset -a gpgopt
# here user is prompted for key password
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
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"
{ 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."
_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."
}
# Set gpg inputs and options
gpgpass="$TOMBSECRET"
gpgopt=(--encrypt `_recipients_arg "--hidden-recipient" $recipients`)
opt=''
} || {
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
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
# Set gpg inputs and options
gpgpass="${tombpass}\n$TOMBSECRET"
gpgopt=(--passphrase-fd 0 --symmetric)
opt='-n'
}
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
_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"
@ -1241,15 +1378,23 @@ 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 -g } && {
_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
# 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 steganography password.
{ option_is_set --tomb-pwd } && {
{ 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"
@ -1257,9 +1402,38 @@ 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 -g && option_is_set --tomb-pwd; then
TOMBPASSWORD="`option_value --tomb-pwd`"
_verbose "tomb-pwd = ::1 tomb pass::" $TOMBPASSWORD
elif option_is_set -g; 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 '
@ -1452,11 +1626,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
@ -1495,7 +1671,7 @@ forge_key() {
_message "Commanded to forge key ::1 key:: with cipher algorithm ::2 algorithm::" \
$destkey $algo
[[ $KDF == 1 ]] && {
[[ $KDF == 1 ]] && { ! option_is_set -g } && {
_message "Using KDF to protect the key password (`option_value --kdf` rounds)"
}
@ -1518,7 +1694,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 -g } && {
{ 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
@ -1553,7 +1737,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.
@ -1794,7 +1978,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 -g } && {
tomb_pwd="`option_value --tomb-pwd`"
_verbose "tomb-pwd = ::1 tomb pass::" $tomb_pwd
ask_key_password "$tomb_pwd"
@ -2563,22 +2747,22 @@ 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: "
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: -shared "
subcommands_opts[dig]="-ignore-swap s: -size=s "
subcommands_opts[lock]="-ignore-swap k: -kdf: o: -tomb-pwd: "
subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -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: r: -shared "
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: -shared "
subcommands_opts[close]=""
subcommands_opts[help]=""
subcommands_opts[slam]=""
@ -2588,14 +2772,14 @@ 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]=""
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]=""