2017-06-12 66 views
2

我查看了可證明的公平隨機數,並且遇到了這個網站:https://dicesites.com/provably-fair如何在C#中生成可證明的公平骰子卷?

首先,應該使用什麼類的服務器雙面散列?有很多像SHA512,SHA256或SHA384Cng這樣的散列算法,我不明白它們之間的區別。

其次,將使用什麼方法將未經哈希的種子轉換爲經哈希的種子,以及在哈希創建過程中將使用何種方法將用戶提供的種子字符串考慮在內。另外,是否在用戶提供的字符串末尾添加了nonce以防止重複的哈希?

第三,我不明白爲什麼哈希服務器種子最初是SHA256哈希,但後來用於計算HMAC SHA512哈希。

最後,將最後生成的散列的前5個字符轉換爲卷號的操作是什麼?

我找不到任何使用服務器種子和客戶端種子的隨機數生成器的例子,只有像System.Security.Cryptography.RandomNumberGenerator這樣的東西。

+4

請問每個問題* 1 *問題,而不是4。請參閱[Meta](https://meta.stackexchange.com/questions/39223/one-post-with-multiple-questions-or-multiple-posts)瞭解更多信息。 – Amy

+1

對不起,我只是假設我可以分組多個相關的問題,所有這些問題都導致產生一個可證明公平的擲骰子的相同的最終黃金。 –

回答

3

您鏈接到的頁面描述了這個過程,但是我會嘗試去詳細介紹並提供一個C#示例。

首先會發生兩個散列。一個通用的散列函數可以證明服務器在您賭博時不會更改服務器密鑰,但這個散列函數並不是祕密的,並且在開始玩遊戲時會發給玩家。還有一個鍵控散列(稱爲HMAC),用於實際生成骰子滾動並使用服務器密鑰,用戶提供的數據和計數的數字的組合。

這裏是發生這種情況的過程:

  1. 服務器生成播放會話密鑰,並設置一個計數器爲0
  2. SHA256上使用的密鑰來生成一個散列,該散列給予玩家。這個散列不用於任何數學運算來生成骰子擲骰,它僅用於玩家的驗證。
  3. 玩家請求骰子擲骰並提供一個用於生成號碼的短語。
  4. 服務器使用SHA512-HMAC使用密鑰作爲密鑰,然後使用用戶提供的字符串加上「 - 」加上在步驟1中設置的計數器的編號以生成哈希。
  5. 服務器將計數器加1,這是因爲每次都使用相同的服務器密鑰,並且如果使用了相同的用戶字符串,它只會一遍又一遍地生成相同的數字。
  6. 服務器將生成的哈希的前21位轉換爲int,然後檢查int是否大於999999,如果它不斷重複直到找到不超過999999的數字。
  7. 它從步驟6中獲得一個浮點數,並對其執行number%(10000)/100.0
  8. 該浮點數返回給用戶。
  9. 重複從第3步開始重新開始或繼續執行第10步。
  10. 播放器發出播放會話結束信號。服務器將密鑰返回給用戶,並在步驟1重新啓動。

用戶一旦從第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返回,用戶可以重新創建所有骰子卷,並證明服務器確實做了一個隨機生成(感謝用戶提供的數據,服務器不允許選擇),而不是一些僞造的預選k這是保證造成損失。

+0

謝謝!這有很大的幫助,但我想知道如何在我鏈接的網站上使用相同類型的字符串種子,這樣可以很容易地爲人們驗證,而不必在我的代碼中使用字節數組。 –

+0

我生成在末尾的示例useage中的字符串種子'Console.WriteLine(string.Join(「」,key.Select(x => x.ToString(「X」))));' –

+0

@ScottChamberlain ToString是有損的。 0x0304變成了「34」。你想'x.ToString(「X2」)'保持前導0。 – bartonjs