Skip to content

Commit

Permalink
Add NIST SP800-90A Hash_DRBG generator
Browse files Browse the repository at this point in the history
  • Loading branch information
noloader committed Dec 31, 2016
1 parent 1fe0cc8 commit 5c932fc
Show file tree
Hide file tree
Showing 9 changed files with 650 additions and 32 deletions.
1 change: 1 addition & 0 deletions Filelist.txt
Expand Up @@ -81,6 +81,7 @@ dlltest.cpp
dlltest.vcxproj
dlltest.vcxproj.filters
dmac.h
drbg.h
dsa.cpp
dsa.h
eax.cpp
Expand Down
1 change: 0 additions & 1 deletion cryptdll.vcxproj
Expand Up @@ -274,7 +274,6 @@
<ClInclude Include="gf2n.h" />
<ClInclude Include="gfpcrypt.h" />
<ClInclude Include="hex.h" />
<ClInclude Include="hkdf.h" />
<ClInclude Include="hmac.h" />
<ClInclude Include="integer.h" />
<ClInclude Include="iterhash.h" />
Expand Down
3 changes: 0 additions & 3 deletions cryptdll.vcxproj.filters
Expand Up @@ -274,9 +274,6 @@
<ClInclude Include="hex.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hkdf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hmac.h">
<Filter>Header Files</Filter>
</ClInclude>
Expand Down
1 change: 1 addition & 0 deletions cryptlib.vcxproj
Expand Up @@ -370,6 +370,7 @@
<ClInclude Include="dh.h" />
<ClInclude Include="dh2.h" />
<ClInclude Include="dmac.h" />
<ClInclude Include="drbg.h" />
<ClInclude Include="dsa.h" />
<ClInclude Include="eax.h" />
<ClInclude Include="ec2n.h" />
Expand Down
3 changes: 3 additions & 0 deletions cryptlib.vcxproj.filters
Expand Up @@ -501,6 +501,9 @@
<ClInclude Include="dmac.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="drbg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dsa.h">
<Filter>Header Files</Filter>
</ClInclude>
Expand Down
245 changes: 245 additions & 0 deletions drbg.h
@@ -0,0 +1,245 @@
// drbg.h - written and placed in public domain by Jeffrey Walton.
// Copyright assigned to Crypto++ project.

//! \file drbg.h
//! \brief Classes for NIST DRBGs from 800-90A Rev 1 (June 2015)
//! \sa <A HREF="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf">Recommendation
//! for Random Number Generation Using Deterministic Random Bit Generators</A>
//! \since Crypto++ 5.7

#ifndef CRYPTOPP_NIST_DETERMINISTIC_RANDOM_BIT_GENERATORS_H
#define CRYPTOPP_NIST_DETERMINISTIC_RANDOM_BIT_GENERATORS_H

#include "cryptlib.h"
#include "secblock.h"

NAMESPACE_BEGIN(CryptoPP)

class NIST_DRBG : public RandomNumberGenerator
{
public:
virtual ~NIST_DRBG() {}

// RandomNumberGenerator
virtual bool CanIncorporateEntropy() const {return true;}
// RandomNumberGenerator
virtual void IncorporateEntropy(const byte *input, size_t length)=0;
// RandomNumberGenerator NIST overload
virtual void IncorporateEntropy(const byte *entropy, size_t entropyLength, const byte* additional, size_t additionaLength)=0;
// RandomNumberGenerator
virtual void GenerateBlock(byte *output, size_t size)=0;
// RandomNumberGenerator NIST overload
virtual void GenerateBlock(const byte* additional, size_t additionaLength, byte *output, size_t size)=0;

virtual unsigned int GetSecurityStrength() const=0;
virtual unsigned int GetSeedLength() const=0;
virtual unsigned int GetMinEntropy() const=0;
virtual unsigned int GetMaxEntropy() const=0;
virtual unsigned int GetMaxRequest() const=0;
virtual unsigned int GetMaxReseed() const=0;

protected:

virtual void DRBG_Instantiate(const byte* entropy, size_t entropyLength,
const byte* nonce, size_t nonceLength, const byte* persoanlization, size_t personalizationLength)=0;

virtual void DRBG_Reseed(const byte* entropy, size_t entropyLength, const byte* additional, size_t additionaLength)=0;
};

