elgamal class

dev
MITSUNARI Shigeo 10 years ago
parent 19adc40cb7
commit da44377532
  1. 504
      include/mcl/elgamal.hpp
  2. 131
      include/mcl/power_window.hpp
  3. 34
      readme.md
  4. 332
      test/elgamal_test.cpp
  5. 23
      test/power_window_test.cpp

@ -0,0 +1,504 @@
#pragma once
/**
@file
@brief lifted-ElGamal encryption
Copyright (c) 2014, National Institute of Advanced Industrial
Science and Technology All rights reserved.
This source file is subject to BSD 3-Clause license.
*/
#include <string>
#include <sstream>
#include <cybozu/unordered_map.hpp>
#include <cybozu/bitvector.hpp>
#ifndef CYBOZU_UNORDERED_MAP_STD
#include <map>
#endif
#include <cybozu/exception.hpp>
#include <mcl/tagmultigr.hpp>
#include <mcl/power_window.hpp>
namespace mcl {
template<class _G, class Zn>
struct ElgamalT {
typedef _G G;
typedef TagMultiGr<G> TagG;
struct CipherText {
typedef _G G;
G c1;
G c2;
/*
(c1, c2) = (0, 0) is trivial valid ciphertext for m = 0
*/
void clear()
{
TagG::init(c1);
TagG::init(c2);
}
/*
add encoded message with encoded message
input : this = Enc(m1), c = Enc(m2)
output : this = Enc(m1 + m2)
*/
void add(const CipherText& c)
{
TagG::mul(c1, c1, c.c1);
TagG::mul(c2, c2, c.c2);
}
/*
mul by x
input : this = Enc(m), x
output : this = Enc(m x)
*/
template<class N>
void mul(const N& x)
{
G::power(c1, c1, x);
G::power(c2, c2, x);
}
/*
negative encoded message
input : this = Enc(m)
output : this = Enc(-m)
*/
void neg()
{
TagG::inv(c1, c1);
TagG::inv(c2, c2);
}
std::string toStr() const
{
std::ostringstream os;
if (!(os << (*this))) throw cybozu::Exception("ElgamalT:CipherText:toStr");
return os.str();
}
void fromStr(const std::string& str)
{
std::istringstream is(str);
if (!(is >> (*this))) throw cybozu::Exception("ElgamalT:CipherText:fromStr") << str;
}
friend inline std::ostream& operator<<(std::ostream& os, const CipherText& self)
{
std::ios_base::fmtflags flags = os.flags();
os << std::hex << self.c1 << ' ' << self.c2;
os.flags(flags);
return os;
}
friend inline std::istream& operator>>(std::istream& is, CipherText& self)
{
std::ios_base::fmtflags flags = is.flags();
is >> std::hex >> self.c1 >> self.c2;
is.flags(flags);
return is;
}
void appendToBitVec(cybozu::BitVector& bv) const
{
c1.appendToBitVec(bv);
c2.appendToBitVec(bv);
}
void fromBitVec(const cybozu::BitVector& bv)
{
size_t bitLen = G::getBitVecSize();
cybozu::BitVector t;
bv.extract(t, 0, bitLen);
c1.fromBitVec(t);
bv.extract(t, bitLen, bitLen);
c2.fromBitVec(t);
}
static inline size_t getBitVecSize()
{
return G::getBitVecSize() * 2;
}
};
/*
Zero Knowledge Proof
cipher text with ZKP to ensure m = 0 or 1
*/
struct Zkp {
typedef _G G;
Zn c0, c1, s0, s1;
std::string toStr() const
{
std::ostringstream os;
if (!(os << (*this))) throw cybozu::Exception("ElgamalT:Zkp:toStr");
return os.str();
}
void fromStr(const std::string& str)
{
std::istringstream is(str);
if (!(is >> (*this))) throw cybozu::Exception("ElgamalT:Zkp:fromStr") << str;
}
friend inline std::ostream& operator<<(std::ostream& os, const Zkp& self)
{
std::ios_base::fmtflags flags = os.flags();
os << std::hex << self.c0 << ' ' << self.c1 << ' ' << self.s0 << ' ' << self.s1;
os.flags(flags);
return os;
}
friend inline std::istream& operator>>(std::istream& is, Zkp& self)
{
std::ios_base::fmtflags flags = is.flags();
is >> std::hex >> self.c0 >> self.c1 >> self.s0 >> self.s1;
is.flags(flags);
return is;
}
};
class PublicKey {
typedef _G G;
size_t bitLen;
G f;
G g;
G h;
bool enablePowerWindow_;
mcl::PowerWindow<G> powf;
mcl::PowerWindow<G> powg;
mcl::PowerWindow<G> powh;
template<class N>
void powerSub(G& z, const G& x, const N& n, const mcl::PowerWindow<G>& pw) const
{
if (enablePowerWindow_) {
pw.power(z, n);
} else {
G::power(z, x, n);
}
}
template<class N>
void powerF(G& z, const N& n) const { powerSub(z, f, n, powf); }
template<class N>
void powerG(G& z, const N& n) const { powerSub(z, g, n, powg); }
template<class N>
void powerH(G& z, const N& n) const { powerSub(z, h, n, powh); }
public:
PublicKey()
: bitLen(0)
, enablePowerWindow_(false)
{
}
void enablePowerWindow(size_t winSize = 10)
{
powf.init(f, bitLen, winSize);
powg.init(g, bitLen, winSize);
powh.init(h, bitLen, winSize);
enablePowerWindow_ = true;
}
const G& getF() const { return f; }
void init(size_t bitLen, const G& f, const G& g, const G& h)
{
this->bitLen = bitLen;
this->f = f;
this->g = g;
this->h = h;
enablePowerWindow_ = false;
}
/*
encode message
input : m
output : c = (c1, c2) = (g^u, h^u f^m)
*/
template<class RG>
void enc(CipherText& c, const Zn& m, RG& rg) const
{
Zn u;
u.setRand(rg);
powerG(c.c1, u);
powerH(c.c2, u);
G t;
powerF(t, m);
TagG::mul(c.c2, c.c2, t);
}
/*
encode message
input : m = 0 or 1
output : c (c1, c2), zkp
*/
template<class RG, class Hash>
void encWithZkp(CipherText& c, Zkp& zkp, int m, Hash& hash, RG& rg) const
{
if (m != 0 && m != 1) {
throw cybozu::Exception("elgamal:PublicKey:encWithZkp") << m;
}
Zn u;
u.setRand(rg);
powerG(c.c1, u);
powerH(c.c2, u);
if (m) {
TagG::mul(c.c2, c.c2, f);
Zn r1;
r1.setRand(rg);
zkp.c0.setRand(rg);
zkp.s0.setRand(rg);
G R01, R02, R11, R12;
G t1, t2;
powerG(t1, zkp.s0);
G::power(t2, c.c1, zkp.c0);
TagG::div(R01, t1, t2);
powerH(t1, zkp.s0);
G::power(t2, c.c2, zkp.c0);
TagG::div(R02, t1, t2);
powerG(R11, r1);
powerH(R12, r1);
std::ostringstream os;
os << R01 << R02 << R11 << R12 << c.c1 << c.c2 << f << g << h;
hash.update(os.str());
const std::string digest = hash.digest();
Zn cc;
cc.setRaw(digest.c_str(), digest.size());
zkp.c1 = cc - zkp.c0;
zkp.s1 = r1 + zkp.c1 * u;
} else {
Zn r0;
r0.setRand(rg);
zkp.c1.setRand(rg);
zkp.s1.setRand(rg);
G R01, R02, R11, R12;
powerG(R01, r0);
powerH(R02, r0);
G t1, t2;
powerG(t1, zkp.s1);
G::power(t2, c.c1, zkp.c1);
TagG::div(R11, t1, t2);
powerH(t1, zkp.s1);
TagG::div(t2, c.c2, f);
G::power(t2, t2, zkp.c1);
TagG::div(R12, t1, t2);
std::ostringstream os;
os << R01 << R02 << R11 << R12 << c.c1 << c.c2 << f << g << h;
hash.update(os.str());
const std::string digest = hash.digest();
Zn c;
c.setRaw(digest.c_str(), digest.size());
zkp.c0 = c - zkp.c1;
zkp.s0 = r0 + zkp.c0 * u;
}
}
/*
verify cipher text with ZKP
*/
template<class Hash>
bool verify(const CipherText& c, const Zkp& zkp, Hash& hash) const
{
G R01, R02, R11, R12;
G t1, t2;
powerG(t1, zkp.s0);
G::power(t2, c.c1, zkp.c0);
TagG::div(R01, t1, t2);
powerH(t1, zkp.s0);
G::power(t2, c.c2, zkp.c0);
TagG::div(R02, t1, t2);
powerG(t1, zkp.s1);
G::power(t2, c.c1, zkp.c1);
TagG::div(R11, t1, t2);
powerH(t1, zkp.s1);
TagG::div(t2, c.c2, f);
G::power(t2, t2, zkp.c1);
TagG::div(R12, t1, t2);
std::ostringstream os;
os << R01 << R02 << R11 << R12 << c.c1 << c.c2 << f << g << h;
hash.update(os.str());
const std::string digest = hash.digest();
Zn cc;
cc.setRaw(digest.c_str(), digest.size());
return cc == zkp.c0 + zkp.c1;
}
/*
rerandomize encoded message
input : c = (c1, c2)
output : c = (c1 g^v, c2 h^v)
*/
template<class RG>
void rerandomize(CipherText& c, RG& rg) const
{
Zn v;
v.setRand(rg);
G t;
powerG(t, v);
TagG::mul(c.c1, c.c1, t);
powerH(t, v);
TagG::mul(c.c2, c.c2, t);
}
/*
add encoded message with plain message
input : c = Enc(m1) = (c1, c2), m2
ouput : c = Enc(m1 + m2) = (c1, c2 f^m2)
*/
template<class N>
void add(CipherText& c, const N& m) const
{
G fm;
powerF(fm, m);
TagG::mul(c.c2, c.c2, fm);
}
std::string toStr() const
{
std::ostringstream os;
if (!(os << (*this))) throw cybozu::Exception("ElgamalT:PublicKey:toStr");
return os.str();
}
void fromStr(const std::string& str)
{
std::istringstream is(str);
if (!(is >> (*this))) throw cybozu::Exception("ElgamalT:PublicKey:fromStr") << str;
}
friend inline std::ostream& operator<<(std::ostream& os, const PublicKey& self)
{
std::ios_base::fmtflags flags = os.flags();
os << std::dec << self.bitLen << ' ' << std::hex << self.f << ' ' << self.g << ' ' << self.h;
os.flags(flags);
return os;
}
friend inline std::istream& operator>>(std::istream& is, PublicKey& self)
{
std::ios_base::fmtflags flags = is.flags();
size_t bitLen;
G f, g, h;
is >> std::dec >> bitLen >> std::hex >> f >> g >> h;
is.flags(flags);
self.init(bitLen, f, g, h);
return is;
}
};
class PrivateKey {
PublicKey pub;
Zn z;
public:
typedef _G G;
/*
init
input : f
output : (g, h, z)
G = <f>
g in G
h = g^z
*/
template<class RG>
void init(const G& f, size_t bitLen, RG& rg)
{
G g, h;
z.setRand(rg);
G::power(g, f, z);
z.setRand(rg);
G::power(h, g, z);
pub.init(bitLen, f, g, h);
}
const PublicKey& getPublicKey() const { return pub; }
/*
decode message
input : c = (c1, c2)
output : m
M = c2 / c1^z
find m such that M = f^m and |m| < limit
@memo 7sec@core i3 for m = 1e6
*/
void dec(Zn& m, const CipherText& c, int limit = 100000) const
{
const G& f = pub.getF();
G c1z;
G::power(c1z, c.c1, z);
if (c1z == c.c2) {
m = 0;
return;
}
G t1(c1z);
G t2(c.c2);
for (int i = 1; i < limit; i++) {
TagG::mul(t1, t1, f);
if (t1 == c.c2) {
m = i;
return;
}
TagG::mul(t2, t2, f);
if (t2 == c1z) {
m = -i;
return;
}
}
throw cybozu::Exception("elgamal:PrivateKey:dec:overflow");
}
/*
powfm = c2 / c1^z = f^m
*/
void getPowerf(G& powfm, const CipherText& c) const
{
G c1z;
G::power(c1z, c.c1, z);
TagG::div(powfm, c.c2, c1z);
}
/*
check whether c is encrypted zero message
*/
bool isZeroMessage(const CipherText& c) const
{
G c1z;
G::power(c1z, c.c1, z);
return c.c2 == c1z;
}
std::string toStr() const
{
std::ostringstream os;
if (!(os << (*this))) throw cybozu::Exception("ElgamalT:PrivateKey:toStr");
return os.str();
}
void fromStr(const std::string& str)
{
std::istringstream is(str);
if (!(is >> (*this))) throw cybozu::Exception("ElgamalT:PrivateKey:fromStr") << str;
}
friend inline std::ostream& operator<<(std::ostream& os, const PrivateKey& self)
{
std::ios_base::fmtflags flags = os.flags();
os << self.pub << ' ' << std::hex << self.z;
os.flags(flags);
return os;
}
friend inline std::istream& operator>>(std::istream& is, PrivateKey& self)
{
std::ios_base::fmtflags flags = is.flags();
is >> self.pub >> std::hex >> self.z;
is.flags(flags);
return is;
}
};
/*
create table f^i for i in [rangeMin, rangeMax]
*/
struct PowerCache {
typedef _G G;
#if (CYBOZU_CPP_VERSION > CYBOZU_CPP_VERSION_CP03)
typedef CYBOZU_NAMESPACE_STD::unordered_map<G, int> Cache;
#else
typedef std::map<G, int> Cache;
#endif
Cache cache;
void init(const G& f, int rangeMin, int rangeMax)
{
if (rangeMin > rangeMax) throw cybozu::Exception("mcl:ElgamalT:PowerCache:bad range") << rangeMin << rangeMax;
G x;
x.clear();
cache[x] = 0;
for (int i = 1; i <= rangeMax; i++) {
TagG::mul(x, x, f);
cache[x] = i;
}
G nf;
TagG::inv(nf, f);
x.clear();
for (int i = -1; i >= rangeMin; i--) {
TagG::mul(x, x, nf);
cache[x] = i;
}
}
/*
return m such that f^m = g
*/
int getExponent(const G& g) const
{
typename Cache::const_iterator i = cache.find(g);
if (i == cache.end()) throw cybozu::Exception("Elgamal:PowerCache:getExponent:not found") << g;
return i->second;
}
};
};
} // mcl

@ -0,0 +1,131 @@
#pragma once
/**
@file
@brief power window method
@author MITSUNARI Shigeo(@herumi)
@note
Copyright (c) 2014, National Institute of Advanced Industrial
Science and Technology All rights reserved.
This source file is subject to BSD 3-Clause license.
*/
#include <vector>
#include <cybozu/exception.hpp>
namespace mcl {
template<class Ec>
class PowerWindow {
public:
typedef TagMultiGr<Ec> TagG;
typedef std::vector<Ec> EcV;
typedef std::vector<EcV> EcVV;
size_t bitLen_;
size_t winSize_;
EcVV tbl_;
PowerWindow(const Ec& x, size_t bitLen, size_t winSize)
{
init(x, bitLen, winSize);
}
PowerWindow()
: bitLen_(0)
, winSize_(0)
{
}
/*
@param x [in] base index
@param bitLen [in] exponent bit length
@param winSize [in] window size
*/
void init(const Ec& x, size_t bitLen, size_t winSize)
{
bitLen_ = bitLen;
winSize_ = winSize;
const size_t tblNum = (bitLen + winSize) / winSize;
const size_t r = size_t(1) << winSize;
// alloc table
tbl_.resize(tblNum);
Ec t(x);
for (size_t i = 0; i < tblNum; i++) {
tbl_[i].resize(r);
EcV& w = tbl_[i];
for (size_t d = 1; d < r; d *= 2) {
for (size_t j = 0; j < d; j++) {
Ec::add(w[j + d], w[j], t);
}
Ec::dbl(t, t);
}
}
}
/*
@param z [out] x^y
@param y [in] exponent
*/
template<class F>
void power(Ec& z, const F& _y) const
{
typedef power_impl::TagInt<F> TagI;
typedef typename TagI::BlockType BlockType;
const Ec& x = tbl_[0][1];
if (_y == 0) {
TagG::init(z);
return;
}
if (_y == 1) {
z = x;
return;
}
const bool isNegative = _y < 0;
F y = isNegative ? -_y : _y;
Ec out;
TagG::init(out);
const BlockType mask = (BlockType(1) << winSize_) - 1;
size_t i = 0;
while (y > 0) {
BlockType v = TagI::getBlock(y, 0) & mask;
assert(i < tbl_.size());
if (i >= tbl_.size()) throw cybozu::Exception("mcl:PowerWindow:power:bad value") << _y << i << tbl_.size();
Ec::add(out, out, tbl_[i][v]);
TagI::shr(y, winSize_);
i++;
}
z = out;
if (isNegative) {
Ec::neg(z, z);
}
}
template<class F>
void powerArray(Ec& z, const Unit* y, size_t yn) const
{
typedef power_impl::TagInt<F> TagI;
typedef typename TagI::BlockType BlockType;
const Ec& x = tbl_[0][1];
if (_y == 0) {
TagG::init(z);
return;
}
if (_y == 1) {
z = x;
return;
}
const bool isNegative = _y < 0;
F y = isNegative ? -_y : _y;
Ec out;
TagG::init(out);
const BlockType mask = (BlockType(1) << winSize_) - 1;
size_t i = 0;
while (y > 0) {
BlockType v = TagI::getBlock(y, 0) & mask;
assert(i < tbl_.size());
if (i >= tbl_.size()) throw cybozu::Exception("mcl:PowerWindow:power:bad value") << _y << i << tbl_.size();
Ec::add(out, out, tbl_[i][v]);
TagI::shr(y, winSize_);
i++;
}
z = out;
if (isNegative) {
Ec::neg(z, z);
}
}
};
} // mcl

