class HMAC_DRBG : public NIST_DRBG, public NotCopyable
{
public:
CRYPTOPP_CONSTANT(SECURITY_STRENGTH=STRENGTH)
CRYPTOPP_CONSTANT(SEED_LENGTH=SEEDLENGTH)
CRYPTOPP_CONSTANT(MINIMUM_ENTROPY=STRENGTH)
CRYPTOPP_CONSTANT(MINIMUM_NONCE=0)
CRYPTOPP_CONSTANT(MINIMUM_ADDITIONAL=0)
CRYPTOPP_CONSTANT(MINIMUM_PERSONALIZATION=0)
CRYPTOPP_CONSTANT(MAXIMUM_ENTROPY=INT_MAX)
CRYPTOPP_CONSTANT(MAXIMUM_NONCE=INT_MAX)
CRYPTOPP_CONSTANT(MAXIMUM_ADDITIONAL=INT_MAX)
CRYPTOPP_CONSTANT(MAXIMUM_PERSONALIZATION=INT_MAX)
CRYPTOPP_CONSTANT(MAXIMUM_BYTES_PER_REQUEST=65536)
CRYPTOPP_CONSTANT(MAXIMUM_REQUESTS_BEFORE_RESEED=INT_MAX)
//! \brief Construct a HMAC DRBG
//! \param entropy the entropy to instantiate the generator
//! \param entropyLength the size of the entropy buffer
//! \param nonce additional input to instantiate the generator
//! \param nonceLength the size of the nonce buffer
//! \param personalization additional input to instantiate the generator
//! \param personalizationLength the size of the personalization buffer
//! \throws NIST_DRBG::Err if the generator is instantiated with insufficient entropy
//! \details All NIST DRBGs must be instaniated with at least MINIMUM_ENTROPY bytes of entropy.
//! The byte array for entropy must meet NIST
//! SP 800-90B or SP 800-90C requirements.
//! \details The nonce and personalization are optional byte arrays. If nonce is supplied,
//! then it should be at least MINIMUM_NONCE bytes of entropy.
//! \details An example of instantiating a SHA256 generator is shown below.
//! The example provides more entropy than required for SHA256. The NonblockingRng meets the
//! requirements of NIST SP 800-90B or SP 800-90C.
//! RDRAND() and RDSEED() generators would work as well.
//!
//! SecByteBlock entropy(48), result(128);
//! NonblockingRng prng;
//! RandomNumberSource rns(prng, entropy.size(), new ArraySink(entropy, entropy.size()));
//!
//! HMAC_DRBG drbg(entropy, 32, entropy+32, 16);
//! drbg.GenerateBlock(result, result.size());
//!
HMAC_DRBG(const byte* entropy, size_t entropyLength=STRENGTH, const byte* nonce=NULL,
size_t nonceLength=0, const byte* personalization=NULL, size_t personalizationLength=0)
: NIST_DRBG(), m_k(HASH::DIGESTSIZE), m_v(HASH::DIGESTSIZE)
{
DRBG_Instantiate(entropy, entropyLength, nonce, nonceLength, personalization, personalizationLength);
}
unsigned int GetSecurityStrength() const {return SECURITY_STRENGTH;}
unsigned int GetSeedLength() const {return SEED_LENGTH;}
unsigned int GetMinEntropy() const {return MINIMUM_ENTROPY;}
unsigned int GetMaxEntropy() const {return MAXIMUM_ENTROPY;}
unsigned int GetMinNonce() const {return MINIMUM_NONCE;}
unsigned int GetMaxNonce() const {return MAXIMUM_NONCE;}
unsigned int GetMaxBytesPerRequest() const {return MAXIMUM_BYTES_PER_REQUEST;}
unsigned int GetMaxRequestBeforeReseed() const {return MAXIMUM_REQUESTS_BEFORE_RESEED;}
void IncorporateEntropy(const byte *input, size_t length)
{return DRBG_Reseed(input, length, NULL, 0);}
void IncorporateEntropy(const byte *entropy, size_t entropyLength, const byte* additional, size_t additionaLength)
{return DRBG_Reseed(entropy, entropyLength, additional, additionaLength);}
void GenerateBlock(byte *output, size_t size)
{return HMAC_Generate(NULL, 0, output, size);}
void GenerateBlock(const byte* additional, size_t additionaLength, byte *output, size_t size)
{return HMAC_Generate(additional, additionaLength, output, size);}
protected:
// 10.1.2.3 Instantiation of HMAC_DRBG (p.45)
void DRBG_Instantiate(const byte* entropy, size_t entropyLength, const byte* nonce, size_t nonceLength,
const byte* personalization, size_t personalizationLength);
// 10.1.2.4 Reseeding a HMAC_DRBG Instantiation (p.46)
void DRBG_Reseed(const byte* entropy, size_t entropyLength, const byte* additional, size_t additionaLength);
// 10.1.2.5 Generating Pseudorandom Bits Using HMAC_DRBG (p.46)
void HMAC_Generate(const byte* additional, size_t additionaLength, byte *output, size_t size);
// 10.1.2.2 Derivation Function Using a HMAC Function (HMAC_Update) (p.44)
void HMAC_Update(const byte* input1, size_t inlen1, const byte* input2, size_t inlen2, const byte* input3, size_t inlen3);
private:
SecByteBlock m_k, m_v;
word64 m_reseed;
};
// typedef HMAC_DRBG HMAC_SHA1_DRBG;
// typedef HMAC_DRBG HMAC_SHA256_DRBG;
// typedef HMAC_DRBG HMAC_SHA384_DRBG;
// typedef HMAC_DRBG HMAC_SHA512_DRBG;
// *************************************************************
// 10.1.1.2 Instantiation of Hash_DRBG (p.39)
template
void Hash_DRBG::DRBG_Instantiate(const byte* entropy, size_t entropyLength, const byte* nonce, size_t nonceLength,
const byte* personalization, size_t personalizationLength)
{
// SP 800-90A, 8.6.3: The entropy input shall have entropy that is equal to or greater than the security
// strength of the instantiation. Additional entropy may be provided in the nonce or the optional
// personalization string during instantiation, or in the additional input during reseeding and generation,
// but this is not required and does not increase the "official" security strength of the DRBG
// instantiation that is recorded in the internal state.
CRYPTOPP_ASSERT(entropyLength >= MINIMUM_ENTROPY);
if (entropyLength < MINIMUM_ENTROPY)
throw NIST_DRBG::Err("Hash_DRBG", "Insufficient entropy during instantiate");
// SP 800-90A, Section 9, says we should throw if we have too much entropy, too large a nonce,
// or too large a persoanlization string. We warn in Debug builds, but do nothing in Release builds.
CRYPTOPP_ASSERT(entropyLength <= MAXIMUM_ENTROPY);
CRYPTOPP_ASSERT(nonceLength <= MAXIMUM_NONCE);
CRYPTOPP_ASSERT(personalizationLength <= MAXIMUM_PERSONALIZATION);
const byte zero = 0;
SecByteBlock t1(SEEDLENGTH), t2(SEEDLENGTH);
Hash_Update(entropy, entropyLength, nonce, nonceLength, personalization, personalizationLength, NULL, 0, t1, t1.size());
Hash_Update(&zero, 1, t1, t1.size(), NULL, 0, NULL, 0, t2, t2.size());
m_v.swap(t1); m_c.swap(t2);
m_reseed = 1;
}
// 10.1.1.3 Reseeding a Hash_DRBG Instantiation (p.40)
template
void Hash_DRBG::DRBG_Reseed(const byte* entropy, size_t entropyLength, const byte* additional, size_t additionaLength)
{
// SP 800-90A, 8.6.3: The entropy input shall have entropy that is equal to or greater than the security
// strength of the instantiation. Additional entropy may be provided in the nonce or the optional
// personalization string during instantiation, or in the additional input during reseeding and generation,
// but this is not required and does not increase the "official" security strength of the DRBG
// instantiation that is recorded in the internal state..
CRYPTOPP_ASSERT(entropyLength >= MINIMUM_ENTROPY);
if (entropyLength < MINIMUM_ENTROPY)
throw NIST_DRBG::Err("Hash_DRBG", "Insufficient entropy during reseed");
// SP 800-90A, Section 9, says we should throw if we have too much entropy, too large a nonce,
// or too large a persoanlization string. We warn in Debug builds, but do nothing in Release builds.
CRYPTOPP_ASSERT(entropyLength <= MAXIMUM_ENTROPY);
CRYPTOPP_ASSERT(additionaLength <= MAXIMUM_ADDITIONAL);
const byte zero = 0, one = 1;
SecByteBlock t1(SEEDLENGTH), t2(SEEDLENGTH);
Hash_Update(&one, 1, m_v, m_v.size(), entropy, entropyLength, additional, additionaLength, t1, t1.size());
Hash_Update(&zero, 1, t1, t1.size(), NULL, 0, NULL, 0, t2, t2.size());
m_v.swap(t1); m_c.swap(t2);
m_reseed = 1;
}
// 10.1.1.4 Generating Pseudorandom Bits Using Hash_DRBG (p.41)
template
void Hash_DRBG::Hash_Generate(const byte* additional, size_t additionaLength, byte *output, size_t size)
{
// Step 1
if (static_cast(m_reseed) >= static_cast(GetMaxRequestBeforeReseed()))
throw NIST_DRBG::Err("Hash_DRBG", "Reseed required");
if (size > GetMaxBytesPerRequest())
throw NIST_DRBG::Err("Hash_DRBG", "Request size exceeds limit");
// SP 800-90A, Section 9, says we should throw if we have too much entropy, too large a nonce,
// or too large a persoanlization string. We warn in Debug builds, but do nothing in Release builds.
CRYPTOPP_ASSERT(additionaLength <= MAXIMUM_ADDITIONAL);
// Step 2
if (additional && additionaLength)
{
HASH hash;
const byte two = 2;
SecByteBlock w(HASH::DIGESTSIZE);
hash.Update(&two, 1);
hash.Update(m_v, m_v.size());
hash.Update(additional, additionaLength);
hash.Final(w);
CRYPTOPP_ASSERT(SEEDLENGTH >= HASH::DIGESTSIZE);
int carry=0, i=SEEDLENGTH-1, j=HASH::DIGESTSIZE-1;
while(i>=0 && j>=0)
{
carry = m_v[i] + w[j] + carry;
m_v[i] = static_cast(carry);
carry >>= 8; i--; j--;
}
while (carry && i>=0)
{
carry = m_v[i] + carry;
m_v[i] = static_cast(carry);
carry >>= 8; i--;
}
}
// Step 3
{
HASH hash;
SecByteBlock data(m_v);
while (size)
{
hash.Update(data, data.size());
size_t count = STDMIN(size, (size_t)HASH::DIGESTSIZE);
hash.TruncatedFinal(output, count);
IncrementCounterByOne(data, static_cast(data.size()));
output += count; size -= count;
}
}
// Steps 4-7
{
HASH hash;
const byte three = 3;
SecByteBlock h(HASH::DIGESTSIZE);
hash.Update(&three, 1);
hash.Update(m_v, m_v.size());
hash.Final(h);
CRYPTOPP_ASSERT(SEEDLENGTH >= HASH::DIGESTSIZE);
CRYPTOPP_ASSERT(HASH::DIGESTSIZE >= sizeof(m_reseed));
int carry=0, i=SEEDLENGTH-1, j=HASH::DIGESTSIZE-1, k=sizeof(m_reseed)-1;
while(i>=0 && j>=0 && k>=0)
{
carry = m_v[i] + m_c[i] + h[j] + GetByte(BIG_ENDIAN_ORDER, m_reseed, k) + carry;
m_v[i] = static_cast(carry);
carry >>= 8; i--; j--; k--;
}
while(i>=0 && j>=0)
{
carry = m_v[i] + m_c[i] + h[j] + carry;
m_v[i] = static_cast(carry);
carry >>= 8; i--; j--;
}
while (i>=0)
{
carry = m_v[i] + m_c[i] + carry;
m_v[i] = static_cast(carry);
carry >>= 8; i--;
}
}
m_reseed++;
}
// 10.3.1 Derivation Function Using a Hash Function (Hash_df) (p.49)
template
void Hash_DRBG::Hash_Update(const byte* input1, size_t inlen1, const byte* input2, size_t inlen2,
const byte* input3, size_t inlen3, const byte* input4, size_t inlen4, byte* output, size_t outlen)
{
HASH hash;
byte counter = 1;
word32 bits = ConditionalByteReverse(BIG_ENDIAN_ORDER, static_cast(outlen*8));
while (outlen)
{
hash.Update(&counter, 1);
hash.Update(reinterpret_cast(&bits), 4);
if (input1 && inlen1)
hash.Update(input1, inlen1);
if (input2 && inlen2)
hash.Update(input2, inlen2);
if (input3 && inlen3)
hash.Update(input3, inlen3);
if (input4 && inlen4)
hash.Update(input4, inlen4);
size_t count = STDMIN(outlen, (size_t)HASH::DIGESTSIZE);
hash.TruncatedFinal(output, count);
output += count; outlen -= count;
counter++;
}
}
// *************************************************************
// 10.1.2.3 Instantiation of HMAC_DRBG (p.45)
template
void HMAC_DRBG::DRBG_Instantiate(const byte* entropy, size_t entropyLength, const byte* nonce, size_t nonceLength,
const byte* personalization, size_t personalizationLength)
{
// SP 800-90A, 8.6.3: The entropy input shall have entropy that is equal to or greater than the security
// strength of the instantiation. Additional entropy may be provided in the nonce or the optional
// personalization string during instantiation, or in the additional input during reseeding and generation,
// but this is not required and does not increase the "official" security strength of the DRBG
// instantiation that is recorded in the internal state.
CRYPTOPP_ASSERT(entropyLength >= MINIMUM_ENTROPY);
if (entropyLength < MINIMUM_ENTROPY)
throw NIST_DRBG::Err("HMAC_DRBG", "Insufficient entropy during instantiate");
// SP 800-90A, Section 9, says we should throw if we have too much entropy, too large a nonce,
// or too large a persoanlization string. We warn in Debug builds, but do nothing in Release builds.
CRYPTOPP_ASSERT(entropyLength <= MAXIMUM_ENTROPY);
CRYPTOPP_ASSERT(nonceLength <= MAXIMUM_NONCE);
CRYPTOPP_ASSERT(personalizationLength <= MAXIMUM_PERSONALIZATION);
std::fill(m_k.begin(), m_k.begin()+m_k.size(), byte(0));
std::fill(m_v.begin(), m_v.begin()+m_v.size(), byte(1));
HMAC_Update(entropy, entropyLength, nonce, nonceLength, personalization, personalizationLength);
m_reseed = 1;
}
// 10.1.2.4 Reseeding a HMAC_DRBG Instantiation (p.46)
template
void HMAC_DRBG::DRBG_Reseed(const byte* entropy, size_t entropyLength, const byte* additional, size_t additionaLength)
{
// SP 800-90A, 8.6.3: The entropy input shall have entropy that is equal to or greater than the security
// strength of the instantiation. Additional entropy may be provided in the nonce or the optional
// personalization string during instantiation, or in the additional input during reseeding and generation,
// but this is not required and does not increase the "official" security strength of the DRBG
// instantiation that is recorded in the internal state..
CRYPTOPP_ASSERT(entropyLength >= MINIMUM_ENTROPY);
if (entropyLength < MINIMUM_ENTROPY)
throw NIST_DRBG::Err("HMAC_DRBG", "Insufficient entropy during reseed");
// SP 800-90A, Section 9, says we should throw if we have too much entropy, too large a nonce,
// or too large a persoanlization string. We warn in Debug builds, but do nothing in Release builds.
CRYPTOPP_ASSERT(entropyLength <= MAXIMUM_ENTROPY);
CRYPTOPP_ASSERT(additionaLength <= MAXIMUM_ADDITIONAL);
HMAC_Update(entropy, entropyLength, additional, additionaLength, NULL, 0);
m_reseed = 1;
}
// 10.1.2.5 Generating Pseudorandom Bits Using HMAC_DRBG (p.46)
template
void HMAC_DRBG::HMAC_Generate(const byte* additional, size_t additionaLength, byte *output, size_t size)
{
// Step 1
if (static_cast(m_reseed) >= static_cast(GetMaxRequestBeforeReseed()))
throw NIST_DRBG::Err("HMAC_DRBG", "Reseed required");
if (size > GetMaxBytesPerRequest())
throw NIST_DRBG::Err("HMAC_DRBG", "Request size exceeds limit");
// SP 800-90A, Section 9, says we should throw if we have too much entropy, too large a nonce,
// or too large a persoanlization string. We warn in Debug builds, but do nothing in Release builds.
CRYPTOPP_ASSERT(additionaLength <= MAXIMUM_ADDITIONAL);
// Step 2
if (additional && additionaLength)
HMAC_Update(additional, additionaLength, NULL, 0, NULL, 0);
// Step 3
HMAC hmac;
hmac.SetKey(m_k, m_k.size());
while (size)
{
hmac.Update(m_v, m_v.size());
hmac.TruncatedFinal(m_v, m_v.size());
size_t count = STDMIN(size, (size_t)HASH::DIGESTSIZE);
memcpy(output, m_v, count);
size -= count; output += count;
}
HMAC_Update(additional, additionaLength, NULL, 0, NULL, 0);
m_reseed++;
}
// 10.1.2.2 Derivation Function Using a HMAC Function (HMAC_Update) (p.44)
template
void HMAC_DRBG::HMAC_Update(const byte* input1, size_t inlen1, const byte* input2, size_t inlen2, const byte* input3, size_t inlen3)
{
const byte zero = 0, one = 1;
HMAC hmac;
// Step 1
hmac.SetKey(m_k, m_k.size());
hmac.Update(m_v, m_v.size());
hmac.Update(&zero, 1);
if (input1 && inlen1)
hmac.Update(input1, inlen1);
if (input2 && inlen2)
hmac.Update(input2, inlen2);
if (input3 && inlen3)
hmac.Update(input3, inlen3);
hmac.TruncatedFinal(m_k, m_k.size());
// Step 2
hmac.SetKey(m_k, m_k.size());
hmac.Update(m_v, m_v.size());
hmac.TruncatedFinal(m_v, m_v.size());
// Step 3
if ((inlen1 | inlen2 | inlen3) == 0)
return;
// Step 4
hmac.SetKey(m_k, m_k.size());
hmac.Update(m_v, m_v.size());
hmac.Update(&one, 1);
if (input1 && inlen1)
hmac.Update(input1, inlen1);
if (input2 && inlen2)
hmac.Update(input2, inlen2);
if (input3 && inlen3)
hmac.Update(input3, inlen3);
hmac.TruncatedFinal(m_k, m_k.size());
// Step 5
hmac.SetKey(m_k, m_k.size());
hmac.Update(m_v, m_v.size());
hmac.TruncatedFinal(m_v, m_v.size());
}
NAMESPACE_END
#endif // CRYPTOPP_NIST_DRBG_H