Suppose I have some ZK proofs that were turned non-interactive using Fiat-Shamir heuristic. So I need to generate the challenge value deterministically using some data shared between the prover and the verifier. Different proofs require different challenges of varying size, so using a fixed-output hash is not very convenient. I can:

  • Feed the shared data to an XOF hash (e.g. SHAKE256), and generate however many output bytes I need to turn it into the challenge.
  • Feed the shared data to a fixed-output hash, use that output to seed an RNG (e.g. something from the ChaCha family), and use the RNG to generate however many output bytes I need.

Is there any difference between these approaches from the security perspective? I would like to use the latter approach, since it is easier programmatically: many entities I use have a from_rng() type constructor, but there may be some non-obvious pitfalls with that. (Of course, I can make an RNG-like object out of an XOF hash, but that feels somehow wrong, because I'd be covertly substituting one cryptographic primitive with another)

Something that may or may not be the case:

  • a hash function will have a "less random" output than an RNG, so the generated values will not be uniform;
  • a hash function does not allow one to deduce the seed value from the output, while an RNG might allow that (or am I mistaken here?).

2 Answers 2


More from a programming / pragmatic perspective: I don't like using seeded CSPRNG's to supply deterministic values. That's generally not what they are designed for. They may skip values (buffering), re-seed, reorder bytes etc. without breaking their API promise to provide random values. In short, they may be deterministic when they are executed, but implementation changes may not guarantee that the results are reproducible.

I don't think the two issues you brought up are valid; XOF's should produce output that is indistinguishable from random. That implies a uniform random distribution.

A good CSPRNG should not allow the seed to be known. It should provide backtracking resistance as well as forward secrecy and many other properties. In general a CSPRNG should not allow the internal state to get known, and recovering the seed will allow all state to be known (until a reseed happens). This is notably not the case for PRNG's in general, only to those that are considered cryptographically secure.

The difference between a XOF and CSPRNG is relatively small. In general, if you want a configurable hash output to derive a specific reproducible value of a specific size then use a XOF. If you want to extract an undetermined amount of random values that depend on a secret seed rather than an external entropy source then use a CSPRNG.

Using ChaCha family - a stream cipher, which can theoretically be considered a CSPRNG - falls in between these. It requires a specific key / seed size and doesn't allow reseeding. After that it produces a reproducible output, and you can extract as much data as required. So at that point the most important difference with a XOF are the input requirements and the fact that you don't need to specify the output size in advance.

I expect that if you have to use it in a "random generator" parameter that you'll have to provide an unchanging implementation in either case. That can be tricky if the existing API of the "random generator" is complex.

Due to the fact that it looks like you require a hash value of a configurable but pre-determined size and because the seed is variable size as well I'd normally go for a XOF. However, if the preexisting random generator API requires indetermined amount of output (over different calls) to be generated then ChaCha might be a better out of practical concerns.

  • $\begingroup$ The APIs of RNGs and XOF hashes are actually almost identical in my ecosystem (Rust), but they are distinct, and most third-party libraries provide convenient constructors out of an RNG but not out of an XOF hash. As I mentioned I could write an adapter from one to the other easily, but I'm not sure it's a good idea from the transparency perspective. But an important thing you said is that an RNG does not guarantee consistency, so I think in my case an XOF will be a safer choice. $\endgroup$
    – fjarri
    Commented Aug 15, 2023 at 23:01
  • $\begingroup$ (what I mean is that even though ChaCha may guarantee consistency, the RNG API in general doesn't, so it signals the wrong intent to a possible reader/auditor of the code) $\endgroup$
    – fjarri
    Commented Aug 15, 2023 at 23:03
  • $\begingroup$ Right, I think we're in sync :) For your use case I would go for the XOF unless the amount of output is undetermined, as a XOF won't do that. $\endgroup$
    – Maarten Bodewes
    Commented Aug 16, 2023 at 8:21

seed() function is used to initialise to generate random number where as XOF hash is used for creating hash of variable lengths and in creating encryption keys.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.