From 8ceeca87692462d9cf207e18eecea59c750bc634 Mon Sep 17 00:00:00 2001 From: Jaromil Date: Sun, 20 Feb 2022 22:05:01 +0100 Subject: [PATCH] KDF support for argon2 memory intensive algorithm (#432) * KDF support for argon2 memory intensive algorithm following many requests, here is support for argon2 KDF to be switched on using --kdftype argon2 (--kdf iterations --kdfmem memory) effective memory required is 2^memory KiB, defaults to 18 (262 MiB) number of iterations are still specified as --kdf argument requires the argon2 reference C implementation from P-H-C also requires tomb-kdb-pbkdf2-gensalt in extras/kdf-keys example usage: tomb forge -k argon.key --kdf 10 --kdftype argon2 * manual updates for argon2 --- doc/tomb.1 | 45 ++++++++++++++++++------------ tomb | 82 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 84 insertions(+), 43 deletions(-) diff --git a/doc/tomb.1 b/doc/tomb.1 index a14a171..8fea536 100644 --- a/doc/tomb.1 +++ b/doc/tomb.1 @@ -39,19 +39,17 @@ with random data, decreasing the tomb's security. .B .IP "forge" -Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to -protect its usage using symmetric encryption. This operation uses -random data from a non-blocking source (/dev/urandom) and it may take -long only in some cases; to switch using a blocking source the -\fI--use-random\fR flag can be used. The \fI-g\fR option switches on -the use of a GPG key instead of a password (asymmetric encryption), -then the \fI-r\fR option indicates the recipient key; more recipient -GPG ids can be indicated (comma separated). The default cipher to -protect the key is AES256, a custom one can be specified using the -\fI-o\fR option, for a list of supported ciphers use \fI-v\fR. For -additional protection against dictionary attacks on keys, the -\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 +Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to protect +its usage using symmetric encryption. This operation uses random data from a +non-blocking source (/dev/urandom) and it may take long only in some cases; to +switch using a blocking source the \fI--use-random\fR flag can be used. The +\fI-g\fR option switches on the use of a GPG key instead of a password +(asymmetric encryption), then the \fI-r\fR option indicates the recipient key; +more recipient GPG ids can be indicated (comma separated). The default cipher +to protect the key is AES256, a custom one can be specified using the \fI-o\fR +option, for a list of supported ciphers use \fI-v\fR. For additional protection +against dictionary attacks on keys, the \fI--kdf\fR option can be used when +forging a key, making sure that the binaries in \fIextras/kdf\fR were compiled and installed on the system. .B @@ -269,11 +267,22 @@ can be one or more GPG key ID, comma separated. All GPG keys must be trusted keys in GPG. .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 -used. The actual time to wait depends on the CPU speed of the -computer where the key is used. Using 5 or 10 is a sane amount for -modern computers, the value is multiplied by 1 million. +Activate the KDF feature against dictionary attacks when creating a key: forces +a delay of \fI\fR times every time this key is used. The actual time +to wait depends on the CPU speed (default) or the RAM size (argon2) of the +computer where the key is used. Using 5 or 10 is a sane amount for modern +computers, the value is multiplied by 1 million. +.B +.IP "--kdftype \fIargon2 | pbkdf2\fR" +Adopt the \fIargon2\fR algorithm for KDF, stressing the RAM capacity rather +than the CPU speed of the computer decrypting the tomb. Requires the +\fIargon2\fR binary by P-H-C to be installed, as packaged by most distros. +Default is \fIpbkdf2\fR. +.B +.IP "--kdfmem \fI\fR" +In case of \fIargon2\fR KDF algorithm, this value specifies the size of RAM +used: it consists of a number which is the elevated power of two in bytes. +Default is 18 which is 262 MiB (2^18 bytes). .B .IP "--sudo \fI\fR" Select a different tool than sudo for privilege escalation. diff --git a/tomb b/tomb index 5be3a9e..0d63ef1 100755 --- a/tomb +++ b/tomb @@ -889,7 +889,7 @@ function _print() { _list_optional_tools() { typeset -a _deps _deps=(gettext dcfldd shred steghide) - _deps+=(resize2fs tomb-kdb-pbkdf2 qrencode swish-e unoconv lsof) + _deps+=(resize2fs tomb-kdb-pbkdf2 argon2 qrencode swish-e unoconv lsof) for d in $_deps; do _print "`which $d`" done @@ -945,6 +945,8 @@ _ensure_dependencies() { command -v resize2fs 1>/dev/null 2>/dev/null || RESIZER=0 # Check for KDF auxiliary tools command -v tomb-kdb-pbkdf2 1>/dev/null 2>/dev/null || KDF=0 + # Check for ARGON2 KDF auxiliary tools + command -v argon2 1>/dev/null 2>/dev/null || ARGON2=0 # Check for Swish-E file content indexer command -v swish-e 1>/dev/null 2>/dev/null || SWISH=0 # Check for QREncode for paper backups of keys @@ -1203,6 +1205,18 @@ get_lukskey() { _verbose "KDF len: $kdf_len" _password=$(tomb-kdb-pbkdf2 $kdf_salt $kdf_ic $kdf_len 2>/dev/null <<<$_password) ;; + + "argon2") + kdf_salt="${firstline[(ws:_:)3]}" + kdf_ic="${firstline[(ws:_:)4]}" + kdf_mem="${firstline[(ws:_:)5]}" + _message "Unlocking KDF key protection (::1 kdf::)" $kdf_hash + _verbose "KDF salt: $kdf_salt" + _verbose "KDF ic: $kdf_ic" + _verbose "KDF mem: $kdf_mem" + _password=$(argon2 $kdf_salt -m $kdf_mem -t $kdf_ic -l 64 -r 2>/dev/null <<<$_password) + ;; + *) _failure "No suitable program for KDF ::1 program::." $pbkdf_hash unset _password @@ -1478,26 +1492,44 @@ gen_key() { # 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}"` + # 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 - header="_KDF_pbkdf2sha1_${pbkdf2_salt}_${pbkdf2_iter}_64\n" + kdftype="`option_value --kdftype`" + kdftype=${kdftype:-pbkdf2} + case ${kdftype} in + pbkdf2) + 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" + ;; + argon2) + _success "Using KDF Argon2" + kdfmem="`option_value --kdfmem`" + kdfmem=${kdfmem:-18} + _message "memory used: 2^::1 kdfmemory::" $kdfmem + kdfsalt=`tomb-kdb-pbkdf2-gensalt` + _message "kdf salt: ::1 kdfsalt::" $kdfsalt + _message "kdf iterations: ::1 kdfiterations::" $itertime + tombpass=`argon2 $kdfsalt -m $kdfmem -t $itertime -l 64 -r <<<"${tombpass}"` + header="_KDF_argon2_${kdfsalt}_${itertime}_${kdfmem}_64\n" + ;; + esac } } print $header >> "$1" @@ -3176,19 +3208,19 @@ 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 -sudo:) 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: R: -sphx-host: -sphx-user: p -preserve-ownership=p" + subcommands_opts[open]="n -nohook=n k: -kdf: -kdftype: -kdfmem: o: -ignore-swap -tomb-pwd: r: R: -sphx-host: -sphx-user: p -preserve-ownership=p" 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-random r: R: -sphx-host: -sphx-user: " + subcommands_opts[forge]="-ignore-swap k: -kdf: -kdftype: -kdfmem: o: -tomb-pwd: -use-random r: R: -sphx-host: -sphx-user: " subcommands_opts[dig]="-ignore-swap s: -size=s " - subcommands_opts[lock]="-ignore-swap k: -kdf: o: -tomb-pwd: r: R: -sphx-host: -sphx-user: -filesystem: " - subcommands_opts[setkey]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: R: -sphx-host: -sphx-user: " + subcommands_opts[lock]="-ignore-swap k: -kdf: -kdftype: -kdfmem: o: -tomb-pwd: r: R: -sphx-host: -sphx-user: -filesystem: " + subcommands_opts[setkey]="k: -ignore-swap -kdf: -kdftype: -kdfmem: -tomb-old-pwd: -tomb-pwd: r: R: -sphx-host: -sphx-user: " subcommands_opts[engrave]="k: " - subcommands_opts[passwd]="k: -ignore-swap -kdf: -tomb-old-pwd: -tomb-pwd: r: R: -sphx-host: -sphx-user: " + subcommands_opts[passwd]="k: -ignore-swap -kdf: -kdftype: -kdfmem: -tomb-old-pwd: -tomb-pwd: r: R: -sphx-host: -sphx-user: " subcommands_opts[close]="" subcommands_opts[help]="" subcommands_opts[slam]=""