From e90e24b30b379752bf6531c663085de1d2a653d7 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Tue, 9 Aug 2016 14:25:52 +0200 Subject: Windows: Add support for Streebog (hash) and kuznyechik (encryption) --- src/Common/BootEncryption.cpp | 15 +++ src/Common/Crypto.c | 8 ++ src/Common/Crypto.h | 23 ++-- src/Common/Dlgcode.c | 28 ++++- src/Common/EncryptionThreadPool.c | 5 + src/Common/Pkcs5.c | 229 ++++++++++++++++++++++++++++++++++++++ src/Common/Pkcs5.h | 3 + src/Common/Random.c | 15 +++ src/Common/Tests.c | 163 ++++++++++++++++++++++++++- src/Common/Volumes.c | 10 +- 10 files changed, 486 insertions(+), 13 deletions(-) (limited to 'src/Common') diff --git a/src/Common/BootEncryption.cpp b/src/Common/BootEncryption.cpp index bf65fd6a..b18d1ce1 100644 --- a/src/Common/BootEncryption.cpp +++ b/src/Common/BootEncryption.cpp @@ -1082,11 +1082,23 @@ namespace VeraCrypt ea = SERPENT; else if (_stricmp (request.BootEncryptionAlgorithmName, "Twofish") == 0) ea = TWOFISH; + else if (_stricmp (request.BootEncryptionAlgorithmName, "Camellia") == 0) + ea = CAMELLIA; +#if defined(CIPHER_GOST89) + else if (_stricmp (request.BootEncryptionAlgorithmName, "GOST89") == 0) + ea = GOST89; +#endif if (_stricmp(request.BootPrfAlgorithmName, "SHA-256") == 0) pkcs5_prf = SHA256; else if (_stricmp(request.BootPrfAlgorithmName, "RIPEMD-160") == 0) pkcs5_prf = RIPEMD160; + else if (_stricmp(request.BootPrfAlgorithmName, "SHA-512") == 0) + pkcs5_prf = SHA512; + else if (_stricmp(request.BootPrfAlgorithmName, "Whirlpool") == 0) + pkcs5_prf = WHIRLPOOL; + else if (_stricmp(request.BootPrfAlgorithmName, "Streebog") == 0) + pkcs5_prf = STREEBOG; else if (strlen(request.BootPrfAlgorithmName) == 0) // case of version < 1.0f pkcs5_prf = RIPEMD160; } @@ -1101,6 +1113,9 @@ namespace VeraCrypt } catch (...) { } } + + if (pkcs5_prf == 0) + throw ParameterIncorrect (SRC_POS); } else { diff --git a/src/Common/Crypto.c b/src/Common/Crypto.c index 16115bf6..69d5b5dc 100644 --- a/src/Common/Crypto.c +++ b/src/Common/Crypto.c @@ -58,6 +58,7 @@ static Cipher Ciphers[] = #if defined(CIPHER_GOST89) { GOST89, L"GOST89", 16, 32, GOST_KS }, #endif // defined(CIPHER_GOST89) + { KUZNYECHIK, L"Kuznyechik",16, 32, KUZNYECHIK_KS }, #endif { 0, 0, 0, 0, 0 } }; @@ -78,6 +79,7 @@ static EncryptionAlgorithm EncryptionAlgorithms[] = #if defined(CIPHER_GOST89) { { GOST89, 0 }, { XTS, 0 }, 1 }, #endif // defined(CIPHER_GOST89) + { { KUZNYECHIK, 0 }, { XTS, 0 }, 1 }, { { TWOFISH, AES, 0 }, { XTS, 0 }, 1 }, { { SERPENT, TWOFISH, AES, 0 }, { XTS, 0 }, 1 }, { { AES, SERPENT, 0 }, { XTS, 0 }, 1 }, @@ -112,6 +114,7 @@ static Hash Hashes[] = { WHIRLPOOL, L"Whirlpool", FALSE, FALSE }, { SHA256, L"SHA-256", FALSE, TRUE }, { RIPEMD160, L"RIPEMD-160", TRUE, TRUE }, + { STREEBOG, L"Streebog", FALSE, FALSE }, { 0, 0, 0 } }; #endif @@ -156,6 +159,9 @@ int CipherInit (int cipher, unsigned char *key, unsigned __int8 *ks) gost_set_key(key, (gost_kds*)ks); break; #endif // && defined(CIPHER_GOST89) + case KUZNYECHIK: + kuznyechik_set_key(key, (kuznyechik_kds*)ks); + break; #endif // !defined(TC_WINDOWS_BOOT) default: @@ -189,6 +195,7 @@ void EncipherBlock(int cipher, void *data, void *ks) #if defined(CIPHER_GOST89) case GOST89: gost_encrypt(data, data, ks, 1); break; #endif // defined(CIPHER_GOST89) + case KUZNYECHIK: kuznyechik_encrypt_block(data, data, ks); break; #endif // !defined(TC_WINDOWS_BOOT) default: TC_THROW_FATAL_EXCEPTION; // Unknown/wrong ID } @@ -252,6 +259,7 @@ void DecipherBlock(int cipher, void *data, void *ks) #if defined(CIPHER_GOST89) case GOST89: gost_decrypt(data, data, ks, 1); break; #endif // defined(CIPHER_GOST89) + case KUZNYECHIK: kuznyechik_decrypt_block(data, data, ks); break; #endif // !defined(TC_WINDOWS_BOOT) diff --git a/src/Common/Crypto.h b/src/Common/Crypto.h index 9b5fbace..abcdfa5b 100644 --- a/src/Common/Crypto.h +++ b/src/Common/Crypto.h @@ -54,6 +54,7 @@ enum WHIRLPOOL, SHA256, RIPEMD160, + STREEBOG, HASH_ENUM_END_ID }; @@ -72,6 +73,9 @@ enum #define WHIRLPOOL_BLOCKSIZE 64 #define WHIRLPOOL_DIGESTSIZE 64 +#define STREEBOG_BLOCKSIZE 64 +#define STREEBOG_DIGESTSIZE 64 + #define MAX_DIGESTSIZE WHIRLPOOL_DIGESTSIZE #define DEFAULT_HASH_ALGORITHM FIRST_PRF_ID @@ -108,7 +112,8 @@ enum SERPENT, TWOFISH, CAMELLIA, - GOST89 + GOST89, + KUZNYECHIK }; typedef struct @@ -162,9 +167,11 @@ typedef struct # endif #else - -#define MAX_EXPANDED_KEY (AES_KS + SERPENT_KS + TWOFISH_KS) - +#ifdef TC_WINDOWS_BOOT +#define MAX_EXPANDED_KEY max((AES_KS + SERPENT_KS + TWOFISH_KS), CAMELLIA_KS) +#else +#define MAX_EXPANDED_KEY max(max(max((AES_KS + SERPENT_KS + TWOFISH_KS), GOST_KS), CAMELLIA_KS), KUZNYECHIK_KS) +#endif #endif #ifdef DEBUG @@ -190,7 +197,9 @@ typedef struct #ifndef TC_WINDOWS_BOOT # include "Sha2.h" # include "Whirlpool.h" +# include "Streebog.h" # include "GostCipher.h" +# include "kuznyechik.h" # include "Camellia.h" #else # include "CamelliaSmall.h" @@ -209,7 +218,7 @@ typedef struct keyInfo_t int keyLength; /* Length of the key */ uint64 dummy; /* Dummy field to ensure 16-byte alignment of this structure */ __int8 salt[PKCS5_SALT_SIZE]; /* PKCS-5 salt */ - __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* Concatenated master primary and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ + CRYPTOPP_ALIGN_DATA(16) __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* Concatenated master primary and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ CRYPTOPP_ALIGN_DATA(16) __int8 userKey[MAX_PASSWORD]; /* Password (to which keyfiles may have been applied). WITHOUT +1 for the null terminator. */ } KEY_INFO, *PKEY_INFO; @@ -231,8 +240,8 @@ typedef struct CRYPTO_INFO_t GfCtx gf_ctx; - unsigned __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* This holds the volume header area containing concatenated master key(s) and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ - unsigned __int8 k2[MASTER_KEYDATA_SIZE]; /* For XTS, this contains the secondary key (if cascade, multiple concatenated). For LRW (deprecated/legacy), it contains the tweak key. For CBC (deprecated/legacy), it contains the IV seed. */ + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 master_keydata[MASTER_KEYDATA_SIZE]; /* This holds the volume header area containing concatenated master key(s) and secondary key(s) (XTS mode). For LRW (deprecated/legacy), it contains the tweak key before the master key(s). For CBC (deprecated/legacy), it contains the IV seed before the master key(s). */ + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 k2[MASTER_KEYDATA_SIZE]; /* For XTS, this contains the secondary key (if cascade, multiple concatenated). For LRW (deprecated/legacy), it contains the tweak key. For CBC (deprecated/legacy), it contains the IV seed. */ unsigned __int8 salt[PKCS5_SALT_SIZE]; int noIterations; BOOL bTrueCryptMode; diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index 1ecc3c0a..31be6b90 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -5154,6 +5154,7 @@ static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) RMD160_CTX rctx; sha512_ctx s2ctx; sha256_ctx s256ctx; + STREEBOG_CTX stctx; int hid, i; @@ -5190,6 +5191,13 @@ static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) WHIRLPOOL_add (lpTestBuffer, benchmarkBufferSize, &wctx); WHIRLPOOL_finalize (&wctx, (unsigned char *) digest); break; + + case STREEBOG: + STREEBOG_init(&stctx); + STREEBOG_add(&stctx, lpTestBuffer, benchmarkBufferSize); + STREEBOG_finalize(&stctx, (unsigned char *)digest); + break; + } } @@ -5249,6 +5257,11 @@ static BOOL PerformBenchmark(HWND hBenchDlg, HWND hwndDlg) /* PKCS-5 test with HMAC-Whirlpool used as the PRF */ derive_key_whirlpool ("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot), dk, MASTER_KEYDATA_SIZE); break; + + case STREEBOG: + /* PKCS-5 test with HMAC-STREEBOG used as the PRF */ + derive_key_streebog("passphrase-1234567890", 21, tmp_salt, 64, get_pkcs5_iteration_count(thid, benchmarkPim, FALSE, benchmarkPreBoot), dk, MASTER_KEYDATA_SIZE); + break; } } @@ -6612,7 +6625,12 @@ ResetCipherTest(HWND hwndDlg, int idTestCipher) SetWindowText(GetDlgItem(hwndDlg, IDC_PLAINTEXT), L"0000000000000000"); SetWindowText(GetDlgItem(hwndDlg, IDC_CIPHERTEXT), L"0000000000000000"); - if (idTestCipher == AES || idTestCipher == SERPENT || idTestCipher == TWOFISH || idTestCipher == CAMELLIA) + if (idTestCipher == AES || idTestCipher == SERPENT || idTestCipher == TWOFISH || idTestCipher == CAMELLIA +#if defined(CIPHER_GOST89) + || idTestCipher == GOST89 +#endif + || idTestCipher == KUZNYECHIK + ) { ndx = (int) SendMessage (GetDlgItem(hwndDlg, IDC_KEY_SIZE), CB_ADDSTRING, 0,(LPARAM) L"256"); SendMessage(GetDlgItem(hwndDlg, IDC_KEY_SIZE), CB_SETITEMDATA, ndx,(LPARAM) 32); @@ -10186,6 +10204,14 @@ void Applink (char *dest, BOOL bSendOS, char *extraOutput) { StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=Twofish"); } + else if (strcmp(dest, "gost89") == 0) + { + StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=GOST89"); + } + else if (strcmp(dest, "kuznyechik") == 0) + { + StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=Kuznyechik"); + } else if (strcmp(dest, "camellia") == 0) { StringCbCopyA (url, sizeof (url),"https://veracrypt.codeplex.com/wikipage?title=Camellia"); diff --git a/src/Common/EncryptionThreadPool.c b/src/Common/EncryptionThreadPool.c index d99512a9..f1207113 100644 --- a/src/Common/EncryptionThreadPool.c +++ b/src/Common/EncryptionThreadPool.c @@ -182,6 +182,11 @@ static TC_THREAD_PROC EncryptionThreadProc (void *threadArg) workItem->KeyDerivation.IterationCount, workItem->KeyDerivation.DerivedKey, GetMaxPkcs5OutSize()); break; + case STREEBOG: + derive_key_streebog(workItem->KeyDerivation.Password, workItem->KeyDerivation.PasswordLength, workItem->KeyDerivation.Salt, PKCS5_SALT_SIZE, + workItem->KeyDerivation.IterationCount, workItem->KeyDerivation.DerivedKey, GetMaxPkcs5OutSize()); + break; + default: TC_THROW_FATAL_EXCEPTION; } diff --git a/src/Common/Pkcs5.c b/src/Common/Pkcs5.c index eb1a66ad..2ce475ef 100644 --- a/src/Common/Pkcs5.c +++ b/src/Common/Pkcs5.c @@ -912,6 +912,222 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uin } +typedef struct hmac_streebog_ctx_struct +{ + STREEBOG_CTX ctx; + STREEBOG_CTX inner_digest_ctx; /*pre-computed inner digest context */ + STREEBOG_CTX outer_digest_ctx; /*pre-computed outer digest context */ + CRYPTOPP_ALIGN_DATA(16) char k[PKCS5_SALT_SIZE + 4]; /* enough to hold (salt_len + 4) and also the Streebog hash */ + char u[STREEBOG_DIGESTSIZE]; +} hmac_streebog_ctx; + +void hmac_streebog_internal +( + char *k, /* secret key */ + int lk, /* length of the key in bytes */ + char *d, /* input/output data. d pointer is guaranteed to be at least 64-bytes long */ + int ld, /* length of input data in bytes */ + hmac_streebog_ctx* hmac /* HMAC-Whirlpool context which holds temporary variables */ +) +{ + STREEBOG_CTX* ctx = &(hmac->ctx); + + /**** Restore Precomputed Inner Digest Context ****/ + + memcpy (ctx, &(hmac->inner_digest_ctx), sizeof (STREEBOG_CTX)); + + STREEBOG_add (ctx, (unsigned char *) d, ld); + + STREEBOG_finalize (ctx, (unsigned char *) d); + + /**** Restore Precomputed Outer Digest Context ****/ + + memcpy (ctx, &(hmac->outer_digest_ctx), sizeof (STREEBOG_CTX)); + + STREEBOG_add (ctx, (unsigned char *) d, STREEBOG_DIGESTSIZE); + + STREEBOG_finalize (ctx, (unsigned char *) d); +} + +void hmac_streebog +( + char *k, /* secret key */ + int lk, /* length of the key in bytes */ + char *d, /* input data. d pointer is guaranteed to be at least 32-bytes long */ + int ld /* length of data in bytes */ +) +{ + hmac_streebog_ctx hmac; + STREEBOG_CTX* ctx; + char* buf = hmac.k; + int b; + CRYPTOPP_ALIGN_DATA(16) char key[STREEBOG_DIGESTSIZE]; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (HasSSE2() || HasSSE41()) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif + /* If the key is longer than the hash algorithm block size, + let key = streebog(key), as per HMAC specifications. */ + if (lk > STREEBOG_BLOCKSIZE) + { + STREEBOG_CTX tctx; + + STREEBOG_init (&tctx); + STREEBOG_add (&tctx, (unsigned char *) k, lk); + STREEBOG_finalize (&tctx, (unsigned char *) key); + + k = key; + lk = STREEBOG_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } + + /**** Precompute HMAC Inner Digest ****/ + + ctx = &(hmac.inner_digest_ctx); + STREEBOG_init (ctx); + + /* Pad the key for inner digest */ + for (b = 0; b < lk; ++b) + buf[b] = (char) (k[b] ^ 0x36); + memset (&buf[lk], 0x36, STREEBOG_BLOCKSIZE - lk); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + /**** Precompute HMAC Outer Digest ****/ + + ctx = &(hmac.outer_digest_ctx); + STREEBOG_init (ctx); + + for (b = 0; b < lk; ++b) + buf[b] = (char) (k[b] ^ 0x5C); + memset (&buf[lk], 0x5C, STREEBOG_BLOCKSIZE - lk); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + hmac_streebog_internal(k, lk, d, ld, &hmac); + +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && (HasSSE2() || HasSSE41())) + KeRestoreFloatingPointState (&floatingPointState); +#endif + /* Prevent leaks */ + burn(&hmac, sizeof(hmac)); +} + +static void derive_u_streebog (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, int b, hmac_streebog_ctx* hmac) +{ + char* u = hmac->u; + char* k = hmac->k; + uint32 c, i; + + /* iteration 1 */ + memcpy (k, salt, salt_len); /* salt */ + /* big-endian block number */ + memset (&k[salt_len], 0, 3); + k[salt_len + 3] = (char) b; + + hmac_streebog_internal (pwd, pwd_len, k, salt_len + 4, hmac); + memcpy (u, k, STREEBOG_DIGESTSIZE); + + /* remaining iterations */ + for (c = 1; c < iterations; c++) + { + hmac_streebog_internal (pwd, pwd_len, k, STREEBOG_DIGESTSIZE, hmac); + for (i = 0; i < STREEBOG_DIGESTSIZE; i++) + { + u[i] ^= k[i]; + } + } +} + +void derive_key_streebog (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen) +{ + hmac_streebog_ctx hmac; + STREEBOG_CTX* ctx; + char* buf = hmac.k; + char key[STREEBOG_DIGESTSIZE]; + int b, l, r; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (HasSSE2() || HasSSE41()) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif + /* If the password is longer than the hash algorithm block size, + let pwd = streebog(pwd), as per HMAC specifications. */ + if (pwd_len > STREEBOG_BLOCKSIZE) + { + STREEBOG_CTX tctx; + + STREEBOG_init (&tctx); + STREEBOG_add (&tctx, (unsigned char *) pwd, pwd_len); + STREEBOG_finalize (&tctx, (unsigned char *) key); + + pwd = key; + pwd_len = STREEBOG_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } + + if (dklen % STREEBOG_DIGESTSIZE) + { + l = 1 + dklen / STREEBOG_DIGESTSIZE; + } + else + { + l = dklen / STREEBOG_DIGESTSIZE; + } + + r = dklen - (l - 1) * STREEBOG_DIGESTSIZE; + + /**** Precompute HMAC Inner Digest ****/ + + ctx = &(hmac.inner_digest_ctx); + STREEBOG_init (ctx); + + /* Pad the key for inner digest */ + for (b = 0; b < pwd_len; ++b) + buf[b] = (char) (pwd[b] ^ 0x36); + memset (&buf[pwd_len], 0x36, STREEBOG_BLOCKSIZE - pwd_len); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + /**** Precompute HMAC Outer Digest ****/ + + ctx = &(hmac.outer_digest_ctx); + STREEBOG_init (ctx); + + for (b = 0; b < pwd_len; ++b) + buf[b] = (char) (pwd[b] ^ 0x5C); + memset (&buf[pwd_len], 0x5C, STREEBOG_BLOCKSIZE - pwd_len); + + STREEBOG_add (ctx, (unsigned char *) buf, STREEBOG_BLOCKSIZE); + + /* first l - 1 blocks */ + for (b = 1; b < l; b++) + { + derive_u_streebog (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, STREEBOG_DIGESTSIZE); + dk += STREEBOG_DIGESTSIZE; + } + + /* last block */ + derive_u_streebog (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, r); + +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && (HasSSE2() || HasSSE41())) + KeRestoreFloatingPointState (&floatingPointState); +#endif + + /* Prevent possible leaks. */ + burn (&hmac, sizeof(hmac)); + burn (key, sizeof(key)); +} + wchar_t *get_pkcs5_prf_name (int pkcs5_prf_id) { switch (pkcs5_prf_id) @@ -928,6 +1144,9 @@ wchar_t *get_pkcs5_prf_name (int pkcs5_prf_id) case WHIRLPOOL: return L"HMAC-Whirlpool"; + case STREEBOG: + return L"HMAC-STREEBOG"; + default: return L"(Unknown)"; } @@ -973,6 +1192,16 @@ int get_pkcs5_iteration_count (int pkcs5_prf_id, int pim, BOOL truecryptMode, BO return bBoot? pim * 2048 : 15000 + pim * 1000; } + case STREEBOG: + if (truecryptMode) + return 1000; + else if (pim == 0) + return bBoot? 200000 : 500000; + else + { + return bBoot? pim * 2048 : 15000 + pim * 1000; + } + default: TC_THROW_FATAL_EXCEPTION; // Unknown/wrong ID } diff --git a/src/Common/Pkcs5.h b/src/Common/Pkcs5.h index 81d9de64..261df85d 100644 --- a/src/Common/Pkcs5.h +++ b/src/Common/Pkcs5.h @@ -37,6 +37,9 @@ void derive_key_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, uint32 void hmac_whirlpool (char *k, int lk, char *d, int ld); void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen); +void hmac_streebog (char *k, int32 lk, char *d, int32 ld); +void derive_key_streebog (char *pwd, int pwd_len, char *salt, int salt_len, uint32 iterations, char *dk, int dklen); + int get_pkcs5_iteration_count (int pkcs5_prf_id, int pim, BOOL truecryptMode, BOOL bBoot); wchar_t *get_pkcs5_prf_name (int pkcs5_prf_id); #endif diff --git a/src/Common/Random.c b/src/Common/Random.c index 2be230fd..edc6acca 100644 --- a/src/Common/Random.c +++ b/src/Common/Random.c @@ -247,6 +247,7 @@ BOOL Randmix () RMD160_CTX rctx; sha512_ctx sctx; sha256_ctx s256ctx; + STREEBOG_CTX stctx; int poolIndex, digestIndex, digestSize; switch (HashFunction) @@ -267,6 +268,10 @@ BOOL Randmix () digestSize = WHIRLPOOL_DIGESTSIZE; break; + case STREEBOG: + digestSize = STREEBOG_DIGESTSIZE; + break; + default: TC_THROW_FATAL_EXCEPTION; } @@ -303,6 +308,12 @@ BOOL Randmix () WHIRLPOOL_finalize (&wctx, hashOutputBuffer); break; + case STREEBOG: + STREEBOG_init (&stctx); + STREEBOG_add (&stctx, pRandPool, RNG_POOL_SIZE); + STREEBOG_finalize (&stctx, hashOutputBuffer); + break; + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; @@ -335,6 +346,10 @@ BOOL Randmix () burn (&wctx, sizeof(wctx)); break; + case STREEBOG: + burn (&stctx, sizeof(sctx)); + break; + default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; diff --git a/src/Common/Tests.c b/src/Common/Tests.c index 02f893c7..c51b6338 100644 --- a/src/Common/Tests.c +++ b/src/Common/Tests.c @@ -19,10 +19,11 @@ #include "Xts.h" #include #include "Pkcs5.h" +#include "cpu.h" typedef struct { - unsigned __int8 key1[32]; - unsigned __int8 key2[32]; + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 key1[32]; + CRYPTOPP_ALIGN_DATA(16) unsigned __int8 key2[32]; unsigned __int8 dataUnitNo[8]; unsigned int blockNo; unsigned __int8 plaintext[ENCRYPTION_DATA_UNIT_SIZE]; @@ -475,6 +476,95 @@ char *hmac_whirlpool_test_vectors = "\x6a\xbf\xa4\x02" }; +typedef struct _HashTestVector +{ + const char* hexInput; + const char* hexOutput; + +} HashTestVector; + +typedef int (__cdecl HashFunction) (unsigned char* input, unsigned long inputLen, unsigned char* output); + +unsigned char HexCharToByte (char c) +{ + if (c >= ('0') && c <= ('9')) + return c - ('0'); + else if (c >= ('A') && c <= ('F')) + return c - ('A') + 10; + else if (c >= ('a') && c <= ('f')) + return c - ('a') + 10; + else + return 0xFF; +} + +unsigned long HexStringToByteArray(const char* hexStr, unsigned char* pbData) +{ + unsigned long count = 0; + while (*hexStr) + { + *pbData++ = (HexCharToByte(hexStr[0]) << 4) | HexCharToByte(hexStr[1]); + hexStr += 2; + count++; + } + return count; +} + +BOOL RunHashTest (HashFunction fn, HashTestVector* vector, BOOL bUseSSE) +{ + ALIGN (16) unsigned char input[256]; + unsigned char output[64]; + unsigned char digest[64]; + unsigned long i = 0, inputLen, outputLen, digestLen; + BOOL bRet = TRUE; +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + KFLOATING_SAVE floatingPointState; + NTSTATUS saveStatus = STATUS_SUCCESS; + if (bUseSSE && (HasSSE2() || HasSSE41())) + saveStatus = KeSaveFloatingPointState (&floatingPointState); +#endif + while (vector[i].hexInput && vector[i].hexOutput) + { + inputLen = HexStringToByteArray (vector[i].hexInput, input); + outputLen = HexStringToByteArray (vector[i].hexOutput, output); + digestLen = fn (input, inputLen, digest); + if ((digestLen != outputLen) || (0 != memcmp (digest, output, digestLen))) + { + bRet = FALSE; + break; + } + i++; + } + +#if defined (DEVICE_DRIVER) && !defined (_WIN64) + if (NT_SUCCESS (saveStatus) && bUseSSE && (HasSSE2() || HasSSE41())) + KeRestoreFloatingPointState (&floatingPointState); +#endif + + return bRet; +} + + +/* https://www.streebog.net/src/trunk/examples/ */ +HashTestVector Streebog512TestVectors[] = { + /* M1 */ + {"303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132", + "1b54d01a4af5b9d5cc3d86d68d285462b19abc2475222f35c085122be4ba1ffa00ad30f8767b3a82384c6574f024c311e2a481332b08ef7f41797891c1646f48" + }, + /* M2 */ + {"d1e520e2e5f2f0e82c20d1f2f0e8e1eee6e820e2edf3f6e82c20e2e5fef2fa20f120eceef0ff20f1f2f0e5ebe0ece820ede020f5f0e0e1f0fbff20efebfaeafb20c8e3eef0e5e2fb", + "1e88e62226bfca6f9994f1f2d51569e0daf8475a3b0fe61a5300eee46d961376035fe83549ada2b8620fcd7c496ce5b33f0cb9dddc2b6460143b03dabac9fb28" + }, + /* M3 */ + {"", + "8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a" + }, + /* M4 */ + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "b0fd29ac1b0df441769ff3fdb8dc564df67721d6ac06fb28ceffb7bbaa7948c6c014ac999235b58cb26fb60fb112a145d7b4ade9ae566bf2611402c552d20db7" + }, + {NULL, NULL} +}; + unsigned char ks_tmp[MAX_EXPANDED_KEY]; void CipherInit2(int cipher, void* key, void* ks, int key_len) @@ -502,6 +592,9 @@ void CipherInit2(int cipher, void* key, void* ks, int key_len) CipherInit(cipher,key,ks); break; #endif // defined(CIPHER_GOST89) + case KUZNYECHIK: + CipherInit(cipher, key, ks); + break; default: /* Unknown/wrong ID */ TC_THROW_FATAL_EXCEPTION; @@ -521,7 +614,7 @@ BOOL TestSectorBufEncryption (PCRYPTO_INFO ci) int testCase = 0; int nTestsPerformed = 0; - static unsigned char key1[] = + static CRYPTOPP_ALIGN_DATA(16) unsigned char key1[] = { 0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45, 0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26, 0x62, 0x49, 0x77, 0x57, 0x24, 0x70, 0x93, 0x69, 0x99, 0x59, 0x57, 0x49, 0x66, 0x96, 0x76, 0x27, 0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93, 0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95, 0x02, 0x88, 0x41, 0x97, 0x16, 0x93, 0x99, 0x37, 0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92, @@ -939,7 +1032,7 @@ BOOL TestSectorBufEncryption (PCRYPTO_INFO ci) static BOOL DoAutoTestAlgorithms (void) { PCRYPTO_INFO ci; - char key[32]; + CRYPTOPP_ALIGN_DATA(16) char key[32]; unsigned char tmp[16]; BOOL bFailed = FALSE; int i; @@ -1172,6 +1265,50 @@ BOOL test_hmac_whirlpool () return TRUE; } +/* http://www.tc26.ru/methods/recommendation/%D0%A2%D0%9A26%D0%90%D0%9B%D0%93.pdf */ +/* https://tools.ietf.org/html/draft-smyshlyaev-gost-usage-00 */ +/* https://datatracker.ietf.org/doc/rfc7836/?include_text=1 */ +static const unsigned char gost3411_2012_hmac_k1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f +}; +static const unsigned char gost3411_2012_hmac_m1[] = { + 0x01, 0x26, 0xbd, 0xb8, 0x78, 0x00, 0xaf, 0x21, + 0x43, 0x41, 0x45, 0x65, 0x63, 0x78, 0x01, 0x00 +}; +static const unsigned char gost3411_2012_hmac_r1[] = { + 0xA5, 0x9B, 0xAB, 0x22, 0xEC, 0xAE, 0x19, 0xC6, 0x5F, 0xBD, 0xE6, 0xE5, + 0xF4, 0xE9, 0xF5, 0xD8, 0x54, 0x9D, 0x31, 0xF0, 0x37, 0xF9, 0xDF, 0x9B, + 0x90, 0x55, 0x00, 0xE1, 0x71, 0x92, 0x3A, 0x77, 0x3D, 0x5F, 0x15, 0x30, + 0xF2, 0xED, 0x7E, 0x96, 0x4C, 0xB2, 0xEE, 0xDC, 0x29, 0xE9, 0xAD, 0x2F, + 0x3A, 0xFE, 0x93, 0xB2, 0x81, 0x4F, 0x79, 0xF5, 0x00, 0x0F, 0xFC, 0x03, + 0x66, 0xC2, 0x51, 0xE6 +}; + + +BOOL test_hmac_streebog () +{ + ALIGN(16) char digest[64]; /* large enough to hold digets and test vector inputs */ + + memcpy (digest, gost3411_2012_hmac_m1, sizeof (gost3411_2012_hmac_m1)); + hmac_streebog ((char*) gost3411_2012_hmac_k1, sizeof(gost3411_2012_hmac_k1), digest, (int) sizeof (gost3411_2012_hmac_m1)); + if (memcmp (digest, gost3411_2012_hmac_r1, STREEBOG_DIGESTSIZE) != 0) + return FALSE; + + return TRUE; +} + +int __cdecl StreebogHash (unsigned char* input, unsigned long inputLen, unsigned char* output) +{ + STREEBOG_CTX ctx; + STREEBOG_init (&ctx); + STREEBOG_add (&ctx, input, inputLen); + STREEBOG_finalize (&ctx, output); + return STREEBOG_DIGESTSIZE; +} + BOOL test_pkcs5 () { char dk[144]; @@ -1192,6 +1329,14 @@ BOOL test_pkcs5 () if (test_hmac_whirlpool() == FALSE) return FALSE; + /* HMAC-STREEBOG tests */ + if (test_hmac_streebog() == FALSE) + return FALSE; + + /* STREEBOG hash tests */ + if (RunHashTest (StreebogHash, Streebog512TestVectors, TRUE) == FALSE) + return FALSE; + /* PKCS-5 test 1 with HMAC-SHA-256 used as the PRF (https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00) */ derive_key_sha256 ("passwd", 6, "\x73\x61\x6C\x74", 4, 1, dk, 64); if (memcmp (dk, "\x55\xac\x04\x6e\x56\xe3\x08\x9f\xec\x16\x91\xc2\x25\x44\xb6\x05\xf9\x41\x85\x21\x6d\xde\x04\x65\xe6\x8b\x9d\x57\xc2\x0d\xac\xbc\x49\xca\x9c\xcc\xf1\x79\xb6\x45\x99\x16\x64\xb3\x9d\x77\xef\x31\x7c\x71\xb8\x45\xb1\xe3\x0b\xd5\x09\x11\x20\x41\xd3\xa1\x97\x83", 64) != 0) @@ -1243,5 +1388,15 @@ BOOL test_pkcs5 () if (memcmp (dk, "\x50\x7c\x36\x6f\xee\x10\x2e\x9a\xe2\x8a\xd5\x82\x72\x7d\x27\x0f\xe8\x4d\x7f\x68\x7a\xcf\xb5\xe7\x43\x67\xaa\x98\x93\x52\x2b\x09\x6e\x42\xdf\x2c\x59\x4a\x91\x6d\x7e\x10\xae\xb2\x1a\x89\x8f\xb9\x8f\xe6\x31\xa9\xd8\x9f\x98\x26\xf4\xda\xcd\x7d\x65\x65\xde\x10\x95\x91\xb4\x84\x26\xae\x43\xa1\x00\x5b\x1e\xb8\x38\x97\xa4\x1e\x4b\xd2\x65\x64\xbc\xfa\x1f\x35\x85\xdb\x4f\x97\x65\x6f\xbd\x24", 96) != 0) return FALSE; + /* PKCS-5 test 1 with HMAC-STREEBOG used as the PRF */ + derive_key_streebog ("password", 8, "\x12\x34\x56\x78", 4, 5, dk, 4); + if (memcmp (dk, "\xd0\x53\xa2\x30", 4) != 0) + return FALSE; + + /* PKCS-5 test 2 with HMAC-STREEBOG used as the PRF (derives a key longer than the underlying hash) */ + derive_key_streebog ("password", 8, "\x12\x34\x56\x78", 4, 5, dk, 96); + if (memcmp (dk, "\xd0\x53\xa2\x30\x6f\x45\x81\xeb\xbc\x06\x81\xc5\xe7\x53\xa8\x5d\xc7\xf1\x23\x33\x1e\xbe\x64\x2c\x3b\x0f\x26\xd7\x00\xe1\x95\xc9\x65\x26\xb1\x85\xbe\x1e\xe2\xf4\x9b\xfc\x6b\x14\x84\xda\x24\x61\xa0\x1b\x9e\x79\x5c\xee\x69\x6e\xf9\x25\xb1\x1d\xca\xa0\x31\xba\x02\x6f\x9e\x99\x0f\xdb\x25\x01\x5b\xf1\xc7\x10\x19\x53\x3b\x29\x3f\x18\x00\xd6\xfc\x85\x03\xdc\xf2\xe5\xe9\x5a\xb1\x1e\x61\xde", 96) != 0) + return FALSE; + return TRUE; } diff --git a/src/Common/Volumes.c b/src/Common/Volumes.c index 8ad40ac1..21b34f6d 100644 --- a/src/Common/Volumes.c +++ b/src/Common/Volumes.c @@ -171,7 +171,7 @@ int ReadVolumeHeader (BOOL bBoot, char *encryptedHeader, Password *password, int char header[TC_VOLUME_HEADER_EFFECTIVE_SIZE]; CRYPTOPP_ALIGN_DATA(16) KEY_INFO keyInfo; PCRYPTO_INFO cryptoInfo; - char dk[MASTER_KEYDATA_SIZE]; + CRYPTOPP_ALIGN_DATA(16) char dk[MASTER_KEYDATA_SIZE]; int enqPkcs5Prf, pkcs5_prf; uint16 headerVersion; int status = ERR_PARAMETER_INCORRECT; @@ -345,6 +345,10 @@ KeyReady: ; PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); break; + case STREEBOG: + derive_key_streebog(keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; default: // Unknown/wrong ID TC_THROW_FATAL_EXCEPTION; @@ -912,6 +916,10 @@ int CreateVolumeHeaderInMemory (HWND hwndDlg, BOOL bBoot, char *header, int ea, PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); break; + case STREEBOG: + derive_key_streebog(keyInfo.userKey, keyInfo.keyLength, keyInfo.salt, + PKCS5_SALT_SIZE, keyInfo.noIterations, dk, GetMaxPkcs5OutSize()); + break; default: // Unknown/wrong ID -- cgit v1.2.3