File Coverage

blib/lib/Crypt/Perl/Ed25519/Math.pm
Criterion Covered Total %
statement 249 252 98.8
branch 21 24 87.5
condition 9 12 75.0
subroutine 36 36 100.0
pod 0 10 0.0
total 315 334 94.3


line stmt bran cond sub pod time code
1             package Crypt::Perl::Ed25519::Math;
2              
3 6     6   61646 use strict;
  6         20  
  6         140  
4 6     6   43 use warnings;
  6         11  
  6         5722  
5              
6             sub reduce {
7 51     51 0 10344 my ($r) = @_;
8              
9 51         90 my @x;
10              
11 51         124 for my $i ( 0 .. 63 ) {
12 3264         3795 $x[$i] = $r->[$i];
13 3264         3926 $r->[$i] = 0;
14             }
15              
16 51         267 modL( $r, \@x );
17              
18 51         198 return;
19             }
20              
21             # p and q are arrays of arrays; s is an array of numbers
22             sub scalarmult {
23 71     71 0 173 my ($p, $q, $s) = @_;
24              
25 71         568 @{$p}[0 .. 3] = ( [ gf0() ], [ gf1() ], [ gf1() ], [ gf0() ] );
  71         238  
26              
27 71         266 my $b;
28              
29 71         214 for my $i ( reverse( 0 .. 255 ) ) {
30 18176         43210 $b = ( $s->[ ( $i >> 3 ) | 0 ] >> ($i & 7) ) & 1;
31 18176         35922 _cswap( $p, $q, $b );
32 18176         38987 add( $q, $p );
33 18176         37876 add( $p, $p );
34 18176         40297 _cswap( $p, $q, $b );
35             }
36              
37 71         654 return;
38             }
39              
40             # p is an array of arrays; s is an array of numbers
41             sub scalarbase {
42 50     50 0 148 my ($p, $s) = @_;
43              
44 50         588 my @q = ( [ X() ], [ Y() ], [ gf1() ], [ gf0() ] );
45              
46 50         316 _M( $q[3], [X()], [Y()] );
47              
48 50         307 scalarmult($p, \@q, $s);
49             }
50              
51             # p is an array of arrays
52             sub pack {
53 50     50 0 163 my ($p) = @_;
54              
55 50         188 my $tx = [ gf0() ];
56 50         159 my $ty = [ gf0() ];
57 50         177 my $zi = [ gf0() ];
58              
59 50         242 _inv25519( $zi, $p->[2] );
60              
61 50         185 _M( $tx, $p->[0], $zi );
62 50         214 _M( $ty, $p->[1], $zi );
63              
64 50         244 my $r = _pack25519($ty);
65              
66 50         154 $r->[31] ^= (_par25519($tx) << 7);
67              
68 50         464 return $r;
69             }
70              
71             sub add {
72 36373     36373 0 56624 my ($p, $q) = @_;
73              
74 36373         114524 my $a = [ gf0() ];
75 36373         87267 my $b = [ gf0() ];
76 36373         82572 my $c = [ gf0() ];
77 36373         81356 my $d = [ gf0() ];
78 36373         89677 my $e = [ gf0() ];
79 36373         85917 my $f = [ gf0() ];
80 36373         77615 my $g = [ gf0() ];
81 36373         80293 my $h = [ gf0() ];
82 36373         83229 my $t = [ gf0() ];
83              
84 36373         78739 _Z($a, $p->[1], $p->[0]);
85 36373         70679 _Z($t, $q->[1], $q->[0]);
86 36373         73560 _M($a, $a, $t);
87 36373         77719 _A($b, $p->[0], $p->[1]);
88 36373         67302 _A($t, $q->[0], $q->[1]);
89 36373         67621 _M($b, $b, $t);
90 36373         74060 _M($c, $p->[3], $q->[3]);
91 36373         117653 _M($c, $c, [ D2() ]);
92 36373         101832 _M($d, $p->[2], $q->[2]);
93 36373         71480 _A($d, $d, $d);
94 36373         70810 _Z($e, $b, $a);
95 36373         66205 _Z($f, $d, $c);
96 36373         65267 _A($g, $d, $c);
97 36373         65875 _A($h, $b, $a);
98              
99 36373         66780 _M($p->[0], $e, $f);
100 36373         70589 _M($p->[1], $h, $g);
101 36373         70064 _M($p->[2], $g, $f);
102 36373         58842 _M($p->[3], $e, $h);
103             }
104              
105             sub modL {
106 66     66 0 2118 my ($r, $x) = @_;
107              
108 66         135 my ($k);
109              
110 66         169 for my $i ( reverse( 32 .. 63 ) ) {
111 2112         2505 my $carry = 0;
112              
113 2112         2607 my ($j, $k);
114              
115 2112         3563 for (
116             ($j = $i - 32), ($k = $i - 12);
117             $j < $k;
118             ++$j
119             ) {
120 42240         69039 $x->[$j] += $carry - 16 * $x->[$i] * (L())[$j - ($i - 32)];
121              
122             # originally “>> 8” rather than “/ 256”;
123 42240         62763 $carry = _floor( ($x->[$j] + 128) / 256 );
124              
125 42240         72513 $x->[$j] -= $carry * 256;
126             }
127              
128 2112         2524 $x->[$j] += $carry;
129 2112         3086 $x->[$i] = 0;
130             }
131              
132 66         95 my $carry = 0;
133              
134             # In Perl, -98 >> 4 = 1152921504606846969. :-<
135 66         146 my $x31_rshift_4 = _floor( $x->[31] / 16 );
136              
137 66         140 for my $j ( 0 .. 31 ) {
138 2112         3290 $x->[$j] += $carry - $x31_rshift_4 * (L())[$j];
139              
140             # originally “>> 8” rather than “/ 256”; we also need floor
141 2112         3180 $carry = _floor( $x->[$j] / 256 );
142              
143 2112         2991 $x->[$j] &= 255;
144             }
145              
146 66         973 $x->[$_] -= $carry * (L())[$_] for 0 .. 31;
147              
148 66         151 for my $i ( 0 .. 31 ) {
149 2112         2575 $x->[$i + 1] += $x->[$i] >> 8;
150 2112         2763 $r->[$i] = $x->[$i] & 255;
151             }
152              
153 66         135 return;
154             }
155              
156 6     6   41 use constant gf0 => (0) x 16;
  6         1105  
  6         577  
