1<?php
2
3/**
4 * Class ParagonIE_Sodium_Core_Ristretto255
5 */
6class 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}
708