Node.js - 如何使用crypto.randomBytes生成特定范围内的随机数

Nic*_*olo 10 random math cryptography node.js

如何使用crypto.randomBytes生成特定范围内的随机数?

我希望能够像这样生成一个随机数:

console.log(random(55, 956)); // where 55 is minimum and 956 is maximum
Run Code Online (Sandbox Code Playgroud)

我只能在随机函数内使用crypto.randomBytes来为这个范围生成随机数.

我知道如何将生成的字节从randomBytes转换为十六进制或十进制,但我无法弄清楚如何在数学上从随机字节中获取特定范围内的随机数.

Mus*_*amg 19

要生成特定范围内的随机数,可以使用以下等式

Math.random() * (high - low) + low
Run Code Online (Sandbox Code Playgroud)

但是你想使用crypto.randomBytes而不是Math.random()这个函数返回一个随机生成的字节的缓冲区.反过来,您需要将此函数的结果从字节转换为十进制.这可以使用biguint格式包完成.要安装此软件包,只需使用以下命令:

npm install biguint-format --save
Run Code Online (Sandbox Code Playgroud)

现在你需要将crypto.randomBytes的结果转换为十进制,你可以这样做:

var x= crypto.randomBytes(1);
return format(x, 'dec');
Run Code Online (Sandbox Code Playgroud)

现在您可以创建随机函数,如下所示:

var crypto = require('crypto'),
    format = require('biguint-format');

function randomC (qty) {
    var x= crypto.randomBytes(qty);
    return format(x, 'dec');
}
function random (low, high) {
    return randomC(4)/Math.pow(2,4*8-1) * (high - low) + low;
}
console.log(random(50,1000));
Run Code Online (Sandbox Code Playgroud)


Set*_*eth 6

crypto包现在有了一个randomInt()功能。它是在 v14.10.0 和 v12.19.0 中添加的。

console.log(crypto.randomInt(55, 957)); // where 55 is minimum and 956 is maximum
Run Code Online (Sandbox Code Playgroud)

上限是排他的。

这是(删节的)实现:

// Largest integer we can read from a buffer.
// e.g.: Buffer.from("ff".repeat(6), "hex").readUIntBE(0, 6);
const RAND_MAX = 0xFFFF_FFFF_FFFF;

const range = max - min;

const excess = RAND_MAX % range;
const randLimit = RAND_MAX - excess;

while (true) {
  const x = randomBytes(6).readUIntBE(0, 6);
  // If x > (maxVal - (maxVal % range)), we will get "modulo bias"
  if (x > randLimit) {
    // Try again
    continue;
  }
  const n = (x % range) + min;
  return n;
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅完整源代码官方文档


Nic*_*olo 5

感谢@Mustafamg 的回答和@CodesInChaos 的巨大帮助,我设法解决了这个问题。我做了一些调整并将范围增加到最大 256^6-1 或 281,474,976,710,655。范围可以增加更多,但您需要为大整数使用额外的库,因为 256^7-1 超出 Number.MAX_SAFE_INTEGER 限制。

如果有人有同样的问题,请随时使用它。

var crypto = require('crypto');

/*
Generating random numbers in specific range using crypto.randomBytes from crypto library
Maximum available range is 281474976710655 or 256^6-1
Maximum number for range must be equal or less than Number.MAX_SAFE_INTEGER (usually 9007199254740991)
Usage examples:
cryptoRandomNumber(0, 350);
cryptoRandomNumber(556, 1250425);
cryptoRandomNumber(0, 281474976710655);
cryptoRandomNumber((Number.MAX_SAFE_INTEGER-281474976710655), Number.MAX_SAFE_INTEGER);

Tested and working on 64bit Windows and Unix operation systems.
*/

function cryptoRandomNumber(minimum, maximum){
	var distance = maximum-minimum;
	
	if(minimum>=maximum){
		console.log('Minimum number should be less than maximum');
		return false;
	} else if(distance>281474976710655){
		console.log('You can not get all possible random numbers if range is greater than 256^6-1');
		return false;
	} else if(maximum>Number.MAX_SAFE_INTEGER){
		console.log('Maximum number should be safe integer limit');
		return false;
	} else {
		var maxBytes = 6;
		var maxDec = 281474976710656;
		
		// To avoid huge mathematical operations and increase function performance for small ranges, you can uncomment following script
		/*
		if(distance<256){
			maxBytes = 1;
			maxDec = 256;
		} else if(distance<65536){
			maxBytes = 2;
			maxDec = 65536;
		} else if(distance<16777216){
			maxBytes = 3;
			maxDec = 16777216;
		} else if(distance<4294967296){
			maxBytes = 4;
			maxDec = 4294967296;
		} else if(distance<1099511627776){
			maxBytes = 4;
			maxDec = 1099511627776;
		}
		*/
		
		var randbytes = parseInt(crypto.randomBytes(maxBytes).toString('hex'), 16);
		var result = Math.floor(randbytes/maxDec*(maximum-minimum+1)+minimum);
		
		if(result>maximum){
			result = maximum;
		}
		return result;
	}
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,它运行良好,您可以将其用作非常好的随机数生成器,但我严格不建议将此功能用于任何加密服务。如果您愿意,请自行承担风险。

欢迎所有评论、推荐和批评!