Skip to content

Unix: try cached passwords only with the KDF they opened a volume with - #1972

Open
flatstik wants to merge 3 commits into
veracrypt:masterfrom
flatstik:fix-1969-cache-kdf
Open

flatstik wants to merge 3 commits into
veracrypt:masterfrom
flatstik:fix-1969-cache-kdf

Conversation

@flatstik

Copy link
Copy Markdown
Contributor

Linux and macOS part of #1969 (see #1815 and #1831 for the same cost path).

When a volume is mounted with an empty password, every cached password is tried with the selected KDF, or with autodetection, which also tries Argon2. Above PIM 31, Argon2 uses 1 GiB and its passes grow with the PIM, so each cached password that does not belong to the volume took minutes.

The cache now keeps the KDF of the mounted volume with the password, and with autodetection each cached password is tried only with that KDF. A password used with several KDFs gets an entry for each. If the KDF is unknown, autodetection is used as before. An explicitly selected KDF is still used for all cached passwords.

The Windows driver cache (src/Common/Cache.c) has the same problem and is not changed here; I cannot test Windows or macOS.

Tested with three volumes (SHA-512, A and C share a password, B has another), mounted as favorites in one run with password caching, typing only the passwords of A and B:

@idrassi idrassi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for investigating this and putting together a reproducible case. I have noted inline the issues I see.

Before revising the implementation, I would like us to settle what should happen when the quick attempts don't find a match. Preserving full autodetection means an unknown Argon2 volume may still need an expensive attempt. Simply adding a full retry at the end would bring back the original worst case. We need a deliberate choice about when to ask the user for input and when to continue searching.

Comment thread src/Core/Unix/CoreServiceProxy.h Outdated
Comment thread src/Volume/VolumePasswordCache.cpp
Comment thread src/Core/Unix/CoreServiceProxy.h Outdated
Comment thread Tests/test_password_cache_kdf.py
@flatstik

Copy link
Copy Markdown
Contributor Author

Thanks for the review. The test now dismounts only its own volumes and keeps its files if that fails (4ef4ad1); the implementation is unchanged until we settle the behaviour. A proposal:

  1. The remembered KDF only orders the attempts. Every cached password is still tried with all PBKDF2 KDFs (so a password cached from a SHA-512 volume still opens a SHA-256 volume), with derivation failures handled as in autodetection rather than through an explicit Kdf.
  2. First pass: all cached passwords with the PBKDF2 KDFs only, each with its remembered KDF first; entries learned with a PBKDF2 KDF before entries learned with Argon2.
  3. Argon2 is attempted only for cached passwords whose entry was learned with Argon2 (at most one entry per password and KDF, without moving an Argon2 association ahead of a PBKDF2 one for the same password).
  4. If nothing matches, the user is asked for the password instead of trying Argon2 with the remaining cached passwords. An Argon2 volume whose password is cached only from PBKDF2 volumes then needs the password once, after which its entry knows Argon2.

This bounds the cost of a cache miss to PBKDF2 derivations, plus Argon2 only for passwords that have been used with Argon2. Excluding Argon2 from an autodetection attempt needs a new mount option for the core service; I would add it like MountVolumeRequestV2, so that an older service rejects the request instead of ignoring the option.

Would this be acceptable, or would you rather keep full autodetection for some case in step 4 (for example the most recent entry)?

@flatstik

Copy link
Copy Markdown
Contributor Author

Updated in 24a3019, which settles the question in the code rather than through a protocol change:

  • With autodetection, each cached password is tried with the KDF it opened a volume with, then with the other PBKDF2 KDFs. Argon2 is tried only for passwords that opened an Argon2 volume, or a volume with an unknown KDF, through autodetection.
  • If nothing matches, the user is asked for the password instead of trying Argon2 with every cached password. An Argon2 volume whose password is cached only from PBKDF2 volumes needs it once; afterwards its entry is known as Argon2.
  • No new mount option is needed, contrary to what I wrote above: the attempts are made by the proxy, with an explicit PBKDF2 KDF or with autodetection.

The extra PBKDF2 attempts are made one at a time, so a cache miss costs more than before this PR's first version: in the test (PIM 100, three favorites) about 10 s instead of 2 s, against 173 s on master with Argon2. https://github.com/flatstik/VeraCrypt/actions/runs/40772767313

When a volume is mounted with an empty password, every cached password
is tried with the selected KDF, or with autodetection, which tries all
KDFs including Argon2. Argon2 uses up to 1 GiB, and above PIM 31 its
passes grow with the PIM, so with a large PIM every cached password that
does not belong to the volume took minutes, as with favorites in veracrypt#1815.

Cache the KDF of the mounted volume together with the password, and with
autodetection try each cached password only with that KDF. A password
used with several KDFs gets an entry for each. If the KDF is unknown,
autodetection is used as before.

The Windows driver cache (src/Common/Cache.c) has the same problem
(veracrypt#1969) and is not changed here.
Trying a cached password only with the KDF that it last opened a volume
with stopped it from opening a volume that uses another KDF until the
password was typed again, which matters most for keyfile contents. With
an explicit KDF, an Argon2 derivation failure also ended the search
instead of being skipped as in autodetection.

With autodetection, each cached password is now tried with the KDF it
opened a volume with, then with the other PBKDF2 KDFs, which are cheap.
Argon2 is tried only for passwords that opened an Argon2 volume, or a
volume whose KDF is unknown, through autodetection so that its handling
of derivation failures is kept. Passwords associated with PBKDF2 come
first, so an Argon2 association no longer delays them. If nothing
matches, the user is asked for the password instead of trying Argon2
with every cached password: an Argon2 volume whose password is cached
only from PBKDF2 volumes needs it once, and is then known as Argon2.
Tests/test_password_cache_kdf.py mounts three favorites with password
caching and a PIM of 100, and checks that a cached password that does
not belong to a volume is not tried with Argon2, and that a matching
cached password still mounts a volume without a prompt.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants