我查看了相当公平的随机数字,并且发现了这个网站:https://dicesites.com/provably-fair
首先,什么类应该用于服务器端哈希?有很多哈希算法,如SHA512,SHA256或SHA384Cng,我不明白它们之间的区别。
其次,将使用什么方法将未散列种子转换为散列种子,以及在散列创建期间将用于将用户提供的种子的字符串用于何种方法。另外,是否只是在用户提供的字符串末尾添加了nonce以防止重复哈希?
第三,我不明白为什么散列服务器种子最初是SHA256散列,但后来用于计算HMAC SHA512散列。
最后,将用于将最终生成的哈希的前5个字符转换为卷号的内容是什么?
我没有找到使用服务器种子和客户端种子的任何随机数生成器的任何示例,只有像System.Security.Cryptography.RandomNumberGenerator
这样的东西。
答案 0 :(得分:3)
您链接的页面描述了该过程,但我将尝试深入了解更多详细信息并提供C#示例。
首先有两个哈希发生。一个通用哈希来证明服务器在你赌博时没有改变服务器密钥,这个哈希不是秘密的,并且在游戏开始时被提供给玩家。还有一个键控哈希(称为HMAC)来实际生成骰子卷,并使用服务器密钥,用户提供的数据和计数的数字的组合。
以下是发生的过程:
int
,然后检查int
是否大于999999,如果是,它会一直重复直到找到一个不超过999999的数字。number%(10000)/100.0
上获取浮点数。用户一旦从步骤10获得密钥,就可以使用SHA256对其进行散列,并检查他是否获得了在播放会话开始时告知的相同哈希值。然后,他可以重新执行服务器现在执行的所有步骤,即他拥有密钥并验证服务器是否没有伪造任何骰子。
如何在代码中执行此操作:
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace SandboxConsole
{
public class Result
{
public Result(string hmacMessage, float roll)
{
HmacMessage = hmacMessage;
Roll = roll;
}
public string HmacMessage { get; }
public float Roll { get; }
}
class FairDiceRollServer
{
private byte[] _serverKey;
private ulong _nonce;
public byte[] StartSession()
{
if (_serverKey != null)
throw new InvalidOperationException("You must call EndSession before starting a new session");
//Generate a new server key.
using (var rng = RandomNumberGenerator.Create())
{
_serverKey = new byte[128];
rng.GetBytes(_serverKey);
}
_nonce = 0;
//Hash the server key and return it to the player.
using (var sha = SHA256.Create())
{
return sha.ComputeHash(_serverKey);
}
}
public Result RollDice(string userKey)
{
if(_serverKey == null)
throw new InvalidOperationException("You must call StartSession first");
if(_nonce == ulong.MaxValue)
throw new InvalidOperationException("Ran out of Nonce values, you must start a new session.");
using (var hmac = new HMACSHA256(_serverKey))
{
float? roll = null;
string message = null;
while (roll == null)
{
message = userKey + "-" + _nonce;
_nonce++;
var data = Encoding.UTF8.GetBytes(message);
var hash = hmac.ComputeHash(data);
roll = GetNumberFromByteArray(hash);
}
return new Result(message, roll.Value);
}
}
private float? GetNumberFromByteArray(byte[] hash)
{
var hashString = string.Join("", hash.Select(x => x.ToString("X2")));
const int chars = 5;
for (int i = 0; i <= hashString.Length - chars; i += chars)
{
var substring = hashString.Substring(i, chars);
var number = int.Parse(substring, System.Globalization.NumberStyles.HexNumber);
if(number > 999999)
continue;
return (number % 10000) / 100.0f;
}
return null;
}
public byte[] EndSession()
{
var key = _serverKey;
_serverKey = null;
return key;
}
}
}
使用中的示例
using System;
using System.Linq;
namespace SandboxConsole
{
class Program
{
private int _test;
static void Main(string[] args)
{
var server = new FairDiceRollServer();
var hash = server.StartSession();
Console.WriteLine(string.Join("", hash.Select(x => x.ToString("X2"))));
for (int i = 0; i < 10; i++)
{
var roll = server.RollDice("My Key");
Console.WriteLine("Message: {0} Result: {1}", roll.HmacMessage, roll.Roll);
}
var key= server.EndSession();
Console.WriteLine(string.Join("", key.Select(x => x.ToString("X2"))));
Console.ReadLine();
}
}
}
使用已发布的有关所使用的algorthom的信息,RollDice
返回的信息以及从EndSession
返回给用户的密钥,用户可以重新创建所有骰子卷并证明服务器真正做到了随机生成(感谢用户提供的数据,服务器不允许选择),而不是一些预先确定会导致丢失的伪造预选密钥。