-5

我需要在 PHP 中与使用ISAAC 流密码的客户端进行通信。据我所知,PHP 可用的加密库都没有实现这个密码。如何在 PHP 应用程序中实现 ISAAC 密码?

(我找到了 ISAAC 的 Java 实现,并且几乎成功地将其移植到 PHP。唯一的问题是 PHP 中的无符号右移。我写了一个方法来做到这一点,但是当移位中的数字为消极的。)

4

2 回答 2

21

ISAAC 密码相当简单,而且由于它是用 C 编写的,因此将其移植到 PHP 应该不会太难。唯一真正的复杂之处是 ISAAC 使用 32 位无符号整数进行环绕,而 PHP 的整数可能是 32 位或 64 位,并且在溢出时自动转换为浮点数。不过,这并不是什么大问题,因为我们可以通过在需要将中间值强制为 32 位的地方应用位掩码来轻松解决它。

无论如何,这是 ISAAC 参考代码到 PHP 的一个相当直接的端口:

截至 2017 年 9 月,此代码现在也可以在 GitHub 上获得

<?php
/*
------------------------------------------------------------------------------
ISAAC random number generator by Bob Jenkins.  PHP port by Ilmari Karonen.

Based on the randport.c and readable.c reference C implementations by Bob
Jenkins, with some inspiration taken from the Perl port by John L. Allen.

This code is released into the public domain.  Do whatever you want with it.

HISTORY:
 2013-01-20: Initial version.  (Typo in version date fixed on 2017-09-19.)
------------------------------------------------------------------------------
*/

class ISAAC {
    private $m, $a, $b, $c; // internal state
    public  $r;   // current chunk of results

    public function isaac()
    {
        $c = ++$this->c;     // c gets incremented once per 256 results
        $b = $this->b += $c; // then combined with b
        $a = $this->a;

        $m =& $this->m;
        $r = array();

        for ($i = 0; $i < 256; ++$i) {
            $x = $m[$i];
            switch ($i & 3) {
            case 0: $a ^= ($a << 13); break;
            case 1: $a ^= ($a >>  6) & 0x03ffffff; break;
            case 2: $a ^= ($a <<  2); break;
            case 3: $a ^= ($a >> 16) & 0x0000ffff; break;
            }
            $a += $m[$i ^ 128]; $a &= 0xffffffff;
            $m[$i] = $y = ($m[($x >>  2) & 255] + $a + $b) & 0xffffffff;
            $r[$i] = $b = ($m[($y >> 10) & 255] + $x) & 0xffffffff;
        }

        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
        $this->r = $r;
    }

    public function rand()
    {
        if (empty($this->r)) $this->isaac();
        return array_pop($this->r);
    }

    private static function mix( &$a, &$b, &$c, &$d, &$e, &$f, &$g, &$h )
    {
        $a ^= ($b << 11);              $d += $a; $b += $c;
        $b ^= ($c >>  2) & 0x3fffffff; $e += $b; $c += $d;
        $c ^= ($d <<  8);              $f += $c; $d += $e;
        $d ^= ($e >> 16) & 0x0000ffff; $g += $d; $e += $f;
        $e ^= ($f << 10);              $h += $e; $f += $g;
        $f ^= ($g >>  4) & 0x0fffffff; $a += $f; $g += $h;
        $g ^= ($h <<  8);              $b += $g; $h += $a;
        $h ^= ($a >>  9) & 0x007fffff; $c += $h; $a += $b;
        // 64-bit PHP does something weird on integer overflow; avoid it
        $a &= 0xffffffff; $b &= 0xffffffff; $c &= 0xffffffff; $d &= 0xffffffff;
        $e &= 0xffffffff; $f &= 0xffffffff; $g &= 0xffffffff; $h &= 0xffffffff;
    }

    public function __construct ( $seed = null )
    {
        $this->a = $this->b = $this->c = 0;
        $this->m = array_fill(0, 256, 0);
        $m =& $this->m;

        $a = $b = $c = $d = $e = $f = $g = $h = 0x9e3779b9;  // golden ratio

        for ($i = 0; $i < 4; ++$i) {
            ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h);      // scramble it
        }

