package Crypt::Perl::ECDSA::EC::Point;
use strict;
use warnings;
#----------------------------------------------------------------------
# NOTE TO SELF: This module’s internal coordinates are
# homogeneous/projective coordinates, not Cartesian.
#----------------------------------------------------------------------
use Crypt::Perl::BigInt ();
my ($bi1, $bi2, $bi3);
END {
undef $bi1, $bi2, $bi3;
}
sub new_infinity {
my ($class) = @_;
return $class->new( undef, undef );
}
#$curve is ECCurve
#$x and $y are “ECFieldElement”
#$z isa bigint (?)
sub new {
my ($class, $curve, $x, $y, $z) = @_;
$bi1 ||= Crypt::Perl::BigInt->new(1);
$bi2 ||= Crypt::Perl::BigInt->new(2);
$bi3 ||= Crypt::Perl::BigInt->new(3);
my $self = {
curve => $curve,
x => $x,
y => $y,
# Generally z won’t be given since we expect
# Cartesian coordinates as input. But accepting
# z allows this constructor to receive Jacobi
# coordinates as well.
z => $z || $bi1->copy(),
zinv => undef,
};
return bless $self, $class;
}
sub is_infinity {
my ($self) = @_;
return 1 if !defined $self->{'x'} && !defined $self->{'y'};
return( ($self->{'z'}->is_zero() && !$self->{'y'}->to_bigint()->is_zero()) || 0 );
}
#returns ECFieldElement (Cartesian)
sub get_x {
my ($self) = @_;
return $self->_get_x_or_y('x');
}
#returns ECFieldElement (Cartesian)
#Used in key generation (not signing … ?)
sub get_y {
my ($self) = @_;
return $self->_get_x_or_y('y');
}
sub _get_x_or_y {
my ($self, $to_get) = @_;
if (!defined $self->{'zinv'}) {
$self->{'zinv'} = $self->{'z'}->copy()->bmodinv($self->{'curve'}{'q'});
}
return $self->{'curve'}->from_bigint(
$self->{$to_get}->to_bigint()->copy()->bmul($self->{'zinv'})->bmod($self->{'curve'}{'q'})
);
}
sub negate {
my ($self) = @_;
my @args = @{$self}{qw( curve x y z )};
$args[2] = $args[2]->negate();
return (ref $self)->new(@args);
}
sub twice {
my ($self) = @_;
return $self if $self->is_infinity();
#if ($self->{'y'}->to_bigint()->signum() == 0) {
# return $self->{'curve'}->get_infinity();
#}
my $x1 = $self->{'x'}->to_bigint();
my $y1 = $self->{'y'}->to_bigint();
my $y1z1 = $y1->copy()->bmul($self->{'z'});
my $y1sqz1 = $y1z1->copy()->bmul($y1)->bmod($self->{'curve'}{'q'});
my $a = $self->{'curve'}{'a'};
# w = 3 * x1^2 + a * z1^2
#var w = x1.square().multiply(THREE);
my $w = $x1->copy()->bpow($bi2)->bmul($bi3);
if (!$a->is_zero()) {
#$w += ($self->{'z'} ** 2) * $a;
$w->badd( $a->copy()->bmul( $self->{'z'} )->bmul($self->{'z'}) );
}
$w->bmod($self->{'curve'}{'q'});
# x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1)
#var x3 = w.square().subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(this.curve.q);
my $x3 = $w->copy()->bmuladd( $w, $y1sqz1->copy()->bmul($x1)->blsft($bi3)->bneg() )->bmul($bi2)->bmul($y1z1);
#my $x3 = 2 * $y1z1 * (($w ** 2) - ($x1 << 3) * $y1sqz1);
#my $x3 = ($w ** 2) - (($x1 << 3) * $y1sqz1);
#$x3 = $x3 << 1;
#$x3 *= $y1z1;
# y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3
#var y3 = w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1)).shiftLeft(2).multiply(y1sqz1).subtract(w.square().multiply(w)).mod(this.curve.q);
#my $y3 = 4 * $y1sqz1 * (3 * $w * $x1 - 2 * $y1sqz1) - ($w ** 3);
my $y3 = $y1sqz1->copy()->blsft($bi2);
$y3->bmuladd(
#We don’t need y1sqz1 anymore
$w->copy()->bmul($bi3)->bmuladd($x1, $y1sqz1->blsft($bi1)->bneg()),
#Don’t need $w anymore
$w->bpow($bi3)->bneg(),
);
#// z3 = 8 * (y1 * z1)^3
#var z3 = y1z1.square().multiply(y1z1).shiftLeft(3).mod(this.curve.q);
#my $z3 = ($y1z1 ** 3) << 3;
my $z3 = $y1z1->bpow($bi3)->blsft($bi3); #don’t need y1z1 anymore
#In original JS logic
$_->bmod($self->{'curve'}{'q'}) for ($x3, $y3, $z3);
#return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3);
return (ref $self)->new(
$self->{'curve'},
$self->{'curve'}->from_bigint($x3),
$self->{'curve'}->from_bigint($y3),
$z3,
);
}
#XXX clear
sub dump {
my ($self, $label) = @_;
$label = q<> if !defined $label;
printf "$label.x: %s\n", $self->{'x'}->to_bigint()->as_hex();
printf "$label.y: %s\n", $self->{'y'}->to_bigint()->as_hex();
printf "$label.z: %s\n", $self->{'z'}->as_hex();
}
sub multiply {
my ($self, $k) = @_;
if ($self->is_infinity()) {
return $self;
}
# “Montgomery ladder” algorithm taken from Wikipedia:
#
# R0 ← 0
# R1 ← P
# for i from m downto 0 do
# if di = 0 then
# R1 ← point_add(R0, R1)
# R0 ← point_double(R0)
# else
# R0 ← point_add(R0, R1)
# R1 ← point_double(R1)
# return R0
#
# This thwarts the timing attacks that can recover private keys
# by running the standard “double-and-add” algorithm over and over
# and analyzing response times.
my $r0 = ref($self)->new_infinity();
my $r1 = $self;
for my $i ( reverse( 0 .. ($k->bit_length() - 1) ) ) {
if ($k->test_bit($i)) {
$r0 = $r0->add($r1);
$r1 = $r1->twice();
}
else {
$r1 = $r0->add($r1);
$r0 = $r0->twice();
}
}
return $r0;
}
#$b isa ECPoint
sub add {
my ($self, $b) = @_;
#$b->dump('$b');
#if(this.isInfinity()) return b;
#if(b.isInfinity()) return this;
return $b if $self->is_infinity();
return $self if $b->is_infinity();
#// u = Y2 * Z1 - Y1 * Z2
#var u = b.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(b.z)).mod(this.curve.q);
my $u = $b->{'y'}->to_bigint()->copy()->bmuladd(
$self->{'z'},
$self->{'y'}->to_bigint()->copy()->bneg()->bmul($b->{'z'}),
);
# $b->{'z'};
#// v = X2 * Z1 - X1 * Z2
#var v = b.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(b.z)).mod(this.curve.q);
my $v = $b->{'x'}->to_bigint()->copy()->bmuladd(
$self->{'z'},
$self->{'x'}->to_bigint()->copy()->bneg()->bmul($b->{'z'}),
);
$_->bmod($self->{'curve'}{'q'}) for ($u, $v);
#print "u: " . $u->as_hex() . $/;
#print "v: " . $v->as_hex() . $/;
#if(BigInteger.ZERO.equals(v)) {
# if(BigInteger.ZERO.equals(u)) {
# return this.twice(); // this == b, so double
# }
#return this.curve.getInfinity(); // this = -b, so infinity
#}
if ($v->is_zero()) {
if ($u->is_zero()) {
return $self->twice();
}
return $self->{'curve'}->get_infinity();
}
#var THREE = new BigInteger("3");
#var x1 = this.x.toBigInteger();
#var y1 = this.y.toBigInteger();
#var x2 = b.x.toBigInteger();
#var y2 = b.y.toBigInteger();
my ($x1, $y1, $z1) = @{$self}{ qw( x y z ) };
my ($x2, $y2, $z2) = @{$b}{ qw( x y z ) };
$_ = $_->to_bigint() for ($x1, $y1, $x2, $y2);
#var v2 = v.square();
#var v3 = v2.multiply(v);
#var x1v2 = x1.multiply(v2);
#var zu2 = u.square().multiply(this.z);
my $v2 = $v->copy()->bmul($v);
my $v3 = $v->copy()->bmul($v2);
my $x1v2 = $x1->copy()->bmul($v2);
my $zu2 = $u->copy()->bmul($u)->bmul($self->{'z'});
#use Data::Dumper;
#print Dumper( map { $_->as_hex() } $u, $v, $x1, $y1, $z1, $x2, $y2, $z2, $v2, $v3, $x1v2, $zu2 );
#// x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3)
#var x3 = zu2.subtract(x1v2.shiftLeft(1)).multiply(b.z).subtract(v3).multiply(v).mod(this.curve.q);
#my $x3 = $v * ($z2 * ($z1 * ($u ** 2) - 2 * $x1 * ($v ** 2)) - ($v ** 3));
my $x3 = $u->copy()->bmul($u);
$x3->bmuladd( $z1, $x1->copy()->blsft($bi1)->bneg()->bmul($v)->bmul($v) );
$x3->bmuladd( $z2, $v->copy()->bpow($bi3)->bneg() );
$x3->bmul($v);
#// y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3
#var y3 = x1v2.multiply(THREE).multiply(u).subtract(y1.multiply(v3)).subtract(zu2.multiply(u)).multiply(b.z).add(u.multiply(v3)).mod(this.curve.q);
#my $y3 = $z2 * (3 * $x1 * $u * $v2 - $y1 * $v3 - $z1 * ($u ** 3)) + $u * $v3;
my $y3 = $u->copy()->bmul($bi3)->bmul($x1);
$y3->bmuladd($v2, $y1->copy()->bmul($v3)->bneg()); #no more y1 after this
$y3->bsub( $u->copy()->bpow($bi3)->bmul($z1) );
$y3->bmuladd( $z2, $u->bmul($v3) ); #we don’t need $u anymore
#// z3 = v^3 * z1 * z2
#var z3 = v3.multiply(this.z).multiply(b.z).mod(this.curve.q);
my $z3 = $v3->bmul($z1)->bmul($z2);
$_->bmod($self->{'curve'}{'q'}) for ($x3, $y3, $z3);
return (ref $self)->new(
$self->{'curve'},
$self->{'curve'}->from_bigint($x3),
$self->{'curve'}->from_bigint($y3),
$z3,
);
}
1;