|
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 } |