        if ( isset($seed) ) {
            if ( is_string($seed) ) {
                // emulate casting char* to int* on a little-endian CPU
                $seed = array_values(unpack("V256", pack("a1024", $seed)));
            }

            // initialize using the contents of $seed as the seed
            for ($i = 0; $i < 256; $i += 8) {
                $a += $seed[$i  ]; $b += $seed[$i+1];
                $c += $seed[$i+2]; $d += $seed[$i+3];
                $e += $seed[$i+4]; $f += $seed[$i+5];
                $g += $seed[$i+6]; $h += $seed[$i+7];
                ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h);
                $m[$i  ] = $a; $m[$i+1] = $b; $m[$i+2] = $c; $m[$i+3] = $d;
                $m[$i+4] = $e; $m[$i+5] = $f; $m[$i+6] = $g; $m[$i+7] = $h;
            }

            // do a second pass to make all of the seed affect all of $m
            for ($i = 0; $i < 256; $i += 8) {
                $a += $m[$i  ]; $b += $m[$i+1]; $c += $m[$i+2]; $d += $m[$i+3];
                $e += $m[$i+4]; $f += $m[$i+5]; $g += $m[$i+6]; $h += $m[$i+7];
                ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h);
                $m[$i  ] = $a; $m[$i+1] = $b; $m[$i+2] = $c; $m[$i+3] = $d;
                $m[$i+4] = $e; $m[$i+5] = $f; $m[$i+6] = $g; $m[$i+7] = $h;
            }
        }
        else {
            // fill in $m with messy stuff (does anyone really use this?)
            for ($i = 0; $i < 256; $i += 8) {
                ISAAC::mix($a, $b, $c, $d, $e, $f, $g, $h);
                $m[$i  ] = $a; $m[$i+1] = $b; $m[$i+2] = $c; $m[$i+3] = $d;
                $m[$i+4] = $e; $m[$i+5] = $f; $m[$i+6] = $g; $m[$i+7] = $h;
            }
        }

        // fill in the first set of results
        $this->isaac();
    }
}

ISAAC构造函数接受一个可选$seed参数,它应该是一个 256 元素的 32 位整数数组、一个最多 1024 字节的字符串(使用 ; 转换为数组unpack())或null(默认值)。如果种子为 null 或省略,则使用较短的单遍初始化,对应于 C 参考实现中的调用randinit()with ;flag == FALSE否则,使用对应的两遍初始化flag == TRUE

该类提供了一个rand()返回单个 32 位数字的方法,就像rand()在 Jenkins' 中定义的宏一样rand.h。或者,我将核心isaac()方法及其内部结果缓冲区$r公开,以便那些更喜欢直接访问生成器的人可以调用isaac()自己并以这种方式获取输出。请注意,构造函数已经调用了isaac()一次,就像randinit()在参考实现中一样,因此您只需在用完前 256 个输出值后再次调用它。


这里有两个测试程序,第一个对应于包含在rand.c中的测试代码:

<?php
require_once('isaac.php');

$seed = array_fill(0, 256, 0);
$isaac = new ISAAC ( $seed );

for ($i = 0; $i < 2; ++$i) {
    $isaac->isaac();  // XXX: the first output block is dropped!
    for ($j = 0; $j < 256; ++$j) {
        printf("%08x", $isaac->r[$j]);
        if (($j & 7) == 7) echo "\n";
    }
}

以及来自ISAAC 挑战的第二个 randtest.c

<?php
require_once('isaac.php');

$seed = "This is <i>not</i> the right mytext.";
$isaac = new ISAAC ( $seed );

for ($j = 0; $j < 10 * 256; ++$j) {
    printf("%08x ", $isaac->rand());
    if (($j & 7) == 7) echo "\n";
}

这些程序的输出应该分别匹配randvect.txtrandseed.txt

于 2013-01-20T19:30:56.227 回答
0

AFAIK 在 PHP 中没有公开实现 ISAAC。它不是那么流行,因此您无法在 PHP 中找到它的现成实现。

你必须选择:

a) 阅读算法论文并尝试在 PHP 中实现它,并成为第一个这样做的人。

b) 使用一些代理,在 Java、Ruby 等中有一些公开的算法实现。您可以通过命令行等调用它们。

于 2013-01-20T11:22:38.750 回答