Difference between revisions of "Present python implementation"

From YobiWiki
Jump to navigation Jump to search
m (code update)
m
Line 1: Line 1:
 
what should be working (only tested with 1 or 2 test vectors yet):
 
what should be working (only tested with 1 or 2 test vectors yet):
 
* calculating round keys
 
* calculating round keys
  +
** to have the same results as the reference C implementation, salting the roundkeys should only XOR 5 bits with roundkey when using 128bits key (line 95 in code) but should XOR with no 5bits limit when using 80bits key (line 76 in code). But the standard describes that it should be limited to 5 bits for both key generators...
 
* encrypting a block
 
* encrypting a block
 
* decrypting a block
 
* decrypting a block
* should support rounds up to 65534 (tested with 32 and 64 rounds only)
+
* should support rounds up to 65534 (tested with 32, 64, 128 and 65534 rounds)
 
** decryption testvectors have errors: the sbox on decryption behaves like the inverse of the p-box... every S-Box value is incorrect in the testvectors.<br>Example:
 
** decryption testvectors have errors: the sbox on decryption behaves like the inverse of the p-box... every S-Box value is incorrect in the testvectors.<br>Example:
 
Round 1
 
Round 1
Line 20: Line 21:
 
def __init__(self,key,rounds=32):
 
def __init__(self,key,rounds=32):
 
"""Generating roundkeys
 
"""Generating roundkeys
  +
 
 
When a Present class initialized, the roundkeys will be generated.
 
When a Present class initialized, the roundkeys will be generated.
 
You can supply the key as a 128bit or 80bit rawstring.
 
You can supply the key as a 128bit or 80bit rawstring.
Line 35: Line 36:
 
def encrypt(self,block):
 
def encrypt(self,block):
 
"""Encrypting 1 block (8 bytes)
 
"""Encrypting 1 block (8 bytes)
  +
 
 
Supply the plaintext block as a raw string and the raw
 
Supply the plaintext block as a raw string and the raw
 
ciphertext will be returned.
 
ciphertext will be returned.
Line 49: Line 50:
 
def decrypt(self,block):
 
def decrypt(self,block):
 
"""Decrypting 1 block (8 bytes)
 
"""Decrypting 1 block (8 bytes)
  +
 
 
Supply the ciphertext block as a raw string and the raw
 
Supply the ciphertext block as a raw string and the raw
 
plaintext will be returned.
 
plaintext will be returned.
Line 62: Line 63:
   
 
def get_block_size(self):
 
def get_block_size(self):
return 16
+
return 8
   
 
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
 
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
Line 73: Line 74:
 
def generateRoundkeys80(key,rounds):
 
def generateRoundkeys80(key,rounds):
 
"""Generate the roundkeys for a 80 bit key
 
"""Generate the roundkeys for a 80 bit key
  +
 
 
Give a 80bit hex string as input and get a list of roundkeys in return"""
 
Give a 80bit hex string as input and get a list of roundkeys in return"""
 
roundkeys = []
 
roundkeys = []
 
for i in range(1,rounds+1): # (K0 ... K32)
 
for i in range(1,rounds+1): # (K0 ... K32)
print i
 
 
# rawKey[0:63]
 
# rawKey[0:63]
 
roundkeys.append(("%x" % (int(key,16) >>16 )).zfill(64/4))
 
roundkeys.append(("%x" % (int(key,16) >>16 )).zfill(64/4))
Line 86: Line 86:
 
#rawKey[76:79] = S(rawKey[76:79])
 
#rawKey[76:79] = S(rawKey[76:79])
 
key = SBox[int(key[0],16)]+key[1:20]
 
key = SBox[int(key[0],16)]+key[1:20]
#print "sbox"
 
#print key
 
 
#3. Salt
 
#3. Salt
 
#rawKey[15:19] ^ i
 
#rawKey[15:19] ^ i
 
temp = (int(key,16) >> 15)
 
temp = (int(key,16) >> 15)
temp = (temp ^ i )
+
temp = temp ^ i
 
key = ( int(key,16) & (pow(2,15)-1) ) + (temp << 15)
 
key = ( int(key,16) & (pow(2,15)-1) ) + (temp << 15)
 
key = ("%x" % key).zfill(80/4)
 
key = ("%x" % key).zfill(80/4)
Line 98: Line 96:
 
def generateRoundkeys128(key,rounds):
 
def generateRoundkeys128(key,rounds):
 
"""Generate the roundkeys for a 128 bit key
 
"""Generate the roundkeys for a 128 bit key
  +
 
Give a 80bit hex string as input and get a list of roundkeys in return"""
+
Give a 128bit hex string as input and get a list of roundkeys in return"""
 
