2016-06-23 23:51:08 +02:00
|
|
|
<?php
|
2016-07-14 15:15:50 +02:00
|
|
|
|
|
|
|
set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).DIRECTORY_SEPARATOR.'libpy2php');
|
|
|
|
require_once 'libpy2php.php';
|
2016-07-20 20:12:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2016-07-19 11:56:05 +02:00
|
|
|
class PrimeModule
|
|
|
|
{
|
|
|
|
public function __construct()
|
|
|
|
{
|
2016-07-18 18:43:50 +02:00
|
|
|
$this->smallprimeset = array_unique($this->primesbelow(100000));
|
|
|
|
$this->_smallprimeset = 100000;
|
|
|
|
$this->smallprimes = $this->primesbelow(10000);
|
|
|
|
}
|
2016-07-29 17:19:34 +02:00
|
|
|
|
2016-07-19 11:56:05 +02:00
|
|
|
public function primesbelow($N)
|
|
|
|
{
|
2016-07-18 18:43:50 +02:00
|
|
|
$res = [];
|
2016-07-19 11:56:05 +02:00
|
|
|
for ($i = 2; $i <= $N; $i++) {
|
|
|
|
if ($i % 2 != 1 && $i != 2) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-07-18 18:43:50 +02:00
|
|
|
$d = 3;
|
|
|
|
$x = sqrt($i);
|
2016-07-19 11:56:05 +02:00
|
|
|
while ($i % $d != 0 && $d < $x) {
|
|
|
|
$d += 2;
|
|
|
|
}
|
|
|
|
if ((($i % $d == 0 && $i != $d) * 1) == 0) {
|
|
|
|
$res[] = $i;
|
|
|
|
}
|
2016-07-14 15:15:50 +02:00
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
|
2016-07-18 18:43:50 +02:00
|
|
|
return $res;
|
2016-07-14 15:15:50 +02:00
|
|
|
}
|
|
|
|
|
2016-07-19 11:56:05 +02:00
|
|
|
public function isprime($n, $precision = 7)
|
2016-07-18 18:43:50 +02:00
|
|
|
{
|
|
|
|
if (($n == 1) || (($n % 2) == 0)) {
|
|
|
|
return false;
|
|
|
|
} elseif (($n < 1)) {
|
|
|
|
throw new Exception('Out of bounds, first argument must be > 0');
|
|
|
|
} elseif (($n < $this->_smallprimeset)) {
|
|
|
|
return in_array($n, $this->smallprimeset);
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-18 18:43:50 +02:00
|
|
|
$d = ($n - 1);
|
|
|
|
$s = 0;
|
|
|
|
while (($d % 2) == 0) {
|
|
|
|
$d = floor($d / 2);
|
2016-07-20 20:12:32 +02:00
|
|
|
$s++;
|
2016-07-18 18:43:50 +02:00
|
|
|
}
|
2016-07-20 20:12:32 +02:00
|
|
|
$break = false;
|
2016-07-18 18:43:50 +02:00
|
|
|
foreach (pyjslib_range($precision) as $repeat) {
|
|
|
|
$a = rand(2, ($n - 2));
|
|
|
|
$x = posmod(pow($a, $d), $n);
|
|
|
|
if (($x == 1) || ($x == ($n - 1))) {
|
|
|
|
continue;
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-20 20:12:32 +02:00
|
|
|
foreach (pyjslib_range($s - 1) as $r) {
|
2016-07-18 18:43:50 +02:00
|
|
|
$x = posmod(pow($x, 2), $n);
|
|
|
|
if (($x == 1)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (($x == ($n - 1))) {
|
2016-07-20 20:12:32 +02:00
|
|
|
$break = true;
|
2016-07-18 18:43:50 +02:00
|
|
|
}
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-29 17:19:34 +02:00
|
|
|
if (!$break) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-14 15:15:50 +02:00
|
|
|
|
2016-07-18 18:43:50 +02:00
|
|
|
return true;
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-08-04 23:34:00 +02:00
|
|
|
// taken from https://github.com/enricostara/telegram-mt-node/blob/master/lib/security/pq-finder.js
|
|
|
|
public function factorization($num) {
|
|
|
|
$zero = new \phpseclib\Math\BigInteger(0);
|
|
|
|
$one = new \phpseclib\Math\BigInteger(1);
|
|
|
|
$two = new \phpseclib\Math\BigInteger(2);
|
|
|
|
$three = new \phpseclib\Math\BigInteger(3);
|
2016-07-19 11:56:05 +02:00
|
|
|
|
2016-08-04 23:34:00 +02:00
|
|
|
$prime = new \phpseclib\Math\BigInteger();
|
|
|
|
|
|
|
|
for ($i = 0; $i < 3; $i++) {
|
|
|
|
$q = new \phpseclib\Math\BigInteger((random_int(0, 128) & 15) + 17);
|
|
|
|
$x = new \phpseclib\Math\BigInteger(random_int(0, 1000000000) + 1);
|
|
|
|
$y = $x;
|
|
|
|
$lim = 1 << ($i + 18);
|
|
|
|
for ($j = 1; $j < $lim; $j++) {
|
|
|
|
$a = $x;
|
|
|
|
$b = $x;
|
|
|
|
$c = $q;
|
|
|
|
while (!$b->equals($zero)) {
|
|
|
|
if (b.repr[0] & 1) {
|
|
|
|
c.addEquals(a);
|
|
|
|
if (c.gt(num)) {
|
|
|
|
c = c.subtract(num);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a.addEquals(a);
|
|
|
|
if (a.gt(num)) {
|
|
|
|
a = a.subtract(num);
|
|
|
|
}
|
|
|
|
b = b.shiftRight(1);
|
|
|
|
}
|
|
|
|
$x = $c;
|
|
|
|
$z = $y.gt(x) ? y.subtract(x) : x.subtract(y);
|
|
|
|
$prime = z.gcd(num, a, b);
|
|
|
|
if (!prime.eql(BigInteger.One())) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((j & (j - 1)) === 0) {
|
|
|
|
$y = $x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (prime.gt(BigInteger.One())) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$cofactor = num.divide(prime)[0];
|
|
|
|
$_pq = cofactor.gt(prime) ? [prime, cofactor] : [cofactor, prime];
|
|
|
|
return _$pq;
|
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
public function pollard_brent($n)
|
2016-07-18 18:43:50 +02:00
|
|
|
{
|
2016-07-30 17:16:54 +02:00
|
|
|
$zero = new \phpseclib\Math\BigInteger(0);
|
|
|
|
$one = new \phpseclib\Math\BigInteger(1);
|
|
|
|
$two = new \phpseclib\Math\BigInteger(2);
|
|
|
|
$three = new \phpseclib\Math\BigInteger(3);
|
2016-07-30 17:17:15 +02:00
|
|
|
if ($n->powMod($one, $two)->toString() == '0') {
|
2016-07-18 18:43:50 +02:00
|
|
|
return 2;
|
|
|
|
}
|
2016-07-30 17:17:15 +02:00
|
|
|
if ($n->powMod($one, $three)->toString() == '0') {
|
2016-07-18 18:43:50 +02:00
|
|
|
return 3;
|
|
|
|
}
|
2016-07-20 20:23:26 +02:00
|
|
|
$big = new \phpseclib\Math\BigInteger();
|
2016-07-30 17:16:54 +02:00
|
|
|
$max = $n->subtract($one);
|
|
|
|
list($y, $c, $m) = [new \phpseclib\Math\BigInteger(87552211475113995), new \phpseclib\Math\BigInteger(330422027228888537), new \phpseclib\Math\BigInteger(226866727920975483)];
|
|
|
|
//[$big->random($one, $max), $big->random($one, $max), $big->random($one, $max)];
|
|
|
|
list($g, $r, $q) = [$one, $one, $one];
|
|
|
|
while ($g->equals($one)) {
|
2016-07-18 18:43:50 +02:00
|
|
|
$x = $y;
|
2016-08-03 22:07:58 +02:00
|
|
|
$params = ['y' => $y, 'two' => $two, 'c' => $c, 'one' => $one, 'n' => $n];
|
2016-08-03 22:07:40 +02:00
|
|
|
$r->loopforeach(function ($i, $params) {
|
2016-08-03 22:07:58 +02:00
|
|
|
$params['y'] = $params['y']->powMod($params['two'], $params['n'])->add($params['c'])->powMod($params['one'], $params['n']);
|
2016-08-03 21:49:11 +02:00
|
|
|
}, $params);
|
2016-08-03 22:07:40 +02:00
|
|
|
each($params);
|
2016-07-30 17:16:54 +02:00
|
|
|
$k = $zero;
|
|
|
|
while ($k->compare($r) == -1 && $g->equals($one)) {
|
2016-07-18 18:43:50 +02:00
|
|
|
$ys = $y;
|
2016-08-03 22:07:58 +02:00
|
|
|
$params = ['x' => $x, 'y' => $y, 'two' => $two, 'c' => $c, 'one' => $one, 'n' => $n, 'q' => $q];
|
2016-08-03 22:07:40 +02:00
|
|
|
$m->min($r->subtract($k))->loopforeach(function ($i, $params) {
|
2016-08-03 22:07:58 +02:00
|
|
|
$params['y'] = $params['y']->powMod($params['two'], $params['n'])->add($params['c'])->powMod($params['one'], $params['n']);
|
|
|
|
$params['q'] = $params['q']->multiply($params['x']->subtract($params['y'])->abs())->powMod($params['one'], $params['n']);
|
2016-08-03 21:49:11 +02:00
|
|
|
}, $params);
|
2016-08-03 22:07:40 +02:00
|
|
|
each($params);
|
2016-07-30 17:16:54 +02:00
|
|
|
$g = $q->gcd($n);
|
|
|
|
$k = $k->add($m);
|
2016-07-18 18:43:50 +02:00
|
|
|
}
|
2016-07-30 17:16:54 +02:00
|
|
|
$r = $r->multiply($two);
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-08-03 22:07:40 +02:00
|
|
|
die;
|
2016-07-30 17:16:54 +02:00
|
|
|
if ($g->equals($n)) {
|
2016-07-18 18:43:50 +02:00
|
|
|
while (true) {
|
2016-07-30 17:16:54 +02:00
|
|
|
$ys = $ys->powMod($two, $n)->add($c)->powMod($one, $n);
|
|
|
|
$g = $x->subtract($ys)->abs()->gcd($n);
|
|
|
|
if ($g->compare($one) == 1) {
|
2016-07-18 18:43:50 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
|
|
|
}
|
2016-07-14 15:15:50 +02:00
|
|
|
|
2016-07-18 18:43:50 +02:00
|
|
|
return $g;
|
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
|
|
|
|
public function primefactors($n, $sort = false)
|
2016-07-18 18:43:50 +02:00
|
|
|
{
|
|
|
|
$factors = [];
|
2016-07-30 17:16:54 +02:00
|
|
|
$n = new \phpseclib\Math\BigInteger(1724114033281923457);
|
|
|
|
$one = new \phpseclib\Math\BigInteger(1);
|
|
|
|
$two = new \phpseclib\Math\BigInteger(2);
|
|
|
|
$limit = $n->root()->add($one);
|
2016-07-18 18:43:50 +02:00
|
|
|
foreach ($this->smallprimes as $checker) {
|
2016-07-30 17:16:54 +02:00
|
|
|
$checker = new \phpseclib\Math\BigInteger($checker);
|
|
|
|
if ($limit->compare($checker) == -1) {
|
2016-06-23 23:51:08 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-07-30 17:17:15 +02:00
|
|
|
while ($n->modPow($one, $checker)->toString() == '0') {
|
2016-07-18 18:43:50 +02:00
|
|
|
$factors[] = $checker;
|
2016-07-30 17:16:54 +02:00
|
|
|
$n = $n->divide($checker)[0];
|
|
|
|
$limit = $n->root()->add($one);
|
|
|
|
if ($limit->compare($checker) == -1) {
|
2016-07-18 18:43:50 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-30 17:16:54 +02:00
|
|
|
if ($n->compare($two) == -1) {
|
2016-07-18 18:43:50 +02:00
|
|
|
return $factors;
|
|
|
|
}
|
2016-07-30 17:16:54 +02:00
|
|
|
while ($n->compare($two) == 1) {
|
|
|
|
if ($n->isprime()) {
|
2016-07-18 18:43:50 +02:00
|
|
|
$factors[] = $n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$factor = $this->pollard_brent($n);
|
|
|
|
$factors[] = $this->primefactors($factor);
|
|
|
|
$n = floor($n / $factor);
|
|
|
|
}
|
|
|
|
if ($sort) {
|
|
|
|
$factors = sort($factors);
|
|
|
|
}
|
|
|
|
|
2016-06-23 23:51:08 +02:00
|
|
|
return $factors;
|
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
|
|
|
|
public function factorization($n)
|
2016-07-18 18:43:50 +02:00
|
|
|
{
|
|
|
|
$factors = [];
|
|
|
|
foreach (primefactors($n) as $p1) {
|
2016-07-19 11:56:05 +02:00
|
|
|
if (isset($factors[$p1])) {
|
2016-07-18 18:43:50 +02:00
|
|
|
$factors[$p1] += 1;
|
|
|
|
} else {
|
|
|
|
$factors[$p1] = 1;
|
|
|
|
}
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-14 15:15:50 +02:00
|
|
|
|
2016-07-18 18:43:50 +02:00
|
|
|
return $factors;
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
|
|
|
|
public function totient($n)
|
2016-07-18 18:43:50 +02:00
|
|
|
{
|
|
|
|
$totients = [];
|
|
|
|
if (($n == 0)) {
|
|
|
|
return 1;
|
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
if (isset($totients[$n])) {
|
2016-07-18 18:43:50 +02:00
|
|
|
return $totients[$n];
|
|
|
|
}
|
|
|
|
$tot = 1;
|
|
|
|
foreach (factorization($n) as $p => $exp) {
|
|
|
|
$tot *= (($p - 1) * pow($p, ($exp - 1)));
|
|
|
|
}
|
|
|
|
$totients[$n] = $tot;
|
|
|
|
|
|
|
|
return $tot;
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
|
|
|
|
public function gcd($a, $b)
|
2016-07-18 18:43:50 +02:00
|
|
|
{
|
|
|
|
if (($a == $b)) {
|
|
|
|
return $a;
|
|
|
|
}
|
|
|
|
while (($b > 0)) {
|
2016-07-20 20:12:32 +02:00
|
|
|
list($a, $b) = [$b, posmod($a, $b)];
|
2016-07-18 18:43:50 +02:00
|
|
|
}
|
2016-07-14 15:15:50 +02:00
|
|
|
|
2016-06-23 23:51:08 +02:00
|
|
|
return $a;
|
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
|
|
|
|
public function lcm($a, $b)
|
2016-07-18 18:43:50 +02:00
|
|
|
{
|
2016-07-18 18:56:33 +02:00
|
|
|
return floor(abs(($a * $b)) / $this->gcd($a, $b));
|
2016-06-23 23:51:08 +02:00
|
|
|
}
|
2016-07-19 11:56:05 +02:00
|
|
|
|
|
|
|
}
|