File Coverage

blib/lib/Crypt/Perl/Ed25519/Math.pm
Criterion Covered Total %
statement 249 252 98.8
branch 19 22 86.3
condition 6 9 66.6
subroutine 36 36 100.0
pod 0 10 0.0
total 310 329 94.2


line stmt bran cond sub pod time code
1             package Crypt::Perl::Ed25519::Math;
2              
3 6     6   58491 use strict;
  6         19  
  6         115  
4 6     6   22 use warnings;
  6         8  
  6         108  
5              
6 6     6   2228 use Math::Utils ();
  6         12555  
  6         5730  
7              
8             sub reduce {
9 51     51 0 7771 my ($r) = @_;
10              
11 51         76 my @x;
12              
13 51         130 for my $i ( 0 .. 63 ) {
14 3264         3180 $x[$i] = $r->[$i];
15 3264         3206 $r->[$i] = 0;
16             }
17              
18 51         214 modL( $r, \@x );
19              
20 51         138 return;
21             }
22              
23             # p and q are arrays of arrays; s is an array of numbers
24             sub scalarmult {
25 71     71 0 205 my ($p, $q, $s) = @_;
26              
27 71         502 @{$p}[0 .. 3] = ( [ gf0() ], [ gf1() ], [ gf1() ], [ gf0() ] );
  71         227  
28              
29 71         236 my $b;
30              
31 71         240 for my $i ( reverse( 0 .. 255 ) ) {
32 18176         38992 $b = ( $s->[ ( $i >> 3 ) | 0 ] >> ($i & 7) ) & 1;
33 18176         35295 _cswap( $p, $q, $b );
34 18176         37274 add( $q, $p );
35 18176         34723 add( $p, $p );
36 18176         34594 _cswap( $p, $q, $b );
37             }
38              
39 71         446 return;
40             }
41              
42             # p is an array of arrays; s is an array of numbers
43             sub scalarbase {
44 50     50 0 112 my ($p, $s) = @_;
45              
46 50         503 my @q = ( [ X() ], [ Y() ], [ gf1() ], [ gf0() ] );
47              
48 50         320 _M( $q[3], [X()], [Y()] );
49              
50 50         210 scalarmult($p, \@q, $s);
51             }
52              
53             # p is an array of arrays
54             sub pack {
55 50     50 0 120 my ($p) = @_;
56              
57 50         218 my $tx = [ gf0() ];
58 50         168 my $ty = [ gf0() ];
59 50         111 my $zi = [ gf0() ];
60              
61 50         175 _inv25519( $zi, $p->[2] );
62              
63 50         215 _M( $tx, $p->[0], $zi );
64 50         145 _M( $ty, $p->[1], $zi );
65              
66 50         130 my $r = _pack25519($ty);
67              
68 50         140 $r->[31] ^= (_par25519($tx) << 7);
69              
70 50         404 return $r;
71             }
72              
73             sub add {
74 36373     36373 0 53408 my ($p, $q) = @_;
75              
76 36373         110487 my $a = [ gf0() ];
77 36373         82706 my $b = [ gf0() ];
78 36373         74878 my $c = [ gf0() ];
79 36373         75015 my $d = [ gf0() ];
80 36373         74734 my $e = [ gf0() ];
81 36373         75268 my $f = [ gf0() ];
82 36373         70501 my $g = [ gf0() ];
83 36373         65518 my $h = [ gf0() ];
84 36373         66824 my $t = [ gf0() ];
85              
86 36373         72645 _Z($a, $p->[1], $p->[0]);
87 36373         58599 _Z($t, $q->[1], $q->[0]);
88 36373         58034 _M($a, $a, $t);
89 36373         71188 _A($b, $p->[0], $p->[1]);
90 36373         58862 _A($t, $q->[0], $q->[1]);
91 36373         55904 _M($b, $b, $t);
92 36373         63067 _M($c, $p->[3], $q->[3]);
93 36373         112603 _M($c, $c, [ D2() ]);
94 36373         85081 _M($d, $p->[2], $q->[2]);
95 36373         64139 _A($d, $d, $d);
96 36373         59173 _Z($e, $b, $a);
97 36373         53959 _Z($f, $d, $c);
98 36373         56105 _A($g, $d, $c);
99 36373         54772 _A($h, $b, $a);
100              
101 36373         56171 _M($p->[0], $e, $f);
102 36373         62750 _M($p->[1], $h, $g);
103 36373         62883 _M($p->[2], $g, $f);
104 36373         55051 _M($p->[3], $e, $h);
105             }
106              
107             sub modL {
108 66     66 0 1727 my ($r, $x) = @_;
109              
110 66         92 my ($k);
111              
112 66         162 for my $i ( reverse( 32 .. 63 ) ) {
113 2112         2118 my $carry = 0;
114              
115 2112         2132 my ($j, $k);
116              
117 2112         2940 for (
118             ($j = $i - 32), ($k = $i - 12);
119             $j < $k;
120             ++$j
121             ) {
122 42240         56239 $x->[$j] += $carry - 16 * $x->[$i] * (L())[$j - ($i - 32)];
123              
124             # originally “>> 8” rather than “/ 256”;
125 42240         56265 $carry = Math::Utils::floor( ($x->[$j] + 128) / 256 );
126              
127 42240         126924 $x->[$j] -= $carry * 256;
128             }
129              
130 2112         2113 $x->[$j] += $carry;
131 2112         2783 $x->[$i] = 0;
132             }
133              
134 66         98 my $carry = 0;
135              
136             # In Perl, -98 >> 4 = 1152921504606846969. :-<
137 66         134 my $x31_rshift_4 = Math::Utils::floor( $x->[31] / 16 );
138              
139 66         259 for my $j ( 0 .. 31 ) {
140 2112         2546 $x->[$j] += $carry - $x31_rshift_4 * (L())[$j];
141              
142             # originally “>> 8” rather than “/ 256”; we also need floor
143 2112         2903 $carry = Math::Utils::floor( $x->[$j] / 256 );
144              
145 2112         5692 $x->[$j] &= 255;
146             }
147              
148 66         732 $x->[$_] -= $carry * (L())[$_] for 0 .. 31;
149              
150 66         116 for my $i ( 0 .. 31 ) {
151 2112         2148 $x->[$i + 1] += $x->[$i] >> 8;
152 2112         2297 $r->[$i] = $x->[$i] & 255;
153             }
154              
155 66         119 return;
156             }
157              
158 6     6   42 use constant gf0 => (0) x 16;
  6         11  
  6         520  
