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 3     3   57523 use strict;
  3         12  
  3         60  
4 3     3   11 use warnings;
  3         4  
  3         48  
5              
6 3     3   1096 use Math::Utils ();
  3         6321  
  3         2299  
7              
8             sub reduce {
9 49     49 0 8007 my ($r) = @_;
10              
11 49         72 my @x;
12              
13 49         111 for my $i ( 0 .. 63 ) {
14 3136         3166 $x[$i] = $r->[$i];
15 3136         3294 $r->[$i] = 0;
16             }
17              
18 49         250 modL( $r, \@x );
19              
20 49         162 return;
21             }
22              
23             # p and q are arrays of arrays; s is an array of numbers
24             sub scalarmult {
25 69     69 0 140 my ($p, $q, $s) = @_;
26              
27 69         523 @{$p}[0 .. 3] = ( [ gf0() ], [ gf1() ], [ gf1() ], [ gf0() ] );
  69         288  
28              
29 69         198 my $b;
30              
31 69         224 for my $i ( reverse( 0 .. 255 ) ) {
32 17664         33956 $b = ( $s->[ ( $i >> 3 ) | 0 ] >> ($i & 7) ) & 1;
33 17664         30960 _cswap( $p, $q, $b );
34 17664         34796 add( $q, $p );
35 17664         34155 add( $p, $p );
36 17664         34590 _cswap( $p, $q, $b );
37             }
38              
39 69         535 return;
40             }
41              
42             # p is an array of arrays; s is an array of numbers
43             sub scalarbase {
44 48     48 0 115 my ($p, $s) = @_;
45              
46 48         511 my @q = ( [ X() ], [ Y() ], [ gf1() ], [ gf0() ] );
47              
48 48         274 _M( $q[3], [X()], [Y()] );
49              
50 48         215 scalarmult($p, \@q, $s);
51             }
52              
53             # p is an array of arrays
54             sub pack {
55 48     48 0 102 my ($p) = @_;
56              
57 48         168 my $tx = [ gf0() ];
58 48         134 my $ty = [ gf0() ];
59 48         113 my $zi = [ gf0() ];
60              
61 48         195 _inv25519( $zi, $p->[2] );
62              
63 48         188 _M( $tx, $p->[0], $zi );
64 48         167 _M( $ty, $p->[1], $zi );
65              
66 48         130 my $r = _pack25519($ty);
67              
68 48         132 $r->[31] ^= (_par25519($tx) << 7);
69              
70 48         422 return $r;
71             }
72              
73             sub add {
74 35349     35349 0 51366 my ($p, $q) = @_;
75              
76 35349         95348 my $a = [ gf0() ];
77 35349         74345 my $b = [ gf0() ];
78 35349         72271 my $c = [ gf0() ];
79 35349         73610 my $d = [ gf0() ];
80 35349         74530 my $e = [ gf0() ];
81 35349         70751 my $f = [ gf0() ];
82 35349         68527 my $g = [ gf0() ];
83 35349         68258 my $h = [ gf0() ];
84 35349         78407 my $t = [ gf0() ];
85              
86 35349         70266 _Z($a, $p->[1], $p->[0]);
87 35349         60303 _Z($t, $q->[1], $q->[0]);
88 35349         66505 _M($a, $a, $t);
89 35349         73029 _A($b, $p->[0], $p->[1]);
90 35349         60090 _A($t, $q->[0], $q->[1]);
91 35349         61558 _M($b, $b, $t);
92 35349         68956 _M($c, $p->[3], $q->[3]);
93 35349         99538 _M($c, $c, [ D2() ]);
94 35349         83433 _M($d, $p->[2], $q->[2]);
95 35349         64707 _A($d, $d, $d);
96 35349         61161 _Z($e, $b, $a);
97 35349         57857 _Z($f, $d, $c);
98 35349         56938 _A($g, $d, $c);
99 35349         54862 _A($h, $b, $a);
100              
101 35349         57910 _M($p->[0], $e, $f);
102 35349         63100 _M($p->[1], $h, $g);
103 35349         63025 _M($p->[2], $g, $f);
104 35349         49224 _M($p->[3], $e, $h);
105             }
106              
107             sub modL {
108 63     63 0 1525 my ($r, $x) = @_;
109              
110 63         102 my ($k);
111              
112 63         161 for my $i ( reverse( 32 .. 63 ) ) {
113 2016         2115 my $carry = 0;
114              
115 2016         2103 my ($j, $k);
116              
117 2016         2841 for (
118             ($j = $i - 32), ($k = $i - 12);
119             $j < $k;
120             ++$j
121             ) {
122 40320         56505 $x->[$j] += $carry - 16 * $x->[$i] * (L())[$j - ($i - 32)];
123              
124             # originally “>> 8” rather than “/ 256”;
125 40320         54806 $carry = Math::Utils::floor( ($x->[$j] + 128) / 256 );
126              
127 40320         125666 $x->[$j] -= $carry * 256;
128             }
129              
130 2016         2140 $x->[$j] += $carry;
131 2016         2518 $x->[$i] = 0;
132             }
133              
134 63         91 my $carry = 0;
135              
136             # In Perl, -98 >> 4 = 1152921504606846969. :-<
137 63         131 my $x31_rshift_4 = Math::Utils::floor( $x->[31] / 16 );
138              
139 63         233 for my $j ( 0 .. 31 ) {
140 2016         2603 $x->[$j] += $carry - $x31_rshift_4 * (L())[$j];
141              
142             # originally “>> 8” rather than “/ 256”; we also need floor
143 2016         2837 $carry = Math::Utils::floor( $x->[$j] / 256 );
144              
145 2016         5700 $x->[$j] &= 255;
146             }
147              
148 63         1067 $x->[$_] -= $carry * (L())[$_] for 0 .. 31;
149              
150 63         109 for my $i ( 0 .. 31 ) {
151 2016         2143 $x->[$i + 1] += $x->[$i] >> 8;
152 2016         2321 $r->[$i] = $x->[$i] & 255;
153             }
154              
155 63         109 return;
156             }
157              
158 3     3   50 use constant gf0 => (0) x 16;
  3         6  
  3         297  
