312 lines
8.9 KiB
C++
312 lines
8.9 KiB
C++
// elgamal.cpp - written and placed in the public domain by Wei Dai
|
|
|
|
#include "pch.h"
|
|
#include "elgamal.h"
|
|
#include "asn.h"
|
|
|
|
ElGamalCryptoPublicKey::ElGamalCryptoPublicKey(const Integer &p, const Integer &g, const Integer &y)
|
|
: p(p), g(g), y(y), modulusLen(p.ByteCount()),
|
|
gpc(p, g, ExponentBitLength(), 1), ypc(p, y, ExponentBitLength(), 1)
|
|
{
|
|
}
|
|
|
|
ElGamalCryptoPublicKey::~ElGamalCryptoPublicKey()
|
|
{
|
|
// Note: This empty descrutor was added to get around a SunPro compiler bug.
|
|
// Without this destructor, the code core dumped.
|
|
}
|
|
|
|
ElGamalCryptoPublicKey::ElGamalCryptoPublicKey(BufferedTransformation &bt)
|
|
{
|
|
BERSequenceDecoder seq(bt);
|
|
p.BERDecode(seq);
|
|
modulusLen=p.ByteCount();
|
|
g.BERDecode(seq);
|
|
y.BERDecode(seq);
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
}
|
|
|
|
void ElGamalCryptoPublicKey::DEREncode(BufferedTransformation &bt) const
|
|
{
|
|
DERSequenceEncoder seq(bt);
|
|
p.DEREncode(seq);
|
|
g.DEREncode(seq);
|
|
y.DEREncode(seq);
|
|
}
|
|
|
|
void ElGamalCryptoPublicKey::Precompute(unsigned int precomputationStorage)
|
|
{
|
|
gpc.Precompute(p, g, ExponentBitLength(), precomputationStorage);
|
|
ypc.Precompute(p, y, ExponentBitLength(), precomputationStorage);
|
|
}
|
|
|
|
void ElGamalCryptoPublicKey::LoadPrecomputation(BufferedTransformation &bt)
|
|
{
|
|
gpc.Load(p, bt);
|
|
ypc.Load(p, bt);
|
|
}
|
|
|
|
void ElGamalCryptoPublicKey::SavePrecomputation(BufferedTransformation &bt) const
|
|
{
|
|
gpc.Save(bt);
|
|
ypc.Save(bt);
|
|
}
|
|
|
|
void ElGamalCryptoPublicKey::Encrypt(RandomNumberGenerator &rng, const byte *plainText, unsigned int plainTextLength, byte *cipherText)
|
|
{
|
|
assert(plainTextLength <= MaxPlainTextLength());
|
|
|
|
SecByteBlock block(modulusLen-1);
|
|
rng.GetBlock(block, modulusLen-2-plainTextLength);
|
|
memcpy(block+modulusLen-2-plainTextLength, plainText, plainTextLength);
|
|
block[modulusLen-2] = plainTextLength;
|
|
|
|
Integer m(block, modulusLen-1);
|
|
Integer a,b;
|
|
RawEncrypt(Integer(rng, ExponentBitLength()), m, a, b);
|
|
|
|
a.Encode(cipherText, modulusLen);
|
|
b.Encode(cipherText+modulusLen, modulusLen);
|
|
}
|
|
|
|
void ElGamalCryptoPublicKey::RawEncrypt(const Integer &k, const Integer &m, Integer &a, Integer &b) const
|
|
{
|
|
// a = a_exp_b_mod_c(g, k, p);
|
|
// b = m * a_exp_b_mod_c(y, k, p) % p;
|
|
a = gpc.Exponentiate(k);
|
|
b = m * ypc.Exponentiate(k) % p;
|
|
}
|
|
|
|
unsigned int ElGamalCryptoPublicKey::ExponentBitLength() const
|
|
{
|
|
return 2*DiscreteLogWorkFactor(p.BitCount());
|
|
}
|
|
|
|
// *************************************************************
|
|
|
|
ElGamalCryptoPrivateKey::ElGamalCryptoPrivateKey(const Integer &p, const Integer &g, const Integer &y, const Integer &x)
|
|
: ElGamalCryptoPublicKey(p, g, y), x(x)
|
|
{
|
|
}
|
|
|
|
ElGamalCryptoPrivateKey::ElGamalCryptoPrivateKey(RandomNumberGenerator &rng, unsigned int pbits)
|
|
{
|
|
PrimeAndGenerator pg(1, rng, pbits);
|
|
p = pg.Prime();
|
|
modulusLen=p.ByteCount();
|
|
g = pg.Generator();
|
|
x.Randomize(rng, ExponentBitLength());
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
y = gpc.Exponentiate(x);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
}
|
|
|
|
ElGamalCryptoPrivateKey::ElGamalCryptoPrivateKey(RandomNumberGenerator &rng, const Integer &pIn, const Integer &gIn)
|
|
{
|
|
p = pIn;
|
|
modulusLen=p.ByteCount();
|
|
g = gIn;
|
|
x.Randomize(rng, ExponentBitLength());
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
y = gpc.Exponentiate(x);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
}
|
|
|
|
ElGamalCryptoPrivateKey::~ElGamalCryptoPrivateKey()
|
|
{
|
|
// Note: This empty descrutor was added to get around a SunPro compiler bug.
|
|
// Without this destructor, the code core dumped.
|
|
}
|
|
|
|
ElGamalCryptoPrivateKey::ElGamalCryptoPrivateKey(BufferedTransformation &bt)
|
|
{
|
|
BERSequenceDecoder seq(bt);
|
|
p.BERDecode(seq);
|
|
modulusLen=p.ByteCount();
|
|
g.BERDecode(seq);
|
|
y.BERDecode(seq);
|
|
x.BERDecode(seq);
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
}
|
|
|
|
void ElGamalCryptoPrivateKey::DEREncode(BufferedTransformation &bt) const
|
|
{
|
|
DERSequenceEncoder seq(bt);
|
|
p.DEREncode(seq);
|
|
g.DEREncode(seq);
|
|
y.DEREncode(seq);
|
|
x.DEREncode(seq);
|
|
}
|
|
|
|
unsigned int ElGamalCryptoPrivateKey::Decrypt(const byte *cipherText, byte *plainText)
|
|
{
|
|
Integer a(cipherText, modulusLen);
|
|
Integer b(cipherText+modulusLen, modulusLen);
|
|
Integer m;
|
|
|
|
RawDecrypt(a, b, m);
|
|
m.Encode(plainText, 1);
|
|
unsigned int plainTextLength = plainText[0];
|
|
if (plainTextLength > MaxPlainTextLength())
|
|
return 0;
|
|
m >>= 8;
|
|
m.Encode(plainText, plainTextLength);
|
|
return plainTextLength;
|
|
}
|
|
|
|
void ElGamalCryptoPrivateKey::RawDecrypt(const Integer &a, const Integer &b, Integer &m) const
|
|
{
|
|
if (x.BitCount()+20 < p.BitCount()) // if x is short
|
|
m = b * EuclideanMultiplicativeInverse(a_exp_b_mod_c(a, x, p), p) % p;
|
|
else // save a multiplicative inverse calculation
|
|
m = b * a_exp_b_mod_c(a, p-1-x, p) % p;
|
|
}
|
|
|
|
// ******************************************************************
|
|
|
|
ElGamalSigPublicKey::ElGamalSigPublicKey(const Integer &p, const Integer &q, const Integer &g, const Integer &y)
|
|
: p(p), q(q), g(g), y(y), qLen(q.ByteCount()),
|
|
gpc(p, g, ExponentBitLength(), 1), ypc(p, y, ExponentBitLength(), 1)
|
|
{
|
|
}
|
|
|
|
ElGamalSigPublicKey::ElGamalSigPublicKey(BufferedTransformation &bt)
|
|
{
|
|
BERSequenceDecoder seq(bt);
|
|
p.BERDecode(seq);
|
|
q.BERDecode(seq);
|
|
g.BERDecode(seq);
|
|
y.BERDecode(seq);
|
|
qLen = q.ByteCount();
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
}
|
|
|
|
void ElGamalSigPublicKey::DEREncode(BufferedTransformation &bt) const
|
|
{
|
|
DERSequenceEncoder seq(bt);
|
|
p.DEREncode(seq);
|
|
q.DEREncode(seq);
|
|
g.DEREncode(seq);
|
|
y.DEREncode(seq);
|
|
}
|
|
|
|
void ElGamalSigPublicKey::Precompute(unsigned int precomputationStorage)
|
|
{
|
|
gpc.Precompute(p, g, ExponentBitLength(), precomputationStorage);
|
|
ypc.Precompute(p, y, ExponentBitLength(), precomputationStorage);
|
|
}
|
|
|
|
void ElGamalSigPublicKey::LoadPrecomputation(BufferedTransformation &bt)
|
|
{
|
|
gpc.Load(p, bt);
|
|
ypc.Load(p, bt);
|
|
}
|
|
|
|
void ElGamalSigPublicKey::SavePrecomputation(BufferedTransformation &bt) const
|
|
{
|
|
gpc.Save(bt);
|
|
ypc.Save(bt);
|
|
}
|
|
|
|
bool ElGamalSigPublicKey::Verify(const byte *message, unsigned int messageLen, const byte *signature)
|
|
{
|
|
assert(messageLen <= MaxMessageLength());
|
|
|
|
Integer m(message, messageLen);
|
|
Integer r(signature, qLen);
|
|
Integer s(signature+qLen, qLen);
|
|
return RawVerify(m, r, s);
|
|
}
|
|
|
|
bool ElGamalSigPublicKey::RawVerify(const Integer &m, const Integer &r, const Integer &s) const
|
|
{
|
|
// check r != 0 && r == (g^s * y^r + m) mod q
|
|
return !!r && r == (gpc.CascadeExponentiate(s, ypc, r) + m) % q;
|
|
}
|
|
|
|
unsigned int ElGamalSigPublicKey::ExponentBitLength() const
|
|
{
|
|
return q.BitCount();
|
|
}
|
|
|
|
// *************************************************************
|
|
|
|
ElGamalSigPrivateKey::ElGamalSigPrivateKey(const Integer &p, const Integer &q, const Integer &g, const Integer &y, const Integer &x)
|
|
: ElGamalSigPublicKey(p, q, g, y), x(x)
|
|
{
|
|
}
|
|
|
|
ElGamalSigPrivateKey::ElGamalSigPrivateKey(RandomNumberGenerator &rng, unsigned int pbits)
|
|
{
|
|
PrimeAndGenerator pg(1, rng, pbits, 2*DiscreteLogWorkFactor(pbits));
|
|
p = pg.Prime();
|
|
q = pg.SubPrime();
|
|
g = pg.Generator();
|
|
x.Randomize(rng, 2, q-2, Integer::ANY);
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
y = gpc.Exponentiate(x);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
qLen = q.ByteCount();
|
|
}
|
|
|
|
ElGamalSigPrivateKey::ElGamalSigPrivateKey(RandomNumberGenerator &rng, const Integer &pIn, const Integer &qIn, const Integer &gIn)
|
|
{
|
|
p = pIn;
|
|
q = qIn;
|
|
g = gIn;
|
|
x.Randomize(rng, 2, q-2, Integer::ANY);
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
y = gpc.Exponentiate(x);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
qLen = q.ByteCount();
|
|
}
|
|
|
|
ElGamalSigPrivateKey::ElGamalSigPrivateKey(BufferedTransformation &bt)
|
|
{
|
|
BERSequenceDecoder seq(bt);
|
|
p.BERDecode(seq);
|
|
q.BERDecode(seq);
|
|
g.BERDecode(seq);
|
|
y.BERDecode(seq);
|
|
x.BERDecode(seq);
|
|
gpc.Precompute(p, g, ExponentBitLength(), 1);
|
|
ypc.Precompute(p, y, ExponentBitLength(), 1);
|
|
qLen = q.ByteCount();
|
|
}
|
|
|
|
void ElGamalSigPrivateKey::DEREncode(BufferedTransformation &bt) const
|
|
{
|
|
DERSequenceEncoder seq(bt);
|
|
p.DEREncode(seq);
|
|
q.DEREncode(seq);
|
|
g.DEREncode(seq);
|
|
y.DEREncode(seq);
|
|
x.DEREncode(seq);
|
|
}
|
|
|
|
void ElGamalSigPrivateKey::Sign(RandomNumberGenerator &rng, const byte *message, unsigned int messageLen, byte *signature)
|
|
{
|
|
assert(messageLen <= MaxMessageLength());
|
|
|
|
Integer m(message, messageLen);
|
|
Integer r;
|
|
Integer s;
|
|
|
|
RawSign(rng, m, r, s);
|
|
r.Encode(signature, qLen);
|
|
s.Encode(signature+qLen, qLen);
|
|
}
|
|
|
|
void ElGamalSigPrivateKey::RawSign(RandomNumberGenerator &rng, const Integer &m, Integer &r, Integer &s) const
|
|
{
|
|
do
|
|
{
|
|
Integer k(rng, 2, q-2, Integer::ANY);
|
|
r = (gpc.Exponentiate(k) + m) % q;
|
|
s = (k - x*r) % q;
|
|
} while (!r); // make sure r != 0
|
|
}
|