Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inverse function of Java's Random function

Tags:

java

random

Java's Random function takes a seed and produces the a sequence of 'psuedo-random' numbers. (It is implemented based on some algorithm discussed in Donald Knuth, The Art of Computer Programming, Volume 3, Section 3.2.1.), but the article is too technical for me to understand)

Is there an inverse function of it? That is, given a sequence of numbers, would it be possible to mathematically determine what the seed would be? (, which means, brute-forcing doesn't count as a valid method)

[Edit] There seems to be quite a number of comments here... I thought I'd clarify what I am looking for.

So for instance, the function y = f(x) = 3x has an inverse function, which is y = g(x) = x/3.

But the function z = f(x, y) = x * y does not have an inverse function, because (I could give a full mathematical proof here, but I don't want to sidetrack my main question), intuitively speaking, there are more than one pair of (x, y) such that (x * y) == z.

Now back to my question, if you say the function is not inversible, please explain why.

(And I am hoping to get answers from those who have really read to article and understand it. Answers like "It's just not possible" aren't really helping)

like image 774
One Two Three Avatar asked Mar 05 '13 23:03

One Two Three


People also ask

What is math random () method?

Math.random() The Math.random() function returns a floating-point, pseudo-random number that's greater than or equal to 0 and less than 1, with approximately uniform distribution over that range — which you can then scale to your desired range.

What is math random () in Java?

random() method returns a pseudorandom double type number greater than or equal to 0.0 and less than 1.0. . When this method is first called, it creates a single new pseudorandom-number generator, exactly as if by the expression new java. util. Random.

How do you write inverse functions in Java?

There is no automatic "find the inverse" method in Java. However, you are of course free to implement the inverse function yourself. For many real-world functions their inverses are well documented and/or easily calculated with the right algorithm. Save this answer.

How do you generate a random number between 1 to 10 in Java?

Java Random number between 1 and 10 Below is the code showing how to generate a random number between 1 and 10 inclusive. Random random = new Random(); int rand = 0; while (true){ rand = random. nextInt(11); if(rand != 0) break; } System.


2 Answers

If we're talking about the Oracle (née Sun) implementation of java.util.Random, then yes, it is possible once you know enough bits.

Random uses a 48-bit seed and a linear congruential generator. These are not cryptographically safe generators, because of the tiny state size (bruteforceable!) and the fact that the output just isn't that random (many generators will exhibit small cycle length in certain bits, meaning that those bits can be easily predicted even if the other bits seem random).

Random's seed update is as follows:

nextseed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1) 

This is a very simple function, and it can be inverted if you know all the bits of the seed by calculating

seed = ((nextseed - 0xBL) * 0xdfe05bcb1365L) & ((1L << 48) - 1) 

since 0x5DEECE66DL * 0xdfe05bcb1365L = 1 mod 248. With this, a single seed value at any point in time suffices to recover all past and future seeds.

Random has no functions that reveal the whole seed, though, so we'll have to be a bit clever.

Now, obviously, with a 48-bit seed, you have to observe at least 48 bits of output or you clearly don't have an injective (and thus invertible) function to work with. We're in luck: nextLong returns ((long)(next(32)) << 32) + next(32);, so it produces 64 bits of output (more than we need). Indeed, we could probably make do with nextDouble (which produces 53 bits), or just repeated calls of any other function. Note that these functions cannot output more than 248 unique values because of the seed's limited size (hence, for example, there are 264-248longs that nextLong will never produce).

Let's specifically look at nextLong. It returns a number (a << 32) + b where a and b are both 32-bit quantities. Let s be the seed before nextLong is called. Then, let t = s * 0x5DEECE66DL + 0xBL, so that a is the high 32 bits of t, and let u = t * 0x5DEECE66DL + 0xBL so that b is the high 32 bits of u. Let c and d be the low 16 bits of t and u respectively.

Note that since c and d are 16-bit quantities, we can just bruteforce them (since we only need one) and be done with it. That's pretty cheap, since 216 is only 65536 -- tiny for a computer. But let's be a bit more clever and see if there's a faster way.

We have (b << 16) + d = ((a << 16) + c) * 0x5DEECE66DL + 11. Thus, doing some algebra, we obtain (b << 16) - 11 - (a << 16)*0x5DEECE66DL = c*0x5DEECE66DL - d, mod 248. Since c and d are both 16-bit quantities, c*0x5DEECE66DL has at most 51 bits. This usefully means that

(b << 16) - 11 - (a << 16)*0x5DEECE66DL + (k<<48) 

is equal to c*0x5DEECE66DL - d for some k at most 6. (There are more sophisticated ways to compute c and d, but because the bound on k is so tiny, it's easier to just bruteforce).

We can just test all the possible values for k until we get a value whos negated remainder mod 0x5DEECE66DL is 16 bits (mod 248 again), so that we recover the lower 16 bits of both t and u. At that point, we have a full seed, so we can either find future seeds using the first equation, or past seeds using the second equation.

Code demonstrating the approach:

import java.util.Random;  public class randhack {     public static long calcSeed(long nextLong) {         final long x = 0x5DEECE66DL;         final long xinv = 0xdfe05bcb1365L;         final long y = 0xBL;         final long mask = ((1L << 48)-1);          long a = nextLong >>> 32;         long b = nextLong & ((1L<<32)-1);         if((b & 0x80000000) != 0)             a++; // b had a sign bit, so we need to restore a         long q = ((b << 16) - y - (a << 16)*x) & mask;         for(long k=0; k<=5; k++) {             long rem = (x - (q + (k<<48))) % x;             long d = (rem + x)%x; // force positive             if(d < 65536) {                 long c = ((q + d) * xinv) & mask;                 if(c < 65536) {                     return ((((a << 16) + c) - y) * xinv) & mask;                 }             }         }         throw new RuntimeException("Failed!!");     }      public static void main(String[] args) {         Random r = new Random();         long next = r.nextLong();         System.out.println("Next long value: " + next);         long seed = calcSeed(next);         System.out.println("Seed " + seed);         // setSeed mangles the input, so demangle it here to get the right output         Random r2 = new Random((seed ^ 0x5DEECE66DL) & ((1L << 48)-1));         System.out.println("Next long value from seed: " + r2.nextLong());     } } 
like image 50
nneonneo Avatar answered Sep 20 '22 13:09

nneonneo


I normally wouldn't just link articles... But I found a site where someone looks into this in some depth and thought it was worth posting. http://jazzy.id.au/default/2010/09/20/cracking_random_number_generators_part_1.html

It seems that you can calculate a seed this way:

seed = (seed * multiplier + addend) mod (2 ^ precision) 

where multiplier is 25214903917, addend is 11, and precision is 48 (bits). You can't calculate what the seed was with only 1 number, but you can with 2.

EDIT: As nhahtdh said there's a part 2 where he delves into more of the math behind the seeds.

like image 33
Memento Mori Avatar answered Sep 18 '22 13:09

Memento Mori