v0.96 improved scalar multiplication

update-fork
MITSUNARI Shigeo 5 years ago
commit 96e6d628ec
  1. 259
      include/mcl/bn.hpp
  2. 143
      include/mcl/ec.hpp
  3. 2
      include/mcl/ecdsa.hpp
  4. 2
      include/mcl/gmp_util.hpp
  5. 2
      include/mcl/op.hpp
  6. 5
      include/mcl/she.hpp
  7. 1
      readme.md
  8. 2
      test/ecdsa_test.cpp
  9. 20
      test/glv_test.cpp
  10. 5
      test/she_test.cpp

@ -561,30 +561,14 @@ struct MapTo {
};
typedef mcl::FixedArray<int8_t, MCL_MAX_FR_BIT_SIZE / 2 + 2> NafArray;
template<class G>
void addTbl(G& Q, const G *tbl, const NafArray& naf, size_t i)
{
if (i >= naf.size()) return;
int n = naf[i];
if (n > 0) {
Q += tbl[(n - 1) >> 1];
} else if (n < 0) {
Q -= tbl[(-n - 1) >> 1];
}
}
/*
Software implementation of Attribute-Based Encryption: Appendixes
GLV for G1 on BN/BLS12
*/
struct GLV1 {
Fp rw; // rw = 1 / w = (-1 - sqrt(-3)) / 2
size_t rBitSize;
mpz_class v0, v1;
mpz_class B[2][2];
mpz_class r;
private:
bool usePrecomputedTable(int curveType)
struct GLV1 : mcl::GLV1T<G1> {
static bool usePrecomputedTable(int curveType)
{
if (curveType < 0) return false;
const struct Tbl {
@ -619,48 +603,25 @@ private:
bool b;
rw.setStr(&b, tbl[i].rw, 16); if (!b) continue;
rBitSize = tbl[i].rBitSize;
mcl::gmp::setStr(&b, v0, tbl[i].v0, 16); if (!b) continue;
mcl::gmp::setStr(&b, v1, tbl[i].v1, 16); if (!b) continue;
mcl::gmp::setStr(&b, B[0][0], tbl[i].B[0][0], 16); if (!b) continue;
mcl::gmp::setStr(&b, B[0][1], tbl[i].B[0][1], 16); if (!b) continue;
mcl::gmp::setStr(&b, B[1][0], tbl[i].B[1][0], 16); if (!b) continue;
mcl::gmp::setStr(&b, B[1][1], tbl[i].B[1][1], 16); if (!b) continue;
mcl::gmp::setStr(&b, r, tbl[i].r, 16); if (!b) continue;
gmp::setStr(&b, v0, tbl[i].v0, 16); if (!b) continue;
gmp::setStr(&b, v1, tbl[i].v1, 16); if (!b) continue;
gmp::setStr(&b, B[0][0], tbl[i].B[0][0], 16); if (!b) continue;
gmp::setStr(&b, B[0][1], tbl[i].B[0][1], 16); if (!b) continue;
gmp::setStr(&b, B[1][0], tbl[i].B[1][0], 16); if (!b) continue;
gmp::setStr(&b, B[1][1], tbl[i].B[1][1], 16); if (!b) continue;
gmp::setStr(&b, r, tbl[i].r, 16); if (!b) continue;
return true;
}
return false;
}
public:
bool operator==(const GLV1& rhs) const
{
return rw == rhs.rw && rBitSize == rhs.rBitSize && v0 == rhs.v0 && v1 == rhs.v1
&& B[0][0] == rhs.B[0][0] && B[0][1] == rhs.B[0][1] && B[1][0] == rhs.B[1][0]
&& B[1][1] == rhs.B[1][1] && r == rhs.r;
}
bool operator!=(const GLV1& rhs) const { return !operator==(rhs); }
#ifndef CYBOZU_DONT_USE_STRING
void dump(const mpz_class& x) const
{
printf("\"%s\",\n", mcl::gmp::getStr(x, 16).c_str());
}
void dump() const
{
printf("\"%s\",\n", rw.getStr(16).c_str());
printf("%d,\n", (int)rBitSize);
dump(v0);
dump(v1);
dump(B[0][0]); dump(B[0][1]); dump(B[1][0]); dump(B[1][1]);
dump(r);
}
#endif
void init(const mpz_class& r, const mpz_class& z, bool isBLS12 = false, int curveType = -1)
static void initForBN(const mpz_class& _r, const mpz_class& z, bool isBLS12 = false, int curveType = -1)
{
if (usePrecomputedTable(curveType)) return;
bool b = Fp::squareRoot(rw, -3);
assert(b);
(void)b;
rw = -(rw + 1) / 2;
this->r = r;
r = _r;
rBitSize = gmp::getBitSize(r);
rBitSize = (rBitSize + fp::UnitBitSize - 1) & ~(fp::UnitBitSize - 1);// a little better size
if (isBLS12) {
@ -690,68 +651,6 @@ public:
v0 = ((-B[1][1]) << rBitSize) / r;
v1 = ((B[1][0]) << rBitSize) / r;
}
/*
L = lambda = p^4
L (x, y) = (rw x, y)
*/
void mulLambda(G1& Q, const G1& P) const
{
Fp::mul(Q.x, P.x, rw);
Q.y = P.y;
Q.z = P.z;
}
/*
x = a + b * lambda mod r
*/
void split(mpz_class& a, mpz_class& b, const mpz_class& x) const
{
mpz_class t;
t = (x * v0) >> rBitSize;
b = (x * v1) >> rBitSize;
a = x - (t * B[0][0] + b * B[1][0]);
b = - (t * B[0][1] + b * B[1][1]);
}
void mul(G1& Q, const G1& P, mpz_class x, bool constTime = false) const
{
const int w = 5;
const size_t tblSize = 1 << (w - 2);
NafArray naf[2];
mpz_class u[2];
G1 tbl[2][tblSize];
bool b;
x %= r;
if (x == 0) {
Q.clear();
if (!constTime) return;
}
if (x < 0) {
x += r;
}
split(u[0], u[1], x);
gmp::getNAFwidth(&b, naf[0], u[0], w);
assert(b); (void)b;
gmp::getNAFwidth(&b, naf[1], u[1], w);
assert(b); (void)b;
tbl[0][0] = P;
mulLambda(tbl[1][0], tbl[0][0]);
{
G1 P2;
G1::dbl(P2, P);
for (size_t i = 1; i < tblSize; i++) {
G1::add(tbl[0][i], tbl[0][i - 1], P2);
mulLambda(tbl[1][i], tbl[0][i]);
}
}
const size_t maxBit = fp::max_(naf[0].size(), naf[1].size());
Q.clear();
for (size_t i = 0; i < maxBit; i++) {
G1::dbl(Q, Q);
addTbl(Q, tbl[0], naf[0], maxBit - 1 - i);
addTbl(Q, tbl[1], naf[1], maxBit - 1 - i);
}
}
};
/*
@ -772,7 +671,7 @@ struct GLV2 {
this->z = z;
this->abs_z = z < 0 ? -z : z;
this->isBLS12 = isBLS12;
rBitSize = mcl::gmp::getBitSize(r);
rBitSize = gmp::getBitSize(r);
rBitSize = (rBitSize + mcl::fp::UnitBitSize - 1) & ~(mcl::fp::UnitBitSize - 1);// a little better size
mpz_class z2p1 = z * 2 + 1;
B[0][0] = z + 1;
@ -859,7 +758,6 @@ struct GLV2 {
template<class T>
void mul(T& Q, const T& P, mpz_class x, bool constTime = false) const
{
#if 1
const int w = 5;
const size_t tblSize = 1 << (w - 2);
const size_t splitN = 4;
@ -902,120 +800,11 @@ struct GLV2 {
Q.clear();
for (size_t i = 0; i < maxBit; i++) {
T::dbl(Q, Q);
addTbl(Q, tbl[0], naf[0], maxBit - 1 - i);
addTbl(Q, tbl[1], naf[1], maxBit - 1 - i);
addTbl(Q, tbl[2], naf[2], maxBit - 1 - i);
addTbl(Q, tbl[3], naf[3], maxBit - 1 - i);
}
#else
#if 0 // #ifndef NDEBUG
{
T R;
T::mulGeneric(R, P, r);
assert(R.isZero());
}
#endif
typedef mcl::fp::Unit Unit;
const size_t maxUnit = 512 / 2 / mcl::fp::UnitBitSize;
const int splitN = 4;
mpz_class u[splitN];
T in[splitN];
T tbl[16];
int bitTbl[splitN]; // bit size of u[i]
Unit w[splitN][maxUnit]; // unit array of u[i]
int maxBit = 0; // max bit of u[i]
int maxN = 0;
int remainBit = 0;
x %= r;
if (x == 0) {
Q.clear();
if (constTime) goto DummyLoop;
return;
}
if (x < 0) {
x += r;
}
split(u, x);
in[0] = P;
Frobenius(in[1], in[0]);
Frobenius(in[2], in[1]);
Frobenius(in[3], in[2]);
for (int i = 0; i < splitN; i++) {
if (u[i] < 0) {
u[i] = -u[i];
T::neg(in[i], in[i]);
}
// in[i].normalize(); // slow
}
#if 0
for (int i = 0; i < splitN; i++) {
T::mulGeneric(in[i], in[i], u[i]);
}
T::add(Q, in[0], in[1]);
Q += in[2];
Q += in[3];
return;
#else
tbl[0] = in[0];
for (size_t i = 1; i < 16; i++) {
tbl[i].clear();
if (i & 1) {
tbl[i] += in[0];
}
if (i & 2) {
tbl[i] += in[1];
}
if (i & 4) {
tbl[i] += in[2];
}
if (i & 8) {
tbl[i] += in[3];
}
// tbl[i].normalize();
}
for (int i = 0; i < splitN; i++) {
bool b;
mcl::gmp::getArray(&b, w[i], maxUnit, u[i]);
assert(b);
bitTbl[i] = (int)mcl::gmp::getBitSize(u[i]);
maxBit = fp::max_(maxBit, bitTbl[i]);
}
maxBit--;
/*
maxBit = maxN * UnitBitSize + remainBit
0 < remainBit <= UnitBitSize
*/
maxN = maxBit / mcl::fp::UnitBitSize;
remainBit = maxBit % mcl::fp::UnitBitSize;
remainBit++;
Q.clear();
for (int i = maxN; i >= 0; i--) {
for (int j = remainBit - 1; j >= 0; j--) {
T::dbl(Q, Q);
uint32_t b0 = (w[0][i] >> j) & 1;
uint32_t b1 = (w[1][i] >> j) & 1;
uint32_t b2 = (w[2][i] >> j) & 1;
uint32_t b3 = (w[3][i] >> j) & 1;
uint32_t c = b3 * 8 + b2 * 4 + b1 * 2 + b0;
if (c == 0) {
if (constTime) tbl[0] += tbl[1];
} else {
Q += tbl[c];
}
mcl::local::addTbl(Q, tbl[0], naf[0], maxBit - 1 - i);
mcl::local::addTbl(Q, tbl[1], naf[1], maxBit - 1 - i);
mcl::local::addTbl(Q, tbl[2], naf[2], maxBit - 1 - i);
mcl::local::addTbl(Q, tbl[3], naf[3], maxBit - 1 - i);
}
remainBit = (int)mcl::fp::UnitBitSize;
}
#endif
DummyLoop:
if (!constTime) return;
const int limitBit = (int)rBitSize / splitN;
T D = tbl[0];
for (int i = maxBit + 1; i < limitBit; i++) {
T::dbl(D, D);
D += tbl[0];
}
#endif
}
void pow(Fp12& z, const Fp12& x, mpz_class y, bool constTime = false) const
{
@ -1035,7 +824,6 @@ struct Param {
mpz_class p;
mpz_class r;
local::MapTo mapTo;
local::GLV1 glv1;
local::GLV2 glv2;
// for G2 Frobenius
Fp2 g2;
@ -1151,7 +939,7 @@ struct Param {
} else {
mapTo.init(2 * p - r, z, cp.curveType);
}
glv1.init(r, z, isBLS12, cp.curveType);
GLV1::initForBN(r, z, isBLS12, cp.curveType);
glv2.init(r, z, isBLS12);
basePoint.clear();
*pb = true;
@ -1200,15 +988,6 @@ static const local::Param& param = local::StaticVar<>::param;
namespace local {
inline void mulArrayGLV1(G1& z, const G1& x, const mcl::fp::Unit *y, size_t yn, bool isNegative, bool constTime)
{
mpz_class s;
bool b;
mcl::gmp::setArray(&b, s, y, yn);
assert(b);
if (isNegative) s = -s;
BN::param.glv1.mul(z, x, s, constTime);
}
inline void mulArrayGLV2(G2& z, const G2& x, const mcl::fp::Unit *y, size_t yn, bool isNegative, bool constTime)
{
mpz_class s;
@ -2227,7 +2006,7 @@ inline void init(bool *pb, const mcl::CurveParam& cp = mcl::BN254, fp::Mode mode
{
local::StaticVar<>::param.init(pb, cp, mode);
if (!*pb) return;
G1::setMulArrayGLV(local::mulArrayGLV1);
G1::setMulArrayGLV(local::GLV1::mulArray);
G2::setMulArrayGLV(local::mulArrayGLV2);
Fp12::setPowArrayGLV(local::powArrayGLV2);
G1::setCompressedExpression();

@ -1068,6 +1068,149 @@ template<class Fp> void (*EcT<Fp>::mulArrayGLV)(EcT& z, const EcT& x, const fp::
template<class Fp> int EcT<Fp>::mode_;
#endif
namespace local {
template<class Ec, class Vec>
void addTbl(Ec& Q, const Ec *tbl, const Vec& naf, size_t i)
{
if (i >= naf.size()) return;
int n = naf[i];
if (n > 0) {
Q += tbl[(n - 1) >> 1];
} else if (n < 0) {
Q -= tbl[(-n - 1) >> 1];
}
}
} // mcl::local
template<class Ec>
struct GLV1T {
typedef typename Ec::Fp Fp;
static Fp rw; // rw = 1 / w = (-1 - sqrt(-3)) / 2
static size_t rBitSize;
static mpz_class v0, v1;
static mpz_class B[2][2];
static mpz_class r;
public:
#ifndef CYBOZU_DONT_USE_STRING
static void dump(const mpz_class& x)
{
printf("\"%s\",\n", mcl::gmp::getStr(x, 16).c_str());
}
static void dump()
{
printf("\"%s\",\n", rw.getStr(16).c_str());
printf("%d,\n", (int)rBitSize);
dump(v0);
dump(v1);
dump(B[0][0]); dump(B[0][1]); dump(B[1][0]); dump(B[1][1]);
dump(r);
}
#endif
/*
L (x, y) = (rw x, y)
*/
static void mulLambda(Ec& Q, const Ec& P)
{
Fp::mul(Q.x, P.x, rw);
Q.y = P.y;
Q.z = P.z;
}
/*
x = a + b * lambda mod r
*/
static void split(mpz_class& a, mpz_class& b, const mpz_class& x)
{
mpz_class t;
t = (x * v0) >> rBitSize;
b = (x * v1) >> rBitSize;
a = x - (t * B[0][0] + b * B[1][0]);
b = - (t * B[0][1] + b * B[1][1]);
}
static void mul(Ec& Q, const Ec& P, mpz_class x, bool constTime = false)
{
const int w = 5;
const size_t tblSize = 1 << (w - 2);
typedef mcl::FixedArray<int8_t, sizeof(Fp) * 8 / 2 + 2> NafArray;
NafArray naf[2];
mpz_class u[2];
Ec tbl[2][tblSize];
bool b;
x %= r;
if (x == 0) {
Q.clear();
if (!constTime) return;
}
if (x < 0) {
x += r;
}
split(u[0], u[1], x);
gmp::getNAFwidth(&b, naf[0], u[0], w);
assert(b); (void)b;
gmp::getNAFwidth(&b, naf[1], u[1], w);
assert(b); (void)b;
tbl[0][0] = P;
mulLambda(tbl[1][0], tbl[0][0]);
{
Ec P2;
Ec::dbl(P2, P);
for (size_t i = 1; i < tblSize; i++) {
Ec::add(tbl[0][i], tbl[0][i - 1], P2);
mulLambda(tbl[1][i], tbl[0][i]);
}
}
const size_t maxBit = fp::max_(naf[0].size(), naf[1].size());
Q.clear();
for (size_t i = 0; i < maxBit; i++) {
Ec::dbl(Q, Q);
local::addTbl(Q, tbl[0], naf[0], maxBit - 1 - i);
local::addTbl(Q, tbl[1], naf[1], maxBit - 1 - i);
}
}
static void mulArray(Ec& z, const Ec& x, const mcl::fp::Unit *y, size_t yn, bool isNegative, bool constTime)
{
mpz_class s;
bool b;
mcl::gmp::setArray(&b, s, y, yn);
assert(b);
if (isNegative) s = -s;
mul(z, x, s, constTime);
}
/*
initForBN() is defined in bn.hpp
*/
static void initForSecp256k1(const mpz_class& _r)
{
bool b = Fp::squareRoot(rw, -3);
assert(b);
(void)b;
rw = -(rw + 1) / 2;
r = _r;
rBitSize = gmp::getBitSize(r);
rBitSize = (rBitSize + fp::UnitBitSize - 1) & ~(fp::UnitBitSize - 1);
gmp::setStr(&b, B[0][0], "0x3086d221a7d46bcde86c90e49284eb15");
assert(b); (void)b;
gmp::setStr(&b, B[0][1], "-0xe4437ed6010e88286f547fa90abfe4c3");
assert(b); (void)b;
gmp::setStr(&b, B[1][0], "0x114ca50f7a8e2f3f657c1108d9d44cfd8");
assert(b); (void)b;
B[1][1] = B[0][0];
v0 = ((B[1][1]) << rBitSize) / r;
v1 = ((-B[0][1]) << rBitSize) / r;
}
};
// rw = 1 / w = (-1 - sqrt(-3)) / 2
template<class Ec> typename Ec::Fp GLV1T<Ec>::rw;
template<class Ec> size_t GLV1T<Ec>::rBitSize;
template<class Ec> mpz_class GLV1T<Ec>::v0;
template<class Ec> mpz_class GLV1T<Ec>::v1;
template<class Ec> mpz_class GLV1T<Ec>::B[2][2];
template<class Ec> mpz_class GLV1T<Ec>::r;
struct EcParam {
const char *name;
const char *p;

@ -99,6 +99,8 @@ inline void init(bool *pb)
p.P.set(pb, x, y);
if (!*pb) return;
p.Pbase.init(pb, p.P, ecParam.bitSize, local::winSize);
mcl::GLV1T<Ec>::initForSecp256k1(Zn::getOp().mp);
Ec::setMulArrayGLV(mcl::GLV1T<Ec>::mulArray);
}
#ifndef CYBOZU_DONT_USE_EXCEPTION

@ -642,7 +642,7 @@ void getNAFwidth(bool *pb, Vec& naf, mpz_class x, size_t w)
x++;
v -= maxW;
}
naf.push(pb, Vec::value_type(v));
naf.push(pb, typename Vec::value_type(v));
if (!*pb) return;
zeroNum = w - 1;
}

@ -23,7 +23,7 @@
namespace mcl {
static const int version = 0x095; /* 0xABC = A.BC */
static const int version = 0x096; /* 0xABC = A.BC */
/*
specifies available string format mode for X::setIoMode()

@ -26,6 +26,7 @@
#include <mcl/window_method.hpp>
#include <cybozu/endian.hpp>
#include <cybozu/serializer.hpp>
#include <mcl/ecparam.hpp>
namespace mcl { namespace she {
@ -588,6 +589,10 @@ public:
useDecG2ViaGT_ = false;
isG1only_ = true;
setTryNum(tryNum);
if (std::string(para.name) == mcl::ecparam::secp256k1.name) {
mcl::GLV1T<G1>::initForSecp256k1(Fr::getOp().mp);
G1::setMulArrayGLV(mcl::GLV1T<G1>::mulArray);
}
}
/*
set range for G1-DLP

@ -10,6 +10,7 @@ mcl is a library for pairing-based cryptography.
The current version supports the optimal Ate pairing over BN curves and BLS12-381 curves.
# News
* v0.96 improved scalar multiplication
* mclBn_setETHserialization(true) (de)serialize acoording to [ETH2.0 serialization of BLS12-381](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/bls_signature.md#point-representations) when BLS12-381 is used.
* (Break backward compatibility) libmcl_dy.a is renamed to libmcl.a
* The option SHARE_BASENAME_SUF is removed

@ -1,4 +1,4 @@
#define PUT(x) std::cout << #x "=" << x << std::endl;
#define PUT(x) std::cout << #x "=" << (x) << std::endl;
#include <stdlib.h>
#include <stdio.h>
void put(const void *buf, size_t bufSize)

@ -77,7 +77,7 @@ struct oldGLV {
};
template<class GLV1, class GLV2>
void compareLength(const GLV1& rhs, const GLV2& lhs)
void compareLength(const GLV2& lhs)
{
cybozu::XorShift rg;
int lt = 0;
@ -88,7 +88,7 @@ void compareLength(const GLV1& rhs, const GLV2& lhs)
for (int i = 1; i < 1000; i++) {
r.setRand(rg);
x = r.getMpz();
rhs.split(R0, R1, x);
mcl::bn::local::GLV1::split(R0, R1, x);
lhs.split(L0, L1, x);
size_t R0n = mcl::gmp::getBitSize(R0);
@ -121,10 +121,10 @@ void testGLV1()
oldGlv.init(BN::param.r, BN::param.z);
}
mcl::bn::local::GLV1 glv;
glv.init(BN::param.r, BN::param.z, BN::param.isBLS12);
typedef mcl::bn::local::GLV1 GLV1;
GLV1::initForBN(BN::param.r, BN::param.z, BN::param.isBLS12);
if (!BN::param.isBLS12) {
compareLength(glv, oldGlv);
compareLength<GLV1>(oldGlv);
}
for (int i = 1; i < 100; i++) {
@ -133,9 +133,9 @@ void testGLV1()
s.setRand(rg);
mpz_class ss = s.getMpz();
G1::mulGeneric(P1, P0, ss);
glv.mul(P2, P0, ss);
GLV1::mul(P2, P0, ss);
CYBOZU_TEST_EQUAL(P1, P2);
glv.mul(P2, P0, ss, true);
GLV1::mul(P2, P0, ss, true);
CYBOZU_TEST_EQUAL(P1, P2);
if (!BN::param.isBLS12) {
oldGlv.mul(P2, P0, ss);
@ -145,15 +145,15 @@ void testGLV1()
for (int i = -100; i < 100; i++) {
mpz_class ss = i;
G1::mulGeneric(P1, P0, ss);
glv.mul(P2, P0, ss);
GLV1::mul(P2, P0, ss);
CYBOZU_TEST_EQUAL(P1, P2);
glv.mul(P2, P0, ss, true);
GLV1::mul(P2, P0, ss, true);
CYBOZU_TEST_EQUAL(P1, P2);
}
Fr s;
mapToG1(P0, 123);
CYBOZU_BENCH_C("Ec::mul", 100, P1 = P0; s.setRand(rg); G1::mulGeneric, P2, P1, s.getMpz());
CYBOZU_BENCH_C("Ec::glv", 100, P1 = P0; s.setRand(rg); glv.mul, P2, P1, s.getMpz());
CYBOZU_BENCH_C("Ec::glv", 100, P1 = P0; s.setRand(rg); GLV1::mul, P2, P1, s.getMpz());
}
/*

@ -716,8 +716,9 @@ CYBOZU_TEST_AUTO(hashBench)
CYBOZU_TEST_AUTO(liftedElGamal)
{
const size_t hashSize = 1024;
initG1only(mcl::ecparam::secp192k1, hashSize);
const size_t byteSize = 192 / 8;
const mcl::EcParam& param = mcl::ecparam::secp256k1;
initG1only(param, hashSize);
const size_t byteSize = (param.bitSize + 7) / 8;
SecretKey sec;
sec.setByCSPRNG();
PublicKey pub;

Loading…
Cancel
Save