1 # Python PRESENT implementation
5 # =============================================================================
6 # Copyright (c) 2008 Christophe Oosterlynck <christophe.oosterlynck_AT_gmail.com>
7 # & NXP ( Philippe Teuwen <philippe.teuwen_AT_nxp.com> )
9 # Permission is hereby granted, free of charge, to any person obtaining a copy
10 # of this software and associated documentation files (the "Software"), to deal
11 # in the Software without restriction, including without limitation the rights
12 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 # copies of the Software, and to permit persons to whom the Software is
14 # furnished to do so, subject to the following conditions:
16 # The above copyright notice and this permission notice shall be included in
17 # all copies or substantial portions of the Software.
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 # =============================================================================
28 """ PRESENT block cipher implementation
34 >>> from pypresent import Present
36 Encrypting with a 80-bit key:
37 ------------------------------
38 >>> key = "00000000000000000000".decode('hex')
39 >>> plain = "0000000000000000".decode('hex')
40 >>> cipher = Present(key)
41 >>> encrypted = cipher.encrypt(plain)
42 >>> encrypted.encode('hex')
44 >>> decrypted = cipher.decrypt(encrypted)
45 >>> decrypted.encode('hex')
48 Encrypting with a 128-bit key:
49 -------------------------------
50 >>> key = "0123456789abcdef0123456789abcdef".decode('hex')
51 >>> plain = "0123456789abcdef".decode('hex')
52 >>> cipher = Present(key)
53 >>> encrypted = cipher.encrypt(plain)
54 >>> encrypted.encode('hex')
56 >>> decrypted = cipher.decrypt(encrypted)
57 >>> decrypted.encode('hex')
60 fully based on standard specifications: http://www.crypto.ruhr-uni-bochum.de/imperia/md/content/texte/publications/conferences/present_ches2007.pdf
61 test vectors: http://www.crypto.ruhr-uni-bochum.de/imperia/md/content/texte/publications/conferences/slides/present_testvectors.zip
65 def __init__(self
,key
,rounds
=32):
66 """Create a PRESENT cipher object
68 key: the key as a 128-bit or 80-bit rawstring
69 rounds: the number of rounds as an integer, 32 by default
72 if len(key
) * 8 == 80:
73 self
.roundkeys
= generateRoundkeys80(string2number(key
),self
.rounds
)
74 elif len(key
) * 8 == 128:
75 self
.roundkeys
= generateRoundkeys128(string2number(key
),self
.rounds
)
77 raise ValueError, "Key must be a 128-bit or 80-bit rawstring"
79 def encrypt(self
,block
):
80 """Encrypt 1 block (8 bytes)
82 Input: plaintext block as raw string
83 Output: ciphertext block as raw string
85 state
= string2number(block
)
86 for i
in xrange (self
.rounds
-1):
87 state
= addRoundKey(state
,self
.roundkeys
[i
])
88 state
= sBoxLayer(state
)
90 cipher
= addRoundKey(state
,self
.roundkeys
[-1])
91 return number2string_N(cipher
,8)
93 def decrypt(self
,block
):
94 """Decrypt 1 block (8 bytes)
96 Input: ciphertext block as raw string
97 Output: plaintext block as raw string
99 state
= string2number(block
)
100 for i
in xrange (self
.rounds
-1):
101 state
= addRoundKey(state
,self
.roundkeys
[-i
-1])
102 state
= pLayer_dec(state
)
103 state
= sBoxLayer_dec(state
)
104 decipher
= addRoundKey(state
,self
.roundkeys
[0])
105 return number2string_N(decipher
,8)
107 def get_block_size(self
):
110 # 0 1 2 3 4 5 6 7 8 9 a b c d e f
111 Sbox
= [0xc,0x5,0x6,0xb,0x9,0x0,0xa,0xd,0x3,0xe,0xf,0x8,0x4,0x7,0x1,0x2]
112 Sbox_inv
= [Sbox
.index(x
) for x
in xrange(16)]
113 PBox
= [0,16,32,48,1,17,33,49,2,18,34,50,3,19,35,51,
114 4,20,36,52,5,21,37,53,6,22,38,54,7,23,39,55,
115 8,24,40,56,9,25,41,57,10,26,42,58,11,27,43,59,
116 12,28,44,60,13,29,45,61,14,30,46,62,15,31,47,63]
117 PBox_inv
= [PBox
.index(x
) for x
in xrange(64)]
119 def generateRoundkeys80(key
,rounds
):
120 """Generate the roundkeys for a 80-bit key
123 key: the key as a 80-bit integer
124 rounds: the number of rounds as an integer
125 Output: list of 64-bit roundkeys as integers"""
127 for i
in xrange(1,rounds
+1): # (K1 ... K32)
128 # rawkey: used in comments to show what happens at bitlevel
130 roundkeys
.append(key
>>16)
132 #rawKey[19:len(rawKey)]+rawKey[0:19]
133 key
= ((key
& (2**19-1)) << 61) + (key
>> 19)
135 #rawKey[76:80] = S(rawKey[76:80])
136 key
= (Sbox
[key
>> 76] << 76)+(key
& (2**76-1))
142 def generateRoundkeys128(key
,rounds
):
143 """Generate the roundkeys for a 128-bit key
146 key: the key as a 128-bit integer
147 rounds: the number of rounds as an integer
148 Output: list of 64-bit roundkeys as integers"""
150 for i
in xrange(1,rounds
+1): # (K1 ... K32)
151 # rawkey: used in comments to show what happens at bitlevel
152 roundkeys
.append(key
>>64)
154 key
= ((key
& (2**67-1)) << 61) + (key
>> 67)
156 key
= (Sbox
[key
>> 124] << 124)+(Sbox
[(key
>> 120) & 0xF] << 120)+(key
& (2**120-1))
162 def addRoundKey(state
,roundkey
):
163 return state ^ roundkey
165 def sBoxLayer(state
):
166 """SBox function for encryption
168 Input: 64-bit integer
169 Output: 64-bit integer"""
173 output
+= Sbox
[( state
>> (i
*4)) & 0xF] << (i
*4)
176 def sBoxLayer_dec(state
):
177 """Inverse SBox function for decryption
179 Input: 64-bit integer
180 Output: 64-bit integer"""
183 output
+= Sbox_inv
[( state
>> (i
*4)) & 0xF] << (i
*4)
187 """Permutation layer for encryption
189 Input: 64-bit integer
190 Output: 64-bit integer"""
193 output
+= ((state
>> i
) & 0x01) << PBox
[i
]
196 def pLayer_dec(state
):
197 """Permutation layer for decryption
199 Input: 64-bit integer
200 Output: 64-bit integer"""
203 output
+= ((state
>> i
) & 0x01) << PBox_inv
[i
]
206 def string2number(i
):
207 """ Convert a string to a number
209 Input: string (big-endian)
210 Output: long or integer
212 return int(i
.encode('hex'),16)
214 def number2string_N(i
, N
):
215 """Convert a number to a string of fixed size
219 Output: string (big-endian)
221 s
= '%0*x' % (N
*2, i
)
222 return s
.decode('hex')
228 if __name__
== "__main__":