parent
19adc40cb7
commit
da44377532
@ -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…
Reference in new issue