159              
160             #----------------------------------------------------------------------
161              
162 3     3   16 use constant gf1 => ( 1, (0) x 15 );
  3         10  
  3         197  
163              
164 3         182 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 3     3   15 );
  3         5  
169              
170 3         171 use constant D2 => (
171             0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,
172             0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406,
173 3     3   49 );
  3         5  
174              
175 3         180 use constant X => (
176             0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,
177             0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169,
178 3     3   20 );
  3         4  
179              
180 3         3467 use constant Y => (
181             0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
182             0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
183 3     3   14 );
  3         4  
184              
185 17568     17568   23991 sub _S { _M( $_[0], $_[1], $_[1] ) }
186              
187             sub _inv25519 {
188 48     48   120 my ($o, $i) = @_;
189              
190 48         120 my $c = [ @{$i}[0 .. 15] ];
  48         135  
191              
192 48         178 for my $a ( reverse( 0 .. 253 ) ) {
193 12192         19896 _S($c, $c);
194              
195 12192 100       18387 next if $a == 2;
196 12144 100       16099 next if $a == 4;
197              
198 12096         15874 _M( $c, $c, $i );
199             }
200              
201 48         143 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  48         114  
  48         111  
202              
203 48         240 return;
204             }
205              
206             sub _pack25519 {
207 201     201   310 my ($n) = @_;
208              
209 201         261 my $b;
210              
211 201         384 my $o = [];
212              
213 201         278 my $t = [ @{$n}[0 .. 15] ];
  201         611  
214              
215 201         483 my $m = [ gf0() ];
216              
217 201         464 _car25519($t) for 1 .. 3;
218              
219 201         293 for my $j (0, 1) {
220 402         601 $m->[0] = $t->[0] - 0xffed;
221              
222 402         534 for my $i ( 1 .. 14 ) {
223 5628         6808 $m->[$i] = $t->[$i] - 0xffff - (($m->[$i - 1] >> 16) & 1);
224 5628         6264 $m->[$i - 1] &= 0xffff;
225             }
226              
227 402         564 $m->[15] = $t->[15] - 0x7fff - (($m->[14] >> 16) & 1);
228              
229 402         445 $b = ($m->[15] >> 16) & 1;
230              
231 402         489 $m->[14] &= 0xffff;
232              
233 402         652 _sel25519( $t, $m, 1 - $b );
234             }
235              
236 201         321 for my $i ( 0 .. 15 ) {
237 3216         4439 $o->[2 * $i] = $t->[$i] & 0xff;
238 3216         4510 $o->[2 * $i + 1] = $t->[$i] >> 8;
239             }
240              
241 201         482 return $o;
242             }
243              
244             sub _par25519 {
245 69     69   164 my ($a) = @_;
246              
247 69         141 my $d = _pack25519($a);
248              
249 69         263 return $d->[0] & 1;
250             }
251              
252             # o, a, and b are arrays of numbers
253             sub _A {
254 176766     176766   219636 my ($o, $a, $b) = @_;
255              
256 176766         742851 $o->[$_] = $a->[$_] + $b->[$_] for 0 .. 15;
257              
258 176766         205056 return;
259             }
260              
261             # o, a, and b are arrays of numbers
262             sub _Z {
263 141430     141430   178298 my ($o, $a, $b) = @_;
264              
265 141430         618429 $o->[$_] = $a->[$_] - $b->[$_] for 0 .. 15;
266              
267 141430         166037 return;
268             }
269              
270             # o, a, and b are arrays of numbers
271             sub _M {
272 353444     353444   436494 my ($o, $a, $b) = @_;
273              
274 353444         664079 my @t = (0) x 31;
275              
276 353444         430663 for my $a_idx ( 0 .. 15 ) {
277 5655104         23948896 $t[$a_idx + $_] += $a->[$a_idx] * $b->[$_] for 0 .. 15;
278             }
279              
280             # $t->[15] left as-is
281 353444         425114 for my $t_idx ( 0 .. 14 ) {
282 5301660         5870418 $t[$t_idx] += 38 * $t[16 + $t_idx];
283             }
284              
285 353444         381260 my ($c, $v);
286              
287 353444         580625 _car25519(\@t);
288 353444         598649 _car25519(\@t);
289              
290 353444         487160 @{$o}[0 .. 15] = @t[0 .. 15];
  353444         585914  
291              
292 353444         655987 return;
293             }
294              
295             sub _car25519 {
296 707491     707491   813310 my ($o) = @_;
297              
298 707491         750937 my $c = 1;
299 707491         695571 my $v;
300              
301 707491         752908 for my $o_item ( @{$o}[0 .. 15] ) {
  707491         960736  
302 11319856         11364696 $v = $o_item + $c + 65535;
303              
304             # c = Math.floor(v / 65536)
305 11319856         11772679 $c = int( $v / 65536 );
306 11319856 100       13907666 $c-- if $v < 0;
307              
308             # t0 = v - c * 65536
309 11319856         12277401 $o_item = $v - ($c * 65536);
310             }
311              
312 707491         904864 $o->[0] += $c - 1 + 37 * ($c - 1);
313              
314 707491         854088 return;
315             }
316              
317             # p and q are arrays of numbers
318             sub _sel25519 {
319 141714     141714   177288 my ($p, $q, $b) = @_;
320              
321             # $b is either 0 or 1.
322 141714   100     232030 my $c = $b && -1;
323              
324 141714         175294 for my $i ( 0 .. 15 ) {
325 2267424   100     3059507 my $t = $c && ($c & signed_xor($p->[$i], $q->[$i]));
326              
327 2267424 100       3083118 $p->[$i] = signed_xor($p->[$i], $t) if $t;
328 2267424 100       3186728 $q->[$i] = signed_xor($q->[$i], $t) if $t;
329             }
330             }
331              
332             # p and q are arrays of arrays
333             sub _cswap {
334 35328     35328   48728 my ($p, $q, $b) = @_;
335              
336 35328         45814 for my $i ( 0 .. 3 ) {
337 141312         180939 _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 3384516 50 25 3384516 0 6470476 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 3384516         4545568 return $_[0] ^ $_[1];
353             }
354              
355             sub signed_or {
356              
357             # signs are same -> can use native xor
358 2016 50   2016 0 2730 if ( ($_[0] < 0) eq ($_[1] < 0) ) {
359 2016         2673 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 63 my ($r, $p) = @_;
370              
371 21         173 $_ = [ gf0() ] for my (
372             $t,
373             $chk,
374             $num,
375             $den,
376             $den2,
377             $den4,
378             $den6,
379             );
380              
381 21         124 _set25519( $r->[2], [ gf1() ]);
382              
383 21         93 _unpack25519($r->[1], $p);
384              
385 21         60 _S($num, $r->[1]);
386 21         122 _M($den, $num, [ D() ]);
387 21         90 _Z($num, $num, $r->[2]);
388 21         70 _A($den, $r->[2], $den);
389              
390 21         48 _S($den2, $den);
391 21         63 _S($den4, $den2);
392 21         50 _M($den6, $den4, $den2);
393 21         57 _M($t, $den6, $num);
394 21         48 _M($t, $t, $den);
395              
396 21         90 _pow2523($t, $t);
397 21         117 _M($t, $t, $num);
398 21         47 _M($t, $t, $den);
399 21         55 _M($t, $t, $den);
400 21         79 _M($r->[0], $t, $den);
401              
402 21         61 _S($chk, $r->[0]);
403 21         73 _M($chk, $chk, $den);
404              
405 21 100       68 if (_neq25519($chk, $num)) {
406 14         84 _M($r->[0], $r->[0], [ I() ]);
407             }
408              
409 21         62 _S($chk, $r->[0]);
410 21         52 _M($chk, $chk, $den);
411              
412 21 50       53 if (_neq25519($chk, $num)) {
413 0         0 die "-1??";
414             }
415              
416             # “>>” appears to be safe here.
417 21 100       52 if (_par25519($r->[0]) == ($p->[31] >> 7)) {
418 13         85 _Z($r->[0], [ gf0() ], $r->[0]);
419             }
420              
421 21         65 _M( $r->[3], $r->[0], $r->[1] );
422              
423 21         212 return 0;
424             }
425              
426             sub crypto_verify_32 {
427 63     63 0 165 my ($x, $xi, $y, $yi) = @_;
428              
429 63         174 return _vn($x, $xi, $y, $yi, 32);
430             }
431              
432 3         214 use constant D => (
433             0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
434             0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203,
435 3     3   16 );
  3         5  
436              
437 3         1026 use constant I => (
438             0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
439             0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
440 3     3   15 );
  3         4  
441              
442             sub _set25519 {
443 21     21   50 my ($r, $a) = @_;
444              
445 21         116 $r->[$_] = $a->[$_] | 0 for 0 .. 15;
446             }
447              
448             sub _unpack25519 {
449 21     21   43 my ($o, $n) = @_;
450              
451 21         33 for my $i (0 .. 15) {
452              
453             # originally “<< 8” rather than “256 *”
454 336         490 $o->[$i] = $n->[ 2 * $i ] + (256 * $n->[ 2 * $i + 1 ])
455             }
456              
457 21         43 $o->[15] &= 0x7fff;
458             }
459              
460             sub _pow2523 {
461 21     21   40 my ($o, $i) = @_;
462              
463 21         35 my $c = [ @{$i}[0 .. 15] ];
  21         77  
464              
465 21         75 for my $a ( reverse( 0 .. 250 ) ) {
466 5271         8740 _S( $c, $c );
467              
468 5271 100       7489 if ($a != 1) {
469 5250         6318 _M( $c, $c, $i );
470             }
471             }
472              
473 21         57 @{$o}[0 .. 15] = @{$c}[0 .. 15];
  21         62  
  21         39  
474             }
475              
476             sub _neq25519 {
477 42     42   67 my ($a, $b) = @_;
478              
479 42         111 my $c = _pack25519($a);
480 42         68 my $d = _pack25519($b);
481              
482 42         110 return crypto_verify_32($c, 0, $d, 0);
483             }
484              
485             sub _vn {
486 63     63   142 my ($x, $xi, $y, $yi, $n) = @_;
487              
488 63         96 my $d = 0;
489              
490 63         144 for my $i ( 0 .. ($n - 1) ) {
491 2016         2665 $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         296 return (1 & (($d - 1) >> 8)) - 1;
497             }
498              
499             1;