Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pycipher/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
108 changes: 108 additions & 0 deletions pycipher/sdes.py
Original file line number Diff line number Diff line change
@@ -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]<<x
return intNumber

def padding(self,plaintext):
plaintext=self.removeNonBin(plaintext)
if len(plaintext)%self.BLOCK_LEN!=0:
for i in xrange(len(plaintext)%self.BLOCK_LEN,self.BLOCK_LEN):
plaintext+='0'
return plaintext

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 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 ) >> 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')
73 changes: 73 additions & 0 deletions tests/test_sdes.py
Original file line number Diff line number Diff line change
@@ -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()