diff options
-rw-r--r-- | lib/Crypto/SelfTest/PublicKey/test_ElGamal.py | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/Crypto/SelfTest/PublicKey/test_ElGamal.py b/lib/Crypto/SelfTest/PublicKey/test_ElGamal.py new file mode 100644 index 0000000..f7443d9 --- /dev/null +++ b/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') + |