Permalink
Browse files

Added some test vectors for ElGamal (encryption only). Fixed two smal…

…l bugs in ElGamal code.
  • Loading branch information...
1 parent 52ad7cf commit 7834db2809024536cdfd7fef0b5657dd390bd780 Legrandin committed Apr 26, 2012
Showing with 166 additions and 0 deletions.
  1. +166 −0 lib/Crypto/SelfTest/PublicKey/test_ElGamal.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+#
+# SelfTest/PublicKey/test_ElGamal.py: Self-test for the ElGamal primitive
+#
+# ===================================================================
+# The contents of this file are dedicated to the public domain. To
+# the extent that dedication to the public domain is not available,
+# everyone is granted a worldwide, perpetual, royalty-free,
+# non-exclusive license to exercise all rights associated with the
+# contents of this file for any purpose whatsoever.
+# No rights are reserved.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+# ===================================================================
+
+"""Self-test suite for Crypto.PublicKey.ElGamal"""
+
+__revision__ = "$Id$"
+
+import unittest
+from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
+from Crypto import Random
+from Crypto.PublicKey import ElGamal
+from Crypto.Util.number import *
+from Crypto.Util.py3compat import *
+
+class ElGamalTest(unittest.TestCase):
+
+ #
+ # Test vectors
+ #
+ # There seem to be no real ElGamal test vectors available in the
+ # public domain. The following test vectors have been generated
+ # with libgcrypt 1.5.0.
+ #
+ tvs=[
+ {
+ # 256 bits
+ 'p' :'BA4CAEAAED8CBE952AFD2126C63EB3B345D65C2A0A73D2A3AD4138B6D09BD933',
+ 'g' :'05',
+ 'y' :'60D063600ECED7C7C55146020E7A31C4476E9793BEAED420FEC9E77604CAE4EF',
+ 'x' :'1D391BA2EE3C37FE1BA175A69B2C73A11238AD77675932',
+ 'k' :'F5893C5BAB4131264066F57AB3D8AD89E391A0B68A68A1',
+ 'pt' :'48656C6C6F207468657265',
+ 'ct1':'32BFD5F487966CEA9E9356715788C491EC515E4ED48B58F0F00971E93AAA5EC7',
+ 'ct2':'7BE8FBFF317C93E82FCEF9BD515284BA506603FEA25D01C0CB874A31F315EE68'
+ },
+
+ {
+ # 512 bits
+ 'p' :'F1B18AE9F7B4E08FDA9A04832F4E919D89462FD31BF12F92791A93519F75076D6CE3942689CDFF2F344CAFF0F82D01864F69F3AECF566C774CBACF728B81A227',
+ 'g' :'07',
+ 'y' :'688628C676E4F05D630E1BE39D0066178CA7AA83836B645DE5ADD359B4825A12B02EF4252E4E6FA9BEC1DB0BE90F6D7C8629CABB6E531F472B2664868156E20C',
+ 'x' :'14E60B1BDFD33436C0DA8A22FDC14A2CCDBBED0627CE68',
+ 'k' :'38DBF14E1F319BDA9BAB33EEEADCAF6B2EA5250577ACE7',
+ 'pt' :'48656C6C6F207468657265',
+ 'ct1':'290F8530C2CC312EC46178724F196F308AD4C523CEABB001FACB0506BFED676083FE0F27AC688B5C749AB3CB8A80CD6F7094DBA421FB19442F5A413E06A9772B',
+ 'ct2':'1D69AAAD1DC50493FB1B8E8721D621D683F3BF1321BE21BC4A43E11B40C9D4D9C80DE3AAC2AB60D31782B16B61112E68220889D53C4C3136EE6F6CE61F8A23A0'
+ }
+ ]
+
+ def test_generate_128(self):
+ self._test_random_key(128)
+
+ def test_generate_512(self):
+ self._test_random_key(512)
+
+ def test_encryption(self):
+ for tv in self.tvs:
+ for as_longs in (0,1):
+ d = self.convert_tv(tv, as_longs)
+ key = ElGamal.construct(d['key'])
+ ct = key.encrypt(d['pt'], d['k'])
+ self.assertEquals(ct[0], d['ct1'])
+ self.assertEquals(ct[1], d['ct2'])
+
+ def test_decryption(self):
+ for tv in self.tvs:
+ for as_longs in (0,1):
+ d = self.convert_tv(tv, as_longs)
+ key = ElGamal.construct(d['key'])
+ pt = key.decrypt((d['ct1'], d['ct2']))
+ self.assertEquals(pt, d['pt'])
+
+ def convert_tv(self, tv, as_longs=0):
+ """Convert a test vector from textual form (hexadecimal ascii
+ to either integers or byte strings."""
+ comps = []
+ for c in 'p','g','y','x','k','pt','ct1','ct2':
+ comps += [ tv[c] ]
+ comps = map(a2b_hex, comps)
+ if as_longs:
+ comps = map(bytes_to_long, comps)
+ else:
+ comps = map(bytes_to_long, comps[:4]) + comps[4:]
+ ret = {}
+ ret['key'] = comps[:4]
+ i = 4
+ for v in 'k','pt','ct1','ct2':
+ ret[v] = comps[i]
+ i += 1
+ return ret
+
+ def _test_random_key(self, bits):
+ elgObj = ElGamal.generate(bits, Random.new().read)
+ self._check_private_key(elgObj)
+ self._exercise_primitive(elgObj)
+ pub = elgObj.publickey()
+ self._check_public_key(pub)
+ self._exercise_public_primitive(elgObj)
+
+ def _check_private_key(self, elgObj):
+
+ # Check capabilities
+ self.failUnless(elgObj.has_private())
+ self.failUnless(elgObj.can_sign())
+ self.failUnless(elgObj.can_encrypt())
+
+ # Sanity check key data
+ self.failUnless(1<elgObj.g<(elgObj.p-1))
+ self.assertEquals(pow(elgObj.g, elgObj.p-1, elgObj.p), 1)
+ self.failUnless(1<elgObj.x<(elgObj.p-1))
+ self.assertEquals(pow(elgObj.g, elgObj.x, elgObj.p), elgObj.y)
+
+ def _check_public_key(self, elgObj):
+
+ # Check capabilities
+ self.failIf(elgObj.has_private())
+ self.failUnless(elgObj.can_sign())
+ self.failUnless(elgObj.can_encrypt())
+
+ # Sanity check key data
+ self.failUnless(1<elgObj.g<(elgObj.p-1))
+ self.assertEquals(pow(elgObj.g, elgObj.p-1, elgObj.p), 1)
+
+ def _exercise_primitive(self, elgObj):
+ # Test encryption/decryption
+ plaintext = b("Test")
+ ciphertext = elgObj.encrypt(plaintext, 123456789L)
+ plaintextP = elgObj.decrypt(ciphertext)
+ self.assertEquals(plaintext, plaintextP)
+
+ # Test signature/verification
+ signature = elgObj.sign(plaintext, 987654321L)
+ elgObj.verify(plaintext, signature)
+
+ def _exercise_public_primitive(self, elgObj):
+ plaintext = b("Test")
+ ciphertext = elgObj.encrypt(plaintext, 123456789L)
+
+def get_tests(config={}):
+ tests = []
+ tests += list_test_cases(ElGamalTest)
+ return tests
+
+if __name__ == '__main__':
+ suite = lambda: unittest.TestSuite(get_tests())
+ unittest.main(defaultTest='suite')
+

0 comments on commit 7834db2

Please sign in to comment.