1
0
mirror of https://github.com/veracrypt/VeraCrypt.git synced 2026-05-21 13:20:53 -05:00

Document fixed Argon2id header key size

Argon2id includes the requested output length in its computation, so deriving 192 bytes and using a prefix is not equivalent to deriving only the selected cipher's key material length. This differs from PBKDF2, where the prefix property made this detail invisible.

VeraCrypt derives the maximum header key material currently needed by the supported cipher/cascade set, which is 192 bytes, and then uses the required prefix for the selected encryption algorithm. For AES-XTS this means the first 64 bytes of the 192-byte Argon2id output are used.

Make this design rule explicit in code and documentation by introducing ARGON2_HEADER_KEYDATA_SIZE instead of relying implicitly on GetMaxPkcs5OutSize. If a future cipher or cascade requires more than 192 bytes, that must be handled as an explicit format/design change.

Document the 192-byte Argon2id header KDF output requirement so third-party implementations derive the same header key material.

References: https://github.com/veracrypt/VeraCrypt/issues/1614
This commit is contained in:
Mounir IDRASSI
2026-05-21 17:19:23 +09:00
parent c4acb6a0be
commit c3ce2db9ac
12 changed files with 81 additions and 24 deletions
+1 -1
View File
@@ -161,7 +161,7 @@ When using Argon2id in VeraCrypt:
<strong>Algorithm:</strong> Argon2id as defined in RFC 9106<br/>
<strong>Internal hash:</strong> BLAKE2b<br/>
<strong>Salt size:</strong> 512 bits (same as PBKDF2-HMAC)<br/>
<strong>Output length:</strong> Variable, depending on the encryption algorithm (e.g., 256 bits for AES-256, 768 bits for AES-Twofish-Serpent cascade)<br/>
<strong>Header KDF output length:</strong> 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.<br/>
<strong>Version:</strong> Argon2 version 0x13 (19 decimal)
</div>
+1
View File
@@ -87,6 +87,7 @@ PIM </a>value is given by the user, the number of iterations of the PBKDF2 key d
<h4>Argon2id Parameters</h4>
<p>When Argon2id is selected as the key derivation function, the PIM value controls both memory and time costs as described in the <a href="Personal%20Iterations%20Multiplier%20%28PIM%29.html">PIM section</a>. If no PIM is specified, default parameters equivalent to PIM = 12 are used (416 MiB memory, 6 iterations).</p>
<p>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.</p>
</div>
<div style="text-align:left; margin-top:19px; margin-bottom:19px; padding-top:0px; padding-bottom:0px">
+2 -2
View File
@@ -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
+6
View File
@@ -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
+1 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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:
+17 -2
View File
@@ -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);
+2 -2
View File
@@ -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 <VolumePassword> 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 <VolumePassword> passwordKey (Keyfile::ApplyListToPassword (keyfiles, password, emvSupportEnabled));
+7 -1
View File
@@ -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)
+12
View File
@@ -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
{
+30 -14
View File
@@ -100,13 +100,13 @@ namespace VeraCrypt
ConstBufferPtr salt (encryptedData.GetRange (SaltOffset, SaltSize));
SecureBuffer header (EncryptedHeaderDataSize);
SecureBuffer headerKey (GetLargestSerializedKeySize());
foreach (shared_ptr <Pkcs5Kdf> 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 <EncryptionMode> 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 <EncryptionAlgorithm> 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 <Pkcs5Kdf> 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());
+1
View File
@@ -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 <Pkcs5Kdf> kdf);
static size_t GetLargestSerializedKeySize ();
shared_ptr <Pkcs5Kdf> GetPkcs5Kdf () const { return Pkcs5; }
uint16 GetRequiredMinProgramVersion () const { return RequiredMinProgramVersion; }