roundkeys = []
 
roundkeys = []
 
for i in range(1,rounds+1): # (K0 ... K32)
 
for i in range(1,rounds+1): # (K0 ... K32)
Line 109: Line 107:
 
#3. Salt
 
#3. Salt
 
#rawKey[15:19] ^ i
 
#rawKey[15:19] ^ i
temp = (int(key,16) >> 62) & (pow(2,5)-1) # rawKey[15:19]
+
temp = (int(key,16) >> 62)
temp = temp ^ i
+
temp = temp ^ (i%32)
key = ( int(key,16) & (pow(2,62)-1) ) + (temp << 62) + ( (int(key,16) >> 67) <<67 )
+
key = ( int(key,16) & (pow(2,62)-1) ) + (temp << 62)
key = "%x" % key
+
key = ("%x" % key).zfill(128/4)
 
return roundkeys
 
return roundkeys
   
Line 120: Line 118:
 
def sBoxLayer(state):
 
def sBoxLayer(state):
 
"""SBox function for encryption
 
"""SBox function for encryption
  +
 
 
Takes a hex string as input and will output a hex string"""
 
Takes a hex string as input and will output a hex string"""
 
output =''
 
output =''
Line 129: Line 127:
 
def sBoxLayer_dec(state):
 
def sBoxLayer_dec(state):
 
"""Inverse SBox function for decryption
 
"""Inverse SBox function for decryption
  +
 
 
Takes a hex string as input and will output a hex string"""
 
Takes a hex string as input and will output a hex string"""
 
output =''
 
output =''
Line 138: Line 136:
 
def pLayer(state):
 
def pLayer(state):
 
"""Permutation layer for encryption
 
"""Permutation layer for encryption
  +
 
 
Takes a 64bit hex string as input and will output a 64bit hex string"""
 
Takes a 64bit hex string as input and will output a 64bit hex string"""
 
output = ''
 
output = ''
Line 148: Line 146:
 
def pLayer_dec(state):
 
def pLayer_dec(state):
 
"""Permutation layer for decryption
 
"""Permutation layer for decryption
  +
 
 
Takes a 64bit hex string as input and will output a 64bit hex string"""
 
Takes a 64bit hex string as input and will output a 64bit hex string"""
 
output = ''
 
output = ''

Revision as of 13:39, 6 October 2008

what should be working (only tested with 1 or 2 test vectors yet):

  • calculating round keys
    • to have the same results as the reference C implementation, salting the roundkeys should only XOR 5 bits with roundkey when using 128bits key (line 95 in code) but should XOR with no 5bits limit when using 80bits key (line 76 in code). But the standard describes that it should be limited to 5 bits for both key generators...
  • encrypting a block
  • decrypting a block
  • should support rounds up to 65534 (tested with 32, 64, 128 and 65534 rounds)
    • decryption testvectors have errors: the sbox on decryption behaves like the inverse of the p-box... every S-Box value is incorrect in the testvectors.
      Example:
Round 1
Subkey: 6dab31744f41d700
Text after...
...Key-Xor: 38d2f04c34635345 
.....P-Box: 45ef82118f2845a3 
.....S-Box: 38d2f04c34635345

