Compare commits

...

3 Commits

Author SHA1 Message Date
Milan Broz
8b3eb37046 tests: Add new trcypt images for Argon2 PBKDF.
Also modify test to use longer PIM password, as VeraCrypt
requires at least 20 character password with lower PIM values.
2025-11-20 19:07:47 +01:00
Milan Broz
e9bd43a8fa tests: Add PBKDF check for crypto helper.
This can be used in FIPS mode to skip Argon2 if not available.
2025-11-20 19:07:47 +01:00
Milan Broz
8311a8a903 tcrypt: Support Argon2id PBKDF introduced in VeraCrypt 1.26.27.
It uses Argon2id only, with maximal memory cost 1024 MiB.
Parallel cost is always 1.

Default parameters (no PIM) is 416 MiB, 6 iterations (same as PIM 12).

In PIM mode, iterations and memory costs are calculated according
to the specific formula. Memory cost is always limited as above.
2025-11-20 19:07:47 +01:00
4 changed files with 137 additions and 55 deletions

View File

@@ -22,25 +22,28 @@ static const struct {
const char *name;
const char *hash;
unsigned int iterations;
uint32_t parallel_cost;
uint32_t memory_cost;
uint32_t veracrypt_pim_const;
uint32_t veracrypt_pim_mult;
} tcrypt_kdf[] = {
{ false, false, "pbkdf2", "ripemd160", 2000, 0, 0 },
{ false, false, "pbkdf2", "ripemd160", 1000, 0, 0 },
{ false, false, "pbkdf2", "sha512", 1000, 0, 0 },
{ false, false, "pbkdf2", "whirlpool", 1000, 0, 0 },
{ true, false, "pbkdf2", "sha1", 2000, 0, 0 },
{ false, true, "pbkdf2", "sha512", 500000, 15000, 1000 },
{ false, true, "pbkdf2", "whirlpool", 500000, 15000, 1000 },
{ false, true, "pbkdf2", "sha256", 500000, 15000, 1000 }, // VeraCrypt 1.0f
{ false, true, "pbkdf2", "sha256", 200000, 0, 2048 }, // boot only
{ false, true, "pbkdf2", "blake2s-256", 500000, 15000, 1000 }, // VeraCrypt 1.26.2
{ false, true, "pbkdf2", "blake2s-256", 200000, 0, 2048 }, // boot only
{ false, true, "pbkdf2", "ripemd160", 655331, 15000, 1000 },
{ false, true, "pbkdf2", "ripemd160", 327661, 0, 2048 }, // boot only
{ false, true, "pbkdf2", "stribog512",500000, 15000, 1000 },
// { false, true, "pbkdf2", "stribog512",200000, 0, 2048 }, // boot only
{ false, false, NULL, NULL, 0, 0, 0 }
{ false, false, "pbkdf2", "ripemd160", 2000, 0, 0, 0, 0 },
{ false, false, "pbkdf2", "ripemd160", 1000, 0, 0, 0, 0 },
{ false, false, "pbkdf2", "sha512", 1000, 0, 0, 0, 0 },
{ false, false, "pbkdf2", "whirlpool", 1000, 0, 0, 0, 0 },
{ true, false, "pbkdf2", "sha1", 2000, 0, 0, 0, 0 },
{ false, true, "pbkdf2", "sha512", 500000, 0, 0, 15000, 1000 },
{ false, true, "pbkdf2", "whirlpool", 500000, 0, 0, 15000, 1000 },
{ false, true, "pbkdf2", "sha256", 500000, 0, 0, 15000, 1000 }, // VeraCrypt 1.0f
{ false, true, "pbkdf2", "sha256", 200000, 0, 0, 0, 2048 }, // boot only
{ false, true, "argon2id", NULL, 6, 1, 425984, 0, 0 }, // VeraCrypt 1.26.27
{ false, true, "pbkdf2", "blake2s-256", 500000, 0, 0, 15000, 1000 }, // VeraCrypt 1.26.2
{ false, true, "pbkdf2", "blake2s-256", 200000, 0, 0, 0, 2048 }, // boot only
{ false, true, "pbkdf2", "ripemd160", 655331, 0, 0, 15000, 1000 },
{ false, true, "pbkdf2", "ripemd160", 327661, 0, 0, 0, 2048 }, // boot only
{ false, true, "pbkdf2", "stribog512", 500000, 0, 0, 15000, 1000 },
// { false, true, "pbkdf2", "stribog512", 200000, 0, 0, 0, 2048 }, // boot only
{ false, false, NULL, NULL, 0, 0, 0, 0, 0 }
};
struct tcrypt_alg {
@@ -239,7 +242,8 @@ static int TCRYPT_hdr_from_disk(struct crypt_device *cd,
/* Set params */
params->passphrase = NULL;
params->passphrase_size = 0;
params->hash_name = tcrypt_kdf[kdf_index].hash;
/* For Argon2, overload hash_name */
params->hash_name = tcrypt_kdf[kdf_index].hash ?: tcrypt_kdf[kdf_index].name;
params->key_size = tcrypt_cipher[cipher_index].chain_key_size;
params->cipher = tcrypt_cipher[cipher_index].long_name;
params->mode = tcrypt_cipher[cipher_index].mode;
@@ -522,7 +526,8 @@ static int TCRYPT_init_hdr(struct crypt_device *cd,
unsigned char pwd[VCRYPT_KEY_POOL_LEN] = {};
size_t passphrase_size, max_passphrase_size;
char *key;
unsigned int i, skipped = 0, iterations;
unsigned int i, skipped = 0;
uint32_t iterations, memory;
int r = -EPERM, keyfiles_pool_length;
if (posix_memalign((void*)&key, crypt_getpagesize(), TCRYPT_HDR_KEY_LEN))
@@ -561,7 +566,9 @@ static int TCRYPT_init_hdr(struct crypt_device *cd,
pwd[i] += params->passphrase[i];
for (i = 0; tcrypt_kdf[i].name; i++) {
if (params->hash_name && !strstr(tcrypt_kdf[i].hash, params->hash_name))
if (params->hash_name && tcrypt_kdf[i].hash && !strstr(tcrypt_kdf[i].hash, params->hash_name))
continue;
if (params->hash_name && !tcrypt_kdf[i].hash && !strstr(tcrypt_kdf[i].name, params->hash_name))
continue;
if (!(params->flags & CRYPT_TCRYPT_LEGACY_MODES) && tcrypt_kdf[i].legacy)
continue;
@@ -572,19 +579,36 @@ static int TCRYPT_init_hdr(struct crypt_device *cd,
if (!tcrypt_kdf[i].veracrypt)
continue;
/* adjust iterations to given PIM cmdline parameter */
iterations = tcrypt_kdf[i].veracrypt_pim_const +
(tcrypt_kdf[i].veracrypt_pim_mult * params->veracrypt_pim);
} else
if (!strcmp(tcrypt_kdf[i].name, "argon2id")) {
if (params->veracrypt_pim <= 31) {
iterations = (params->veracrypt_pim - 1) / 3 + 3;
memory = 1024 * (64 + (params->veracrypt_pim - 1) * 32);
} else{
iterations = params->veracrypt_pim - 18;
memory = 1024 * 1024;
}
} else {
iterations = tcrypt_kdf[i].veracrypt_pim_const +
(tcrypt_kdf[i].veracrypt_pim_mult * params->veracrypt_pim);
memory = 0;
}
} else {
iterations = tcrypt_kdf[i].iterations;
memory = tcrypt_kdf[i].memory_cost;
}
/* Derive header key */
log_dbg(cd, "TCRYPT: trying KDF: %s-%s-%d%s.",
tcrypt_kdf[i].name, tcrypt_kdf[i].hash, tcrypt_kdf[i].iterations,
params->veracrypt_pim && tcrypt_kdf[i].veracrypt ? "-PIM" : "");
if (!strcmp(tcrypt_kdf[i].name, "argon2id"))
log_dbg(cd, "TCRYPT: trying KDF: %s%s.", tcrypt_kdf[i].name,
params->veracrypt_pim && tcrypt_kdf[i].veracrypt ? "-PIM" : "");
else
log_dbg(cd, "TCRYPT: trying KDF: %s-%s-%d%s.",
tcrypt_kdf[i].name, tcrypt_kdf[i].hash, tcrypt_kdf[i].iterations,
params->veracrypt_pim && tcrypt_kdf[i].veracrypt ? "-PIM" : "");
r = crypt_pbkdf(tcrypt_kdf[i].name, tcrypt_kdf[i].hash,
(char*)pwd, passphrase_size,
hdr->salt, TCRYPT_HDR_SALT_LEN,
key, TCRYPT_HDR_KEY_LEN,
iterations, 0, 0);
iterations, memory, tcrypt_kdf[i].parallel_cost);
if (r < 0) {
log_verbose(cd, _("PBKDF2 hash algorithm %s not available, skipping."),
tcrypt_kdf[i].hash);
@@ -1187,7 +1211,11 @@ int TCRYPT_dump(struct crypt_device *cd,
log_std(cd, "Volume size:\t%" PRIu64 " [bytes]\n", hdr->d.volume_size);
if (hdr->d.hidden_volume_size)
log_std(cd, "Hidden size:\t%" PRIu64 " [bytes]\n", hdr->d.hidden_volume_size);
log_std(cd, "PBKDF2 hash:\t%s\n", params->hash_name);
if (strcmp(params->hash_name, "argon2id")) {
log_std(cd, "PBKDF:\t\tPBKDF2\n");
log_std(cd, "PBKDF2 hash:\t%s\n", params->hash_name);
} else
log_std(cd, "PBKDF:\t\tArgon2id\n");
}
log_std(cd, "Cipher chain:\t%s\n", params->cipher);
log_std(cd, "Cipher mode:\t%s\n", params->mode);

View File

@@ -47,9 +47,36 @@ static int check_hash(const char *hash)
return EXIT_SUCCESS;
}
static int check_pbkdf(const char *pbkdf)
{
const char *hash;
uint32_t iterations, memory, parallel;
char out[32];
if (!strcmp(pbkdf, "pbkdf2")) {
hash = "sha256";
iterations = 1000;
memory = 0;
parallel = 0;
} else if (!strncmp(pbkdf, "argon2", 6)) {
hash = NULL;
iterations = 3;
memory = 256;
parallel = 1;
} else
return EXIT_FAILURE;
if (!crypt_pbkdf(pbkdf, hash, "01234567890abcdef01234567890abcdef", 32,
"11234567890abcdef11234567890abcdef", 32, out, sizeof(out),
iterations, memory, parallel))
return EXIT_SUCCESS;
return EXIT_FAILURE;
}
static void __attribute__((noreturn)) exit_help(bool destroy_backend)
{
printf("Use: crypto_check version | fips_mode | fips_mode_kernel | hash <alg> | cipher <alg> <mode> [key_bits]\n");
printf("Use: crypto_check version | fips_mode | fips_mode_kernel | hash <alg> | cipher <alg> <mode> [key_bits] | pbkdf <alg>\n");
if (destroy_backend)
crypt_backend_destroy();
exit(EXIT_FAILURE);
@@ -92,6 +119,10 @@ int main(int argc, char *argv[])
exit_help(true);
}
r = check_cipher(argv[2], argv[3], ul);
} else if (!strcmp(argv[1], "pbkdf")) {
if (argc != 3)
exit_help(true);
r = check_pbkdf(argv[2]);
}
crypt_backend_destroy();

View File

@@ -9,7 +9,7 @@ MAP=tctst
PASSWORD="aaaaaaaaaaaa"
PASSWORD_HIDDEN="bbbbbbbbbbbb"
PASSWORD_72C="aaaaaaaaaaaabbbbbbbbbbbbccccccccccccddddddddddddeeeeeeeeeeeeffffffffffff"
PIM=1234
PASSWORD_PIM="cccccccccccccccccccc"
LOOP_SYS=""
PART_IMG=tctst-part-img
@@ -77,11 +77,40 @@ test_kdf() # hash img_hash
fi
}
get_HASH_CIPHER() # filename
test_pbkdf() # pbkdf img_hash
{
$CRYPTOCHECK pbkdf $1
if [ $? -ne 0 ] ; then
echo "$1 [N/A]"
IMGS=$(ls $TST_DIR/[tv]c* | grep "$2")
[ -n "$IMGS" ] && rm $IMGS
else
echo "$1 [OK]"
fi
}
get_PARAMS() # filename
{
# speed up the test by limiting options for hash and (first) cipher
HASH=$(echo $file | cut -d'-' -f3)
CIPHER=$(echo $file | cut -d'-' -f5)
if [[ $file =~ vcpim.* ]] ; then
PIM=$(echo $file | sed -r s/.*vcpim_1_\([[:digit:]]+\).*/\\1/)
PIM_OPT="--veracrypt-pim $PIM"
PWD=$PASSWORD_PIM
else
PIM=""
PIM_OPT=""
PWD=$PASSWORD
fi
SYS_OPT=""
if [[ $file =~ sys_.* ]] ; then
SYS_OPT="--tcrypt-system"
else
SYS_OPT=""
fi
}
test_required()
@@ -97,6 +126,8 @@ test_required()
test_kdf whirlpool whirlpool
test_kdf stribog512 stribog
test_pbkdf argon2id argon2id
echo "REQUIRED CIPHERS TEST"
test_one aes cbc 256 cbc-aes
test_one aes lrw 384 lrw-aes
@@ -155,16 +186,12 @@ test_required
echo "HEADER CHECK"
for file in $(ls $TST_DIR/[tv]c_* $TST_DIR/vcpim_* $TST_DIR/sys_[tv]c_*) ; do
echo -n " $file"
PIM_OPT=""
[[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM"
SYS_OPT=""
[[ $file =~ sys_.* ]] && SYS_OPT="--tcrypt-system"
get_HASH_CIPHER $file
echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h $HASH -c $CIPHER $file >/dev/null || fail
get_PARAMS $file
echo $PWD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h $HASH -c $CIPHER $file >/dev/null || fail
if [[ $file =~ .*-sha512-xts-aes$ ]] ; then
echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h sha512 -c aes $file >/dev/null || fail
echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h xxxx $file 2>/dev/null && fail
echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h sha512 -c xxx $file 2>/dev/null && fail
echo $PWD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h sha512 -c aes $file >/dev/null || fail
echo $PWD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h xxxx $file 2>/dev/null && fail
echo $PWD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h sha512 -c xxx $file 2>/dev/null && fail
fi
echo " [OK]"
done
@@ -172,17 +199,15 @@ done
echo "HEADER CHECK (TCRYPT only)"
for file in $(ls $TST_DIR/vc_* $TST_DIR/vcpim_*) ; do
echo -n " $file"
PIM_OPT=""
[[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM"
get_HASH_CIPHER $file
echo $PASSWORD | $CRYPTSETUP tcryptDump --disable-veracrypt $PIM_OPT -h $HASH -c $CIPHER $file >/dev/null 2>&1 && fail
get_PARAMS $file
echo $PWD | $CRYPTSETUP tcryptDump --disable-veracrypt $PIM_OPT -h $HASH -c $CIPHER $file >/dev/null 2>&1 && fail
echo " [OK]"
done
echo "HEADER CHECK (HIDDEN)"
for file in $(ls $TST_DIR/[tv]c_*-hidden) ; do
echo -n " $file (hidden)"
get_HASH_CIPHER $file
get_PARAMS $file
echo $PASSWORD_HIDDEN | $CRYPTSETUP tcryptDump --tcrypt-hidden -h $HASH -c $CIPHER $file >/dev/null || fail
echo " [OK]"
done
@@ -190,10 +215,10 @@ done
echo "HEADER KEYFILES CHECK"
for file in $(ls $TST_DIR/[tv]ck_*) ; do
echo -n " $file"
get_PARAMS $file
PWD=$PASSWORD
[[ $file =~ vck_1_nopw.* ]] && PWD=""
[[ $file =~ vck_1_pw72.* ]] && PWD=$PASSWORD_72C
get_HASH_CIPHER $file
echo $PWD | $CRYPTSETUP tcryptDump -d $TST_DIR/keyfile1 -d $TST_DIR/keyfile2 -h $HASH -c $CIPHER $file >/dev/null || fail
echo " [OK]"
done
@@ -207,10 +232,8 @@ fi
echo "ACTIVATION FS UUID CHECK"
for file in $(ls $TST_DIR/[tv]c_* $TST_DIR/vcpim_*) ; do
echo -n " $file"
PIM_OPT=""
[[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM"
get_HASH_CIPHER $file
out=$(echo $PASSWORD | $CRYPTSETUP tcryptOpen $PIM_OPT -r -h $HASH -c $CIPHER $file $MAP 2>&1)
get_PARAMS $file
out=$(echo $PWD | $CRYPTSETUP tcryptOpen $PIM_OPT -r -h $HASH -c $CIPHER $file $MAP 2>&1)
ret=$?
[ $ret -eq 1 ] && ( echo "$out" | grep -q -e "TCRYPT legacy mode" ) && echo " [N/A]" && continue
[ $ret -eq 1 ] && ( echo "$out" | grep -q -e "TCRYPT compatible mapping" ) && echo " [N/A]" && continue
@@ -241,28 +264,28 @@ for file in $(ls $TST_DIR/sys_[tv]c_*) ; do
LOOP_SYS=""
continue
fi
get_HASH_CIPHER $file
get_PARAMS $file
# map through partition name
echo -n " [PART]"
echo $PASSWORD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER $LOOP_PART $MAP || fail
echo $PWD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER $LOOP_PART $MAP || fail
check_uuid DEAD-BABE
$CRYPTSETUP close $MAP || fail
if [[ $file =~ _part ]]; then
# map through image only (TCRYPT hdr contains partition offset and size)
echo -n "[IMG]"
echo $PASSWORD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER $file $MAP 2>/dev/null || fail
echo $PWD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER $file $MAP 2>/dev/null || fail
check_uuid DEAD-BABE
$CRYPTSETUP close $MAP || fail
# map through full device (TCRYPT hdr contains partition offset and size)
echo -n "[DRIVE]"
echo $PASSWORD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER $LOOP_SYS $MAP || fail
echo $PWD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER $LOOP_SYS $MAP || fail
check_uuid DEAD-BABE
$CRYPTSETUP close $MAP || fail
elif [[ $file =~ _full ]]; then
# map through image + header in real partition (whole system)
dd if=$LOOP_PART of=$PART_IMG bs=1M >/dev/null 2>&1
echo -n "[PART+IMG]"
echo $PASSWORD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER --header $LOOP_PART $PART_IMG $MAP || fail
echo $PWD | $CRYPTSETUP tcryptOpen --tcrypt-system -r -h $HASH -c $CIPHER --header $LOOP_PART $PART_IMG $MAP || fail
check_uuid DEAD-BABE
$CRYPTSETUP close $MAP || fail
rm $PART_IMG
@@ -275,7 +298,7 @@ done
echo "ACTIVATION FS UUID (HIDDEN) CHECK"
for file in $(ls $TST_DIR/[tv]c_*-hidden) ; do
echo -n " $file"
get_HASH_CIPHER $file
get_PARAMS $file
out=$(echo $PASSWORD_HIDDEN | $CRYPTSETUP tcryptOpen -r -h $HASH -c $CIPHER $file $MAP --tcrypt-hidden 2>&1)
ret=$?
[ $ret -eq 1 ] && ( echo "$out" | grep -q -e "TCRYPT legacy mode" ) && echo " [N/A]" && continue

Binary file not shown.