157              
158             #----------------------------------------------------------------------
159              
160 6     6   37 use constant gf1 => ( 1, (0) x 15 );
  6         10  
  6         409  
161              
162 6         441 use constant L => (
163             0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
164             0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
165             (0) x 15, 0x10,
166 6     6   40 );
  6         29  
167              
168 6         374 use constant D2 => (
169             0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,
170             0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406,
171 6     6   34 );
  6         9  
172              
173 6         407 use constant X => (
174             0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,
175             0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169,
176 6     6   35 );
  6         9  
177              
178 6         7960 use constant Y => (
179             0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
180             0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
181 6     6   35 );
  6         14  
182              
183 18076     18076   27132 sub _S { _M( $_[0], $_[1], $_[1] ) }
184              
185             sub _inv25519 {
186 50     50   150 my ($o, $i) = @_;
187              
188 50         115 my $c = [ @{$i}[0 .. 15] ];
  50         122  
189              
190 50         183 for my $a ( reverse( 0 .. 253 ) ) {
191 12700         23690 _S($c, $c);
192              
193 12700 100       20021 next if $a == 2;
194 12650 100       18262 next if $a == 4;
195              
196 12600         17259 _M( $c, $c, $i );
197             }
198              
199 50         214 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  50         141  
  50         122  
200              
201 50         232 return;
202             }
203              
204             sub _pack25519 {
205 205     205   333 my ($n) = @_;
206              
207 205         244 my $b;
208              
209 205         367 my $o = [];
210              
211 205         330 my $t = [ @{$n}[0 .. 15] ];
  205         604  
212              
213 205         554 my $m = [ gf0() ];
214              
215 205         456 _car25519($t) for 1 .. 3;
216              
217 205         390 for my $j (0, 1) {
218 410         608 $m->[0] = $t->[0] - 0xffed;
219              
220 410         604 for my $i ( 1 .. 14 ) {
221 5740         7476 $m->[$i] = $t->[$i] - 0xffff - (($m->[$i - 1] >> 16) & 1);
222 5740         7140 $m->[$i - 1] &= 0xffff;
223             }
224              
225 410         724 $m->[15] = $t->[15] - 0x7fff - (($m->[14] >> 16) & 1);
226              
227 410         650 $b = ($m->[15] >> 16) & 1;
228              
229 410         522 $m->[14] &= 0xffff;
230              
231 410         809 _sel25519( $t, $m, 1 - $b );
232             }
233              
234 205         348 for my $i ( 0 .. 15 ) {
235 3280         5123 $o->[2 * $i] = $t->[$i] & 0xff;
236 3280         5100 $o->[2 * $i + 1] = $t->[$i] >> 8;
237             }
238              
239 205         558 return $o;
240             }
241              
242             sub _par25519 {
243 71     71   160 my ($a) = @_;
244              
245 71         134 my $d = _pack25519($a);
246              
247 71         293 return $d->[0] & 1;
248             }
249              
250             # o, a, and b are arrays of numbers
251             sub _A {
252 181886     181886   248810 my ($o, $a, $b) = @_;
253              
254 181886         888903 $o->[$_] = $a->[$_] + $b->[$_] for 0 .. 15;
255              
256 181886         243247 return;
257             }
258              
259             # o, a, and b are arrays of numbers
260             sub _Z {
261 145524     145524   197938 my ($o, $a, $b) = @_;
262              
263 145524         726944 $o->[$_] = $a->[$_] - $b->[$_] for 0 .. 15;
264              
265 145524         197541 return;
266             }
267              
268             # o, a, and b are arrays of numbers
269             sub _M {
270 363670     363670   492911 my ($o, $a, $b) = @_;
271              
272 363670         748985 my @t = (0) x 31;
273              
274 363670         503772 for my $a_idx ( 0 .. 15 ) {
275 5818720         28727382 $t[$a_idx + $_] += $a->[$a_idx] * $b->[$_] for 0 .. 15;
276             }
277              
278             # $t->[15] left as-is
279 363670         467291 for my $t_idx ( 0 .. 14 ) {
280 5455050         6896941 $t[$t_idx] += 38 * $t[16 + $t_idx];
281             }
282              
283 363670         461134 my ($c, $v);
284              
285 363670         660912 _car25519(\@t);
286 363670         673550 _car25519(\@t);
287              
288 363670         553242 @{$o}[0 .. 15] = @t[0 .. 15];
  363670         664164  
289              
290 363670         758396 return;
291             }
292              
293             sub _car25519 {
294 727955     727955   938056 my ($o) = @_;
295              
296 727955         868680 my $c = 1;
297 727955         780251 my $v;
298              
299 727955         851826 for my $o_item ( @{$o}[0 .. 15] ) {
  727955         1100279  
300 11647280         12904274 $v = $o_item + $c + 65535;
301              
302             # c = Math.floor(v / 65536)
303 11647280         14486242 $c = int( $v / 65536 );
304 11647280 100       16678545 $c-- if $v < 0;
305              
306             # t0 = v - c * 65536
307 11647280         14207141 $o_item = $v - ($c * 65536);
308             }
309              
310 727955         983256 $o->[0] += $c - 1 + 37 * ($c - 1);
311              
312 727955         988950 return;
313             }
314              
315             # p and q are arrays of numbers
316             sub _sel25519 {
317 145818     145818   198506 my ($p, $q, $b) = @_;
318              
319             # $b is either 0 or 1.
320 145818   100     275710 my $c = $b && -1;
321              
322 145818         192392 for my $i ( 0 .. 15 ) {
323 2333088   100     3573180 my $t = $c && ($c & signed_xor($p->[$i], $q->[$i]));
324              
325 2333088 100       3557080 $p->[$i] = signed_xor($p->[$i], $t) if $t;
326 2333088 100       3731172 $q->[$i] = signed_xor($q->[$i], $t) if $t;
327             }
328             }
329              
330             # p and q are arrays of arrays
331             sub _cswap {
332 36352     36352   51222 my ($p, $q, $b) = @_;
333              
334 36352         52421 for my $i ( 0 .. 3 ) {
335 145408         220787 _sel25519( $p->[$i], $q->[$i], $b );
336             }
337             }
338              
339             # Perl’s ^ operator isn’t signed-savvy,
340             # so (-60116 ^ 0) = 18446744073709491500.
341             #
342             # TODO: add tests
343             sub signed_xor {
344              
345 3479608 50 25 3479608 0 7730521 if ( ($_[0] < 0) xor ($_[1] < 0) ) {
346 0         0 return ($_[0] ^ $_[1]) - ~0 - 1;
347             }
348              
349             # signs are same -> can use native xor
350 3479608         5251480 return $_[0] ^ $_[1];
351             }
352              
353             sub signed_or {
354              
355             # signs are same -> can use native xor
356 2016 50   2016 0 3137 if ( ($_[0] < 0) eq ($_[1] < 0) ) {
357 2016         3071 return $_[0] | $_[1];
358             }
359              
360 0         0 return ($_[0] | $_[1]) - ~0 - 1;
361             }
362              
363             #----------------------------------------------------------------------
364             # Verify logic
365              
366             sub unpackneg {
367 21     21 0 51 my ($r, $p) = @_;
368              
369 21         199 $_ = [ gf0() ] for my (
370             $t,
371             $chk,
372             $num,
373             $den,
374             $den2,
375             $den4,
376             $den6,
377             );
378              
379 21         122 _set25519( $r->[2], [ gf1() ]);
380              
381 21         109 _unpack25519($r->[1], $p);
382              
383 21         60 _S($num, $r->[1]);
384 21         128 _M($den, $num, [ D() ]);
385 21         112 _Z($num, $num, $r->[2]);
386 21         87 _A($den, $r->[2], $den);
387              
388 21         54 _S($den2, $den);
389 21         76 _S($den4, $den2);
390 21         66 _M($den6, $den4, $den2);
391 21         71 _M($t, $den6, $num);
392 21         55 _M($t, $t, $den);
393              
394 21         105 _pow2523($t, $t);
395 21         117 _M($t, $t, $num);
396 21         50 _M($t, $t, $den);
397 21         51 _M($t, $t, $den);
398 21         116 _M($r->[0], $t, $den);
399              
400 21         55 _S($chk, $r->[0]);
401 21         67 _M($chk, $chk, $den);
402              
403 21 100       110 if (_neq25519($chk, $num)) {
404 6         38 _M($r->[0], $r->[0], [ I() ]);
405             }
406              
407 21         69 _S($chk, $r->[0]);
408 21         61 _M($chk, $chk, $den);
409              
410 21 50       45 if (_neq25519($chk, $num)) {
411 0         0 die "-1??";
412             }
413              
414             # “>>” appears to be safe here.
415 21 100       67 if (_par25519($r->[0]) == ($p->[31] >> 7)) {
416 11         44 _Z($r->[0], [ gf0() ], $r->[0]);
417             }
418              
419 21         65 _M( $r->[3], $r->[0], $r->[1] );
420              
421 21         205 return 0;
422             }
423              
424             sub crypto_verify_32 {
425 63     63 0 162 my ($x, $xi, $y, $yi) = @_;
426              
427 63         186 return _vn($x, $xi, $y, $yi, 32);
428             }
429              
430 6         471 use constant D => (
431             0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
432             0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203,
433 6     6   54 );
  6         10  
