From cb9859fa0e061fa3b068447c3860588cf3522f4e Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sun, 22 Feb 2015 00:23:19 +0100 Subject: [PATCH] Speed and memory usage optimization for key derivation based on patches by Nils Maier. 20% improvement on 64-bit CPU for SHA512 and SHA256, 11% for Whirlpool and 13% for RIPEMD-160. --- src/Common/Pkcs5.c | 680 +++++++++++++++++++++++++-------------------- src/Common/Pkcs5.h | 19 +- src/Common/Tests.c | 20 +- 3 files changed, 406 insertions(+), 313 deletions(-) diff --git a/src/Common/Pkcs5.c b/src/Common/Pkcs5.c index 396e3625..6585704c 100644 --- a/src/Common/Pkcs5.c +++ b/src/Common/Pkcs5.c @@ -36,24 +36,68 @@ void hmac_truncate #if !defined(TC_WINDOWS_BOOT) || defined(TC_WINDOWS_BOOT_SHA2) -void hmac_sha256 +typedef struct hmac_sha256_ctx_struct +{ + sha256_ctx ctx; + char buf[SHA256_BLOCKSIZE]; + char k[PKCS5_SALT_SIZE + 4]; /* enough to hold (salt_len + 4) and also the SHA256 hash */ + char u[SHA256_DIGESTSIZE]; +} hmac_sha256_ctx; + +void hmac_sha256_internal ( - char *k, /* secret key */ + char *k, /* secret key. It's ensured to be always <= 32 bytes */ int lk, /* length of the key in bytes */ - char *d, /* data */ - int ld, /* length of data in bytes */ - char *out /* output buffer, at least "t" bytes */ + char *d, /* input data. d pointer is guaranteed to be at least 32-bytes long */ + int ld, /* length of input data in bytes */ + hmac_sha256_ctx* hmac /* HMAC-SHA256 context which holds temporary variables */ ) { - sha256_ctx ictx, octx; - char isha[SHA256_DIGESTSIZE], osha[SHA256_DIGESTSIZE]; -#ifndef TC_WINDOWS_BOOT - char key[SHA256_DIGESTSIZE]; -#endif - char buf[SHA256_BLOCKSIZE]; int i; + sha256_ctx* ctx = &(hmac->ctx); + char* buf = hmac->buf; + + /**** Inner Digest ****/ + + sha256_begin (ctx); + + /* Pad the key for inner digest */ + for (i = 0; i < lk; ++i) + buf[i] = (char) (k[i] ^ 0x36); + for (i = lk; i < SHA256_BLOCKSIZE; ++i) + buf[i] = 0x36; + + sha256_hash ((unsigned char *) buf, SHA256_BLOCKSIZE, ctx); + sha256_hash ((unsigned char *) d, ld, ctx); + + sha256_end ((unsigned char *) d, ctx); /* d = inner digest */ + + /**** Outer Digest ****/ + + sha256_begin (ctx); + + for (i = 0; i < lk; ++i) + buf[i] = (char) (k[i] ^ 0x5C); + for (i = lk; i < SHA256_BLOCKSIZE; ++i) + buf[i] = 0x5C; + + sha256_hash ((unsigned char *) buf, SHA256_BLOCKSIZE, ctx); + sha256_hash ((unsigned char *) d, SHA256_DIGESTSIZE, ctx); + + sha256_end ((unsigned char *) d, ctx); /* d = outer digest */ +} #ifndef TC_WINDOWS_BOOT +void hmac_sha256 +( + char *k, /* secret key */ + int lk, /* length of the key in bytes */ + char *d, /* data */ + int ld /* length of data in bytes */ +) +{ + hmac_sha256_ctx hmac; + char key[SHA256_DIGESTSIZE]; /* If the key is longer than the hash algorithm block size, let key = sha256(key), as per HMAC specifications. */ if (lk > SHA256_BLOCKSIZE) @@ -69,58 +113,19 @@ void hmac_sha256 burn (&tctx, sizeof(tctx)); // Prevent leaks } -#endif - /**** Inner Digest ****/ - - sha256_begin (&ictx); - - /* Pad the key for inner digest */ - for (i = 0; i < lk; ++i) - buf[i] = (char) (k[i] ^ 0x36); - for (i = lk; i < SHA256_BLOCKSIZE; ++i) - buf[i] = 0x36; - - sha256_hash ((unsigned char *) buf, SHA256_BLOCKSIZE, &ictx); - sha256_hash ((unsigned char *) d, ld, &ictx); - - sha256_end ((unsigned char *) isha, &ictx); - - /**** Outer Digest ****/ - - sha256_begin (&octx); - - for (i = 0; i < lk; ++i) - buf[i] = (char) (k[i] ^ 0x5C); - for (i = lk; i < SHA256_BLOCKSIZE; ++i) - buf[i] = 0x5C; - - sha256_hash ((unsigned char *) buf, SHA256_BLOCKSIZE, &octx); - sha256_hash ((unsigned char *) isha, SHA256_DIGESTSIZE, &octx); - - sha256_end ((unsigned char *) osha, &octx); - - /* truncate and print the results */ - hmac_truncate (osha, out, SHA256_DIGESTSIZE); - + hmac_sha256_internal(k, lk, d, ld, &hmac); /* Prevent leaks */ - burn (&ictx, sizeof(ictx)); - burn (&octx, sizeof(octx)); - burn (isha, sizeof(isha)); - burn (osha, sizeof(osha)); - burn (buf, sizeof(buf)); -#ifndef TC_WINDOWS_BOOT - burn (key, sizeof(key)); -#endif + burn(&hmac, sizeof(hmac)); + burn(key, sizeof(key)); } +#endif - -void derive_u_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *u, int b) +static void derive_u_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, int b, hmac_sha256_ctx* hmac) { - char j[SHA256_DIGESTSIZE], k[SHA256_DIGESTSIZE]; - char init[128]; - char counter[4]; + char* k = hmac->k; + char* u = hmac->u; uint32 c; - int i; + int i; #ifdef TC_WINDOWS_BOOT /* In bootloader, iterations is a boolean : TRUE for boot derivation mode, FALSE otherwise @@ -135,35 +140,50 @@ void derive_u_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, int iter #endif /* iteration 1 */ - memset (counter, 0, 4); - counter[3] = (char) b; - memcpy (init, salt, salt_len); /* salt */ - memcpy (&init[salt_len], counter, 4); /* big-endian block number */ - hmac_sha256 (pwd, pwd_len, init, salt_len + 4, j); - memcpy (u, j, SHA256_DIGESTSIZE); + memcpy (k, salt, salt_len); /* salt */ + + /* big-endian block number */ + memset (&k[salt_len], 0, 3); + k[salt_len + 3] = (char) b; + + hmac_sha256_internal (pwd, pwd_len, k, salt_len + 4, hmac); + memcpy (u, k, SHA256_DIGESTSIZE); /* remaining iterations */ while (c > 1) { - hmac_sha256 (pwd, pwd_len, j, SHA256_DIGESTSIZE, k); + hmac_sha256_internal (pwd, pwd_len, k, SHA256_DIGESTSIZE, hmac); for (i = 0; i < SHA256_DIGESTSIZE; i++) { u[i] ^= k[i]; - j[i] = k[i]; } c--; } - - /* Prevent possible leaks. */ - burn (j, sizeof(j)); - burn (k, sizeof(k)); } void derive_key_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *dk, int dklen) -{ - char u[SHA256_DIGESTSIZE]; +{ + hmac_sha256_ctx hmac; int b, l, r; +#ifndef TC_WINDOWS_BOOT + char key[SHA256_DIGESTSIZE]; + /* If the password is longer than the hash algorithm block size, + let pwd = sha256(pwd), as per HMAC specifications. */ + if (pwd_len > SHA256_BLOCKSIZE) + { + sha256_ctx tctx; + + sha256_begin (&tctx); + sha256_hash ((unsigned char *) pwd, pwd_len, &tctx); + sha256_end ((unsigned char *) key, &tctx); + + pwd = key; + pwd_len = SHA256_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } +#endif if (dklen % SHA256_DIGESTSIZE) { @@ -179,43 +199,89 @@ void derive_key_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, int it /* first l - 1 blocks */ for (b = 1; b < l; b++) { - derive_u_sha256 (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, SHA256_DIGESTSIZE); + derive_u_sha256 (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, SHA256_DIGESTSIZE); dk += SHA256_DIGESTSIZE; } /* last block */ - derive_u_sha256 (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, r); + derive_u_sha256 (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, r); /* Prevent possible leaks. */ - burn (u, sizeof(u)); + burn (&hmac, sizeof(hmac)); +#ifndef TC_WINDOWS_BOOT + burn (key, sizeof(key)); +#endif } #endif #ifndef TC_WINDOWS_BOOT +typedef struct hmac_sha512_ctx_struct +{ + sha512_ctx ctx; + char buf[SHA512_BLOCKSIZE]; + char k[PKCS5_SALT_SIZE + 4]; /* enough to hold (salt_len + 4) and also the SHA512 hash */ + char u[SHA512_DIGESTSIZE]; +} hmac_sha512_ctx; + +void hmac_sha512_internal +( + char *k, /* secret key */ + int lk, /* length of the key in bytes */ + char *d, /* data and also output buffer of at least 64 bytes */ + int ld, /* length of data in bytes */ + hmac_sha512_ctx* hmac +) +{ + sha512_ctx* ctx = &(hmac->ctx); + char* buf = hmac->buf; + int i; + + /**** Inner Digest ****/ + + sha512_begin (ctx); + + /* Pad the key for inner digest */ + for (i = 0; i < lk; ++i) + buf[i] = (char) (k[i] ^ 0x36); + for (i = lk; i < SHA512_BLOCKSIZE; ++i) + buf[i] = 0x36; + + sha512_hash ((unsigned char *) buf, SHA512_BLOCKSIZE, ctx); + sha512_hash ((unsigned char *) d, ld, ctx); + + sha512_end ((unsigned char *) d, ctx); + + /**** Outer Digest ****/ + + sha512_begin (ctx); + + for (i = 0; i < lk; ++i) + buf[i] = (char) (k[i] ^ 0x5C); + for (i = lk; i < SHA512_BLOCKSIZE; ++i) + buf[i] = 0x5C; + + sha512_hash ((unsigned char *) buf, SHA512_BLOCKSIZE, ctx); + sha512_hash ((unsigned char *) d, SHA512_DIGESTSIZE, ctx); + + sha512_end ((unsigned char *) d, ctx); +} + void hmac_sha512 ( char *k, /* secret key */ int lk, /* length of the key in bytes */ - char *d, /* data */ - int ld, /* length of data in bytes */ - char *out, /* output buffer, at least "t" bytes */ - int t + char *d, /* data and also output buffer of at least 64 bytes */ + int ld /* length of data in bytes */ ) { - sha512_ctx ictx, octx; - char isha[SHA512_DIGESTSIZE], osha[SHA512_DIGESTSIZE]; -#ifndef TC_WINDOWS_BOOT + hmac_sha512_ctx hmac; char key[SHA512_DIGESTSIZE]; -#endif - char buf[SHA512_BLOCKSIZE]; - int i; -#ifndef TC_WINDOWS_BOOT /* If the key is longer than the hash algorithm block size, let key = sha512(key), as per HMAC specifications. */ if (lk > SHA512_BLOCKSIZE) @@ -231,88 +297,62 @@ void hmac_sha512 burn (&tctx, sizeof(tctx)); // Prevent leaks } -#endif - /**** Inner Digest ****/ - sha512_begin (&ictx); - - /* Pad the key for inner digest */ - for (i = 0; i < lk; ++i) - buf[i] = (char) (k[i] ^ 0x36); - for (i = lk; i < SHA512_BLOCKSIZE; ++i) - buf[i] = 0x36; - - sha512_hash ((unsigned char *) buf, SHA512_BLOCKSIZE, &ictx); - sha512_hash ((unsigned char *) d, ld, &ictx); - - sha512_end ((unsigned char *) isha, &ictx); - - /**** Outer Digest ****/ - - sha512_begin (&octx); - - for (i = 0; i < lk; ++i) - buf[i] = (char) (k[i] ^ 0x5C); - for (i = lk; i < SHA512_BLOCKSIZE; ++i) - buf[i] = 0x5C; - - sha512_hash ((unsigned char *) buf, SHA512_BLOCKSIZE, &octx); - sha512_hash ((unsigned char *) isha, SHA512_DIGESTSIZE, &octx); - - sha512_end ((unsigned char *) osha, &octx); - - /* truncate and print the results */ - t = t > SHA512_DIGESTSIZE ? SHA512_DIGESTSIZE : t; - hmac_truncate (osha, out, t); + hmac_sha512_internal (k, lk, d, ld, &hmac); /* Prevent leaks */ - burn (&ictx, sizeof(ictx)); - burn (&octx, sizeof(octx)); - burn (isha, sizeof(isha)); - burn (osha, sizeof(osha)); - burn (buf, sizeof(buf)); -#ifndef TC_WINDOWS_BOOT + burn (&hmac, sizeof(hmac)); burn (key, sizeof(key)); -#endif } - -void derive_u_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *u, int b) +static void derive_u_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, int b, hmac_sha512_ctx* hmac) { - char j[SHA512_DIGESTSIZE], k[SHA512_DIGESTSIZE]; - char init[128]; - char counter[4]; + char* k = hmac->k; + char* u = hmac->u; int c, i; /* iteration 1 */ - memset (counter, 0, 4); - counter[3] = (char) b; - memcpy (init, salt, salt_len); /* salt */ - memcpy (&init[salt_len], counter, 4); /* big-endian block number */ - hmac_sha512 (pwd, pwd_len, init, salt_len + 4, j, SHA512_DIGESTSIZE); - memcpy (u, j, SHA512_DIGESTSIZE); + memcpy (k, salt, salt_len); /* salt */ + /* big-endian block number */ + memset (&k[salt_len], 0, 3); + k[salt_len + 3] = (char) b; + + hmac_sha512_internal (pwd, pwd_len, k, salt_len + 4, hmac); + memcpy (u, k, SHA512_DIGESTSIZE); /* remaining iterations */ for (c = 1; c < iterations; c++) { - hmac_sha512 (pwd, pwd_len, j, SHA512_DIGESTSIZE, k, SHA512_DIGESTSIZE); + hmac_sha512_internal (pwd, pwd_len, k, SHA512_DIGESTSIZE, hmac); for (i = 0; i < SHA512_DIGESTSIZE; i++) { u[i] ^= k[i]; - j[i] = k[i]; } } - - /* Prevent possible leaks. */ - burn (j, sizeof(j)); - burn (k, sizeof(k)); } void derive_key_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *dk, int dklen) { - char u[SHA512_DIGESTSIZE]; + hmac_sha512_ctx hmac; int b, l, r; + char key[SHA512_DIGESTSIZE]; + + /* If the password is longer than the hash algorithm block size, + let pwd = sha512(pwd), as per HMAC specifications. */ + if (pwd_len > SHA512_BLOCKSIZE) + { + sha512_ctx tctx; + + sha512_begin (&tctx); + sha512_hash ((unsigned char *) pwd, pwd_len, &tctx); + sha512_end ((unsigned char *) key, &tctx); + + pwd = key; + pwd_len = SHA512_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } if (dklen % SHA512_DIGESTSIZE) { @@ -328,35 +368,85 @@ void derive_key_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, int it /* first l - 1 blocks */ for (b = 1; b < l; b++) { - derive_u_sha512 (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, SHA512_DIGESTSIZE); + derive_u_sha512 (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, SHA512_DIGESTSIZE); dk += SHA512_DIGESTSIZE; } /* last block */ - derive_u_sha512 (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, r); + derive_u_sha512 (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, r); /* Prevent possible leaks. */ - burn (u, sizeof(u)); + burn (&hmac, sizeof(hmac)); + burn (key, sizeof(key)); } #endif // TC_WINDOWS_BOOT #if !defined(TC_WINDOWS_BOOT) || defined(TC_WINDOWS_BOOT_RIPEMD160) -void hmac_ripemd160 (char *key, int keylen, char *input, int len, char *digest) +typedef struct hmac_ripemd160_ctx_struct { - RMD160_CTX context; - unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ - unsigned char k_opad[65]; /* outer padding - key XORd with opad */ -#ifndef TC_WINDOWS_BOOT - unsigned char tk[RIPEMD160_DIGESTSIZE]; -#endif - int i; + RMD160_CTX context; + char k_pad[65]; + char k[PKCS5_SALT_SIZE + 4]; /* enough to hold (salt_len + 4) and also the RIPEMD-160 hash */ + char u[RIPEMD160_DIGESTSIZE]; +} hmac_ripemd160_ctx; + +void hmac_ripemd160_internal (char *key, int keylen, char *input_digest, int len, hmac_ripemd160_ctx* hmac) +{ + RMD160_CTX* context = &(hmac->context); + unsigned char* k_pad = hmac->k_pad; /* inner/outer padding - key XORd with ipad */ + int i; + + /* + + RMD160(K XOR opad, RMD160(K XOR ipad, text)) + + where K is an n byte key + ipad is the byte 0x36 repeated RIPEMD160_BLOCKSIZE times + opad is the byte 0x5c repeated RIPEMD160_BLOCKSIZE times + and text is the data being protected */ + + + /* start out by storing key in pads */ + memset(k_pad, 0x36, 65); + + /* XOR key with ipad and opad values */ + for (i=0; i RIPEMD160_BLOCKSIZE) @@ -372,56 +462,19 @@ void hmac_ripemd160 (char *key, int keylen, char *input, int len, char *digest) burn (&tctx, sizeof(tctx)); // Prevent leaks } -#endif - /* - RMD160(K XOR opad, RMD160(K XOR ipad, text)) + hmac_ripemd160_internal (key, keylen, input_digest, len, &hmac); - where K is an n byte key - ipad is the byte 0x36 repeated RIPEMD160_BLOCKSIZE times - opad is the byte 0x5c repeated RIPEMD160_BLOCKSIZE times - and text is the data being protected */ - - - /* start out by storing key in pads */ - memset(k_ipad, 0x36, sizeof(k_ipad)); - memset(k_opad, 0x5c, sizeof(k_opad)); - - /* XOR key with ipad and opad values */ - for (i=0; ik; + char* u = hmac->u; uint32 c; int i; @@ -438,34 +491,49 @@ void derive_u_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, int i #endif /* iteration 1 */ - memset (counter, 0, 4); - counter[3] = (char) b; - memcpy (init, salt, salt_len); /* salt */ - memcpy (&init[salt_len], counter, 4); /* big-endian block number */ - hmac_ripemd160 (pwd, pwd_len, init, salt_len + 4, j); - memcpy (u, j, RIPEMD160_DIGESTSIZE); + memcpy (k, salt, salt_len); /* salt */ + + /* big-endian block number */ + memset (&k[salt_len], 0, 3); + k[salt_len + 3] = (char) b; + + hmac_ripemd160_internal (pwd, pwd_len, k, salt_len + 4, hmac); + memcpy (u, k, RIPEMD160_DIGESTSIZE); /* remaining iterations */ while ( c > 1) { - hmac_ripemd160 (pwd, pwd_len, j, RIPEMD160_DIGESTSIZE, k); + hmac_ripemd160_internal (pwd, pwd_len, k, RIPEMD160_DIGESTSIZE, hmac); for (i = 0; i < RIPEMD160_DIGESTSIZE; i++) { u[i] ^= k[i]; - j[i] = k[i]; } c--; } - - /* Prevent possible leaks. */ - burn (j, sizeof(j)); - burn (k, sizeof(k)); } void derive_key_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *dk, int dklen) -{ - char u[RIPEMD160_DIGESTSIZE]; +{ int b, l, r; + hmac_ripemd160_ctx hmac; +#ifndef TC_WINDOWS_BOOT + unsigned char tk[RIPEMD160_DIGESTSIZE]; + /* If the password is longer than the hash algorithm block size, + let password = ripemd160(password), as per HMAC specifications. */ + if (pwd_len > RIPEMD160_BLOCKSIZE) + { + RMD160_CTX tctx; + + RMD160Init(&tctx); + RMD160Update(&tctx, (const unsigned char *) pwd, pwd_len); + RMD160Final(tk, &tctx); + + pwd = (char *) tk; + pwd_len = RIPEMD160_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } +#endif if (dklen % RIPEMD160_DIGESTSIZE) { @@ -481,42 +549,87 @@ void derive_key_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, int /* first l - 1 blocks */ for (b = 1; b < l; b++) { - derive_u_ripemd160 (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, RIPEMD160_DIGESTSIZE); + derive_u_ripemd160 (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, RIPEMD160_DIGESTSIZE); dk += RIPEMD160_DIGESTSIZE; } /* last block */ - derive_u_ripemd160 (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, r); + derive_u_ripemd160 (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, r); /* Prevent possible leaks. */ - burn (u, sizeof(u)); + burn (&hmac, sizeof(hmac)); +#ifndef TC_WINDOWS_BOOT + burn (tk, sizeof(tk)); +#endif } #endif // TC_WINDOWS_BOOT #ifndef TC_WINDOWS_BOOT +typedef struct hmac_whirlpool_ctx_struct +{ + WHIRLPOOL_CTX ctx; + char buf[WHIRLPOOL_BLOCKSIZE]; + char k[PKCS5_SALT_SIZE + 4]; /* enough to hold (salt_len + 4) and also the Whirlpool hash */ + char u[WHIRLPOOL_DIGESTSIZE]; +} hmac_whirlpool_ctx; + +void hmac_whirlpool_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_whirlpool_ctx* hmac /* HMAC-Whirlpool context which holds temporary variables */ +) +{ + WHIRLPOOL_CTX* ctx = &(hmac->ctx); + char* buf = hmac->buf; + int i; + + /**** Inner Digest ****/ + + WHIRLPOOL_init (ctx); + + /* Pad the key for inner digest */ + for (i = 0; i < lk; ++i) + buf[i] = (char) (k[i] ^ 0x36); + for (i = lk; i < WHIRLPOOL_BLOCKSIZE; ++i) + buf[i] = 0x36; + + WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, ctx); + WHIRLPOOL_add ((unsigned char *) d, ld * 8, ctx); + + WHIRLPOOL_finalize (ctx, (unsigned char *) d); + + /**** Outer Digest ****/ + + WHIRLPOOL_init (ctx); + + for (i = 0; i < lk; ++i) + buf[i] = (char) (k[i] ^ 0x5C); + for (i = lk; i < WHIRLPOOL_BLOCKSIZE; ++i) + buf[i] = 0x5C; + + WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, ctx); + WHIRLPOOL_add ((unsigned char *) d, WHIRLPOOL_DIGESTSIZE * 8, ctx); + + WHIRLPOOL_finalize (ctx, (unsigned char *) d); +} + void hmac_whirlpool ( char *k, /* secret key */ int lk, /* length of the key in bytes */ - char *d, /* data */ - int ld, /* length of data in bytes */ - char *out, /* output buffer, at least "t" bytes */ - int t + char *d, /* input data. d pointer is guaranteed to be at least 32-bytes long */ + int ld /* length of data in bytes */ ) { - WHIRLPOOL_CTX ictx, octx; - char iwhi[WHIRLPOOL_DIGESTSIZE], owhi[WHIRLPOOL_DIGESTSIZE]; -#ifndef TC_WINDOWS_BOOT + hmac_whirlpool_ctx hmac; char key[WHIRLPOOL_DIGESTSIZE]; -#endif - char buf[WHIRLPOOL_BLOCKSIZE]; - int i; - -#ifndef TC_WINDOWS_BOOT /* If the key is longer than the hash algorithm block size, let key = whirlpool(key), as per HMAC specifications. */ if (lk > WHIRLPOOL_BLOCKSIZE) @@ -532,86 +645,58 @@ void hmac_whirlpool burn (&tctx, sizeof(tctx)); // Prevent leaks } -#endif - /**** Inner Digest ****/ - WHIRLPOOL_init (&ictx); - - /* Pad the key for inner digest */ - for (i = 0; i < lk; ++i) - buf[i] = (char) (k[i] ^ 0x36); - for (i = lk; i < WHIRLPOOL_BLOCKSIZE; ++i) - buf[i] = 0x36; - - WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, &ictx); - WHIRLPOOL_add ((unsigned char *) d, ld * 8, &ictx); - - WHIRLPOOL_finalize (&ictx, (unsigned char *) iwhi); - - /**** Outer Digest ****/ - - WHIRLPOOL_init (&octx); - - for (i = 0; i < lk; ++i) - buf[i] = (char) (k[i] ^ 0x5C); - for (i = lk; i < WHIRLPOOL_BLOCKSIZE; ++i) - buf[i] = 0x5C; - - WHIRLPOOL_add ((unsigned char *) buf, WHIRLPOOL_BLOCKSIZE * 8, &octx); - WHIRLPOOL_add ((unsigned char *) iwhi, WHIRLPOOL_DIGESTSIZE * 8, &octx); - - WHIRLPOOL_finalize (&octx, (unsigned char *) owhi); - - /* truncate and print the results */ - t = t > WHIRLPOOL_DIGESTSIZE ? WHIRLPOOL_DIGESTSIZE : t; - hmac_truncate (owhi, out, t); - - /* Prevent possible leaks. */ - burn (&ictx, sizeof(ictx)); - burn (&octx, sizeof(octx)); - burn (owhi, sizeof(owhi)); - burn (iwhi, sizeof(iwhi)); - burn (buf, sizeof(buf)); -#ifndef TC_WINDOWS_BOOT - burn (key, sizeof(key)); -#endif + hmac_whirlpool_internal(k, lk, d, ld, &hmac); + /* Prevent leaks */ + burn(&hmac, sizeof(hmac)); } -void derive_u_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *u, int b) +static void derive_u_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, int b, hmac_whirlpool_ctx* hmac) { - char j[WHIRLPOOL_DIGESTSIZE], k[WHIRLPOOL_DIGESTSIZE]; - char init[128]; - char counter[4]; + char* u = hmac->u; + char* k = hmac->k; int c, i; /* iteration 1 */ - memset (counter, 0, 4); - counter[3] = (char) b; - memcpy (init, salt, salt_len); /* salt */ - memcpy (&init[salt_len], counter, 4); /* big-endian block number */ - hmac_whirlpool (pwd, pwd_len, init, salt_len + 4, j, WHIRLPOOL_DIGESTSIZE); - memcpy (u, j, WHIRLPOOL_DIGESTSIZE); + memcpy (k, salt, salt_len); /* salt */ + /* big-endian block number */ + memset (&k[salt_len], 0, 3); + k[salt_len + 3] = (char) b; + + hmac_whirlpool_internal (pwd, pwd_len, k, salt_len + 4, hmac); + memcpy (u, k, WHIRLPOOL_DIGESTSIZE); /* remaining iterations */ for (c = 1; c < iterations; c++) { - hmac_whirlpool (pwd, pwd_len, j, WHIRLPOOL_DIGESTSIZE, k, WHIRLPOOL_DIGESTSIZE); + hmac_whirlpool_internal (pwd, pwd_len, k, WHIRLPOOL_DIGESTSIZE, hmac); for (i = 0; i < WHIRLPOOL_DIGESTSIZE; i++) { u[i] ^= k[i]; - j[i] = k[i]; } } - - /* Prevent possible leaks. */ - burn (j, sizeof(j)); - burn (k, sizeof(k)); } void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *dk, int dklen) { - char u[WHIRLPOOL_DIGESTSIZE]; + hmac_whirlpool_ctx hmac; + char key[WHIRLPOOL_DIGESTSIZE]; int b, l, r; + /* If the password is longer than the hash algorithm block size, + let pwd = whirlpool(pwd), as per HMAC specifications. */ + if (pwd_len > WHIRLPOOL_BLOCKSIZE) + { + WHIRLPOOL_CTX tctx; + + WHIRLPOOL_init (&tctx); + WHIRLPOOL_add ((unsigned char *) pwd, pwd_len * 8, &tctx); + WHIRLPOOL_finalize (&tctx, (unsigned char *) key); + + pwd = key; + pwd_len = WHIRLPOOL_DIGESTSIZE; + + burn (&tctx, sizeof(tctx)); // Prevent leaks + } if (dklen % WHIRLPOOL_DIGESTSIZE) { @@ -627,18 +712,19 @@ void derive_key_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, int /* first l - 1 blocks */ for (b = 1; b < l; b++) { - derive_u_whirlpool (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, WHIRLPOOL_DIGESTSIZE); + derive_u_whirlpool (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, WHIRLPOOL_DIGESTSIZE); dk += WHIRLPOOL_DIGESTSIZE; } /* last block */ - derive_u_whirlpool (pwd, pwd_len, salt, salt_len, iterations, u, b); - memcpy (dk, u, r); + derive_u_whirlpool (pwd, pwd_len, salt, salt_len, iterations, b, &hmac); + memcpy (dk, hmac.u, r); /* Prevent possible leaks. */ - burn (u, sizeof(u)); + burn (&hmac, sizeof(hmac)); + burn (key, sizeof(key)); } diff --git a/src/Common/Pkcs5.h b/src/Common/Pkcs5.h index d7ab90db..ef931397 100644 --- a/src/Common/Pkcs5.h +++ b/src/Common/Pkcs5.h @@ -18,19 +18,22 @@ extern "C" { #endif -void hmac_sha256 (char *k, int lk, char *d, int ld, char *out); -void derive_u_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *u, int b); +/* output written to d which must be at lease 32 bytes long */ +void hmac_sha256 (char *k, int lk, char *d, int ld); void derive_key_sha256 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *dk, int dklen); -void hmac_sha512 (char *k, int lk, char *d, int ld, char *out, int t); -void derive_u_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *u, int b); +/* output written to d which must be at lease 64 bytes long */ +void hmac_sha512 (char *k, int lk, char *d, int ld); void derive_key_sha512 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *dk, int dklen); -void hmac_ripemd160 (char *key, int keylen, char *input, int len, char *digest); -void derive_u_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *u, int b); + +/* output written to input_digest which must be at lease 20 bytes long */ +void hmac_ripemd160 (char *key, int keylen, char *input_digest, int len); void derive_key_ripemd160 (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *dk, int dklen); -void hmac_whirlpool (char *k, int lk, char *d, int ld, char *out, int t); -void derive_u_whirlpool (char *pwd, int pwd_len, char *salt, int salt_len, int iterations, char *u, int b); + +/* output written to d which must be at lease 64 bytes long */ +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, int iterations, char *dk, int dklen); + int get_pkcs5_iteration_count (int pkcs5_prf_id, BOOL truecryptMode, BOOL bBoot); char *get_pkcs5_prf_name (int pkcs5_prf_id); diff --git a/src/Common/Tests.c b/src/Common/Tests.c index c5aea91d..6e2b0d95 100644 --- a/src/Common/Tests.c +++ b/src/Common/Tests.c @@ -1016,8 +1016,9 @@ BOOL test_hmac_sha256 () for (i = 0; i < sizeof (hmac_sha256_test_data) / sizeof(char *); i++) { - char digest[SHA256_DIGESTSIZE]; - hmac_sha256 (hmac_sha256_test_keys[i], (int) strlen (hmac_sha256_test_keys[i]), hmac_sha256_test_data[i], (int) strlen (hmac_sha256_test_data[i]), digest); + char digest[1024]; /* large enough to hold digets and test vector inputs */ + memcpy (digest, hmac_sha256_test_data[i], strlen (hmac_sha256_test_data[i])); + hmac_sha256 (hmac_sha256_test_keys[i], (int) strlen (hmac_sha256_test_keys[i]), digest, (int) strlen (hmac_sha256_test_data[i])); if (memcmp (digest, hmac_sha256_test_vectors[i], SHA256_DIGESTSIZE) != 0) return FALSE; else @@ -1034,8 +1035,9 @@ BOOL test_hmac_sha512 () for (i = 0; i < sizeof (hmac_sha512_test_data) / sizeof(char *); i++) { - char digest[SHA512_DIGESTSIZE]; - hmac_sha512 (hmac_sha512_test_keys[i], (int) strlen (hmac_sha512_test_keys[i]), hmac_sha512_test_data[i], (int) strlen (hmac_sha512_test_data[i]), digest, SHA512_DIGESTSIZE); + char digest[1024]; /* large enough to hold digets and test vector inputs */ + memcpy (digest, hmac_sha512_test_data[i], (int) strlen (hmac_sha512_test_data[i])); + hmac_sha512 (hmac_sha512_test_keys[i], (int) strlen (hmac_sha512_test_keys[i]), digest, (int) strlen (hmac_sha512_test_data[i])); if (memcmp (digest, hmac_sha512_test_vectors[i], SHA512_DIGESTSIZE) != 0) return FALSE; else @@ -1052,8 +1054,9 @@ BOOL test_hmac_ripemd160 () for (i = 0; i < sizeof (hmac_ripemd160_test_data) / sizeof(char *); i++) { - char digest[RIPEMD160_DIGESTSIZE]; - hmac_ripemd160 (hmac_ripemd160_test_keys[i], RIPEMD160_DIGESTSIZE, hmac_ripemd160_test_data[i], (int) strlen (hmac_ripemd160_test_data[i]), digest); + char digest[1024]; /* large enough to hold digets and test vector inputs */ + memcpy (digest, hmac_ripemd160_test_data[i], strlen (hmac_ripemd160_test_data[i])); + hmac_ripemd160 (hmac_ripemd160_test_keys[i], RIPEMD160_DIGESTSIZE, digest, (int) strlen (hmac_ripemd160_test_data[i])); if (memcmp (digest, hmac_ripemd160_test_vectors[i], RIPEMD160_DIGESTSIZE) != 0) return FALSE; else @@ -1065,9 +1068,10 @@ BOOL test_hmac_ripemd160 () BOOL test_hmac_whirlpool () { - unsigned char digest[WHIRLPOOL_DIGESTSIZE]; + unsigned char digest[1024]; /* large enough to hold digets and test vector inputs */ - hmac_whirlpool (hmac_whirlpool_test_key, 64, hmac_whirlpool_test_data, (int) strlen (hmac_whirlpool_test_data), digest, WHIRLPOOL_DIGESTSIZE); + memcpy (digest, hmac_whirlpool_test_data, strlen (hmac_whirlpool_test_data)); + hmac_whirlpool (hmac_whirlpool_test_key, 64, digest, (int) strlen (hmac_whirlpool_test_data)); if (memcmp (digest, hmac_whirlpool_test_vectors, WHIRLPOOL_DIGESTSIZE) != 0) return FALSE;