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   61515 use strict;
  6         16  
  6         138  
4 6     6   25 use warnings;
  6         11  
  6         108  
5              
6 6     6   2390 use Math::Utils ();
  6         15709  
  6         5336  
7              
8             sub reduce {
9 51     51 0 8066 my ($r) = @_;
10              
11 51         89 my @x;
12              
13 51         130 for my $i ( 0 .. 63 ) {
14 3264         3517 $x[$i] = $r->[$i];
15 3264         3716 $r->[$i] = 0;
16             }
17              
18 51         205 modL( $r, \@x );
19              
20 51         172 return;
21             }
22              
23             # p and q are arrays of arrays; s is an array of numbers
24             sub scalarmult {
25 71     71 0 200 my ($p, $q, $s) = @_;
26              
27 71         757 @{$p}[0 .. 3] = ( [ gf0() ], [ gf1() ], [ gf1() ], [ gf0() ] );
  71         189  
28              
29 71         218 my $b;
30              
31 71         201 for my $i ( reverse( 0 .. 255 ) ) {
32 18176         32256 $b = ( $s->[ ( $i >> 3 ) | 0 ] >> ($i & 7) ) & 1;
33 18176         33190 _cswap( $p, $q, $b );
34 18176         35447 add( $q, $p );
35 18176         35503 add( $p, $p );
36 18176         31345 _cswap( $p, $q, $b );
37             }
38              
39 71         454 return;
40             }
41              
42             # p is an array of arrays; s is an array of numbers
43             sub scalarbase {
44 50     50 0 122 my ($p, $s) = @_;
45              
46 50         518 my @q = ( [ X() ], [ Y() ], [ gf1() ], [ gf0() ] );
47              
48 50         336 _M( $q[3], [X()], [Y()] );
49              
50 50         188 scalarmult($p, \@q, $s);
51             }
52              
53             # p is an array of arrays
54             sub pack {
55 50     50 0 136 my ($p) = @_;
56              
57 50         171 my $tx = [ gf0() ];
58 50         163 my $ty = [ gf0() ];
59 50         161 my $zi = [ gf0() ];
60              
61 50         199 _inv25519( $zi, $p->[2] );
62              
63 50         193 _M( $tx, $p->[0], $zi );
64 50         126 _M( $ty, $p->[1], $zi );
65              
66 50         179 my $r = _pack25519($ty);
67              
68 50         150 $r->[31] ^= (_par25519($tx) << 7);
69              
70 50         438 return $r;
71             }
72              
73             sub add {
74 36373     36373 0 50028 my ($p, $q) = @_;
75              
76 36373         102158 my $a = [ gf0() ];
77 36373         81113 my $b = [ gf0() ];
78 36373         77405 my $c = [ gf0() ];
79 36373         71260 my $d = [ gf0() ];
80 36373         74916 my $e = [ gf0() ];
81 36373         79514 my $f = [ gf0() ];
82 36373         71785 my $g = [ gf0() ];
83 36373         68906 my $h = [ gf0() ];
84 36373         72148 my $t = [ gf0() ];
85              
86 36373         78641 _Z($a, $p->[1], $p->[0]);
87 36373         63704 _Z($t, $q->[1], $q->[0]);
88 36373         62649 _M($a, $a, $t);
89 36373         69961 _A($b, $p->[0], $p->[1]);
90 36373         68763 _A($t, $q->[0], $q->[1]);
91 36373         61388 _M($b, $b, $t);
92 36373         64789 _M($c, $p->[3], $q->[3]);
93 36373         102942 _M($c, $c, [ D2() ]);
94 36373         86452 _M($d, $p->[2], $q->[2]);
95 36373         62942 _A($d, $d, $d);
96 36373         65112 _Z($e, $b, $a);
97 36373         59366 _Z($f, $d, $c);
98 36373         59549 _A($g, $d, $c);
99 36373         59240 _A($h, $b, $a);
100              
101 36373         64499 _M($p->[0], $e, $f);
102 36373         63960 _M($p->[1], $h, $g);
103 36373         64004 _M($p->[2], $g, $f);
104 36373         52884 _M($p->[3], $e, $h);
105             }
106              
107             sub modL {
108 66     66 0 1861 my ($r, $x) = @_;
109              
110 66         88 my ($k);
111              
112 66         149 for my $i ( reverse( 32 .. 63 ) ) {
113 2112         2259 my $carry = 0;
114              
115 2112         2323 my ($j, $k);
116              
117 2112         3136 for (
118             ($j = $i - 32), ($k = $i - 12);
119             $j < $k;
120             ++$j
121             ) {
122 42240         63548 $x->[$j] += $carry - 16 * $x->[$i] * (L())[$j - ($i - 32)];
123              
124             # originally “>> 8” rather than “/ 256”;
125 42240         61647 $carry = Math::Utils::floor( ($x->[$j] + 128) / 256 );
126              
127 42240         140813 $x->[$j] -= $carry * 256;
128             }
129              
130 2112         2387 $x->[$j] += $carry;
131 2112         2795 $x->[$i] = 0;
132             }
133              
134 66         102 my $carry = 0;
135              
136             # In Perl, -98 >> 4 = 1152921504606846969. :-<
137 66         158 my $x31_rshift_4 = Math::Utils::floor( $x->[31] / 16 );
138              
139 66         256 for my $j ( 0 .. 31 ) {
140 2112         2937 $x->[$j] += $carry - $x31_rshift_4 * (L())[$j];
141              
142             # originally “>> 8” rather than “/ 256”; we also need floor
143 2112         3084 $carry = Math::Utils::floor( $x->[$j] / 256 );
144              
145 2112         6551 $x->[$j] &= 255;
146             }
147              
148 66         821 $x->[$_] -= $carry * (L())[$_] for 0 .. 31;
149              
150 66         141 for my $i ( 0 .. 31 ) {
151 2112         2450 $x->[$i + 1] += $x->[$i] >> 8;
152 2112         2484 $r->[$i] = $x->[$i] & 255;
153             }
154              
155 66         130 return;
156             }
157              
158 6     6   44 use constant gf0 => (0) x 16;
  6         11  
  6         538  
