// 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 }