VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Common/Pkcs5.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Common/Pkcs5.c')
-rw-r--r--src/Common/Pkcs5.c229
1 files changed, 229 insertions, 0 deletions
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
}