Bump patch version post tag.
[asdcplib.git] / src / KM_prng.cpp
index c51b2dbddf294b1460a41ce9d96eeeb45f913e69..d11a330d56f049f233eb478cb6b2d6bcee6d01f0 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2006, John Hurst
+Copyright (c) 2006-2009, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -36,38 +36,16 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assert.h>
 #include <openssl/aes.h>
 #include <openssl/sha.h>
+#include <openssl/bn.h>
 
 using namespace Kumu;
 
 
 #ifdef KM_WIN32
-
-//  make up a byte by sampling the perf counter LSB
-static byte_t get_perf_byte(byte_t carry)
-{
-  LARGE_INTEGER ticks;
-  byte_t sha_buf[20];
-  SHA_CTX SHA;
-  SHA1_Init(&SHA);
-  SHA1_Update(&SHA, &carry, sizeof(byte_t));
-
-  for ( int i = 0; i < 128; i++ )
-    {
-      QueryPerformanceCounter(&ticks);
-      SHA1_Update(&SHA, &ticks.LowPart, sizeof(ticks.LowPart));
-    }
-  
-  SHA1_Final(sha_buf, &SHA);
-
-  fprintf(stderr, "0x%02x ", sha_buf[0]);
-  return sha_buf[0];
-}
-
+# include <wincrypt.h>
 #else // KM_WIN32
-
-#include <KM_fileio.h>
+# include <KM_fileio.h>
 const char* DEV_URANDOM = "/dev/urandom";
-
 #endif // KM_WIN32
 
 
@@ -97,11 +75,9 @@ public:
       AutoMutex Lock(m_Lock);
 
 #ifdef KM_WIN32
-      for ( ui32_t i = 0; i < RNG_KEY_SIZE; i++ )
-       {
-         byte_t carry = ( i == 0 ) ? 0xa3 : rng_key[i-1];
-         rng_key[i] = get_perf_byte(carry);
-       }
+      HCRYPTPROV hProvider = 0;
+      CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+      CryptGenRandom(hProvider, RNG_KEY_SIZE, rng_key);
 #else // KM_WIN32
       // on POSIX systems we simply read some seed from /dev/urandom
       FileReader URandom;
@@ -160,8 +136,7 @@ public:
       {
        byte_t tmp[RNG_BLOCK_SIZE];
        AES_encrypt(m_ctr_buf, tmp, &m_Context);
-       *(ui32_t*)(m_ctr_buf + 12) += 1;
-       memcpy(buf, tmp, len - gen_count);
+       memcpy(buf + gen_count, tmp, len - gen_count);
       }
   }
 };
@@ -172,7 +147,7 @@ static h__RNG* s_RNG = 0;
 
 //------------------------------------------------------------------------------------------
 //
-// public interface
+// Fortuna public interface
 
 Kumu::FortunaRNG::FortunaRNG()
 {
@@ -188,6 +163,7 @@ Kumu::FortunaRNG::FillRandom(byte_t* buf, ui32_t len)
 {
   assert(buf);
   assert(s_RNG);
+  const byte_t* front_of_buffer = buf;
 
   while ( len )
     {
@@ -204,7 +180,7 @@ Kumu::FortunaRNG::FillRandom(byte_t* buf, ui32_t len)
       s_RNG->set_key(rng_key);
   }
   
-  return buf;
+  return front_of_buffer;
 }
 
 //
@@ -216,6 +192,91 @@ Kumu::FortunaRNG::FillRandom(Kumu::ByteString& Buffer)
   return Buffer.Data();
 }
 
+//------------------------------------------------------------------------------------------
+
+//
+// FIPS 186-2 Sec. 3.1 as modified by Change 1, section entitled "General Purpose Random Number Generation"
+void
+Kumu::Gen_FIPS_186_Value(const byte_t* key, ui32_t key_size, byte_t* out_buf, ui32_t out_buf_len)
+{
+  byte_t sha_buf[SHA_DIGEST_LENGTH];
+  ui32_t const xkey_len = 64; // 512/8
+  byte_t xkey[xkey_len];
+  BN_CTX* ctx1 = BN_CTX_new(); // used by BN_* functions
+  assert(ctx1);
+
+  if ( key_size > xkey_len )
+    DefaultLogSink().Warn("Key too large for FIPS 186 seed, truncating to 64 bytes.\n");
+
+  // init key
+  memset(xkey, 0, xkey_len);
+  memcpy(xkey, key, xmin<ui32_t>(key_size, xkey_len));
+
+  if ( key_size < SHA_DIGEST_LENGTH )
+    key_size = SHA_DIGEST_LENGTH; // pad short key ( b < 160 )
+
+  // create the 2^b constant
+  BIGNUM *c_2powb = BN_new();
+  BIGNUM * c_2 = BN_new();
+  BIGNUM * c_b = BN_new();
+  assert(c_2powb);
+  assert(c_2);
+  assert(c_b);
+
+  BN_set_word(c_2, 2);
+  BN_set_word(c_b, key_size * 8);
+  BN_exp(c_2powb, c_2, c_b, ctx1);
+
+  for (;;)
+    {
+      SHA_CTX SHA;
+
+      // step c -- x = G(t,xkey)
+      SHA1_Init(&SHA); // set t
+      SHA1_Update(&SHA, xkey, xkey_len);
+
+      ui32_t* buf_p = (ui32_t*)sha_buf;
+      *buf_p++ = KM_i32_BE(SHA.h0);
+      *buf_p++ = KM_i32_BE(SHA.h1);
+      *buf_p++ = KM_i32_BE(SHA.h2);
+      *buf_p++ = KM_i32_BE(SHA.h3);
+      *buf_p++ = KM_i32_BE(SHA.h4);
+      memcpy(out_buf, sha_buf, xmin<ui32_t>(out_buf_len, SHA_DIGEST_LENGTH));
+
+      if ( out_buf_len <= SHA_DIGEST_LENGTH )
+       break;
+
+      out_buf_len -= SHA_DIGEST_LENGTH;
+      out_buf += SHA_DIGEST_LENGTH;
+
+      // step d -- XKEY = (1 + XKEY + x) mod 2^b
+      BIGNUM *bn_tmp = BN_new();
+      BIGNUM *bn_xkey = BN_new();
+      BIGNUM *bn_x_n = BN_new();
+      assert(bn_tmp);
+      assert(bn_xkey);
+      assert(bn_x_n);
+
+      BN_bin2bn(xkey, key_size, bn_xkey);
+      BN_bin2bn(sha_buf, SHA_DIGEST_LENGTH, bn_x_n);
+      BN_add_word(bn_xkey, 1);            // xkey += 1
+      BN_add(bn_tmp, bn_xkey, bn_x_n);       // xkey += x
+      BN_mod(bn_xkey, bn_tmp, c_2powb, ctx1);  // xkey = xkey mod (2^b)
+
+      memset(xkey, 0, xkey_len);
+      ui32_t bn_buf_len = BN_num_bytes(bn_xkey);
+      ui32_t idx = ( bn_buf_len < key_size ) ? key_size - bn_buf_len : 0;
+      BN_bn2bin(bn_xkey, &xkey[idx]);
+      BN_free(bn_tmp);
+      BN_free(bn_xkey);
+      BN_free(bn_x_n);
+    }
+
+  BN_free(c_2powb);
+  BN_free(c_2);
+  BN_free(c_b);
+  BN_CTX_free(ctx1);
+}
 
 //
 // end KM_prng.cpp