diff options
author | Mounir IDRASSI <mounir.idrassi@idrix.fr> | 2019-02-12 18:49:12 +0100 |
---|---|---|
committer | Mounir IDRASSI <mounir.idrassi@idrix.fr> | 2019-02-12 19:06:14 +0100 |
commit | 86f0fde6e7914f055c5872bf7f2f565cc09977fc (patch) | |
tree | fea427f46509ccaa1cb77ec233cb2ab41157576e /src | |
parent | a5943c07fbc2754e0785cfa3d4645e96ae87b405 (diff) | |
download | VeraCrypt-86f0fde6e7914f055c5872bf7f2f565cc09977fc.tar.gz VeraCrypt-86f0fde6e7914f055c5872bf7f2f565cc09977fc.zip |
Windows: Use Hardware RNG based on CPU timing jitter "Jitterentropy" by Stephan Mueller as a good alternative to RDRAND (http://www.chronox.de/jent.html, smueller@chronox.de)
Diffstat (limited to 'src')
-rw-r--r-- | src/Common/Random.c | 27 | ||||
-rw-r--r-- | src/Crypto/Crypto.vcxproj | 6 | ||||
-rw-r--r-- | src/Crypto/Crypto.vcxproj.filters | 9 | ||||
-rw-r--r-- | src/Crypto/GostCipher.h | 5 | ||||
-rw-r--r-- | src/Crypto/Sources | 1 | ||||
-rw-r--r-- | src/Crypto/cpu.h | 17 | ||||
-rw-r--r-- | src/Crypto/jitterentropy-base-user.h | 136 | ||||
-rw-r--r-- | src/Crypto/jitterentropy-base.c | 713 | ||||
-rw-r--r-- | src/Crypto/jitterentropy.h | 155 | ||||
-rw-r--r-- | src/Driver/Driver.vcxproj | 1 | ||||
-rw-r--r-- | src/Driver/Driver.vcxproj.filters | 3 | ||||
-rw-r--r-- | src/Driver/Ntdriver.c | 16 |
12 files changed, 1084 insertions, 5 deletions
diff --git a/src/Common/Random.c b/src/Common/Random.c index c8655b56..1c6b9530 100644 --- a/src/Common/Random.c +++ b/src/Common/Random.c @@ -15,6 +15,7 @@ #include "Crc.h" #include "Random.h" #include "Crypto\cpu.h" +#include "Crypto\jitterentropy.h" #include "Crypto\rdrand.h" #include <Strsafe.h> @@ -776,6 +777,19 @@ BOOL SlowPoll (void) return FALSE; } + /* use JitterEntropy library to get good quality random bytes based on CPU timing jitter */ + if (0 == jent_entropy_init ()) + { + struct rand_data *ec = jent_entropy_collector_alloc (1, 0); + if (ec) + { + ssize_t rndLen = jent_read_entropy (ec, (char*) buffer, sizeof (buffer)); + if (rndLen > 0) + RandaddBuf (buffer, (int) rndLen); + jent_entropy_collector_free (ec); + } + } + // use RDSEED or RDRAND from CPU as source of entropy if present if ( IsCpuRngEnabled() && ( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer))) @@ -908,6 +922,19 @@ BOOL FastPoll (void) return FALSE; } + /* use JitterEntropy library to get good quality random bytes based on CPU timing jitter */ + if (0 == jent_entropy_init ()) + { + struct rand_data *ec = jent_entropy_collector_alloc (1, 0); + if (ec) + { + ssize_t rndLen = jent_read_entropy (ec, (char*) buffer, sizeof (buffer)); + if (rndLen > 0) + RandaddBuf (buffer, (int) rndLen); + jent_entropy_collector_free (ec); + } + } + // use RDSEED or RDRAND from CPU as source of entropy if enabled if ( IsCpuRngEnabled() && ( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer))) diff --git a/src/Crypto/Crypto.vcxproj b/src/Crypto/Crypto.vcxproj index 5fb52d97..9f351bee 100644 --- a/src/Crypto/Crypto.vcxproj +++ b/src/Crypto/Crypto.vcxproj @@ -220,6 +220,10 @@ <ClCompile Include="chachaRng.c" /> <ClCompile Include="cpu.c" /> <ClCompile Include="GostCipher.c" /> + <ClCompile Include="jitterentropy-base.c"> + <Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Disabled</Optimization> + <Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Disabled</Optimization> + </ClCompile> <ClCompile Include="kuznyechik.c" /> <ClCompile Include="kuznyechik_simd.c" /> <ClCompile Include="rdrand.c" /> @@ -244,6 +248,8 @@ <ClInclude Include="config.h" /> <ClInclude Include="cpu.h" /> <ClInclude Include="GostCipher.h" /> + <ClInclude Include="jitterentropy-base-user.h" /> + <ClInclude Include="jitterentropy.h" /> <ClInclude Include="kuznyechik.h" /> <ClInclude Include="misc.h" /> <ClInclude Include="rdrand.h" /> diff --git a/src/Crypto/Crypto.vcxproj.filters b/src/Crypto/Crypto.vcxproj.filters index abf81655..7a8da57e 100644 --- a/src/Crypto/Crypto.vcxproj.filters +++ b/src/Crypto/Crypto.vcxproj.filters @@ -69,6 +69,9 @@ <ClCompile Include="chachaRng.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="jitterentropy-base.c"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="Aes.h"> @@ -137,6 +140,12 @@ <ClInclude Include="chachaRng.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="jitterentropy.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="jitterentropy-base-user.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <CustomBuild Include="Aes_hw_cpu.asm"> diff --git a/src/Crypto/GostCipher.h b/src/Crypto/GostCipher.h index 6031c438..bcb77207 100644 --- a/src/Crypto/GostCipher.h +++ b/src/Crypto/GostCipher.h @@ -14,6 +14,7 @@ #include "Common/Tcdefs.h" #include "config.h" +#include "misc.h" #ifdef __cplusplus extern "C" { @@ -30,10 +31,6 @@ extern "C" { #if defined(CIPHER_GOST89) -#ifndef rotl32 -#define rotl32(b, shift) ((b << shift) | (b >> (32 - shift))) -#endif - #ifdef GST_WINDOWS_BOOT typedef int gst_word; typedef long gst_dword; diff --git a/src/Crypto/Sources b/src/Crypto/Sources index 5c44c371..36fa89e7 100644 --- a/src/Crypto/Sources +++ b/src/Crypto/Sources @@ -31,6 +31,7 @@ SOURCES = \ chacha256.c \ chachaRng.c \ cpu.c \ + jitterentropy-base.c \ rdrand.c \ Rmd160.c \ SerpentFast.c \ diff --git a/src/Crypto/cpu.h b/src/Crypto/cpu.h index 9fac453b..12576b47 100644 --- a/src/Crypto/cpu.h +++ b/src/Crypto/cpu.h @@ -25,6 +25,23 @@ #define ATT_NOPREFIX #endif +#if CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64 +#if defined(TC_WINDOWS_DRIVER) || defined (_UEFI) +#if defined(__cplusplus) +extern "C" { +#endif +extern unsigned __int64 __rdtsc(); +#if defined(__cplusplus) +} +#endif +#else +#include <intrin.h> +#ifdef _MSC_VER +#pragma intrinsic(__rdtsc) +#endif +#endif +#endif + #ifdef CRYPTOPP_GENERATE_X64_MASM #define CRYPTOPP_X86_ASM_AVAILABLE diff --git a/src/Crypto/jitterentropy-base-user.h b/src/Crypto/jitterentropy-base-user.h new file mode 100644 index 00000000..cbb2f47e --- /dev/null +++ b/src/Crypto/jitterentropy-base-user.h @@ -0,0 +1,136 @@ +/* + * Non-physical true random number generator based on timing jitter. + * + * Copyright Stephan Mueller <smueller@chronox.de>, 2013 + * + * License + * ======= + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU General Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + e USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +/* Adapted for VeraCrypt */ + +#pragma once + +#include "Common/Tcdefs.h" +#include "misc.h" +#include "cpu.h" +#include <stdlib.h> +#include <string.h> + +typedef uint64 __u64; + +#ifdef _MSC_VER + +typedef uint64 uint64_t; +typedef int64 int64_t; + +#ifndef _UEFI +#define CONFIG_CRYPTO_CPU_JITTERENTROPY_SECURE_MEMORY +#endif + +#ifndef _UEFI +typedef SSIZE_T ssize_t; +#else +#if CRYPTOPP_BOOL_X64 +typedef int64 ssize_t; +#else +typedef int32 ssize_t; +#endif +#endif + +static VC_INLINE void jent_get_nstime(__u64 *out) +{ + *out = __rdtsc();; +} + +#else + +/* taken from Linux kernel */ +#if CRYPTOPP_BOOL_X64 +#define DECLARE_ARGS(val, low, high) unsigned low, high +#define EAX_EDX_VAL(val, low, high) ((low) | ((__u64)(high) << 32)) +#define EAX_EDX_RET(val, low, high) "=a" (low), "=d" (high) +#else +#define DECLARE_ARGS(val, low, high) unsigned long long val +#define EAX_EDX_VAL(val, low, high) (val) +#define EAX_EDX_RET(val, low, high) "=A" (val) +#endif + +static VC_INLINE void jent_get_nstime(__u64 *out) +{ + DECLARE_ARGS(val, low, high); + asm volatile("rdtsc" : EAX_EDX_RET(val, low, high)); + *out = EAX_EDX_VAL(val, low, high); +} + +#endif + +static VC_INLINE void *jent_zalloc(size_t len) +{ + void *tmp = NULL; + tmp = TCalloc(len); + if(NULL != tmp) + { + memset(tmp, 0, len); +#if defined(_WIN32) && !defined(TC_WINDOWS_DRIVER) && !defined(_UEFI) + VirtualLock (tmp, len); +#endif + } + return tmp; +} + +static VC_INLINE void jent_zfree(void *ptr, unsigned int len) +{ + if (len % 8) + burn(ptr, len); + else + FAST_ERASE64(ptr, len); +#if defined(_WIN32) && !defined(TC_WINDOWS_DRIVER) && !defined(_UEFI) + VirtualUnlock (ptr, len); +#endif + TCfree(ptr); +} + +static VC_INLINE int jent_fips_enabled(void) +{ + return 0; +} + +/* --- helpers needed in user space -- */ + +#define rol64(x,n) rotl64(x,n) + + + diff --git a/src/Crypto/jitterentropy-base.c b/src/Crypto/jitterentropy-base.c new file mode 100644 index 00000000..c05f0c37 --- /dev/null +++ b/src/Crypto/jitterentropy-base.c @@ -0,0 +1,713 @@ +/* + * Non-physical true random number generator based on timing jitter. + * + * Copyright Stephan Mueller <smueller@chronox.de>, 2014 - 2018 + * + * Design + * ====== + * + * See documentation in doc/ folder. + * + * Interface + * ========= + * + * See documentation in doc/ folder. + * + * License + * ======= + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU General Public License, in which case the provisions of the GPL2 are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +/* Adapted for VeraCrypt */ + +#undef _FORTIFY_SOURCE + +#ifdef _MSC_VER +#pragma optimize( "", off ) +#pragma warning(disable:4242 4244 4334) /* disable warnings on the original code */ +#else +#pragma GCC optimize ("O0") +#endif + +#include "jitterentropy.h" + +#ifndef CONFIG_CRYPTO_CPU_JITTERENTROPY_STAT + /* only check optimization in a compilation for real work */ + #ifdef __OPTIMIZE__ + #error "The CPU Jitter random number generator must not be compiled with optimizations. See documentation. Use the compiler switch -O0 for compiling jitterentropy-base.c." + #endif +#endif + +#define MAJVERSION 2 /* API / ABI incompatible changes, functional changes that + * require consumer to be updated (as long as this number + * is zero, the API is not considered stable and can + * change without a bump of the major version) */ +#define MINVERSION 1 /* API compatible, ABI may change, functional + * enhancements only, consumer can be left unchanged if + * enhancements are not considered */ +#define PATCHLEVEL 2 /* API / ABI compatible, no functional changes, no + * enhancements, bug fixes only */ + +/** + * jent_version() - Return machine-usable version number of jent library + * + * The function returns a version number that is monotonic increasing + * for newer versions. The version numbers are multiples of 100. For example, + * version 1.2.3 is converted to 1020300 -- the last two digits are reserved + * for future use. + * + * The result of this function can be used in comparing the version number + * in a calling program if version-specific calls need to be make. + * + * Return: Version number of jitterentropy library + */ +JENT_PRIVATE_STATIC +unsigned int jent_version(void) +{ + unsigned int version = 0; + + version = MAJVERSION * 1000000; + version += MINVERSION * 10000; + version += PATCHLEVEL * 100; + + return version; +} + +/** + * Update of the loop count used for the next round of + * an entropy collection. + * + * Input: + * @ec entropy collector struct -- may be NULL + * @bits is the number of low bits of the timer to consider + * @min is the number of bits we shift the timer value to the right at + * the end to make sure we have a guaranteed minimum value + * + * @return Newly calculated loop counter + */ +static uint64_t jent_loop_shuffle(struct rand_data *ec, + unsigned int bits, unsigned int min) +{ + uint64_t time = 0; + uint64_t shuffle = 0; + unsigned int i = 0; + unsigned int mask = (1<<bits) - 1; + + jent_get_nstime(&time); + /* + * Mix the current state of the random number into the shuffle + * calculation to balance that shuffle a bit more. + */ + if (ec) + time ^= ec->data; + /* + * We fold the time value as much as possible to ensure that as many + * bits of the time stamp are included as possible. + */ + for (i = 0; ((DATA_SIZE_BITS + bits - 1) / bits) > i; i++) { + shuffle ^= time & mask; + time = time >> bits; + } + + /* + * We add a lower boundary value to ensure we have a minimum + * RNG loop count. + */ + return (shuffle + (1<<min)); +} + +/*************************************************************************** + * Noise sources + ***************************************************************************/ + +/** + * CPU Jitter noise source -- this is the noise source based on the CPU + * execution time jitter + * + * This function injects the individual bits of the time value into the + * entropy pool using an LFSR. + * + * The code is deliberately inefficient with respect to the bit shifting + * and shall stay that way. This function is the root cause why the code + * shall be compiled without optimization. This function not only acts as + * folding operation, but this function's execution is used to measure + * the CPU execution time jitter. Any change to the loop in this function + * implies that careful retesting must be done. + * + * Input: + * @ec entropy collector struct -- may be NULL + * @time time stamp to be injected + * @loop_cnt if a value not equal to 0 is set, use the given value as number of + * loops to perform the folding + * + * Output: + * updated ec->data + * + * @return Number of loops the folding operation is performed + */ +static uint64_t jent_lfsr_time(struct rand_data *ec, uint64_t time, + uint64_t loop_cnt) +{ + unsigned int i; + uint64_t j = 0; + uint64_t new = 0; +#define MAX_FOLD_LOOP_BIT 4 +#define MIN_FOLD_LOOP_BIT 0 + uint64_t fold_loop_cnt = + jent_loop_shuffle(ec, MAX_FOLD_LOOP_BIT, MIN_FOLD_LOOP_BIT); + + /* + * testing purposes -- allow test app to set the counter, not + * needed during runtime + */ + if (loop_cnt) + fold_loop_cnt = loop_cnt; + for (j = 0; j < fold_loop_cnt; j++) { + new = ec->data; + for (i = 1; (DATA_SIZE_BITS) >= i; i++) { + uint64_t tmp = time << (DATA_SIZE_BITS - i); + + tmp = tmp >> (DATA_SIZE_BITS - 1); + + /* + * Fibonacci LSFR with polynomial of + * x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is + * primitive according to + * http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf + * (the shift values are the polynomial values minus one + * due to counting bits from 0 to 63). As the current + * position is always the LSB, the polynomial only needs + * to shift data in from the left without wrap. + */ + tmp ^= ((new >> 63) & 1); + tmp ^= ((new >> 60) & 1); + tmp ^= ((new >> 55) & 1); + tmp ^= ((new >> 30) & 1); + tmp ^= ((new >> 27) & 1); + tmp ^= ((new >> 22) & 1); + new <<= 1; + new ^= tmp; + } + } + ec->data = new; + + return fold_loop_cnt; +} + +/** + * Memory Access noise source -- this is a noise source based on variations in + * memory access times + * + * This function performs memory accesses which will add to the timing + * variations due to an unknown amount of CPU wait states that need to be + * added when accessing memory. The memory size should be larger than the L1 + * caches as outlined in the documentation and the associated testing. + * + * The L1 cache has a very high bandwidth, albeit its access rate is usually + * slower than accessing CPU registers. Therefore, L1 accesses only add minimal + * variations as the CPU has hardly to wait. Starting with L2, significant + * variations are added because L2 typically does not belong to the CPU any more + * and therefore a wider range of CPU wait states is necessary for accesses. + * L3 and real memory accesses have even a wider range of wait states. However, + * to reliably access either L3 or memory, the ec->mem memory must be quite + * large which is usually not desirable. + * + * Input: + * @ec Reference to the entropy collector with the memory access data -- if + * the reference to the memory block to be accessed is NULL, this noise + * source is disabled + * @loop_cnt if a value not equal to 0 is set, use the given value as number of + * loops to perform the folding + * + * @return Number of memory access operations + */ +static unsigned int jent_memaccess(struct rand_data *ec, uint64_t loop_cnt) +{ + unsigned int wrap = 0; + uint64_t i = 0; +#define MAX_ACC_LOOP_BIT 7 +#define MIN_ACC_LOOP_BIT 0 + uint64_t acc_loop_cnt = + jent_loop_shuffle(ec, MAX_ACC_LOOP_BIT, MIN_ACC_LOOP_BIT); + + if (NULL == ec || NULL == ec->mem) + return 0; + wrap = ec->memblocksize * ec->memblocks; + + /* + * testing purposes -- allow test app to set the counter, not + * needed during runtime + */ + if (loop_cnt) + acc_loop_cnt = loop_cnt; + + for (i = 0; i < (ec->memaccessloops + acc_loop_cnt); i++) { + unsigned char *tmpval = ec->mem + ec->memlocation; + /* + * memory access: just add 1 to one byte, + * wrap at 255 -- memory access implies read + * from and write to memory location + */ + *tmpval = (*tmpval + 1) & 0xff; + /* + * Addition of memblocksize - 1 to pointer + * with wrap around logic to ensure that every + * memory location is hit evenly + */ + ec->memlocation = ec->memlocation + ec->memblocksize - 1; + ec->memlocation = ec->memlocation % wrap; + } + return i; +} + +/*************************************************************************** + * Start of entropy processing logic + ***************************************************************************/ + +/** + * Stuck test by checking the: + * 1st derivation of the jitter measurement (time delta) + * 2nd derivation of the jitter measurement (delta of time deltas) + * 3rd derivation of the jitter measurement (delta of delta of time deltas) + * + * All values must always be non-zero. + * + * Input: + * @ec Reference to entropy collector + * @current_delta Jitter time delta + * + * @return + * 0 jitter measurement not stuck (good bit) + * 1 jitter measurement stuck (reject bit) + */ +static int jent_stuck(struct rand_data *ec, uint64_t current_delta) +{ + int64_t delta2 = ec->last_delta - current_delta; + int64_t delta3 = delta2 - ec->last_delta2; + + ec->last_delta = current_delta; + ec->last_delta2 = delta2; + + if (!current_delta || !delta2 || !delta3) + return 1; + + return 0; +} + +/** + * This is the heart of the entropy generation: calculate time deltas and + * use the CPU jitter in the time deltas. The jitter is injected into the + * entropy pool. + * + * WARNING: ensure that ->prev_time is primed before using the output + * of this function! This can be done by calling this function + * and not using its result. + * + * Input: + * @entropy_collector Reference to entropy collector + * + * @return: result of stuck test + */ +static int jent_measure_jitter(struct rand_data *ec) +{ + uint64_t time = 0; + uint64_t current_delta = 0; + + /* Invoke one noise source before time measurement to add variations */ + jent_memaccess(ec, 0); + + /* + * Get time stamp and calculate time delta to previous + * invocation to measure the timing variations + */ + jent_get_nstime(&time); + current_delta = time - ec->prev_time; + ec->prev_time = time; + + /* Now call the next noise sources which also injects the data */ + jent_lfsr_time(ec, current_delta, 0); + + /* Check whether we have a stuck measurement. */ + return jent_stuck(ec, current_delta); +} + +/** + * Generator of one 64 bit random number + * Function fills rand_data->data + * + * Input: + * @ec Reference to entropy collector + */ +static void jent_gen_entropy(struct rand_data *ec) +{ + unsigned int k = 0; + + /* priming of the ->prev_time value */ + jent_measure_jitter(ec); + + while (1) { + /* If a stuck measurement is received, repeat measurement */ + if (jent_measure_jitter(ec)) + continue; + + /* + * We multiply the loop value with ->osr to obtain the + * oversampling rate requested by the caller + */ + if (++k >= (DATA_SIZE_BITS * ec->osr)) + break; + } +} + +/** + * The continuous test required by FIPS 140-2 -- the function automatically + * primes the test if needed. + * + * Return: + * 0 if FIPS test passed + * < 0 if FIPS test failed + */ +static int jent_fips_test(struct rand_data *ec) +{ + if (ec->fips_enabled == -1) + return 0; + + if (ec->fips_enabled == 0) { + if (!jent_fips_enabled()) { + ec->fips_enabled = -1; + return 0; + } else + ec->fips_enabled = 1; + } + + /* prime the FIPS test */ + if (!ec->old_data) { + ec->old_data = ec->data; + jent_gen_entropy(ec); + } + + if (ec->data == ec->old_data) + return -1; + + ec->old_data = ec->data; + + return 0; +} + +/** + * Entry function: Obtain entropy for the caller. + * + * This function invokes the entropy gathering logic as often to generate + * as many bytes as requested by the caller. The entropy gathering logic + * creates 64 bit per invocation. + * + * This function truncates the last 64 bit entropy value output to the exact + * size specified by the caller. + * + * Input: + * @ec Reference to entropy collector + * @data pointer to buffer for storing random data -- buffer must already + * exist + * @len size of the buffer, specifying also the requested number of random + * in bytes + * + * @return number of bytes returned when request is fulfilled or an error + * + * The following error codes can occur: + * -1 entropy_collector is NULL + * -2 FIPS test failed + */ +JENT_PRIVATE_STATIC +ssize_t jent_read_entropy(struct rand_data *ec, char *data, size_t len) +{ + char *p = data; + size_t orig_len = len; + + if (NULL == ec) + return -1; + + while (0 < len) { + size_t tocopy; + + jent_gen_entropy(ec); + if (jent_fips_test(ec)) + return -2; + + if ((DATA_SIZE_BITS / 8) < len) + tocopy = (DATA_SIZE_BITS / 8); + else + tocopy = len; + memcpy(p, &ec->data, tocopy); + + len -= tocopy; + p += tocopy; + } + + /* + * To be on the safe side, we generate one more round of entropy + * which we do not give out to the caller. That round shall ensure + * that in case the calling application crashes, memory dumps, pages + * out, or due to the CPU Jitter RNG lingering in memory for long + * time without being moved and an attacker cracks the application, + * all he reads in the entropy pool is a value that is NEVER EVER + * being used for anything. Thus, he does NOT see the previous value + * that was returned to the caller for cryptographic purposes. + */ + /* + * If we use secured memory, do not use that precaution as the secure + * memory protects the entropy pool. Moreover, note that using this + * call reduces the speed of the RNG by up to half + */ +#ifndef CONFIG_CRYPTO_CPU_JITTERENTROPY_SECURE_MEMORY + jent_gen_entropy(ec); +#endif + return orig_len; +} + +/*************************************************************************** + * Initialization logic + ***************************************************************************/ + +JENT_PRIVATE_STATIC +struct rand_data *jent_entropy_collector_alloc(unsigned int osr, + unsigned int flags) +{ + struct rand_data *entropy_collector; + + entropy_collector = jent_zalloc(sizeof(struct rand_data)); + if (NULL == entropy_collector) + return NULL; + + if (!(flags & JENT_DISABLE_MEMORY_ACCESS)) { + /* Allocate memory for adding variations based on memory + * access + */ + entropy_collector->mem = + (unsigned char *)jent_zalloc(JENT_MEMORY_SIZE); + if (NULL == entropy_collector->mem) { + jent_zfree(entropy_collector, sizeof(struct rand_data)); + return NULL; + } + entropy_collector->memblocksize = JENT_MEMORY_BLOCKSIZE; + entropy_collector->memblocks = JENT_MEMORY_BLOCKS; + entropy_collector->memaccessloops = JENT_MEMORY_ACCESSLOOPS; + } + + /* verify and set the oversampling rate */ + if (0 == osr) + osr = 1; /* minimum sampling rate is 1 */ + entropy_collector->osr = osr; + + entropy_collector->stir = 1; + if (flags & JENT_DISABLE_STIR) + entropy_collector->stir = 0; + if (flags & JENT_DISABLE_UNBIAS) + entropy_collector->disable_unbias = 1; + + /* fill the data pad with non-zero values */ + jent_gen_entropy(entropy_collector); + + return entropy_collector; +} + +JENT_PRIVATE_STATIC +void jent_entropy_collector_free(struct rand_data *entropy_collector) +{ + if (NULL != entropy_collector) { + if (NULL != entropy_collector->mem) { + jent_zfree(entropy_collector->mem, JENT_MEMORY_SIZE); + entropy_collector->mem = NULL; + } + jent_zfree(entropy_collector, sizeof(struct rand_data)); + } +} + +JENT_PRIVATE_STATIC +int jent_entropy_init(void) +{ + int i; + uint64_t delta_sum = 0; + uint64_t old_delta = 0; + int time_backwards = 0; + int count_mod = 0; + int count_stuck = 0; + struct rand_data ec; + + memset(&ec, 0, sizeof(ec)); + + /* We could perform statistical tests here, but the problem is + * that we only have a few loop counts to do testing. These + * loop counts may show some slight skew and we produce + * false positives. + * + * Moreover, only old systems show potentially problematic + * jitter entropy that could potentially be caught here. But + * the RNG is intended for hardware that is available or widely + * used, but not old systems that are long out of favor. Thus, + * no statistical tests. + */ + + /* + * We could add a check for system capabilities such as clock_getres or + * check for CONFIG_X86_TSC, but it does not make much sense as the + * following sanity checks verify that we have a high-resolution + * timer. + */ + /* + * TESTLOOPCOUNT needs some loops to identify edge systems. 100 is + * definitely too little. + */ +#define TESTLOOPCOUNT 300 +#define CLEARCACHE 100 + for (i = 0; (TESTLOOPCOUNT + CLEARCACHE) > i; i++) { + uint64_t time = 0; + uint64_t time2 = 0; + uint64_t delta = 0; + unsigned int lowdelta = 0; + int stuck; + + /* Invoke core entropy collection logic */ + jent_get_nstime(&time); + ec.prev_time = time; + jent_lfsr_time(&ec, time, 0); + jent_get_nstime(&time2); + + /* test whether timer works */ + if (!time || !time2) + return ENOTIME; + delta = time2 - time; + /* + * test whether timer is fine grained enough to provide + * delta even when called shortly after each other -- this + * implies that we also have a high resolution timer + */ + if (!delta) + return ECOARSETIME; + + stuck = jent_stuck(&ec, delta); + + /* + * up to here we did not modify any variable that will be + * evaluated later, but we already performed some work. Thus we + * already have had an impact on the caches, branch prediction, + * etc. with the goal to clear it to get the worst case + * measurements. + */ + if (CLEARCACHE > i) + continue; + + if (stuck) + count_stuck++; + + /* test whether we have an increasing timer */ + if (!(time2 > time)) + time_backwards++; + + /* use 32 bit value to ensure compilation on 32 bit arches */ + lowdelta = time2 - time; + if (!(lowdelta % 100)) + count_mod++; + + /* + * ensure that we have a varying delta timer which is necessary + * for the calculation of entropy -- perform this check + * only after the first loop is executed as we need to prime + * the old_data value + */ + if (delta > old_delta) + delta_sum += (delta - old_delta); + else + delta_sum += (old_delta - delta); + old_delta = delta; + } + + /* + * we allow up to three times the time running backwards. + * CLOCK_REALTIME is affected by adjtime and NTP operations. Thus, + * if such an operation just happens to interfere with our test, it + * should not fail. The value of 3 should cover the NTP case being + * performed during our test run. + */ + if (3 < time_backwards) + return ENOMONOTONIC; + + /* + * Variations of deltas of time must on average be larger + * than 1 to ensure the entropy estimation + * implied with 1 is preserved + */ + if ((delta_sum) <= 1) + return EMINVARVAR; + + /* + * Ensure that we have variations in the time stamp below 10 for at least + * 10% of all checks -- on some platforms, the counter increments in + * multiples of 100, but not always + */ + if ((TESTLOOPCOUNT/10 * 9) < count_mod) + return ECOARSETIME; + + /* + * If we have more than 90% stuck results, then this Jitter RNG is + * likely to not work well. + */ + if (JENT_STUCK_INIT_THRES(TESTLOOPCOUNT) < count_stuck) + return ESTUCK; + + return 0; +} + +/*************************************************************************** + * Statistical test logic not compiled for regular operation + ***************************************************************************/ + +#ifdef CONFIG_CRYPTO_CPU_JITTERENTROPY_STAT +/* + * Statistical test: return the time duration for the folding operation. If min + * is set, perform the given number of LFSR ops. Otherwise, allow the + * loop count shuffling to define the number of LFSR ops. + */ +JENT_PRIVATE_STATIC +uint64_t jent_lfsr_var_stat(struct rand_data *ec, unsigned int min) +{ + uint64_t time = 0; + uint64_t time2 = 0; + + jent_get_nstime(&time); + jent_memaccess(ec, min); + jent_lfsr_time(ec, time, min); + jent_get_nstime(&time2); + return ((time2 - time)); +} +#endif /* CONFIG_CRYPTO_CPU_JITTERENTROPY_STAT */ diff --git a/src/Crypto/jitterentropy.h b/src/Crypto/jitterentropy.h new file mode 100644 index 00000000..b6929933 --- /dev/null +++ b/src/Crypto/jitterentropy.h @@ -0,0 +1,155 @@ +/* + * Non-physical true random number generator based on timing jitter. + * + * Copyright Stephan Mueller <smueller@chronox.de>, 2014 + * + * License + * ======= + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU General Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +/* Adapted for VeraCrypt */ + +#ifndef _JITTERENTROPY_H +#define _JITTERENTROPY_H + +#include "jitterentropy-base-user.h" + +/* The entropy pool */ +struct rand_data +{ + /* all data values that are vital to maintain the security + * of the RNG are marked as SENSITIVE. A user must not + * access that information while the RNG executes its loops to + * calculate the next random value. */ + uint64_t data; /* SENSITIVE Actual random number */ + uint64_t old_data; /* SENSITIVE Previous random number */ + uint64_t prev_time; /* SENSITIVE Previous time stamp */ +#define DATA_SIZE_BITS ((sizeof(uint64_t)) * 8) + uint64_t last_delta; /* SENSITIVE stuck test */ + int64_t last_delta2; /* SENSITIVE stuck test */ + unsigned int osr; /* Oversample rate */ + int fips_enabled; /* FIPS enabled? */ + unsigned int stir:1; /* Post-processing stirring */ + unsigned int disable_unbias:1; /* Deactivate Von-Neuman unbias */ +#define JENT_MEMORY_BLOCKS 64 +#define JENT_MEMORY_BLOCKSIZE 32 +#define JENT_MEMORY_ACCESSLOOPS 128 +#define JENT_MEMORY_SIZE (JENT_MEMORY_BLOCKS*JENT_MEMORY_BLOCKSIZE) + unsigned char *mem; /* Memory access location with size of + * memblocks * memblocksize */ + unsigned int memlocation; /* Pointer to byte in *mem */ + unsigned int memblocks; /* Number of memory blocks in *mem */ + unsigned int memblocksize; /* Size of one memory block in bytes */ + unsigned int memaccessloops; /* Number of memory accesses per random + * bit generation */ +}; + +/* Flags that can be used to initialize the RNG */ +#define JENT_DISABLE_STIR (1<<0) /* Disable stirring the entropy pool */ +#define JENT_DISABLE_UNBIAS (1<<1) /* Disable the Von-Neuman Unbiaser */ +#define JENT_DISABLE_MEMORY_ACCESS (1<<2) /* Disable memory access for more + entropy, saves MEMORY_SIZE RAM for + entropy collector */ + +/* -- BEGIN Main interface functions -- */ + +#ifndef JENT_STUCK_INIT_THRES +/* + * Per default, not more than 90% of all measurements during initialization + * are allowed to be stuck. + * + * It is allowed to change this value as required for the intended environment. + */ +#define JENT_STUCK_INIT_THRES(x) (x/10 * 9) +#endif + +#ifdef JENT_PRIVATE_COMPILE +# define JENT_PRIVATE_STATIC static +#else /* JENT_PRIVATE_COMPILE */ +# define JENT_PRIVATE_STATIC +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Number of low bits of the time value that we want to consider */ +/* get raw entropy */ +JENT_PRIVATE_STATIC +ssize_t jent_read_entropy(struct rand_data *ec, char *data, size_t len); +/* initialize an instance of the entropy collector */ +JENT_PRIVATE_STATIC +struct rand_data *jent_entropy_collector_alloc(unsigned int osr, + unsigned int flags); +/* clearing of entropy collector */ +JENT_PRIVATE_STATIC +void jent_entropy_collector_free(struct rand_data *entropy_collector); + +/* initialization of entropy collector */ +JENT_PRIVATE_STATIC +int jent_entropy_init(void); + +/* return version number of core library */ +JENT_PRIVATE_STATIC +unsigned int jent_version(void); + +/* -- END of Main interface functions -- */ + +/* -- BEGIN error codes for init function -- */ +#define ENOTIME 1 /* Timer service not available */ +#define ECOARSETIME 2 /* Timer too coarse for RNG */ +#define ENOMONOTONIC 3 /* Timer is not monotonic increasing */ +#define EMINVARIATION 4 /* Timer variations too small for RNG */ +#define EVARVAR 5 /* Timer does not produce variations of variations + (2nd derivation of time is zero) */ +#define EMINVARVAR 6 /* Timer variations of variations is too small */ +#define EPROGERR 7 /* Programming error */ +#define ESTUCK 8 /* Too many stuck results during init. */ + +/* -- BEGIN statistical test functions only complied with CONFIG_CRYPTO_CPU_JITTERENTROPY_STAT -- */ + +#ifdef CONFIG_CRYPTO_CPU_JITTERENTROPY_STAT +JENT_PRIVATE_STATIC +uint64_t jent_lfsr_var_stat(struct rand_data *ec, unsigned int min); +#endif /* CONFIG_CRYPTO_CPU_JITTERENTROPY_STAT */ + +/* -- END of statistical test function -- */ + +#ifdef __cplusplus +} +#endif + + +#endif /* _JITTERENTROPY_H */ diff --git a/src/Driver/Driver.vcxproj b/src/Driver/Driver.vcxproj index 7104be84..5744934b 100644 --- a/src/Driver/Driver.vcxproj +++ b/src/Driver/Driver.vcxproj @@ -196,6 +196,7 @@ BuildDriver.cmd -rebuild -debug -x64 "$(SolutionDir)\Common" "$(SolutionDir)\Cry <ClCompile Include="..\Crypto\chacha-xmm.c" /> <ClCompile Include="..\Crypto\chacha256.c" /> <ClCompile Include="..\Crypto\chachaRng.c" /> + <ClCompile Include="..\Crypto\jitterentropy-base.c" /> <ClCompile Include="..\Crypto\rdrand.c" /> <ClCompile Include="..\Crypto\SerpentFast.c" /> <ClCompile Include="..\Crypto\SerpentFast_simd.cpp" /> diff --git a/src/Driver/Driver.vcxproj.filters b/src/Driver/Driver.vcxproj.filters index 20227b48..a6f5da3c 100644 --- a/src/Driver/Driver.vcxproj.filters +++ b/src/Driver/Driver.vcxproj.filters @@ -123,6 +123,9 @@ <ClCompile Include="..\Crypto\Streebog.c"> <Filter>Source Files\Crypto</Filter> </ClCompile> + <ClCompile Include="..\Crypto\jitterentropy-base.c"> + <Filter>Source Files\Crypto</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="..\Crypto\Aes_hw_cpu.asm"> diff --git a/src/Driver/Ntdriver.c b/src/Driver/Ntdriver.c index 9719c91b..ba2de477 100644 --- a/src/Driver/Ntdriver.c +++ b/src/Driver/Ntdriver.c @@ -32,6 +32,7 @@ #include "VolumeFilter.h" #include "cpu.h" #include "rdrand.h" +#include "jitterentropy.h" #include <tchar.h> #include <initguid.h> @@ -162,7 +163,7 @@ void GetDriverRandomSeed (unsigned char* pbRandSeed, size_t cbRandSeed) while (cbRandSeed) { WHIRLPOOL_init (&tctx); - // we hash current content of digest buffer which is initialized the first time + // we hash current content of digest buffer which is uninitialized the first time WHIRLPOOL_add (digest, WHIRLPOOL_DIGESTSIZE, &tctx); // we use various time information as source of entropy @@ -174,6 +175,19 @@ void GetDriverRandomSeed (unsigned char* pbRandSeed, size_t cbRandSeed) iSeed.QuadPart = KeQueryInterruptTime (); WHIRLPOOL_add ((unsigned char *) &(iSeed.QuadPart), sizeof(iSeed.QuadPart), &tctx); + /* use JitterEntropy library to get good quality random bytes based on CPU timing jitter */ + if (0 == jent_entropy_init ()) + { + struct rand_data *ec = jent_entropy_collector_alloc (1, 0); + if (ec) + { + ssize_t rndLen = jent_read_entropy (ec, (char*) digest, sizeof (digest)); + if (rndLen > 0) + WHIRLPOOL_add (digest, (unsigned int) rndLen, &tctx); + jent_entropy_collector_free (ec); + } + } + // use RDSEED or RDRAND from CPU as source of entropy if enabled if ( IsCpuRngEnabled() && ( (HasRDSEED() && RDSEED_getBytes (digest, sizeof (digest))) |