package Math::Prime::Util::ECAffinePoint; use strict; use warnings; use Carp qw/carp croak confess/; BEGIN { $Math::Prime::Util::ECAffinePoint::AUTHORITY = 'cpan:DANAJ'; $Math::Prime::Util::ECAffinePoint::VERSION = '0.73'; } BEGIN { do { require Math::BigInt; Math::BigInt->import(try=>"GMP,Pari"); } unless defined $Math::BigInt::VERSION; } # Pure perl (with Math::BigInt) manipulation of Elliptic Curves # in affine coordinates. Should be split into a point class. sub new { my ($class, $a, $b, $n, $x, $y) = @_; $a = Math::BigInt->new("$a") unless ref($a) eq 'Math::BigInt'; $b = Math::BigInt->new("$b") unless ref($b) eq 'Math::BigInt'; $n = Math::BigInt->new("$n") unless ref($n) eq 'Math::BigInt'; $x = Math::BigInt->new("$x") unless ref($x) eq 'Math::BigInt'; $y = Math::BigInt->new("$y") unless ref($y) eq 'Math::BigInt'; croak "n must be >= 2" unless $n >= 2; $a->bmod($n); $b->bmod($n); my $self = { a => $a, b => $b, n => $n, x => $x, y => $y, f => $n->copy->bone, }; bless $self, $class; return $self; } sub _add { my ($self, $P1x, $P1y, $P2x, $P2y) = @_; my $n = $self->{'n'}; if ($P1x == $P2x) { my $t = ($P1y + $P2y) % $n; return (Math::BigInt->bzero,Math::BigInt->bone) if $t == 0; } my $deltax = ($P2x - $P1x) % $n; $deltax->bmodinv($n); return (Math::BigInt->bzero,Math::BigInt->bone) if $deltax eq "NaN"; my $deltay = ($P2y - $P1y) % $n; my $m = ($deltay * $deltax) % $n; # m = deltay / deltax my $x = ($m*$m - $P1x - $P2x) % $n; my $y = ($m*($P1x - $x) - $P1y) % $n; return ($x,$y); } sub _double { my ($self, $P1x, $P1y) = @_; my $n = $self->{'n'}; my $m = 2*$P1y; $m->bmodinv($n); return (Math::BigInt->bzero,Math::BigInt->bone) if $m eq "NaN"; $m = ((3*$P1x*$P1x + $self->{'a'}) * $m) % $n; my $x = ($m*$m - 2*$P1x) % $n; my $y = ($m*($P1x - $x) - $P1y) % $n; return ($x,$y); } sub _inplace_add { my ($P1x, $P1y, $x, $y, $n) = @_; if ($P1x == $x) { my $t = ($P1y + $y) % $n; if ($t == 0) { ($_[2],$_[3]) = (Math::BigInt->bzero,Math::BigInt->bone); return; } } my $deltax = ($x - $P1x) % $n; $deltax->bmodinv($n); if ($deltax eq 'NaN') { ($_[2],$_[3]) = (Math::BigInt->bzero,Math::BigInt->bone); return; } my $deltay = ($y - $P1y) % $n; my $m = ($deltay * $deltax) % $n; # m = deltay / deltax $_[2] = ($m*$m - $P1x - $x) % $n; $_[3] = ($m*($P1x - $_[2]) - $P1y) % $n; } sub _inplace_double { my($x, $y, $a, $n) = @_; my $m = $y+$y; $m->bmodinv($n); if ($m eq 'NaN') { ($_[0],$_[1]) = (Math::BigInt->bzero,Math::BigInt->bone); return; } $m->bmul($x->copy->bmul($x)->bmul(3)->badd($a))->bmod($n); my $xin = $x->copy; $_[0] = ($m*$m - $x - $x) % $n; $_[1] = ($m*($xin - $_[0]) - $y) % $n; } sub mul { my ($self, $k) = @_; my $x = $self->{'x'}; my $y = $self->{'y'}; my $a = $self->{'a'}; my $n = $self->{'n'}; my $f = $self->{'f'}; if (ref($k) eq 'Math::BigInt' && $k < ''.~0) { if ($] >= 5.008 || ~0 == 4294967295) { $k = int($k->bstr); } elsif ($] < 5.008 && ~0 > 4294967295 && $k < 562949953421312) { $k = unpack('Q',pack('Q',$k->bstr)); } } my $Bx = $n->copy->bzero; my $By = $n->copy->bone; my $v = 1; while ($k > 0) { if ( ($k % 2) != 0) { $k--; $f->bmul($Bx-$x)->bmod($n); if ($x->is_zero && $y->is_one) { } elsif ($Bx->is_zero && $By->is_one) { ($Bx,$By) = ($x,$y); } else { _inplace_add($x,$y,$Bx,$By,$n); } } else { $k >>= 1; $f->bmul(2*$y)->bmod($n); _inplace_double($x,$y,$a,$n); } } $f = Math::BigInt::bgcd($f, $n); $self->{'x'} = $Bx; $self->{'y'} = $By; $self->{'f'} = $f; return $self; } sub add { my ($self, $other) = @_; croak "add takes a EC point" unless ref($other) eq 'Math::Prime::Util::ECAffinePoint'; croak "second point is not on the same curve" unless $self->{'a'} == $other->{'a'} && $self->{'b'} == $other->{'b'} && $self->{'n'} == $other->{'n'}; ($self->{'x'}, $self->{'y'}) = $self->_add($self->{'x'}, $self->{'y'}, $other->{'x'}, $other->{'y'}); return $self; } sub a { return shift->{'a'}; } sub b { return shift->{'b'}; } sub n { return shift->{'n'}; } sub x { return shift->{'x'}; } sub y { return shift->{'y'}; } sub f { return shift->{'f'}; } sub is_infinity { my $self = shift; return ($self->{'x'}->is_zero() && $self->{'y'}->is_one()); } 1; __END__ # ABSTRACT: Elliptic curve operations for affine points =pod =encoding utf8 =for stopwords mul =for test_synopsis use v5.14; my($a,$b,$n,$k,$ECP2); =head1 NAME Math::Prime::Util::ECAffinePoint - Elliptic curve operations for affine points =head1 VERSION Version 0.73 =head1 SYNOPSIS # Create a point on a curve (a,b,n) with coordinates 0,1 my $ECP = Math::Prime::Util::ECAffinePoint->new($a, $b, $n, 0, 1); # scalar multiplication by k. $ECP->mul($k); # add two points on the same curve $ECP->add($ECP2); say "P = O" if $ECP->is_infinity(); =head1 DESCRIPTION This really should just be in Math::EllipticCurve. To write. =head1 FUNCTIONS =head2 new $point = Math::Prime::Util::ECAffinePoint->new(a, b, n, x, y); Returns a new point at C<(x,y,1)> on the curve C<(a,b,n)>. =head2 a =head2 b =head2 n Returns the C, C, or C values that describe the curve. =head2 x =head2 y Returns the C or C values that define the point on the curve. =head2 f Returns a possible factor found during EC multiplication. =head2 add Takes another point on the same curve as an argument and adds it this point. =head2 mul Takes an integer and performs scalar multiplication of the point. =head2 is_infinity Returns true if the point is (0,1), which is the point at infinity for the affine coordinates. =head1 SEE ALSO L This really should just be in a L module. =head1 AUTHORS Dana Jacobsen Edana@acm.orgE =head1 COPYRIGHT Copyright 2012-2013 by Dana Jacobsen Edana@acm.orgE This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut