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   85940 use strict;
  6         48  
  6         186  
4 6     6   30 use warnings;
  6         17  
  6         7449  
5              
6             sub reduce {
7 51     51 0 11327 my ($r) = @_;
8              
9 51         83 my @x;
10              
11 51         134 for my $i ( 0 .. 63 ) {
12 3264         4368 $x[$i] = $r->[$i];
13 3264         4460 $r->[$i] = 0;
14             }
15              
16 51         484 modL( $r, \@x );
17              
18 51         285 return;
19             }
20              
21             # p and q are arrays of arrays; s is an array of numbers
22             sub scalarmult {
23 71     71 0 272 my ($p, $q, $s) = @_;
24              
25 71         743 @{$p}[0 .. 3] = ( [ gf0() ], [ gf1() ], [ gf1() ], [ gf0() ] );
  71         264  
26              
27 71         296 my $b;
28              
29 71         319 for my $i ( reverse( 0 .. 255 ) ) {
30 18176         41760 $b = ( $s->[ ( $i >> 3 ) | 0 ] >> ($i & 7) ) & 1;
31 18176         50748 _cswap( $p, $q, $b );
32 18176         42572 add( $q, $p );
33 18176         42538 add( $p, $p );
34 18176         43413 _cswap( $p, $q, $b );
35             }
36              
37 71         1138 return;
38             }
39              
40             # p is an array of arrays; s is an array of numbers
41             sub scalarbase {
42 50     50 0 192 my ($p, $s) = @_;
43              
44 50         632 my @q = ( [ X() ], [ Y() ], [ gf1() ], [ gf0() ] );
45              
46 50         391 _M( $q[3], [X()], [Y()] );
47              
48 50         411 scalarmult($p, \@q, $s);
49             }
50              
51             # p is an array of arrays
52             sub pack {
53 50     50 0 116 my ($p) = @_;
54              
55 50         222 my $tx = [ gf0() ];
56 50         165 my $ty = [ gf0() ];
57 50         185 my $zi = [ gf0() ];
58              
59 50         251 _inv25519( $zi, $p->[2] );
60              
61 50         262 _M( $tx, $p->[0], $zi );
62 50         381 _M( $ty, $p->[1], $zi );
63              
64 50         367 my $r = _pack25519($ty);
65              
66 50         225 $r->[31] ^= (_par25519($tx) << 7);
67              
68 50         558 return $r;
69             }
70              
71             sub add {
72 36373     36373 0 63278 my ($p, $q) = @_;
73              
74 36373         131904 my $a = [ gf0() ];
75 36373         109279 my $b = [ gf0() ];
76 36373         100668 my $c = [ gf0() ];
77 36373         96319 my $d = [ gf0() ];
78 36373         96278 my $e = [ gf0() ];
79 36373         92909 my $f = [ gf0() ];
80 36373         94125 my $g = [ gf0() ];
81 36373         90805 my $h = [ gf0() ];
82 36373         94998 my $t = [ gf0() ];
83              
84 36373         96345 _Z($a, $p->[1], $p->[0]);
85 36373         78100 _Z($t, $q->[1], $q->[0]);
86 36373         83085 _M($a, $a, $t);
87 36373         82696 _A($b, $p->[0], $p->[1]);
88 36373         77956 _A($t, $q->[0], $q->[1]);
89 36373         74517 _M($b, $b, $t);
90 36373         84718 _M($c, $p->[3], $q->[3]);
91 36373         136643 _M($c, $c, [ D2() ]);
92 36373         110458 _M($d, $p->[2], $q->[2]);
93 36373         81386 _A($d, $d, $d);
94 36373         73172 _Z($e, $b, $a);
95 36373         72148 _Z($f, $d, $c);
96 36373         73443 _A($g, $d, $c);
97 36373         77883 _A($h, $b, $a);
98              
99 36373         72540 _M($p->[0], $e, $f);
100 36373         79607 _M($p->[1], $h, $g);
101 36373         76626 _M($p->[2], $g, $f);
102 36373         65786 _M($p->[3], $e, $h);
103             }
104              
105             sub modL {
106 66     66 0 2390 my ($r, $x) = @_;
107              
108 66         135 my ($k);
109              
110 66         267 for my $i ( reverse( 32 .. 63 ) ) {
111 2112         2780 my $carry = 0;
112              
113 2112         2836 my ($j, $k);
114              
115 2112         3799 for (
116             ($j = $i - 32), ($k = $i - 12);
117             $j < $k;
118             ++$j
119             ) {
120 42240         75561 $x->[$j] += $carry - 16 * $x->[$i] * (L())[$j - ($i - 32)];
121              
122             # originally “>> 8” rather than “/ 256”;
123 42240         68009 $carry = _floor( ($x->[$j] + 128) / 256 );
124              
125 42240         77464 $x->[$j] -= $carry * 256;
126             }
127              
128 2112         2797 $x->[$j] += $carry;
129 2112         3577 $x->[$i] = 0;
130             }
131              
132 66         255 my $carry = 0;
133              
134             # In Perl, -98 >> 4 = 1152921504606846969. :-<
135 66         222 my $x31_rshift_4 = _floor( $x->[31] / 16 );
136              
137 66         213 for my $j ( 0 .. 31 ) {
138 2112         3613 $x->[$j] += $carry - $x31_rshift_4 * (L())[$j];
139              
140             # originally “>> 8” rather than “/ 256”; we also need floor
141 2112         3423 $carry = _floor( $x->[$j] / 256 );
142              
143 2112         3324 $x->[$j] &= 255;
144             }
145              
146 66         1163 $x->[$_] -= $carry * (L())[$_] for 0 .. 31;
147              
148 66         220 for my $i ( 0 .. 31 ) {
149 2112         2906 $x->[$i + 1] += $x->[$i] >> 8;
150 2112         3135 $r->[$i] = $x->[$i] & 255;
151             }
152              
153 66         170 return;
154             }
155              
156 6     6   57 use constant gf0 => (0) x 16;
  6         19  
  6         709  
