先上代码
const random = require('./im_random');
/*
* 首先,服务器生成一个随机数,返回 seed_hash 、 nonce 给客户端
*/
let server = random.getHash();
console.log(`
服务器 >
| 种子哈希: ${server.seed_hash}
| 随机数: ${server.nonce}
`);
/*
* 客户端也提供一个种子
*/
let client = {
seed: 'awa'
}; // client input
/*
* 然后服务器根据自己的种子、客户端的种子、以及生成的随机数计算出 roll
*/
let roll = random.getRoll(server.seed, client.seed, server.nonce);
/*
* 此时公开服务器 seed 给客户端校验
*/
console.log(`
生产 >
| 服务器 >
| | 种子: ${server.seed}
| | 随机数: ${server.nonce}
| 客户端 >
| | 种子: ${client.seed}
| 结果:${roll}
`)
/*
* 客户端校验
*/
let real = random.verify(server.seed, server.seed_hash);
console.log(real ? `
--- 验证 > 有效
服务器先前公布的种子哈希 ${server.seed_hash}
与现在公布的种子 ${server.seed}
> 哈希正确 < 服务器没有伪造结果
` : `
--- 验证 > 无效
服务器先前公布的种子哈希 ${server.seed_hash}
与现在公布的种子 ${server.seed}
> 哈希错误 < 服务器可能伪造结果
`);
/*
* 网页在线校验
*/
console.log(`
第三方验证页面 >
https://s3.amazonaws.com/roll-verifier/verify.html?server_seed=${server.seed}&client_seed=${client.seed}&server_seed_hash=${server.seed_hash}&nonce=${server.nonce}
`);
然后这个库(放在上面代码的同一目录下,记得修改名称成 im-random.js
)
const crypto = require("crypto");
function getHash() {
let seed = crypto.randomBytes(32).toString('hex');
let seed_hash = crypto.createHash('sha256').update(seed).digest('hex');
let nonce = parseInt(crypto.randomBytes(2).toString('hex'), 16);
return {seed, seed_hash, nonce};
}
function getRoll(server_seed, client_seed, nonce) {
let string1 = `${nonce}:${server_seed}:${nonce}`;
let string2 = `${nonce}:${client_seed}:${nonce}`;
let hmac512 = crypto.createHmac('sha512', string2).update(string1).digest('hex');
let string3 = hmac512.substring(0,8);
let number = parseInt(string3, 16);
return (Math.round(number/429496.7295)).toFixed(0);
}
function verify(seed, seed_hash) {
return crypto.createHash('sha256').update(seed).digest('hex') === seed_hash;
}
module.exports = { getHash, getRoll, verify };
(库没有直接用 Math.random() 是因为这个 Math.random() 已经是可预测的了,不安全,所以使用 parseInt(crypto.randomBytes(2).toString('hex'), 16);
这样子的方法生成随机数)
最后 npm install crypto
and enjoy!
服务器 >
| 种子哈希: 103ed122ae2ce0ab1ef6e52509de30acc9210e1dc7513d1498785da869f64d78
| 随机数: 42938
生产 >
| 服务器 >
| | 种子: e165903210223f8e67b00e6fb8519663257371ddafa74002f3d3e5d6baeac1c0
| | 随机数: 42938
| 客户端 >
| | 种子: awa
| 结果:3831
--- 验证 > 有效
服务器先前公布的种子哈希 103ed122ae2ce0ab1ef6e52509de30acc9210e1dc7513d1498785da869f64d78
与现在公布的种子 e165903210223f8e67b00e6fb8519663257371ddafa74002f3d3e5d6baeac1c0
> 哈希正确 < 服务器没有伪造结果
第三方验证页面 >
https://s3.amazonaws.com/roll-verifier/verify.html?server_seed=e165903210223f8e67b00e6fb8519663257371ddafa74002f3d3e5d6baeac1c0&client_seed=awa&server_seed_hash=103ed122ae2ce0ab1ef6e52509de30acc9210e1dc7513d1498785da869f64d78&nonce=42938
第三方验证页面打开后点击页面 VERIFY!
结果:

可以看见显示了……
3831
和前面控制台输出的一样,说明服务器没有作弊,然后这个出来的数字看起来是最大 10,000
这种方法意味着服务器无法通过选择对他们有利的数字来欺骗您
@James 强烈建议用于刮刮乐、抽奖、签到等,看谁说不公平 