mode constants are only in blockcipher
[python-cryptoplus.git] / src / Cipher / blockcipher.py
blob772c764ba7efdaada08ea3a84940a7b5def036f1
1 from ..Util import util
2 from array import array
3 import struct
4 from ..Util.padding import Padding
6 MODE_ECB = 1
7 MODE_CBC = 2
8 MODE_CFB = 3
9 MODE_OFB = 5
10 MODE_CTR = 6
11 MODE_XTS = 7
12 MODE_CMAC = 8
14 class BlockCipher():
15 """ Base class for all blockciphers
16 """
18 def __init__(self,key,mode,IV,counter):
19 # Cipher classes inhereting from this one take care of:
20 # self.blocksize
21 # self.cipher
22 self.key = key
23 self.mode = mode
24 self.cache = ''
25 self.ed = None
26 if mode == MODE_ECB:
27 self.chain = ECB(self.cipher, self.blocksize)
28 elif mode == MODE_CBC:
29 assert IV <> None, "Provide an IV!"
30 self.chain = CBC(self.cipher, self.blocksize,IV)
31 elif mode == MODE_CFB:
32 assert IV <> None, "Provide an IV!"
33 self.chain = CFB(self.cipher, self.blocksize,IV)
34 elif mode == MODE_OFB:
35 assert IV <> None, "Provide an IV!"
36 self.chain = OFB(self.cipher, self.blocksize,IV)
37 elif mode == MODE_CTR:
38 assert counter != None
39 self.chain = CTR(self.cipher,self.blocksize,counter)
40 elif mode == MODE_XTS:
41 assert self.blocksize == 16
42 self.chain = XTS(self.cipher, self.cipher2)
43 elif mode == MODE_CMAC:
44 assert self.blocksize in (8,16)
45 self.chain = CMAC(self.cipher,self.blocksize)
47 def encrypt(self,plaintext,n=''):
48 """Encrypt some plaintext
50 encrypt(plaintext,n='')
51 plaintext = a string of binary data
52 n = the 'tweak' value when the chaining mode is XTS
54 The encrypt function will encrypt the supplied plaintext. When the supplied plaintext is not a multiple of the blocksize of the cipher,
55 then the remaining plaintext will be cached. The next time the encrypt function is called with some plaintext, the new plaintext will be concatenated
56 to the cache and then cache+plaintext will be encrypted.
58 When the chaining mode allows the cipher to act as a stream cipher (CFB, OFB, CTR), the encrypt function will always encrypt all of the
59 supplied plaintext immediately. No cache will be kept.
61 For XTS the behavious is somewhat different: it needs the whole block of plaintext to be supplied at once. Every encrypt function called on a XTS cipher
62 will output an encrypted block based on the current supplied plaintext block.
63 """
64 #self.ed = 'e' if chain is encrypting, 'd' if decrypting, None if nothing happened with the chain yet
65 #assert self.ed in ('e',None) # makes sure you don't encrypt with a cipher that has started decrypting
66 self.ed = 'e'
67 if self.mode == MODE_XTS:
68 # data sequence number (or 'tweak') has to be provided when in XTS mode
69 return self.chain.update(plaintext,'e',n)
70 else:
71 return self.chain.update(plaintext,'e')
73 def decrypt(self,ciphertext,n=''):
74 """Decrypt some ciphertext
76 decrypt(plaintext,n='')
77 ciphertext = a string of binary data
78 n = the 'tweak' value when the chaining mode is XTS
80 The decrypt function will decrypt the supplied ciphertext. When the supplied ciphertext is not a multiple of the blocksize of the cipher,
81 then the remaining ciphertext will be cached. The next time the decrypt function is called with some ciphertext, the new ciphertext will be concatenated
82 to the cache and then cache+ciphertext will be decrypted.
84 When the chaining mode allows the cipher to act as a stream cipher (CFB, OFB, CTR), the decrypt function will always decrypt all of the
85 supplied ciphertext immediately. No cache will be kept.
87 For XTS the behavious is somewhat different: it needs the whole block of ciphertext to be supplied at once. Every decrypt function called on a XTS cipher
88 will output an decrypted block based on the current supplied ciphertext block.
89 """
90 #self.ed = 'e' if chain is encrypting, 'd' if decrypting, None if nothing happened with the chain yet
91 #assert self.ed in ('d',None) # makes sure you don't decrypt with a cipher that has started encrypting
92 self.ed = 'd'
93 if self.mode == MODE_XTS:
94 # data sequence number (or 'tweak') has to be provided when in XTS mode
95 return self.chain.update(ciphertext,'d',n)
96 else:
97 return self.chain.update(ciphertext,'d')
99 def final(self,padding='PKCS7'):
100 # TODO: after calling final, reset the IV? so the cipher is as good as new?
101 """finalizes the chain by padding
103 final(padding='PKCS7'):
104 padding = padding function provided as an argument. Possible padding functions:
105 - 'zerosPadding'
106 - 'bitPadding'
107 - 'PKCS7'
108 - 'ANSI_X923'
109 - 'ISO_10126'
111 While a cipher object is in encryption mode, the final function will pad the remaining cache and encrypt it.
112 If the cipher has been used for decryption, the final function won't do antyhing. You have to manually unpad if necessary or
113 construct a Padder yourself en use its unpad function.
115 After finalization, the chain can still be used but the IV, counter etc aren't reset but just continu as they were after the last step (finalization step).
117 assert self.mode not in (MODE_XTS, MODE_CMAC) # finalizing (=padding) doesn't make sense when in XTS or CMAC mode
118 if self.ed == 'e':
119 # when the chain is in encryption mode, finalizing will pad the cache and encrypt this last block
120 padder = Padding(self.blocksize)
121 if self.mode in (MODE_OFB,MODE_CFB,MODE_CTR):
122 dummy = '0'*(self.blocksize - self.chain.keystream.buffer_info()[1]) # a dummy string that will be used to get a valid padding
123 else: #ECB, CBC
124 dummy = self.chain.cache
125 return self.chain.update(padder.pad(dummy,padding)[len(dummy):],'e') # pad the cache and then only supply the padding to the update function
126 else:
127 # final function doesn't make sense when decrypting => padding should be removed manually
128 pass
130 class ECB:
131 def __init__(self, codebook, blocksize):
132 self.cache = ''
133 self.codebook = codebook
134 self.blocksize = blocksize
136 def update(self, plaintext,ed):
137 """update the chain
139 ed = 'e' or 'd' = encrypt or decrypt => encrypt() or decrypt() from BlockCipher will pass the right one
140 codebook = encrypt/decrypt will pass "self.cipher.encrypt()" or "decrypt()"
142 output_blocks = []
143 self.cache += plaintext
144 if len(self.cache) < self.blocksize:
145 return ''
146 for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize):
147 #the only difference between encryption/decryption in the chain is the cipher block
148 if ed == 'e':
149 output_blocks.append(self.codebook.encrypt( self.cache[i:i + self.blocksize] ))
150 else:
151 output_blocks.append(self.codebook.decrypt( self.cache[i:i + self.blocksize] ))
152 self.cache = self.cache[i+self.blocksize:]
153 return ''.join(output_blocks)
155 class CBC:
156 def __init__(self, codebook, blocksize, IV):
157 self.IV = IV
158 self.cache = ''
159 self.codebook = codebook
160 self.blocksize = blocksize
162 def update(self, input,ed):
163 """update the chain
165 if ed == 'e':
166 encrypted_blocks = []
167 self.cache += input
168 if len(self.cache) < self.blocksize:
169 return ''
170 for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize):
171 self.IV = self.codebook.encrypt(util.xorstring(self.cache[i:i+self.blocksize],self.IV))
172 encrypted_blocks.append(self.IV)
173 self.cache = self.cache[i+self.blocksize:]
174 return ''.join(encrypted_blocks)
175 else:
176 decrypted_blocks = []
177 self.cache += input
178 if len(self.cache) < self.blocksize:
179 return ''
180 for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize):
181 plaintext = util.xorstring(self.IV,self.codebook.decrypt(self.cache[i:i + self.blocksize]))
182 self.IV = self.cache[i:i + self.blocksize]
183 decrypted_blocks.append(plaintext)
184 self.cache = self.cache[i+self.blocksize:]
185 return ''.join(decrypted_blocks)
187 class CFB:
188 """CFB Chaining Mode
190 Can be accessed as a stream cipher. Input to the chain must be a multiple of bytes."""
191 def __init__(self, codebook, blocksize, IV):
192 self.codebook = codebook
193 self.IV = IV
194 self.blocksize = blocksize
195 self.keystream =array('B', '')
196 def update(self, data,ed):
197 n = len(data)
198 blocksize = self.blocksize
199 output = array('B', data)
201 for i in xrange(n):
202 if ed =='e':
203 if self.keystream.buffer_info()[1] == 0:
204 block = self.codebook.encrypt(self.IV)
205 self.keystream = array('B', block)
206 self.IV = ''
207 output[i] ^= self.keystream.pop(0)
208 self.IV += chr(output[i]) # the IV for the next block in the chain is being built byte per byte as the ciphertext flows in
209 else:
210 if self.keystream.buffer_info()[1] == 0:
211 block = self.codebook.encrypt(self.IV)
212 self.keystream = array('B', block)
213 self.IV = ''
214 self.IV += chr(output[i])
215 output[i] ^= self.keystream.pop(0)
216 return output.tostring()
218 class OFB:
219 """OFB Chaining Mode
221 Can be accessed as a stream cipher. Input to the chain must be a multiple of bytes."""
222 def __init__(self, codebook, blocksize, IV):
223 self.codebook = codebook
224 self.IV = IV
225 self.blocksize = blocksize
226 self.keystream =array('B', '')
227 def update(self, data,ed):
228 #no difference between encryption and decryption mode
229 n = len(data)
230 blocksize = self.blocksize
231 output = array('B', data)
233 for i in xrange(n):
234 if self.keystream.buffer_info()[1] == 0: #encrypt a new counter block when the current keystream is fully used
235 self.IV = self.codebook.encrypt(self.IV)
236 self.keystream = array('B', self.IV)
237 output[i] ^= self.keystream.pop(0) #as long as an encrypted counter value is available, the output is just "input XOR keystream"
238 return output.tostring()
240 class CTR:
241 """CTR Mode
243 Implemented so it can be accessed as a stream cipher.
245 # initial counter value can be choosen, decryption always starts from beginning
246 # -> you can start from anywhere yourself: just feed the cipher encoded blocks and feed a counter with the corresponding value
247 def __init__(self, codebook, blocksize, counter):
248 self.codebook = codebook
249 self.counter = counter
250 self.blocksize = blocksize
251 self.keystream =array('B', '') #holds the output of the current encrypted counter value
253 def update(self, data,ed):
254 # no need for the encryption/decryption distinction: both are the same
255 n = len(data)
256 blocksize = self.blocksize
258 output = array('B', data)
259 for i in xrange(n):
260 if self.keystream.buffer_info()[1] == 0: #encrypt a new counter block when the current keystream is fully used
261 block = self.codebook.encrypt(self.counter())
262 self.keystream = array('B', block)
263 output[i] ^= self.keystream.pop(0) #as long as an encrypted counter value is available, the output is just "input XOR keystream"
264 return output.tostring()
266 class XTS:
267 # TODO: allow other blocksizes besides 16bytes?
268 def __init__(self,codebook1, codebook2):
269 self.cache = ''
270 self.codebook1 = codebook1
271 self.codebook2 = codebook2
273 def update(self, data, ed,tweak=''):
274 # supply n as a raw string
275 # tweak = data sequence number
276 """Perform a XTS encrypt/decrypt operation.
278 In contrast to the other chaining modes: the whole data block has to encrypted at once."""
280 output = ''
281 assert len(data) > 15, "At least one block of 128 bits needs to be supplied"
282 assert len(data) < 128*pow(2,20)
285 while i < ((len(data) // 16)-1): #Decrypt all the blocks but one last full block and opt one last partial block
286 output += self.__xts_step(ed,data[i*16:(i+1)*16],i,tweak)
287 i+=1
288 # Check if the data supplied is a multiple of 16 bytes -> one last full block and we're done
289 if len(data[i*16:]) == 16:
290 output += self.__xts_step(ed,data[i*16:(i+1)*16],i,tweak)
291 else:
292 if ed=='e':
293 (x, y) = (i, i+1)
294 else: # Permutation of the last two indexes
295 (x, y) = (i+1, i)
296 # Decrypt/Encrypt the last two blocks when data is not a multiple of 16 bytes
297 Cm1 = data[i*16:(i+1)*16]
298 Cm = data[(i+1)*16:]
299 PP = self.__xts_step(ed,Cm1,x,tweak)
300 Cp = PP[len(Cm):]
301 Pm = PP[:len(Cm)]
302 CC = Cm+Cp
303 Pm1 = self.__xts_step(ed,CC,y,tweak)
304 output += Pm1 + Pm
305 return output
307 def __xts_step(self,ed,tocrypt,i,tweak):
308 # based on the pseudo code in the P1619 standard
309 # pseudo: the tweak is kept as the shifted version after every xts-step
310 # here: the tweak is supplied as the clean value every step => while loop to simulate
311 # previous steps
313 # e_k2_n = E_K2(tweak)
314 e_k2_n = self.codebook2.encrypt(tweak+ '\x00' * (16-len(tweak)))[::-1]
316 # T = E_K2(n) mul (a pow i)
317 T = util.string2number(e_k2_n)
318 while i:
319 T = T << 1
320 # if (Cout)
321 if T >> (8*16):
322 #T[0] ^= GF_128_FDBK;
323 T = T ^ 0x100000000000000000000000000000087L
324 i-=1
325 T = util.number2string_N(T, 16)[::-1]
327 # C = E_K1(P xor T) xor T
328 if ed == 'd':
329 return util.xorstring(T, self.codebook1.decrypt(util.xorstring(T, tocrypt)))
330 else:
331 return util.xorstring(T, self.codebook1.encrypt(util.xorstring(T, tocrypt)))
333 class CMAC:
334 """CMAC chaining mode
336 Supports every cipher with a blocksize available in de Rb_dictionary.
337 Calling update(), immediately calculates the hash. No finalizing needed.
338 The hashlenght is equal to block size of the used block cipher
340 # TODO: move to hash module?
341 # TODO: change update behaviour to .update() and .digest() as for all hash modules?
342 # -> other hash functions in pycrypto: calling update, concatenates current input with previous input and hashes everything
343 def __init__(self,codebook,blocksize):
344 # Purpose of init: calculate Lu & Lu2
345 #blocksize (in bytes): to select the Rb constant in the dictionary
346 #Rb as a dictionary: adding support for other blocksizes is easy
347 self.cache=''
348 self.blocksize = blocksize
349 self.codebook = codebook
351 #Rb_dictionary: holds values for Rb for different blocksizes
352 # values for 64 and 128 bits found here: http://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
353 # explanation from: http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
354 # Rb is a representation of a certain irreducible binary polynomial of degree b, namely,
355 # the lexicographically first among all such polynomials with the minimum possible number of
356 # nonzero terms. If this polynomial is expressed as ub+cb-1ub-1+...+c2u2+c1u+c0, where the
357 # coefficients cb-1, cb-2, ..., c2, c1, c0 are either 0 or 1, then Rb is the bit string cb-1cb-2...c2c1c0.
359 Rb_dictionary = {64:0x000000000000001b,128:0x00000000000000000000000000000087}
360 self.Rb = Rb_dictionary[blocksize*8]
362 mask1 = int(('\xff'*blocksize).encode('hex'),16)
363 mask2 = int(('\x80' + '\x00'*(blocksize-1) ).encode('hex'),16)
365 L = int(self.codebook.encrypt('\x00'*blocksize).encode('hex'),16)
367 if L & mask2:
368 Lu = ((L << 1) & mask1) ^ self.Rb
369 else:
370 Lu = L << 1
371 Lu = Lu & mask1
373 if Lu & mask2:
374 Lu2 = ((Lu << 1) & mask1)^ self.Rb
375 else:
376 Lu2 = Lu << 1
377 Lu2 = Lu2 & mask1
379 self.Lu =Lu
380 self.Lu2=Lu2
382 def update(self,data,ed):
383 # not really an update function: everytime the function is called, the hash from the input data is calculated
384 assert ed == 'e'
385 blocksize = self.blocksize
387 m = (len(data)+blocksize-1)/blocksize #m = amount of datablocks
388 y = '\x00'*blocksize
390 for i in range(1,m):
391 y = self.codebook.encrypt( util.xorstring(data[(i-1)*blocksize:(i)*blocksize],y) )
393 if len(data[(i)*blocksize:])==blocksize:
394 Lu_string = util.number2string(self.Lu)
395 X = util.xorstring(util.xorstring(data[(i)*blocksize:],y),Lu_string)
396 else:
397 tmp = data[(i)*blocksize:] + '\x80' + '\x00'*(blocksize - len(data[(i)*blocksize:])-1)
398 Lu2_string = util.number2string(self.Lu2)
399 #Lu2_string = '\x00'*(blocksize - len(Lu2_string)) + Lu2_string
400 X = util.xorstring(util.xorstring(tmp,y),Lu2_string)
402 T = self.codebook.encrypt(X)
403 return T