密码随机播放和选择

时间:2013-12-20 23:28:50

标签: random

我正在实施一个加密安全的shuffle例程,并有几个问题:

我使用的方法是加权排序,其中每个权重都是加密强随机数。

  1. 我通过使用列表中的项目数(X)并将其插入此公式= log10(X!) / log10(2)来计算每个权重所需的位数。例如,52卡片组每重量需要log10(52!) / log10(2) = 225.58100312370276194634244437667位。我总是把它四舍五入,因为有些小部分无法表示。我总是在纠正中是正确的还是给了我太多的东西?

  2. 从硬件rng中检索位可能不太实用,因此必须检索字节。与前面的示例226 / 8 = 28.25一样,我们有28个完整字节,还有一个额外的字节来获取剩余的2个比特。我正在做的是丢弃最后一个字节的未使用的高6位,这样只有2个位被附加到数字上。我是在纠正丢弃这些位还是通过这样做来破坏熵?

  3. 我按照分配给每个数字的(左边填充,全部大写,ASCII)十六进制的权重字符串进行排序。这似乎产生了正确的排序顺序。我应该注意以这种方式排序字符串吗?

  4. 我应该使用硬件rng来测试它产生的数字的熵,但我使用的是MS RNGCryptoServiceProvider。是否有更好的加密RNG与.NET一起使用?

  5. 要从加密和排序列表中“选择”一个数字,我只是选择索引0.是否有更好的加密随机方法来选择列表中的项目?

  6. 如果我能帮助澄清或者如果这是错误的网站,请告诉我,请告诉我更好的网站。

    这是我的代码,如果它有助于说明我所说的VB.NET控制台应用程序:

    Imports System.Security.Cryptography
    
    Module Module1
    
        Public Class Ball
            Public Weight As String
            Public Value As Integer
            Public Sub New(ByVal _Weight As String, ByVal _Value As Integer)
                Weight = _Weight
                Value = _Value
            End Sub
        End Class
    
        Public Class BallComparer_Weight
            Implements IComparer(Of Ball)
            Public Function Compare(x As Ball, y As Ball) As Integer Implements System.Collections.Generic.IComparer(Of Ball).Compare
                If x.Weight > y.Weight Then
                    Return 1
                ElseIf x.Weight < y.Weight Then
                    Return -1
                Else
                    Return 0
                End If
            End Function
        End Class
    
        Public Class BallComparer_Value
            Implements IComparer(Of Ball)
            Public Function Compare(x As Ball, y As Ball) As Integer Implements System.Collections.Generic.IComparer(Of Ball).Compare
                If x.Value > y.Value Then
                    Return 1
                ElseIf x.Value < y.Value Then
                    Return -1
                Else
                    Return 0
                End If
            End Function
        End Class
    
        Public Function Weight(ByVal rng As RNGCryptoServiceProvider, ByVal bits As Integer) As String
            ' generate a "cryptographically" random string of length 'bits' (should be using hardware rng)
            Dim remainder As Integer = bits Mod 8
            Dim quotient As Integer = bits \ 8
            Dim byteCount As Integer = quotient + If(remainder <> 0, 1, 0)
            Dim bytes() As Byte = New Byte(byteCount - 1) {}
            Dim result As String = String.Empty
            rng.GetBytes(bytes)
            For index As Integer = bytes.Length - 1 To 0 Step -1
                If index = bytes.Length - 1 Then
                    ' remove upper `remainder` bits from upper byte
                    Dim value As Byte = (bytes(0) << remainder) >> remainder
                    result &= value.ToString("X2")
                Else
                    result &= bytes(index).ToString("X2")
                End If
            Next
            Return result
        End Function
    
        Public Function ContainsValue(ByVal lst As List(Of Ball), ByVal value As Integer) As Boolean
            For i As Integer = 0 To lst.Count - 1
                If lst(i).Value = value Then
                    Return True
                End If
            Next
            Return False
        End Function
    
        Sub Main()
    
            Dim valueComparer As New BallComparer_Value()
            Dim weightComparer As New BallComparer_Weight()
            Dim picks As New List(Of Ball)
            Dim balls As New List(Of Ball)
            ' number of bits after each "ball" is drawn
            Dim bits() As Integer = New Integer() {364, 358, 351, 345, 339}
    
            Using rng As New RNGCryptoServiceProvider
                While True
    
                    picks.Clear()
    
                    ' simulate random balls
                    'log10(75!) / log10(2) = number of bits required for weighted random shuffle (reduces each time ball is pulled) = 363.40103411549404253061653790169 = 364
                    For i As Integer = 0 To 4
                        balls.Clear()
                        For value As Integer = 1 To 75
                            ' do not add previous picks
                            If Not ContainsValue(picks, value) Then
                                balls.Add(New Ball(Weight(rng, bits(i)), value))
                            End If
                        Next
                        balls.Sort(weightComparer)
    
                        'For Each x As Ball In balls
                        '    Console.WriteLine(x.Weight)
                        'Next
                        'Console.ReadLine()
    
                        ' choose first ball in sorted list
                        picks.Add(balls(0))
                    Next
                    picks.Sort(valueComparer)
    
                    ' simulate random balls
                    'log10(15!) / log10(2) = number of bits required for weighted random shuffle = 40.250140469882621763813506287601 = 41 bits required for megaball
                    balls.Clear()
                    For value As Integer = 1 To 15
                        balls.Add(New Ball(Weight(rng, 41), value))
                    Next
                    balls.Sort(weightComparer)
    
                    ' print to stdout
                    For i As Integer = 0 To 4
                        Console.Write(picks(i).Value.ToString("D2") & " "c)
                    Next
                    Console.WriteLine(balls(0).Value.ToString("D2"))
    
                End While
            End Using
    
        End Sub
    
    End Module
    