159              
160             #----------------------------------------------------------------------
161              
162 6     6   37 use constant gf1 => ( 1, (0) x 15 );
  6         42  
  6         376  
163              
164 6         665 use constant L => (
165             0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
166             0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
167             (0) x 15, 0x10,
168 6     6   31 );
  6         10  
169              
170 6         390 use constant D2 => (
171             0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,
172             0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406,
173 6     6   44 );
  6         10  
174              
175 6         351 use constant X => (
176             0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,
177             0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169,
178 6     6   29 );
  6         9  
179              
180 6         6947 use constant Y => (
181             0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
182             0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
183 6     6   28 );
  6         8  
184              
185 18076     18076   22993 sub _S { _M( $_[0], $_[1], $_[1] ) }
186              
187             sub _inv25519 {
188 50     50   126 my ($o, $i) = @_;
189              
190 50         96 my $c = [ @{$i}[0 .. 15] ];
  50         130  
191              
192 50         164 for my $a ( reverse( 0 .. 253 ) ) {
193 12700         19271 _S($c, $c);
194              
195 12700 100       17060 next if $a == 2;
196 12650 100       15104 next if $a == 4;
197              
198 12600         14404 _M( $c, $c, $i );
199             }
200              
201 50         113 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  50         108  
  50         84  
202              
203 50         270 return;
204             }
205              
206             sub _pack25519 {
207 205     205   284 my ($n) = @_;
208              
209 205         221 my $b;
210              
211 205         315 my $o = [];
212              
213 205         276 my $t = [ @{$n}[0 .. 15] ];
  205         531  
214              
215 205         541 my $m = [ gf0() ];
216              
217 205         366 _car25519($t) for 1 .. 3;
218              
219 205         370 for my $j (0, 1) {
220 410         548 $m->[0] = $t->[0] - 0xffed;
221              
222 410         558 for my $i ( 1 .. 14 ) {
223 5740         6326 $m->[$i] = $t->[$i] - 0xffff - (($m->[$i - 1] >> 16) & 1);
224 5740         6043 $m->[$i - 1] &= 0xffff;
225             }
226              
227 410         560 $m->[15] = $t->[15] - 0x7fff - (($m->[14] >> 16) & 1);
228              
229 410         443 $b = ($m->[15] >> 16) & 1;
230              
231 410         413 $m->[14] &= 0xffff;
232              
233 410         607 _sel25519( $t, $m, 1 - $b );
234             }
235              
236 205         295 for my $i ( 0 .. 15 ) {
237 3280         4198 $o->[2 * $i] = $t->[$i] & 0xff;
238 3280         4315 $o->[2 * $i + 1] = $t->[$i] >> 8;
239             }
240              
241 205         511 return $o;
242             }
243              
244             sub _par25519 {
245 71     71   132 my ($a) = @_;
246              
247 71         205 my $d = _pack25519($a);
248              
249 71         275 return $d->[0] & 1;
250             }
251              
252             # o, a, and b are arrays of numbers
253             sub _A {
254 181886     181886   214848 my ($o, $a, $b) = @_;
255              
256 181886         732697 $o->[$_] = $a->[$_] + $b->[$_] for 0 .. 15;
257              
258 181886         194923 return;
259             }
260              
261             # o, a, and b are arrays of numbers
262             sub _Z {
263 145528     145528   177895 my ($o, $a, $b) = @_;
264              
265 145528         609284 $o->[$_] = $a->[$_] - $b->[$_] for 0 .. 15;
266              
267 145528         159742 return;
268             }
269              
270             # o, a, and b are arrays of numbers
271             sub _M {
272 363674     363674   451392 my ($o, $a, $b) = @_;
273              
274 363674         685060 my @t = (0) x 31;
275              
276 363674         457928 for my $a_idx ( 0 .. 15 ) {
277 5818784         23868799 $t[$a_idx + $_] += $a->[$a_idx] * $b->[$_] for 0 .. 15;
278             }
279              
280             # $t->[15] left as-is
281 363674         398556 for my $t_idx ( 0 .. 14 ) {
282 5455110         5698112 $t[$t_idx] += 38 * $t[16 + $t_idx];
283             }
284              
285 363674         371761 my ($c, $v);
286              
287 363674         581814 _car25519(\@t);
288 363674         589481 _car25519(\@t);
289              
290 363674         488961 @{$o}[0 .. 15] = @t[0 .. 15];
  363674         598308  
291              
292 363674         655613 return;
293             }
294              
295             sub _car25519 {
296 727963     727963   871472 my ($o) = @_;
297              
298 727963         714000 my $c = 1;
299 727963         676318 my $v;
300              
301 727963         745067 for my $o_item ( @{$o}[0 .. 15] ) {
  727963         1005446  
302 11647408         10668848 $v = $o_item + $c + 65535;
303              
304             # c = Math.floor(v / 65536)
305 11647408         11248437 $c = int( $v / 65536 );
306 11647408 100       13563337 $c-- if $v < 0;
307              
308             # t0 = v - c * 65536
309 11647408         11740978 $o_item = $v - ($c * 65536);
310             }
311              
312 727963         866594 $o->[0] += $c - 1 + 37 * ($c - 1);
313              
314 727963         814471 return;
315             }
316              
317             # p and q are arrays of numbers
318             sub _sel25519 {
319 145818     145818   177863 my ($p, $q, $b) = @_;
320              
321             # $b is either 0 or 1.
322 145818   100     235758 my $c = $b && -1;
323              
324 145818         176229 for my $i ( 0 .. 15 ) {
325 2333088   100     3054138 my $t = $c && ($c & signed_xor($p->[$i], $q->[$i]));
326              
327 2333088 100       2936606 $p->[$i] = signed_xor($p->[$i], $t) if $t;
328 2333088 100       3084031 $q->[$i] = signed_xor($q->[$i], $t) if $t;
329             }
330             }
331              
332             # p and q are arrays of arrays
333             sub _cswap {
334 36352     36352   52562 my ($p, $q, $b) = @_;
335              
336 36352         54954 for my $i ( 0 .. 3 ) {
337 145408         181460 _sel25519( $p->[$i], $q->[$i], $b );
338             }
339             }
340              
341             # Perl’s ^ operator isn’t signed-savvy,
342             # so (-60116 ^ 0) = 18446744073709491500.
343             #
344             # TODO: add tests
345             sub signed_xor {
346              
347 3468544 50 25 3468544 0 6342305 if ( ($_[0] < 0) xor ($_[1] < 0) ) {
348 0         0 return ($_[0] ^ $_[1]) - ~0 - 1;
349             }
350              
351             # signs are same -> can use native xor
352 3468544         4474391 return $_[0] ^ $_[1];
353             }
354              
355             sub signed_or {
356              
357             # signs are same -> can use native xor
358 2016 50   2016 0 2623 if ( ($_[0] < 0) eq ($_[1] < 0) ) {
359 2016         2542 return $_[0] | $_[1];
360             }
361              
362 0         0 return ($_[0] | $_[1]) - ~0 - 1;
363             }
364              
365             #----------------------------------------------------------------------
366             # Verify logic
367              
368             sub unpackneg {
369 21     21 0 52 my ($r, $p) = @_;
370              
371 21         166 $_ = [ gf0() ] for my (
372             $t,
373             $chk,
374             $num,
375             $den,
376             $den2,
377             $den4,
378             $den6,
379             );
380              
381 21         113 _set25519( $r->[2], [ gf1() ]);
382              
383 21         89 _unpack25519($r->[1], $p);
384              
385 21         71 _S($num, $r->[1]);
386 21         105 _M($den, $num, [ D() ]);
387 21         85 _Z($num, $num, $r->[2]);
388 21         87 _A($den, $r->[2], $den);
389              
390 21         48 _S($den2, $den);
391 21         42 _S($den4, $den2);
392 21         45 _M($den6, $den4, $den2);
393 21         42 _M($t, $den6, $num);
394 21         44 _M($t, $t, $den);
395              
396 21         67 _pow2523($t, $t);
397 21         128 _M($t, $t, $num);
398 21         50 _M($t, $t, $den);
399 21         52 _M($t, $t, $den);
400 21         78 _M($r->[0], $t, $den);
401              
402 21         52 _S($chk, $r->[0]);
403 21         51 _M($chk, $chk, $den);
404              
405 21 100       54 if (_neq25519($chk, $num)) {
406 10         58 _M($r->[0], $r->[0], [ I() ]);
407             }
408              
409 21         56 _S($chk, $r->[0]);
410 21         46 _M($chk, $chk, $den);
411              
412 21 50       45 if (_neq25519($chk, $num)) {
413 0         0 die "-1??";
414             }
415              
416             # “>>” appears to be safe here.
417 21 100       58 if (_par25519($r->[0]) == ($p->[31] >> 7)) {
418 15         64 _Z($r->[0], [ gf0() ], $r->[0]);
419             }
420              
421 21         65 _M( $r->[3], $r->[0], $r->[1] );
422              
423 21         176 return 0;
424             }
425              
426             sub crypto_verify_32 {
427 63     63 0 169 my ($x, $xi, $y, $yi) = @_;
428              
429 63         173 return _vn($x, $xi, $y, $yi, 32);
430             }
431              
432 6         393 use constant D => (
433             0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
434             0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203,
435 6     6   38 );
  6         9  
