2013-08-16 52 views
2

使用加密庫我可以要求庫給我一些隨機字節。問題是這個字節是從0到255每個(包含)的數字,因此創建一個均勻分佈的結果並不那麼簡單。使用node.js生成10的冪模數的cryptographc隨機數

我的意思是以下內容:

的函數接收數N,其中是10,100,... 10^b,其中b是1和8之間(一個數量可以大,但我不需要任何更大的數字)並返回一個介於0和給定數字(不包括給定數字)之間的數字,所以假設N是100,函數的結果是從0到99,如果N是10結果是從0到9.

您可以使用Math.random創建一個隨機數,然後乘以N然後使用floor。但是,Math.random不是加密安全的,所以必須使用隨機生成的2^8m數​​字來完成,其中m只是給予crypto.randomBytes的任意數量的字節。

我創建了一個簡單的函數,顯然是工作。但是,我知道,在隨機數字中引入一些偏倚是相當容易的,我只希望對它進行驗證,因爲這對項目來說有些重要。

genera_aleatorio_residuo_potencia10 : function (n, cb) { 
    var digitos = Math.log(n)/Math.LN10; 
    var extra_base2 = digitos > 8 ? digitos - 8 : 0; 
    if (Math.floor(digitos + .4) - digitos > 0.00000001) { 
    return cb("Numero no es potencia de 10 (10, 100, 1000...)", null); 
    } 
    digitos = Math.round(digitos); 
    async.parallel({ 
    r1 : crypto_helper.generador_random_bytes(1), 
    r2 : crypto_helper.generador_random_bytes(1) 
    }, function (err, res) { 
    if (err) { 
     return cb(err, null); 
    } 
    var r1 = res.r1[0] + 1; 
    var r2 = res.r2[0] + 1; 
    var aleatorio = (Math.pow(5, digitos) - 1) * Math.pow(2, extra_base2) * r1 + r2; 
    cb(null, aleatorio % n); 
    }); 
} 

不消說:crypto_helper.generador_random_bytes是node.js的crypto.randomBytes,我經常使用,使之與異步庫友好的包裝。

我對使用Math.pow(5,digitos)和Math.pow(2,extra_base2)的推理是N和256之間的最小公倍數。在實踐中,n永遠不會大於100000000,所以我們的產品不應該使用Math.pow(2,extra_base2),但我仍然希望確保它對其他人有意義。

+0

這裏您的最終目標是什麼?我喜歡加密問題,如果我能理解你想要做什麼,我會完全沉浸在這個問題中。 – naomik

+0

我會在幾分鐘內描述它,此刻我很匆忙。感謝您的關注。我發現使用的公式中存在一個錯誤,所以我需要更新它。 – Mamsaac

回答

1

我通過簡單地模仿Java的SecureRandom.nextInt(int)在這裏做的事情發現了一個很好的解決方案:SecureRandom.nextInt(int)只要我可以編寫代碼(我現在很忙)就會發布代碼。我打算使用我開發的代碼,因爲我確認提出的解決方案存在偏見(根本不可接受)。

這裏是JDK代碼的改編。注意31位數字的限制,因爲我意識到JS在32位補碼上完成所有按位運算。 因爲我不會使用它,所以我沒有實現2個數字的特殊情況。解決方案是一般的數字,而不僅是10的權力......我相信對於基數爲10的數字必須有更好的解決方案,但無論如何。我重寫了代碼,不使用我的庫並使用英文編寫,以便其他人可以更輕鬆地使用它。

var crypto_random_number_range = function (n, cb) { 
    //result is a number from 0 a n-1 
    //Javascript works with 32 bits for bitwise operations but these are signed (2-complement), so it is good to limit the size of n 
    if (n <= 0 || n > 2147483647) { 
    return cb("n must be larger than 0 and smaller than 2147483647", null); 
    } 
    var bits, val; 
    async.doWhilst(
    function (cb2) { 
     crypto.randomBytes(4, function (err, rbytes) { 
     if (err) { 
      return cb2(err); 
     } 
     bits = ((rbytes[3] & 0x7f) << 24) + 
      (rbytes[2] << 16) + (rbytes[1] << 8) + rbytes[0]; 
     val = bits % n; 
     cb2(); 
     }); 
    }, function() { 
     return (bits - val + (n-1)) < 0; 
    }, function (err) { 
     if (err) { 
     return cb(err, null); 
     } 
     return cb(null, val); 
    } 
); 
} 

做了幾個測試,它似乎工作得很好。

+1

我爲Java Card編寫了同樣的東西,工作也很好。至少爲隨機數的分佈創建測試很重要。請注意,雖然這是一個比直接測試範圍更好的解決方案 - 但它不是最優的(特別是關於數字略高於最大值的一半,其中大約一半的循環將失敗)。然後再次,它可能足夠好,編程非常快速和簡單。 –

+0

我對這個發行版做了一些測試(發現了一個愚蠢的錯誤,當我輕率地編寫它時),它似乎工作得很好。事實上,我在該方法中找到的文檔估計最差的平均情況是2次迭代。在做測試時,我發現它在大多數情況下只執行一次迭代。感謝您的反饋意見! – Mamsaac

+1

是的,大多數情況下,第一次嘗試是可靠的,但如果您有一個字節,值爲0..255,並且您請求128或稍多一些的數字,則它變得不那麼有效。這通常只是一個問題,如果你的RNG非常慢,或者如果你需要一個範圍內的隨機數字(當然是+1) –

-3
var kazutsukuru = function kazutsukuru(kaketeiruno, kotae) { 
    crypto.randomBytes(4, function(mondai, baito) { 
    if (mondai) { 
     return kotae(mondai); 
    } 

    kotae(null, Math.floor(baito.readUInt32BE(0)/4294967296 * kaketeiruno)); 
    }); 
}; 

這應該做你想做的。我決定讓我的答案難以閱讀,因爲你的問題很難閱讀。