@ -0,0 +1,34 @@
# mcl
A class library of finite field and elliptic curve.
# Abstract
This is a library to make a protocol for elliptic curve cryptography.
# Installation Requirements
Create a working directory (e.g., work) and clone the following repositories.
mkdir work
cd work
git clone git://github.com/herumi/xbyak.git
git clone git://github.com/herumi/cybozulib.git
git clone git://github.com/herumi/cybozulib_ext.git
* Cybozulib_ext is a prerequisite for running OpenSSL and GMP on VC (Visual C++).
* Xbyak is a prerequisite for optimizing the operations in the finite field on Intel CPUs.
* OpenSSL and libgmp-dev are available via apt-get (or other similar commands) if using Linux.
# License
modified new BSD License
http://opensource.org/licenses/BSD-3-Clause
The original source of the followings are git://github.com/aistcrypt/Lifted-ElGamal.git .
These files are licensed by BSD-3-Clause and are used for only tests.
# Author
MITSUNARI Shigeo(herumi@nifyt.com)

@ -0,0 +1,332 @@
#include <cybozu/test.hpp>
#include <mcl/fp.hpp>
#include <mcl/gmp_util.hpp>
#include <mcl/elgamal.hpp>
#include <cybozu/random_generator.hpp>
#include <mcl/ecparam.hpp>
#include <cybozu/crypto.hpp>
#if defined(_WIN64) || defined(__x86_64__)
#define USE_MONT_FP
#endif
#ifdef USE_MONT_FP
#include <mcl/mont_fp.hpp>
typedef mcl::MontFpT<3> Fp;
#else
typedef mcl::FpT<mcl::Gmp> Fp;
#endif
typedef mcl::EcT<Fp> Ec;
struct TagFp;
struct TagEc;
const mcl::EcParam& para = mcl::ecparam::secp192k1;
cybozu::RandomGenerator rg;
CYBOZU_TEST_AUTO(testFp)
{
typedef mcl::FpT<mcl::Gmp, TagFp> Zn;
typedef mcl::ElgamalT<Fp, Zn> ElgamalFp;
/*
Zn = (Z/mZ) - {0}
*/
const int m = 65537;
{
std::ostringstream os;
os << m;
Fp::setModulo(os.str());
}
{
std::ostringstream os;
os << m - 1;
Zn::setModulo(os.str());
}
ElgamalFp::PrivateKey prv;
/*
3^(m-1) = 1
*/
const int f = 3;
{
Fp x(f);
Fp::power(x, x, m - 1);
CYBOZU_TEST_EQUAL(x, 1);
}
prv.init(f, 17, rg);
const ElgamalFp::PublicKey& pub = prv.getPublicKey();
const int m1 = 12345;
const int m2 = 17655;
ElgamalFp::CipherText c1, c2;
pub.enc(c1, m1, rg);
pub.enc(c2, m2, rg);
// BitVector
{
cybozu::BitVector bv;
c1.appendToBitVec(bv);
ElgamalFp::CipherText c3;
c3.fromBitVec(bv);
CYBOZU_TEST_EQUAL(c1.c1, c3.c1);
CYBOZU_TEST_EQUAL(c1.c2, c3.c2);
}
Zn dec1, dec2;
prv.dec(dec1, c1);
prv.dec(dec2, c2);
// dec(enc) = id
CYBOZU_TEST_EQUAL(dec1, m1);
CYBOZU_TEST_EQUAL(dec2, m2);
// iostream
{
ElgamalFp::PublicKey pub2;
ElgamalFp::PrivateKey prv2;
ElgamalFp::CipherText cc1, cc2;
{
std::stringstream ss;
ss << prv;
ss >> prv2;
}
Zn d;
prv2.dec(d, c1);
CYBOZU_TEST_EQUAL(d, m1);
{
std::stringstream ss;
ss << c1;
ss >> cc1;
}
d = 0;
prv2.dec(d, cc1);
CYBOZU_TEST_EQUAL(d, m1);
{
std::stringstream ss;
ss << pub;
ss >> pub2;
}
pub2.enc(cc2, m2, rg);
prv.dec(d, cc2);
CYBOZU_TEST_EQUAL(d, m2);
}
// enc(m1) enc(m2) = enc(m1 + m2)
c1.add(c2);
prv.dec(dec1, c1);
CYBOZU_TEST_EQUAL(dec1, m1 + m2);
// enc(m1) x = enc(m1 + x)
const int x = 555;
pub.add(c1, x);
prv.dec(dec1, c1);
CYBOZU_TEST_EQUAL(dec1, m1 + m2 + x);
// rerandomize
c1 = c2;
pub.rerandomize(c1, rg);
// verify c1 != c2
CYBOZU_TEST_ASSERT(c1.c1 != c2.c1);
CYBOZU_TEST_ASSERT(c1.c2 != c2.c2);
prv.dec(dec1, c1);
// dec(c1) = dec(c2)
CYBOZU_TEST_EQUAL(dec1, m2);
// check neg
{
ElgamalFp::CipherText c;
Zn m = 1234;
pub.enc(c, m, rg);
c.neg();
Zn dec;
prv.dec(dec, c);
CYBOZU_TEST_EQUAL(dec, -m);
}
// check mul
{
ElgamalFp::CipherText c;
Zn m = 1234;
int x = 111;
pub.enc(c, m, rg);
c.mul(x);
Zn dec;
prv.dec(dec, c);
m *= x;
CYBOZU_TEST_EQUAL(dec, m);
}
// check negative value
for (int i = -10; i < 10; i++) {
ElgamalFp::CipherText c;
const Zn mm = i;
pub.enc(c, mm, rg);
Zn dec;
prv.dec(dec, c, 1000);
CYBOZU_TEST_EQUAL(dec, mm);
}
// isZeroMessage
for (int m = 0; m < 10; m++) {
ElgamalFp::CipherText c0;
pub.enc(c0, m, rg);
if (m == 0) {
CYBOZU_TEST_ASSERT(prv.isZeroMessage(c0));
} else {
CYBOZU_TEST_ASSERT(!prv.isZeroMessage(c0));
}
}
// zkp
{
ElgamalFp::Zkp zkp;
ElgamalFp::CipherText c;
cybozu::crypto::Hash hash(cybozu::crypto::Hash::N_SHA256);
pub.encWithZkp(c, zkp, 0, hash, rg);
CYBOZU_TEST_ASSERT(pub.verify(c, zkp, hash));
zkp.s0 += 1;
CYBOZU_TEST_ASSERT(!pub.verify(c, zkp, hash));
pub.encWithZkp(c, zkp, 1, hash, rg);
CYBOZU_TEST_ASSERT(pub.verify(c, zkp, hash));
zkp.s0 += 1;
CYBOZU_TEST_ASSERT(!pub.verify(c, zkp, hash));
CYBOZU_TEST_EXCEPTION_MESSAGE(pub.encWithZkp(c, zkp, 2, hash, rg), cybozu::Exception, "encWithZkp");
}
}
CYBOZU_TEST_AUTO(testEc)
{
typedef mcl::FpT<mcl::Gmp, TagEc> Zn;
typedef mcl::ElgamalT<Ec, Zn> ElgamalEc;
Fp::setModulo(para.p);
Zn::setModulo(para.n);
Ec::setParam(para.a, para.b);
const Fp x0(para.gx);
const Fp y0(para.gy);
const size_t bitLen = Zn(-1).getBitLen();
const Ec P(x0, y0);
/*
Zn = <P>
*/
ElgamalEc::PrivateKey prv;
prv.init(P, bitLen, rg);
const ElgamalEc::PublicKey& pub = prv.getPublicKey();
const int m1 = 12345;
const int m2 = 17655;
ElgamalEc::CipherText c1, c2;
pub.enc(c1, m1, rg);
pub.enc(c2, m2, rg);
// BitVector
{
cybozu::BitVector bv;
c1.appendToBitVec(bv);
ElgamalEc::CipherText c3;
c3.fromBitVec(bv);
CYBOZU_TEST_EQUAL(c1.c1, c3.c1);
CYBOZU_TEST_EQUAL(c1.c2, c3.c2);
}
Zn dec1, dec2;
prv.dec(dec1, c1);
prv.dec(dec2, c2);
// dec(enc) = id
CYBOZU_TEST_EQUAL(dec1, m1);
CYBOZU_TEST_EQUAL(dec2, m2);
// iostream
{
ElgamalEc::PublicKey pub2;
ElgamalEc::PrivateKey prv2;
ElgamalEc::CipherText cc1, cc2;
{
std::stringstream ss;
ss << prv;
ss >> prv2;
}
Zn d;
prv2.dec(d, c1);
CYBOZU_TEST_EQUAL(d, m1);
{
std::stringstream ss;
ss << c1;
ss >> cc1;
}
d = 0;
prv2.dec(d, cc1);
CYBOZU_TEST_EQUAL(d, m1);
{
std::stringstream ss;
ss << pub;
ss >> pub2;
}
pub2.enc(cc2, m2, rg);
prv.dec(d, cc2);
CYBOZU_TEST_EQUAL(d, m2);
}
// enc(m1) enc(m2) = enc(m1 + m2)
c1.add(c2);
prv.dec(dec1, c1);
CYBOZU_TEST_EQUAL(dec1, m1 + m2);
// enc(m1) x = enc(m1 + x)
const int x = 555;
pub.add(c1, x);
prv.dec(dec1, c1);
CYBOZU_TEST_EQUAL(dec1, m1 + m2 + x);
// rerandomize
c1 = c2;
pub.rerandomize(c1, rg);
// verify c1 != c2
CYBOZU_TEST_ASSERT(c1.c1 != c2.c1);
CYBOZU_TEST_ASSERT(c1.c2 != c2.c2);
prv.dec(dec1, c1);
// dec(c1) = dec(c2)
CYBOZU_TEST_EQUAL(dec1, m2);
// check neg
{
ElgamalEc::CipherText c;
Zn m = 1234;
pub.enc(c, m, rg);
c.neg();
Zn dec;
prv.dec(dec, c);
CYBOZU_TEST_EQUAL(dec, -m);
}
// check mul
{
ElgamalEc::CipherText c;
Zn m = 123;
int x = 111;
pub.enc(c, m, rg);
Zn dec;
prv.dec(dec, c);
c.mul(x);
prv.dec(dec, c);
m *= x;
CYBOZU_TEST_EQUAL(dec, m);
}
// check negative value
for (int i = -10; i < 10; i++) {
ElgamalEc::CipherText c;
const Zn mm = i;
pub.enc(c, mm, rg);
Zn dec;
prv.dec(dec, c, 1000);
CYBOZU_TEST_EQUAL(dec, mm);
}
// isZeroMessage
for (int m = 0; m < 10; m++) {
ElgamalEc::CipherText c0;
pub.enc(c0, m, rg);
if (m == 0) {
CYBOZU_TEST_ASSERT(prv.isZeroMessage(c0));
} else {
CYBOZU_TEST_ASSERT(!prv.isZeroMessage(c0));
}
}
// zkp
{
ElgamalEc::Zkp zkp;
ElgamalEc::CipherText c;
// cybozu::Sha1 hash;
cybozu::crypto::Hash hash(cybozu::crypto::Hash::N_SHA256);
pub.encWithZkp(c, zkp, 0, hash, rg);
CYBOZU_TEST_ASSERT(pub.verify(c, zkp, hash));
zkp.s0 += 1;
CYBOZU_TEST_ASSERT(!pub.verify(c, zkp, hash));
pub.encWithZkp(c, zkp, 1, hash, rg);
CYBOZU_TEST_ASSERT(pub.verify(c, zkp, hash));
zkp.s0 += 1;
CYBOZU_TEST_ASSERT(!pub.verify(c, zkp, hash));
CYBOZU_TEST_EXCEPTION_MESSAGE(pub.encWithZkp(c, zkp, 2, hash, rg), cybozu::Exception, "encWithZkp");
}
}

@ -0,0 +1,23 @@
#include <cybozu/test.hpp>
#include <mcl/power_window.hpp>
#include <mcl/fp.hpp>
CYBOZU_TEST_AUTO(int)
{
typedef mcl::FpT<> Fp;
Fp::setModulo("65537");
typedef mcl::PowerWindow<Fp> PW;
const Fp g = 123;
const size_t bitLen = 16;
for (size_t winSize = 1; winSize <= 16; winSize++) {
PW pw(g, bitLen, winSize);
for (int i = 0; i < (1 << bitLen); i++) {
Fp x, y;
pw.power(x, i);
Fp::power(y, g, i);
CYBOZU_TEST_EQUAL(x, y);
}
}
}
Loading…
Cancel
Save