Random-Number-Generator

Javascript中的CSPRNG?

  • July 6, 2021

我正在嘗試使用 Javascript(由使用者的客戶端創建)盡可能快速和輕便地獲得一個隨機的、不可預測的相當長的數字(± 20-25 位)。這個解決方案是否足夠可靠、強大和安全?

打開線上頁面時,會儲存 13 位時間戳。計時器確定使用者點擊“確定”之前的毫秒數(假設他有一個短文本要閱讀或其他任何事情要做)。使用隨機固定的初始 RGBA 顏色(A=0=透明度)創建了 100 個不可見“像素”(1*1px HTML 跨度)的集合。

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>'
}

完成後,我們每 100 秒隨機更改每個“像素”的顏色

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);

當使用者點擊“確定”時,該功能停止,確定一個隨機像素並找到其 RGB 值

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]

然後我們將每個 RGB 值乘以 x * 一個隨機因子

$$ 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

通過添加 x + 計時器 + 我們獲得的時間戳的隨機提取:

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

然後我們隨機打亂結果:

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

如果我們需要更長的結果(50、100 位),我們可以創建 500 個“像素”並隨機選擇其中的 10 個而不是 1 個。或者將熵值(滑鼠在螢幕上的移動:https ://www.grc.com/r&d/js.htm )添加到獲得的值。你怎麼看?

Math.random不是加密安全的 PRNG。例如,Firefox 使用基於 XorShift 的生成器,它會在幾次呼叫後洩漏其整個狀態。因此,您並沒有真正在這裡創建隨機像素。您只是根據相同的先前種子選擇值。

MDN 描述了用於生成加密安全隨機數的 Web Cryptography API 函式。如果您使用的是 Node.js,它會附帶執行相同操作的內置函式。這些旨在實現 CSPRNG,通常是系統之一,除非您確定需要其他東西,否則您應該使用它。它們將適用於幾乎所有的密碼需求,並且大多數密碼學家強烈推薦使用系統 CSPRNG。

如果您需要可重現的東西,您可以使用 ChaCha20 之類的東西,它帶有從系統 CSPRNG 生成的密鑰和隨機數,或者帶有 SHA-2 或 SHA-3 的 HMAC-DRBG。但是,在大多數情況下,這不是必需的,最好盡可能避免實現自己的加密技術,而支持已知的、受信任的實現,因此,如果您確實需要這個,最好使用已知的實現。

我已經使用了 6 次以上 Math.random() 函式,一次獲得 0->255 值(r,g,b),一次獲得 0->100 值(像素數),還有一些獲得獲得更小的值(即時間戳為 3->5)

0->255 (r,g,b) 值現在取自

   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];

和 0->100 值(像素數)來自

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

一旦我用這些新實現替換了所有 Math.random() 函式,你認為它更安全、更可靠/更能抵抗攻擊嗎?

引用自:https://crypto.stackexchange.com/questions/91808