286 lines
6.1 KiB
Perl
286 lines
6.1 KiB
Perl
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<a>, C<b>, or C<n> values that describe the curve.
|
|
|
|
=head2 x
|
|
|
|
=head2 y
|
|
|
|
Returns the C<x> or C<y> 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<Math::EllipticCurve::Prime>
|
|
|
|
This really should just be in a L<Math::EllipticCurve> module.
|
|
|
|
=head1 AUTHORS
|
|
|
|
Dana Jacobsen E<lt>dana@acm.orgE<gt>
|
|
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright 2012-2013 by Dana Jacobsen E<lt>dana@acm.orgE<gt>
|
|
|
|
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
|
|
|
|
=cut
|