/*
 * Compilation Copyright (c) 1995-2019 by Wei Dai.  All rights reserved.
 *	This copyright applies only to this software distribution package
 *	as a compilation, and does not imply a copyright on any particular
 *	file in the package.
 * 
 *	Boost Software License - Version 1.0 - August 17th, 2003
 *
 *	Permission is hereby granted, free of charge, to any person or organization
 *	obtaining a copy of the software and accompanying documentation covered by
 *	this license (the "Software") to use, reproduce, display, distribute,
 *	execute, and transmit the Software, and to prepare derivative works of the
 *	Software, and to permit third-parties to whom the Software is furnished to
 *	do so, all subject to the following:
 * 
 *	The copyright notices in the Software and this entire statement, including
 *	the above license grant, this restriction and the following disclaimer,
 *	must be included in all copies of the Software, in whole or in part, and
 *	all derivative works of the Software, unless such copies or derivative
 *	works are solely in the form of machine-executable object code generated by
 *	a source language processor.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 *	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 *	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 *	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *	DEALINGS IN THE SOFTWARE.
 */

// rdrand.h - written and placed in public domain by Jeffrey Walton and Uri Blumenthal.

/// \file rdrand.h
/// \brief Classes for RDRAND and RDSEED
/// \since Crypto++ 5.6.3

#ifndef CRYPTOPP_RDRAND_H
#define CRYPTOPP_RDRAND_H

#include "cryptlib.h"

// This class file 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 "throw NotImplemented". At runtime the
//   constructor will throw RDRAND_Err or RDSEED_Err if a generator is
//   is not available.
// 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.

// Throughput varies wildly depending on processor and manufacturer. A Core i5 or
//   Core i7 RDRAND can generate at over 200 MiB/s. It is below the theroetical
//   maximum, but it takes about 5 instructions to generate, retry and store a
//   result. A low-end Celeron may perform RDRAND at about 7 MiB/s. RDSEED
//   performs at about 1/4 to 1/2 the rate of RDRAND. AMD RDRAND performed poorly
//   during testing with Athlon X4 845. The Bulldozer v4 only performed at 1 MiB/s.

// Microsoft added RDRAND in August 2012, VS2012; RDSEED in October 2013, VS2013.
// GCC added RDRAND in December 2010, GCC 4.6. LLVM added RDRAND in July 2012,
// Clang 3.2. Intel added RDRAND in September 2011, ICC 12.1.

NAMESPACE_BEGIN(CryptoPP)

/// \brief Exception thrown when a RDRAND generator encounters
///    a generator related error.
/// \since Crypto++ 5.6.3
class RDRAND_Err : public Exception
{
public:
    RDRAND_Err(const std::string &operation)
        : Exception(OTHER_ERROR, "RDRAND: " + operation + " operation failed") {}
};

/// \brief Hardware generated random numbers using RDRAND instruction
/// \sa MaurerRandomnessTest() for random bit generators
/// \since Crypto++ 5.6.3
class RDRAND : public RandomNumberGenerator
{
public:
    CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "RDRAND"; }

    virtual ~RDRAND() {}

    /// \brief Construct a RDRAND generator
    /// \details According to DJ of Intel, the Intel RDRAND circuit does not underflow.
    ///   If it did hypothetically underflow, then it would return 0 for the random value.
    ///   AMD's RDRAND implementation appears to provide the same behavior.
     /// \throws RDRAND_Err if the random number generator is not available
    RDRAND();

    /// \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);
    }

    std::string AlgorithmProvider() const {
        return "RDRAND";
    }
};

/// \brief Exception thrown when a RDSEED generator encounters
///    a generator related error.
/// \since Crypto++ 5.6.3
class RDSEED_Err : public Exception
{
public:
    RDSEED_Err(const std::string &operation)
        : Exception(OTHER_ERROR, "RDSEED: " + operation + " operation failed") {}
};

/// \brief Hardware generated random numbers using RDSEED instruction
/// \sa MaurerRandomnessTest() for random bit generators
/// \since Crypto++ 5.6.3
class RDSEED : public RandomNumberGenerator
{
public:
    CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() { return "RDSEED"; }

    virtual ~RDSEED() {}

    /// \brief Construct a RDSEED generator
    /// \details Empirical testing under a 6th generaton i7 (6200U) shows RDSEED fails
    ///   to fulfill requests at about once every for every 256 bytes requested.
    ///   The generator runs about 4 times slower than RDRAND.
     /// \throws RDSEED_Err if the random number generator is not available
    RDSEED();

    /// \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);
    }

    std::string AlgorithmProvider() const {
        return "RDSEED";
    }
};

NAMESPACE_END

#endif // CRYPTOPP_RDRAND_H
