diff --git a/config.h b/config.h index c88c30a1..672d0af1 100644 --- a/config.h +++ b/config.h @@ -461,7 +461,7 @@ NAMESPACE_END #if !defined(CRYPTOPP_DISABLE_ASM) && ((defined(_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))) // C++Builder 2010 does not allow "call label" where label is defined within inline assembly - #define CRYPTOPP_X86_ASM_AVAILABLE + #define CRYPTOPP_X86_ASM_AVAILABLE 1 #if !defined(CRYPTOPP_DISABLE_SSE2) && (defined(_MSC_VER) || CRYPTOPP_GCC_VERSION >= 30300 || defined(__SSE2__)) #define CRYPTOPP_BOOL_SSE2_ASM_AVAILABLE 1 diff --git a/padlkrng.cpp b/padlkrng.cpp index ebe7d4f2..c6a29035 100644 --- a/padlkrng.cpp +++ b/padlkrng.cpp @@ -7,80 +7,81 @@ #include "padlkrng.h" #include "cpu.h" +// The Padlock Security Engine RNG has a few items to be aware of. You can +// find copies of the Programmer's manual, Cryptography Research Inc audit +// report, and other goodies at http://www.cryptopp.com/wiki/VIA_Padlock. + NAMESPACE_BEGIN(CryptoPP) -PadlockRNG::PadlockRNG() +PadlockRNG::PadlockRNG(word32 divisor) + : m_divisor(DivisorHelper(divisor)) { -#if CRYPTOPP_BOOL_X86 - if (!HasPadlockRNG()) - throw PadlockRNG_Err("HasPadlockRNG"); +#if CRYPTOPP_X86_ASM_AVAILABLE + if (!HasPadlockRNG()) + throw PadlockRNG_Err("PadlockRNG", "PadlockRNG generator not available"); #else - throw PadlockRNG_Err("HasPadlockRNG"); + throw PadlockRNG_Err("PadlockRNG", "PadlockRNG generator not available"); #endif } void PadlockRNG::GenerateBlock(byte *output, size_t size) { CRYPTOPP_UNUSED(output); CRYPTOPP_UNUSED(size); -#if CRYPTOPP_BOOL_X86 +#if defined(CRYPTOPP_X86_ASM_AVAILABLE) && defined(__GNUC__) while (size) { -# if defined(__GNUC__) - - word32 result; __asm__ __volatile__ ( "movl %1, %%edi ;\n" - "movl $1, %%edx ;\n" + "movl $2, %%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" + : "=g" (m_msr) : "g" (m_buffer.data()), "g" (m_divisor) + : "eax", "edx", "edi", "cc" ); - const size_t rem = STDMIN(result, STDMIN(size, m_buffer.SizeInBytes())); + const size_t ret = m_msr & 0x1f; + const size_t rem = STDMIN(ret, STDMIN(size, 16U)); std::memcpy(output, m_buffer, rem); size -= rem; output += rem; - -# elif defined(_MSC_VER) - - word32 result; - byte* buffer = reinterpret_cast(m_buffer.begin()); - + } +#elif defined(CRYPTOPP_X86_ASM_AVAILABLE) && defined(_MSC_VER) + while (size) + { + word32 result, divisor = m_divisor; + byte *buffer = reinterpret_cast(m_buffer.data()); __asm { mov edi, buffer - mov edx, 0x01 + mov edx, divisor _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); + const size_t ret = (m_msr = result) & 0x1f; + const size_t rem = STDMIN(ret, STDMIN(size, 16U)); + std::memcpy(output, buffer, rem); size -= rem; output += rem; - -# else - throw NotImplemented("PadlockRNG::GenerateBlock"); -# endif } -#endif // CRYPTOPP_BOOL_X86 +#else + throw PadlockRNG_Err("GenerateBlock", "PadlockRNG generator not available"); +#endif // CRYPTOPP_X86_ASM_AVAILABLE } void PadlockRNG::DiscardBytes(size_t n) { - FixedSizeSecBlock discard; - n = RoundUpToMultipleOf(n, sizeof(word32)); + 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()); - } + 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 index 9a70fd87..f62dc5dd 100644 --- a/padlkrng.h +++ b/padlkrng.h @@ -1,8 +1,10 @@ // via-rng.h - written and placed in public domain by Jeffrey Walton -//! \file PadlockRNG.h -//! \brief Class for VIA Padlock RNG +//! \file padlkrng.h +//! \brief Classes for VIA Padlock RNG //! \since Crypto++ 6.0 +//! \sa VIA +//! Padlock on the Crypto++ wiki #ifndef CRYPTOPP_PADLOCK_RNG_H #define CRYPTOPP_PADLOCK_RNG_H @@ -13,56 +15,114 @@ NAMESPACE_BEGIN(CryptoPP) //! \brief Exception thrown when a PadlockRNG generator encounters -//! a generator related error. +//! 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") {} + PadlockRNG_Err(const std::string &operation) + : Exception(OTHER_ERROR, "PadlockRNG: " + operation + " operation failed") {} + PadlockRNG_Err(const std::string &component, const std::string &message) + : Exception(OTHER_ERROR, component + ": " + message) {} }; //! \brief Hardware generated random numbers using PadlockRNG instruction +//! \details The PadlockRNG uses an 8 byte FIFO buffer for random numbers. The +//! generator can be configured to discard bits from the buffer to resist analysis. +//! The divisor controls the number of bytes discarded. The formula for +//! the discard amount is 2**divisor - 1. When divisor=0 no bits +//! are discarded and the entire 8 byte buffer is read. If divisor=3 then +//! 7 bytes are discarded and 1 byte is read. TheVIA SDK samples use divisor=1. +//! \details Cryptography Research, Inc (CRI) audited the Padlock Security Engine +//! in 2003. CRI provided recommendations to operate the generator for secure and +//! non-secure applications. Additionally, the Programmers Guide and SDK provided a +//! different configuration in the sample code. +//! \details You can operate the generator according to CRI recommendations by setting +//! divisor, reading one word (or partial word) at a time, and then inspecting +//! the MSR after each read. +//! \details The audit report with recommendations is available on the Crypto++ wiki +//! at VIA Padlock. //! \sa MaurerRandomnessTest() for random bit generators //! \since Crypto++ 6.0 class PadlockRNG : public RandomNumberGenerator { public: - CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "PadlockRNG"; } + CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "PadlockRNG"; } - virtual ~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 Construct a PadlockRNG generator + //! \param divisor the XSTORE divisor + //! \details The PadlockRNG uses an 8 byte FIFO buffer for random numbers. The + //! generator can be configured to discard bits from the buffer to resist analysis. + //! The divisor controls the number of bytes discarded. The formula for + //! the discard amount is 2**divisor - 1. When divisor=0 no bits + //! are discarded and the entire 8 byte buffer is read. If divisor=3 then + //! 7 bytes are discarded and 1 byte is read. VIA SDK samples use divisor=1. + //! \details Cryptography Research, Inc (CRI) audited the Padlock Security Engine + //! in 2003. CRI provided recommendations to operate the generator for secure and + //! non-secure applications. Additionally, the Programmers SDK provided a different + //! configuration in the sample code. + //! \details The audit report with recommendations is available on the Crypto++ wiki + //! at VIA Padlock. + //! \sa SetDivisor, GetDivisor + PadlockRNG(word32 divisor=1); - //! \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 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 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); - } + //! \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); + } + + //! \brief Set the XSTORE divisor + //! \param divisor the XSTORE divisor + //! \returns the old XSTORE divisor + word32 SetDivisor(word32 divisor) + { + word32 old = m_divisor; + m_divisor = DivisorHelper(divisor); + return old; + } + + //! \brief Get the XSTORE divisor + //! \returns the current XSTORE divisor + word32 GetDivisor() const + { + return m_divisor; + } + + //! \brief Get the MSR for the last operation + //! \returns the MSR for the last read operation + word32 GetMSR() const + { + return m_msr; + } + +protected: + inline word32 DivisorHelper(word32 divisor) + { + return divisor > 3 ? 3 : divisor; + } private: - FixedSizeAlignedSecBlock m_buffer; + FixedSizeAlignedSecBlock m_buffer; + word32 m_divisor, m_msr; }; NAMESPACE_END