157              
158             #----------------------------------------------------------------------
159              
160 6     6   46 use constant gf1 => ( 1, (0) x 15 );
  6         19  
  6         570  
161              
162 6         537 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   42 );
  6         14  
167              
168 6         476 use constant D2 => (
169             0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,
170             0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406,
171 6     6   43 );
  6         11  
172              
173 6         493 use constant X => (
174             0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,
175             0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169,
176 6     6   67 );
  6         15  
177              
178 6         10063 use constant Y => (
179             0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
180             0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
181 6     6   54 );
  6         12  
182              
183 18076     18076   28998 sub _S { _M( $_[0], $_[1], $_[1] ) }
184              
185             sub _inv25519 {
186 50     50   159 my ($o, $i) = @_;
187              
188 50         169 my $c = [ @{$i}[0 .. 15] ];
  50         164  
189              
190 50         228 for my $a ( reverse( 0 .. 253 ) ) {
191 12700         25534 _S($c, $c);
192              
193 12700 100       22573 next if $a == 2;
194 12650 100       19920 next if $a == 4;
195              
196 12600         19039 _M( $c, $c, $i );
197             }
198              
199 50         152 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  50         159  
  50         232  
200              
201 50         269 return;
202             }
203              
204             sub _pack25519 {
205 205     205   401 my ($n) = @_;
206              
207 205         409 my $b;
208              
209 205         377 my $o = [];
210              
211 205         330 my $t = [ @{$n}[0 .. 15] ];
  205         628  
212              
213 205         643 my $m = [ gf0() ];
214              
215 205         536 _car25519($t) for 1 .. 3;
216              
217 205         450 for my $j (0, 1) {
218 410         886 $m->[0] = $t->[0] - 0xffed;
219              
220 410         714 for my $i ( 1 .. 14 ) {
221 5740         8835 $m->[$i] = $t->[$i] - 0xffff - (($m->[$i - 1] >> 16) & 1);
222 5740         8550 $m->[$i - 1] &= 0xffff;
223             }
224              
225 410         981 $m->[15] = $t->[15] - 0x7fff - (($m->[14] >> 16) & 1);
226              
227 410         684 $b = ($m->[15] >> 16) & 1;
228              
229 410         506 $m->[14] &= 0xffff;
230              
231 410         908 _sel25519( $t, $m, 1 - $b );
232             }
233              
234 205         701 for my $i ( 0 .. 15 ) {
235 3280         6097 $o->[2 * $i] = $t->[$i] & 0xff;
236 3280         5808 $o->[2 * $i + 1] = $t->[$i] >> 8;
237             }
238              
239 205         775 return $o;
240             }
241              
242             sub _par25519 {
243 71     71   246 my ($a) = @_;
244              
245 71         211 my $d = _pack25519($a);
246              
247 71         502 return $d->[0] & 1;
248             }
249              
250             # o, a, and b are arrays of numbers
251             sub _A {
252 181886     181886   277996 my ($o, $a, $b) = @_;
253              
254 181886         1012301 $o->[$_] = $a->[$_] + $b->[$_] for 0 .. 15;
255              
256 181886         254248 return;
257             }
258              
259             # o, a, and b are arrays of numbers
260             sub _Z {
261 145522     145522   225275 my ($o, $a, $b) = @_;
262              
263 145522         838834 $o->[$_] = $a->[$_] - $b->[$_] for 0 .. 15;
264              
265 145522         208567 return;
266             }
267              
268             # o, a, and b are arrays of numbers
269             sub _M {
270 363672     363672   566255 my ($o, $a, $b) = @_;
271              
272 363672         898839 my @t = (0) x 31;
273              
274 363672         590377 for my $a_idx ( 0 .. 15 ) {
275 5818752         33262524 $t[$a_idx + $_] += $a->[$a_idx] * $b->[$_] for 0 .. 15;
276             }
277              
278             # $t->[15] left as-is
279 363672         511958 for my $t_idx ( 0 .. 14 ) {
280 5455080         7412331 $t[$t_idx] += 38 * $t[16 + $t_idx];
281             }
282              
283 363672         495310 my ($c, $v);
284              
285 363672         744953 _car25519(\@t);
286 363672         732705 _car25519(\@t);
287              
288 363672         635675 @{$o}[0 .. 15] = @t[0 .. 15];
  363672         761999  
289              
290 363672         854552 return;
291             }
292              
293             sub _car25519 {
294 727959     727959   1026832 my ($o) = @_;
295              
296 727959         915029 my $c = 1;
297 727959         898016 my $v;
298              
299 727959         959196 for my $o_item ( @{$o}[0 .. 15] ) {
  727959         1260760  
300 11647344         14242690 $v = $o_item + $c + 65535;
301              
302             # c = Math.floor(v / 65536)
303 11647344         15113821 $c = int( $v / 65536 );
304 11647344 100       18234418 $c-- if $v < 0;
305              
306             # t0 = v - c * 65536
307 11647344         16020882 $o_item = $v - ($c * 65536);
308             }
309              
310 727959         1132176 $o->[0] += $c - 1 + 37 * ($c - 1);
311              
312 727959         1056280 return;
313             }
314              
315             # p and q are arrays of numbers
316             sub _sel25519 {
317 145818     145818   230265 my ($p, $q, $b) = @_;
318              
319             # $b is either 0 or 1.
320 145818   100     284450 my $c = $b && -1;
321              
322 145818         214444 for my $i ( 0 .. 15 ) {
323 2333088   100     3963924 my $t = $c && ($c & signed_xor($p->[$i], $q->[$i]));
324              
325 2333088 100       3979586 $p->[$i] = signed_xor($p->[$i], $t) if $t;
326 2333088 100       4102785 $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   58222 my ($p, $q, $b) = @_;
333              
334 36352         60415 for my $i ( 0 .. 3 ) {
335 145408         232076 _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 3504580 50 25 3504580 0 8636030 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 3504580         5745703 return $_[0] ^ $_[1];
351             }
352              
353             sub signed_or {
354              
355             # signs are same -> can use native xor
356 2016 50   2016 0 3572 if ( ($_[0] < 0) eq ($_[1] < 0) ) {
357 2016         3332 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 65 my ($r, $p) = @_;
368              
369 21         198 $_ = [ gf0() ] for my (
370             $t,
371             $chk,
372             $num,
373             $den,
374             $den2,
375             $den4,
376             $den6,
377             );
378              
379 21         143 _set25519( $r->[2], [ gf1() ]);
380              
381 21         136 _unpack25519($r->[1], $p);
382              
383 21         81 _S($num, $r->[1]);
384 21         243 _M($den, $num, [ D() ]);
385 21         168 _Z($num, $num, $r->[2]);
386 21         95 _A($den, $r->[2], $den);
387              
388 21         95 _S($den2, $den);
389 21         172 _S($den4, $den2);
390 21         162 _M($den6, $den4, $den2);
391 21         128 _M($t, $den6, $num);
392 21         129 _M($t, $t, $den);
393              
394 21         223 _pow2523($t, $t);
395 21         125 _M($t, $t, $num);
396 21         221 _M($t, $t, $den);
397 21         135 _M($t, $t, $den);
398 21         186 _M($r->[0], $t, $den);
399              
400 21         97 _S($chk, $r->[0]);
401 21         135 _M($chk, $chk, $den);
402              
403 21 100       137 if (_neq25519($chk, $num)) {
404 8         67 _M($r->[0], $r->[0], [ I() ]);
405             }
406              
407 21         117 _S($chk, $r->[0]);
408 21         119 _M($chk, $chk, $den);
409              
410 21 50       178 if (_neq25519($chk, $num)) {
411 0         0 die "-1??";
412             }
413              
414             # “>>” appears to be safe here.
415 21 100       87 if (_par25519($r->[0]) == ($p->[31] >> 7)) {
416 9         57 _Z($r->[0], [ gf0() ], $r->[0]);
417             }
418              
419 21         83 _M( $r->[3], $r->[0], $r->[1] );
420              
421 21         255 return 0;
422             }
423              
424             sub crypto_verify_32 {
425 63     63 0 565 my ($x, $xi, $y, $yi) = @_;
426              
427 63         295 return _vn($x, $xi, $y, $yi, 32);
428             }
429              
430 6         645 use constant D => (
431             0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
432             0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203,
433 6     6   48 );
  6         13  
434              
435 6         3860 use constant I => (
436             0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
437             0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
438 6     6   45 );
  6         9  
439              
440             sub _set25519 {
441 21     21   49 my ($r, $a) = @_;
442              
443 21         155 $r->[$_] = $a->[$_] | 0 for 0 .. 15;
444             }
445              
446             sub _unpack25519 {
447 21     21   53 my ($o, $n) = @_;
448              
449 21         49 for my $i (0 .. 15) {
450              
451             # originally “<< 8” rather than “256 *”
452 336         597 $o->[$i] = $n->[ 2 * $i ] + (256 * $n->[ 2 * $i + 1 ])
453             }
454              
455 21         75 $o->[15] &= 0x7fff;
456             }
457              
458             sub _pow2523 {
459 21     21   78 my ($o, $i) = @_;
460              
461 21         46 my $c = [ @{$i}[0 .. 15] ];
  21         76  
462              
463 21         134 for my $a ( reverse( 0 .. 250 ) ) {
464 5271         10426 _S( $c, $c );
465              
466 5271 100       9214 if ($a != 1) {
467 5250         8270 _M( $c, $c, $i );
468             }
469             }
470              
471 21         115 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  21         64  
  21         74  
472             }
473              
474             sub _neq25519 {
475 42     42   91 my ($a, $b) = @_;
476              
477 42         140 my $c = _pack25519($a);
478 42         132 my $d = _pack25519($b);
479              
480 42         201 return crypto_verify_32($c, 0, $d, 0);
481             }
482              
483             sub _vn {
484 63     63   163 my ($x, $xi, $y, $yi, $n) = @_;
485              
486 63         135 my $d = 0;
487              
488 63         166 for my $i ( 0 .. ($n - 1) ) {
489 2016         3387 $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         526 return (1 & (($d - 1) >> 8)) - 1;
495             }
496              
497             sub _floor {
498 44418     44418   62567 my $int = int $_[0];
499              
500 44418 100 100     99943 $int -= 1 if ($_[0] < 0) && ($int != $_[0]);
501              
502 44418         64648 return $int;
503             }
504              
505             1;