From 61c1baa4bf5a97675187a37cf203e1937a060daa Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Thu, 31 Jan 2019 01:05:19 +0100 Subject: Windows: use CPU RDRAND or RDSEED as an additional entropy source for our random generator when available --- src/Common/Random.c | 30 ++- src/Crypto/Crypto.vcxproj | 19 ++ src/Crypto/Crypto.vcxproj.filters | 9 + src/Crypto/Makefile.inc | 9 + src/Crypto/Sources | 4 + src/Crypto/rdrand.c | 32 +++ src/Crypto/rdrand.h | 26 +++ src/Crypto/rdrand_ml.asm | 420 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 544 insertions(+), 5 deletions(-) create mode 100644 src/Crypto/rdrand.c create mode 100644 src/Crypto/rdrand.h create mode 100644 src/Crypto/rdrand_ml.asm (limited to 'src') diff --git a/src/Common/Random.c b/src/Common/Random.c index 6c95cf6a..12e9d9af 100644 --- a/src/Common/Random.c +++ b/src/Common/Random.c @@ -14,6 +14,8 @@ #include "Tcdefs.h" #include "Crc.h" #include "Random.h" +#include "Crypto\cpu.h" +#include "Crypto\rdrand.h" #include static unsigned __int8 buffer[RNG_POOL_SIZE]; @@ -766,10 +768,6 @@ BOOL SlowPoll (void) if (CryptGenRandom (hCryptProv, sizeof (buffer), buffer)) { RandaddBuf (buffer, sizeof (buffer)); - - burn(buffer, sizeof (buffer)); - Randmix(); - return TRUE; } else { @@ -777,6 +775,19 @@ BOOL SlowPoll (void) CryptoAPILastError = GetLastError (); return FALSE; } + + // use RDSEED or RDRAND from CPU as source of entropy if present + if ( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer))) + || (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer))) + ) + { + RandaddBuf (buffer, sizeof (buffer)); + } + + burn(buffer, sizeof (buffer)); + Randmix(); + + return TRUE; } @@ -888,7 +899,6 @@ BOOL FastPoll (void) if (CryptGenRandom (hCryptProv, sizeof (buffer), buffer)) { RandaddBuf (buffer, sizeof (buffer)); - burn (buffer, sizeof(buffer)); } else { @@ -897,6 +907,16 @@ BOOL FastPoll (void) return FALSE; } + // use RDSEED or RDRAND from CPU as source of entropy if present + if ( (HasRDSEED() && RDSEED_getBytes (buffer, sizeof (buffer))) + || (HasRDRAND() && RDRAND_getBytes (buffer, sizeof (buffer))) + ) + { + RandaddBuf (buffer, sizeof (buffer)); + } + + burn (buffer, sizeof(buffer)); + /* Apply the pool mixing function */ Randmix(); diff --git a/src/Crypto/Crypto.vcxproj b/src/Crypto/Crypto.vcxproj index cd087bea..43ac766f 100644 --- a/src/Crypto/Crypto.vcxproj +++ b/src/Crypto/Crypto.vcxproj @@ -219,6 +219,7 @@ + @@ -238,6 +239,7 @@ + @@ -366,6 +368,23 @@ $(TargetDir)\%(Filename).obj;%(Outputs) + + + Document + echo %(Filename)%(Extension) & ml64.exe /nologo /D_M_X64 /W3 /Cx /Zi /Fo "$(TargetDir)\%(Filename).obj" /c "%(FullPath)" + + echo %(Filename)%(Extension) & ml64.exe /nologo /D_M_X64 /W3 /Cx /Zi /Fo "$(TargetDir)\%(Filename).obj" /c "%(FullPath)" + + echo %(Filename)%(Extension) & ml.exe /nologo /D_M_X86 /W3 /Cx /Zi /safeseh /Fo "$(TargetDir)\%(Filename).obj" /c "%(FullPath)" + + echo %(Filename)%(Extension) & ml.exe /nologo /D_M_X86 /W3 /Cx /Zi /safeseh /Fo "$(TargetDir)\%(Filename).obj" /c "%(FullPath)" + + $(TargetDir)\%(Filename).obj;%(Outputs) + $(TargetDir)\%(Filename).obj;%(Outputs) + $(TargetDir)\%(Filename).obj;%(Outputs) + $(TargetDir)\%(Filename).obj;%(Outputs) + + diff --git a/src/Crypto/Crypto.vcxproj.filters b/src/Crypto/Crypto.vcxproj.filters index f107088e..c8d91b65 100644 --- a/src/Crypto/Crypto.vcxproj.filters +++ b/src/Crypto/Crypto.vcxproj.filters @@ -57,6 +57,9 @@ Source Files + + Source Files + @@ -110,6 +113,9 @@ Header Files + + Header Files + @@ -160,5 +166,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/src/Crypto/Makefile.inc b/src/Crypto/Makefile.inc index 016451ad..86b7a6fa 100644 --- a/src/Crypto/Makefile.inc +++ b/src/Crypto/Makefile.inc @@ -1,12 +1,17 @@ TC_ASFLAGS = -Xvc -Ox VC_YASMFLAGS = -Xvc -D WINABI -D __YASM__ +VC_MLFLAGS = /nologo /W3 /Cx /Zi +VC_MLEXE = ml.exe !if "$(TC_ARCH)" == "x86" TC_ASFLAGS = $(TC_ASFLAGS) -f win32 --prefix _ -D MS_STDCALL -D DLL_EXPORT VC_YASMFLAGS = $(VC_YASMFLAGS) -f win32 -D MS_STDCALL +VC_MLFLAGS = $(VC_MLFLAGS) /D_M_X86 /safeseh !else TC_ASFLAGS = $(TC_ASFLAGS) -f win64 VC_YASMFLAGS = $(VC_YASMFLAGS) -f win64 +VC_MLFLAGS = $(VC_MLFLAGS) /D_M_X64 +VC_MLEXE = ml64.exe !endif TC_ASM_ERR_LOG = ..\Driver\build_errors_asm.log @@ -52,3 +57,7 @@ TC_ASM_ERR_LOG = ..\Driver\build_errors_asm.log "$(OBJ_PATH)\$(O)\sha256_sse4_$(TC_ARCH).obj": sha256_sse4_$(TC_ARCH).asm yasm.exe $(VC_YASMFLAGS) -o "$@" -l "$(OBJ_PATH)\$(O)\sha256_sse4_$(TC_ARCH).lst" sha256_sse4_$(TC_ARCH).asm 2>$(TC_ASM_ERR_LOG) + +"$(OBJ_PATH)\$(O)\rdrand_ml.obj": rdrand_ml.asm + $(VC_MLEXE) $(VC_MLFLAGS) /Fo "$@" /c rdrand_ml.asm 2>$(TC_ASM_ERR_LOG) + diff --git a/src/Crypto/Sources b/src/Crypto/Sources index 054bf023..271edca6 100644 --- a/src/Crypto/Sources +++ b/src/Crypto/Sources @@ -6,6 +6,8 @@ INCLUDES = .. NTTARGETFILES = \ "$(OBJ_PATH)\$(O)\Aes_$(TC_ARCH).obj" \ "$(OBJ_PATH)\$(O)\Aes_hw_cpu.obj" \ + "$(OBJ_PATH)\$(O)\rdrand.obj" \ + "$(OBJ_PATH)\$(O)\rdrand_ml.obj" \ "$(OBJ_PATH)\$(O)\gost89_$(TC_ARCH).obj" \ "$(OBJ_PATH)\$(O)\Twofish_$(TC_ARCH).obj" \ "$(OBJ_PATH)\$(O)\Camellia_$(TC_ARCH).obj" \ @@ -23,9 +25,11 @@ SOURCES = \ Aes_$(TC_ARCH).asm \ gost89_$(TC_ARCH).asm \ Aes_hw_cpu.asm \ + rdrand_ml.asm \ Aeskey.c \ Aestab.c \ cpu.c \ + rdrand.c \ Rmd160.c \ SerpentFast.c \ SerpentFast_simd.cpp \ diff --git a/src/Crypto/rdrand.c b/src/Crypto/rdrand.c new file mode 100644 index 00000000..afed7cd1 --- /dev/null +++ b/src/Crypto/rdrand.c @@ -0,0 +1,32 @@ +// rdrand.cpp - written and placed in public domain by Jeffrey Walton and Uri Blumenthal. + +/* modified for VeraCrypt */ + +#include "chacha256.h" +#include "cpu.h" +#include "misc.h" + +void CRYPTOPP_FASTCALL MASM_RDRAND_GenerateBlock(byte*, size_t); +void CRYPTOPP_FASTCALL MASM_RDSEED_GenerateBlock(byte*, size_t); + +int RDRAND_getBytes(unsigned char* buf, size_t bufLen) +{ + if (!buf || !HasRDRAND()) + return 0; + + if (bufLen) + MASM_RDRAND_GenerateBlock(buf, bufLen); + + return 1; +} + +int RDSEED_getBytes(unsigned char* buf, size_t bufLen) +{ + if (!buf || !HasRDSEED()) + return 0; + + if (bufLen) + MASM_RDSEED_GenerateBlock(buf, bufLen); + + return 1; +} diff --git a/src/Crypto/rdrand.h b/src/Crypto/rdrand.h new file mode 100644 index 00000000..ff8cfd29 --- /dev/null +++ b/src/Crypto/rdrand.h @@ -0,0 +1,26 @@ +#ifndef HEADER_Crypto_RDRAND +#define HEADER_Crypto_RDRAND + +#include "Common/Tcdefs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * generate bufLen random bytes using CPU RDRAND instruction + * return 1 in case of success and 0 in case of failure + */ +int RDRAND_getBytes(unsigned char* buf, size_t bufLen); + +/* + * generate bufLen random bytes using CPU RDSEED instruction + * return 1 in case of success and 0 in case of failure + */ +int RDSEED_getBytes(unsigned char* buf, size_t bufLen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Crypto/rdrand_ml.asm b/src/Crypto/rdrand_ml.asm new file mode 100644 index 00000000..1579a155 --- /dev/null +++ b/src/Crypto/rdrand_ml.asm @@ -0,0 +1,420 @@ +;; rdrand.asm - written and placed in public domain by Jeffrey Walton and Uri Blumenthal. +;; Copyright assigned to the Crypto++ project. + +;; This ASM file provides RDRAND and RDSEED to downlevel Microsoft tool chains. +;; Everything "just works" under Visual Studio. Other platforms will have to +;; run MASM/MASM-64 and then link to the object files. + +;; set ASFLAGS=/nologo /D_M_X86 /W3 /Cx /Zi /safeseh +;; set ASFLAGS64=/nologo /D_M_X64 /W3 /Cx /Zi +;; "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\ml.exe" %ASFLAGS% /Fo rdrand-x86.obj /c rdrand.asm +;; "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\amd64\ml64.exe" %ASFLAGS64% /Fo rdrand-x64.obj /c rdrand.asm + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +TITLE MASM_RDRAND_GenerateBlock and MASM_RDSEED_GenerateBlock +SUBTITLE Microsoft specific ASM code to utilize RDRAND and RDSEED for down level Microsoft toolchains + +PUBLIC MASM_RDRAND_GenerateBlock +PUBLIC MASM_RDSEED_GenerateBlock + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; C/C++ Function prototypes (both are fastcall) +;; X86: +;; extern "C" void __fastcall MASM_RDRAND_GenerateBlock(byte* ptr, size_t size); +;; X64: +;; extern "C" void __fastcall MASM_RDRAND_GenerateBlock(byte* ptr, size_t size); + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IFDEF _M_X86 ;; Set via the command line + +.486 +.MODEL FLAT + +;; Fastcall calling conventions exports +ALIAS <@MASM_RDRAND_GenerateBlock@8> = +ALIAS <@MASM_RDSEED_GenerateBlock@8> = + +ENDIF + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IFDEF _M_X86 ;; Set via the command line + +.CODE +ALIGN 8 +OPTION PROLOGUE:NONE +OPTION EPILOGUE:NONE + +;; No need for Load_Arguments due to fastcall +;; ECX (in): arg1, byte* buffer +;; EDX (in): arg2, size_t bsize + +MASM_RDRAND_GenerateBlock PROC ;; arg1:DWORD, arg2:DWORD + + MWSIZE EQU 04h ;; machine word size + buffer EQU ecx + bsize EQU edx + + ;; Top of While loop +GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je GenerateBlock_Return + +Call_RDRAND_EAX: + ;; RDRAND is not available prior to VS2012. Just emit + ;; the byte codes using DB. This is `rdrand eax`. + DB 0Fh, 0C7h, 0F0h + + ;; If CF=1, the number returned by RDRAND is valid. + ;; If CF=0, a random number was not available. + + ;; Retry immediately + jnc Call_RDRAND_EAX + +RDRAND_succeeded: + + cmp bsize, MWSIZE + jb Partial_Machine_Word + +Full_Machine_Word: + + mov DWORD PTR [buffer], eax + add buffer, MWSIZE ;; No need for Intel Core 2 slow workarounds, like + sub bsize, MWSIZE ;; `lea buffer,[buffer+MWSIZE]` for faster adds + + ;; Continue + jmp GenerateBlock_Top + + ;; 1,2,3 bytes remain +Partial_Machine_Word: + + ;; Test bit 1 to see if size is at least 2 + test bsize, 2 + jz Bit_1_Not_Set + + mov WORD PTR [buffer], ax + shr eax, 16 + add buffer, 2 + +Bit_1_Not_Set: + + ;; Test bit 0 to see if size is at least 1 + test bsize, 1 + jz Bit_0_Not_Set + + mov BYTE PTR [buffer], al + +Bit_0_Not_Set: + + ;; We've hit all the bits + +GenerateBlock_Return: + + ;; Clear artifacts + xor eax, eax + ret + +MASM_RDRAND_GenerateBlock ENDP + +ENDIF ;; _M_X86 + +OPTION PROLOGUE:PrologueDef +OPTION EPILOGUE:EpilogueDef + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IFDEF _M_X64 ;; Set via the command line + +.CODE +ALIGN 16 +OPTION PROLOGUE:NONE +OPTION EPILOGUE:NONE + +;; No need for Load_Arguments due to fastcall +;; RCX (in): arg1, byte* buffer +;; RDX (in): arg2, size_t bsize + +MASM_RDRAND_GenerateBlock PROC ;; arg1:QWORD, arg2:QWORD + + MWSIZE EQU 08h ;; machine word size + buffer EQU rcx + bsize EQU rdx + + ;; Top of While loop +GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je GenerateBlock_Return + +Call_RDRAND_RAX: + ;; RDRAND is not available prior to VS2012. Just emit + ;; the byte codes using DB. This is `rdrand rax`. + DB 048h, 0Fh, 0C7h, 0F0h + + ;; If CF=1, the number returned by RDRAND is valid. + ;; If CF=0, a random number was not available. + + ;; Retry immediately + jnc Call_RDRAND_RAX + +RDRAND_succeeded: + + cmp bsize, MWSIZE + jb Partial_Machine_Word + +Full_Machine_Word: + + mov QWORD PTR [buffer], rax + add buffer, MWSIZE + sub bsize, MWSIZE + + ;; Continue + jmp GenerateBlock_Top + + ;; 1,2,3,4,5,6,7 bytes remain +Partial_Machine_Word: + + ;; Test bit 2 to see if size is at least 4 + test bsize, 4 + jz Bit_2_Not_Set + + mov DWORD PTR [buffer], eax + shr rax, 32 + add buffer, 4 + +Bit_2_Not_Set: + + ;; Test bit 1 to see if size is at least 2 + test bsize, 2 + jz Bit_1_Not_Set + + mov WORD PTR [buffer], ax + shr eax, 16 + add buffer, 2 + +Bit_1_Not_Set: + + ;; Test bit 0 to see if size is at least 1 + test bsize, 1 + jz Bit_0_Not_Set + + mov BYTE PTR [buffer], al + +Bit_0_Not_Set: + + ;; We've hit all the bits + +GenerateBlock_Return: + + ;; Clear artifacts + xor rax, rax + ret + +MASM_RDRAND_GenerateBlock ENDP + +ENDIF ;; _M_X64 + +OPTION PROLOGUE:PrologueDef +OPTION EPILOGUE:EpilogueDef + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IFDEF _M_X86 ;; Set via the command line + +.CODE +ALIGN 8 +OPTION PROLOGUE:NONE +OPTION EPILOGUE:NONE + +;; No need for Load_Arguments due to fastcall +;; ECX (in): arg1, byte* buffer +;; EDX (in): arg2, size_t bsize + +MASM_RDSEED_GenerateBlock PROC ;; arg1:DWORD, arg2:DWORD + + MWSIZE EQU 04h ;; machine word size + buffer EQU ecx + bsize EQU edx + + ;; Top of While loop +GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je GenerateBlock_Return + +Call_RDSEED_EAX: + ;; RDSEED is not available prior to VS2012. Just emit + ;; the byte codes using DB. This is `rdseed eax`. + DB 0Fh, 0C7h, 0F8h + + ;; If CF=1, the number returned by RDSEED is valid. + ;; If CF=0, a random number was not available. + + ;; Retry immediately + jnc Call_RDSEED_EAX + +RDSEED_succeeded: + + cmp bsize, MWSIZE + jb Partial_Machine_Word + +Full_Machine_Word: + + mov DWORD PTR [buffer], eax + add buffer, MWSIZE ;; No need for Intel Core 2 slow workarounds, like + sub bsize, MWSIZE ;; `lea buffer,[buffer+MWSIZE]` for faster adds + + ;; Continue + jmp GenerateBlock_Top + + ;; 1,2,3 bytes remain +Partial_Machine_Word: + + ;; Test bit 1 to see if size is at least 2 + test bsize, 2 + jz Bit_1_Not_Set + + mov WORD PTR [buffer], ax + shr eax, 16 + add buffer, 2 + +Bit_1_Not_Set: + + ;; Test bit 0 to see if size is at least 1 + test bsize, 1 + jz Bit_0_Not_Set + + mov BYTE PTR [buffer], al + +Bit_0_Not_Set: + + ;; We've hit all the bits + +GenerateBlock_Return: + + ;; Clear artifacts + xor eax, eax + ret + +MASM_RDSEED_GenerateBlock ENDP + +ENDIF ;; _M_X86 + +OPTION PROLOGUE:PrologueDef +OPTION EPILOGUE:EpilogueDef + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +IFDEF _M_X64 ;; Set via the command line + +.CODE +ALIGN 16 +OPTION PROLOGUE:NONE +OPTION EPILOGUE:NONE + +;; No need for Load_Arguments due to fastcall +;; RCX (in): arg1, byte* buffer +;; RDX (in): arg2, size_t bsize + +MASM_RDSEED_GenerateBlock PROC ;; arg1:QWORD, arg2:QWORD + + MWSIZE EQU 08h ;; machine word size + buffer EQU rcx + bsize EQU rdx + + ;; Top of While loop +GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je GenerateBlock_Return + +Call_RDSEED_RAX: + ;; RDSEED is not available prior to VS2012. Just emit + ;; the byte codes using DB. This is `rdseed rax`. + DB 048h, 0Fh, 0C7h, 0F8h + + ;; If CF=1, the number returned by RDSEED is valid. + ;; If CF=0, a random number was not available. + + ;; Retry immediately + jnc Call_RDSEED_RAX + +RDSEED_succeeded: + + cmp bsize, MWSIZE + jb Partial_Machine_Word + +Full_Machine_Word: + + mov QWORD PTR [buffer], rax + add buffer, MWSIZE + sub bsize, MWSIZE + + ;; Continue + jmp GenerateBlock_Top + + ;; 1,2,3,4,5,6,7 bytes remain +Partial_Machine_Word: + + ;; Test bit 2 to see if size is at least 4 + test bsize, 4 + jz Bit_2_Not_Set + + mov DWORD PTR [buffer], eax + shr rax, 32 + add buffer, 4 + +Bit_2_Not_Set: + + ;; Test bit 1 to see if size is at least 2 + test bsize, 2 + jz Bit_1_Not_Set + + mov WORD PTR [buffer], ax + shr eax, 16 + add buffer, 2 + +Bit_1_Not_Set: + + ;; Test bit 0 to see if size is at least 1 + test bsize, 1 + jz Bit_0_Not_Set + + mov BYTE PTR [buffer], al + +Bit_0_Not_Set: + + ;; We've hit all the bits + +GenerateBlock_Return: + + ;; Clear artifacts + xor rax, rax + ret + +MASM_RDSEED_GenerateBlock ENDP + +ENDIF ;; _M_X64 + +OPTION PROLOGUE:PrologueDef +OPTION EPILOGUE:EpilogueDef + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +END -- cgit v1.2.3