blob: 78fefd76d86307a4cf7c09ab9d941c2ecda652f9 [file] [log] [blame]
Nico Huberee52fbc2023-06-24 11:52:57 +00001<?php
2/*
3 * SimpleID
4 *
5 * Copyright (C) Kelvin Mo 2009
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * $Id$
22 */
23
24/**
25 * Abstraction library for multiple precision mathematics. This file uses either
26 * the GNU Multiple Precision Arithmic Libary (GMP) if it is installed, or the
27 * default BCMath library if it is not installed.
28 *
29 * @package simpleid
30 * @filesource
31 */
32
33
34if (function_exists('gmp_init')) {
35 /**
36 * Defines whether the GMP library is available.
37 */
38 define('BIGNUM_GMP', true);
39} else {
40 /** @ignore */
41 define('BIGNUM_GMP', false);
42}
43
44/**
45 * Returns whether either the GMP or the BCMath library is installed. If neither
46 * of these libraries are installed, the functions in this file will not work.
47 *
48 * @return bool true if either GMP or BCMath is installed.
49 */
50function bignum_loaded() {
51 return (function_exists('gmp_init') || function_exists('bcadd'));
52}
53
54/**
55 * Creates a bignum.
56 *
57 * @param mixed $str An integer, a string in base 2 to 36, or a byte stream in base 256
58 * @param int $base an integer between 2 and 36, or 256
59 * @return resource a bignum
60 */
61function bignum_new($str, $base = 10) {
62 switch ($base) {
63 case 10:
64 if (BIGNUM_GMP) {
65 return gmp_init($str, 10);
66 } else {
67 return $str;
68 }
69 break;
70 case 256:
71 $bytes = array_merge(unpack('C*', $str));
72
73 $num = bignum_new(0);
74
75 foreach ($bytes as $byte) {
76 $num = bignum_mul($num, 256);
77 $num = bignum_add($num, bignum_new($byte));
78 }
79 return $num;
80 break;
81 default:
82 if (!is_integer($base) || ($base < 2) || ($base > 36)) return FALSE;
83
84 $num = bignum_new(0);
85
86 for ($i = 0; $i < strlen($str); $i++) {
87 $num = bignum_mul($num, $base);
88 $num = bignum_add($num, bignum_new(base_convert($str[$i], $base, 10)));
89 }
90 return $num;
91 }
92
93 return FALSE;
94}
95
96/**
97 * Converts a bignum into a string representation (base 2 to 36) or a byte stream
98 * (base 256)
99 *
100 * @param resource $num the bignum
101 * @param int $base an integer between 2 and 36, or 256
102 * @return string the converted bignum
103 */
104function bignum_val($num, $base = 10) {
105 switch ($base) {
106 case 10:
107 if (BIGNUM_GMP) {
108 $base10 = gmp_strval($num, 10);
109 } else {
110 $base10 = $num;
111 }
112
113 return $base10;
114 break;
115
116 case 256:
117 $cmp = bignum_cmp($num, 0);
118 if ($cmp < 0) {
119 return FALSE;
120 }
121
122 if ($cmp == 0) {
123 return "\x00";
124 }
125
126 $bytes = array();
127
128 while (bignum_cmp($num, 0) > 0) {
129 array_unshift($bytes, bignum_mod($num, 256));
130 $num = bignum_div($num, 256);
131 }
132
133 if ($bytes && ($bytes[0] > 127)) {
134 array_unshift($bytes, 0);
135 }
136
137 $byte_stream = '';
138 foreach ($bytes as $byte) {
139 $byte_stream .= pack('C', $byte);
140 }
141
142 return $byte_stream;
143 break;
144 default:
145 if (!is_integer($base) || ($base < 2) || ($base > 36)) return FALSE;
146
147 $cmp = bignum_cmp($num, 0);
148 if ($cmp < 0) {
149 return FALSE;
150 }
151
152 if ($cmp == 0) {
153 return "0";
154 }
155
156 $str = '';
157 while (bignum_cmp($num, 0) > 0) {
158 $r = intval(bignum_val(bignum_mod($num, $base)));
159 $str = base_convert($r, 10, $base) . $str;
160 $num = bignum_div($num, $base);
161 }
162
163 return $str;
164 }
165
166 return FALSE;
167}
168
169/**
170 * Adds two bignums
171 *
172 * @param resource $a
173 * @param resource $b
174 * @return resource a bignum representing a + b
175 */
176function bignum_add($a, $b) {
177 if (BIGNUM_GMP) {
178 return gmp_add($a, $b);
179 } else {
180 return bcadd($a, $b);
181 }
182}
183
184/**
185 * Multiplies two bignums
186 *
187 * @param resource $a
188 * @param resource $b
189 * @return resource a bignum representing a * b
190 */
191function bignum_mul($a, $b) {
192 if (BIGNUM_GMP) {
193 return gmp_mul($a, $b);
194 } else {
195 return bcmul($a, $b);
196 }
197}
198
199/**
200 * Divides two bignums
201 *
202 * @param resource $a
203 * @param resource $b
204 * @return resource a bignum representing a / b
205 */
206function bignum_div($a, $b) {
207 if (BIGNUM_GMP) {
208 return gmp_div($a, $b);
209 } else {
210 return bcdiv($a, $b);
211 }
212}
213
214/**
215 * Raise base to power exp
216 *
217 * @param resource $base the base
218 * @param mixed $exp the exponent, as an integer or a bignum
219 * @return resource a bignum representing base ^ exp
220 */
221function bignum_pow($base, $exp) {
222 if (BIGNUM_GMP) {
223 if (is_resource($exp) && (get_resource_type($exp) == 'gmp')) $exp = gmp_intval($exp);
224 return gmp_pow($base, $exp);
225 } else {
226 return bcpow($base, $exp);
227 }
228}
229
230/**
231 * Returns n modulo d
232 *
233 * @param resource $n
234 * @param resource $d
235 * @return resource a bignum representing n mod d
236 */
237function bignum_mod($n, $d) {
238 if (BIGNUM_GMP) {
239 return gmp_mod($n, $d);
240 } else {
241 return bcmod($n, $d);
242 }
243}
244
245/**
246 * Raise a number into power with modulo
247 *
248 * @param resource $base the base
249 * @param resource $exp the exponent
250 * @param resource $mod the modulo
251 * @return resource a bignum representing base ^ exp mod mod
252 */
253function bignum_powmod($base, $exp, $mod) {
254 if (BIGNUM_GMP) {
255 return gmp_powm($base, $exp, $mod);
256 } elseif (function_exists('bcpowmod')) {
257 return bcpowmod($base, $exp, $mod);
258 } else {
259 $square = bignum_mod($base, $mod);
260 $result = 1;
261 while (bignum_cmp($exp, 0) > 0) {
262 if (bignum_mod($exp, 2)) {
263 $result = bignum_mod(bignum_mul($result, $square), $mod);
264 }
265 $square = bignum_mod(bignum_mul($square, $square), $mod);
266 $exp = bignum_div($exp, 2);
267 }
268 return $result;
269 }
270}
271
272/**
273 * Compares two bignum
274 *
275 * @param resource $a
276 * @param resource $b
277 * @return int positive value if a > b, zero if a = b and a negative value if a < b
278 */
279function bignum_cmp($a, $b) {
280 if (BIGNUM_GMP) {
281 return gmp_cmp($a, $b);
282 } else {
283 return bccomp($a, $b);
284 }
285}
286?>