Clean up some hash and RNG code

This commit is contained in:
Ilari Liusvaara 2013-12-22 05:48:58 +02:00
parent b6143a6125
commit 42f8862283
7 changed files with 213 additions and 83 deletions

View file

@ -23,7 +23,7 @@ public:
/**
* Construct.
*/
request_hash(const std::string& _id, const uint8_t* _key, unsigned _nonce, skein_hash _h,
request_hash(const std::string& _id, const uint8_t* _key, unsigned _nonce, skein::hash _h,
const uint8_t* _prereq)
: id(_id), nonce(_nonce), h(_h)
{
@ -46,7 +46,7 @@ public:
uint8_t pubkey[32];
uint8_t prereq[8];
unsigned nonce;
skein_hash h;
skein::hash h;
};
/**
* Create a new instance.

View file

@ -3,10 +3,22 @@
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
struct skein_hash
namespace skein
{
/**
* Skein hash function (v1.3).
*/
struct hash
{
/**
* Variant to use (256-bit, 512-bit, 1024-bit)
*/
enum variant { PIPE_256, PIPE_512, PIPE_1024 };
/**
* Data type for piece of data.
*/
enum datatype
{
T_KEY = 0,
@ -16,9 +28,39 @@ struct skein_hash
T_NONCE = 20,
T_MESSAGE = 48
};
skein_hash(variant v, uint64_t outbits);
void write(const uint8_t* data, size_t datalen, datatype type = T_MESSAGE);
void read(uint8_t* output);
/**
* Create a new hash state.
*
* Parameter v: The variant to use.
* Parameter outbits: Number of output bits.
* Throws std::runtime_error: Variant is invalid.
*/
hash(variant v, uint64_t outbits) throw(std::runtime_error);
/**
* Write data to be hashed.
*
* Parameter data: The data to append.
* Parameter datalen: Number of bytes in data.
* Parameter type: The data type. Must be monotonically increasing.
* Throws std::runtime_error: Types not monotonic, or invalid type.
*
* Note: Data types 4 (CONFIG) and 63 (OUTPUT) are not allowed.
*/
void write(const uint8_t* data, size_t datalen, datatype type = T_MESSAGE) throw(std::runtime_error);
/**
* Read the output hash.
*
* Parameter output: Buffer to store the output to.
*/
void read(uint8_t* output) throw();
/**
* Read partial output hash.
*
* Parameter output: Buffer to store the output to.
* Parameter startblock: The block number (each block is 256/512/1024 bits depending on variant) to start from.
* Parameter bits: Number of bits to output.
*/
void read_partial(uint8_t* output, uint64_t startblock, uint64_t bits) throw();
private:
void typechange(uint8_t newtype);
void configure();
@ -34,5 +76,42 @@ private:
int8_t last_type;
};
/**
* Skein PRNG.
*/
struct prng
{
public:
/**
* Construct a PRNG.
*
* Note: To seed the PRNG, write the initial seed there.
*/
prng() throw();
/**
* (Re)seed the PRNG and mark it seeded.
*
* Parameter buffer: Buffer to read the seed from.
* Parameter size: Number of bytes in seed.
*/
void write(const void* buffer, size_t size) throw();
/**
* Read data from PRNG.
*
* Parameter buffer: Buffer to write the data to.
* Parameter size: Number of random bytes to write.
* Throws std::runtime_error: Generator is not seeded.
*/
void read(void* buffer, size_t size) throw(std::runtime_error);
/**
* Is seeded?
*/
bool is_seeded() const throw();
private:
uint8_t state[128];
bool _is_seeded;
};
}
#endif

View file

