Hacker News new | ask | show | jobs
by altern 3839 days ago
Somewhat off-topic I guess, but aren't PRNG, hashing and (symmetric) encryption in some sense all the same thing? If you have some magic, keyed mixing function that permutes a block of data without leaking information about the key, it seems like you can implement any of the three operations.

In oversimplified pseudo-code:

    def encrypt(data, key):
        counter = 0
        for block in data:
            yield block ^ mix(counter, key)
            counter += 1

    def generate(key):
        counter = 0
        while True:
            yield mix(counter, key)
            counter += 1

    def hash(data, key):
        state = 0
        for block in data:
            state = block ^ mix(state, key)
        return state
And similarly, if you have any one of those functions you can implement the rest. Xor with an unpredictable stream of numbers is a good encryption method, encrypting a string of zeroes should give you unpredictable random numbers and the hash can be used as the mix function when implementing the others, etc.
2 comments

Your keyed hash has the following vulnerability: if I want to create a message with a given hash, I can simply take an arbitrary message, compute target_hash ^ mix(current_hash, key), and append that block.

More fundamentally it's keyed, and finding a way to make this work for an unkeyed hash is somewhat more complicated. But yes, I'm pretty sure that a secure stream cipher and a secure deterministic CSPRNG are basically the same thing.

They're all kind of the same idea. As you note, each has some magic mixing function to get some output that's (hopefully) entirely uncorrelated to your input, but the specifics are a bit different.

When you encrypt something, the output is the same size as the input. The key is not mutated. You have to be able to go the other way if you know the key (for symmetric encryption).

When you hash something, the input is variable but the output length is fixed. Typically this is one-way - you should not be able to reconstruct the input for a given output.

When you have a stateful PRNG you get a fixed-length output but you have some internal state that gets changed with every call. So its inputs are generate(state) and you yield output and newstate. Going the other way is possible in theory but not typical.

The state for PRNGs is usually larger than the amount of output yielded, sometimes by quite a lot. E.g. Mersenne Twister has about 2.5 KB of state but yields only 32 bits of output per iteration. You generally want to store newstate every time, because setting them up is often expensive (MT takes a lot of calls before it's fully initialized and providing good randomness) and iterating through N-1 steps every time can take a very long time in a long-running program.

Usually you want PRNGs to be fast (but strongly random), but encryption and hashing should sometimes be slow (eg to protect passwords in a DB, or as a proof-of-work as in Bitcoin/Hashcash). It all depends on what role you're using them in.

You can sorta compose them into each other - like you can make a stateful PRNG by doing a hash on some internal state and then mixing some of it back into the state (eg like RDRAND). Or by encrypting a counter with a seed to make a PRNG (like Random123). Here be dragons, though - there's nothing guaranteeing your hand-rolled encryption algorithm will actually resist an attack, or whatever. So stay with a known, tested implementation.