a portable and fast pairing-based cryptography library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
mcl/include/mcl/ec.hpp

755 lines
14 KiB

#pragma once
/**
@file
@brief elliptic curve
@author MITSUNARI Shigeo(@herumi)
@license modified new BSD license
http://opensource.org/licenses/BSD-3-Clause
*/
#include <sstream>
#include <stdlib.h>
#include <cybozu/exception.hpp>
#include <mcl/op.hpp>
#include <mcl/util.hpp>
//#define MCL_EC_USE_AFFINE
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4458)
#endif
namespace mcl {
namespace ec {
enum Mode {
Jacobi,
Proj
};
} // mcl::ecl
/*
elliptic curve
y^2 = x^3 + ax + b (affine)
y^2 = x^3 + az^4 + bz^6 (Jacobi) x = X/Z^2, y = Y/Z^3
*/
template<class _Fp>
class EcT {
enum {
zero,
minus3,
generic
};
public:
typedef _Fp Fp;
#ifdef MCL_EC_USE_AFFINE
Fp x, y;
bool inf_;
#else
mutable Fp x, y, z;
static int mode_;
#endif
static Fp a_;
static Fp b_;
static int specialA_;
static bool compressedExpression_;
#ifdef MCL_EC_USE_AFFINE
EcT() : inf_(true) {}
#else
EcT() { z.clear(); }
#endif
EcT(const Fp& _x, const Fp& _y)
{
set(_x, _y);
}
bool isNormalized() const
{
#ifdef MCL_EC_USE_AFFINE
return true;
#else
return isZero() || z.isOne();
#endif
}
#ifndef MCL_EC_USE_AFFINE
private:
void normalizeJacobi() const
{
assert(!z.isZero());
Fp rz2;
Fp::inv(z, z);
Fp::sqr(rz2, z);
x *= rz2;
y *= rz2;
y *= z;
z = 1;
}
void normalizeProj() const
{
assert(!z.isZero());
Fp::inv(z, z);
x *= z;
y *= z;
z = 1;
}
// Y^2 == X(X^2 + aZ^4) + bZ^6
bool isValidJacobi() const
{
Fp y2, x2, z2, z4, t;
Fp::sqr(x2, x);
Fp::sqr(y2, y);
Fp::sqr(z2, z);
Fp::sqr(z4, z2);
Fp::mul(t, z4, a_);
t += x2;
t *= x;
z4 *= z2;
z4 *= b_;
t += z4;
return y2 == t;
}
// (Y^2 - bZ^2)Z = X(X^2 + aZ^2)
bool isValidProj() const
{
Fp y2, x2, z2, t;
Fp::sqr(x2, x);
Fp::sqr(y2, y);
Fp::sqr(z2, z);
Fp::mul(t, a_, z2);
t += x2;
t *= x;
z2 *= b_;
y2 -= z2;
y2 *= z;
return y2 == t;
}
public:
#endif
void normalize() const
{
#ifndef MCL_EC_USE_AFFINE
if (isNormalized()) return;
switch (mode_) {
case ec::Jacobi:
normalizeJacobi();
break;
case ec::Proj:
normalizeProj();
break;
}
#endif
}
static inline void init(const Fp& a, const Fp& b, int mode = ec::Jacobi)
{
a_ = a;
b_ = b;
if (a_.isZero()) {
specialA_ = zero;
} else if (a_ == -3) {
specialA_ = minus3;
} else {
specialA_ = generic;
}
#ifdef MCL_EC_USE_AFFINE
cybozu::disable_warning_unused_variable(mode);
#else
switch (mode) {
case ec::Jacobi:
case ec::Proj:
mode_ = mode;
break;
default:
throw cybozu::Exception("ec:EcT:init:bad mode") << mode;
}
#endif
}
// backward compatilibity
static inline void setParam(const std::string& astr, const std::string& bstr, int mode = ec::Jacobi)
{
init(astr, bstr, mode);
}
static inline void init(const std::string& astr, const std::string& bstr, int mode = ec::Jacobi)
{
init(Fp(astr), Fp(bstr), mode);
}
// y^2 == (x^2 + a)x + b
static inline bool isValid(const Fp& _x, const Fp& _y)
{
Fp y2, t;
Fp::sqr(y2, _y);
Fp::sqr(t, _x);
t += a_;
t *= _x;
t += b_;
return y2 == t;
}
bool isValid() const
{
if (isZero()) return true;
#ifndef MCL_EC_USE_AFFINE
if (!z.isOne()) {
switch (mode_) {
case ec::Jacobi:
return isValidJacobi();
case ec::Proj:
return isValidProj();
}
}
#endif
return isValid(x, y);
}
void set(const Fp& _x, const Fp& _y, bool verify = true)
{
if (verify && !isValid(_x, _y)) throw cybozu::Exception("ec:EcT:set") << _x << _y;
x = _x; y = _y;
#ifdef MCL_EC_USE_AFFINE
inf_ = false;
#else
z = 1;
#endif
}
void clear()
{
#ifdef MCL_EC_USE_AFFINE
inf_ = true;
#else
z.clear();
#endif
x.clear();
y.clear();
}
#ifndef MCL_EC_USE_AFFINE
static inline void dblNoVerifyInfJacobi(EcT& R, const EcT& P)
{
Fp S, M, t, y2;
Fp::sqr(y2, P.y);
Fp::mul(S, P.x, y2);
const bool isPzOne = P.z.isOne();
S += S;
S += S;
Fp::sqr(M, P.x);
switch (specialA_) {
case zero:
Fp::add(t, M, M);
M += t;
break;
case minus3:
if (isPzOne) {
M -= P.z;
} else {
Fp::sqr(t, P.z);
Fp::sqr(t, t);
M -= t;
}
Fp::add(t, M, M);
M += t;
break;
case generic:
default:
if (isPzOne) {
t = a_;
} else {
Fp::sqr(t, P.z);
Fp::sqr(t, t);
t *= a_;
}
t += M;
M += M;
M += t;
break;
}
Fp::sqr(R.x, M);
R.x -= S;
R.x -= S;
if (isPzOne) {
R.z = P.y;
} else {
Fp::mul(R.z, P.y, P.z);
}
R.z += R.z;
Fp::sqr(y2, y2);
y2 += y2;
y2 += y2;
y2 += y2;
Fp::sub(R.y, S, R.x);
R.y *= M;
R.y -= y2;
}
static inline void dblNoVerifyInfProj(EcT& R, const EcT& P)
{
const bool isPzOne = P.z.isOne();
Fp w, t, h;
switch (specialA_) {
case zero:
Fp::sqr(w, P.x);
Fp::add(t, w, w);
w += t;
break;
case minus3:
Fp::sqr(w, P.x);
if (isPzOne) {
w -= P.z;
} else {
Fp::sqr(t, P.z);
w -= t;
}
Fp::add(t, w, w);
w += t;
break;
case generic:
default:
if (isPzOne) {
w = a_;
} else {
Fp::sqr(w, P.z);
w *= a_;
}
Fp::sqr(t, P.x);
w += t;
w += t;
w += t; // w = a z^2 + 3x^2
break;
}
if (isPzOne) {
R.z = P.y;
} else {
Fp::mul(R.z, P.y, P.z); // s = yz
}
Fp::mul(t, R.z, P.x);
t *= P.y; // xys
t += t;
t += t; // 4(xys) ; 4B
Fp::sqr(h, w);
h -= t;
h -= t; // w^2 - 8B
Fp::mul(R.x, h, R.z);
t -= h; // h is free
t *= w;
Fp::sqr(w, P.y);
R.x += R.x;
R.z += R.z;
Fp::sqr(h, R.z);
w *= h;
R.z *= h;
Fp::sub(R.y, t, w);
R.y -= w;
}
#endif
static inline void dblNoVerifyInf(EcT& R, const EcT& P)
{
#ifdef MCL_EC_USE_AFFINE
Fp t, s;
Fp::sqr(t, P.x);
Fp::add(s, t, t);
t += s;
t += a_;
Fp::add(s, P.y, P.y);
t /= s;
Fp::sqr(s, t);
s -= P.x;
Fp x3;
Fp::sub(x3, s, P.x);
Fp::sub(s, P.x, x3);
s *= t;
Fp::sub(R.y, s, P.y);
R.x = x3;
R.inf_ = false;
#else
switch (mode_) {
case ec::Jacobi:
dblNoVerifyInfJacobi(R, P);
break;
case ec::Proj:
dblNoVerifyInfProj(R, P);
break;
}
#endif
}
static inline void dbl(EcT& R, const EcT& P)
{
if (P.isZero()) {
R.clear();
return;
}
dblNoVerifyInf(R, P);
}
#ifndef MCL_EC_USE_AFFINE
static inline void addJacobi(EcT& R, const EcT& P, const EcT& Q)
{
const bool isQzOne = Q.z.isOne();
Fp r, U1, S1, H, H3;
Fp::sqr(r, P.z);
if (isQzOne) {
U1 = P.x;
Fp::mul(H, Q.x, r);
H -= U1;
r *= P.z;
S1 = P.y;
} else {
Fp::sqr(S1, Q.z);
Fp::mul(U1, P.x, S1);
Fp::mul(H, Q.x, r);
H -= U1;
r *= P.z;
S1 *= Q.z;
S1 *= P.y;
}
r *= Q.y;
r -= S1;
if (H.isZero()) {
if (r.isZero()) {
dblNoVerifyInf(R, P);
} else {
R.clear();
}
return;
}
if (isQzOne) {
Fp::mul(R.z, P.z, H);
} else {
Fp::mul(R.z, P.z, Q.z);
R.z *= H;
}
Fp::sqr(H3, H); // H^2
Fp::sqr(R.y, r); // r^2
U1 *= H3; // U1 H^2
H3 *= H; // H^3
R.y -= U1;
R.y -= U1;
Fp::sub(R.x, R.y, H3);
U1 -= R.x;
U1 *= r;
H3 *= S1;
Fp::sub(R.y, U1, H3);
}
static inline void addProj(EcT& R, const EcT& P, const EcT& Q)
{
const bool isQzOne = Q.z.isOne();
Fp r, PyQz, v, A, vv;
if (isQzOne) {
r = P.x;
PyQz = P.y;
} else {
Fp::mul(r, P.x, Q.z);
Fp::mul(PyQz, P.y, Q.z);
}
Fp::mul(A, Q.y, P.z);
Fp::mul(v, Q.x, P.z);
v -= r;
if (v.isZero()) {
if (A == PyQz) {
dblNoVerifyInf(R, P);
} else {
R.clear();
}
return;
}
Fp::sub(R.y, A, PyQz);
Fp::sqr(A, R.y);
Fp::sqr(vv, v);
r *= vv;
vv *= v;
if (isQzOne) {
R.z = P.z;
} else {
Fp::mul(R.z, P.z, Q.z);
}
A *= R.z;
R.z *= vv;
A -= vv;
vv *= PyQz;
A -= r;
A -= r;
Fp::mul(R.x, v, A);
r -= A;
R.y *= r;
R.y -= vv;
}
#endif
static inline void add(EcT& R, const EcT& _P, const EcT& _Q)
{
if (_P.isZero()) { R = _Q; return; }
if (_Q.isZero()) { R = _P; return; }
if (&_P == &_Q) {
dblNoVerifyInf(R, _P);
return;
}
#ifdef MCL_EC_USE_AFFINE
const EcT& P(_P);
const EcT& Q(_Q);
Fp t;
Fp::neg(t, Q.y);
if (P.y == t) { R.clear(); return; }
Fp::sub(t, Q.x, P.x);
if (t.isZero()) {
dblNoVerifyInf(R, P);
return;
}
Fp s;
Fp::sub(s, Q.y, P.y);
Fp::div(t, s, t);
R.inf_ = false;
Fp x3;
Fp::sqr(x3, t);
x3 -= P.x;
x3 -= Q.x;
Fp::sub(s, P.x, x3);
s *= t;
Fp::sub(R.y, s, P.y);
R.x = x3;
#else
const EcT *pP = &_P;
const EcT *pQ = &_Q;
if (pP->z.isOne()) {
std::swap(pP, pQ);
}
const EcT& P(*pP);
const EcT& Q(*pQ);
switch (mode_) {
case ec::Jacobi:
addJacobi(R, P, Q);
break;
case ec::Proj:
addProj(R, P, Q);
break;
}
#endif
}
static inline void sub(EcT& R, const EcT& P, const EcT& Q)
{
EcT nQ;
neg(nQ, Q);
add(R, P, nQ);
}
static inline void neg(EcT& R, const EcT& P)
{
if (P.isZero()) {
R.clear();
return;
}
R.x = P.x;
Fp::neg(R.y, P.y);
#ifdef MCL_EC_USE_AFFINE
R.inf_ = false;
#else
R.z = P.z;
#endif
}
template<class tag, size_t maxBitSize, template<class _tag, size_t _maxBitSize>class FpT>
static inline void mul(EcT& z, const EcT& x, const FpT<tag, maxBitSize>& y)
{
fp::Block b;
y.getBlock(b);
mulArray(z, x, b.p, b.n, false);
}
static inline void mul(EcT& z, const EcT& x, int y)
{
const fp::Unit u = abs(y);
mulArray(z, x, &u, 1, y < 0);
}
static inline void mul(EcT& z, const EcT& x, const mpz_class& y)
{
mulArray(z, x, gmp::getUnit(y), abs(y.get_mpz_t()->_mp_size), y < 0);
}
/*
0 <= P for any P
(Px, Py) <= (P'x, P'y) iff Px < P'x or Px == P'x and Py <= P'y
*/
template<class F>
static inline int compareFunc(const EcT& P, const EcT& Q, F comp)
{
P.normalize();
Q.normalize();
const bool QisZero = Q.isZero();
if (P.isZero()) {
if (QisZero) return 0;
return -1;
}
if (QisZero) return 1;
int c = comp(P.x, Q.x);
if (c > 0) return 1;
if (c < 0) return -1;
return comp(P.y, Q.y);
}
static inline int compare(const EcT& P, const EcT& Q)
{
return compareFunc(P, Q, Fp::compare);
}
static inline int compareRaw(const EcT& P, const EcT& Q)
{
return compareFunc(P, Q, Fp::compareRaw);
}
bool isZero() const
{
#ifdef MCL_EC_USE_AFFINE
return inf_;
#else
return z.isZero();
#endif
}
/*
"0" ; infinity
"1 <x> <y is odd ? 1 : 0>" ; compressed
"2 <x> <y>" ; not compressed
*/
void getStr(std::string& str, int base = 10, bool withPrefix = false) const
{
if (isZero()) {
str = '0';
return;
}
normalize();
if (compressedExpression_) {
str = "1 ";
str += x.getStr(base, withPrefix);
const char *p = y.isOdd() ? " 1" : " 0";
str += p;
} else {
str = "2 ";
str += x.getStr(base, withPrefix);
str += ' ';
str += y.getStr(base, withPrefix);
}
}
std::string getStr(int base = 10, bool withPrefix = false) const
{
std::string str;
getStr(str, base, withPrefix);
return str;
}
friend inline std::ostream& operator<<(std::ostream& os, const EcT& self)
{
const std::ios_base::fmtflags f = os.flags();
if (f & std::ios_base::oct) throw cybozu::Exception("fpT:operator<<:oct is not supported");
const int base = (f & std::ios_base::hex) ? 16 : 10;
const bool withPrefix = (f & std::ios_base::showbase) != 0;
return os << self.getStr(base, withPrefix);
}
friend inline std::istream& operator>>(std::istream& is, EcT& self)
{
std::string str;
is >> str;
if (str == "0") {
self.clear();
return is;
}
#ifdef MCL_EC_USE_AFFINE
self.inf_ = false;
#else
self.z = 1;
#endif
is >> self.x;
if (str == "1") {
is >> str;
if (str == "0") {
getYfromX(self.y, self.x, false);
} else if (str == "1") {
getYfromX(self.y, self.x, true);
} else {
throw cybozu::Exception("EcT:operator>>:bad y") << str;
}
} else if (str == "2") {
is >> self.y;
if (!isValid(self.x, self.y)) {
throw cybozu::Exception("EcT:setStr:bad value") << self.x << self.y;
}
} else {
throw cybozu::Exception("EcT:operator>>:bad format") << str;
}
return is;
}
void setStr(const std::string& str)
{
std::istringstream is(str);
if (!(is >> *this)) {
throw cybozu::Exception("EcT:setStr:bad str") << str;
}
}
static inline void setCompressedExpression(bool compressedExpression = true)
{
compressedExpression_ = compressedExpression;
}
static inline void getYfromX(Fp& y, const Fp& x, bool isYodd)
{
Fp t;
Fp::sqr(t, x);
t += a_;
t *= x;
t += b_;
Fp::squareRoot(y, t);
if (y.isOdd() ^ isYodd) {
Fp::neg(y, y);
}
}
inline friend EcT operator+(const EcT& x, const EcT& y) { EcT z; add(z, x, y); return z; }
inline friend EcT operator-(const EcT& x, const EcT& y) { EcT z; sub(z, x, y); return z; }
EcT& operator+=(const EcT& x) { add(*this, *this, x); return *this; }
EcT& operator-=(const EcT& x) { sub(*this, *this, x); return *this; }
EcT operator-() const { EcT x; neg(x, *this); return x; }
bool operator==(const EcT& rhs) const
{
EcT R;
sub(R, *this, rhs); // QQQ : optimized later
return R.isZero();
}
bool operator!=(const EcT& rhs) const { return !operator==(rhs); }
private:
static inline void mulArray(EcT& z, const EcT& x, const fp::Unit *y, size_t yn, bool isNegative)
{
x.normalize();
EcT tmp;
const EcT *px = &x;
if (&z == &x) {
tmp = x;
px = &tmp;
}
z.clear();
fp::powGeneric(z, *px, y, yn, EcT::add, EcT::dbl);
if (isNegative) {
neg(z, z);
}
}
};
template<class Fp> Fp EcT<Fp>::a_;
template<class Fp> Fp EcT<Fp>::b_;
template<class Fp> int EcT<Fp>::specialA_;
template<class Fp> bool EcT<Fp>::compressedExpression_;
#ifndef MCL_EC_USE_AFFINE
template<class Fp> int EcT<Fp>::mode_;
#endif
struct EcParam {
const char *name;
const char *p;
const char *a;
const char *b;
const char *gx;
const char *gy;
const char *n;
size_t bitSize; // bit length of p
};
} // mcl
namespace std { CYBOZU_NAMESPACE_TR1_BEGIN
template<class T> struct hash;
template<class Fp>
struct hash<mcl::EcT<Fp> > {
size_t operator()(const mcl::EcT<Fp>& P) const
{
if (P.isZero()) return 0;
P.normalize();
uint64_t v = hash<Fp>()(P.x);
v = hash<Fp>()(P.y, v);
return static_cast<size_t>(v);
}
};
CYBOZU_NAMESPACE_TR1_END } // std
#ifdef _MSC_VER
#pragma warning(pop)
#endif