@ -99,7 +99,7 @@ namespace
std::ifstream fp(path, std::ios::binary);
if(!fp)
throw std::runtime_error("Can't open dh25519 keyfile");
skein_hash h(skein_hash::PIPE_512, 256);
skein::hash h(skein::hash::PIPE_512, 256);
while(true) {
char buf[4096];
fp.read(buf, sizeof(buf));

View file

@ -14,6 +14,7 @@
#include "library/loadlib.hpp"
#include "library/sha256.hpp"
#include "library/string.hpp"
#include "library/skein.hpp"
#include "library/serialization.hpp"
#include "library/arch-detect.hpp"
@ -40,7 +41,7 @@
namespace
{
std::string rseed;
skein::prng prng;
uint64_t rcounter = 0;
bool reached_main_flag;
mutex_class seed_mutex;
@ -85,41 +86,31 @@ namespace
const int slots = 32;
static unsigned count = 0;
static uint64_t last_reseed = 0;
static uint64_t buf[slots];
static uint64_t buf[slots + 1];
buf[count++] = arch_get_tsc();
umutex_class h(seed_mutex);
if(count == slots || buf[count - 1] - last_reseed > 300000000) {
last_reseed = buf[count - 1];
std::vector<char> x;
x.resize(rseed.length() + slots * 8 + 8);
std::copy(rseed.begin(), rseed.end(), x.begin());
for(unsigned i = 0; i < slots; i++)
serialization::u64l(&x[rseed.length() + 8 * i], buf[i]);
serialization::u64l(&x[rseed.length() + 8 * slots], arch_get_random());
rseed = "32 " + sha256::hash(reinterpret_cast<uint8_t*>(&x[0]), x.size());
buf[slots] = arch_get_random();
prng.write(buf, sizeof(buf));
count = 0;
}
}
std::string get_random_hexstring_64(size_t index)
{
std::ostringstream str;
{
umutex_class h(seed_mutex);
str << rseed << " ";
str << time(NULL) << " ";
str << arch_get_tsc() << " ";
str << arch_get_random() << " ";
str << arch_get_random() << " ";
str << arch_get_random() << " ";
str << arch_get_random() << " ";
str << (rcounter++) << " " << index;
}
std::string s = str.str();
std::vector<char> x;
x.resize(s.length());
std::copy(s.begin(), s.end(), x.begin());
return sha256::hash(reinterpret_cast<uint8_t*>(&x[0]), x.size());
umutex_class h(seed_mutex);
uint64_t buf[6];
uint8_t out[32];
buf[0] = time(NULL);
buf[1] = arch_get_tsc();
buf[2] = arch_get_random();
buf[3] = arch_get_random();
buf[4] = arch_get_random();
buf[5] = arch_get_random();
prng.write(buf, sizeof(buf));
prng.read(out, sizeof(out));
return hex::b_to(out, sizeof(out));
}
std::string collect_identifying_information()
@ -203,11 +194,10 @@ std::string get_random_hexstring(size_t length) throw(std::bad_alloc)
void set_random_seed(const std::string& seed) throw(std::bad_alloc)
{
std::ostringstream str;
str << seed.length() << " " << seed;
std::vector<char> x(seed.begin(), seed.end());
{
umutex_class h(seed_mutex);
rseed = str.str();
prng.write(&x[0], x.size());
}
rrdata.set_internal(random_rrdata());
}
@ -218,9 +208,9 @@ void set_random_seed() throw(std::bad_alloc)
{
std::ifstream r("/dev/urandom", std::ios::binary);
if(r.is_open()) {
char buf[64];
r.read(buf, 64);
std::string s(buf, 64);
char buf[128];
r.read(buf, sizeof(buf));
std::string s(buf, sizeof(buf));
set_random_seed(s);
return;
}
@ -228,7 +218,7 @@ void set_random_seed() throw(std::bad_alloc)
//If libgcrypt is available, use that.
#ifdef USE_LIBGCRYPT_SHA256
{
char buf[64];
char buf[128];
gcry_randomize((unsigned char*)buf, sizeof(buf), GCRY_STRONG_RANDOM);
std::string s(buf, sizeof(buf));
set_random_seed(s);

View file

@ -262,18 +262,18 @@ dh25519_http_auth::request_hash dh25519_http_auth::start_request(const std::stri
_nonce = nonce++;
sprintf(buf, "%u", _nonce);
skein_hash hp(skein_hash::PIPE_512, 64);
hp.write(ssecret, 32, skein_hash::T_KEY);
hp.write((const uint8_t*)personalization.c_str(), personalization.length(), skein_hash::T_PERSONALIZATION);
hp.write(pubkey, 32, skein_hash::T_PUBKEY);
hp.write((uint8_t*)buf, strlen(buf), skein_hash::T_NONCE);
skein::hash hp(skein::hash::PIPE_512, 64);
hp.write(ssecret, 32, skein::hash::T_KEY);
hp.write((const uint8_t*)personalization.c_str(), personalization.length(), skein::hash::T_PERSONALIZATION);
hp.write(pubkey, 32, skein::hash::T_PUBKEY);
hp.write((uint8_t*)buf, strlen(buf), skein::hash::T_NONCE);
hp.read(prereq);
skein_hash h(skein_hash::PIPE_512, 256);
h.write(ssecret, 32, skein_hash::T_KEY);
h.write((const uint8_t*)personalization.c_str(), personalization.length(), skein_hash::T_PERSONALIZATION);
h.write(pubkey, 32, skein_hash::T_PUBKEY);
h.write((uint8_t*)buf, strlen(buf), skein_hash::T_NONCE);
skein::hash h(skein::hash::PIPE_512, 256);
h.write(ssecret, 32, skein::hash::T_KEY);
h.write((const uint8_t*)personalization.c_str(), personalization.length(), skein::hash::T_PERSONALIZATION);
h.write(pubkey, 32, skein::hash::T_PUBKEY);
h.write((uint8_t*)buf, strlen(buf), skein::hash::T_NONCE);
return request_hash(id, pubkey, _nonce, h, prereq);
}

View file

@ -4,11 +4,14 @@
#include <iostream>
#include <stdexcept>
#include <iomanip>
#include "skein512c.inc"
#include "arch-detect.hpp"
#include "hex.hpp"
#include "minmax.hpp"
namespace skein
{
//Jerry Solinas was not here.
#include "skein512c.inc"
static uint8_t bitmasks[] = {0, 128, 192, 224, 240, 248, 252, 254, 255};
@ -32,6 +35,28 @@ static void show_array(const char* prefix, const uint8_t* a, size_t e)
std::cerr << std::endl;
}
inline static void to_words(uint64_t* out, const void* in, size_t words)
{
#ifdef ARCH_IS_I386
memcpy(out, in, words<<3);
#else
for(unsigned i = 0; i < words; i++)
out[i]=0;
for(unsigned i = 0; i < (words<<3); i++)
out[i>>3]|=((uint64_t)reinterpret_cast<uint8_t*>(in)[i] << ((i&7)<<3));
#endif
}
inline static void to_bytes(void* out, const uint64_t* in, size_t bytes)
{
#ifdef ARCH_IS_I386
memcpy(out, in, bytes);
#else
for(size_t i = 0; i < bytes; i++)
output[i] = (out[i>>3] >> ((i&7)<<3));
#endif
}
inline static void _skein256_compress(uint64_t* a, const uint64_t* b, const uint64_t* c, const uint64_t* d)
{
#ifdef TEST_SKEIN_CODE
@ -71,7 +96,7 @@ inline static void _skein1024_compress(uint64_t* a, const uint64_t* b, const uin
#endif
}
skein_hash::skein_hash(skein_hash::variant v, uint64_t _outbits)
hash::hash(hash::variant v, uint64_t _outbits) throw(std::runtime_error)
{
memset(chain, 0, sizeof(chain));
memset(buffer, 0, sizeof(buffer));
@ -88,7 +113,7 @@ skein_hash::skein_hash(skein_hash::variant v, uint64_t _outbits)
last_type = -1;
}
void skein_hash::configure()
void hash::configure()
{
uint64_t config[16] = {0x133414853ULL,outbits};
uint64_t tweak[2] = {32,0xC400000000000000ULL};
@ -98,7 +123,7 @@ void skein_hash::configure()
last_type = 4;
}
void skein_hash::typechange(uint8_t newtype)
void hash::typechange(uint8_t newtype)
{
if(last_type != newtype) {
//Type change.
@ -121,7 +146,7 @@ void skein_hash::typechange(uint8_t newtype)
}
}
void skein_hash::write(const uint8_t* data, size_t datalen, skein_hash::datatype type)
void hash::write(const uint8_t* data, size_t datalen, hash::datatype type) throw(std::runtime_error)
{
if(type < 0 || type == 4 || type > 62)
throw std::runtime_error("Invalid data type to write");
@ -145,7 +170,7 @@ void skein_hash::write(const uint8_t* data, size_t datalen, skein_hash::datatype
}
}
void skein_hash::flush_buffer(uint8_t type, bool final)
void hash::flush_buffer(uint8_t type, bool final)
{
uint64_t _buffer[16];
uint64_t _buffer2[16];
@ -159,14 +184,7 @@ void skein_hash::flush_buffer(uint8_t type, bool final)
tweak[1] += (1ULL << 62);
if(final)
tweak[1] += (1ULL << 63);
#ifdef ARCH_IS_I386
memcpy(_buffer, buffer, fullbuffer);
#else
for(unsigned i = 0; i < (fullbuffer>>3); i++)
_buffer[i]=0;
for(unsigned i = 0; i < fullbuffer; i++)
_buffer[i>>3]|=((uint64_t)buffer[i] << ((i&7)<<3));
#endif
to_words(_buffer, buffer, fullbuffer >> 3);
compress(_buffer2, _buffer, chain, tweak);
memcpy(chain, _buffer2, fullbuffer);
data_low += bufferfill;
@ -176,7 +194,7 @@ void skein_hash::flush_buffer(uint8_t type, bool final)
memset(buffer, 0, fullbuffer);
}
void skein_hash::read(uint8_t* output)
void hash::read_partial(uint8_t* output, uint64_t startblock, uint64_t bits) throw()
{
typechange(63); //Switch to output.
//The final one is special.
@ -184,25 +202,68 @@ void skein_hash::read(uint8_t* output)
uint64_t out[16];
uint64_t tweak[2] = {8,0xFF00000000000000ULL};
uint64_t offset = 0;
for(uint64_t i = 0; i < outbits; i += (fullbuffer<<3)) {
zeroes[0] = startblock;
for(uint64_t i = 0; i < bits; i += (fullbuffer<<3)) {
compress(out, zeroes, chain, tweak);
zeroes[0]++;
uint64_t fullbytes = (outbits - i) >> 3;
if(fullbytes > fullbuffer) fullbytes = fullbuffer;
#ifdef ARCH_IS_I386
memcpy(output + offset, out, fullbytes);
#else
for(unsigned i = 0; i < fullbytes; i++)
output[offset + i] = (out[i>>3] >> ((i&7)<<3));
#endif
if(fullbytes < fullbuffer && i + 8 * fullbytes < outbits) {
uint64_t fullbytes = min((bits - i) >> 3, static_cast<uint64_t>(fullbuffer));
to_bytes(output + offset, out, fullbytes);
if(fullbytes < fullbuffer && i + 8 * fullbytes < bits) {
output[offset + fullbytes] = (out[fullbytes>>3] >> ((fullbytes&7)<<3));
output[offset + fullbytes] &= bitmasks[outbits&7];
output[offset + fullbytes] &= bitmasks[bits&7];
}
offset += fullbuffer;
}
}
void hash::read(uint8_t* output) throw()
{
read_partial(output, 0, outbits);
}
prng::prng() throw()
{
_is_seeded = false;
memset(state, 0, 128);
}
void prng::write(const void* buffer, size_t size) throw()
{
hash h(hash::PIPE_1024, 1024);
h.write(state, 128, hash::T_NONCE);
h.write(reinterpret_cast<const uint8_t*>(buffer), size, hash::T_MESSAGE);
h.read(state);
if(size > 0)
_is_seeded = true;
}
void prng::read(void* buffer, size_t size) throw(std::runtime_error)
{
if(!_is_seeded)
throw std::runtime_error("PRNG is not initialized");
//We can't use skein itself here, but the underlying compression function.
uint64_t chain[16] = {0};
uint64_t zeroes[16] = {0};
uint64_t out[16];
uint64_t tweak[2] = {8,0xFF00000000000000ULL};
to_words(chain, state, 16);
zeroes[0] = 1;
for(uint64_t i = 0; i < size; i += 128) {
_skein1024_compress(out, zeroes, chain, tweak);
zeroes[0]++;
uint64_t fullbytes = min(size - i, static_cast<uint64_t>(128));
to_bytes(reinterpret_cast<uint8_t*>(buffer) + i, out, fullbytes);
}
zeroes[0] = 0;
_skein1024_compress(out, zeroes, chain, tweak);
to_bytes(state, out, 128);
}
bool prng::is_seeded() const throw()
{
return _is_seeded;
}
}
#ifdef TEST_SKEIN_CODE
#define SKEIN_DEBUG
#include <skein.h>
@ -215,9 +276,9 @@ int main(int argc, char** argv)
/*
//skein_DebugFlag = SKEIN_DEBUG_STATE | SKEIN_DEBUG_TWEAK | SKEIN_DEBUG_INPUT_64;
uint8_t out[128];
skein_hash ctx(skein_hash::PIPE_512, 256);
ctx.write((uint8_t*)argv[1], strlen(argv[1]), skein_hash::T_KEY);
ctx.write((uint8_t*)argv[2], strlen(argv[2]), skein_hash::T_MESSAGE);
skein::hash ctx(skein::hash::PIPE_512, 256);
ctx.write((uint8_t*)argv[1], strlen(argv[1]), skein::hash::T_KEY);
ctx.write((uint8_t*)argv[2], strlen(argv[2]), skein::hash::T_MESSAGE);
ctx.read(out);
show_array("New: ", out, 32);
Skein_512_Ctxt_t ctx2;
@ -235,9 +296,9 @@ int main()
uint8_t buf[129] = {0xFF,0xFE,0xFD,0xFC,0xFB,0xFA,0xF9,0xF8,0xF7};
uint8_t key[135] = {0x05,0x04,0x46,0x22,0x26,0x35,0x63,0x26,0xFF};
uint8_t out[128];
skein_hash ctx(skein_hash::PIPE_256, 256);
ctx.write(key, sizeof(key), skein_hash::T_KEY);
ctx.write(key, sizeof(key), skein_hash::T_NONCE);
skein::hash ctx(skein::hash::PIPE_256, 256);
ctx.write(key, sizeof(key), skein::hash::T_KEY);
ctx.write(key, sizeof(key), skein::hash::T_NONCE);
ctx.write(buf, 2);
ctx.read(out);
show_array("", out, 32);

View file

@ -172,7 +172,7 @@ void wxeditor_uploadtarget::generate_dh25519(wxCommandEvent& e)
std::vector<char> x;
x.resize(entropy.length());
std::copy(entropy.begin(), entropy.end(), x.begin());
skein_hash h(skein_hash::PIPE_1024, 1024);
skein::hash h(skein::hash::PIPE_1024, 1024);
h.write((uint8_t*)&x[0], x.size());
h.read((uint8_t*)rbuf + 64);
{