//! \class Hash_DRBG
//! \tparam HASH NIST approved hash derived from HashTransformation
//! \tparam STRENGTH security strength, in bytes
//! \tparam SEEDLENGTH seed length, in bytes
//! \brief Classes for NIST DRBGs from 800-90A Rev 1 (June 2015)
//! \details The NIST Hash DRBG is instantiated with a number of parameters. Two of the parameters,
//! Security Strength and Seed Length, depend on the hash and are specified as template parameters.
//! The remaining parameters are included in the class. The parameters and their values are listed
//! in NIST SP 800-90A Rev. 1, Table 2: Definitions for Hash-Based DRBG Mechanisms (p.38).
//! \sa <A HREF="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf">Recommendation
//! for Random Number Generation Using Deterministic Random Bit Generators</A>
//! \since Crypto++ 5.7
template <typename HASH=SHA256, unsigned int STRENGTH=128/8, unsigned int SEEDLENGTH=440/8>
class Hash_DRBG : public NIST_DRBG
{
public:
CRYPTOPP_CONSTANT(SECURITY_STRENGTH=STRENGTH)
CRYPTOPP_CONSTANT(SEED_LENGTH=SEEDLENGTH)
CRYPTOPP_CONSTANT(MINIMUM_ENTROPY=STRENGTH)

Hash_DRBG(const byte* entropy, size_t entropyLength=STRENGTH, const byte* nonce=NULL,
size_t nonceLength=0, const byte* persoanlization=NULL, size_t persoanlizationLength=0)
: NIST_DRBG(), m_c(SEEDLENGTH), m_v(SEEDLENGTH)
{
DRBG_Instantiate(entropy, entropyLength, nonce, nonceLength, persoanlization, persoanlizationLength);
}

unsigned int GetSecurityStrength() const {return SECURITY_STRENGTH;}
unsigned int GetSeedLength() const {return SEED_LENGTH;}
unsigned int GetMinEntropy() const {return SECURITY_STRENGTH;}
unsigned int GetMaxEntropy() const {return (unsigned int)STDMIN((word64)UINT_MAX, W64LIT(4294967296));}
unsigned int GetMaxRequest() const {return 65536;} // 2^16 bytes per request
unsigned int GetMaxReseed() const {return (unsigned int)STDMIN((word64)UINT_MAX, W64LIT(35184372088832));}

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 Hash_Generate(NULL, 0, output, size);}

void GenerateBlock(const byte* additional, size_t additionaLength, byte *output, size_t size)
{return Hash_Generate(additional, additionaLength, output, size);}

protected:
// 10.3.1 Derivation Function Using a Hash Function (Hash_df) (p.58)
void Hash_df(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<word32>(outlen*8));

size_t count;
for (count=0; outlen; outlen -= count, output += count, counter++)
{
hash.Update(&counter, 1);
hash.Update(reinterpret_cast<const byte*>(&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);

count = STDMIN(outlen, (size_t)HASH::DIGESTSIZE);
hash.TruncatedFinal(output, count);
}
}

// 10.1.1.2 Instantiation of Hash_DRBG (p.48)
void DRBG_Instantiate(const byte* entropy, size_t entropyLength, const byte* nonce, size_t nonceLength,
const byte* persoanlization, size_t persoanlizationLength)
{
CRYPTOPP_ASSERT(entropyLength+nonceLength+persoanlizationLength >= GetMinEntropy());

const byte zero = 0;
SecByteBlock t1(SEEDLENGTH), t2(SEEDLENGTH);
Hash_df(entropy, entropyLength, nonce, nonceLength, persoanlization, persoanlizationLength, NULL, 0, t1, t1.size());
Hash_df(&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.49)
void DRBG_Reseed(const byte* entropy, size_t entropyLength, const byte* additional, size_t additionaLength)
{
const byte zero = 0, one = 1;
SecByteBlock t1(SEEDLENGTH), t2(SEEDLENGTH);
Hash_df(&one, 1, m_v, m_v.size(), entropy, entropyLength, additional, additionaLength, t1, t1.size());
Hash_df(&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.50)
void Hash_Generate(const byte* additional, size_t additionaLength, byte *output, size_t size)
{
// Step 1
if (static_cast<word64>(m_reseed) >= static_cast<word64>(GetMaxReseed()))
throw Exception(Exception::OTHER_ERROR, "Reseed required");

// 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<byte>(carry);
carry >>= 8; i--; j--;
}
while (carry && i>=0)
{
carry = m_v[i] + carry;
m_v[i] = static_cast<byte>(carry);
carry >>= 8; i--;
}
}

// Step 3
{
HASH hash;
SecByteBlock data(m_v);

size_t count;
for (count = 0; size; size -= count, output += count)
{
hash.Update(data, data.size());
count = STDMIN(size, (size_t)HASH::DIGESTSIZE);
hash.TruncatedFinal(output, count);

IncrementCounterByOne(data, static_cast<unsigned int>(data.size()));
}
}

// 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<word64>(BIG_ENDIAN_ORDER, m_reseed, k) + carry;
m_v[i] = static_cast<byte>(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<byte>(carry);
carry >>= 8; i--; j--;
}
while (i>=0)
{
carry = m_v[i] + m_c[i] + carry;
m_v[i] = static_cast<byte>(carry);
carry >>= 8; i--;
}
}

