diff --git a/doc/html/en/Argon2id.html b/doc/html/en/Argon2id.html index 3dc1717b..6200327e 100644 --- a/doc/html/en/Argon2id.html +++ b/doc/html/en/Argon2id.html @@ -161,7 +161,7 @@ When using Argon2id in VeraCrypt: Algorithm: Argon2id as defined in RFC 9106
Internal hash: BLAKE2b
Salt size: 512 bits (same as PBKDF2-HMAC)
-Output length: Variable, depending on the encryption algorithm (e.g., 256 bits for AES-256, 768 bits for AES-Twofish-Serpent cascade)
+Header KDF output length: Fixed at 1536 bits (192 bytes) for the current VeraCrypt format. The required prefix is used for the selected encryption algorithm (for example, the first 64 bytes for AES (AES-256-XTS)). Third-party implementations must request 192 bytes from Argon2id before selecting the required prefix; requesting only the selected algorithm's key material length produces a different Argon2id output.
Version: Argon2 version 0x13 (19 decimal) diff --git a/doc/html/en/Header Key Derivation.html b/doc/html/en/Header Key Derivation.html index 1b4872c9..926f8829 100644 --- a/doc/html/en/Header Key Derivation.html +++ b/doc/html/en/Header Key Derivation.html @@ -87,6 +87,7 @@ PIM value is given by the user, the number of iterations of the PBKDF2 key d

Argon2id Parameters

When Argon2id is selected as the key derivation function, the PIM value controls both memory and time costs as described in the PIM section. If no PIM is specified, default parameters equivalent to PIM = 12 are used (416 MiB memory, 6 iterations).

+

For Argon2id, VeraCrypt derives a fixed 192 bytes of header key material for the current volume format, independently of the selected encryption algorithm. The selected encryption algorithm then uses the required prefix of that derived output. For example, AES-XTS uses the first 64 bytes. Implementations must request 192 bytes from Argon2id and then select the required prefix; requesting only the selected algorithm's key material length produces a different Argon2id output because Argon2id includes the requested output length in its computation.

