Background
I am designing a system which enables the development of dynamic authentication schemes for a user of static web content. The motivation is to pre-generate large amounts of complex-to-generate, yet sensitive web-content, and then serve it statically with cookie-based (embedding reversably encrypted information) authentication in place, enforced by the web-server inately. Using an AEAD-mode encryption primitive.
The Problem
I need to generate IVEC's and keys that are valid for a duration of time, say one week (the current-valid pair). and that past IVECs/Keys are also valid for say 2 weeks(historically-valid) and any data encrypted with the historically valid secrets will just be re-encrypted with the current-valid IVEC/KEY.
What I need is a deterministic CSPRNG that seeds of a random number and a passphrase and that can produce in an indexed fashion 64-bit or 128-bit blocks of numbers. If I use a weeks-since-"jan 1 1970" as one of the index element of my hypothetical CSPRNG I should be able to build a system that innately changes keys automatically as time goes by.
Approach I am Considering
Now I don't see such functionality in cryptopp, or I do now know the terminology well enough, and as cryptopp is the most advanced of the encryption libraries out there, I don't have confidence I will find another one. So, If I can't find an implementation out there, I should roll my own. Will generating a static string structure out of the concatinated data and then hashing it (shown below) do the trick ?
RIPEMD160(RandomPreGeneratedFixedNonce:PassPhrase:UInt64SinceEpoch:128BitBlockIndexNumber);
Note: The blocknumbers will be assigned and have a regular structure, so for example for a 128-bit digest, the first 64-bits of block 0 will be for the ivec, and all of element 1 for the 128-bit key.
Is this a sound approach (--.i.e, cryptographically secure) ?
-- edit: post accept comment --
After some reflection, I have decided to merge what I originally considered the passphrase and the nonce/salt into a 16-byte (cryptographicall strong) key, and use the techniques outlined in the PKCS #5 to derive multiple time-based keys. There isn't a need for a salt, as passphrases aren't used.
Interesting question.
First, your Initial Vectors don't have to be cryptographically strong random quantities, but they should be unique per-message. The IV is really just a kind of salt value that ensures that similar messages encrypted using the same key don't look similar once they're encrypted. You can use any quick pseudo-random generator to generate the IV, and then send it (preferably encrypted) along with the encrypted data.
The keys, of course, should be as strong as you can practically make them.
Your proposal to hash a text string containing a nonce, passphrase, and validity data seems to me to be very reasonable -- it's broadly in line with what is done by other system that use a passphrase to generate a key. You should hash more many times -- not just once -- to make the key generation computationally expensive (which will be a bigger problem for anyone trying to brute-force the key than it will for you).
You might also want to have a look at the key-generation scheme set out in PKCS#5 (e.g. at http://www.faqs.org/rfcs/rfc2898.html) which is implemented in cryptopp as PasswordBasedKeyDerivationFunction. This mechanism is already widely used and known to be reasonable secure (note that PKCS#5 recommends hashing the passphrase data at least 1000 times). You could just append your validity period and index data to the passphrase and use PasswordBasedKeyDerivationFunction as it stands.
You don't say what encryption algorithm you propose to use to encrypt the data, but I would suggest that you should pick something widely-used and known to be secure ... and in particular I'd suggest that you use AES. I'd also suggest using one of the SHA digest functions (maybe as an input to PasswordBasedKeyDerivationFunction). SHA-2 is current, but SHA-1 is sufficient for key generation purposes.
You also don't say what key length you're looking to generate, but you should be aware that the amount of entropy in your keys depends on the length of the passphrase that you use, and unless the passphrase is very long that will be much less than the keylength ideally requires.
The weakest link in this scheme is the passphrase itself, and that's always going to limit the level of security you can achive. As long as you salt your data (as you are doing) and make the key-generation expensive to slow down brute-force attacks you should be fine.
What I need is a deterministic CSPRNG that seeds of a random number and a passphrase and that can produce in an indexed fashion 64-bit or 128-bit blocks of numbers. If I use a weeks-since-"jan 1 1970" as one of the index element of my hypothetical CSPRNG I should be able to build a system that innately changes keys automatically as time goes by.
Well, I think part of the solution is to use a non-time based generator. That way, if both sides start with the same seed, then they both produce the same random stream. You can layer your "weeks since Week 1, 1970" logic on top of that.
To do that, you would use OFB_mode<T>::Encryption
. It can be used as a generator because OFB mode uses AdditiveCipherTemplate<T>
, which derives from RandomNumberGenerator
.
In fact, Crpyto++ uses the generator in test.cpp
so that results can be reproduced if something fails. Here's how you would use OFB_mode<T>::Encryption
. It also applies to CTR_Mode<T>::Encryption
:
SecByteBlock seed(32 + 16);
OS_GenerateRandomBlock(false, seed, seed.size());
for(unsigned int i = 0; i < 10; i++)
{
OFB_Mode<AES>::Encryption prng;
prng.SetKeyWithIV(seed, 32, seed + 32, 16);
SecByteBlock t(16);
prng.GenerateBlock(t, t.size());
string s;
HexEncoder hex(new StringSink(s));
hex.Put(t, t.size());
hex.MessageEnd();
cout << "Random: " << s << endl;
}
The call to OS_GenerateRandomBlock
fetches bytes from /dev/{u|s}random
and then uses that as a simulated shared seed. Each run of the program will be different. Within each run of the program, it prints similar to the following:
$ ./cryptopp-test.exe
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
There's another generator available that does the same, but its not part of the Crypto++ library. Its called AES_RNG
, and its based on AES-256. Its a header only implementation, and you can find it at the Crypto++ wiki under RandomNumberGenerator.
Also see the topic Reproducibility for RandomNumberGenerator
class on the Crypto++ wiki.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With