436              
437 6         2132 use constant I => (
438             0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
439             0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
440 6     6   35 );
  6         9  
441              
442             sub _set25519 {
443 21     21   48 my ($r, $a) = @_;
444              
445 21         118 $r->[$_] = $a->[$_] | 0 for 0 .. 15;
446             }
447              
448             sub _unpack25519 {
449 21     21   39 my ($o, $n) = @_;
450              
451 21         61 for my $i (0 .. 15) {
452              
453             # originally “<< 8” rather than “256 *”
454 336         454 $o->[$i] = $n->[ 2 * $i ] + (256 * $n->[ 2 * $i + 1 ])
455             }
456              
457 21         38 $o->[15] &= 0x7fff;
458             }
459              
460             sub _pow2523 {
461 21     21   42 my ($o, $i) = @_;
462              
463 21         36 my $c = [ @{$i}[0 .. 15] ];
  21         70  
464              
465 21         81 for my $a ( reverse( 0 .. 250 ) ) {
466 5271         8319 _S( $c, $c );
467              
468 5271 100       7291 if ($a != 1) {
469 5250         6549 _M( $c, $c, $i );
470             }
471             }
472              
473 21         45 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  21         63  
  21         71  
474             }
475              
476             sub _neq25519 {
477 42     42   81 my ($a, $b) = @_;
478              
479 42         89 my $c = _pack25519($a);
480 42         83 my $d = _pack25519($b);
481              
482 42         112 return crypto_verify_32($c, 0, $d, 0);
483             }
484              
485             sub _vn {
486 63     63   130 my ($x, $xi, $y, $yi, $n) = @_;
487              
488 63         98 my $d = 0;
489              
490 63         130 for my $i ( 0 .. ($n - 1) ) {
491 2016         2578 $d = signed_or( $d, signed_xor($x->[ $xi + $i ], $y->[ $yi + $i ]) );
492             }
493              
494             # Originally “>>> 8”, which appears to be JS’s equivalent
495             # operator to Perl’s >>.
496 63         305 return (1 & (($d - 1) >> 8)) - 1;
497             }
498              
499             1;