0
$\begingroup$

I'm trying to obtain a random, unpredictable quite long number (± 20-25 digits) using Javascript (created by user's client) as fast and light as possible. Is this solution reliable, robust and secure enough?

When the online page is opened, the 13-digit timestamp is stored. A timer determines the number of milliseconds before user clicks on 'OK' (let's assume he got a short text to read or anything else to do). A collection of 100 invisible 'pixels' (1*1px HTML spans) is created with randomly fixed initial RGBA colors (A=0=transparency).

 let d = Date.now(); // timestamp 13 digits
 let n = 100         // number of 'pixels' 
 let pixels = ''

 for (i=0; i<n; i++) {
    let r =  Math.floor(Math.random() * 256); 
    let g =  Math.floor(Math.random() * 256); 
    let b =  Math.floor(Math.random() * 256);
    let c =  'rgba('+r+','+g+','+b+',0)'
    pixels += '<span id="pix'+i+'" style="background-color:'+c+'"></span>'
 }

Once it's done we randomly change the color of each 'pixel' every 100th of a second

 let changeColor = setInterval(function(){
    for (i=0; i<n; i++) {
        let r =  Math.floor(Math.random() * 256); 
        let g =  Math.floor(Math.random() * 256); 
        let b =  Math.floor(Math.random() * 256); 
        let c =  'rgba('+r+','+g+','+b+',0)'
        document.getElementById('pix'+i).style.backgroundColor = c
    }
 },10);

When user clicks on 'OK' the function is stopped, a random pixel is determined and its RGB values are found

let x =  Math.floor(Math.random() * n);  // 39
let px = window.getComputedStyle(document.getElementById('pix'+x),null).backgroundColor
let rgb = px.match(/\d+/g);  // pix 39 = [21,13,152]

Then we multiply each RGB value by x * a random factor [1 to 5]:

let r = rgb[0]*(x*Math.floor(Math.random() * 5)+1), // 21 * 39 * 2 = 1638
    g = rgb[1]*(x*Math.floor(Math.random() * 5)+1), // 13 * 39 * 1 = 507
    b = rgb[2]*(x*Math.floor(Math.random() * 5)+1)  // 152 * 39 * 4 = 23712

By adding x + the timer + a random extract of the timestamp we obtain:

let t = Date.now()-d;                       // timer on click
let p = Math.floor(Math.random() * 4)+3;    
let z = d.toString().substr(-p)             // the xx last timestamp value (3->7)

let val = x+''+r+g+b+''+t+z                 // 39 1368 507 23712 1348 55601

Then we randomly shuffle the result:

function shuffle(a) {
  let r = a.length, temp, rand;
  while (0 !== r) {
    rand = Math.floor(Math.random() * r);
    r -= 1;
    temp = a[r];
    a[r] = a[rand];
    a[rand] = temp;
  }
  return a;   // 39136850723712134855601 -> 25851963017738613021534
}

  Tests -> console
  17:22:34 pix #39 = [21,13,152] 9348234523267751239843
  17:22:42 pix #39 = [21,13,152] 109715237240854257137
  17:23:02 pix #39 = [21,13,152] 100889146450039658553439

If we need a longer result (50, 100 digits) we can create 500 'pixels' and randomly choose 10 of them instead of one. Or add an entropy value (mouse's movement on screen : https://www.grc.com/r&d/js.htm) to the obtained value. What do you think?

$\endgroup$
4
  • 2
    $\begingroup$ From: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…: Note: Math.random() does not provide cryptographically secure random numbers. Do not use them for anything related to security. Use the Web Crypto API instead, and more precisely the window.crypto.getRandomValues() method. $\endgroup$ Jun 29 at 16:57
  • $\begingroup$ The problem with this scheme is that Math.random() doesn't supply random numbers; usually it is generated using a 32 bit seed that may be easy to predict. So the only input that seems entirely random is the time between the calculations starting and the user hitting [OK]. Oh, and you include a Date(), which is not that random at all. How random this is depends more on the system than anything else I suppose. I would at least expect that somebody that can program a script to start the calculation at certain time & almost instantaneously could force it to be very non-random. $\endgroup$
    – Maarten Bodewes
    Jun 29 at 17:23
  • $\begingroup$ This would be much more random $\endgroup$
    – Maarten Bodewes
    Jun 29 at 17:25
  • $\begingroup$ Using colored pixels doesn't provide any functionality above storing those values in an array. $\endgroup$
    – bmm6o
    Jun 29 at 20:39
1
$\begingroup$

Math.random is not a cryptographically secure PRNG. For example, Firefox uses an XorShift-based generator, which will leak its entire state after just a few calls. Therefore, you're not really creating random pixels here; you're just picking values based on the same previous seed.

MDN describes the Web Cryptography API function for generating cryptographically secure random numbers. If you're working in Node.js instead, it comes with built-in functions which do the same thing. These are designed to implement CSPRNGs, usually the system one, which you should use unless you are certain you need something else. They will be suitable for almost all cryptographic needs, and using the system CSPRNG is strongly recommended by most cryptographers.

If you needed something that was reproducible, you could use something like ChaCha20 with a key and nonce generated from the system CSPRNG, or an HMAC-DRBG with SHA-2 or SHA-3. However, this is not necessary in most situations, and it's better when possible to avoid implementing your own cryptography in favor of known, trusted implementations, so if you did need this, you'd be better off using a known implementation.

$\endgroup$
4
  • $\begingroup$ Thanks for answer and comments. I agree for the weakness of Math.random but beyond the positions of principle, does it make these results obtained predictable and therefore not secure. If so, why and how? Thank you for the help in understanding, I'm nothing but a beginner in cryptography ;-) $\endgroup$
    – Wolden
    Jun 29 at 18:54
  • $\begingroup$ as shown in my last 3 lines (tests->console) once the function is stopped, timestamp and pixel are defined. I've clicked a lot of times on the OK button till I've found 3 results for the same pix (#39): with the same RGB values I obtain 3 very different results. Are they predictable (i.e in a predictable 'list of possibles results')? $\endgroup$
    – Wolden
    Jun 29 at 19:26
  • 1
    $\begingroup$ They are probably predicable enough that this approach is subject to brute force. Just because it "looks random" doesn't mean that it isn't predictable. I'm not going to do a full cryptanalysis on this approach because I think using the system CSPRNG is obviously better and is the logical choice, but if you use Math.random as a random source where you need a CSPRNG, I will request a CVE against your software. $\endgroup$
    – bk2204
    Jun 29 at 19:31
  • $\begingroup$ Thanks for explanations :-) $\endgroup$
    – Wolden
    Jun 29 at 21:19
0
$\begingroup$

I've used above 6 times the Math.random() function, one to get a 0->255 value (r,g,b), one to get a 0->100 value (pixel number), and some others to get smaller values (i.e 3->5 for timestamp)

The 0->255 (r,g,b) value is now taken from

    let rand = new Uint8Array(3);           // 3 vals 0 -> 255
    window.crypto.getRandomValues(rand);
    let r =  rand[0]; 
    let g =  rand[1]; 
    let b =  rand[2];

and the 0->100 value (pixel number) from

    let pix = new Uint8Array(1);
    window.crypto.getRandomValues(pix);
    let v =  Math.floor(pix[0]/2.55);       // val 0->100

Once I've replaced all the Math.random() functions with these new implementations, do you think it is more secure and reliable/resistant to attacks?

$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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