You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
8.4 KiB
327 lines
8.4 KiB
// rdrand.cpp - written and placed in public domain by Jeffrey Walton and Uri Blumenthal. |
|
|
|
#include "pch.h" |
|
#include "config.h" |
|
#include "cryptlib.h" |
|
#include "secblock.h" |
|
#include "rdrand.h" |
|
#include "cpu.h" |
|
|
|
// This file (and friends) provides both RDRAND and RDSEED. They were added |
|
// at Crypto++ 5.6.3. At compile time, it uses CRYPTOPP_BOOL_{X86|X32|X64} |
|
// to select an implementation or throws "NotImplemented". Users of the |
|
// classes should call HasRDRAND() or HasRDSEED() to determine if a |
|
// generator is available at runtime. |
|
// The original classes accepted a retry count. Retries were superflous for |
|
// RDRAND, and RDSEED encountered a failure about 1 in 256 bytes depending |
|
// on the processor. Retries were removed at Crypto++ 6.0 because |
|
// GenerateBlock unconditionally retries and always fulfills the request. |
|
// Intel recommends using a retry count in case RDRAND or RDSEED circuit |
|
// is bad. This implemenation does not follow the advice and requires |
|
// good silicon. If the circuit or processor is bad then the user has |
|
// bigger problems than generating random numbers. |
|
|
|
///////////////////////////////////////////////////////////////////// |
|
///////////////////////////////////////////////////////////////////// |
|
|
|
#if CRYPTOPP_MSC_VERSION |
|
# pragma warning(disable: 4702) |
|
#endif |
|
|
|
#if defined(CRYPTOPP_RDRAND_AVAILABLE) |
|
# if defined(CRYPTOPP_MSC_VERSION) |
|
# define MASM_RDRAND_ASM_AVAILABLE 1 |
|
# endif |
|
# if (__SUNPRO_CC >= 0x5100) || (CRYPTOPP_APPLE_CLANG_VERSION >= 30000) || \ |
|
(CRYPTOPP_LLVM_CLANG_VERSION >= 20800) || (CRYPTOPP_GCC_VERSION >= 30200) |
|
# define GCC_RDRAND_ASM_AVAILABLE 1 |
|
# endif |
|
#endif // CRYPTOPP_RDRAND_AVAILABLE |
|
|
|
#if defined(CRYPTOPP_RDSEED_AVAILABLE) |
|
# if defined(CRYPTOPP_MSC_VERSION) |
|
# define MASM_RDSEED_ASM_AVAILABLE 1 |
|
# endif |
|
# if (__SUNPRO_CC >= 0x5100) || (CRYPTOPP_APPLE_CLANG_VERSION >= 30000) || \ |
|
(CRYPTOPP_LLVM_CLANG_VERSION >= 20800) || (CRYPTOPP_GCC_VERSION >= 30200) |
|
# define GCC_RDSEED_ASM_AVAILABLE 1 |
|
# endif |
|
#endif // CRYPTOPP_RDSEED_AVAILABLE |
|
|
|
typedef unsigned char byte; |
|
|
|
#if MASM_RDRAND_ASM_AVAILABLE |
|
extern "C" void CRYPTOPP_FASTCALL MASM_RDRAND_GenerateBlock(byte*, size_t); |
|
#endif |
|
|
|
#if MASM_RDSEED_ASM_AVAILABLE |
|
extern "C" void CRYPTOPP_FASTCALL MASM_RDSEED_GenerateBlock(byte*, size_t); |
|
#endif |
|
|
|
///////////////////////////////////////////////////////////////////// |
|
///////////////////////////////////////////////////////////////////// |
|
|
|
NAMESPACE_BEGIN(CryptoPP) |
|
|
|
#if defined(CRYPTOPP_RDRAND_AVAILABLE) |
|
|
|
// Fills 4 bytes |
|
inline void RDRAND32(void* output) |
|
{ |
|
CRYPTOPP_UNUSED(output); // MSC warning |
|
# if defined(GCC_RDRAND_ASM_AVAILABLE) |
|
__asm__ __volatile__ |
|
( |
|
"1:\n" |
|
".byte 0x0f, 0xc7, 0xf0;\n" |
|
"jnc 1b;\n" |
|
: "=a" (*reinterpret_cast<word32*>(output)) |
|
: : "cc" |
|
); |
|
# endif |
|
} |
|
|
|
# if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32) |
|
// Fills 8 bytes |
|
inline void RDRAND64(void* output) |
|
{ |
|
CRYPTOPP_UNUSED(output); // MSC warning |
|
# if defined(GCC_RDRAND_ASM_AVAILABLE) |
|
__asm__ __volatile__ |
|
( |
|
"1:\n" |
|
".byte 0x48, 0x0f, 0xc7, 0xf0;\n" |
|
"jnc 1b;\n" |
|
: "=a" (*reinterpret_cast<word64*>(output)) |
|
: : "cc" |
|
); |
|
# endif |
|
} |
|
# endif // RDRAND64 |
|
|
|
RDRAND::RDRAND() |
|
{ |
|
if (!HasRDRAND()) |
|
throw RDRAND_Err("HasRDRAND"); |
|
} |
|
|
|
void RDRAND::GenerateBlock(byte *output, size_t size) |
|
{ |
|
CRYPTOPP_ASSERT((output && size) || !(output || size)); |
|
if (size == 0) return; |
|
|
|
# if defined(MASM_RDRAND_ASM_AVAILABLE) |
|
|
|
MASM_RDRAND_GenerateBlock(output, size); |
|
|
|
# elif defined(GCC_RDRAND_ASM_AVAILABLE) |
|
|
|
# if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32) |
|
size_t i = 0; |
|
for (i = 0; i < size/8; i++) |
|
RDRAND64(output+i*8); |
|
|
|
output += i*8; |
|
size -= i*8; |
|
|
|
if (size) |
|
{ |
|
word64 val; |
|
RDRAND64(&val); |
|
std::memcpy(output, &val, size); |
|
} |
|
# else |
|
size_t i = 0; |
|
for (i = 0; i < size/4; i++) |
|
RDRAND32(output+i*4); |
|
|
|
output += i*4; |
|
size -= i*4; |
|
|
|
if (size) |
|
{ |
|
word32 val; |
|
RDRAND32(&val); |
|
std::memcpy(output, &val, size); |
|
} |
|
# endif |
|
# else |
|
// No suitable compiler found |
|
CRYPTOPP_UNUSED(output); |
|
throw NotImplemented("RDRAND: failed to find a suitable implementation"); |
|
# endif |
|
} |
|
|
|
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. |
|
FixedSizeSecBlock<word64, 16> discard; |
|
n = RoundUpToMultipleOf(n, sizeof(word64)); |
|
|
|
size_t count = STDMIN(n, discard.SizeInBytes()); |
|
while (count) |
|
{ |
|
GenerateBlock(discard.BytePtr(), count); |
|
n -= count; |
|
count = STDMIN(n, discard.SizeInBytes()); |
|
} |
|
} |
|
|
|
#endif // CRYPTOPP_RDRAND_AVAILABLE |
|
|
|
///////////////////////////////////////////////////////////////////// |
|
///////////////////////////////////////////////////////////////////// |
|
|
|
#if defined(CRYPTOPP_RDSEED_AVAILABLE) |
|
|
|
// Fills 4 bytes |
|
inline void RDSEED32(void* output) |
|
{ |
|
CRYPTOPP_UNUSED(output); // MSC warning |
|
# if defined(GCC_RDSEED_ASM_AVAILABLE) |
|
__asm__ __volatile__ |
|
( |
|
"1:\n" |
|
".byte 0x0f, 0xc7, 0xf8;\n" |
|
"jnc 1b;\n" |
|
: "=a" (*reinterpret_cast<word32*>(output)) |
|
: : "cc" |
|
); |
|
# endif |
|
} |
|
|
|
# if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32) |
|
// Fills 8 bytes |
|
inline void RDSEED64(void* output) |
|
{ |
|
CRYPTOPP_UNUSED(output); // MSC warning |
|
# if defined(GCC_RDSEED_ASM_AVAILABLE) |
|
__asm__ __volatile__ |
|
( |
|
"1:\n" |
|
".byte 0x48, 0x0f, 0xc7, 0xf8;\n" |
|
"jnc 1b;\n" |
|
: "=a" (*reinterpret_cast<word64*>(output)) |
|
: : "cc" |
|
); |
|
# endif |
|
} |
|
# endif // RDSEED64 |
|
|
|
RDSEED::RDSEED() |
|
{ |
|
if (!HasRDSEED()) |
|
throw RDSEED_Err("HasRDSEED"); |
|
} |
|
|
|
void RDSEED::GenerateBlock(byte *output, size_t size) |
|
{ |
|
CRYPTOPP_ASSERT((output && size) || !(output || size)); |
|
if (size == 0) return; |
|
|
|
# if defined(MASM_RDSEED_ASM_AVAILABLE) |
|
|
|
MASM_RDSEED_GenerateBlock(output, size); |
|
|
|
# elif defined(GCC_RDSEED_ASM_AVAILABLE) |
|
# if (CRYPTOPP_BOOL_X64 || CRYPTOPP_BOOL_X32) |
|
size_t i = 0; |
|
for (i = 0; i < size/8; i++) |
|
RDSEED64(output+i*8); |
|
|
|
output += i*8; |
|
size -= i*8; |
|
|
|
if (size) |
|
{ |
|
word64 val; |
|
RDSEED64(&val); |
|
std::memcpy(output, &val, size); |
|
} |
|
# else |
|
size_t i = 0; |
|
for (i = 0; i < size/4; i++) |
|
RDSEED32(output+i*4); |
|
|
|
output += i*4; |
|
size -= i*4; |
|
|
|
if (size) |
|
{ |
|
word32 val; |
|
RDSEED32(&val); |
|
std::memcpy(output, &val, size); |
|
} |
|
# endif |
|
# else |
|
// No suitable compiler found |
|
CRYPTOPP_UNUSED(output); |
|
throw NotImplemented("RDSEED: failed to find a suitable implementation"); |
|
# endif // RDSEED64 |
|
} |
|
|
|
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. |
|
FixedSizeSecBlock<word64, 16> discard; |
|
n = RoundUpToMultipleOf(n, sizeof(word64)); |
|
|
|
size_t count = STDMIN(n, discard.SizeInBytes()); |
|
while (count) |
|
{ |
|
GenerateBlock(discard.BytePtr(), count); |
|
n -= count; |
|
count = STDMIN(n, discard.SizeInBytes()); |
|
} |
|
} |
|
|
|
#endif // CRYPTOPP_RDSEED_AVAILABLE |
|
|
|
///////////////////////////////////////////////////////////////////// |
|
///////////////////////////////////////////////////////////////////// |
|
|
|
#if !defined(CRYPTOPP_RDRAND_AVAILABLE) |
|
|
|
RDRAND::RDRAND() |
|
{ |
|
throw RDRAND_Err("HasRDRAND"); |
|
} |
|
|
|
void RDRAND::GenerateBlock(byte *output, size_t size) |
|
{ |
|
// Constructor will throw, should not get here |
|
CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size); |
|
} |
|
|
|
void RDRAND::DiscardBytes(size_t n) |
|
{ |
|
// Constructor will throw, should not get here |
|
CRYPTOPP_UNUSED(n); |
|
} |
|
|
|
#endif // CRYPTOPP_RDRAND_AVAILABLE |
|
|
|
#if !defined(CRYPTOPP_RDSEED_AVAILABLE) |
|
|
|
RDSEED::RDSEED() |
|
{ |
|
throw RDSEED_Err("HasRDSEED"); |
|
} |
|
|
|
void RDSEED::GenerateBlock(byte *output, size_t size) |
|
{ |
|
// Constructor will throw, should not get here |
|
CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size); |
|
} |
|
|
|
void RDSEED::DiscardBytes(size_t n) |
|
{ |
|
// Constructor will throw, should not get here |
|
CRYPTOPP_UNUSED(n); |
|
} |
|
|
|
#endif // CRYPTOPP_RDSEED_AVAILABLE |
|
|
|
NAMESPACE_END
|
|
|