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 new file mode 100644 index 0000000..ac80c0f --- /dev/null +++ b/pycipher/sdes.py @@ -0,0 +1,108 @@ +''' +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 + 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) + + def binStrToInt(self,string): + binArray=map(lambda x:ord(x)-ord('0'),self.removeNonBin(string)) + intNumber = 0 + for x in range(0,len(binArray)): + intNumber += binArray[len(binArray)-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 K0 = 11100011 and K2 = 10001111 + """ + return ( ( self.key | self.key<> self.KEY_LEN+1-n ) & self.RKEY_MASK + + 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) + + :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,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()