1 个答案:

答案 0 :(得分:1)

你的基本想法似乎很合理。但是:

  • 你的体重中不需要那么多位。你需要的只是make collisions unlikely,即每个项目的⌈log 2 n 2 ⌉比特,加上一些好的衡量标准。对于52张卡,最低限度为每卡约12位,16位将使碰撞概率降至约4%。这应该是充足的,至少只要你明确检查碰撞。

  • 检查是否存在冲突(即两个具有相同随机排序键的项目),如果找到,则重新启动shuffle。或者,你可以增加排序键的长度,使得碰撞的概率可以忽略不计。

  • 是的,以十六进制编码排序键应该没问题。实际上,只要它是确定性的(即总是为相同的随机数提供相同的编码),对它们进行编码的如何并不重要。也就是说,既然您知道随机位串的长度,为什么不将它们存储在原始二进制中呢? (特别是,如果每个键需要少于64位,则可以将每个键存储在适当大小的整数变量中。)

  • 如果你想避免使用side channel attacks,你应该选择一种可以在恒定时间内运行并且功耗不变的排序方法,而不管最终的顺序是什么。这说起来容易做起来难,因为大多数常见的排序算法都没有接近恒定时间。也就是说,根据您的应用程序,此类攻击可能会或可能不会起作用(但在您考虑问题之前不要排除这些攻击!)。

安全地改组阵列的另一种方法是使用带有加密安全RNG的Fisher–Yates shuffle。这种方法可以减少比特浪费,并且更容易在恒定时间内实现(或者至少在时间上独立于输出;见下文),但它确实需要您的生成器能够从任何整数范围返回无偏样本,而不仅仅是来自具有两倍幂的范围。 (Rejection sampling是实现此目的的一种方式 - 它不是恒定时间,但可以显示所需时间不会显示有关最终输出的任何内容,因此它仍然可以。)

最后,如果你只需要来自混洗数组的一个元素,所有这些都是不必要的:只需为数组选择一个随机索引(统一,例如使用上面提到的拒绝抽样方法)和返回相应的元素。