{{#fileanchor: pypresent.py}}

# fully based on standard specifications: http://www.crypto.ruhr-uni-bochum.de/imperia/md/content/texte/publications/conferences/present_ches2007.pdf
# test vectors: http://www.crypto.ruhr-uni-bochum.de/imperia/md/content/texte/publications/conferences/slides/present_testvectors.zip

class Present:

        def __init__(self,key,rounds=32):
                """Generating roundkeys

                When a Present class initialized, the roundkeys will be generated.
                You can supply the key as a 128bit or 80bit rawstring.
                """
                self.rounds = rounds
                self.key = key.encode('hex')
                if len(self.key) == 80/4:
                        self.roundkeys = generateRoundkeys80(self.key,self.rounds)
                elif len(self.key) == 128/4:
                        self.roundkeys = generateRoundkeys128(self.key,self.rounds)
                else:
                        pass

        def encrypt(self,block):
                """Encrypting 1 block (8 bytes)

                Supply the plaintext block as a raw string and the raw
                ciphertext will be returned.
                """
                state = block.encode('hex')
                for i in range (1,self.rounds):
                        state = addRoundKey(state,self.roundkeys[i-1])
                        state = sBoxLayer(state)
                        state = pLayer(state)
                cipher = addRoundKey(state,self.roundkeys[self.rounds-1])
                return cipher.decode('hex')

        def decrypt(self,block):
                """Decrypting 1 block (8 bytes)

                Supply the ciphertext block as a raw string and the raw
                plaintext will be returned.
                """
                state = block.encode('hex')
                for i in range (1,self.rounds):
                        state = addRoundKey(state,self.roundkeys[self.rounds-i])
                        state = pLayer_dec(state)
                        state = sBoxLayer_dec(state)
                decipher = addRoundKey(state,self.roundkeys[0])
                return decipher.decode('hex')

        def get_block_size(self):
                return 8

#        0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
SBox = ['c','5','6','b','9','0','a','d','3','e','f','8','4','7','1','2']
PBox = [0,16,32,48,1,17,33,49,2,18,34,50,3,19,35,51,
        4,20,36,52,5,21,37,53,6,22,38,54,7,23,39,55,
        8,24,40,56,9,25,41,57,10,26,42,58,11,27,43,59,
        12,28,44,60,13,29,45,61,14,30,46,62,15,31,47,63]

def generateRoundkeys80(key,rounds):
        """Generate the roundkeys for a 80 bit key

        Give a 80bit hex string as input and get a list of roundkeys in return"""
        roundkeys = []
        for i in range(1,rounds+1): # (K0 ... K32)
                # rawKey[0:63]
                roundkeys.append(("%x" % (int(key,16) >>16 )).zfill(64/4))
                #1. Shift
                #rawKey[19:(len(rawKey)-1)]+rawKey[0:18]
                key = ("%x" % ( ((int(key,16) & (pow(2,19)-1)) << 61) + (int(key,16) >> 19))).zfill(80/4)
                #2. SBox
                #rawKey[76:79] = S(rawKey[76:79])
                key = SBox[int(key[0],16)]+key[1:20]
                #3. Salt
                #rawKey[15:19] ^ i
                temp = (int(key,16) >> 15)
                temp = temp ^ i
                key = ( int(key,16) & (pow(2,15)-1) ) + (temp << 15)
                key = ("%x" % key).zfill(80/4)
        return roundkeys

def generateRoundkeys128(key,rounds):
        """Generate the roundkeys for a 128 bit key

        Give a 128bit hex string as input and get a list of roundkeys in return"""
        roundkeys = []
        for i in range(1,rounds+1): # (K0 ... K32)
                roundkeys.append(("%x" % (int(key,16) >>64)).zfill(64/4))
                #1. Shift
                key = ("%x" % ( ((int(key,16) & (pow(2,67)-1)) << 61) + (int(key,16) >> 67))).zfill(128/4)
                #2. SBox
                key = SBox[int(key[0],16)]+SBox[int(key[1],16)]+key[2:]
                #3. Salt
                #rawKey[15:19] ^ i
                temp = (int(key,16) >> 62)
                temp = temp ^ (i%32)
                key = ( int(key,16) & (pow(2,62)-1) ) + (temp << 62)
                key = ("%x" % key).zfill(128/4)
        return roundkeys

def addRoundKey(state,roundkey):
        return ( "%x" % ( int(state,16) ^ int(roundkey,16) ) ).zfill(16)

def sBoxLayer(state):
        """SBox function for encryption

        Takes a hex string as input and will output a hex string"""
        output =''
        for i in range(len(state)):
                output += SBox[int(state[i],16)]
        return output

def sBoxLayer_dec(state):
        """Inverse SBox function for decryption

        Takes a hex string as input and will output a hex string"""
        output =''
        for i in range(len(state)):
                output += hex( SBox.index(state[i]) )[2:]
        return output

def pLayer(state):
        """Permutation layer for encryption

        Takes a 64bit hex string as input and will output a 64bit hex string"""
        output = ''
        state_bin = bin(int(state,16)).zfill(64)[::-1][0:64]
        for i in range(64):
                output += state_bin[PBox.index(i)]
        return ("%x" % int(output[::-1],2)).zfill(16)

def pLayer_dec(state):
        """Permutation layer for decryption

        Takes a 64bit hex string as input and will output a 64bit hex string"""
        output = ''
        state_bin = bin(int(state,16)).zfill(64)[::-1][0:64]
        for i in range(64):
                output += state_bin[PBox[i]]
        return ("%x" % int(output[::-1],2)).zfill(16)

def bin(a):
        """Convert an integer to a bin string (1 char represents 1 bit)"""
        #http://wiki.python.org/moin/BitManipulation
        s=''
        t={'0':'000','1':'001','2':'010','3':'011','4':'100','5':'101','6':'110','7':'111'}
        for c in oct(a).rstrip('L')[1:]:
                s+=t[c]
        return s

Download code: [{{#filelink: pypresent.py}} pypresent.py]