m_reseed++;
}

private:
SecByteBlock m_c, m_v;
word64 m_reseed;
};

NAMESPACE_END

#endif // CRYPTOPP_NIST_DETERMINISTIC_RANDOM_BIT_GENERATORS_H
57 changes: 29 additions & 28 deletions test.cpp
Expand Up @@ -900,34 +900,35 @@ bool Validate(int alg, bool thorough, const char *seedInput)
case 0: result = ValidateAll(thorough); break;
case 1: result = TestSettings(); break;
case 2: result = TestOS_RNG(); break;
case 3: result = ValidateMD5(); break;
case 4: result = ValidateSHA(); break;
case 5: result = ValidateDES(); break;
case 6: result = ValidateIDEA(); break;
case 7: result = ValidateARC4(); break;
case 8: result = ValidateRC5(); break;
case 9: result = ValidateBlowfish(); break;
// case 10: result = ValidateDiamond2(); break;
case 11: result = ValidateThreeWay(); break;
case 12: result = ValidateBBS(); break;
case 13: result = ValidateDH(); break;
case 14: result = ValidateRSA(); break;
case 15: result = ValidateElGamal(); break;
case 16: result = ValidateDSA(thorough); break;
// case 17: result = ValidateHAVAL(); break;
case 18: result = ValidateSAFER(); break;
case 19: result = ValidateLUC(); break;
case 20: result = ValidateRabin(); break;
// case 21: result = ValidateBlumGoldwasser(); break;
case 22: result = ValidateECP(); break;
case 23: result = ValidateEC2N(); break;
// case 24: result = ValidateMD5MAC(); break;
case 25: result = ValidateGOST(); break;
case 26: result = ValidateTiger(); break;
case 27: result = ValidateRIPEMD(); break;
case 28: result = ValidateHMAC(); break;
// case 29: result = ValidateXMACC(); break;
case 30: result = ValidateSHARK(); break;
case 3: result = TestNIST_DRBG(); break;
case 4: result = ValidateMD5(); break;
case 5: result = ValidateSHA(); break;
case 6: result = ValidateDES(); break;
case 7: result = ValidateIDEA(); break;
case 8: result = ValidateARC4(); break;
case 9: result = ValidateRC5(); break;
case 10: result = ValidateBlowfish(); break;
// case 11: result = ValidateDiamond2(); break;
case 12: result = ValidateThreeWay(); break;
case 13: result = ValidateBBS(); break;
case 14: result = ValidateDH(); break;
case 15: result = ValidateRSA(); break;
case 16: result = ValidateElGamal(); break;
case 17: result = ValidateDSA(thorough); break;
// case 18: result = ValidateHAVAL(); break;
case 19: result = ValidateSAFER(); break;
case 20: result = ValidateLUC(); break;
case 21: result = ValidateRabin(); break;
// case 22: result = ValidateBlumGoldwasser(); break;
case 23: result = ValidateECP(); break;
case 24: result = ValidateEC2N(); break;
// case 25: result = ValidateMD5MAC(); break;
case 26: result = ValidateGOST(); break;
case 27: result = ValidateTiger(); break;
case 28: result = ValidateRIPEMD(); break;
case 29: result = ValidateHMAC(); break;
// case 30: result = ValidateXMACC(); break;
case 31: result = ValidateSHARK(); break;
case 32: result = ValidateLUC_DH(); break;
case 33: result = ValidateLUC_DL(); break;
case 34: result = ValidateSEAL(); break;
Expand Down

1 comment on commit 5c932fc

@noloader
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds the Hash_DRBG. We want to add one or both of the remaing generators, HMAC_DRBG and/or CTR_DRBG.

The documentation was provided at Commit ef185fa0136f.

Please sign in to comment.