Subversion Repositories oidplus

Rev

Rev 846 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
827 daniel-mar 1
<?php
2
 
3
/**
4
 * PHP Montgomery Modular Exponentiation Engine
5
 *
6
 * PHP version 5 and 7
7
 *
874 daniel-mar 8
 * @category  Math
9
 * @package   BigInteger
827 daniel-mar 10
 * @author    Jim Wigginton <terrafrost@php.net>
11
 * @copyright 2017 Jim Wigginton
12
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
13
 * @link      http://pear.php.net/package/Math_BigInteger
14
 */
15
 
16
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
17
 
18
use phpseclib3\Math\BigInteger\Engines\PHP\Montgomery as Progenitor;
19
 
20
/**
21
 * PHP Montgomery Modular Exponentiation Engine
22
 *
874 daniel-mar 23
 * @package PHP
827 daniel-mar 24
 * @author  Jim Wigginton <terrafrost@php.net>
874 daniel-mar 25
 * @access  public
827 daniel-mar 26
 */
27
abstract class Montgomery extends Progenitor
28
{
29
    /**
30
     * Prepare a number for use in Montgomery Modular Reductions
31
     *
32
     * @param array $x
33
     * @param array $n
34
     * @param string $class
35
     * @return array
36
     */
37
    protected static function prepareReduce(array $x, array $n, $class)
38
    {
39
        $lhs = new $class();
40
        $lhs->value = array_merge(self::array_repeat(0, count($n)), $x);
41
        $rhs = new $class();
42
        $rhs->value = $n;
43
 
44
        list(, $temp) = $lhs->divide($rhs);
45
        return $temp->value;
46
    }
47
 
48
    /**
49
     * Montgomery Multiply
50
     *
51
     * Interleaves the montgomery reduction and long multiplication algorithms together as described in
52
     * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
53
     *
54
     * @param array $x
55
     * @param array $n
56
     * @param string $class
57
     * @return array
58
     */
59
    protected static function reduce(array $x, array $n, $class)
60
    {
61
        static $cache = [
62
            self::VARIABLE => [],
63
            self::DATA => []
64
        ];
65
 
66
        if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
67
            $key = count($cache[self::VARIABLE]);
68
            $cache[self::VARIABLE][] = $x;
69
            $cache[self::DATA][] = self::modInverse67108864($n, $class);
70
        }
71
 
72
        $k = count($n);
73
 
74
        $result = [self::VALUE => $x];
75
 
76
        for ($i = 0; $i < $k; ++$i) {
77
            $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
78
            $temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
79
            $temp = $class::regularMultiply([$temp], $n);
80
            $temp = array_merge(self::array_repeat(0, $i), $temp);
81
            $result = $class::addHelper($result[self::VALUE], false, $temp, false);
82
        }
83
 
84
        $result[self::VALUE] = array_slice($result[self::VALUE], $k);
85
 
86
        if (self::compareHelper($result, false, $n, false) >= 0) {
87
            $result = $class::subtractHelper($result[self::VALUE], false, $n, false);
88
        }
89
 
90
        return $result[self::VALUE];
91
    }
92
 
93
    /**
94
     * Modular Inverse of a number mod 2**26 (eg. 67108864)
95
     *
96
     * Based off of the bnpInvDigit function implemented and justified in the following URL:
97
     *
98
     * {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
99
     *
100
     * The following URL provides more info:
101
     *
102
     * {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
103
     *
104
     * As for why we do all the bitmasking...  strange things can happen when converting from floats to ints. For
105
     * instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
106
     * int(-2147483648).  To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
107
     * auto-converted to floats.  The outermost bitmask is present because without it, there's no guarantee that
108
     * the "residue" returned would be the so-called "common residue".  We use fmod, in the last step, because the
109
     * maximum possible $x is 26 bits and the maximum $result is 16 bits.  Thus, we have to be able to handle up to
110
     * 40 bits, which only 64-bit floating points will support.
111
     *
112
     * Thanks to Pedro Gimeno Fortea for input!
113
     *
114
     * @param array $x
115
     * @param string $class
116
     * @return int
117
     */
118
    protected static function modInverse67108864(array $x, $class) // 2**26 == 67,108,864
119
    {
120
        $x = -$x[0];
121
        $result = $x & 0x3; // x**-1 mod 2**2
122
        $result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
123
        $result = ($result * (2 - ($x & 0xFF) * $result))  & 0xFF; // x**-1 mod 2**8
124
        $result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
125
        $result = $class::BASE == 26 ?
126
            fmod($result * (2 - fmod($x * $result, $class::BASE_FULL)), $class::BASE_FULL) : // x**-1 mod 2**26
127
            ($result * (2 - ($x * $result) % $class::BASE_FULL)) % $class::BASE_FULL;
128
        return $result & $class::MAX_DIGIT;
129
    }
130
}