diff --git a/src/Common/Crypto.c b/src/Common/Crypto.c index 4b1767c9..3c6a03e2 100644 --- a/src/Common/Crypto.c +++ b/src/Common/Crypto.c @@ -801,7 +801,8 @@ int EAGetLargestKeyForMode (int mode) return key; } -// Returns the maximum number of bytes necessary to be generated by the PBKDF2 (PKCS #5) +// Returns the maximum number of bytes necessary to be generated by PBKDF2 (PKCS #5). +// Argon2id header key material uses the fixed ARGON2_HEADER_KEYDATA_SIZE value. int GetMaxPkcs5OutSize (void) { int size = 32; @@ -1488,4 +1489,3 @@ void VcUnprotectKeys (PCRYPTO_INFO pCryptoInfo, uint64 encID) #endif #endif - diff --git a/src/Common/Crypto.h b/src/Common/Crypto.h index ea0ddb64..b405875a 100644 --- a/src/Common/Crypto.h +++ b/src/Common/Crypto.h @@ -44,6 +44,12 @@ extern "C" { // Size of the volume header area containing concatenated master key(s) and secondary key(s) (XTS mode) #define MASTER_KEYDATA_SIZE 256 +#ifndef VC_DCS_DISABLE_ARGON2 +// VeraCrypt Argon2id header key material size, in bytes, for the current volume format. +// This is intentionally fixed for compatibility and must not depend on GetMaxPkcs5OutSize(). +#define ARGON2_HEADER_KEYDATA_SIZE 192 +#endif + // The first PRF to try when mounting #define FIRST_PRF_ID 1 diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index d73cb551..940f709e 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -6570,7 +6570,7 @@ static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) case ARGON2: /* test with ARGON2 used as the PRF */ - if (derive_key_argon2 ((const unsigned char*) "passphrase-1234567890", 21, (const unsigned char*)tmp_salt, 64, iterations, memoryCost, dk, MASTER_KEYDATA_SIZE, NULL) != 0) + if (derive_key_argon2 ((const unsigned char*) "passphrase-1234567890", 21, (const unsigned char*)tmp_salt, 64, iterations, memoryCost, dk, ARGON2_HEADER_KEYDATA_SIZE, NULL) != 0) goto key_derivation_error; break; } diff --git a/src/Common/EncryptionThreadPool.c b/src/Common/EncryptionThreadPool.c index 9feeda32..ac2f53b6 100644 --- a/src/Common/EncryptionThreadPool.c +++ b/src/Common/EncryptionThreadPool.c @@ -277,7 +277,7 @@ static TC_THREAD_PROC EncryptionThreadProc (void *threadArg) case ARGON2: derivationResult = derive_key_argon2(workItem->KeyDerivation.Password, workItem->KeyDerivation.PasswordLength, workItem->KeyDerivation.Salt, PKCS5_SALT_SIZE, - workItem->KeyDerivation.IterationCount, workItem->KeyDerivation.Memorycost, workItem->KeyDerivation.DerivedKey, GetMaxPkcs5OutSize(), workItem->KeyDerivation.pAbortKeyDerivation); + workItem->KeyDerivation.IterationCount, workItem->KeyDerivation.Memorycost, workItem->KeyDerivation.DerivedKey, ARGON2_HEADER_KEYDATA_SIZE, workItem->KeyDerivation.pAbortKeyDerivation); break; default: diff --git a/src/Common/Volumes.c b/src/Common/Volumes.c index 97fc4de6..495bbc0b 100644 --- a/src/Common/Volumes.c +++ b/src/Common/Volumes.c @@ -448,7 +448,7 @@ KeyReady: ; case ARGON2: { int derivationResult = derive_key_argon2(keyInfo->userKey, keyInfo->keyLength, keyInfo->salt, - PKCS5_SALT_SIZE, keyInfo->noIterations, keyInfo->memoryCost, dk, GetMaxPkcs5OutSize(), &abortKeyDerivation); + PKCS5_SALT_SIZE, keyInfo->noIterations, keyInfo->memoryCost, dk, ARGON2_HEADER_KEYDATA_SIZE, &abortKeyDerivation); if (derivationResult != 0) { if (selected_pkcs5_prf == 0) @@ -492,6 +492,12 @@ KeyReady: ; if (!EAIsModeSupported (cryptoInfo->ea, cryptoInfo->mode)) continue; // This encryption algorithm has never been available with this mode of operation +#ifndef VC_DCS_DISABLE_ARGON2 + /* Only XTS mode reaches this point; both XTS keys must fit in the fixed Argon2id output. */ + if (pkcs5_prf == ARGON2 && EAGetKeySize (cryptoInfo->ea) * 2 > ARGON2_HEADER_KEYDATA_SIZE) + continue; +#endif + blockSize = CipherGetBlockSize (EAGetFirstCipher (cryptoInfo->ea)); status = EAInit (cryptoInfo->ea, dk + primaryKeyOffset, cryptoInfo->ks); @@ -1074,6 +1080,15 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, unsigned char *header, // User selected encryption algorithm cryptoInfo->ea = ea; +#ifndef VC_DCS_DISABLE_ARGON2 + if (pkcs5_prf == ARGON2 && EAGetKeySize (ea) * 2 > ARGON2_HEADER_KEYDATA_SIZE) + { + crypto_close (cryptoInfo); + retVal = ERR_PARAMETER_INCORRECT; + goto err; + } +#endif + // User selected PRF cryptoInfo->pkcs5 = pkcs5_prf; cryptoInfo->noIterations = keyInfo.noIterations; @@ -1130,7 +1145,7 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, unsigned char *header, case ARGON2: { int derivationResult = derive_key_argon2(keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, - PKCS5_SALT_SIZE, keyInfo.noIterations, keyInfo.memoryCost, dk, GetMaxPkcs5OutSize(), NULL); + PKCS5_SALT_SIZE, keyInfo.noIterations, keyInfo.memoryCost, dk, ARGON2_HEADER_KEYDATA_SIZE, NULL); if (derivationResult != 0) { crypto_close (cryptoInfo); diff --git a/src/Core/CoreBase.cpp b/src/Core/CoreBase.cpp index d2fac9ae..deb818b6 100644 --- a/src/Core/CoreBase.cpp +++ b/src/Core/CoreBase.cpp @@ -53,7 +53,7 @@ namespace VeraCrypt RandomNumberGenerator::SetHash (newPkcs5Kdf->GetHash()); SecureBuffer newSalt (openVolume->GetSaltSize()); - SecureBuffer newHeaderKey (VolumeHeader::GetLargestSerializedKeySize()); + SecureBuffer newHeaderKey (VolumeHeader::GetHeaderKeyDerivationSize (newPkcs5Kdf)); shared_ptr password (Keyfile::ApplyListToPassword (newKeyfiles, newPassword, emvSupportEnabled)); @@ -286,7 +286,7 @@ namespace VeraCrypt RandomNumberGenerator::SetHash (pkcs5Kdf->GetHash()); SecureBuffer newSalt (header->GetSaltSize()); - SecureBuffer newHeaderKey (VolumeHeader::GetLargestSerializedKeySize()); + SecureBuffer newHeaderKey (VolumeHeader::GetHeaderKeyDerivationSize (pkcs5Kdf)); shared_ptr passwordKey (Keyfile::ApplyListToPassword (keyfiles, password, emvSupportEnabled)); diff --git a/src/Core/VolumeCreator.cpp b/src/Core/VolumeCreator.cpp index 564e5b27..895b4485 100644 --- a/src/Core/VolumeCreator.cpp +++ b/src/Core/VolumeCreator.cpp @@ -315,6 +315,12 @@ namespace VeraCrypt if (headerOptions.VolumeDataSize < 1) throw ParameterIncorrect (SRC_POS); +#ifndef VC_DCS_DISABLE_ARGON2 + // New volumes are created in XTS mode; Argon2id header key material has a fixed format size. + if (options->VolumeHeaderKdf->IsArgon2() && options->EA->GetKeySize() * 2 > ARGON2_HEADER_KEYDATA_SIZE) + throw ParameterIncorrect (SRC_POS); +#endif + // Master data key MasterKey.Allocate (options->EA->GetKeySize() * 2); RandomNumberGenerator::GetData (MasterKey); @@ -331,7 +337,7 @@ namespace VeraCrypt headerOptions.Salt = salt; // Header key - HeaderKey.Allocate (VolumeHeader::GetLargestSerializedKeySize()); + HeaderKey.Allocate (VolumeHeader::GetHeaderKeyDerivationSize (options->VolumeHeaderKdf)); PasswordKey = Keyfile::ApplyListToPassword (options->Keyfiles, options->Password, options->EMVSupportEnabled); int derivationResult = options->VolumeHeaderKdf->DeriveKey (HeaderKey, *PasswordKey, options->Pim, salt); if (derivationResult != 0) diff --git a/src/Volume/EncryptionTest.cpp b/src/Volume/EncryptionTest.cpp index 9564807f..c98db964 100644 --- a/src/Volume/EncryptionTest.cpp +++ b/src/Volume/EncryptionTest.cpp @@ -1204,14 +1204,26 @@ namespace VeraCrypt 0x30, 0x86, 0x51, 0x21, 0x69, 0x94, 0xab, 0xbf, 0xdd, 0xd6, 0x9b, 0x25, 0x92, 0x03, 0x2e, 0xfd }; + static const uint8 argon2Pim1HeaderKeyPrefix[] = + { + 0x48, 0x8d, 0x71, 0xbd, 0x71, 0x6e, 0x68, 0x45, + 0xaa, 0xe6, 0xe2, 0x29, 0x74, 0x18, 0x2c, 0x20, + 0xe9, 0x42, 0x8d, 0x7b, 0x3d, 0x4b, 0xcf, 0x54, + 0x04, 0x6c, 0x3e, 0xbe, 0x80, 0x33, 0x8f, 0x20 + }; ConstBufferPtr argon2Salt (argon2SaltData, sizeof (argon2SaltData)); Buffer argon2DerivedKey (sizeof (argon2Pim1DerivedKey)); + Buffer argon2HeaderKey (ARGON2_HEADER_KEYDATA_SIZE); // PIM 1 maps to Argon2id t=3, m=64 MiB, p=1. if (pkcs5Argon2.DeriveKey (argon2DerivedKey, password, 1, argon2Salt) != 0) throw TestFailed (SRC_POS); if (memcmp (argon2DerivedKey.Ptr(), argon2Pim1DerivedKey, sizeof (argon2Pim1DerivedKey)) != 0) throw TestFailed (SRC_POS); + if (pkcs5Argon2.DeriveKey (argon2HeaderKey, password, 1, argon2Salt) != 0) + throw TestFailed (SRC_POS); + if (memcmp (argon2HeaderKey.Ptr(), argon2Pim1HeaderKeyPrefix, sizeof (argon2Pim1HeaderKeyPrefix)) != 0) + throw TestFailed (SRC_POS); try { diff --git a/src/Volume/VolumeHeader.cpp b/src/Volume/VolumeHeader.cpp index 6c6f422d..69d9d0eb 100644 --- a/src/Volume/VolumeHeader.cpp +++ b/src/Volume/VolumeHeader.cpp @@ -100,13 +100,13 @@ namespace VeraCrypt ConstBufferPtr salt (encryptedData.GetRange (SaltOffset, SaltSize)); SecureBuffer header (EncryptedHeaderDataSize); - SecureBuffer headerKey (GetLargestSerializedKeySize()); foreach (shared_ptr pkcs5, keyDerivationFunctions) { if (kdf && (kdf->GetName() != pkcs5->GetName())) continue; + SecureBuffer headerKey (GetHeaderKeyDerivationSize (pkcs5)); int derivationResult = pkcs5->DeriveKey (headerKey, password, pim, salt); if (derivationResult != 0) { @@ -118,26 +118,32 @@ namespace VeraCrypt foreach (shared_ptr mode, encryptionModes) { - #ifdef WOLFCRYPT_BACKEND - if (typeid (*mode) != typeid (EncryptionModeWolfCryptXTS)) - #else - if (typeid (*mode) != typeid (EncryptionModeXTS)) - #endif - mode->SetKey (headerKey.GetRange (0, mode->GetKeySize())); + #ifdef WOLFCRYPT_BACKEND + bool xtsMode = typeid (*mode) == typeid (EncryptionModeWolfCryptXTS); + #else + bool xtsMode = typeid (*mode) == typeid (EncryptionModeXTS); + #endif + + if (!xtsMode) + { + if (mode->GetKeySize() > headerKey.Size()) + continue; + mode->SetKey (headerKey.GetRange (0, mode->GetKeySize())); + } foreach (shared_ptr ea, encryptionAlgorithms) { if (!ea->IsModeSupported (mode)) continue; - #ifndef WOLFCRYPT_BACKEND - if (typeid (*mode) == typeid (EncryptionModeXTS)) + size_t requiredHeaderKeySize = xtsMode ? ea->GetKeySize() * 2 : LegacyEncryptionModeKeyAreaSize + ea->GetKeySize(); + if (requiredHeaderKeySize > headerKey.Size()) + continue; + + if (xtsMode) { - ea->SetKey (headerKey.GetRange (0, ea->GetKeySize())); - #else - if (typeid (*mode) == typeid (EncryptionModeWolfCryptXTS)) - { - ea->SetKey (headerKey.GetRange (0, ea->GetKeySize())); + ea->SetKey (headerKey.GetRange (0, ea->GetKeySize())); + #ifdef WOLFCRYPT_BACKEND ea->SetKeyXTS (headerKey.GetRange (ea->GetKeySize(), ea->GetKeySize())); #endif @@ -319,6 +325,16 @@ namespace VeraCrypt Pkcs5 = newPkcs5Kdf; } + size_t VolumeHeader::GetHeaderKeyDerivationSize (shared_ptr kdf) + { + #ifndef VC_DCS_DISABLE_ARGON2 + if (kdf && kdf->IsArgon2()) + return ARGON2_HEADER_KEYDATA_SIZE; + #endif + + return GetLargestSerializedKeySize(); + } + size_t VolumeHeader::GetLargestSerializedKeySize () { size_t largestKey = EncryptionAlgorithm::GetLargestKeySize (EncryptionAlgorithm::GetAvailableAlgorithms()); diff --git a/src/Volume/VolumeHeader.h b/src/Volume/VolumeHeader.h index d6a51d78..b76c37d7 100644 --- a/src/Volume/VolumeHeader.h +++ b/src/Volume/VolumeHeader.h @@ -68,6 +68,7 @@ namespace VeraCrypt uint32 GetFlags () const { return Flags; } VolumeTime GetHeaderCreationTime () const { return HeaderCreationTime; } uint64 GetHiddenVolumeDataSize () const { return HiddenVolumeDataSize; } + static size_t GetHeaderKeyDerivationSize (shared_ptr kdf); static size_t GetLargestSerializedKeySize (); shared_ptr GetPkcs5Kdf () const { return Pkcs5; } uint16 GetRequiredMinProgramVersion () const { return RequiredMinProgramVersion; }