summaryrefslogtreecommitdiff
path: root/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/PHP/Base.php
blob: 40f64bd17ffdca0dd33dc3ed14df91209a7738bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<?php

/**
 * PHP Modular Exponentiation Engine
 *
 * PHP version 5 and 7
 *
 * @author    Jim Wigginton <[email protected]>
 * @copyright 2017 Jim Wigginton
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
 * @link      http://pear.php.net/package/Math_BigInteger
 */

namespace phpseclib3\Math\BigInteger\Engines\PHP;

use phpseclib3\Math\BigInteger\Engines\PHP;

/**
 * PHP Modular Exponentiation Engine
 *
 * @author  Jim Wigginton <[email protected]>
 */
abstract class Base extends PHP
{
    /**
     * Cache constants
     *
     * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
     *
     */
    const VARIABLE = 0;
    /**
     * $cache[self::DATA] contains the cached data.
     *
     */
    const DATA = 1;

    /**
     * Test for engine validity
     *
     * @return bool
     */
    public static function isValidEngine()
    {
        return static::class != __CLASS__;
    }

    /**
     * Performs modular exponentiation.
     *
     * The most naive approach to modular exponentiation has very unreasonable requirements, and
     * and although the approach involving repeated squaring does vastly better, it, too, is impractical
     * for our purposes.  The reason being that division - by far the most complicated and time-consuming
     * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
     *
     * Modular reductions resolve this issue.  Although an individual modular reduction takes more time
     * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
     *
     * The two most commonly used modular reductions are Barrett and Montgomery reduction.  Montgomery reduction,
     * although faster, only works when the gcd of the modulo and of the base being used is 1.  In RSA, when the
     * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
     * the product of two odd numbers is odd), but what about when RSA isn't used?
     *
     * In contrast, Barrett reduction has no such constraint.  As such, some bigint implementations perform a
     * Barrett reduction after every operation in the modpow function.  Others perform Barrett reductions when the
     * modulo is even and Montgomery reductions when the modulo is odd.  BigInteger.java's modPow method, however,
     * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
     * the other, a power of two - and recombine them, later.  This is the method that this modPow function uses.
     * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
     *
     * @param PHP $x
     * @param PHP $e
     * @param PHP $n
     * @param string $class
     * @return PHP
     */
    protected static function powModHelper(PHP $x, PHP $e, PHP $n, $class)
    {
        if (empty($e->value)) {
            $temp = new $class();
            $temp->value = [1];
            return $x->normalize($temp);
        }

        if ($e->value == [1]) {
            list(, $temp) = $x->divide($n);
            return $x->normalize($temp);
        }

        if ($e->value == [2]) {
            $temp = new $class();
            $temp->value = $class::square($x->value);
            list(, $temp) = $temp->divide($n);
            return $x->normalize($temp);
        }

        return $x->normalize(static::slidingWindow($x, $e, $n, $class));
    }

    /**
     * Modular reduction preparation
     *
     * @param array $x
     * @param array $n
     * @param string $class
     * @see self::slidingWindow()
     * @return array
     */
    protected static function prepareReduce(array $x, array $n, $class)
    {
        return static::reduce($x, $n, $class);
    }

    /**
     * Modular multiply
     *
     * @param array $x
     * @param array $y
     * @param array $n
     * @param string $class
     * @see self::slidingWindow()
     * @return array
     */
    protected static function multiplyReduce(array $x, array $y, array $n, $class)
    {
        $temp = $class::multiplyHelper($x, false, $y, false);
        return static::reduce($temp[self::VALUE], $n, $class);
    }

    /**
     * Modular square
     *
     * @param array $x
     * @param array $n
     * @param string $class
     * @see self::slidingWindow()
     * @return array
     */
    protected static function squareReduce(array $x, array $n, $class)
    {
        return static::reduce($class::square($x), $n, $class);
    }
}