2017-06-12 12 views
2

公平な乱数を見て、このウェブサイトを訪れました:https://dicesites.com/provably-fairC#で公平なダイスロールを作る方法は?

まず、サーバー側ハッシュにはどのようなクラスを使うべきですか? SHA512、SHA256、またはSHA384Cngのような非常に多くのハッシュアルゴリズムがあり、私はそれらの違いを理解していません。

ハッシュなしのシードからハッシュされたシードに変換するために使用されるメソッドと、ハッシュ作成中にユーザーによって提供されたシードの文字列を取得するために使用されるメソッドを次に示します。また、重複したハッシュを防ぐために、ユーザが提供する文字列の最後にナンスを追加するだけですか?

3番目の理由から、ハッシュされたサーバーシードが本来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#の例を示します。

まず、発生するハッシュが2つあります。あなたが賭けるときにサーバーがサーバーキーを変更していないことを証明する一般的なハッシュの1つは、このハッシュは秘密ではなく、プレイの開始時にプレイヤーに与えられます。実際にダイスロールを生成するためのキー付きハッシュ(HMACと呼ばれる)もあり、サーバーキー、ユーザーが提供したデータ、およびカウントアップする数字の組み合わせを使用します。ここで

が起こるプロセスである:

  1. サーバーがプレイセッションのための秘密鍵を生成し、0
  2. SHA256にカウンタを設定しますが、ハッシュを生成するためのキーで使用され、このハッシュプレーヤーに与えられる。このハッシュは、ダイスロールを生成するための数学では使用されません。プレーヤーの検証にのみ使用されます。
  3. プレイヤーはサイコロロールを要求し、番号の生成に使用するフレーズを提供します。
  4. サーバーは、秘密キーをキーとして使用し、ユーザーが指定した文字列に「 - 」とステップ1で設定したカウンターの番号を加えてハッシュを生成するSHA512-HMACを使用します。
  5. サーバはカウンタを1ずつインクリメントします。これは同じサーバキーが毎回使用され、同じユーザ文字列が使用された場合に同じ番号を何度も繰り返し生成するために行われます。
  6. サーバーは、生成されたハッシュの最初の21ビットを取り出し、intに変換してから、999999以上でない番号が見つかるまで繰り返している場合、intが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; 
     } 
    } 
} 

それの使用例で

使用algorthom、 RollDiceによって返された情報とに与えられたキーに関する公開された情報を使用して
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(); 
     } 

    } 
} 

ユーザーはEndSessionから返され、すべてのダイスロールを再作成してサーバーが本当にランダム世代を行ったことを証明することができます(サーバーが選択することができなかったロールのユーザーのおかげで)それは損失を引き起こすことが保証されています。

+0

ありがとう!これは多くの助けになりましたが、リンクされたWebサイトのような同じ種類の文字列シードを使用する方法が不思議でした。コードでバイト配列を使用する代わりに、簡単に検証できます。 –

+0

この例の文字列のシードは、Console.WriteLine(string.Join( ""、key.Select(x => x.ToString( "X"))))));); ' –

+0

@ScottChamberlain ToStringは不可解です。 0x0304は "34"になります。 'x.ToString(" X2 ")'に先行0を保持します。 – bartonjs

関連する問題