159              
160             #----------------------------------------------------------------------
161              
162 6     6   36 use constant gf1 => ( 1, (0) x 15 );
  6         51  
  6         420  
163              
164 6         556 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   36 );
  6         12  
169              
170 6         372 use constant D2 => (
171             0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,
172             0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406,
173 6     6   54 );
  6         12  
174              
175 6         355 use constant X => (
176             0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,
177             0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169,
178 6     6   34 );
  6         13  
179              
180 6         7911 use constant Y => (
181             0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
182             0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
183 6     6   73 );
  6         10  
184              
185 18076     18076   24272 sub _S { _M( $_[0], $_[1], $_[1] ) }
186              
187             sub _inv25519 {
188 50     50   117 my ($o, $i) = @_;
189              
190 50         100 my $c = [ @{$i}[0 .. 15] ];
  50         131  
191              
192 50         183 for my $a ( reverse( 0 .. 253 ) ) {
193 12700         21614 _S($c, $c);
194              
195 12700 100       18483 next if $a == 2;
196 12650 100       17494 next if $a == 4;
197              
198 12600         15982 _M( $c, $c, $i );
199             }
200              
201 50         92 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  50         134  
  50         84  
202              
203 50         194 return;
204             }
205              
206             sub _pack25519 {
207 205     205   314 my ($n) = @_;
208              
209 205         241 my $b;
210              
211 205         329 my $o = [];
212              
213 205         288 my $t = [ @{$n}[0 .. 15] ];
  205         565  
214              
215 205         441 my $m = [ gf0() ];
216              
217 205         438 _car25519($t) for 1 .. 3;
218              
219 205         308 for my $j (0, 1) {
220 410         557 $m->[0] = $t->[0] - 0xffed;
221              
222 410         606 for my $i ( 1 .. 14 ) {
223 5740         7072 $m->[$i] = $t->[$i] - 0xffff - (($m->[$i - 1] >> 16) & 1);
224 5740         6683 $m->[$i - 1] &= 0xffff;
225             }
226              
227 410         668 $m->[15] = $t->[15] - 0x7fff - (($m->[14] >> 16) & 1);
228              
229 410         579 $b = ($m->[15] >> 16) & 1;
230              
231 410         520 $m->[14] &= 0xffff;
232              
233 410         640 _sel25519( $t, $m, 1 - $b );
234             }
235              
236 205         604 for my $i ( 0 .. 15 ) {
237 3280         4512 $o->[2 * $i] = $t->[$i] & 0xff;
238 3280         4620 $o->[2 * $i + 1] = $t->[$i] >> 8;
239             }
240              
241 205         469 return $o;
242             }
243              
244             sub _par25519 {
245 71     71   152 my ($a) = @_;
246              
247 71         113 my $d = _pack25519($a);
248              
249 71         233 return $d->[0] & 1;
250             }
251              
252             # o, a, and b are arrays of numbers
253             sub _A {
254 181886     181886   236084 my ($o, $a, $b) = @_;
255              
256 181886         809107 $o->[$_] = $a->[$_] + $b->[$_] for 0 .. 15;
257              
258 181886         218565 return;
259             }
260              
261             # o, a, and b are arrays of numbers
262             sub _Z {
263 145524     145524   186364 my ($o, $a, $b) = @_;
264              
265 145524         667898 $o->[$_] = $a->[$_] - $b->[$_] for 0 .. 15;
266              
267 145524         174904 return;
268             }
269              
270             # o, a, and b are arrays of numbers
271             sub _M {
272 363672     363672   473304 my ($o, $a, $b) = @_;
273              
274 363672         693765 my @t = (0) x 31;
275              
276 363672         454347 for my $a_idx ( 0 .. 15 ) {
277 5818752         26257245 $t[$a_idx + $_] += $a->[$a_idx] * $b->[$_] for 0 .. 15;
278             }
279              
280             # $t->[15] left as-is
281 363672         432318 for my $t_idx ( 0 .. 14 ) {
282 5455080         6234491 $t[$t_idx] += 38 * $t[16 + $t_idx];
283             }
284              
285 363672         428608 my ($c, $v);
286              
287 363672         612718 _car25519(\@t);
288 363672         588508 _car25519(\@t);
289              
290 363672         545736 @{$o}[0 .. 15] = @t[0 .. 15];
  363672         607541  
291              
292 363672         688717 return;
293             }
294              
295             sub _car25519 {
296 727959     727959   871901 my ($o) = @_;
297              
298 727959         769923 my $c = 1;
299 727959         739292 my $v;
300              
301 727959         796092 for my $o_item ( @{$o}[0 .. 15] ) {
  727959         988500  
302 11647344         12037030 $v = $o_item + $c + 65535;
303              
304             # c = Math.floor(v / 65536)
305 11647344         12244159 $c = int( $v / 65536 );
306 11647344 100       14800194 $c-- if $v < 0;
307              
308             # t0 = v - c * 65536
309 11647344         13067346 $o_item = $v - ($c * 65536);
310             }
311              
312 727959         904937 $o->[0] += $c - 1 + 37 * ($c - 1);
313              
314 727959         858930 return;
315             }
316              
317             # p and q are arrays of numbers
318             sub _sel25519 {
319 145818     145818   188886 my ($p, $q, $b) = @_;
320              
321             # $b is either 0 or 1.
322 145818   100     244043 my $c = $b && -1;
323              
324 145818         176026 for my $i ( 0 .. 15 ) {
325 2333088   100     3356181 my $t = $c && ($c & signed_xor($p->[$i], $q->[$i]));
326              
327 2333088 100       3269851 $p->[$i] = signed_xor($p->[$i], $t) if $t;
328 2333088 100       3384298 $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   50704 my ($p, $q, $b) = @_;
335              
336 36352         52450 for my $i ( 0 .. 3 ) {
337 145408         196270 _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 3476942 50 25 3476942 0 7265175 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 3476942         4765060 return $_[0] ^ $_[1];
353             }
354              
355             sub signed_or {
356              
357             # signs are same -> can use native xor
358 2016 50   2016 0 2914 if ( ($_[0] < 0) eq ($_[1] < 0) ) {
359 2016         2750 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 58 my ($r, $p) = @_;
370              
371 21         152 $_ = [ gf0() ] for my (
372             $t,
373             $chk,
374             $num,
375             $den,
376             $den2,
377             $den4,
378             $den6,
379             );
380              
381 21         100 _set25519( $r->[2], [ gf1() ]);
382              
383 21         79 _unpack25519($r->[1], $p);
384              
385 21         73 _S($num, $r->[1]);
386 21         102 _M($den, $num, [ D() ]);
387 21         74 _Z($num, $num, $r->[2]);
388 21         57 _A($den, $r->[2], $den);
389              
390 21         58 _S($den2, $den);
391 21         44 _S($den4, $den2);
392 21         45 _M($den6, $den4, $den2);
393 21         41 _M($t, $den6, $num);
394 21         48 _M($t, $t, $den);
395              
396 21         59 _pow2523($t, $t);
397 21         119 _M($t, $t, $num);
398 21         50 _M($t, $t, $den);
399 21         56 _M($t, $t, $den);
400 21         69 _M($r->[0], $t, $den);
401              
402 21         55 _S($chk, $r->[0]);
403 21         65 _M($chk, $chk, $den);
404              
405 21 100       63 if (_neq25519($chk, $num)) {
406 8         48 _M($r->[0], $r->[0], [ I() ]);
407             }
408              
409 21         55 _S($chk, $r->[0]);
410 21         56 _M($chk, $chk, $den);
411              
412 21 50       42 if (_neq25519($chk, $num)) {
413 0         0 die "-1??";
414             }
415              
416             # “>>” appears to be safe here.
417 21 100       53 if (_par25519($r->[0]) == ($p->[31] >> 7)) {
418 11         40 _Z($r->[0], [ gf0() ], $r->[0]);
419             }
420              
421 21         64 _M( $r->[3], $r->[0], $r->[1] );
422              
423 21         183 return 0;
424             }
425              
426             sub crypto_verify_32 {
427 63     63 0 147 my ($x, $xi, $y, $yi) = @_;
428              
429 63         182 return _vn($x, $xi, $y, $yi, 32);
430             }
431              
432 6         484 use constant D => (
433             0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
434             0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203,
435 6     6   45 );
  6         8  
436              
437 6         2328 use constant I => (
438             0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
439             0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
440 6     6   48 );
  6         16  
441              
442             sub _set25519 {
443 21     21   38 my ($r, $a) = @_;
444              
445 21         126 $r->[$_] = $a->[$_] | 0 for 0 .. 15;
446             }
447              
448             sub _unpack25519 {
449 21     21   42 my ($o, $n) = @_;
450              
451 21         37 for my $i (0 .. 15) {
452              
453             # originally “<< 8” rather than “256 *”
454 336         485 $o->[$i] = $n->[ 2 * $i ] + (256 * $n->[ 2 * $i + 1 ])
455             }
456              
457 21         42 $o->[15] &= 0x7fff;
458             }
459              
460             sub _pow2523 {
461 21     21   39 my ($o, $i) = @_;
462              
463 21         36 my $c = [ @{$i}[0 .. 15] ];
  21         60  
464              
465 21         69 for my $a ( reverse( 0 .. 250 ) ) {
466 5271         8439 _S( $c, $c );
467              
468 5271 100       7865 if ($a != 1) {
469 5250         6407 _M( $c, $c, $i );
470             }
471             }
472              
473 21         35 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  21         70  
  21         43  
474             }
475              
476             sub _neq25519 {
477 42     42   73 my ($a, $b) = @_;
478              
479 42         101 my $c = _pack25519($a);
480 42         74 my $d = _pack25519($b);
481              
482 42         107 return crypto_verify_32($c, 0, $d, 0);
483             }
484              
485             sub _vn {
486 63     63   111 my ($x, $xi, $y, $yi, $n) = @_;
487              
488 63         84 my $d = 0;
489              
490 63         148 for my $i ( 0 .. ($n - 1) ) {
491 2016         2827 $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         299 return (1 & (($d - 1) >> 8)) - 1;
497             }
498              
499             1;