18
|
1 |
<?php |
|
2 |
|
|
3 |
/** |
|
4 |
* Class ParagonIE_Sodium_Core_Ristretto255 |
|
5 |
*/ |
|
6 |
class ParagonIE_Sodium_Core_Ristretto255 extends ParagonIE_Sodium_Core_Ed25519 |
|
7 |
{ |
|
8 |
const crypto_core_ristretto255_HASHBYTES = 64; |
|
9 |
const HASH_SC_L = 48; |
|
10 |
const CORE_H2C_SHA256 = 1; |
|
11 |
const CORE_H2C_SHA512 = 2; |
|
12 |
|
|
13 |
/** |
|
14 |
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f |
|
15 |
* @param int $b |
|
16 |
* @return ParagonIE_Sodium_Core_Curve25519_Fe |
|
17 |
*/ |
|
18 |
public static function fe_cneg(ParagonIE_Sodium_Core_Curve25519_Fe $f, $b) |
|
19 |
{ |
|
20 |
$negf = self::fe_neg($f); |
|
21 |
return self::fe_cmov($f, $negf, $b); |
|
22 |
} |
|
23 |
|
|
24 |
/** |
|
25 |
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f |
|
26 |
* @return ParagonIE_Sodium_Core_Curve25519_Fe |
|
27 |
* @throws SodiumException |
|
28 |
*/ |
|
29 |
public static function fe_abs(ParagonIE_Sodium_Core_Curve25519_Fe $f) |
|
30 |
{ |
|
31 |
return self::fe_cneg($f, self::fe_isnegative($f)); |
|
32 |
} |
|
33 |
|
|
34 |
/** |
|
35 |
* Returns 0 if this field element results in all NUL bytes. |
|
36 |
* |
|
37 |
* @internal You should not use this directly from another application |
|
38 |
* |
|
39 |
* @param ParagonIE_Sodium_Core_Curve25519_Fe $f |
|
40 |
* @return int |
|
41 |
* @throws SodiumException |
|
42 |
*/ |
|
43 |
public static function fe_iszero(ParagonIE_Sodium_Core_Curve25519_Fe $f) |
|
44 |
{ |
|
45 |
static $zero; |
|
46 |
if ($zero === null) { |
|
47 |
$zero = str_repeat("\x00", 32); |
|
48 |
} |
|
49 |
/** @var string $zero */ |
|
50 |
$str = self::fe_tobytes($f); |
|
51 |
|
|
52 |
$d = 0; |
|
53 |
for ($i = 0; $i < 32; ++$i) { |
|
54 |
$d |= self::chrToInt($str[$i]); |
|
55 |
} |
|
56 |
return (($d - 1) >> 31) & 1; |
|
57 |
} |
|
58 |
|
|
59 |
|
|
60 |
/** |
|
61 |
* @param ParagonIE_Sodium_Core_Curve25519_Fe $u |
|
62 |
* @param ParagonIE_Sodium_Core_Curve25519_Fe $v |
|
63 |
* @return array{x: ParagonIE_Sodium_Core_Curve25519_Fe, nonsquare: int} |
|
64 |
* |
|
65 |
* @throws SodiumException |
|
66 |
*/ |
|
67 |
public static function ristretto255_sqrt_ratio_m1( |
|
68 |
ParagonIE_Sodium_Core_Curve25519_Fe $u, |
|
69 |
ParagonIE_Sodium_Core_Curve25519_Fe $v |
|
70 |
) { |
|
71 |
$sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1); |
|
72 |
|
|
73 |
$v3 = self::fe_mul( |
|
74 |
self::fe_sq($v), |
|
75 |
$v |
|
76 |
); /* v3 = v^3 */ |
|
77 |
$x = self::fe_mul( |
|
78 |
self::fe_mul( |
|
79 |
self::fe_sq($v3), |
|
80 |
$u |
|
81 |
), |
|
82 |
$v |
|
83 |
); /* x = uv^7 */ |
|
84 |
|
|
85 |
$x = self::fe_mul( |
|
86 |
self::fe_mul( |
|
87 |
self::fe_pow22523($x), /* x = (uv^7)^((q-5)/8) */ |
|
88 |
$v3 |
|
89 |
), |
|
90 |
$u |
|
91 |
); /* x = uv^3(uv^7)^((q-5)/8) */ |
|
92 |
|
|
93 |
$vxx = self::fe_mul( |
|
94 |
self::fe_sq($x), |
|
95 |
$v |
|
96 |
); /* vx^2 */ |
|
97 |
|
|
98 |
$m_root_check = self::fe_sub($vxx, $u); /* vx^2-u */ |
|
99 |
$p_root_check = self::fe_add($vxx, $u); /* vx^2+u */ |
|
100 |
$f_root_check = self::fe_mul($u, $sqrtm1); /* u*sqrt(-1) */ |
|
101 |
$f_root_check = self::fe_add($vxx, $f_root_check); /* vx^2+u*sqrt(-1) */ |
|
102 |
|
|
103 |
$has_m_root = self::fe_iszero($m_root_check); |
|
104 |
$has_p_root = self::fe_iszero($p_root_check); |
|
105 |
$has_f_root = self::fe_iszero($f_root_check); |
|
106 |
|
|
107 |
$x_sqrtm1 = self::fe_mul($x, $sqrtm1); /* x*sqrt(-1) */ |
|
108 |
|
|
109 |
$x = self::fe_abs( |
|
110 |
self::fe_cmov($x, $x_sqrtm1, $has_p_root | $has_f_root) |
|
111 |
); |
|
112 |
return array( |
|
113 |
'x' => $x, |
|
114 |
'nonsquare' => $has_m_root | $has_p_root |
|
115 |
); |
|
116 |
} |
|
117 |
|
|
118 |
/** |
|
119 |
* @param string $s |
|
120 |
* @return int |
|
121 |
* @throws SodiumException |
|
122 |
*/ |
|
123 |
public static function ristretto255_point_is_canonical($s) |
|
124 |
{ |
|
125 |
$c = (self::chrToInt($s[31]) & 0x7f) ^ 0x7f; |
|
126 |
for ($i = 30; $i > 0; --$i) { |
|
127 |
$c |= self::chrToInt($s[$i]) ^ 0xff; |
|
128 |
} |
|
129 |
$c = ($c - 1) >> 8; |
|
130 |
$d = (0xed - 1 - self::chrToInt($s[0])) >> 8; |
|
131 |
$e = self::chrToInt($s[31]) >> 7; |
|
132 |
|
|
133 |
return 1 - ((($c & $d) | $e | self::chrToInt($s[0])) & 1); |
|
134 |
} |
|
135 |
|
|
136 |
/** |
|
137 |
* @param string $s |
|
138 |
* @param bool $skipCanonicalCheck |
|
139 |
* @return array{h: ParagonIE_Sodium_Core_Curve25519_Ge_P3, res: int} |
|
140 |
* @throws SodiumException |
|
141 |
*/ |
|
142 |
public static function ristretto255_frombytes($s, $skipCanonicalCheck = false) |
|
143 |
{ |
|
144 |
if (!$skipCanonicalCheck) { |
|
145 |
if (!self::ristretto255_point_is_canonical($s)) { |
|
146 |
throw new SodiumException('S is not canonical'); |
|
147 |
} |
|
148 |
} |
|
149 |
|
|
150 |
$s_ = self::fe_frombytes($s); |
|
151 |
$ss = self::fe_sq($s_); /* ss = s^2 */ |
|
152 |
|
|
153 |
$u1 = self::fe_sub(self::fe_1(), $ss); /* u1 = 1-ss */ |
|
154 |
$u1u1 = self::fe_sq($u1); /* u1u1 = u1^2 */ |
|
155 |
|
|
156 |
$u2 = self::fe_add(self::fe_1(), $ss); /* u2 = 1+ss */ |
|
157 |
$u2u2 = self::fe_sq($u2); /* u2u2 = u2^2 */ |
|
158 |
|
|
159 |
$v = self::fe_mul( |
|
160 |
ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d), |
|
161 |
$u1u1 |
|
162 |
); /* v = d*u1^2 */ |
|
163 |
$v = self::fe_neg($v); /* v = -d*u1^2 */ |
|
164 |
$v = self::fe_sub($v, $u2u2); /* v = -(d*u1^2)-u2^2 */ |
|
165 |
$v_u2u2 = self::fe_mul($v, $u2u2); /* v_u2u2 = v*u2^2 */ |
|
166 |
|
|
167 |
// fe25519_1(one); |
|
168 |
// notsquare = ristretto255_sqrt_ratio_m1(inv_sqrt, one, v_u2u2); |
|
169 |
$one = self::fe_1(); |
|
170 |
$result = self::ristretto255_sqrt_ratio_m1($one, $v_u2u2); |
|
171 |
$inv_sqrt = $result['x']; |
|
172 |
$notsquare = $result['nonsquare']; |
|
173 |
|
|
174 |
$h = new ParagonIE_Sodium_Core_Curve25519_Ge_P3(); |
|
175 |
|
|
176 |
$h->X = self::fe_mul($inv_sqrt, $u2); |
|
177 |
$h->Y = self::fe_mul(self::fe_mul($inv_sqrt, $h->X), $v); |
|
178 |
|
|
179 |
$h->X = self::fe_mul($h->X, $s_); |
|
180 |
$h->X = self::fe_abs( |
|
181 |
self::fe_add($h->X, $h->X) |
|
182 |
); |
|
183 |
$h->Y = self::fe_mul($u1, $h->Y); |
|
184 |
$h->Z = self::fe_1(); |
|
185 |
$h->T = self::fe_mul($h->X, $h->Y); |
|
186 |
|
|
187 |
$res = - ((1 - $notsquare) | self::fe_isnegative($h->T) | self::fe_iszero($h->Y)); |
|
188 |
return array('h' => $h, 'res' => $res); |
|
189 |
} |
|
190 |
|
|
191 |
/** |
|
192 |
* @param ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h |
|
193 |
* @return string |
|
194 |
* @throws SodiumException |
|
195 |
*/ |
|
196 |
public static function ristretto255_p3_tobytes(ParagonIE_Sodium_Core_Curve25519_Ge_P3 $h) |
|
197 |
{ |
|
198 |
$sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1); |
|
199 |
$invsqrtamd = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$invsqrtamd); |
|
200 |
|
|
201 |
$u1 = self::fe_add($h->Z, $h->Y); /* u1 = Z+Y */ |
|
202 |
$zmy = self::fe_sub($h->Z, $h->Y); /* zmy = Z-Y */ |
|
203 |
$u1 = self::fe_mul($u1, $zmy); /* u1 = (Z+Y)*(Z-Y) */ |
|
204 |
$u2 = self::fe_mul($h->X, $h->Y); /* u2 = X*Y */ |
|
205 |
|
|
206 |
$u1_u2u2 = self::fe_mul(self::fe_sq($u2), $u1); /* u1_u2u2 = u1*u2^2 */ |
|
207 |
$one = self::fe_1(); |
|
208 |
|
|
209 |
// fe25519_1(one); |
|
210 |
// (void) ristretto255_sqrt_ratio_m1(inv_sqrt, one, u1_u2u2); |
|
211 |
$result = self::ristretto255_sqrt_ratio_m1($one, $u1_u2u2); |
|
212 |
$inv_sqrt = $result['x']; |
|
213 |
|
|
214 |
$den1 = self::fe_mul($inv_sqrt, $u1); /* den1 = inv_sqrt*u1 */ |
|
215 |
$den2 = self::fe_mul($inv_sqrt, $u2); /* den2 = inv_sqrt*u2 */ |
|
216 |
$z_inv = self::fe_mul($h->T, self::fe_mul($den1, $den2)); /* z_inv = den1*den2*T */ |
|
217 |
|
|
218 |
$ix = self::fe_mul($h->X, $sqrtm1); /* ix = X*sqrt(-1) */ |
|
219 |
$iy = self::fe_mul($h->Y, $sqrtm1); /* iy = Y*sqrt(-1) */ |
|
220 |
$eden = self::fe_mul($den1, $invsqrtamd); |
|
221 |
|
|
222 |
$t_z_inv = self::fe_mul($h->T, $z_inv); /* t_z_inv = T*z_inv */ |
|
223 |
$rotate = self::fe_isnegative($t_z_inv); |
|
224 |
|
|
225 |
$x_ = self::fe_copy($h->X); |
|
226 |
$y_ = self::fe_copy($h->Y); |
|
227 |
$den_inv = self::fe_copy($den2); |
|
228 |
|
|
229 |
$x_ = self::fe_cmov($x_, $iy, $rotate); |
|
230 |
$y_ = self::fe_cmov($y_, $ix, $rotate); |
|
231 |
$den_inv = self::fe_cmov($den_inv, $eden, $rotate); |
|
232 |
|
|
233 |
$x_z_inv = self::fe_mul($x_, $z_inv); |
|
234 |
$y_ = self::fe_cneg($y_, self::fe_isnegative($x_z_inv)); |
|
235 |
|
|
236 |
|
|
237 |
// fe25519_sub(s_, h->Z, y_); |
|
238 |
// fe25519_mul(s_, den_inv, s_); |
|
239 |
// fe25519_abs(s_, s_); |
|
240 |
// fe25519_tobytes(s, s_); |
|
241 |
return self::fe_tobytes( |
|
242 |
self::fe_abs( |
|
243 |
self::fe_mul( |
|
244 |
$den_inv, |
|
245 |
self::fe_sub($h->Z, $y_) |
|
246 |
) |
|
247 |
) |
|
248 |
); |
|
249 |
} |
|
250 |
|
|
251 |
/** |
|
252 |
* @param ParagonIE_Sodium_Core_Curve25519_Fe $t |
|
253 |
* @return ParagonIE_Sodium_Core_Curve25519_Ge_P3 |
|
254 |
* |
|
255 |
* @throws SodiumException |
|
256 |
*/ |
|
257 |
public static function ristretto255_elligator(ParagonIE_Sodium_Core_Curve25519_Fe $t) |
|
258 |
{ |
|
259 |
$sqrtm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtm1); |
|
260 |
$onemsqd = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$onemsqd); |
|
261 |
$d = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$d); |
|
262 |
$sqdmone = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqdmone); |
|
263 |
$sqrtadm1 = ParagonIE_Sodium_Core_Curve25519_Fe::fromArray(self::$sqrtadm1); |
|
264 |
|
|
265 |
$one = self::fe_1(); |
|
266 |
$r = self::fe_mul($sqrtm1, self::fe_sq($t)); /* r = sqrt(-1)*t^2 */ |
|
267 |
$u = self::fe_mul(self::fe_add($r, $one), $onemsqd); /* u = (r+1)*(1-d^2) */ |
|
268 |
$c = self::fe_neg(self::fe_1()); /* c = -1 */ |
|
269 |
$rpd = self::fe_add($r, $d); /* rpd = r+d */ |
|
270 |
|
|
271 |
$v = self::fe_mul( |
|
272 |
self::fe_sub( |
|
273 |
$c, |
|
274 |
self::fe_mul($r, $d) |
|
275 |
), |
|
276 |
$rpd |
|
277 |
); /* v = (c-r*d)*(r+d) */ |
|
278 |
|
|
279 |
$result = self::ristretto255_sqrt_ratio_m1($u, $v); |
|
280 |
$s = $result['x']; |
|
281 |
$wasnt_square = 1 - $result['nonsquare']; |
|
282 |
|
|
283 |
$s_prime = self::fe_neg( |
|
284 |
self::fe_abs( |
|
285 |
self::fe_mul($s, $t) |
|
286 |
) |
|
287 |
); /* s_prime = -|s*t| */ |
|
288 |
$s = self::fe_cmov($s, $s_prime, $wasnt_square); |
|
289 |
$c = self::fe_cmov($c, $r, $wasnt_square); |
|
290 |
|
|
291 |
// fe25519_sub(n, r, one); /* n = r-1 */ |
|
292 |
// fe25519_mul(n, n, c); /* n = c*(r-1) */ |
|
293 |
// fe25519_mul(n, n, ed25519_sqdmone); /* n = c*(r-1)*(d-1)^2 */ |
|
294 |
// fe25519_sub(n, n, v); /* n = c*(r-1)*(d-1)^2-v */ |
|
295 |
$n = self::fe_sub( |
|
296 |
self::fe_mul( |
|
297 |
self::fe_mul( |
|
298 |
self::fe_sub($r, $one), |
|
299 |
$c |
|
300 |
), |
|
301 |
$sqdmone |
|
302 |
), |
|
303 |
$v |
|
304 |
); /* n = c*(r-1)*(d-1)^2-v */ |
|
305 |
|
|
306 |
$w0 = self::fe_mul( |
|
307 |
self::fe_add($s, $s), |
|
308 |
$v |
|
309 |
); /* w0 = 2s*v */ |
|
310 |
|
|
311 |
$w1 = self::fe_mul($n, $sqrtadm1); /* w1 = n*sqrt(ad-1) */ |
|
312 |
$ss = self::fe_sq($s); /* ss = s^2 */ |
|
313 |
$w2 = self::fe_sub($one, $ss); /* w2 = 1-s^2 */ |
|
314 |
$w3 = self::fe_add($one, $ss); /* w3 = 1+s^2 */ |
|
315 |
|
|
316 |
return new ParagonIE_Sodium_Core_Curve25519_Ge_P3( |
|
317 |
self::fe_mul($w0, $w3), |
|
318 |
self::fe_mul($w2, $w1), |
|
319 |
self::fe_mul($w1, $w3), |
|
320 |
self::fe_mul($w0, $w2) |
|
321 |
); |
|
322 |
} |
|
323 |
|
|
324 |
/** |
|
325 |
* @param string $h |
|
326 |
* @return string |
|
327 |
* @throws SodiumException |
|
328 |
*/ |
|
329 |
public static function ristretto255_from_hash($h) |
|
330 |
{ |
|
331 |
if (self::strlen($h) !== 64) { |
|
332 |
throw new SodiumException('Hash must be 64 bytes'); |
|
333 |
} |
|
334 |
//fe25519_frombytes(r0, h); |
|
335 |
//fe25519_frombytes(r1, h + 32); |
|
336 |
$r0 = self::fe_frombytes(self::substr($h, 0, 32)); |
|
337 |
$r1 = self::fe_frombytes(self::substr($h, 32, 32)); |
|
338 |
|
|
339 |
//ristretto255_elligator(&p0, r0); |
|
340 |
//ristretto255_elligator(&p1, r1); |
|
341 |
$p0 = self::ristretto255_elligator($r0); |
|
342 |
$p1 = self::ristretto255_elligator($r1); |
|
343 |
|
|
344 |
//ge25519_p3_to_cached(&p1_cached, &p1); |
|
345 |
//ge25519_add_cached(&p_p1p1, &p0, &p1_cached); |
|
346 |
$p_p1p1 = self::ge_add( |
|
347 |
$p0, |
|
348 |
self::ge_p3_to_cached($p1) |
|
349 |
); |
|
350 |
|
|
351 |
//ge25519_p1p1_to_p3(&p, &p_p1p1); |
|
352 |
//ristretto255_p3_tobytes(s, &p); |
|
353 |
return self::ristretto255_p3_tobytes( |
|
354 |
self::ge_p1p1_to_p3($p_p1p1) |
|
355 |
); |
|
356 |
} |
|
357 |
|
|
358 |
/** |
|
359 |
* @param string $p |
|
360 |
* @return int |
|
361 |
* @throws SodiumException |
|
362 |
*/ |
|
363 |
public static function is_valid_point($p) |
|
364 |
{ |
|
365 |
$result = self::ristretto255_frombytes($p); |
|
366 |
if ($result['res'] !== 0) { |
|
367 |
return 0; |
|
368 |
} |
|
369 |
return 1; |
|
370 |
} |
|
371 |
|
|
372 |
/** |
|
373 |
* @param string $p |
|
374 |
* @param string $q |
|
375 |
* @return string |
|
376 |
* @throws SodiumException |
|
377 |
*/ |
|
378 |
public static function ristretto255_add($p, $q) |
|
379 |
{ |
|
380 |
$p_res = self::ristretto255_frombytes($p); |
|
381 |
$q_res = self::ristretto255_frombytes($q); |
|
382 |
if ($p_res['res'] !== 0 || $q_res['res'] !== 0) { |
|
383 |
throw new SodiumException('Could not add points'); |
|
384 |
} |
|
385 |
$p_p3 = $p_res['h']; |
|
386 |
$q_p3 = $q_res['h']; |
|
387 |
$q_cached = self::ge_p3_to_cached($q_p3); |
|
388 |
$r_p1p1 = self::ge_add($p_p3, $q_cached); |
|
389 |
$r_p3 = self::ge_p1p1_to_p3($r_p1p1); |
|
390 |
return self::ristretto255_p3_tobytes($r_p3); |
|
391 |
} |
|
392 |
|
|
393 |
/** |
|
394 |
* @param string $p |
|
395 |
* @param string $q |
|
396 |
* @return string |
|
397 |
* @throws SodiumException |
|
398 |
*/ |
|
399 |
public static function ristretto255_sub($p, $q) |
|
400 |
{ |
|
401 |
$p_res = self::ristretto255_frombytes($p); |
|
402 |
$q_res = self::ristretto255_frombytes($q); |
|
403 |
if ($p_res['res'] !== 0 || $q_res['res'] !== 0) { |
|
404 |
throw new SodiumException('Could not add points'); |
|
405 |
} |
|
406 |
$p_p3 = $p_res['h']; |
|
407 |
$q_p3 = $q_res['h']; |
|
408 |
$q_cached = self::ge_p3_to_cached($q_p3); |
|
409 |
$r_p1p1 = self::ge_sub($p_p3, $q_cached); |
|
410 |
$r_p3 = self::ge_p1p1_to_p3($r_p1p1); |
|
411 |
return self::ristretto255_p3_tobytes($r_p3); |
|
412 |
} |
|
413 |
|
|
414 |
|
|
415 |
/** |
|
416 |
* @param int $hLen |
|
417 |
* @param ?string $ctx |
|
418 |
* @param string $msg |
|
419 |
* @return string |
|
420 |
* @throws SodiumException |
|
421 |
* @psalm-suppress PossiblyInvalidArgument hash API |
|
422 |
*/ |
|
423 |
protected static function h2c_string_to_hash_sha256($hLen, $ctx, $msg) |
|
424 |
{ |
|
425 |
$h = array_fill(0, $hLen, 0); |
|
426 |
$ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0; |
|
427 |
if ($hLen > 0xff) { |
|
428 |
throw new SodiumException('Hash must be less than 256 bytes'); |
|
429 |
} |
|
430 |
|
|
431 |
if ($ctx_len > 0xff) { |
|
432 |
$st = hash_init('sha256'); |
|
433 |
self::hash_update($st, "H2C-OVERSIZE-DST-"); |
|
434 |
self::hash_update($st, $ctx); |
|
435 |
$ctx = hash_final($st, true); |
|
436 |
$ctx_len = 32; |
|
437 |
} |
|
438 |
$t = array(0, $hLen, 0); |
|
439 |
$ux = str_repeat("\0", 64); |
|
440 |
$st = hash_init('sha256'); |
|
441 |
self::hash_update($st, $ux); |
|
442 |
self::hash_update($st, $msg); |
|
443 |
self::hash_update($st, self::intArrayToString($t)); |
|
444 |
self::hash_update($st, $ctx); |
|
445 |
self::hash_update($st, self::intToChr($ctx_len)); |
|
446 |
$u0 = hash_final($st, true); |
|
447 |
|
|
448 |
for ($i = 0; $i < $hLen; $i += 64) { |
|
449 |
$ux = self::xorStrings($ux, $u0); |
|
450 |
++$t[2]; |
|
451 |
$st = hash_init('sha256'); |
|
452 |
self::hash_update($st, $ux); |
|
453 |
self::hash_update($st, self::intToChr($t[2])); |
|
454 |
self::hash_update($st, $ctx); |
|
455 |
self::hash_update($st, self::intToChr($ctx_len)); |
|
456 |
$ux = hash_final($st, true); |
|
457 |
$amount = min($hLen - $i, 64); |
|
458 |
for ($j = 0; $j < $amount; ++$j) { |
|
459 |
$h[$i + $j] = self::chrToInt($ux[$i]); |
|
460 |
} |
|
461 |
} |
|
462 |
return self::intArrayToString(array_slice($h, 0, $hLen)); |
|
463 |
} |
|
464 |
|
|
465 |
/** |
|
466 |
* @param int $hLen |
|
467 |
* @param ?string $ctx |
|
468 |
* @param string $msg |
|
469 |
* @return string |
|
470 |
* @throws SodiumException |
|
471 |
* @psalm-suppress PossiblyInvalidArgument hash API |
|
472 |
*/ |
|
473 |
protected static function h2c_string_to_hash_sha512($hLen, $ctx, $msg) |
|
474 |
{ |
|
475 |
$h = array_fill(0, $hLen, 0); |
|
476 |
$ctx_len = !is_null($ctx) ? self::strlen($ctx) : 0; |
|
477 |
if ($hLen > 0xff) { |
|
478 |
throw new SodiumException('Hash must be less than 256 bytes'); |
|
479 |
} |
|
480 |
|
|
481 |
if ($ctx_len > 0xff) { |
|
482 |
$st = hash_init('sha256'); |
|
483 |
self::hash_update($st, "H2C-OVERSIZE-DST-"); |
|
484 |
self::hash_update($st, $ctx); |
|
485 |
$ctx = hash_final($st, true); |
|
486 |
$ctx_len = 32; |
|
487 |
} |
|
488 |
$t = array(0, $hLen, 0); |
|
489 |
$ux = str_repeat("\0", 128); |
|
490 |
$st = hash_init('sha512'); |
|
491 |
self::hash_update($st, $ux); |
|
492 |
self::hash_update($st, $msg); |
|
493 |
self::hash_update($st, self::intArrayToString($t)); |
|
494 |
self::hash_update($st, $ctx); |
|
495 |
self::hash_update($st, self::intToChr($ctx_len)); |
|
496 |
$u0 = hash_final($st, true); |
|
497 |
|
|
498 |
for ($i = 0; $i < $hLen; $i += 128) { |
|
499 |
$ux = self::xorStrings($ux, $u0); |
|
500 |
++$t[2]; |
|
501 |
$st = hash_init('sha512'); |
|
502 |
self::hash_update($st, $ux); |
|
503 |
self::hash_update($st, self::intToChr($t[2])); |
|
504 |
self::hash_update($st, $ctx); |
|
505 |
self::hash_update($st, self::intToChr($ctx_len)); |
|
506 |
$ux = hash_final($st, true); |
|
507 |
$amount = min($hLen - $i, 128); |
|
508 |
for ($j = 0; $j < $amount; ++$j) { |
|
509 |
$h[$i + $j] = self::chrToInt($ux[$i]); |
|
510 |
} |
|
511 |
} |
|
512 |
return self::intArrayToString(array_slice($h, 0, $hLen)); |
|
513 |
} |
|
514 |
|
|
515 |
/** |
|
516 |
* @param int $hLen |
|
517 |
* @param ?string $ctx |
|
518 |
* @param string $msg |
|
519 |
* @param int $hash_alg |
|
520 |
* @return string |
|
521 |
* @throws SodiumException |
|
522 |
*/ |
|
523 |
public static function h2c_string_to_hash($hLen, $ctx, $msg, $hash_alg) |
|
524 |
{ |
|
525 |
switch ($hash_alg) { |
|
526 |
case self::CORE_H2C_SHA256: |
|
527 |
return self::h2c_string_to_hash_sha256($hLen, $ctx, $msg); |
|
528 |
case self::CORE_H2C_SHA512: |
|
529 |
return self::h2c_string_to_hash_sha512($hLen, $ctx, $msg); |
|
530 |
default: |
|
531 |
throw new SodiumException('Invalid H2C hash algorithm'); |
|
532 |
} |
|
533 |
} |
|
534 |
|
|
535 |
/** |
|
536 |
* @param ?string $ctx |
|
537 |
* @param string $msg |
|
538 |
* @param int $hash_alg |
|
539 |
* @return string |
|
540 |
* @throws SodiumException |
|
541 |
*/ |
|
542 |
protected static function _string_to_element($ctx, $msg, $hash_alg) |
|
543 |
{ |
|
544 |
return self::ristretto255_from_hash( |
|
545 |
self::h2c_string_to_hash(self::crypto_core_ristretto255_HASHBYTES, $ctx, $msg, $hash_alg) |
|
546 |
); |
|
547 |
} |
|
548 |
|
|
549 |
/** |
|
550 |
* @return string |
|
551 |
* @throws SodiumException |
|
552 |
* @throws Exception |
|
553 |
*/ |
|
554 |
public static function ristretto255_random() |
|
555 |
{ |
|
556 |
return self::ristretto255_from_hash( |
|
557 |
ParagonIE_Sodium_Compat::randombytes_buf(self::crypto_core_ristretto255_HASHBYTES) |
|
558 |
); |
|
559 |
} |
|
560 |
|
|
561 |
/** |
|
562 |
* @return string |
|
563 |
* @throws SodiumException |
|
564 |
*/ |
|
565 |
public static function ristretto255_scalar_random() |
|
566 |
{ |
|
567 |
return self::scalar_random(); |
|
568 |
} |
|
569 |
|
|
570 |
/** |
|
571 |
* @param string $s |
|
572 |
* @return string |
|
573 |
* @throws SodiumException |
|
574 |
*/ |
|
575 |
public static function ristretto255_scalar_complement($s) |
|
576 |
{ |
|
577 |
return self::scalar_complement($s); |
|
578 |
} |
|
579 |
|
|
580 |
|
|
581 |
/** |
|
582 |
* @param string $s |
|
583 |
* @return string |
|
584 |
*/ |
|
585 |
public static function ristretto255_scalar_invert($s) |
|
586 |
{ |
|
587 |
return self::sc25519_invert($s); |
|
588 |
} |
|
589 |
|
|
590 |
/** |
|
591 |
* @param string $s |
|
592 |
* @return string |
|
593 |
* @throws SodiumException |
|
594 |
*/ |
|
595 |
public static function ristretto255_scalar_negate($s) |
|
596 |
{ |
|
597 |
return self::scalar_negate($s); |
|
598 |
} |
|
599 |
|
|
600 |
/** |
|
601 |
* @param string $x |
|
602 |
* @param string $y |
|
603 |
* @return string |
|
604 |
*/ |
|
605 |
public static function ristretto255_scalar_add($x, $y) |
|
606 |
{ |
|
607 |
return self::scalar_add($x, $y); |
|
608 |
} |
|
609 |
|
|
610 |
/** |
|
611 |
* @param string $x |
|
612 |
* @param string $y |
|
613 |
* @return string |
|
614 |
*/ |
|
615 |
public static function ristretto255_scalar_sub($x, $y) |
|
616 |
{ |
|
617 |
return self::scalar_sub($x, $y); |
|
618 |
} |
|
619 |
|
|
620 |
/** |
|
621 |
* @param string $x |
|
622 |
* @param string $y |
|
623 |
* @return string |
|
624 |
*/ |
|
625 |
public static function ristretto255_scalar_mul($x, $y) |
|
626 |
{ |
|
627 |
return self::sc25519_mul($x, $y); |
|
628 |
} |
|
629 |
|
|
630 |
/** |
|
631 |
* @param string $ctx |
|
632 |
* @param string $msg |
|
633 |
* @param int $hash_alg |
|
634 |
* @return string |
|
635 |
* @throws SodiumException |
|
636 |
*/ |
|
637 |
public static function ristretto255_scalar_from_string($ctx, $msg, $hash_alg) |
|
638 |
{ |
|
639 |
$h = array_fill(0, 64, 0); |
|
640 |
$h_be = self::stringToIntArray( |
|
641 |
self::h2c_string_to_hash( |
|
642 |
self::HASH_SC_L, $ctx, $msg, $hash_alg |
|
643 |
) |
|
644 |
); |
|
645 |
|
|
646 |
for ($i = 0; $i < self::HASH_SC_L; ++$i) { |
|
647 |
$h[$i] = $h_be[self::HASH_SC_L - 1 - $i]; |
|
648 |
} |
|
649 |
return self::ristretto255_scalar_reduce(self::intArrayToString($h)); |
|
650 |
} |
|
651 |
|
|
652 |
/** |
|
653 |
* @param string $s |
|
654 |
* @return string |
|
655 |
*/ |
|
656 |
public static function ristretto255_scalar_reduce($s) |
|
657 |
{ |
|
658 |
return self::sc_reduce($s); |
|
659 |
} |
|
660 |
|
|
661 |
/** |
|
662 |
* @param string $n |
|
663 |
* @param string $p |
|
664 |
* @return string |
|
665 |
* @throws SodiumException |
|
666 |
*/ |
|
667 |
public static function scalarmult_ristretto255($n, $p) |
|
668 |
{ |
|
669 |
if (self::strlen($n) !== 32) { |
|
670 |
throw new SodiumException('Scalar must be 32 bytes, ' . self::strlen($p) . ' given.'); |
|
671 |
} |
|
672 |
if (self::strlen($p) !== 32) { |
|
673 |
throw new SodiumException('Point must be 32 bytes, ' . self::strlen($p) . ' given.'); |
|
674 |
} |
|
675 |
$result = self::ristretto255_frombytes($p); |
|
676 |
if ($result['res'] !== 0) { |
|
677 |
throw new SodiumException('Could not multiply points'); |
|
678 |
} |
|
679 |
$P = $result['h']; |
|
680 |
|
|
681 |
$t = self::stringToIntArray($n); |
|
682 |
$t[31] &= 0x7f; |
|
683 |
$Q = self::ge_scalarmult(self::intArrayToString($t), $P); |
|
684 |
$q = self::ristretto255_p3_tobytes($Q); |
|
685 |
if (ParagonIE_Sodium_Compat::is_zero($q)) { |
|
686 |
throw new SodiumException('An unknown error has occurred'); |
|
687 |
} |
|
688 |
return $q; |
|
689 |
} |
|
690 |
|
|
691 |
/** |
|
692 |
* @param string $n |
|
693 |
* @return string |
|
694 |
* @throws SodiumException |
|
695 |
*/ |
|
696 |
public static function scalarmult_ristretto255_base($n) |
|
697 |
{ |
|
698 |
$t = self::stringToIntArray($n); |
|
699 |
$t[31] &= 0x7f; |
|
700 |
$Q = self::ge_scalarmult_base(self::intArrayToString($t)); |
|
701 |
$q = self::ristretto255_p3_tobytes($Q); |
|
702 |
if (ParagonIE_Sodium_Compat::is_zero($q)) { |
|
703 |
throw new SodiumException('An unknown error has occurred'); |
|
704 |
} |
|
705 |
return $q; |
|
706 |
} |
|
707 |
} |