434              
435 6         2653 use constant I => (
436             0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
437             0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
438 6     6   37 );
  6         10  
439              
440             sub _set25519 {
441 21     21   60 my ($r, $a) = @_;
442              
443 21         131 $r->[$_] = $a->[$_] | 0 for 0 .. 15;
444             }
445              
446             sub _unpack25519 {
447 21     21   66 my ($o, $n) = @_;
448              
449 21         48 for my $i (0 .. 15) {
450              
451             # originally “<< 8” rather than “256 *”
452 336         503 $o->[$i] = $n->[ 2 * $i ] + (256 * $n->[ 2 * $i + 1 ])
453             }
454              
455 21         66 $o->[15] &= 0x7fff;
456             }
457              
458             sub _pow2523 {
459 21     21   63 my ($o, $i) = @_;
460              
461 21         45 my $c = [ @{$i}[0 .. 15] ];
  21         61  
462              
463 21         77 for my $a ( reverse( 0 .. 250 ) ) {
464 5271         9407 _S( $c, $c );
465              
466 5271 100       7799 if ($a != 1) {
467 5250         7518 _M( $c, $c, $i );
468             }
469             }
470              
471 21         59 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  21         91  
  21         36  
472             }
473              
474             sub _neq25519 {
475 42     42   72 my ($a, $b) = @_;
476              
477 42         98 my $c = _pack25519($a);
478 42         100 my $d = _pack25519($b);
479              
480 42         127 return crypto_verify_32($c, 0, $d, 0);
481             }
482              
483             sub _vn {
484 63     63   153 my ($x, $xi, $y, $yi, $n) = @_;
485              
486 63         82 my $d = 0;
487              
488 63         139 for my $i ( 0 .. ($n - 1) ) {
489 2016         3019 $d = signed_or( $d, signed_xor($x->[ $xi + $i ], $y->[ $yi + $i ]) );
490             }
491              
492             # Originally “>>> 8”, which appears to be JS’s equivalent
493             # operator to Perl’s >>.
494 63         331 return (1 & (($d - 1) >> 8)) - 1;
495             }
496              
497             sub _floor {
498 44418     44418   57910 my $int = int $_[0];
499              
500 44418 100 100     90551 $int -= 1 if ($_[0] < 0) && ($int != $_[0]);
501              
502 44418         59693 return $int;
503             }
504              
505             1;