PRESENTPRESENT is an Ultra-Lightweight Block Cipher with an 80-bit (10 hex characters) or a 128-bit encryption key (16 hex characters). It uses a 64-bit block size and has a 16-value S-box, and which converts 16x4-bit values. |
Examples
The following are some examples:
- message = "hello", key = 0x01234567890123456789. Try
- message = "hello", key = 0x01234567890123456789012345678901. Try
The following outlines the method:
Code
The following shows a Python version of the code [Ref]
import binascii # Python PRESENT implementation # Version: 1.0 # Date: 13/10/2008 # # ============================================================================= # Copyright (c) 2008 Christophe Oosterlynck ([email protected]) # Philippe Teuwen ([email protected]) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # 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. # ============================================================================= """ PRESENT block cipher implementation USAGE EXAMPLE: --------------- Importing: ----------- >>> from pypresent import Present Encrypting with a 80-bit key: ------------------------------ >>> key = "00000000000000000000".decode('hex') >>> plain = "0000000000000000".decode('hex') >>> cipher = Present(key) >>> encrypted = cipher.encrypt(plain) >>> encrypted.encode('hex') '5579c1387b228445' >>> decrypted = cipher.decrypt(encrypted) >>> decrypted.encode('hex') '0000000000000000' Encrypting with a 128-bit key: ------------------------------- >>> key = "0123456789abcdef0123456789abcdef".decode('hex') >>> plain = "0123456789abcdef".decode('hex') >>> cipher = Present(key) >>> encrypted = cipher.encrypt(plain) >>> encrypted.encode('hex') '0e9d28685e671dd6' >>> decrypted = cipher.decrypt(encrypted) >>> decrypted.encode('hex') '0123456789abcdef' 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): """Create a PRESENT cipher object key: the key as a 128-bit or 80-bit rawstring rounds: the number of rounds as an integer, 32 by default """ self.rounds = rounds if len(key) * 8 == 80: self.roundkeys = generateRoundkeys80(string2number(key),self.rounds) elif len(key) * 8 == 128: self.roundkeys = generateRoundkeys128(string2number(key),self.rounds) else: raise ValueError("Key must be a 128-bit or 80-bit rawstring") def encrypt(self,block): """Encrypt 1 block (8 bytes) Input: plaintext block as raw string Output: ciphertext block as raw string """ state = string2number(block) for i in range (self.rounds-1): state = addRoundKey(state,self.roundkeys[i]) state = sBoxLayer(state) state = pLayer(state) cipher = addRoundKey(state,self.roundkeys[-1]) return number2string_N(cipher,8) def decrypt(self,block): """Decrypt 1 block (8 bytes) Input: ciphertext block as raw string Output: plaintext block as raw string """ state = string2number(block) for i in range (self.rounds-1): state = addRoundKey(state,self.roundkeys[-i-1]) state = pLayer_dec(state) state = sBoxLayer_dec(state) decipher = addRoundKey(state,self.roundkeys[0]) return number2string_N(decipher,8) def get_block_size(self): return 8 # 0 1 2 3 4 5 6 7 8 9 a b c d e f Sbox= [0xc,0x5,0x6,0xb,0x9,0x0,0xa,0xd,0x3,0xe,0xf,0x8,0x4,0x7,0x1,0x2] Sbox_inv = [Sbox.index(x) for x in range(16)] 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] PBox_inv = [PBox.index(x) for x in range(64)] def generateRoundkeys80(key,rounds): """Generate the roundkeys for a 80-bit key Input: key: the key as a 80-bit integer rounds: the number of rounds as an integer Output: list of 64-bit roundkeys as integers""" roundkeys = [] for i in range(1,rounds+1): # (K1 ... K32) # rawkey: used in comments to show what happens at bitlevel # rawKey[0:64] roundkeys.append(key >>16) #1. Shift #rawKey[19:len(rawKey)]+rawKey[0:19] key = ((key & (2**19-1)) << 61) + (key >> 19) #2. SBox #rawKey[76:80] = S(rawKey[76:80]) key = (Sbox[key >> 76] << 76)+(key & (2**76-1)) #3. Salt #rawKey[15:20] ^ i key ^= i << 15 return roundkeys def generateRoundkeys128(key,rounds): """Generate the roundkeys for a 128-bit key Input: key: the key as a 128-bit integer rounds: the number of rounds as an integer Output: list of 64-bit roundkeys as integers""" roundkeys = [] for i in range(1,rounds+1): # (K1 ... K32) # rawkey: used in comments to show what happens at bitlevel roundkeys.append(key >>64) #1. Shift key = ((key & (2**67-1)) << 61) + (key >> 67) #2. SBox key = (Sbox[key >> 124] << 124)+(Sbox[(key >> 120) & 0xF] << 120)+(key & (2**120-1)) #3. Salt #rawKey[62:67] ^ i key ^= i << 62 return roundkeys def addRoundKey(state,roundkey): return state ^ roundkey def sBoxLayer(state): """SBox function for encryption Input: 64-bit integer Output: 64-bit integer""" output = 0 for i in range(16): output += Sbox[( state >> (i*4)) & 0xF] << (i*4) return output def sBoxLayer_dec(state): """Inverse SBox function for decryption Input: 64-bit integer Output: 64-bit integer""" output = 0 for i in range(16): output += Sbox_inv[( state >> (i*4)) & 0xF] << (i*4) return output def pLayer(state): """Permutation layer for encryption Input: 64-bit integer Output: 64-bit integer""" output = 0 for i in range(64): output += ((state >> i) & 0x01) << PBox[i] return output def pLayer_dec(state): """Permutation layer for decryption Input: 64-bit integer Output: 64-bit integer""" output = 0 for i in range(64): output += ((state >> i) & 0x01) << PBox_inv[i] return output def string2number(i): """ Convert a string to a number Input: string (big-endian) Output: long or integer """ val = int.from_bytes(i, byteorder='big') return val def number2string_N(i, N): """Convert a number to a string of fixed size i: long or integer N: length of string Output: string (big-endian) """ s = '%0*x' % (N*2, i) return binascii.unhexlify(str(s)) def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test()
And this is called with:
from pypresent import Present import Padding import sys text="hello" k="00000000000000000000" if (len(sys.argv)>1): text=str(sys.argv[1]) if (len(sys.argv)>2): k=str(sys.argv[2]) text = Padding.appendPadding(text,blocksize=8,mode='CMS') print ('Text:\t'+text) print ('Key:\t'+k) print ('--------') print key = bytes.fromhex(k) cipher = Present(key) encrypted = cipher.encrypt(text.encode()) print ('Cipher:\t\t'+encrypted.hex()) decrypted = cipher.decrypt(encrypted) print ('Decrypted:\t'+decrypted.hex()) print ('Decrypted:\t'+Padding.removePadding(decrypted.decode(),blocksize=8,mode='CMS'))