diff --git a/Filelist.txt b/Filelist.txt new file mode 100644 index 00000000..fd503de5 --- /dev/null +++ b/Filelist.txt @@ -0,0 +1,393 @@ +3way.cpp +3way.h +adhoc.cpp.proto +adler32.cpp +adler32.h +aes.h +algebra.cpp +algebra.h +algparam.cpp +algparam.h +arc4.cpp +arc4.h +argnames.h +asn.cpp +asn.h +authenc.cpp +authenc.h +base32.cpp +base32.h +base64.cpp +base64.h +basecode.cpp +basecode.h +bench.cpp +bench.h +bench2.cpp +bfinit.cpp +blowfish.cpp +blowfish.h +blumshub.cpp +blumshub.h +camellia.cpp +camellia.h +cast.cpp +cast.h +casts.cpp +cbcmac.cpp +cbcmac.h +ccm.cpp +ccm.h +channels.cpp +channels.h +cmac.cpp +cmac.h +config.h +config.recommend +cpu.cpp +cpu.h +crc.cpp +crc.h +cryptdll.dsp +cryptdll.vcproj +cryptest.dsp +cryptest.dsw +cryptest.sh +cryptest.sln +cryptest.vcproj +cryptest_bds.bdsgroup +cryptest_bds.bdsproj +cryptest_bds.bpf +cryptlib.cpp +cryptlib.dsp +cryptlib.h +cryptlib.vcproj +cryptlib_bds.bdsproj +cryptlib_bds.cpp +cryptopp.rc +cryptopp563.diff +datatest.cpp +default.cpp +default.h +des.cpp +des.h +dessp.cpp +dh.cpp +dh.h +dh2.cpp +dh2.h +dll.cpp +dll.h +dlltest.cpp +dlltest.dsp +dlltest.vcproj +dmac.h +dsa.cpp +dsa.h +eax.cpp +eax.h +ec2n.cpp +ec2n.h +eccrypto.cpp +eccrypto.h +ecp.cpp +ecp.h +elgamal.cpp +elgamal.h +emsa2.cpp +emsa2.h +eprecomp.cpp +eprecomp.h +esign.cpp +esign.h +factory.h +files.cpp +files.h +filters.cpp +filters.h +fips140.cpp +fips140.h +fipsalgt.cpp +fipstest.cpp +fltrimpl.h +gcm.cpp +gcm.h +gf256.cpp +gf256.h +gf2_32.cpp +gf2_32.h +gf2n.cpp +gf2n.h +gfpcrypt.cpp +gfpcrypt.h +gost.cpp +gost.h +gzip.cpp +gzip.h +hex.cpp +hex.h +hkdf.h +hmac.cpp +hmac.h +hrtimer.cpp +hrtimer.h +ida.cpp +ida.h +idea.cpp +idea.h +integer.cpp +integer.h +iterhash.cpp +iterhash.h +lubyrack.h +luc.cpp +luc.h +mars.cpp +mars.h +marss.cpp +md2.cpp +md2.h +md4.cpp +md4.h +md5.cpp +md5.h +mdc.h +mersenne.h +misc.cpp +misc.h +modarith.h +modes.cpp +modes.h +modexppc.h +mqueue.cpp +mqueue.h +mqv.cpp +mqv.h +nbtheory.cpp +nbtheory.h +network.cpp +network.h +nr.h +oaep.cpp +oaep.h +oids.h +osrng.cpp +osrng.h +panama.cpp +panama.h +pch.cpp +pch.h +pkcspad.cpp +pkcspad.h +polynomi.cpp +polynomi.h +pssr.cpp +pssr.h +pubkey.cpp +pubkey.h +pwdbased.h +queue.cpp +queue.h +rabin.cpp +rabin.h +randpool.cpp +randpool.h +rc2.cpp +rc2.h +rc5.cpp +rc5.h +rc6.cpp +rc6.h +rdrand-masm.cmd +rdrand-nasm.sh +rdrand.S +rdrand.asm +rdrand.cpp +rdrand.h +rdtables.cpp +regtest.cpp +resource.h +rijndael.cpp +rijndael.h +ripemd.cpp +ripemd.h +rng.cpp +rng.h +rsa.cpp +rsa.h +rw.cpp +rw.h +safer.cpp +safer.h +salsa.cpp +salsa.h +seal.cpp +seal.h +secblock.h +seckey.h +seed.cpp +seed.h +serpent.cpp +serpent.h +serpentp.h +sha.cpp +sha.h +sha3.cpp +sha3.h +shacal2.cpp +shacal2.h +shark.cpp +shark.h +sharkbox.cpp +simple.cpp +simple.h +skipjack.cpp +skipjack.h +smartptr.h +socketft.cpp +socketft.h +sosemanuk.cpp +sosemanuk.h +square.cpp +square.h +squaretb.cpp +stdcpp.h +strciphr.cpp +strciphr.h +tea.cpp +tea.h +test.cpp +tftables.cpp +tiger.cpp +tiger.h +tigertab.cpp +trdlocal.cpp +trdlocal.h +trunhash.h +ttmac.cpp +ttmac.h +twofish.cpp +twofish.h +validat1.cpp +validat2.cpp +validat3.cpp +validate.h +vmac.cpp +vmac.h +vs2010.zip +wait.cpp +wait.h +wake.cpp +wake.h +whrlpool.cpp +whrlpool.h +winpipes.cpp +winpipes.h +words.h +x64dll.asm +x64masm.asm +xtr.cpp +xtr.h +xtrcrypt.cpp +xtrcrypt.h +zdeflate.cpp +zdeflate.h +zinflate.cpp +zinflate.h +zlib.cpp +zlib.h +Doxyfile +GNUmakefile +GNUmakefile-cross +License.txt +Readme.txt +Install.txt +Filelist.txt +TestData/3desval.dat +TestData/3wayval.dat +TestData/camellia.dat +TestData/cast128v.dat +TestData/cast256v.dat +TestData/descert.dat +TestData/dh1024.dat +TestData/dh2048.dat +TestData/dlie1024.dat +TestData/dlie2048.dat +TestData/dsa1024.dat +TestData/dsa1024b.dat +TestData/dsa512.dat +TestData/elgc1024.dat +TestData/esig1023.dat +TestData/esig1536.dat +TestData/esig2046.dat +TestData/gostval.dat +TestData/ideaval.dat +TestData/luc1024.dat +TestData/luc2048.dat +TestData/lucc1024.dat +TestData/lucc512.dat +TestData/lucd1024.dat +TestData/lucd512.dat +TestData/lucs1024.dat +TestData/lucs512.dat +TestData/marsval.dat +TestData/mqv1024.dat +TestData/mqv2048.dat +TestData/nr1024.dat +TestData/nr2048.dat +TestData/rabi1024.dat +TestData/rabi2048.dat +TestData/rc2val.dat +TestData/rc5val.dat +TestData/rc6val.dat +TestData/rijndael.dat +TestData/rsa1024.dat +TestData/rsa2048.dat +TestData/rsa400pb.dat +TestData/rsa400pv.dat +TestData/rsa512a.dat +TestData/rw1024.dat +TestData/rw2048.dat +TestData/saferval.dat +TestData/serpentv.dat +TestData/shacal2v.dat +TestData/sharkval.dat +TestData/skipjack.dat +TestData/squareva.dat +TestData/twofishv.dat +TestData/usage.dat +TestData/xtrdh171.dat +TestData/xtrdh342.dat +TestVectors/Readme.txt +TestVectors/aes.txt +TestVectors/all.txt +TestVectors/camellia.txt +TestVectors/ccm.txt +TestVectors/cmac.txt +TestVectors/dlies.txt +TestVectors/dsa.txt +TestVectors/dsa_1363.txt +TestVectors/eax.txt +TestVectors/esign.txt +TestVectors/gcm.txt +TestVectors/hkdf.txt +TestVectors/hmac.txt +TestVectors/mars.txt +TestVectors/nr.txt +TestVectors/panama.txt +TestVectors/rsa_oaep.txt +TestVectors/rsa_pkcs1_1_5.txt +TestVectors/rsa_pss.txt +TestVectors/rw.txt +TestVectors/salsa.txt +TestVectors/seal.txt +TestVectors/seed.txt +TestVectors/sha.txt +TestVectors/sha3.txt +TestVectors/shacal2.txt +TestVectors/sosemanuk.txt +TestVectors/tea.txt +TestVectors/ttmac.txt +TestVectors/vmac.txt +TestVectors/wake.txt +TestVectors/whrlpool.txt diff --git a/rdrand-masm.cmd b/rdrand-masm.cmd new file mode 100755 index 00000000..9c915e01 --- /dev/null +++ b/rdrand-masm.cmd @@ -0,0 +1,117 @@ +REM make-rdrand +@echo OFF + +@cls + +@del rdrand.obj rdrand-x86.obj rdrand-x64.obj rdrand-x86.lib rdrand-x64.lib /Q > nul + +REM Visual Studio 2005 +REM @set TOOLS32=C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin +REM @set TOOLS64=C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin\amd64 + +REM Visual Studio 2010 +REM @set TOOLS32=C:\Program Files (x86)\Microsoft Visual Studio 10\VC\bin +REM @set TOOLS64=C:\Program Files (x86)\Microsoft Visual Studio 10\VC\bin\amd64 + +REM Visual Studio 2012 +REM @set TOOLS32=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin +REM @set TOOLS64=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\amd64 + +REM Visual Studio 2013 +@set TOOLS32=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin +@set TOOLS64=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64 + +@set MASM="%TOOLS32%\ml.exe" +@set MASM64="%TOOLS64%\ml64.exe" +@set DUMPBIN="%TOOLS32%\dumpbin.exe" +@set LIBTOOL="%TOOLS32%\lib.exe" + +REM /W3 - Warning level +REM /Cx - Preserve case in external symbols +REM /Zi - Porgram Database information +@set ASFLAGS=/nologo /D_M_X86 /W3 /Cx /Zi /safeseh +@set ASFLAGS64=/nologo /D_M_X64 /W3 /Cx /Zi +@set LIBFLAGS=/nologo /SUBSYSTEM:CONSOLE + +REM Use _M_X86 and _M_X64 becuase cl.exe uses them. It keeps preprocessor defines consistent. +echo **************************************** +echo Assembling rdrand.asm into rdrand-x86.obj +call %MASM% %ASFLAGS% /Fo rdrand-x86.obj /c rdrand.asm > nul +@IF NOT %ERRORLEVEL% EQU 0 (echo Failed to assemble rdrand.asm with X86 && goto SCRIPT_FAILED) +echo Done... + +echo **************************************** +echo Assembling rdrand.asm into rdrand-x64.obj +call %MASM64% %ASFLAGS64% /Fo rdrand-x64.obj /c rdrand.asm > nul +@IF NOT %ERRORLEVEL% EQU 0 (echo Failed to assemble rdrand.asm with X64 && goto SCRIPT_FAILED) +echo Done... + +echo **************************************** +echo Creating static library rdrand-x86.lib +call %LIBTOOL% %LIBFLAGS% /MACHINE:X86 /OUT:rdrand-x86.lib rdrand-x86.obj > nul +@IF NOT %ERRORLEVEL% EQU 0 (echo Failed to create rdrand-x86.lib && goto SCRIPT_FAILED) +echo Done... + +echo **************************************** +echo Creating static library rdrand-x64.lib +call %LIBTOOL% %LIBFLAGS% /MACHINE:X64 /OUT:rdrand-x64.lib rdrand-x64.obj > nul +@IF NOT %ERRORLEVEL% EQU 0 (echo Failed to create rdrand-x64.lib && goto SCRIPT_FAILED) +echo Done... + +goto SKIP_SYMBOL_DUMP_OBJ + +echo **************************************** +echo Dumping symbols for rdrand-x86.obj +echo. +call %DUMPBIN% /SYMBOLS rdrand-x86.obj + +echo **************************************** +echo Dumping symbols for rdrand-x64.obj +echo. +call %DUMPBIN% /SYMBOLS rdrand-x64.obj + +:SKIP_SYMBOL_DUMP_OBJ + +goto SKIP_SYMBOL_DUMP_LIB + +echo **************************************** +echo Dumping symbols for rdrand-x86.lib +echo. +call %DUMPBIN% /SYMBOLS rdrand-x86.lib + +echo **************************************** +echo Dumping symbols for rdrand-x64.lib +echo. +call %DUMPBIN% /SYMBOLS rdrand-x64.lib + +:SKIP_SYMBOL_DUMP_LIB + +goto SKIP_EXPORT_DUMP + +echo **************************************** +echo Dumping exports for rdrand-x86.lib +echo. +call %DUMPBIN% /EXPORTS rdrand-x86.lib + +echo **************************************** +echo Dumping exports for rdrand-x64.lib +echo. +call %DUMPBIN% /EXPORTS rdrand-x64.lib + +:SKIP_EXPORT_DUMP + +REM goto SKIP_DISASSEMBLY + +echo **************************************** +echo Disassembling rdrand-x64.obj +echo. +call %DUMPBIN% /DISASM:NOBYTES rdrand-x64.obj + +echo **************************************** +echo Disassembling rdrand-x86.obj +echo. +call %DUMPBIN% /DISASM:NOBYTES rdrand-x86.obj + +:SKIP_DISASSEMBLY + +:SCRIPT_FAILED diff --git a/rdrand-nasm.sh b/rdrand-nasm.sh new file mode 100755 index 00000000..bc135c16 --- /dev/null +++ b/rdrand-nasm.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +reset + +nasm -f elf32 rdrand.S -DX86 -g -o rdrand-x86.o +nasm -f elfx32 rdrand.S -DX32 -g -o rdrand-x32.o +nasm -f elf64 rdrand.S -DX64 -g -o rdrand-x64.o + +echo "**************************************" +echo "**************************************" + +objdump --disassemble rdrand-x86.o + +echo +echo "**************************************" +echo "**************************************" + +objdump --disassemble rdrand-x32.o + +echo +echo "**************************************" +echo "**************************************" + +objdump --disassemble rdrand-x64.o diff --git a/rdrand.S b/rdrand.S new file mode 100644 index 00000000..e78b4f1a --- /dev/null +++ b/rdrand.S @@ -0,0 +1,599 @@ +;; 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 Unix and Linux tool chains. +;; Additionally, the inline assembly code produced by GCC and Clang is not that +;; impressive. However, using this code requires NASM and an edit to the GNUmakefile. + +;; nasm -f elf32 rdrand.S -DX86 -g -o rdrand-x86.o +;; nasm -f elfx32 rdrand.S -DX32 -g -o rdrand-x32.o +;; nasm -f elf64 rdrand.S -DX64 -g -o rdrand-x64.o + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Naming convention used in rdrand.{h|cpp|asm|S} +;; MSC = Microsoft Compiler (and compatibles) +;; GCC = GNU Compiler (and compatibles) +;; ALL = MSC and GCC (and compatibles) +;; RRA = RDRAND, Assembly +;; RSA = RDSEED, Assembly +;; RRI = RDRAND, Intrinsic +;; RSA = RDSEED, Intrinsic + +;; Caller/Callee Saved Registers +;; https://msdn.microsoft.com/en-us/library/6t169e9c.aspx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; C/C++ Function prototypes +;; X86, X32 and X64: +;; extern "C" int NASM_RRA_GenerateBlock(byte* ptr, size_t size, unsigned int safety); + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Return values +%define RDRAND_SUCCESS 1 +%define RDRAND_FAILURE 0 + +%define RDSEED_SUCCESS 1 +%define RDSEED_FAILURE 0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +%ifdef X86 or X32 ;; Set via the command line + +;; Arg1, byte* buffer +;; Arg2, size_t bsize +;; Arg3, unsigned int safety +;; EAX (out): success (1), failure (0) + +global NASM_RRA_GenerateBlock +section .text + +%ifdef X86 +align 8 +cpu 486 +%else +align 16 +%endif + +NASM_RRA_GenerateBlock: + +%ifdef X86 +%define arg1 [ebp+04h] +%define arg2 [ebp+08h] +%define arg3 [ebp+0ch] +%define MWSIZE 04h ;; machine word size +%else +%define MWSIZE 08h ;; machine word size +%endif + + %define buffer edi + %define bsize esi + %define safety edx + +%ifdef X86 +.Load_Arguments: + + mov buffer, arg1 + mov bsize, arg2 + mov safety, arg3 +%endif + +.Validate_Pointer: + + cmp buffer, 0 + je .GenerateBlock_PreRet + + ;; Top of While loop +.GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je .GenerateBlock_Success + +%ifdef X86 +.Call_RDRAND_EAX: +%else +.Call_RDRAND_RAX: + DB 48h ;; X32 can use the full register, issue the REX.w prefix +%endif + ;; RDRAND is not available prior to VS2012. Just emit + ;; the byte codes using DB. This is `rdrand eax`. + DB 0Fh, 07h, F0h + + ;; If CF=1, the number returned by RDRAND is valid. + ;; If CF=0, a random number was not available. + jc .RDRAND_succeeded + +.RDRAND_failed: + + ;; Exit if we've reached the limit + cmp safety, 0 + je .GenerateBlock_Failure + + dec safety + jmp .GenerateBlock_Top + +.RDRAND_succeeded: + + cmp bsize, MWSIZE + jb .Partial_Machine_Word + +.Full_Machine_Word: + +%ifdef X32 + mov [buffer+4], eax ;; We can only move 4 at a time + DB 048h ;; Combined, these result in + shr eax, 32 ;; `shr rax, 32` +%endif + + mov [buffer], eax + add buffer, MWSIZE ;; No need for Intel Core 2 slow word workarounds, + sub bsize, MWSIZE ;; like `lea buffer,[buffer+MWSIZE]` for faster adds + + ;; Continue + jmp .GenerateBlock_Top + + ;; 1,2,3 bytes remain for X86 + ;; 1,2,3,4,5,6,7 remain for X32 +.Partial_Machine_Word: + +%ifdef X32 + ;; Test bit 2 to see if size is at least 4 + test bsize, 4 + jz .Bit_2_Not_Set + + mov [buffer], eax + add buffer, 4 + + DB 048h ;; Combined, these result in + shr eax, 32 ;; `shr rax, 32` + +.Bit_2_Not_Set: +%endif + + ;; Test bit 1 to see if size is at least 2 + test bsize, 2 + jz .Bit_1_Not_Set + + mov [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 .GenerateBlock_Success + + mov [buffer], al + +.Bit_0_Not_Set: + + ;; We've hit all the bits + jmp .GenerateBlock_Success + +.GenerateBlock_PreRet: + + ;; Test for success (was the request completely fulfilled?) + cmp bsize, 0 + je .GenerateBlock_Success + +.GenerateBlock_Failure: + + xor eax, eax + mov al, RDRAND_FAILURE + ret + +.GenerateBlock_Success: + + xor eax, eax + mov al, RDRAND_SUCCESS + ret + +%endif ;; X86 and X32 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +%ifdef X64 ;; Set via the command line + +global NASM_RRA_GenerateBlock +section .text +align 16 + +;; Arg1, byte* buffer +;; Arg2, size_t bsize +;; Arg3, unsigned int safety +;; RAX (out): success (1), failure (0) + +NASM_RRA_GenerateBlock: + +%define MWSIZE 08h ;; machine word size +%define buffer rdi +%define bsize rsi +%define safety edx + + ;; No need for Load_Arguments due to fastcall + +.Validate_Pointer: + + ;; Validate pointer + cmp buffer, 0 + je .GenerateBlock_PreRet + + ;; Top of While loop +.GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je .GenerateBlock_Success + +.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. + jc .RDRAND_succeeded + +.RDRAND_failed: + + ;; Exit if we've reached the limit + cmp safety, 0h + je .GenerateBlock_Failure + + dec safety + jmp .GenerateBlock_Top + +.RDRAND_succeeded: + + cmp bsize, MWSIZE + jb .Partial_Machine_Word + +.Full_Machine_Word: + + mov [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 [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 [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 .GenerateBlock_Success + + mov [buffer], al + +.Bit_0_Not_Set: + + ;; We've hit all the bits + jmp .GenerateBlock_Success + +.GenerateBlock_PreRet: + + ;; Test for success (was the request completely fulfilled?) + cmp bsize, 0 + je .GenerateBlock_Success + +.GenerateBlock_Failure: + + xor rax, rax + mov al, RDRAND_FAILURE + ret + +.GenerateBlock_Success: + + xor rax, rax + mov al, RDRAND_SUCCESS + ret + +%endif ;; X64 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +%ifdef X86 or X32 ;; Set via the command line + +;; Arg1, byte* buffer +;; Arg2, size_t bsize +;; Arg3, unsigned int safety +;; EAX (out): success (1), failure (0) + +global NASM_RSA_GenerateBlock +section .text +align 8 + +%ifdef X86 +align 8 +cpu 486 +%else +align 16 +%endif + +NASM_RSA_GenerateBlock: + +%ifdef X86 +%define arg1 [ebp+04h] +%define arg2 [ebp+08h] +%define arg3 [ebp+0ch] +%define MWSIZE 04h ;; machine word size +%else +%define MWSIZE 08h ;; machine word size +%endif + + %define buffer edi + %define bsize esi + %define safety edx + +%ifdef X86 +.Load_Arguments: + + mov buffer, arg1 + mov bsize, arg2 + mov safety, arg3 +%endif + +.Validate_Pointer: + + cmp buffer, 0 + je .GenerateBlock_PreRet + + ;; Top of While loop +.GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je .GenerateBlock_Success + +%ifdef X86 +.Call_RDSEED_EAX: +%else +.Call_RDSEED_RAX: + DB 48h ;; X32 can use the full register, issue the REX.w prefix +%endif + ;; 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. + jc .RDSEED_succeeded + +.RDSEED_failed: + + ;; Exit if we've reached the limit + cmp safety, 0 + je .GenerateBlock_Failure + + dec safety + jmp .GenerateBlock_Top + +.RDSEED_succeeded: + + cmp bsize, MWSIZE + jb .Partial_Machine_Word + +.Full_Machine_Word: + + mov [buffer], eax + add buffer, MWSIZE ;; No need for Intel Core 2 slow word workarounds, + sub bsize, MWSIZE ;; like `lea buffer,[buffer+MWSIZE]` for faster adds + + ;; Continue + jmp .GenerateBlock_Top + + ;; 1,2,3 bytes remain for X86 + ;; 1,2,3,4,5,6,7 remain for X32 +.Partial_Machine_Word: + +%ifdef X32 + ;; Test bit 2 to see if size is at least 4 + test bsize, 4 + jz .Bit_2_Not_Set + + mov [buffer], eax + add buffer, 4 + + DB 048h ;; Combined, these result in + shr eax, 32 ;; `shr rax, 32` + +.Bit_2_Not_Set: +%endif + + ;; Test bit 1 to see if size is at least 2 + test bsize, 2 + jz .Bit_1_Not_Set + + mov [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 .GenerateBlock_Success + + mov [buffer], al + +.Bit_0_Not_Set: + + ;; We've hit all the bits + jmp .GenerateBlock_Success + +.GenerateBlock_PreRet: + + ;; Test for success (was the request completely fulfilled?) + cmp bsize, 0 + je .GenerateBlock_Success + +.GenerateBlock_Failure: + + xor eax, eax + mov al, RDSEED_FAILURE + ret + +.GenerateBlock_Success: + + xor eax, eax + mov al, RDSEED_SUCCESS + ret + +%endif ;; X86 and X32 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +%ifdef X64 ;; Set via the command line + +global NASM_RSA_GenerateBlock +section .text +align 16 + +;; Arg1, byte* buffer +;; Arg2, size_t bsize +;; Arg3, unsigned int safety +;; RAX (out): success (1), failure (0) + +NASM_RSA_GenerateBlock: + +%define MWSIZE 08h ;; machine word size +%define buffer rdi +%define bsize rsi +%define safety edx + + ;; No need for Load_Arguments due to fastcall + +.Validate_Pointer: + + ;; Validate pointer + cmp buffer, 0 + je .GenerateBlock_PreRet + + ;; Top of While loop +.GenerateBlock_Top: + + ;; Check remaining size + cmp bsize, 0 + je .GenerateBlock_Success + +.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. + jc .RDSEED_succeeded + +.RDSEED_failed: + + ;; Exit if we've reached the limit + cmp safety, 0 + je .GenerateBlock_Failure + + dec safety + jmp .GenerateBlock_Top + +.RDSEED_succeeded: + + cmp bsize, MWSIZE + jb .Partial_Machine_Word + +.Full_Machine_Word: + + mov [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 [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 [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 .GenerateBlock_Success + + mov [buffer], al + +.Bit_0_Not_Set: + + ;; We've hit all the bits + jmp .GenerateBlock_Success + +.GenerateBlock_PreRet: + + ;; Test for success (was the request completely fulfilled?) + cmp bsize, 0 + je .GenerateBlock_Success + +.GenerateBlock_Failure: + + xor rax, rax + mov al, RDSEED_FAILURE + ret + +.GenerateBlock_Success: + + xor rax, rax + mov al, RDSEED_SUCCESS + ret + +%endif ;; _M_X64 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + diff --git a/rdrand.asm b/rdrand.asm index ecb19ae4..73f5a964 100644 --- a/rdrand.asm +++ b/rdrand.asm @@ -1,10 +1,9 @@ ;; rdrand.asm - written and placed in public domain by Jeffrey Walton and Uri Blumenthal. ;; Copyright assigned to the Crypto++ project. -;; This ASM file exists for one reason only - to emit the RDRAND instruction for Microsoft platforms. -;; We can't call RDRAND directly on some versions of the toolchain (prior to CL 17.00/VS2012). -;; Additionally, Microsoft/Intel penalizes AMD CPUs with the feature. To avoid these troubles, we -;; provide the ASM and emit the opcodes for RDRAND by hand. +;; 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 @@ -14,11 +13,11 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -TITLE MSC_RRA_GenerateBlock and MSC_RSA_GenerateBlock +TITLE MASM_RRA_GenerateBlock and MASM_RSA_GenerateBlock SUBTITLE Microsoft specific ASM code to utilize RDRAND and RDSEED for down level Microsoft toolchains -PUBLIC MSC_RRA_GenerateBlock -PUBLIC MSC_RSA_GenerateBlock +PUBLIC MASM_RRA_GenerateBlock +PUBLIC MASM_RSA_GenerateBlock ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -26,6 +25,7 @@ PUBLIC MSC_RSA_GenerateBlock ;; Naming convention used in rdrand.{h|cpp|asm} ;; MSC = Microsoft Compiler (and compatibles) ;; GCC = GNU Compiler (and compatibles) +;; ALL = MSC and GCC (and compatibles) ;; RRA = RDRAND, Assembly ;; RSA = RDSEED, Assembly ;; RRI = RDRAND, Intrinsic @@ -39,9 +39,9 @@ PUBLIC MSC_RSA_GenerateBlock ;; C/C++ Function prototypes ;; X86: -;; extern "C" int MSC_RRA_GenerateBlock(byte* ptr, size_t size, unsigned int safety); +;; extern "C" int MASM_RRA_GenerateBlock(byte* ptr, size_t size, unsigned int safety); ;; X64: -;; extern "C" int __fastcall MSC_RRA_GenerateBlock(byte* ptr, size_t size, unsigned int safety); +;; extern "C" int __fastcall MASM_RRA_GenerateBlock(byte* ptr, size_t size, unsigned int safety); ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -79,8 +79,7 @@ OPTION EPILOGUE:NONE ;; Base relative (in): arg3, unsigned int safety ;; EAX (out): success (1), failure (0) -;; MSC_RRA_GenerateBlock PROC USES ecx edx edi arg1:DWORD,arg2:DWORD,arg3:DWORD -MSC_RRA_GenerateBlock PROC arg1:DWORD,arg2:DWORD,arg3:DWORD +MASM_RRA_GenerateBlock PROC arg1:DWORD,arg2:DWORD,arg3:DWORD MWSIZE EQU 04h ;; machine word size buffer EQU edi @@ -108,7 +107,7 @@ GenerateBlock_Top: 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 + DB 0Fh, 0C7h, 0F0h ;; If CF=1, the number returned by RDRAND is valid. ;; If CF=0, a random number was not available. @@ -152,15 +151,13 @@ Bit_1_Not_Set: ;; Test bit 0 to see if size is at least 1 test bsize, 1 - ;; jz Bit_0_Not_Set jz GenerateBlock_Success mov BYTE PTR [buffer], al Bit_0_Not_Set: - ;; We've hit all the bits, set size to 0 - ;; mov bsize, 0 + ;; We've hit all the bits jmp GenerateBlock_Success GenerateBlock_PreRet: @@ -171,18 +168,17 @@ GenerateBlock_PreRet: GenerateBlock_Failure: - mov eax, RDRAND_FAILURE - jmp GenerateBlock_Ret + xor eax, eax + mov al, RDRAND_FAILURE + ret GenerateBlock_Success: - mov eax, RDRAND_SUCCESS - -GenerateBlock_Ret: - + xor eax, eax + mov al, RDRAND_SUCCESS ret -MSC_RRA_GenerateBlock ENDP +MASM_RRA_GenerateBlock ENDP ENDIF ;; _M_X86 @@ -201,8 +197,7 @@ OPTION EPILOGUE:NONE ;; R8d (in): arg3, unsigned int safety ;; RAX (out): success (1), failure (0) -;; MSC_RRA_GenerateBlock PROC USES RCX ;; arg1:QWORD,arg2:QWORD,arg3:DWORD -MSC_RRA_GenerateBlock PROC ;; arg1:QWORD,arg2:QWORD,arg3:DWORD +MASM_RRA_GenerateBlock PROC MWSIZE EQU 08h ;; machine word size buffer EQU rcx @@ -227,7 +222,7 @@ GenerateBlock_Top: 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 + DB 048h, 0Fh, 0C7h, 0F0h ;; If CF=1, the number returned by RDRAND is valid. ;; If CF=0, a random number was not available. @@ -281,15 +276,13 @@ Bit_1_Not_Set: ;; Test bit 0 to see if size is at least 1 test bsize, 1 - ;; jz Bit_0_Not_Set jz GenerateBlock_Success mov BYTE PTR [buffer], al Bit_0_Not_Set: - ;; We've hit all the bits, set size to 0 - ;; mov bsize, 0 + ;; We've hit all the bits jmp GenerateBlock_Success GenerateBlock_PreRet: @@ -300,18 +293,17 @@ GenerateBlock_PreRet: GenerateBlock_Failure: - mov rax, RDRAND_FAILURE - jmp GenerateBlock_Ret + xor rax, rax + mov al, RDRAND_FAILURE + ret GenerateBlock_Success: - mov rax, RDRAND_SUCCESS - -GenerateBlock_Ret: - + xor rax, rax + mov al, RDRAND_SUCCESS ret -MSC_RRA_GenerateBlock ENDP +MASM_RRA_GenerateBlock ENDP ENDIF ;; _M_X64 @@ -331,8 +323,7 @@ OPTION EPILOGUE:NONE ;; Base relative (in): arg3, unsigned int safety ;; EAX (out): success (1), failure (0) -;; MSC_RSA_GenerateBlock PROC USES ecx edx edi arg1:DWORD,arg2:DWORD,arg3:DWORD -MSC_RSA_GenerateBlock PROC arg1:DWORD,arg2:DWORD,arg3:DWORD +MASM_RSA_GenerateBlock PROC arg1:DWORD,arg2:DWORD,arg3:DWORD MWSIZE EQU 04h ;; machine word size buffer EQU edi @@ -347,20 +338,20 @@ Load_Arguments: Validate_Pointer: - cmp buffer, 0 - je GenerateBlock_PreRet + cmp buffer, 0 + je GenerateBlock_PreRet ;; Top of While loop GenerateBlock_Top: ;; Check remaining size - cmp bsize, 0 + cmp bsize, 0 je GenerateBlock_Success 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 + DB 0Fh, 0C7h, 0F8h ;; If CF=1, the number returned by RDSEED is valid. ;; If CF=0, a random number was not available. @@ -404,15 +395,13 @@ Bit_1_Not_Set: ;; Test bit 0 to see if size is at least 1 test bsize, 1 - ;; jz Bit_0_Not_Set jz GenerateBlock_Success mov BYTE PTR [buffer], al Bit_0_Not_Set: - ;; We've hit all the bits, set size to 0 - ;; mov bsize, 0 + ;; We've hit all the bits jmp GenerateBlock_Success GenerateBlock_PreRet: @@ -423,18 +412,17 @@ GenerateBlock_PreRet: GenerateBlock_Failure: - mov eax, RDSEED_FAILURE - jmp GenerateBlock_Ret + xor eax, eax + mov al, RDSEED_FAILURE + ret GenerateBlock_Success: - mov eax, RDSEED_SUCCESS - -GenerateBlock_Ret: - + xor eax, eax + mov al, RDSEED_SUCCESS ret -MSC_RSA_GenerateBlock ENDP +MASM_RSA_GenerateBlock ENDP ENDIF ;; _M_X86 @@ -453,8 +441,7 @@ OPTION EPILOGUE:NONE ;; R8d (in): arg3, unsigned int safety ;; RAX (out): success (1), failure (0) -;; MSC_RSA_GenerateBlock PROC USES RCX ;; arg1:QWORD,arg2:QWORD,arg3:DWORD -MSC_RSA_GenerateBlock PROC ;; arg1:QWORD,arg2:QWORD,arg3:DWORD +MASM_RSA_GenerateBlock PROC ;; arg1:QWORD,arg2:QWORD,arg3:DWORD MWSIZE EQU 08h ;; machine word size buffer EQU rcx @@ -533,15 +520,13 @@ Bit_1_Not_Set: ;; Test bit 0 to see if size is at least 1 test bsize, 1 - ;; jz Bit_0_Not_Set jz GenerateBlock_Success mov BYTE PTR [buffer], al Bit_0_Not_Set: - ;; We've hit all the bits, set size to 0 - ;; mov bsize, 0 + ;; We've hit all the bits jmp GenerateBlock_Success GenerateBlock_PreRet: @@ -552,18 +537,17 @@ GenerateBlock_PreRet: GenerateBlock_Failure: - mov rax, RDSEED_FAILURE - jmp GenerateBlock_Ret + xor rax, rax + mov al, RDSEED_FAILURE + ret GenerateBlock_Success: - mov rax, RDSEED_SUCCESS - -GenerateBlock_Ret: - + xor rax, rax + mov al, RDSEED_SUCCESS ret -MSC_RSA_GenerateBlock ENDP +MASM_RSA_GenerateBlock ENDP ENDIF ;; _M_X64 diff --git a/rdrand.cpp b/rdrand.cpp index 66cb6561..48fea517 100644 --- a/rdrand.cpp +++ b/rdrand.cpp @@ -20,225 +20,163 @@ // available. A lazy throw strategy is used in case the CPU does not support // the instruction. I.e., the throw is deferred until GenerateBlock is called. -// For GCC/ICC/Clang on Unix/Linux/Apple, you can use `-mrdrnd` to force the -// option. If you use `-mrdrnd`, then __RDRND__ is defined and intrinsics -// are used. If you omit the otion, then assembly language routines are -// used if the compiler supports RDRAND. The same applies to -mrdseed and -// __RDSEED__ (but they did not skimp on the extra vowel). Also see -// http://gcc.gnu.org/onlinedocs/gcc/x86-Built-in-Functions.html#x86-Built-in-Functions - // Here's the naming convention for the functions.... // MSC = Microsoft Compiler (and compatibles) // GCC = GNU Compiler (and compatibles) +// ALL = MSC and GCC (and compatibles) // RRA = RDRAND, Assembly // RSA = RDSEED, Assembly // RRI = RDRAND, Intrinsic // RSA = RDSEED, Intrinsic -// Helper macros. IA32_ASM captuers the architecture. MSC_RDRAND_COMPILER means -// MSC_RDSEED_COMPILER; GCC_RDRAND_COMPILER means GCC_RDSEED_COMPILER. -#define IA32_ASM (!defined(CRYPTOPP_DISABLE_ASM) && (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64)) -#define MSC_RDRAND_COMPILER ((CRYPTOPP_MSC_VERSION >= 1700) || (CRYPTOPP_CLANG_VERSION >= 30200) || (_INTEL_COMPILER >= 1210)) -#define GCC_RDRAND_COMPILER ((CRYPTOPP_GCC_VERSION >= 40600) || (CRYPTOPP_CLANG_VERSION >= 30200) || (_INTEL_COMPILER >= 1210)) -#define MSC_RDSEED_COMPILER MSC_RDRAND_COMPILER -#define GCC_RDSEED_COMPILER GCC_RDRAND_COMPILER +///////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// -// GCC cannot compile programs with __builtin_ia32_rdseed{16|32|64}_step -#if __GNUC__ -# define GCC_RDSEED_INTRINSIC_AVAILABLE 0 +// For Linux, install NASM, run rdrand-nasm.asm, add the apppropriate +// object file to the Makefile's LIBOBJS (rdrand-x{86|32|64}.o). After +// that, define these. They are not enabled by default because they +// are not easy to cut-in in the Makefile. + +#if 0 +#define NASM_RDRAND_ASM_AVAILABLE 1 +#define NASM_RDSEED_ASM_AVAILABLE 1 #endif ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// -// Microsoft uses Intel's intrinsics, and it excludes AMD's CPUs. You should -// "#define MSC_RDRAND_INTRINSIC_AVAILABLE 0" and -// "#define MSC_RDSEED_INTRINSIC_AVAILABLE 0", if possible. The downside is -// you must assemble the object files rdrand-x86.obj and rdrand-x86.obj and -// then build the rdrand-x86.lib and rdrand-x86.lib libraries. To build the -// libraries run "make-rdrand.cmd" from a developer prompt. - -///////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////// - -// MSC Compatible on Windows. Set MSC_RDRAND_ASM_AVAILABLE if it (and Intrinsics) -// are not set. The requirement is rdrand.asm assembled with MASM/MAS64. We use -// CRYTPOPP_MSC_VERSION as a proxy for MASM/MAS64 availability. -#if defined(CRYPTOPP_WIN32_AVAILABLE) && IA32_ASM && defined(CRYTPOPP_MSC_VERSION) -# if !defined(MSC_RDRAND_ASM_AVAILABLE) && !(MSC_RDRAND_INTRINSIC_AVAILABLE > 0) -# define MSC_RDRAND_ASM_AVAILABLE 1 -# define MSC_RDRAND_INTRINSIC_AVAILABLE 0 +// According to Wei, CRYPTOPP_DISABLE_ASM is a failsafe due to the assembler. +// We sidestep it because it does not limit us. The assembler does not limit +// us because we emit out own byte codes as needed. To diasble RDRAND or +// RDSEED, set CRYPTOPP_BOOL_RDRAND_ASM or CRYPTOPP_BOOL_RDSEED_ASM to 0. +#ifndef CRYPTOPP_CPUID_AVAILABLE +# if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) +# define CRYPTOPP_CPUID_AVAILABLE # endif #endif -// Fallback to MSC_RDRAND_INTRINSIC_AVAILABLE on Windows. The compiler must support it. -#if defined(CRYPTOPP_WIN32_AVAILABLE) && MSC_RDRAND_COMPILER -# if !defined(MSC_RDRAND_INTRINSIC_AVAILABLE) && !(MSC_RDRAND_ASM_AVAILABLE > 0) -# define MSC_RDRAND_INTRINSIC_AVAILABLE 1 -# define MSC_RDRAND_ASM_AVAILABLE 0 -# endif +#if defined(CRYPTOPP_CPUID_AVAILABLE) && !defined(CRYPTOPP_BOOL_RDRAND_ASM) +# define CRYPTOPP_BOOL_RDRAND_ASM 1 +#else +# define CRYPTOPP_BOOL_RDRAND_ASM 0 +#endif +#if defined(CRYPTOPP_CPUID_AVAILABLE) && !defined(CRYPTOPP_BOOL_RDSEED_ASM) +# define CRYPTOPP_BOOL_RDSEED_ASM 1 +#else +# define CRYPTOPP_BOOL_RDSEED_ASM 0 #endif -// GCC Compatible on Unix/Linux/Apple. Set GCC_RDRAND_INTRINSIC_AVAILABLE if -// it (and ASM) are not set. The requirements are __RDRND__ preprocessor. -#if defined(CRYPTOPP_UNIX_AVAILABLE) && GCC_RDRAND_COMPILER && (__RDRND__ >= 1) -# if !defined(GCC_RDRAND_INTRINSIC_AVAILABLE) && !(defined(GCC_RDRAND_ASM_AVAILABLE) && (GCC_RDRAND_ASM_AVAILABLE > 0)) -# define GCC_RDRAND_INTRINSIC_AVAILABLE 1 -# define GCC_RDRAND_ASM_AVAILABLE 0 -# endif +#if defined(CRYPTOPP_CPUID_AVAILABLE) +# define MSC_INTRIN_COMPILER ((CRYPTOPP_MSC_VERSION >= 1700) || (CRYPTOPP_CLANG_VERSION >= 30200) || (_INTEL_COMPILER >= 1210)) +# define GCC_INTRIN_COMPILER ((CRYPTOPP_GCC_VERSION >= 40600) || (CRYPTOPP_CLANG_VERSION >= 30200) || (_INTEL_COMPILER >= 1210)) +#else +# define MSC_INTRIN_COMPILER 0 +# define GCC_INTRIN_COMPILER 0 #endif -// Fallback to MSC_ASM_INTRINSIC_AVAILABLE on Unix/Linux/Apple -#if defined(CRYPTOPP_UNIX_AVAILABLE) && IA32_ASM && GCC_RDRAND_COMPILER -# if !defined(GCC_RDRAND_INTRINSIC_AVAILABLE) && !(defined(GCCC_RDRAND_ASM_AVAILABLE) && (GCCC_RDRAND_ASM_AVAILABLE > 0)) -# define GCC_RDRAND_ASM_AVAILABLE 1 -# define GCC_RDRAND_INTRINSIC_AVAILABLE 0 -# endif +// In general, the library's ASM code is best on Windows, and Intrinsics is +// the best code under GCC and compatibles. We favor them accordingly. +// The NASM code is optimized well on Linux, but its not easy to cut-in. +#if defined(CRYPTOPP_CPUID_AVAILABLE) && (CRYPTOPP_MSC_VERSION >= 1200) +# if CRYPTOPP_BOOL_RDRAND_ASM +# define MASM_RDRAND_ASM_AVAILABLE 1 +# elif MSC_INTRIN_COMPILER +# define ALL_RDRAND_INTRIN_AVAILABLE 1 +# endif +# if CRYPTOPP_BOOL_RDSEED_ASM +# define MASM_RDSEED_ASM_AVAILABLE 1 +# elif MSC_INTRIN_COMPILER +# define ALL_RDSEED_INTRIN_AVAILABLE 1 +# endif +#elif defined(CRYPTOPP_CPUID_AVAILABLE) && (CRYPTOPP_GCC_VERSION >= 30200) +# if GCC_INTRIN_COMPILER && defined(__RDRND__) +# define ALL_RDRAND_INTRIN_AVAILABLE 1 +# elif CRYPTOPP_BOOL_RDRAND_ASM +# define GCC_RDRAND_ASM_AVAILABLE 1 +# endif +# if GCC_INTRIN_COMPILER && defined(__RDSEED__) +# define ALL_RDSEED_INTRIN_AVAILABLE 1 +# elif CRYPTOPP_BOOL_RDSEED_ASM +# define GCC_RDSEED_ASM_AVAILABLE 1 +# endif +#endif + +// Debug diagnostics +#if !defined(NDEBUG) +# if MASM_RDRAND_ASM_AVAILABLE +# pragma message ("MASM_RDRAND_ASM_AVAILABLE is 1") +# elif NASM_RDRAND_ASM_AVAILABLE +# pragma message ("NASM_RDRAND_ASM_AVAILABLE is 1") +# elif GCC_RDRAND_ASM_AVAILABLE +# pragma message ("GCC_RDRAND_ASM_AVAILABLE is 1") +# elif ALL_RDRAND_INTRIN_AVAILABLE +# pragma message ("ALL_RDRAND_INTRIN_AVAILABLE is 1") +# else +# pragma message ("RDRAND is not available") +# endif +# if MASM_RDSEED_ASM_AVAILABLE +# pragma message ("MASM_RDSEED_ASM_AVAILABLE is 1") +# elif NASM_RDSEED_ASM_AVAILABLE +# pragma message ("NASM_RDSEED_ASM_AVAILABLE is 1") +# elif GCC_RDSEED_ASM_AVAILABLE +# pragma message ("GCC_RDSEED_ASM_AVAILABLE is 1") +# elif ALL_RDSEED_INTRIN_AVAILABLE +# pragma message ("ALL_RDSEED_INTRIN_AVAILABLE is 1") +# else +# pragma message ("RDSEED is not available") +# endif #endif ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// -// MSC Compatible on Windows. Set MSC_RDRAND_ASM_AVAILABLE if it (and Intrinsics) -// are not set. The requirement is rdrand.asm assembled with MASM/MAS64. We use -// CRYTPOPP_MSC_VERSION as a proxy for MASM/MAS64 availability. -#if defined(CRYPTOPP_WIN32_AVAILABLE) && IA32_ASM && defined(CRYTPOPP_MSC_VERSION) -# if !defined(MSC_RDSEED_ASM_AVAILABLE) && !(MSC_RDSEED_INTRINSIC_AVAILABLE > 0) -# define MSC_RDSEED_ASM_AVAILABLE 1 -# define MSC_RDSEED_INTRINSIC_AVAILABLE 0 +#if (ALL_RDRAND_INTRIN_AVAILABLE || ALL_RDSEED_INTRIN_AVAILABLE) +# include // rdrand, MSC, ICC, and GCC +# if defined(__has_include) +# if __has_include() +# include // rdseed for some compilers, like GCC +# endif # endif #endif -// Fallback to MSC_RDSEED_INTRINSIC_AVAILABLE on Windows. The compiler must support it. -#if defined(CRYPTOPP_WIN32_AVAILABLE) && MSC_RDSEED_COMPILER -# if !defined(MSC_RDSEED_INTRINSIC_AVAILABLE) && !(MSC_RDSEED_ASM_AVAILABLE > 0) -# define MSC_RDSEED_INTRINSIC_AVAILABLE 1 -# define MSC_RDSEED_ASM_AVAILABLE 0 -# endif -#endif - -// GCC Compatible on Unix/Linux/Apple. Set GCC_RDSEED_INTRINSIC_AVAILABLE if -// it (and ASM) are not set. The requirements are __RDSEED__ preprocessor. -#if defined(CRYPTOPP_UNIX_AVAILABLE) && GCC_RDSEED_COMPILER && (__RDSEED__ >= 1) -# if !defined(GCC_RDSEED_INTRINSIC_AVAILABLE) && !(defined(GCC_RDSEED_ASM_AVAILABLE) && (GCC_RDSEED_ASM_AVAILABLE > 0)) -# define GCC_RDSEED_INTRINSIC_AVAILABLE 1 -# define GCC_RDSEED_ASM_AVAILABLE 0 -# endif -#endif - -// Fallback to MSC_ASM_INTRINSIC_AVAILABLE on Unix/Linux/Apple -#if defined(CRYPTOPP_UNIX_AVAILABLE) && IA32_ASM && GCC_RDSEED_COMPILER -# if !defined(GCC_RDSEED_INTRINSIC_AVAILABLE) && !(defined(GCCC_RDSEED_ASM_AVAILABLE) && (GCCC_RDSEED_ASM_AVAILABLE > 0)) -# define GCC_RDSEED_ASM_AVAILABLE 1 -# define GCC_RDSEED_INTRINSIC_AVAILABLE 0 -# endif -#endif - -///////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////// - -#if MSC_RDRAND_ASM_AVAILABLE +#if MASM_RDRAND_ASM_AVAILABLE # ifdef _M_X64 -extern "C" int CRYPTOPP_FASTCALL MSC_RRA_GenerateBlock(byte*, size_t, unsigned int); +extern "C" int CRYPTOPP_FASTCALL MASM_RRA_GenerateBlock(byte*, size_t, unsigned int); // # pragma comment(lib, "rdrand-x64.lib") # else -extern "C" int MSC_RRA_GenerateBlock(byte*, size_t, unsigned int); +extern "C" int MASM_RRA_GenerateBlock(byte*, size_t, unsigned int); // # pragma comment(lib, "rdrand-x86.lib") # endif #endif -#if MSC_RDSEED_ASM_AVAILABLE +#if MASM_RDSEED_ASM_AVAILABLE # ifdef _M_X64 -extern "C" int CRYPTOPP_FASTCALL MSC_RSA_GenerateBlock(byte*, size_t, unsigned int); +extern "C" int CRYPTOPP_FASTCALL MASM_RSA_GenerateBlock(byte*, size_t, unsigned int); // # pragma comment(lib, "rdrand-x64.lib") # else -extern "C" int MSC_RSA_GenerateBlock(byte*, size_t, unsigned int); +extern "C" int MASM_RSA_GenerateBlock(byte*, size_t, unsigned int); // # pragma comment(lib, "rdrand-x86.lib") # endif #endif +#if NASM_RDRAND_ASM_AVAILABLE +extern "C" int NASM_RRA_GenerateBlock(byte*, size_t, unsigned int); +#endif + +#if NASM_RDSEED_ASM_AVAILABLE +extern "C" int NASM_RSA_GenerateBlock(byte*, size_t, unsigned int); +#endif + ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// -#if MSC_RDRAND_INTRINSIC_AVAILABLE || MSC_RDSEED_INTRINSIC_AVAILABLE -# include -#elif GCC_RDRAND_INTRINSIC_AVAILABLE || GCC_RDSEED_INTRINSIC_AVAILABLE -# include -#endif - -// Define ERROR_DEV_NOT_EXIST for this TU if not already defined -#ifndef ERROR_DEV_NOT_EXIST -# define ERROR_DEV_NOT_EXIST 0x37 -#endif - -#if defined(CRYPTOPP_UNIX_AVAILABLE) -# include -#endif - NAMESPACE_BEGIN(CryptoPP) -///////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////// - -#if defined(CRYPTOPP_CPUID_AVAILABLE) -extern CRYPTOPP_DLL bool CpuId(word32 input, word32 *output); -#endif - -///////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////// - -// Intel and AMD provide RDRAND, only Intel provides RDSEED. To call these -// functions, use the word32 array returned from CpuId(0, output[]). - -static bool IsIntel(const word32 output[4]) +#if ALL_RDRAND_INTRIN_AVAILABLE +static int ALL_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety) { - // This is the "GenuineIntel" string - return (output[1] /*EBX*/ == 0x756e6547) && - (output[2] /*ECX*/ == 0x6c65746e) && - (output[3] /*EDX*/ == 0x49656e69); -} - -static bool IsAMD(const word32 output[4]) -{ - // This is the "AuthenticAMD" string - return (output[1] /*EBX*/ == 0x68747541) && - (output[2] /*ECX*/ == 0x69746E65) && - (output[3] /*EDX*/ == 0x444D4163); -} - -///////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////// - -static bool RDRAND_Runtime_Helper() -{ -#if defined(CRYPTOPP_CPUID_AVAILABLE) - bool rdrand = false; word32 output[4]; - if (CpuId(0, output)) - { - if (IsIntel(output) || IsAMD(output)) - { - if (output[0] /*EAX*/ >= 1 && CpuId(1, output)) - { - static const unsigned int RDRAND_FLAG = (1 << 30); - rdrand = !!(output[2] /*ECX*/ & RDRAND_FLAG); - } - } - } - return rdrand; -#else - return false; -#endif -} - -// This will be moved to CPU.h/CPU.cpp eventually -static bool hasRDRAND = RDRAND_Runtime_Helper(); - -#if defined(CRYPTOPP_CPUID_AVAILABLE) - -#if MSC_RDRAND_INTRINSIC_AVAILABLE -static int MSC_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety) -{ -#if CRYPTOPP_BOOL_X64 + assert((output && size) || !(output || size)); +#if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32 word64 val; #else word32 val; @@ -246,7 +184,7 @@ static int MSC_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety) while (size >= sizeof(val)) { -#if CRYPTOPP_BOOL_X64 +#if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32 if (_rdrand64_step((word64*)output)) #else if (_rdrand32_step((word32*)output)) @@ -264,7 +202,7 @@ static int MSC_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety) if (size) { -#if CRYPTOPP_BOOL_X64 +#if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32 if (_rdrand64_step(&val)) #else if (_rdrand32_step(&val)) @@ -280,7 +218,7 @@ static int MSC_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety) } } -#if CRYPTOPP_BOOL_X64 +#if CRYPTOPP_BOOL_X64 || CRYTPOPP_BOOL_X32 *((volatile word64*)&val) = 0; #else *((volatile word32*)&val) = 0; @@ -288,68 +226,12 @@ static int MSC_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety) return int(size == 0); } - -#endif // MSC_RDRAND_INTRINSIC_AVAILABLE - -#if GCC_RDRAND_INTRINSIC_AVAILABLE -static int GCC_RRI_GenerateBlock(byte *output, size_t size, unsigned int safety) -{ -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 - word64 val; -#else // CRYPTOPP_BOOL_X86 - word32 val; -#endif - - while (size >= sizeof(val)) - { -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 - if (__builtin_ia32_rdrand64_step((word64*)output)) -#else - if (__builtin_ia32_rdrand32_step((word32*)output)) -#endif - { - output += sizeof(val); - size -= sizeof(val); - } - else - { - if (!safety--) - return 0; - } - } - - if (size) - { -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 - if (__builtin_ia32_rdrand64_step(&val)) -#else - if (__builtin_ia32_rdrand32_step(&val)) -#endif - { - memcpy(output, &val, size); - size = 0; - } - else - { - if (!safety--) - return 0; - } - } - -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 - *((volatile word64*)&val) = 0; -#else - *((volatile word32*)&val) = 0; -#endif - - return int(size == 0); -} - -#endif // GCC_RDRAND_INTRINSIC_AVAILABLE +#endif // ALL_RDRAND_INTRINSIC_AVAILABLE #if GCC_RDRAND_ASM_AVAILABLE static int GCC_RRA_GenerateBlock(byte *output, size_t size, unsigned int safety) { + assert((output && size) || !(output || size)); #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 word64 val; #else @@ -359,9 +241,13 @@ static int GCC_RRA_GenerateBlock(byte *output, size_t size, unsigned int safety) while (size) { __asm__ volatile( - "rdrand %0; " +#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 + ".byte 0x48, 0x0f, 0xc7, 0xf0;\n" // rdrand rax +#else + ".byte 0x0f, 0xc7, 0xf0;\n" // rdrand eax +#endif "setc %1; " - : "=r" (val), "=qm" (rc) + : "=a" (val), "=qm" (rc) : : "cc" ); @@ -404,103 +290,39 @@ static int GCC_RRA_GenerateBlock(byte *output, size_t size, unsigned int safety) #endif // GCC_RDRAND_ASM_AVAILABLE -#endif // CRYPTOPP_CPUID_AVAILABLE (CRYPTOPP_BOOL_{X86|X32|X64}) - -//! generate random array of bytes +#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) void RDRAND::GenerateBlock(byte *output, size_t size) { - assert((output == NULL && size == 0) || (output != NULL && size != 0)); - assert(Available()); + CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size); + assert((output && size) || !(output || size)); - // We could (should?) test Ready, but Available conveys more useful information. - if(!Available()) + if(!HasRDRAND()) throw NotImplemented("RDRAND: rdrand is not available on this platform"); -#if defined(CRYPTOPP_CPUID_AVAILABLE) int rc; CRYPTOPP_UNUSED(rc); -#if MSC_RDRAND_ASM_AVAILABLE - rc = MSC_RRA_GenerateBlock(output, size, m_retries); - if (!rc) { throw RDRAND_Err("MSC_RRA_GenerateBlock"); } +#if MASM_RDRAND_ASM_AVAILABLE + rc = MASM_RRA_GenerateBlock(output, size, m_retries); + if (!rc) { throw RDRAND_Err("MASM_RRA_GenerateBlock"); } +#elif NASM_RDRAND_ASM_AVAILABLE + rc = NASM_RRA_GenerateBlock(output, size, m_retries); + if (!rc) { throw RDRAND_Err("NASM_RRA_GenerateBlock"); } +#elif ALL_RDRAND_INTRIN_AVAILABLE + rc = ALL_RRI_GenerateBlock(output, size, m_retries); + if (!rc) { throw RDRAND_Err("ALL_RRI_GenerateBlock"); } #elif GCC_RDRAND_ASM_AVAILABLE rc = GCC_RRA_GenerateBlock(output, size, m_retries); if (!rc) { throw RDRAND_Err("GCC_RRA_GenerateBlock"); } -#elif MSC_RDRAND_INTRINSIC_AVAILABLE - rc = MSC_RRI_GenerateBlock(output, size, m_retries); - if (!rc) { throw RDRAND_Err("MSC_RRI_GenerateBlock"); } -#elif GCC_RDRAND_INTRINSIC_AVAILABLE - rc = GCC_RRI_GenerateBlock(output, size, m_retries); - if (!rc) { throw RDRAND_Err("GCC_RRI_GenerateBlock"); } -#elif (__RDRND__ >= 1) - // RDRAND detected at compile time, GCC Compatible compiler, but no suitable implementations -# error "Please report on the Crypto++ user group" #else // RDRAND not detected at compile time, and no suitable compiler found throw NotImplemented("RDRAND: failed to find a suitable implementation???"); -#endif - -#endif // CRYPTOPP_CPUID_AVAILABLE (CRYPTOPP_BOOL_{X86|X32|X64}) +#endif // CRYPTOPP_CPUID_AVAILABLE } -//! returns true if RDRAND is present or available according to CPUID, false otherwise -bool RDRAND::Available() const -{ - word64 unused; - return Available(unused); -} - -//! returns true if RDRAND is present or available according to CPUID, false otherwise. There is no exended information available. -bool RDRAND::Available(word64& extendedInfo) const -{ - if (hasRDRAND) - { - extendedInfo = 0; - return true; - } - -#if defined(CRYPTOPP_WIN32_AVAILABLE) - extendedInfo = ERROR_DEV_NOT_EXIST; // 0x00000037 -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - extendedInfo = ENODEV; // 19 -#else - extendedInfo = word64(-1); -#endif - - return false; -} - -//! returns true if RDRAND is online/ready to produce random numbers, false otherwise -bool RDRAND::Ready() const -{ - word64 unused; - return Ready(unused); -} - -//! returns true if RDRAND is online/ready to produce random numbers, false otherwise. There is no exended information available. -bool RDRAND::Ready(word64& extendedInfo) const -{ - if (hasRDRAND) - { - extendedInfo = 0; - return true; - } - -#if defined(CRYPTOPP_WIN32_AVAILABLE) - extendedInfo = ERROR_DEV_NOT_EXIST; // 0x00000037 -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - extendedInfo = ENODEV; // 19 -#else - extendedInfo = word64(-1); -#endif - - return false; -} - -//! generate and discard n bytes. void RDRAND::DiscardBytes(size_t n) -{ +{ // RoundUpToMultipleOf is used because a full word is read, and its cheaper // to discard full words. There's no sense in dealing with tail bytes. - assert(Ready()); + assert(HasRDRAND()); #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 FixedSizeSecBlock discard; n = RoundUpToMultipleOf(n, sizeof(word64)); @@ -517,41 +339,16 @@ void RDRAND::DiscardBytes(size_t n) count = STDMIN(n, discard.SizeInBytes()); } } +#endif // CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64 ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// -static bool RDSEED_Runtime_Helper() +#if ALL_RDSEED_INTRIN_AVAILABLE +static int ALL_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety) { -#if defined(CRYPTOPP_CPUID_AVAILABLE) - bool rdseed = false; word32 output[4]; - if (CpuId(0, output)) - { - // Only Intel supports RDSEED at the moment. - if (IsIntel(output)) - { - if (output[0] /*EAX*/ >= 7 && CpuId(7, output)) - { - static const unsigned int RDSEED_FLAG = (1 << 18); - rdseed = !!(output[1] /*EBX*/ & RDSEED_FLAG); - } - } - } - return rdseed; -#else - return false; -#endif -} - -// This will be moved to CPU.h/CPU.cpp eventually -static bool hasRDSEED = RDSEED_Runtime_Helper(); - -#if defined(CRYPTOPP_CPUID_AVAILABLE) - -#if MSC_RDSEED_INTRINSIC_AVAILABLE -static int MSC_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety) -{ -#if CRYPTOPP_BOOL_X64 + assert((output && size) || !(output || size)); +#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 word64 val; #else word32 val; @@ -559,7 +356,7 @@ static int MSC_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety) while (size >= sizeof(val)) { -#if CRYPTOPP_BOOL_X64 +#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 if (_rdseed64_step((word64*)output)) #else if (_rdseed32_step((word32*)output)) @@ -577,7 +374,7 @@ static int MSC_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety) if (size) { -#if CRYPTOPP_BOOL_X64 +#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 if (_rdseed64_step(&val)) #else if (_rdseed32_step(&val)) @@ -593,62 +390,6 @@ static int MSC_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety) } } -#if CRYPTOPP_BOOL_X64 - *((volatile word64*)&val) = 0; -#else - *((volatile word32*)&val) = 0; -#endif - - return int(size == 0); -} - -#endif // MSC_RDSEED_INTRINSIC_AVAILABLE - -#if GCC_RDSEED_INTRINSIC_AVAILABLE -static int GCC_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety) -{ -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 - word64 val; -#else // CRYPTOPP_BOOL_X86 - word32 val; -#endif - - while (size >= sizeof(val)) - { -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 - if (__builtin_ia32_rdseed64_step((word64*)output)) -#else - if (__builtin_ia32_rdseed32_step((word32*)output)) -#endif - { - output += sizeof(val); - size -= sizeof(val); - } - else - { - if (!safety--) - return 0; - } - } - - if (size) - { -#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 - if (__builtin_ia32_rdseed64_step(&val)) -#else - if (__builtin_ia32_rdseed32_step(&val)) -#endif - { - memcpy(output, &val, size); - size = 0; - } - else - { - if (!safety--) - return 0; - } - } - #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 *((volatile word64*)&val) = 0; #else @@ -657,12 +398,12 @@ static int GCC_RSI_GenerateBlock(byte *output, size_t size, unsigned int safety) return int(size == 0); } - -#endif // GCC_RDSEED_INTRINSIC_AVAILABLE +#endif // ALL_RDSEED_INTRIN_AVAILABLE #if GCC_RDSEED_ASM_AVAILABLE static int GCC_RSA_GenerateBlock(byte *output, size_t size, unsigned int safety) { + assert((output && size) || !(output || size)); #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 word64 val; #else @@ -672,9 +413,13 @@ static int GCC_RSA_GenerateBlock(byte *output, size_t size, unsigned int safety) while (size) { __asm__ volatile( - "rdseed %0; " +#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 + ".byte 0x48, 0x0f, 0xc7, 0xf8;\n" // rdseed rax +#else + ".byte 0x0f, 0xc7, 0xf8;\n" // rdseed eax +#endif "setc %1; " - : "=r" (val), "=qm" (rc) + : "=a" (val), "=qm" (rc) : : "cc" ); @@ -714,107 +459,41 @@ static int GCC_RSA_GenerateBlock(byte *output, size_t size, unsigned int safety) return int(size == 0); } - #endif // GCC_RDSEED_ASM_AVAILABLE -#endif // CRYPTOPP_CPUID_AVAILABLE (CRYPTOPP_BOOL_{X86|X32|X64}) - -//! generate random array of bytes +#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) void RDSEED::GenerateBlock(byte *output, size_t size) { CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size); - assert((output == NULL && size == 0) || (output != NULL && size != 0)); - assert(Available()); + assert((output && size) || !(output || size)); - // We could (should?) test Ready, but Available conveys more useful information. - if(!Available()) + if(!HasRDSEED()) throw NotImplemented("RDSEED: rdseed is not available on this platform"); -#if defined(CRYPTOPP_CPUID_AVAILABLE) int rc; CRYPTOPP_UNUSED(rc); -#if MSC_RDSEED_ASM_AVAILABLE - rc = MSC_RSA_GenerateBlock(output, size, m_retries); - if (!rc) { throw RDSEED_Err("MSC_RSA_GenerateBlock"); } +#if MASM_RDSEED_ASM_AVAILABLE + rc = MASM_RSA_GenerateBlock(output, size, m_retries); + if (!rc) { throw RDSEED_Err("MASM_RSA_GenerateBlock"); } +#elif NASM_RDSEED_ASM_AVAILABLE + rc = NASM_RSA_GenerateBlock(output, size, m_retries); + if (!rc) { throw RDRAND_Err("NASM_RSA_GenerateBlock"); } +#elif ALL_RDSEED_INTRIN_AVAILABLE + rc = ALL_RSI_GenerateBlock(output, size, m_retries); + if (!rc) { throw RDSEED_Err("ALL_RSI_GenerateBlock"); } #elif GCC_RDSEED_ASM_AVAILABLE rc = GCC_RSA_GenerateBlock(output, size, m_retries); if (!rc) { throw RDSEED_Err("GCC_RSA_GenerateBlock"); } -#elif MSC_RDSEED_INTRINSIC_AVAILABLE - rc = MSC_RSI_GenerateBlock(output, size, m_retries); - if (!rc) { throw RDSEED_Err("MSC_RSI_GenerateBlock"); } -#elif GCC_RDSEED_INTRINSIC_AVAILABLE - rc = GCC_RSI_GenerateBlock(output, size, m_retries); - if (!rc) { throw RDSEED_Err("GCC_RSI_GenerateBlock"); } -#elif (__RDSEED__ >= 1) - // RDSEED detected at compile time, GCC Compatible compiler, but no suitable implementations -# error "Please report on the Crypto++ user group" #else // RDSEED not detected at compile time, and no suitable compiler found throw NotImplemented("RDSEED: failed to find a suitable implementation???"); #endif - -#endif // CRYPTOPP_CPUID_AVAILABLE (CRYPTOPP_BOOL_{X86|X32|X64}) } -//! returns true if RDSEED is present or available according to CPUID, false otherwise -bool RDSEED::Available() const -{ - word64 unused; - return Available(unused); -} - -//! returns true if RDSEED is present or available according to CPUID, false otherwise. There is no exended information available. -bool RDSEED::Available(word64& extendedInfo) const -{ - if (hasRDSEED) - { - extendedInfo = 0; - return true; - } - -#if defined(CRYPTOPP_WIN32_AVAILABLE) - extendedInfo = ERROR_DEV_NOT_EXIST; // 0x00000037 -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - extendedInfo = ENODEV; // 19 -#else - extendedInfo = word64(-1); -#endif - - return false; -} - -//! returns true if RDSEED is online/ready to produce random numbers, false otherwise -bool RDSEED::Ready() const -{ - word64 unused; - return Ready(unused); -} - -//! returns true if RDSEED is online/ready to produce random numbers, false otherwise. There is no exended information available. -bool RDSEED::Ready(word64& extendedInfo) const -{ - if (hasRDSEED) - { - extendedInfo = 0; - return true; - } - -#if defined(CRYPTOPP_WIN32_AVAILABLE) - extendedInfo = ERROR_DEV_NOT_EXIST; // 0x00000037 -#elif defined(CRYPTOPP_UNIX_AVAILABLE) - extendedInfo = ENODEV; // 19 -#else - extendedInfo = word64(-1); -#endif - - return false; -} - -//! generate and discard n bytes. void RDSEED::DiscardBytes(size_t n) -{ +{ // RoundUpToMultipleOf is used because a full word is read, and its cheaper // to discard full words. There's no sense in dealing with tail bytes. - assert(Ready()); + assert(HasRDSEED()); #if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32 FixedSizeSecBlock discard; n = RoundUpToMultipleOf(n, sizeof(word64)); @@ -831,5 +510,6 @@ void RDSEED::DiscardBytes(size_t n) count = STDMIN(n, discard.SizeInBytes()); } } +#endif // CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64 NAMESPACE_END diff --git a/rdrand.h b/rdrand.h index 6f02165b..8d5b7380 100644 --- a/rdrand.h +++ b/rdrand.h @@ -1,21 +1,30 @@ // rdrand.h - written and placed in public domain by Jeffrey Walton and Uri Blumenthal. // Copyright assigned to Crypto++ project. +//! \file +//! \headerfile rdrand.h +//! \brief Classes for RDRAND and RDSEED + #ifndef CRYPTOPP_RDRAND_H #define CRYPTOPP_RDRAND_H #include "cryptlib.h" +// This file (and friends) provides both RDRAND and RDSEED, but its somewhat +// experimental. They were added at Crypto++ 5.6.3. At compile time, it +// indirectly uses CRYPTOPP_BOOL_{X86|X32|X64} (via CRYPTOPP_CPUID_AVAILABLE) +// to select an implementation or "throw NotImplemented". At runtime, the +// class uses the result of CPUID to determine if RDRAND or RDSEED are +// available. A lazy throw strategy is used in case the CPU does not support +// the instruction. I.e., the throw is deferred until GenerateBlock is called. + // Microsoft added RDRAND in August 2012, VS2012. GCC added RDRAND in December 2010, GCC 4.6. // Clang added RDRAND in July 2012, Clang 3.2. Intel added RDRAND in September 2011, ICC 12.1. -// Visual Studio 2015 (CL version 1900) is missing _rdseed{16|32|64}_step -#if (CRYPTOPP_MSC_VERSION <= 1900) -# define MSC_RDSEED_INTRINSIC_AVAILABLE 0 -#endif - NAMESPACE_BEGIN(CryptoPP) +//! \brief Exception thrown when a RDRAND generator encounters +//! a generator related error. class RDRAND_Err : public Exception { public: @@ -23,61 +32,65 @@ public: : Exception(OTHER_ERROR, "RDRAND: " + operation + " operation failed") {} }; -//! \brief Read hardware generated random numbers. - -//! This file (and friends) provides both RDRAND and RDSEED, but its somewhat -//! experimental. They were added at Crypto++ 5.6.3. At compile time, it -//! indirectly uses CRYPTOPP_BOOL_{X86|X32|X64} (via CRYPTOPP_CPUID_AVAILABLE) -//! to select an implementation or "throw NotImplemented". At runtime, the -//! class uses the result of CPUID to determine if RDRAND or RDSEED are -//! available. A lazy throw strategy is used in case the CPU does not support -//! the instruction. I.e., the throw is deferred until GenerateBlock is called. -class RDRAND : public RandomNumberGenerator, public DeviceState +//! \brief Hardware generated random numbers using RDRAND instruction +//! \sa MaurerRandomnessTest() for random bit generators +class RDRAND : public RandomNumberGenerator { public: std::string AlgorithmName() const {return "RDRAND";} - //! construct a RDRAND generator with a maximum number of retires for failed generation attempts + //! \brief Construct a RDRAND generator + //! \param retries the number of retries for failed calls to the hardware + //! \details RDRAND() constructs a generator with a maximum number of retires + //! for failed generation attempts. RDRAND(unsigned int retries = 8) : m_retries(retries) {} virtual ~RDRAND() {} - //! returns true if RDRAND is present or available according to CPUID, false otherwise - bool Available() const; - - //! returns true if RDRAND is present or available according to CPUID, false otherwise. There is no exended information available. - bool Available(word64& extendedInfo) const; - - //! returns true if RDRAND is online/ready to produce random numbers, false otherwise - bool Ready() const; - - //! returns true if RDRAND is online/ready to produce random numbers, false otherwise. There is no exended information available. - bool Ready(word64& extendedInfo) const; - + //! \brief Retrieve the number of retries used by the generator //! returns the number of times GenerateBlock will attempt to recover from a failed generation unsigned int GetRetries() const { return m_retries; } - //! sets the number of times GenerateBlock will attempt to recover from a failed generation + //! \brief Set the number of retries used by the generator + //! \param the number of times GenerateBlock will attempt to recover from a failed generation void SetRetries(unsigned int retries) { m_retries = retries; } - //! generate random array of bytes + //! \brief Generate random array of bytes //! \param output the byte buffer //! \param size the length of the buffer, in bytes +#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) virtual void GenerateBlock(byte *output, size_t size); +#else + virtual void GenerateBlock(byte *output, size_t size) { + CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size); + throw NotImplemented("RDRAND: rdrand is not available on this platform"); + } +#endif - //! generate and discard n bytes. - //! \param n the number of bytes to discard + //! \brief Generate and discard n bytes + //! \param n the number of bytes to generate and discard + //! \details the RDSEED generator discards words, not bytes. If n is + //! not a multiple of a machine word, then it is rounded up to + //! that size. +#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) virtual void DiscardBytes(size_t n); +#else + virtual void DiscardBytes(size_t n) { + CRYPTOPP_UNUSED(n); + throw NotImplemented("RDRAND: rdrand is not available on this platform"); + } +#endif - //! update RNG state with additional unpredictable values. The operation is a nop for this generator. + //! Update RNG state with additional unpredictable values //! \param input unused //! \param length unused + //! \details The operation is a nop for this generator. virtual void IncorporateEntropy(const byte *input, size_t length) { // Override to avoid the base class' throw. @@ -89,6 +102,8 @@ private: unsigned int m_retries; }; +//! \brief Exception thrown when a RDSEED generator encounters +//! a generator related error. class RDSEED_Err : public Exception { public: @@ -96,61 +111,65 @@ public: : Exception(OTHER_ERROR, "RDSEED: " + operation + " operation failed") {} }; -//! \brief Read hardware generated random numbers. - -//! This file (and friends) provides both RDRAND and RDSEED, but its somewhat -//! experimental. They were added at Crypto++ 5.6.3. At compile time, it -//! indirectly uses CRYPTOPP_BOOL_{X86|X32|X64} (via CRYPTOPP_CPUID_AVAILABLE) -//! to select an implementation or "throw NotImplemented". At runtime, the -//! class uses the result of CPUID to determine if RDRAND or RDSEED are -//! available. A lazy throw strategy is used in case the CPU does not support -//! the instruction. I.e., the throw is deferred until GenerateBlock is called. -class RDSEED : public RandomNumberGenerator, public DeviceState +//! \brief Hardware generated random numbers using RDSEED instruction +//! \sa MaurerRandomnessTest() for random bit generators +class RDSEED : public RandomNumberGenerator { public: std::string AlgorithmName() const {return "RDSEED";} - //! construct a RDSEED generator with a maximum number of retires for failed generation attempts + //! \brief Construct a RDSEED generator + //! \param retries the number of retries for failed calls to the hardware + //! \details RDSEED() constructs a generator with a maximum number of retires + //! for failed generation attempts. RDSEED(unsigned int retries = 8) : m_retries(retries) {} virtual ~RDSEED() {} - //! returns true if RDSEED is present or available according to CPUID, false otherwise - bool Available() const; - - //! returns true if RDSEED is present or available according to CPUID, false otherwise. There is no exended information available. - bool Available(word64& extendedInfo) const; - - //! returns true if RDSEED is online/ready to produce random numbers, false otherwise - bool Ready() const; - - //! returns true if RDSEED is online/ready to produce random numbers, false otherwise. There is no exended information available. - bool Ready(word64& extendedInfo) const; - + //! \brief Retrieve the number of retries used by the generator //! returns the number of times GenerateBlock will attempt to recover from a failed generation unsigned int GetRetries() const { return m_retries; } - //! sets the number of times GenerateBlock will attempt to recover from a failed generation + //! \brief Set the number of retries used by the generator + //! \param the number of times GenerateBlock will attempt to recover from a failed generation void SetRetries(unsigned int retries) { m_retries = retries; } - //! generate random array of bytes + //! \brief Generate random array of bytes //! \param output the byte buffer //! \param size the length of the buffer, in bytes +#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) virtual void GenerateBlock(byte *output, size_t size); +#else + virtual void GenerateBlock(byte *output, size_t size) { + CRYPTOPP_UNUSED(output), CRYPTOPP_UNUSED(size); + throw NotImplemented("RDSEED: rdseed is not available on this platform"); + } +#endif - //! generate and discard n bytes. - //! \param n the number of bytes to discard + //! \brief Generate and discard n bytes + //! \param n the number of bytes to generate and discard + //! \details the RDSEED generator discards words, not bytes. If n is + //! not a multiple of a machine word, then it is rounded up to + //! that size. +#if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) virtual void DiscardBytes(size_t n); +#else + virtual void DiscardBytes(size_t n) { + CRYPTOPP_UNUSED(n); + throw NotImplemented("RDSEED: rdseed is not available on this platform"); + } +#endif - //! update RNG state with additional unpredictable values. The operation is a nop for this generator. + //! Update RNG state with additional unpredictable values //! \param input unused //! \param length unused + //! \details The operation is a nop for this generator. virtual void IncorporateEntropy(const byte *input, size_t length) { // Override to avoid the base class' throw.