+# -*-perl-*-
+#
+# $Id$
+#
+# Elliptic curves
+#
+# (c) 2004 Straylight/Edgeware
+#
+
+#----- Licensing notice -----------------------------------------------------
+#
+# This file is part of the Perl interface to Catacomb.
+#
+# Catacomb/Perl is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Catacomb/Perl is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Catacomb/Perl; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#----- Elliptic curves ------------------------------------------------------
+
+package Catacomb::EC::Curve;
+use Carp;
+use Catacomb::Base;
+use Catacomb::Cache;
+use Catacomb::Field;
+
+$cache = Catacomb::Cache->new();
+
+sub intern {
+ croak("Usage: Catacomb::EC::Curve::intern(c)") unless @_ == 1;
+ my ($c) = @_;
+ return $cache->intern($c);
+}
+
+sub pt {
+ croak("Usage: Catacomb::EC::Curve::pt(c, [x, y | p])")
+ unless @_ >= 1 && @_ <= 3;
+ return Catacomb::EC::Pt->new(@_);
+}
+
+sub a {
+ croak("Usage: Catacomb::EC::Curve::a(c)") unless @_ == 1;
+ my ($c) = @_;
+ return $c->field()->elt($c->_a());
+}
+
+sub b {
+ croak("Usage: Catacomb::EC::Curve::b(c)") unless @_ == 1;
+ my ($c) = @_;
+ return $c->field()->elt($c->_b());
+}
+
+sub inf {
+ croak("Usage: Catacomb::EC::Curve::inf(c)") unless @_ == 1;
+ return Catacomb::EC::Pt->new($_[0]);
+}
+
+sub getraw {
+ croak("Usage: Catacomb::EC::Curve::getraw(c, s)") unless @_ == 2;
+ my ($c, $s) = @_;
+ my ($p, $rest) = $c->_getraw($s);
+ $p = Catacomb::EC::Pt->new($c, $p);
+ return !wantarray() ? $p : ($p, $rest);
+}
+
+sub find {
+ croak("Usage: Catacomb::EC::Curve::find(c, x)") unless @_ == 2;
+ my ($c, $x) = @_;
+ my $p = $c->_find($x);
+ return undef unless defined $p;
+ return Catacomb::EC::Pt->new($c, $p);
+}
+
+sub rand {
+ croak("Usage: Catacomb::EC::Curve::rand(c, [rng])")
+ unless @_ >= 1 && @_ <= 2;
+ my ($c, $rng) = @_;
+ $rng ||= $Catacomb::random;
+ my $p = $c->_rand($rng);
+ return Catacomb::EC::Pt->new($c, $p);
+}
+
+sub mmul {
+ croak("Usage: Catacomb::EC::Curve::mmul(c, p_0, x_0, p_1, x_1, ...)")
+ unless @_ >= 3 && @_ % 2 == 1;
+ my $c = pop(@_);
+ my $i;
+ my @v = ();
+ my @r = ();
+ for ($i = 0; $i < @_; $i += 2) {
+ my $p = $_[$i];
+ my $n = $_[$i + 1];
+ if (UNIVERSAL::isa($p, Catacomb::EC::Pt)) {
+ $p->[1] == $c or croak("curve mismatch");
+ @r or @r = @$p[1, 2, 3, 4];
+ $p = $p->[0];
+ } elsif (UNIVERSAL::isa($p, Catacomb::EC::Point)) {
+ $p = $c->in($p);
+ } else {
+ croak("not a curve point");
+ }
+ push(@v, $p, $n);
+ }
+ unless (@r) {
+ my ($cr, $f, $fr);
+ ($c, $cr) = $c->intern();
+ ($f, $fr) = $c->field()->intern();
+ @r = ($c, $cr, $f, $fr);
+ }
+ return Catacomb::EC::Pt::_pt(immul($c, @v), $c, $cr, $f, $fr);
+}
+
+sub getinfo {
+ croak("Usage: Catacomb::EC::Curve::getinfo(me, spec)") unless @_ == 2;
+ my ($me, $spec) = @_;
+ my ($c, $p, $r, $h) = _getinfo($me, $spec);
+ my $cr;
+ ($c, $cr) = $c->intern();
+ return $c, $c->pt($p), $r, $h;
+}
+
+sub ecgroup {
+ croak("Usage: Catacomb::EC::Curve::ecgroup(c, p, r, h)") unless @_ == 4;
+ return Catacomb::Group->ec(@_);
+}
+
+#----- Elliptic curve points ------------------------------------------------
+
+package Catacomb::EC::Point;
+
+sub tostring {
+ croak("Usage: Catacomb::EC::Point::tostring(p)") unless @_ == 1;
+ my ($p) = @_;
+ if ($p->atinfp()) {
+ return "inf";
+ } else {
+ return "0x" . $p->x()->tostring(16) . ", 0x" . $p->y()->tostring(16);
+ }
+}
+
+package Catacomb::EC::Pt;
+use Carp;
+use Catacomb::Base;
+use Catacomb::Field;
+
+sub _pt { bless [@_], Catacomb::EC::Pt; }
+
+sub _convert {
+ my ($c, $cr, $f, $fr, $x) = @_;
+ if (UNIVERSAL::isa($x, Catacomb::EC::Pt)) {
+ croak("curve mismatch") unless $c == $x->[1];
+ return $x;
+ }
+ if (UNIVERSAL::isa($x, Catacomb::EC::Point)) {
+ return _pt($x, $c, $cr, $f, $fr);
+ }
+ croak("can't convert to curve point");
+}
+
+sub new {
+ croak("Usage: Catacomb::EC::Pt::new(me, c, [x, y | p])")
+ unless @_ >= 2 && @_ <= 4;
+ my ($me, $c, $p);
+ if (@_ == 2) {
+ ($me, $c) = @_;
+ $p = Catacomb::EC::Point->new();
+ } elsif (@_ == 3) {
+ ($me, $c, $p) = @_;
+ if (UNIVERSAL::isa($p, Catacomb::EC::Pt)) {
+ $p = $p->point();
+ } elsif (!UNIVERSAL::isa($p, Catacomb::EC::Point)) {
+ croak("not a curve point");
+ }
+ } else {
+ my ($x, $y);
+ ($me, $c, $x, $y) = @_;
+ $p = Catacomb::EC::Point->new($x, $y);
+ }
+ my ($cr, $f, $fr);
+ ($c, $cr) = $c->intern();
+ ($f, $fr) = $c->field()->intern();
+ return _pt($c->in($p), $c, $cr, $f, $fr);
+}
+
+sub point {
+ croak("Usage: Catacomb::EC::Pt::point(p)") unless @_ == 1;
+ return $_[0][1]->out($_[0][0]);
+}
+
+sub curve {
+ croak("Usage: Catacomb::EC::Pt::curve(p)") unless @_ == 1;
+ return $_[0][1];
+}
+
+sub field {
+ croak("Usage: Catacomb::EC::Pt::field(p)") unless @_ == 1;
+ return $_[0][3];
+}
+
+sub atinfp {
+ croak("Usage: Catacomb::EC::Pt::atinfp(p)") unless @_ == 1;
+ return $_[0]->point()->atinfp();
+}
+
+sub x {
+ croak("Usage: Catacomb::EC::Pt::x(p)") unless @_ == 1;
+ return $_[0][3]->elt($_[0]->point()->x());
+}
+
+sub y {
+ croak("Usage: Catacomb::EC::Pt::y(p)") unless @_ == 1;
+ return $_[0][3]->elt($_[0]->point()->y());
+}
+
+sub check {
+ croak("Usage: Catacomb::EC::Curve::check(c)") unless @_ == 1;
+ return $_[0][1]->check($_[0][0]);
+}
+
+sub pt {
+ croak("Usage: Catacomb::EC::Pt::pt(pp, [x, y | p])")
+ unless @_ >= 1 && @_ <= 3;
+ my ($pp, $p);
+ if (@_ == 1) {
+ ($pp) = @_;
+ $p = Catacomb::EC::Point->new();
+ } elsif (@_ == 2) {
+ ($pp, $p) = @_;
+ if (UNIVERSAL::isa($p, Catacomb::EC::Pt)) {
+ $p = $p->point();
+ } elsif (!UNIVERSAL::isa($p, Catacomb::EC::Point)) {
+ croak("not a curve point");
+ }
+ } else {
+ my ($x, $y);
+ ($pp, $x, $y) = @_;
+ $p = Catacomb::EC::Point->new($x, $y);
+ }
+ my (undef, $c, $cr, $f, $fr) = @$pp;
+ return _pt($c->in($p), $c, $cr, $f, $fr);
+}
+
+sub _binop {
+ my ($op, $x, $y, $swap) = @_;
+ my (undef, $c, $cr, $f, $fr) = @$x;
+ $y = _convert($c, $cr, $f, $fr, $y);
+ my $z = $swap ?
+ &$op($c, $x->[0], $y->[0]) :
+ &$op($c, $y->[0], $x->[0]);
+ return _pt($z, $c, $cr, $f, $fr);
+}
+
+sub _unop {
+ my ($op, $x) = @_;
+ my (undef, $c, $cr, $f, $fr) = @$x;
+ my $z = &$op($c, $x->[0]);
+ return _pt($z, $c, $cr, $f, $fr);
+}
+
+sub _eq {
+ my ($x, $y) = @_;
+ my (undef, $c, $cr, $f, $fr) = @$x;
+ $y = _convert($c, $cr, $f, $fr, $y);
+ return Catacomb::EC::Point::eq($c->out($x), $c->out($y));
+}
+
+sub mul {
+ croak("Usage: Catacomb::EC::Pt::mul(p, n)") unless @_ == 2;
+ my ($p, $x) = @_;
+ my ($pp, $c, $cr, $f, $fr) = @$p;
+ return _pt($c->imul($pp, $x), $c, $cr, $f, $fr);
+}
+
+use overload
+ '+' => sub { _binop(\&Catacomb::EC::Curve::iadd, @_); },
+ '-' => sub { _binop(\&Catacomb::EC::Curve::isub, @_); },
+ '*' => sub { mul($_[0], $_[1]); },
+ '==' => sub { _eq(@_); },
+ '!=' => sub { !_eq(@_); },
+ 'eq' => sub { _eq(@_); },
+ 'ne' => sub { !_eq(@_); },
+ '""' => sub { $_[0]->point()->tostring(); },
+ '0+' => sub { $_[0]->point()->x()->toint(); },
+ 'neg' => sub { _unop(\&Catacomb::EC::Curve::ineg, @_); };
+
+#----- That's all, folks ----------------------------------------------------