PHP种子,确定性,密码安全PRNG(伪随机数发生器)。可能吗?

时间:2016-02-06 19:31:21

标签: php security random cryptography numbers

我需要在PHP中创建一个可证明公平的(确定性和播种)加密安全(CS)随机数生成器。我们正在运行PHP 5,而PHP 7现在还不是一个选项。但是,我发现了一个用于PHP 7新CS功能的polyfill,因此我已经实现了该解决方案(https://github.com/paragonie/random_compat)。

我认为srand()可以用来播种random_int(),但现在我不确定是否是这种情况。 CSPRNG甚至可以播种吗?如果它可以播种,输出是否是确定性的(相同的随机结果,给定相同的种子)?

这是我的代码:

require_once($_SERVER['DOCUMENT_ROOT']."/lib/assets/random_compat/lib/random.php");

$seed_a = 8138707157292429635;
$seed_b = 'JuxJ1XLnBKk7gPASR80hJfq5Ey8QWEIc8Bt';

class CSPRNG{
    private static $RNGseed = 0;

    public function generate_seed_a(){
        return random_int(0, PHP_INT_MAX);
    }

    public function generate_seed_b($length = 35){
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $randomString = '';
        for($i = 0; $i < $length; $i++){
            $randomString .= $characters[random_int(0, strlen($characters) - 1)];
        }
        return $randomString;
    }

    public function seed($s = 0) {
        if($s == 0){
            $this->RNGseed = $this->generate_seed_a();
        }else{
            $this->RNGseed = $s;
        }
        srand($this->RNGseed);
    }

    public function generate_random_integer($min=0, $max=PHP_INT_MAX, $pad_zeros = true){
        if($this->RNGseed == 0){
            $this->seed();
        }
        $rnd_num = random_int($min, $max);
        if($pad_zeros == true){
            $num_digits = strlen((string)$max);
            $format_str = "%0".$num_digits."d";
            return sprintf($format_str, $rnd_num);
        }else{
            return $rnd_num;
        }
    }

    public function drawing_numbers($seed_a, $num_of_balls = 6){
        $this->seed($seed_a);
        $draw_numbers = array();
        for($i = 0; $i < $num_of_balls; $i++) {
            $number = ($this->generate_random_integer(1, 49));
            if(in_array($number, $draw_numbers)){
                $i = $i-1;
            }else{
                array_push($draw_numbers, $number);
            }
        }
        sort($draw_numbers);
        return $draw_numbers;
    }
}

$CSPRNG= new CSPRNG();

echo '<p>Seed A: '.$seed_a.'</p>';
echo '<p>Seed B: '.$seed_b.'</p>';
$hash = hash('sha1', $seed_a.$seed_b);
echo '<p>Hash: '.$hash.'</p>';

$drawNumbers = $CSPRNG->drawing_numbers($seed_a);
$draw_str = implode("-", $drawNumbers);
echo "<br>Drawing: $draw_str<br>";

运行此代码时,每次运行时Drawing($ draw_str)应该相同,但不是。

为了证明绘图是公平的,在挑选和显示中奖号码之前选择种子(种子A)。还生成另一个随机数(种子B)。将种子B用作盐并与种子A组合,并将结果进行哈希处理。在绘图之前向用户显示该哈希。他们还将获得源代码,以便在挑选获胜号码时,揭示两个种子。他们可以验证哈希是否匹配,所有事情都做得很公平。

2 个答案:

答案 0 :(得分:4)

首先,您不应该实现自己的用户空间CSPRNG。安装了PHP 5的操作系统已经安装了CSPRNG,您应该使用它来获取所有随机性,除非您知道可以使用它,或者性能是一个问题。您应该使用random_int()random_bytes()openssl_random_pseudo_bytes()

但是,如果您必须实现用户空间CSPRNG,那么只需使用AES库(E.G。:libsodium)并加密计数器即可完成此操作。 Psuedocode将是:

Uint-128 n = 0;
while true:
    output = AES-ECB(key, n);
    n++;

在这种情况下,他们的AES密钥需要足够的熵来承受复杂的攻击,或者当然,用户空间CSPRNG的安全性也会崩溃。密钥可以是用户提供的密码的bcrypt()

如果您的计数器表示为128位无符号整数始终是唯一的,那么每次生成器被播种时,您将始终获得唯一的输出#34;用一个新柜台。如果它使用之前使用的计数器播种,但使用不同的键,则输出也会不同。最好的情况是每次调用生成器时都会更改密钥和更改计数器。

您可能很想在计数器中使用高精度时间戳,例如使用微秒精度。这很好,除非您冒着操纵系统时钟的人或某事的风险。因此,如果可以操纵时钟,那么CSPRNG发生器可能会受到损害。每次调用生成器时,最好提供一个新密钥,并开始使用128位零加密。

另外,请注意我们正在使用带有AES的ECB模式。不要惊慌失措。 ECB在明文提供的密文中保持结构存在问题。一般而言,您不应使用ECB模式。但是,对于128位数据,您只需要加密单个ECB块,因此不会泄漏结构化数据。对于用户空间CSPRNG,ECB优先于CTR,因为您不必跟踪密钥,计数器对象和要加密的数据。只需要一个密钥和数据。只需确保您永远不会加密超过128位的数据,并且您永远不需要超过1个块。

  

CSPRNG甚至可以播种吗?

是的,应始终播种。如果你看一下你的GNU / Linux操作系统,你可能会注意到/var/lib/urandom/random-seed中的文件。当操作系统关闭时,它会从CSPRNG创建该文件。在下次引导时,此文件用于为内核空间CSPRNG设定种子,以防止重用发生器的先前状态。每次关机时,该文件都应该更改。

  

如果可以播种,输出是否具有确定性(相同的随机结果,给定相同的种子)?

是。提供相同的种子,密钥等,输出是确定性的,因此输出将是相同的。如果您的一个变量发生变化,那么输出将会有所不同。这就是为什么每次调用生成器都应该重新键入的原因。

答案 1 :(得分:4)

Duskwuff问:

  

你打算如何证明种子被公平选择?可疑用户可以轻易声称您选择的种子会为特定用户带来有利结果,或者您提前向特定用户透露种子。

在调查解决方案之前,您要解决的问题究竟是什么?你的威胁模型是什么?

听起来好像 SeedSpring(版本0.3.0支持PHP 5.6)。

$prng = new \ParagonIE\SeedSpring\SeedSpring('JuxJ1XLnBKk7gPAS');
$byte = $prng->getBytes(16);
\var_dump(bin2hex($byte));

这应该总是返回:

string(32) "76482c186f7c5d1cb3f895e044e3c649"

这些数字应该是公正的,但由于它是基于预先共享的种子,因此严格来说,它不是加密安全的。

请记住,SeedSpring是作为玩具实现/概念验证而非官方Paragon Initiative Enterprises开源安全解决方案创建的,因此请随意分叉并根据您的目的进行调整。 (我怀疑我们的分支将达到“稳定的1.0.0发布”)。

(另外,如果您要接受/奖励这些答案中的任何答案,Aaron Toponce的答案更为正确。使用ECB模式加密nonce比使用AES加密NUL字节的长流更高效。点击率,大致相同的安全利益。这是ECB模式可以接受的极少数情况之一。