diff --git a/Filelist.txt b/Filelist.txt index c9a1a8c5..8a9b800a 100644 --- a/Filelist.txt +++ b/Filelist.txt @@ -191,6 +191,8 @@ oids.h osrng.cpp osrng.h ossig.h +padlkrng.cpp +padlkrng.h panama.cpp panama.h pch.cpp diff --git a/bench1.cpp b/bench1.cpp index 4b6d4437..4fb12d2c 100644 --- a/bench1.cpp +++ b/bench1.cpp @@ -15,6 +15,8 @@ #include "smartptr.h" #include "cpu.h" #include "drbg.h" +#include "rdrand.h" +#include "padlkrng.h" #if CRYPTOPP_MSC_VERSION # pragma warning(disable: 4355) @@ -439,6 +441,10 @@ void Benchmark1(double t, double hertz) BenchMarkByNameKeyLess("AutoSeededX917RNG(AES)"); #endif BenchMarkByNameKeyLess("MT19937"); +#if (CRYPTOPP_BOOL_X86) + if (HasPadlockRNG()) + BenchMarkByNameKeyLess("PadlockRNG"); +#endif #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) if (HasRDRAND()) BenchMarkByNameKeyLess("RDRAND"); diff --git a/cryptlib.vcxproj b/cryptlib.vcxproj index aa4ff2d3..c8a24518 100644 --- a/cryptlib.vcxproj +++ b/cryptlib.vcxproj @@ -252,6 +252,7 @@ + Create @@ -434,6 +435,7 @@ + diff --git a/cryptlib.vcxproj.filters b/cryptlib.vcxproj.filters index a20ea011..eb4bb3b6 100644 --- a/cryptlib.vcxproj.filters +++ b/cryptlib.vcxproj.filters @@ -257,6 +257,9 @@ Source Files + + Source Files + Source Files @@ -699,6 +702,9 @@ Header Files + + Header Files + Header Files diff --git a/padlkrng.cpp b/padlkrng.cpp new file mode 100644 index 00000000..ebe7d4f2 --- /dev/null +++ b/padlkrng.cpp @@ -0,0 +1,86 @@ +// via-rng.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 "padlkrng.h" +#include "cpu.h" + +NAMESPACE_BEGIN(CryptoPP) + +PadlockRNG::PadlockRNG() +{ +#if CRYPTOPP_BOOL_X86 + if (!HasPadlockRNG()) + throw PadlockRNG_Err("HasPadlockRNG"); +#else + throw PadlockRNG_Err("HasPadlockRNG"); +#endif +} + +void PadlockRNG::GenerateBlock(byte *output, size_t size) +{ + CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size); +#if CRYPTOPP_BOOL_X86 + while (size) + { +# if defined(__GNUC__) + + word32 result; + __asm__ __volatile__ + ( + "movl %1, %%edi ;\n" + "movl $1, %%edx ;\n" + ".byte 0x0f, 0xa7, 0xc0 ;\n" + "andl $31, %%eax ;\n" + "movl %%eax, %0 ;\n" + + : "=g" (result) : "g" (m_buffer.begin()) : "eax", "edx", "edi", "cc" + ); + + const size_t rem = STDMIN(result, STDMIN(size, m_buffer.SizeInBytes())); + std::memcpy(output, m_buffer, rem); + size -= rem; output += rem; + +# elif defined(_MSC_VER) + + word32 result; + byte* buffer = reinterpret_cast(m_buffer.begin()); + + __asm { + mov edi, buffer + mov edx, 0x01 + _emit 0x0f + _emit 0xa7 + _emit 0xc0 + and eax, 31 + mov result, eax + } + + const size_t rem = STDMIN(result, STDMIN(size, m_buffer.SizeInBytes())); + std::memcpy(output, m_buffer, rem); + size -= rem; output += rem; + +# else + throw NotImplemented("PadlockRNG::GenerateBlock"); +# endif + } +#endif // CRYPTOPP_BOOL_X86 +} + +void PadlockRNG::DiscardBytes(size_t n) +{ + FixedSizeSecBlock discard; + n = RoundUpToMultipleOf(n, sizeof(word32)); + + size_t count = STDMIN(n, discard.SizeInBytes()); + while (count) + { + GenerateBlock(discard.BytePtr(), count); + n -= count; + count = STDMIN(n, discard.SizeInBytes()); + } +} + +NAMESPACE_END diff --git a/padlkrng.h b/padlkrng.h new file mode 100644 index 00000000..9a70fd87 --- /dev/null +++ b/padlkrng.h @@ -0,0 +1,70 @@ +// via-rng.h - written and placed in public domain by Jeffrey Walton + +//! \file PadlockRNG.h +//! \brief Class for VIA Padlock RNG +//! \since Crypto++ 6.0 + +#ifndef CRYPTOPP_PADLOCK_RNG_H +#define CRYPTOPP_PADLOCK_RNG_H + +#include "cryptlib.h" +#include "secblock.h" + +NAMESPACE_BEGIN(CryptoPP) + +//! \brief Exception thrown when a PadlockRNG generator encounters +//! a generator related error. +//! \since Crypto++ 6.0 +class PadlockRNG_Err : public Exception +{ +public: + PadlockRNG_Err(const std::string &operation) + : Exception(OTHER_ERROR, "PadlockRNG: " + operation + " operation failed") {} +}; + +//! \brief Hardware generated random numbers using PadlockRNG instruction +//! \sa MaurerRandomnessTest() for random bit generators +//! \since Crypto++ 6.0 +class PadlockRNG : public RandomNumberGenerator +{ +public: + CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "PadlockRNG"; } + + virtual ~PadlockRNG() {} + + //! \brief Construct a PadlockRNG generator + //! \details According to DJ of Intel, the Intel PadlockRNG circuit does not underflow. + //! If it did hypothetically underflow, then it would return 0 for the random value. + //! AMD's PadlockRNG implementation appears to provide the same behavior. + //! \throws PadlockRNG_Err if the random number generator is not available + PadlockRNG(); + + //! \brief Generate random array of bytes + //! \param output the byte buffer + //! \param size the length of the buffer, in bytes + virtual void GenerateBlock(byte *output, size_t size); + + //! \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. + virtual void DiscardBytes(size_t n); + + //! \brief 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. + CRYPTOPP_UNUSED(input); CRYPTOPP_UNUSED(length); + } + +private: + FixedSizeAlignedSecBlock m_buffer; +}; + +NAMESPACE_END + +#endif // CRYPTOPP_PADLOCK_RNG_H diff --git a/regtest1.cpp b/regtest1.cpp index 33ff69da..a8fdc9ef 100644 --- a/regtest1.cpp +++ b/regtest1.cpp @@ -25,6 +25,7 @@ #include "drbg.h" #include "mersenne.h" #include "rdrand.h" +#include "padlkrng.h" #include "modes.h" #include "aes.h" @@ -109,6 +110,10 @@ void RegisterFactories1() RegisterDefaultFactoryFor >(); #endif RegisterDefaultFactoryFor(); +#if (CRYPTOPP_BOOL_X86) + if (HasPadlockRNG()) + RegisterDefaultFactoryFor(); +#endif #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) if (HasRDRAND()) RegisterDefaultFactoryFor(); diff --git a/validat1.cpp b/validat1.cpp index 10e6932d..650e5f20 100644 --- a/validat1.cpp +++ b/validat1.cpp @@ -45,6 +45,7 @@ #include "osrng.h" #include "drbg.h" #include "rdrand.h" +#include "padlkrng.h" #include "mersenne.h" #include "randpool.h" #include "zdeflate.h" @@ -84,6 +85,7 @@ bool ValidateAll(bool thorough) pass=TestMersenne() && pass; #endif #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) + pass=TestPadlockRNG() && pass; pass=TestRDRAND() && pass; pass=TestRDSEED() && pass; #endif @@ -1043,6 +1045,108 @@ bool TestMersenne() #endif #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) + bool TestPadlockRNG() +{ + std::cout << "\nTesting Padlock RNG generator...\n\n"; + + bool pass = true, fail = false; + member_ptr rng; + + try {rng.reset(new PadlockRNG);} + catch (const PadlockRNG_Err &) {} + if (rng.get()) + { + PadlockRNG& padlock = dynamic_cast(*rng.get()); + static const unsigned int SIZE = 10000; + + MeterFilter meter(new Redirector(TheBitBucket())); + Deflator deflator(new Redirector(meter)); + MaurerRandomnessTest maurer; + + ChannelSwitch chsw; + chsw.AddDefaultRoute(deflator); + chsw.AddDefaultRoute(maurer); + + RandomNumberSource rns(padlock, SIZE, true, new Redirector(chsw)); + deflator.Flush(true); + + CRYPTOPP_ASSERT(0 == maurer.BytesNeeded()); + const double mv = maurer.GetTestValue(); + if (mv < 0.98f) + fail = true; + + // Coverity finding, also see http://stackoverflow.com/a/34509163/608639. + StreamState ss(std::cout); + std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(6); + + pass &= !fail; + if (fail) + std::cout << "FAILED:"; + else + std::cout << "passed:"; + std::cout << " Maurer Randomness Test returned value " << mv << "\n"; + + fail = false; + if (meter.GetTotalBytes() < SIZE) + fail = true; + + pass &= !fail; + if (fail) + std::cout << "FAILED:"; + else + std::cout << "passed:"; + std::cout << " " << SIZE << " generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE\n"; + + try + { + fail = false; + padlock.DiscardBytes(SIZE); + } + catch (const Exception&) + { + fail = true; + } + + pass &= !fail; + if (fail) + std::cout << "FAILED:"; + else + std::cout << "passed:"; + std::cout << " discarded " << SIZE << " bytes\n"; + + try + { + // Miscellaneous for code coverage + (void)padlock.AlgorithmName(); + (void)padlock.CanIncorporateEntropy(); + padlock.IncorporateEntropy(NULLPTR, 0); + + word32 result = padlock.GenerateWord32(); + result = padlock.GenerateWord32((result & 0xff), 0xffffffff - (result & 0xff)); + padlock.GenerateBlock(reinterpret_cast(&result), 4); + padlock.GenerateBlock(reinterpret_cast(&result), 3); + padlock.GenerateBlock(reinterpret_cast(&result), 2); + padlock.GenerateBlock(reinterpret_cast(&result), 1); + fail = false; + } + catch (const Exception&) + { + fail = true; + } + + pass &= !fail; + if (fail) + std::cout << "FAILED:"; + else + std::cout << "passed:"; + std::cout << " GenerateWord32 and Crop\n"; + } + else + std::cout << "Padlock RNG generator not available, skipping test.\n"; + + return pass; +} + bool TestRDRAND() { std::cout << "\nTesting RDRAND generator...\n\n"; diff --git a/validate.h b/validate.h index a7b08b48..398c3b59 100644 --- a/validate.h +++ b/validate.h @@ -28,6 +28,7 @@ bool TestAutoSeededX917(); #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) bool TestRDRAND(); bool TestRDSEED(); +bool TestPadlockRNG(); #endif bool ValidateBaseCode(); bool ValidateCRC32();