File Coverage

blib/lib/Math/EllipticCurve/Prime/Point.pm
Criterion Covered Total %
statement 111 121 91.7
branch 25 36 69.4
condition 11 23 47.8
subroutine 20 23 86.9
pod 16 16 100.0
total 183 219 83.5


line stmt bran cond sub pod time code
1 5     5   70 use 5.006002;
  5         12  
2 5     5   18 use strict;
  5         8  
  5         97  
3 5     5   19 use warnings;
  5         5  
  5         264  
4              
5             package Math::EllipticCurve::Prime::Point;
6             {
7             $Math::EllipticCurve::Prime::Point::VERSION = '0.003';
8             }
9             # ABSTRACT: points for elliptic curve operations over prime fields
10              
11 5     5   21 use Math::BigInt 1.78 try => 'GMP,FastCalc';
  5         59  
  5         29  
12 5     5   2321 use List::Util;
  5         6  
  5         6188  
13              
14              
15             sub new {
16 648     648 1 13483 my ($class, %args) = @_;
17              
18 648 50 66     1644 if (!defined $args{x} && !defined $args{y} && !defined $args{infinity}) {
      33        
19 4         5 $args{infinity} = 1;
20             }
21 648   100     2510 $args{infinity} ||= 0;
22 648 100       1165 delete @args{qw/x y/} if $args{infinity};
23              
24             $args{curve} = Math::EllipticCurve::Prime->from_name($args{curve})
25 648 50 66     2841 if $args{curve} && !ref $args{curve};
26              
27 648         817 my $self = \%args;
28 648   66     1377 $class = ref($class) || $class;
29 648         1738 return bless $self, $class;
30             }
31              
32              
33             sub from_hex {
34 39     39 1 3117 my ($class, $hex) = @_;
35              
36 39 100       120 return $class->new if substr($hex, 0, 2) eq "00";
37 37 50       88 return unless substr($hex, 0, 2) eq "04";
38 37         64 $hex = substr($hex, 2);
39 37         74 my $len = length $hex;
40 37 50       79 return if $len & 4;
41             my ($x, $y) = map {
42 37         136 Math::BigInt->new("0x$_")
  74         16636  
43             } (substr($hex, 0, $len / 2), substr($hex, $len / 2));
44 37         16091 return $class->new(x => $x, y => $y);
45             }
46              
47              
48             sub from_bytes {
49 11     11 1 45 my ($class, $bytes) = @_;
50 11         30 return $class->from_hex(unpack "H*", $bytes);
51             }
52              
53              
54             sub to_hex {
55 32     32 1 29 my $self = shift;
56              
57 32 100       40 return "00" if $self->infinity;
58 30         40 my $x = $self->x->as_hex;
59 30         13172 my $y = $self->y->as_hex;
60 30         12826 $x =~ s/^0x//;
61 30         50 $y =~ s/^0x//;
62 30         64 my $length = List::Util::max(length $x, length $y);
63 30 100       54 $length++ if $length & 1;
64              
65 30         25 my $result = "04";
66 30         54 $result .= ("0" x ($length - length $x)) . $x;
67 30         41 $result .= ("0" x ($length - length $y)) . $y;
68              
69 30         123 return $result;
70             }
71              
72              
73             sub to_bytes {
74 21     21 1 20 my $self = shift;
75              
76 21         35 return pack "H*", $self->to_hex;
77             }
78              
79              
80             sub copy {
81 607     607 1 552047 my $self = shift;
82             return $self->new(x => $self->{x}->copy, y => $self->{y}->copy,
83 607         1595 curve => $self->{curve});
84             }
85              
86              
87             *clone = \©
88              
89             sub _set_infinity {
90 304     304   391 my $self = shift;
91              
92 304         391 $self->{infinity} = 1;
93 304         315 delete @{$self}{qw/x y/};
  304         748  
94              
95 304         465 return $self;
96             }
97              
98              
99             sub bmul {
100 304     304 1 1290 my ($self, $k) = @_;
101              
102 304         576 my $bits = $k->copy->blog(2);
103 304         114810 my $mask = Math::BigInt->bone->blsft($bits);
104 304         67322 my $pt = $self->copy;
105              
106 304         705 $self->_set_infinity;
107              
108 304         1152 for (reverse 0..$bits) {
109 58866         6502356 $self->bdbl;
110 58866 100       253131 if ($k->copy->band($mask)) {
111 36879         23100581 $self->badd($pt);
112             }
113 58866         9812809 $mask->brsft(1);
114             }
115 304         33818 return $self;
116             }
117              
118             # A helper to do the boring and repetitive parts of point addition.
119             sub _add_points {
120 95148     95148   136136 my ($self, $x1, $x2, $y1, $lambda, $p) = @_;
121              
122 95148         236707 my $x = $lambda->copy->bmodpow(2, $p);
123 95148         144306732 $x->bsub($x1);
124 95148         9100497 $x->bsub($x2);
125 95148         5845273 $x->bmod($p);
126              
127 95148         8682554 my $y = $x1->copy->bsub($x);
128 95148         7623607 $y->bmul($lambda);
129 95148         25626455 $y->bsub($y1);
130 95148         5971222 $y->bmod($p);
131              
132 95148         45238966 @{$self}{qw/x y/} = ($x, $y);
  95148         217194  
133 95148         358022 return $self;
134             }
135              
136              
137             # The algorithm used here is specified in SEC 1, page 7.
138             sub badd {
139 36889     36889 1 46779 my ($self, $other) = @_;
140              
141 36889 50       72873 die "Can't add a point without a curve" unless $self->curve;
142              
143 36889 50 66     79300 if ($self->infinity && $other->infinity) {
    50 0        
    100 0        
    50          
    0          
144 0         0 return $self;
145             }
146             elsif ($other->infinity) {
147 0         0 return $self;
148             }
149             elsif ($self->infinity) {
150 304         596 $self->{infinity} = 0;
151 304         386 @{$self}{qw/x y/} = map { $_->copy } @{$other}{qw/x y/};
  304         2976  
  608         3843  
  304         597  
152 304         444 return $self;
153             }
154             elsif ($self->{x}->bcmp($other->{x})) {
155 36585         701721 my $p = $self->curve->p;
156 36585         73111 my $lambda = $other->y->copy->bsub($self->y);
157 36585         2707780 my $bottom = $other->x->copy->bsub($self->x)->bmodinv($p);
158 36585         702419413 $lambda->bmul($bottom)->bmod($p);
159              
160 36585         28591920 return $self->_add_points($self->x, $other->x, $self->y, $lambda, $p);
161             }
162             elsif ($self->{y}->is_zero || $other->{y}->is_zero ||
163             $self->{y}->bcmp($other->{y})) {
164              
165 0         0 return $self->_set_infinity;
166             }
167             else {
168 0         0 return $self->bdbl;
169             }
170             }
171              
172              
173             # The algorithm used here is specified in SEC 1, page 7.
174             sub bdbl {
175 58868     58868 1 81395 my $self = shift;
176              
177 58868 100       116704 return $self if $self->infinity;
178              
179             die "Can't multiply or double a point without a curve"
180 58563 50       145535 unless defined $self->{curve};
181            
182 58563         106556 my $p = $self->curve->p;
183 58563         90096 my $lambda = $self->x->copy->bmodpow(2, $p);
184 58563         85674513 $lambda->bmul(3);
185 58563         4969795 $lambda->badd($self->curve->a);
186 58563         2838616 my $bottom = $self->y->copy->bmul(2)->bmodinv($p);
187 58563         1103624657 $lambda->bmul($bottom)->bmod($p);
188              
189 58563         44001412 return $self->_add_points($self->x, $self->x, $self->y, $lambda, $p);
190             }
191              
192              
193             sub multiply {
194 0     0 1 0 my ($self, $k) = @_;
195 0         0 return $self->copy->bmul($k);
196             }
197              
198              
199             sub add {
200 0     0 1 0 my ($self, $other) = @_;
201 0         0 return $self->copy->badd($other);
202             }
203              
204              
205             sub double {
206 0     0 1 0 my $self = shift;
207 0         0 return $self->copy->bdbl;
208             }
209              
210              
211             sub infinity {
212 169895     169895 1 134067 my $self = shift;
213 169895         523030 return $self->{infinity};
214             }
215              
216              
217             sub x {
218 322357     322357 1 600387 my $self = shift;
219 322357         590664 return $self->{x};
220             }
221              
222              
223             sub y {
224 227189     227189 1 1075635 my $self = shift;
225 227189         494327 return $self->{y};
226             }
227              
228              
229             sub curve {
230 190617     190617 1 233189 my ($self, $curve) = @_;
231              
232 190617 100       341991 $self->{curve} = $curve if defined $curve;
233 190617         571258 return $self->{curve};
234             }
235              
236             1;
237              
238             __END__