diff options
| author | Jeffrey Walton <noloader@gmail.com> | 2015-11-05 01:59:46 -0500 |
|---|---|---|
| committer | Jeffrey Walton <noloader@gmail.com> | 2015-11-05 01:59:46 -0500 |
| commit | 48809d4e85c125814425c621d8d0d89f95405924 (patch) | |
| tree | 1010fd16c4b1199f3d27dd726dda241a2bd29f83 /rdrand.cpp | |
| parent | 025337a94aceb75d188149db70c2094673772816 (diff) | |
| download | cryptopp-git-48809d4e85c125814425c621d8d0d89f95405924.tar.gz | |
CRYPTOPP 5.6.3 RC6 checkin
Diffstat (limited to 'rdrand.cpp')
| -rw-r--r-- | rdrand.cpp | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/rdrand.cpp b/rdrand.cpp new file mode 100644 index 00000000..66cb6561 --- /dev/null +++ b/rdrand.cpp @@ -0,0 +1,835 @@ +// rdrand.cpp - written and placed in public domain by Jeffrey Walton and Uri Blumenthal.
+// Copyright assigned to Crypto++ project.
+
+#include "pch.h"
+#include "config.h"
+#include "cryptlib.h"
+#include "secblock.h"
+#include "rdrand.h"
+#include "cpu.h"
+
+#if CRYPTOPP_MSC_VERSION
+# pragma warning(disable: 4100)
+#endif
+
+// 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.
+
+// 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)
+// 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
+#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
+# 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
+#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
+#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
+#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
+# 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
+# ifdef _M_X64
+extern "C" int CRYPTOPP_FASTCALL MSC_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);
+// # pragma comment(lib, "rdrand-x86.lib")
+# endif
+#endif
+
+#if MSC_RDSEED_ASM_AVAILABLE
+# ifdef _M_X64
+extern "C" int CRYPTOPP_FASTCALL MSC_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);
+// # pragma comment(lib, "rdrand-x86.lib")
+# endif
+#endif
+
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+
+#if MSC_RDRAND_INTRINSIC_AVAILABLE || MSC_RDSEED_INTRINSIC_AVAILABLE
+# include <immintrin.h>
+#elif GCC_RDRAND_INTRINSIC_AVAILABLE || GCC_RDSEED_INTRINSIC_AVAILABLE
+# include <emmintrin.h>
+#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 <errno.h>
+#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])
+{
+ // 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
+ word64 val;
+#else
+ word32 val;
+#endif
+
+ while (size >= sizeof(val))
+ {
+#if CRYPTOPP_BOOL_X64
+ if (_rdrand64_step((word64*)output))
+#else
+ if (_rdrand32_step((word32*)output))
+#endif
+ {
+ output += sizeof(val);
+ size -= sizeof(val);
+ }
+ else
+ {
+ if (!safety--)
+ return 0;
+ }
+ }
+
+ if (size)
+ {
+#if CRYPTOPP_BOOL_X64
+ if (_rdrand64_step(&val))
+#else
+ if (_rdrand32_step(&val))
+#endif
+ {
+ memcpy(output, &val, size);
+ size = 0;
+ }
+ else
+ {
+ if (!safety--)
+ return 0;
+ }
+ }
+
+#if CRYPTOPP_BOOL_X64
+ *((volatile word64*)&val) = 0;
+#else
+ *((volatile word32*)&val) = 0;
+#endif
+
+ 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
+
+#if GCC_RDRAND_ASM_AVAILABLE
+static int GCC_RRA_GenerateBlock(byte *output, size_t size, unsigned int safety)
+{
+#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
+ word64 val;
+#else
+ word32 val;
+#endif
+ char rc;
+ while (size)
+ {
+ __asm__ volatile(
+ "rdrand %0; "
+ "setc %1; "
+ : "=r" (val), "=qm" (rc)
+ :
+ : "cc"
+ );
+
+ if (rc)
+ {
+ if (size >= sizeof(val))
+ {
+#if defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
+ *((word64*)output) = val;
+#elif defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X86)
+ *((word32*)output) = val;
+#else
+ memcpy(output, &val, sizeof(val));
+#endif
+ output += sizeof(val);
+ size -= sizeof(val);
+ }
+ else
+ {
+ memcpy(output, &val, size);
+ size = 0;
+ }
+ }
+ else
+ {
+ if (!safety--)
+ break;
+ }
+ }
+
+#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
+ *((volatile word64*)&val) = 0;
+#else
+ *((volatile word32*)&val) = 0;
+#endif
+
+ return int(size == 0);
+}
+
+#endif // GCC_RDRAND_ASM_AVAILABLE
+
+#endif // CRYPTOPP_CPUID_AVAILABLE (CRYPTOPP_BOOL_{X86|X32|X64})
+
+//! generate random array of bytes
+void RDRAND::GenerateBlock(byte *output, size_t size)
+{
+ assert((output == NULL && size == 0) || (output != NULL && size != 0));
+ assert(Available());
+
+ // We could (should?) test Ready, but Available conveys more useful information.
+ if(!Available())
+ 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"); }
+#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})
+}
+
+//! 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());
+#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
+ FixedSizeSecBlock<word64, 16> discard;
+ n = RoundUpToMultipleOf(n, sizeof(word64));
+#else
+ FixedSizeSecBlock<word32, 16> discard;
+ n = RoundUpToMultipleOf(n, sizeof(word32));
+#endif
+
+ size_t count = STDMIN(n, discard.SizeInBytes());
+ while (count)
+ {
+ GenerateBlock(discard.BytePtr(), count);
+ n -= count;
+ count = STDMIN(n, discard.SizeInBytes());
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+
+static bool RDSEED_Runtime_Helper()
+{
+#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
+ word64 val;
+#else
+ word32 val;
+#endif
+
+ while (size >= sizeof(val))
+ {
+#if CRYPTOPP_BOOL_X64
+ if (_rdseed64_step((word64*)output))
+#else
+ if (_rdseed32_step((word32*)output))
+#endif
+ {
+ output += sizeof(val);
+ size -= sizeof(val);
+ }
+ else
+ {
+ if (!safety--)
+ return 0;
+ }
+ }
+
+ if (size)
+ {
+#if CRYPTOPP_BOOL_X64
+ if (_rdseed64_step(&val))
+#else
+ if (_rdseed32_step(&val))
+#endif
+ {
+ memcpy(output, &val, size);
+ size = 0;
+ }
+ else
+ {
+ if (!safety--)
+ return 0;
+ }
+ }
+
+#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
+ *((volatile word32*)&val) = 0;
+#endif
+
+ return int(size == 0);
+}
+
+#endif // GCC_RDSEED_INTRINSIC_AVAILABLE
+
+#if GCC_RDSEED_ASM_AVAILABLE
+static int GCC_RSA_GenerateBlock(byte *output, size_t size, unsigned int safety)
+{
+#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
+ word64 val;
+#else
+ word32 val;
+#endif
+ char rc;
+ while (size)
+ {
+ __asm__ volatile(
+ "rdseed %0; "
+ "setc %1; "
+ : "=r" (val), "=qm" (rc)
+ :
+ : "cc"
+ );
+
+ if (rc)
+ {
+ if (size >= sizeof(val))
+ {
+#if defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32)
+ *((word64*)output) = val;
+#elif defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && (CRYPTOPP_BOOL_X86)
+ *((word32*)output) = val;
+#else
+ memcpy(output, &val, sizeof(val));
+#endif
+ output += sizeof(val);
+ size -= sizeof(val);
+ }
+ else
+ {
+ memcpy(output, &val, size);
+ size = 0;
+ }
+ }
+ else
+ {
+ if (!safety--)
+ break;
+ }
+ }
+
+#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
+ *((volatile word64*)&val) = 0;
+#else
+ *((volatile word32*)&val) = 0;
+#endif
+
+ return int(size == 0);
+}
+
+#endif // GCC_RDSEED_ASM_AVAILABLE
+
+#endif // CRYPTOPP_CPUID_AVAILABLE (CRYPTOPP_BOOL_{X86|X32|X64})
+
+//! generate random array of bytes
+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());
+
+ // We could (should?) test Ready, but Available conveys more useful information.
+ if(!Available())
+ 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"); }
+#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());
+#if CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32
+ FixedSizeSecBlock<word64, 16> discard;
+ n = RoundUpToMultipleOf(n, sizeof(word64));
+#else
+ FixedSizeSecBlock<word32, 16> discard;
+ n = RoundUpToMultipleOf(n, sizeof(word32));
+#endif
+
+ size_t count = STDMIN(n, discard.SizeInBytes());
+ while (count)
+ {
+ GenerateBlock(discard.BytePtr(), count);
+ n -= count;
+ count = STDMIN(n, discard.SizeInBytes());
+ }
+}
+
+NAMESPACE_END
|
