From 429e3bf34e9487c3c5add3c043465ae533933398 Mon Sep 17 00:00:00 2001 From: jonajames Date: Tue, 12 Sep 2017 11:09:07 +0200 Subject: [PATCH 1/2] Added SDES chypher, missing dechipher function --- pycipher/sdes.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 pycipher/sdes.py diff --git a/pycipher/sdes.py b/pycipher/sdes.py new file mode 100644 index 0000000..1c1d1c7 --- /dev/null +++ b/pycipher/sdes.py @@ -0,0 +1,94 @@ +''' +implements sdes cipher +Author: Giovanni Milani +Created: 2017-09-12 +''' +from pycipher.base import Cipher + +#################################################################################### +class Sdes(Cipher): + """The S-DES Cipher is a simplified version of DES chiper useful for educational purpose. + It is a symmetric-key block cypher with 9-bit key and 12-bit block. + More details can be found online and/or here: https://www.cs.uri.edu/cryptography/dessimplified.htm + + :param key: The binary key, a 10-chart string containing only "1" and "0". + """ + KEY_LEN=9 + KEY_MASK=0b111111111 + RKEY_MASK=0b11111111 + BLOCK_LEN=12 + + def removeNonBin(self,string): + return filter(lambda x:(x=="0" or x=="1"),string) + + def binStrToInt(self,string): + binString=removeNonBin(string) + intNumber = 0 + for x in range(0,len(binString)): + intNumber += binString[len(binString)-x-1]<>1)+((bit6&0b000100)<<3)+((bit6&0b000100)<<1)+(bit6&0b000011) + + def sbox(self,bit8): + sbox=[ + [ + [0b101,0b010,0b001,0b110,0b011,0b100,0b111,0b000], + [0b001,0b100,0b110,0b010,0b000,0b111,0b101,0b011], + ], + [ + [0b100,0b000,0b110,0b101,0b111,0b001,0b011,0b010], + [0b101,0b011,0b000,0b111,0b110,0b010,0b001,0b100] + ], + ] + return ( sbox [0] [(bit8&0b10000000)>>7][(bit8&0b01110000)>>4] <<3 ) | sbox [1] [(bit8&0b00001000)>>3][(bit8&0b00000111)>>0] + + def roundKey(self,n): + """ The key has 9 bits. The key, Ki, for the ith round of encryption is obtained by using 8 bits of K, starting with the ith bit. + Example: If K = 111000111 Then K1 = 11100011 and K3 = 10001111 + """ + return (self.key<>(KEY_LEN-n))&KEY_MASK + + def enchiper(self,string): + """Encipher string using Sdes cipher according to initialised key. + Input string is treated as a binary digit, all characters are removed except for "0" and "1". + Example:: + + ciphertext = Sdes(key='111000111').encipher(plaintext) + + :param string: The string to encipher. + :returns: The enciphered string. + """ + blocks=self.toblocks(string) + chipertext="" + for i in blocks: + r=i&0b111111 + l=i>>6&0b111111 + for x in xrange(0,3): + tmp=self.sbox(self.expansion(r) ^ self.roundkey(x)) ^ l + l=r + r=tmp + chipertext+="{0:06b}".format(l)+"{0:06b}".format(r)+" " + return chipertex + +if __name__ == '__main__': + print('use "import pycipher" to access functions') \ No newline at end of file From a3b860f4dcc371e60f6cc5f8bc13dd82f669ec83 Mon Sep 17 00:00:00 2001 From: jonajames Date: Wed, 13 Sep 2017 10:08:36 +0200 Subject: [PATCH 2/2] implemented dechipher, created tests --- pycipher/__init__.py | 3 +- pycipher/sdes.py | 128 ++++++++++++++++++++++++------------------- tests/test_sdes.py | 73 ++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 58 deletions(-) create mode 100644 tests/test_sdes.py diff --git a/pycipher/__init__.py b/pycipher/__init__.py index a581bdf..b0a9c88 100644 --- a/pycipher/__init__.py +++ b/pycipher/__init__.py @@ -19,10 +19,11 @@ from pycipher.railfence import Railfence from pycipher.porta import Porta from pycipher.fracmorse import FracMorse +from pycipher.sdes import Sdes import pycipher.util #from lorentz import Lorentz as Lorentz __all__=["Atbash","ADFGX","ADFGVX","SimpleSubstitution","Caesar","Affine","Enigma","Autokey","Beaufort", "Bifid","ColTrans","Gronsfeld","Foursquare","M209","PolybiusSquare","Playfair","Vigenere","Rot13","util", - "Railfence","Porta","FracMorse"] + "Railfence","Porta","FracMorse","Sdes"] __version__ = "0.5.1" diff --git a/pycipher/sdes.py b/pycipher/sdes.py index 1c1d1c7..ac80c0f 100644 --- a/pycipher/sdes.py +++ b/pycipher/sdes.py @@ -8,7 +8,7 @@ #################################################################################### class Sdes(Cipher): """The S-DES Cipher is a simplified version of DES chiper useful for educational purpose. - It is a symmetric-key block cypher with 9-bit key and 12-bit block. + It is a symmetric-key block cypher with 9-bit key and 12-bit block. More details can be found online and/or here: https://www.cs.uri.edu/cryptography/dessimplified.htm :param key: The binary key, a 10-chart string containing only "1" and "0". @@ -17,60 +17,61 @@ class Sdes(Cipher): KEY_MASK=0b111111111 RKEY_MASK=0b11111111 BLOCK_LEN=12 + ROUND_NUM=2 + + def __init__(self,key='0000000000'): + self.key=self.binStrToInt(key)&self.KEY_MASK def removeNonBin(self,string): - return filter(lambda x:(x=="0" or x=="1"),string) + return filter(lambda x:(x=="0" or x=="1"),string) def binStrToInt(self,string): - binString=removeNonBin(string) - intNumber = 0 - for x in range(0,len(binString)): - intNumber += binString[len(binString)-x-1]<>1)+((bit6&0b000100)<<3)+((bit6&0b000100)<<1)+(bit6&0b000011) + def toBlocks(self,plaintext): + plaintext=self.padding(plaintext) + chopped=[plaintext[i:i+self.BLOCK_LEN] for i in range(0, len(plaintext), self.BLOCK_LEN)] + return map(lambda x:self.binStrToInt(x),chopped) + + def expansion(self,bit6): + """ 6bit input: 1 2 3 4 5 6 + 8bit output: 1 2 4 3 4 3 5 6 + """ + return ((bit6&0b110000)<<2)+((bit6&0b001000)<<1)+((bit6&0b001000)>>1)+((bit6&0b000100)<<3)+((bit6&0b000100)<<1)+(bit6&0b000011) - def sbox(self,bit8): - sbox=[ - [ - [0b101,0b010,0b001,0b110,0b011,0b100,0b111,0b000], - [0b001,0b100,0b110,0b010,0b000,0b111,0b101,0b011], - ], - [ - [0b100,0b000,0b110,0b101,0b111,0b001,0b011,0b010], - [0b101,0b011,0b000,0b111,0b110,0b010,0b001,0b100] - ], - ] - return ( sbox [0] [(bit8&0b10000000)>>7][(bit8&0b01110000)>>4] <<3 ) | sbox [1] [(bit8&0b00001000)>>3][(bit8&0b00000111)>>0] + def sbox(self,bit8): + sbox=[ + [ + [0b101,0b010,0b001,0b110,0b011,0b100,0b111,0b000], + [0b001,0b100,0b110,0b010,0b000,0b111,0b101,0b011], + ], + [ + [0b100,0b000,0b110,0b101,0b111,0b001,0b011,0b010], + [0b101,0b011,0b000,0b111,0b110,0b010,0b001,0b100] + ], + ] + return ( sbox [0] [(bit8&0b10000000)>>7][(bit8&0b01110000)>>4] <<3 ) | sbox [1] [(bit8&0b00001000)>>3][(bit8&0b00000111)>>0] - def roundKey(self,n): - """ The key has 9 bits. The key, Ki, for the ith round of encryption is obtained by using 8 bits of K, starting with the ith bit. - Example: If K = 111000111 Then K1 = 11100011 and K3 = 10001111 + def roundKey(self,n): + """ The key has 9 bits. The key, Ki, for the ith round of encryption is obtained by using 8 bits of K, starting with the ith bit. + Example: If K = 111000111 Then K0 = 11100011 and K2 = 10001111 """ - return (self.key<>(KEY_LEN-n))&KEY_MASK + return ( ( self.key | self.key<> self.KEY_LEN+1-n ) & self.RKEY_MASK - def enchiper(self,string): - """Encipher string using Sdes cipher according to initialised key. - Input string is treated as a binary digit, all characters are removed except for "0" and "1". + def encipher(self,string): + """Encipher string using Sdes cipher according to initialised key. + Input string is treated as a binary digit, all characters are removed except for "0" and "1". Example:: ciphertext = Sdes(key='111000111').encipher(plaintext) @@ -78,17 +79,30 @@ def enchiper(self,string): :param string: The string to encipher. :returns: The enciphered string. """ - blocks=self.toblocks(string) - chipertext="" - for i in blocks: - r=i&0b111111 - l=i>>6&0b111111 - for x in xrange(0,3): - tmp=self.sbox(self.expansion(r) ^ self.roundkey(x)) ^ l - l=r - r=tmp - chipertext+="{0:06b}".format(l)+"{0:06b}".format(r)+" " - return chipertex + blocks=self.toBlocks(string) + chipertext="" + for i in blocks: + r=i&0b111111 + l=i>>6&0b111111 + for x in xrange(0,self.ROUND_NUM): + tmp=self.sbox(self.expansion(r) ^ self.roundKey(x)) ^ l + l=r + r=tmp + chipertext+="{0:06b}".format(r)+"{0:06b}".format(l) + return chipertext + + def decipher(self,string): + blocks=self.toBlocks(string) + chipertext="" + for i in blocks: + r=i&0b111111 + l=i>>6&0b111111 + for x in xrange(0,self.ROUND_NUM): + tmp=self.sbox(self.expansion(r) ^ self.roundKey(self.ROUND_NUM-1-x)) ^ l + l=r + r=tmp + chipertext+="{0:06b}".format(r)+"{0:06b}".format(l) + return chipertext if __name__ == '__main__': print('use "import pycipher" to access functions') \ No newline at end of file diff --git a/tests/test_sdes.py b/tests/test_sdes.py new file mode 100644 index 0000000..849f628 --- /dev/null +++ b/tests/test_sdes.py @@ -0,0 +1,73 @@ +from pycipher import Sdes +import unittest + +class TestSdes(unittest.TestCase): + + def test_removeNonBin(self): + tests = ("","0","1","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789","111000111") + out = ("","0","1","","01","111000111") + sdes = Sdes("111000111") + for i,test in enumerate(tests): + self.assertEqual(sdes.removeNonBin(test),out[i]) + + def test_binStrToInt(self): + tests = ("","0","1","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789","111000111") + out = (0,0,1,0,1,0b111000111) + sdes = Sdes("111000111") + for i,test in enumerate(tests): + self.assertEqual(sdes.binStrToInt(test),out[i]) + + def test_padding(self): + tests = ("","0","1","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789","111000111") + out = ("","000000000000","100000000000","","010000000000","111000111000") + sdes = Sdes("111000111") + for i,test in enumerate(tests): + self.assertEqual(sdes.padding(test),out[i]) + + def test_toBlocks(self): + tests = ("","0","1","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz","abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789","111000111", + "111000111000111000111000") + out = ([],[0b000000000000],[0b100000000000],[],[0b010000000000],[0b111000111000],[0b111000111000,0b111000111000]) + sdes = Sdes("111000111") + for i,test in enumerate(tests): + self.assertEqual(sdes.toBlocks(test),out[i]) + + def test_expansion(self): + tests = (0b000001,0b000010,0b000100,0b001000,0b010000,0b100000,0b110101,0b001010) + out = (0b00000001,0b00000010,0b00101000,0b00010100,0b01000000,0b10000000,0b11101001,0b00010110) + sdes = Sdes("111000111") + for i,test in enumerate(tests): + self.assertEqual(sdes.expansion(test),out[i]) + + def test_sbox(self): + tests = (0b0001010,0b11010001) + out = (0b101000,0b111000) + sdes = Sdes("111000111") + for i,test in enumerate(tests): + self.assertEqual(sdes.sbox(test),out[i]) + + def test_roundKey(self): + tests = (0,1,2,9) + out = (0b11100011,0b11000111,0b10001111,0b11100011) + sdes = Sdes("111000111") + for i,test in enumerate(tests): + self.assertEqual(sdes.roundKey(test),out[i]) + + def test_encipher(self): + keys = ('111000111','000000000') + plaintext = '100010110101' + ciphertext = ('001101001010','100100001001') + for i,key in enumerate(keys): + chip = Sdes(key).encipher(plaintext) + self.assertEqual(chip.upper(), ciphertext[i].upper()) + + def test_decipher(self): + keys = ('111000111','000000000') + ciphertext = '001101001010' + plaintext = ('100010110101','001011011110') + for i,key in enumerate(keys): + chip = Sdes(key).decipher(ciphertext) + self.assertEqual(chip.upper(), plaintext[i].upper()) + +if __name__ == '__main__': + unittest.main()