当前位置: 首页 > 知识库问答 >
问题:

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

韩经武
2023-03-14

我被要求创建一个可证明的公平(确定性

我原以为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>";

运行此代码时,每次运行时的绘图($draw_str)应该是相同的,但事实并非如此。

为了证明图纸是公平的,在选择并显示获胜号码之前,会选择一个种子(种子A)。还会生成另一个随机数(种子B)。种子B被用作盐,并与种子A结合,结果会被散列。在绘制之前,这个散列会显示给用户。他们还会得到源代码,这样当选择获胜号码时,两个种子都会被显示出来。他们可以验证散列是否匹配,并且一切都做得公平。

共有2个答案

马庆
2023-03-14

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

但是,如果您必须实现用户空间CSPRNG,那么这可以通过简单地使用AES库(例如:libna)和加密计数器来完成。伪代码将是:

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

在这种情况下,它们需要足够的熵来抵御复杂的攻击,否则用户空间CSPRNG的安全性当然会崩溃。密钥可以是用户提供的密码的bcrypt()

如果表示为128位无符号整数的计数器总是唯一的,那么每次生成器用新计数器“播种”时,您总是会得到唯一的输出。如果它是用以前使用的计数器播种的,但是是不同的键,那么输出也会不同。最好的情况是,每次调用生成器时,都会有一个不断变化的键和一个不断变化的计数器。

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

另外,请注意,我们在AES中使用ECB模式。别紧张。欧洲央行在维持明文提供的密文结构方面存在问题。一般来说,您不应该使用ECB模式。然而,对于128位的数据,您将只加密单个ECB块,因此不会泄漏结构化数据。对于用户空间CSPRNG,ECB优于CTR,因为您不必跟踪密钥、计数器对象和要加密的数据。只需要一把钥匙和数据。只需确保加密的数据永远不会超过128位,并且永远不会需要超过1个数据块。

CSPRNG甚至可以播种吗?

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

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

是的。提供相同的种子、键等。,输出是确定性的,所以输出是相同的。如果您的一个变量发生变化,那么输出将不同。这就是为什么在生成器的每次调用中应该重新键控。

轩辕弘雅
2023-03-14

杜斯库夫问道:

你打算如何证明这颗种子是被公平选择的?一个可疑的用户可以很容易地声称,你选择了一个种子,这将为特定用户带来有利的结果,或者你提前向特定用户透露了该种子。

在你调查解决方案之前,你到底想解决什么问题?你的威胁模型是什么?

听起来你想要SeedSpring(版本0.3.0支持PHP5.6)。

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

这应该始终返回:

string(32) "76482c186f7c5d1cb3f895e044e3c649"

这些数字应该是无偏的,但由于它基于预先共享的种子,严格来说,它在加密方面并不安全。

请记住,SeedSpring是作为一个玩具实现/概念验证而创建的,而不是一个官方的Paragon倡议企业开源安全解决方案,所以请随意分叉并调整它以满足您的目的。(我怀疑我们的分支永远不会达到“稳定的1.0.0版本”)。

(此外,如果你打算接受/奖励这些答案中的任何一个,Aaron Toponce的答案更正确。使用ECB模式加密nonce比使用AES-CTR加密长时间的NUL字节流html" target="_blank">性能更好,安全性几乎相同。这是ECB模式可以接受的极少数情况之一。)

 类似资料:
  • 问题内容: PHP的函数不能提供良好的随机数。因此,我开始使用据说效果更好的产品。但是这些结果有多好?有什么方法可以再次改善它们? 我的点子: 这应该给您“完美的”随机数,不是吗? 问题答案: 伪随机数生成器(PRNG)是非常复杂的野兽。 没有真正的“完美”随机数生成器-实际上,可以通过数学函数完成的最好的操作是伪随机数-对于大多数意图和目的,它们似乎足够随机。 实际上,从PRNG返回的数字中执行

  • 生成随机数 # random_random.py import random for i in range(5): print('%04.3f' % random.random(), end=' ') print() # random_uniform.py import random for i in range(5): print('{:04.3f}'.format(ran

  • 问题内容: 用例:“我忘记了密码”按钮。我们找不到用户的原始密码,因为它以散列形式存储,因此唯一要做的就是生成一个新的随机密码,然后通过电子邮件发送给他。这就要求使用密码无法预测的随机数,而mt_rand不够好,因此通常我们不能假定托管服务将提供对操作系统的访问权以安装密码随机数模块等。因此,我在寻找一种方法在PHP本身中生成安全的随机数。 到目前为止,我提出的解决方案包括存储初始种子,然后针对每

  • 当我声明一个变量时,比如: 我得到这个错误在声纳: 确保在此处使用此伪随机数生成器是安全的。 我应该如何解决这个问题?我的代码有什么问题? 有人能帮我吗?

  • 问题内容: 我试图在Go中生成一个随机字符串,这是我到目前为止编写的代码: 我的执行速度很慢。使用进行播种会在一定时间内带来相同的随机数,因此循环会一次又一次地迭代。如何改善我的代码? 问题答案: 每次设置相同的种子,您将获得相同的序列。因此,当然,如果您将种子设置为快速循环中的时间,则可能会多次调用相同的种子。 在您的情况下,在您调用函数直到拥有不同的值之前,您正在等待时间(由Nano返回)。

  • 利用伪随机数生成器(PRNG)对排队型系统进行蒙特卡罗模拟。我使用System.random,因为它速度快,但发现它在后续的绘制之间有某种怪异的相关性,干扰了结果(不够随机)。 现在我使用的是Mersenne Twister(http://takel.jp/mt/mersennetwister.cs),它(到目前为止)已经证明对我的目的来说是足够随机的。它慢了50%,但那是我愿意付出的代价,以得到