3

Is there an asymmetric cryptographically secure pseudo random number generator in which the public key only goes backward? For example, I'd like to generate 5,000 digits, then provide the public key and RNG state to another party. Then they'd generate the same digits backward from 5,000 to 1. The second party should not be able to calculate the digits from 5,001 onward.

Edit: Here's a working demo of DannyNiu's RSA approach.

skibulk
  • 133
  • 4
  • 2
    This might be an XY question. What's the problem you're trying to solve? – forest Jun 02 '19 at 03:29
  • 1
    @forest Well I am envisioning a game where players unlock randomly generated content. A lot of it. It wouldn't be ideal to store the definitions for that content in a database because of storage limitations. Being able to give them a key and let them generate the content dynamically would bypass that problem. They can't go forward because they could cheat. – skibulk Jun 02 '19 at 05:07
  • Note that, depending on your exact needs, you might be able to use a plain old iterated cryptographic hash function instead. Sure, you can only iterate those in one direction, but if you know you'll only ever need a limited number (say, less than a million) of pseudorandom numbers, you can just start at the end of the sequence and work backwards, generating each number by hashing the one the follows it in the sequence. And if you cached the value of, say, every 1000th number in a database, this might still be faster than using RSA. – Ilmari Karonen Jun 03 '19 at 22:49
  • (Ps. A fancier caching scheme should let you generate $n$ hashes one at a time in reverse order using only $O(n \log n)$ time and $O(\log n)$ storage.) – Ilmari Karonen Jun 03 '19 at 22:58
  • @IlmariKaronen So you're saying I could start with a seed X and apply a cryptographic hash to it. Then take output Yi from the cryptographic hash and re-hash it using the same function (generating output Yi+1...). Repeat up to say, 1 million times, caching the output Yi+j every 1K iterations. Then I load the cached output and iterate them up to 1000 times, at which point I can share the cached hashes and allow the user to iterate them on their own? – skibulk Jun 03 '19 at 23:52
  • 1
    @skibulk: Yes, if I understood you correctly. Basically, choose $a_0$ at random and generate $a_1, \dots, a_n$ as e.g. $a_i = \text{SHA-256}(a_{i-1})$. Then, if you send $a_k$ to the user (for some $0 \le k \le n$), they can generate $a_k, \dots, a_n$ on their own, but they can't generate $a_j$ for any $j < k$ until you send them $a_{k'}$ for some $k' \le j$. The (optional) caching is just so you don't have to start the iteration over from $a_0$ again every time you want to reveal another (earlier) element of the sequence. – Ilmari Karonen Jun 04 '19 at 08:18

1 Answers1

2

Disclaimer: I've not verified the security of my proposal.

You can achieve that in two ways: one pre-quantum, using RSA-like scheme, another post-quantum, using multivariate system.

  1. The RSA version.

Generate an RSA key pair, keep the private key $(N,d)$ to yourself, give the public key $(N,e)$ to the second party.

To generating new numbers, calculate $x_{i+1} = x_i^d \pmod N$. This way, the second party can go backwards from known $x_i$ by calculating $x_{i-1} = x_i^e \pmod N$

  1. The multivariate version.

This version is significantly more heavy-weight than the RSA version. Although there are several multivariate signature schemes in the NIST PQC "Competition" (it seems difficult to build secure encryption scheme using MV), many of them uses exotic methods to compress the public key, which may be optional depending on your use-case.

In its simple form, you have a public multivariate system $P$ which is the composition of private components $S \circ F \circ T$ where $F$ is a easily solvable quadratic system, and $S$ and $T$ are linear.

To generate new random numbers,

  1. Calculate $u = S^{-1}(x_i)$

  2. Solve $u = F(v)$ and choose arbitrary $v$

  3. Calculate $x_{i+1} = T^{-1}(v)$

The second party can reverse it with $x_{i-1} = P(x_i)$

As mentioned by poncho in the comment, the solved $v$ may not be a unique solution, but that's not a problem because any generated $x_{i+1}$ satisfies $x_i = P(x_{i+1})$.

Edit

It seems you're not quite familiar with the notations.

In $x_{i\pm1}$, the $i\pm1$ is the subscript, so in terms familiar to programmers, it's x[i+1] or x[i-1].

$x_i$ is the current state of the PRNG. Since that is the output of some arithmetic operations, its bit pattern is not fully uniformly random (i.e. may fail some sophisticated statistic tests), so you may need to truncate it and/or hash it, to obtain some quality random bits - hashing is also done by the TLS protocol for the same reason.

And yes, it's an iterative generator, but I'm not sure what you mean by "algebraic", and why you believe it'd be inefficient in the second comment.

DannyNiu
  • 9,207
  • 2
  • 24
  • 57
  • Thanks DannyNiu. If I'm understanding correctly, the "±1" is not actually factored into the equation? It is just a label that indicates we're referring to the next or previous state X. What is the proper way to initialize the PRNG state? Will any number between 0 and the RSA Modulus work? I have posted a demo below, but it isn't working quite yet. – skibulk Jun 03 '19 at 03:16
  • 1
    One problem with multivariate schemes is that they don't actually implement bijections; instead, in the public direction, there will be multiple preimages that map to the same postimage (and hence in the private direction, a possible preimage will often have multiple possible postimages). That may make you CSPRNG problematic to make it work in the backwards direction, even with the private key – poncho Jun 03 '19 at 03:18
  • 1
    @skibulk Yes, it's just a label. And any number between 2 and the RSA modulus will work, 0 and 1 will not make your PRNG advance. – DannyNiu Jun 03 '19 at 03:24
  • @DannyNiu: my understanding of the problem was that, when going backwards, you recover the previous states, not just a set of plausible ones... – poncho Jun 03 '19 at 11:53
  • @poncho, Exactly, the operation with the MQ public key has a closed outcome AFAIK, where as the operation with the private key has an open outcome. The question doesn't ask for a kleptographic CSPRNG, so it's not the private key that recovers the previous states. – DannyNiu Jun 03 '19 at 12:19
  • 2
    Oh, I misunderstood your construction; however, the fundamental issue still remains (just in the opposite direction of what I original thought); $F$ isn't a permutation, and so what do you do if $F^{-1}(u)$ doesn't exist? – poncho Jun 03 '19 at 12:29
  • @DannyNiu, I like the RSA approach! I know that you haven't verified it but I was wondering but do you think that finding given a list $x^i$ of currently generated value, finding $x_{i+j}$ for some $j > 1$ is necessarily as hard as finding $x_{i+1}$? – Marc Ilunga Jun 03 '19 at 23:09
  • You mentioned truncating and hashing the current state to "obtain some quality bits". Beside the fact the the modulus limits the first digit in the current state to whatever the first digit in the modulus is, is there any reason to hash the current state? Doing that seems like an extra step for no reason. – skibulk Jun 04 '19 at 00:04
  • @MarcIlunga There is a known case where $j=\phi(N)$ is trivial, but even that requires knowing the factorization of $N$, so I assume it's generally as hard. – DannyNiu Jun 04 '19 at 01:15