Javascript中的CSPRNG?
我正在嘗試使用 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() 函式,你認為它更安全、更可靠/更能抵抗攻擊嗎?