summaryrefslogtreecommitdiff
path: root/asdcplib/src/KM_prng.cpp
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2012-07-16 19:25:13 +0100
committerCarl Hetherington <cth@carlh.net>2012-07-16 19:25:13 +0100
commitec1097e8426461b854b38e7b14bc73995c365e0a (patch)
tree19c8e41ff4061f48ba23524bc8e38f5398c1f21a /asdcplib/src/KM_prng.cpp
parent7d48446b5efdf795df1ce22d6d9ed3ebe85d3381 (diff)
asdcplib 1.9.45
Diffstat (limited to 'asdcplib/src/KM_prng.cpp')
-rwxr-xr-xasdcplib/src/KM_prng.cpp268
1 files changed, 268 insertions, 0 deletions
diff --git a/asdcplib/src/KM_prng.cpp b/asdcplib/src/KM_prng.cpp
new file mode 100755
index 00000000..5212595f
--- /dev/null
+++ b/asdcplib/src/KM_prng.cpp
@@ -0,0 +1,268 @@
+/*
+Copyright (c) 2006-2009, John Hurst
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+ /*! \file KM_prng.cpp
+ \version $Id: KM_prng.cpp,v 1.9 2011/06/14 23:38:33 jhurst Exp $
+ \brief Fortuna pseudo-random number generator
+ */
+
+#include <KM_prng.h>
+#include <KM_log.h>
+#include <KM_mutex.h>
+#include <string.h>
+#include <assert.h>
+#include <openssl/aes.h>
+#include <openssl/sha.h>
+#include <openssl/bn.h>
+
+using namespace Kumu;
+
+
+#ifdef KM_WIN32
+# include <wincrypt.h>
+#else // KM_WIN32
+# include <KM_fileio.h>
+const char* DEV_URANDOM = "/dev/urandom";
+#endif // KM_WIN32
+
+
+const ui32_t RNG_KEY_SIZE = 512UL;
+const ui32_t RNG_KEY_SIZE_BITS = 256UL;
+const ui32_t RNG_BLOCK_SIZE = 16UL;
+const ui32_t MAX_SEQUENCE_LEN = 0x00040000UL;
+
+
+// internal implementation class
+class h__RNG
+{
+ KM_NO_COPY_CONSTRUCT(h__RNG);
+
+public:
+ AES_KEY m_Context;
+ byte_t m_ctr_buf[RNG_BLOCK_SIZE];
+ Mutex m_Lock;
+
+ h__RNG()
+ {
+ memset(m_ctr_buf, 0, RNG_BLOCK_SIZE);
+ byte_t rng_key[RNG_KEY_SIZE];
+
+ { // this block scopes the following AutoMutex so that it will be
+ // released before the call to set_key() below.
+ AutoMutex Lock(m_Lock);
+
+#ifdef KM_WIN32
+ 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;
+
+ Result_t result = URandom.OpenRead(DEV_URANDOM);
+
+ if ( KM_SUCCESS(result) )
+ {
+ ui32_t read_count;
+ result = URandom.Read(rng_key, RNG_KEY_SIZE, &read_count);
+ }
+
+ if ( KM_FAILURE(result) )
+ DefaultLogSink().Error("Error opening random device: %s\n", DEV_URANDOM);
+
+#endif // KM_WIN32
+ } // end AutoMutex context
+
+ set_key(rng_key);
+ }
+
+ //
+ void
+ set_key(const byte_t* key_fodder)
+ {
+ assert(key_fodder);
+ byte_t sha_buf[20];
+ SHA_CTX SHA;
+ SHA1_Init(&SHA);
+
+ SHA1_Update(&SHA, (byte_t*)&m_Context, sizeof(m_Context));
+ SHA1_Update(&SHA, key_fodder, RNG_KEY_SIZE);
+ SHA1_Final(sha_buf, &SHA);
+
+ AutoMutex Lock(m_Lock);
+ AES_set_encrypt_key(sha_buf, RNG_KEY_SIZE_BITS, &m_Context);
+ *(ui32_t*)(m_ctr_buf + 12) = 1;
+ }
+
+ //
+ void
+ fill_rand(byte_t* buf, ui32_t len)
+ {
+ assert(len <= MAX_SEQUENCE_LEN);
+ ui32_t gen_count = 0;
+ AutoMutex Lock(m_Lock);
+
+ while ( gen_count + RNG_BLOCK_SIZE <= len )
+ {
+ AES_encrypt(m_ctr_buf, buf + gen_count, &m_Context);
+ *(ui32_t*)(m_ctr_buf + 12) += 1;
+ gen_count += RNG_BLOCK_SIZE;
+ }
+
+ if ( len != gen_count ) // partial count needed?
+ {
+ byte_t tmp[RNG_BLOCK_SIZE];
+ AES_encrypt(m_ctr_buf, tmp, &m_Context);
+ memcpy(buf + gen_count, tmp, len - gen_count);
+ }
+ }
+};
+
+
+static h__RNG* s_RNG = 0;
+
+
+//------------------------------------------------------------------------------------------
+//
+// Fortuna public interface
+
+Kumu::FortunaRNG::FortunaRNG()
+{
+ if ( s_RNG == 0 )
+ s_RNG = new h__RNG;
+}
+
+Kumu::FortunaRNG::~FortunaRNG() {}
+
+//
+const byte_t*
+Kumu::FortunaRNG::FillRandom(byte_t* buf, ui32_t len)
+{
+ assert(buf);
+ assert(s_RNG);
+ const byte_t* front_of_buffer = buf;
+
+ while ( len )
+ {
+ // 2^20 bytes max per seeding, use 2^19 to save
+ // room for generating reseed values
+ ui32_t gen_size = xmin(len, MAX_SEQUENCE_LEN);
+ s_RNG->fill_rand(buf, gen_size);
+ buf += gen_size;
+ len -= gen_size;
+
+ // re-seed the generator
+ byte_t rng_key[RNG_KEY_SIZE];
+ s_RNG->fill_rand(rng_key, RNG_KEY_SIZE);
+ s_RNG->set_key(rng_key);
+ }
+
+ return front_of_buffer;
+}
+
+//
+const byte_t*
+Kumu::FortunaRNG::FillRandom(Kumu::ByteString& Buffer)
+{
+ FillRandom(Buffer.Data(), Buffer.Capacity());
+ Buffer.Length(Buffer.Capacity());
+ 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, c_2, c_b;
+ BN_init(&c_2powb); BN_init(&c_2); BN_init(&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_xkey, bn_x_n;
+ BN_init(&bn_tmp); BN_init(&bn_xkey); BN_init(&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_CTX_free(ctx1);
+}
+
+//
+// end KM_prng.cpp
+//