# ============================================================================= # Copyright (c) 2008 Christophe Oosterlynck # & NXP ( Philippe Teuwen ) # # 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. # ============================================================================= from . import util from . import padding import collections import sys MODE_ECB = 1 MODE_CBC = 2 MODE_CFB = 3 MODE_OFB = 5 MODE_CTR = 6 MODE_XTS = 7 MODE_CMAC = 8 class BlockCipher(): """ Base class for all blockciphers """ key_error_message = "Wrong key size" #should be overwritten in child classes def __init__(self,key,mode,IV,counter,cipher_module,segment_size,args={}): # Cipher classes inheriting from this one take care of: # self.blocksize # self.cipher self.key = key self.mode = mode self.cache = '' self.ed = None if 'keylen_valid' in dir(self): #wrappers for pycrypto functions don't have this function if not self.keylen_valid(key) and type(key) is not tuple: raise ValueError(self.key_error_message) if IV == None: self.IV = '\x00'*self.blocksize else: self.IV = IV if mode != MODE_XTS: self.cipher = cipher_module(self.key,**args) if mode == MODE_ECB: self.chain = ECB(self.cipher, self.blocksize) elif mode == MODE_CBC: if len(self.IV) != self.blocksize: raise Exception("the IV length should be %i bytes"%self.blocksize) self.chain = CBC(self.cipher, self.blocksize,self.IV) elif mode == MODE_CFB: if len(self.IV) != self.blocksize: raise Exception("the IV length should be %i bytes"%self.blocksize) if segment_size == None: raise ValueError("segment size must be defined explicitely for CFB mode") if segment_size > self.blocksize*8 or segment_size%8 != 0: # current CFB implementation doesn't support bit level acces => segment_size should be multiple of bytes raise ValueError("segment size should be a multiple of 8 bits between 8 and %i"%(self.blocksize*8)) self.chain = CFB(self.cipher, self.blocksize,self.IV,segment_size) elif mode == MODE_OFB: if len(self.IV) != self.blocksize: raise ValueError("the IV length should be %i bytes"%self.blocksize) self.chain = OFB(self.cipher, self.blocksize,self.IV) elif mode == MODE_CTR: if (counter == None) or not isinstance(counter, collections.Callable): raise Exception("Supply a valid counter object for the CTR mode") self.chain = CTR(self.cipher,self.blocksize,counter) elif mode == MODE_XTS: if self.blocksize != 16: raise Exception('XTS only works with blockcipher that have a 128-bit blocksize') if not(type(key) == tuple and len(key) == 2): raise Exception('Supply two keys as a tuple when using XTS') if 'keylen_valid' in dir(self): #wrappers for pycrypto functions don't have this function if not self.keylen_valid(key[0]) or not self.keylen_valid(key[1]): raise ValueError(self.key_error_message) self.cipher = cipher_module(self.key[0],**args) self.cipher2 = cipher_module(self.key[1],**args) self.chain = XTS(self.cipher, self.cipher2) elif mode == MODE_CMAC: if self.blocksize not in (8,16): raise Exception('CMAC only works with blockcipher that have a 64 or 128-bit blocksize') self.chain = CMAC(self.cipher,self.blocksize,self.IV) else: raise Exception("Unknown chaining mode!") def encrypt(self,plaintext,n=''): """Encrypt some plaintext plaintext = a string of binary data n = the 'tweak' value when the chaining mode is XTS The encrypt function will encrypt the supplied plaintext. The behavior varies slightly depending on the chaining mode. ECB, CBC: --------- When the supplied plaintext is not a multiple of the blocksize of the cipher, then the remaining plaintext will be cached. The next time the encrypt function is called with some plaintext, the new plaintext will be concatenated to the cache and then cache+plaintext will be encrypted. CFB, OFB, CTR: -------------- When the chaining mode allows the cipher to act as a stream cipher, the encrypt function will always encrypt all of the supplied plaintext immediately. No cache will be kept. XTS: ---- Because the handling of the last two blocks is linked, it needs the whole block of plaintext to be supplied at once. Every encrypt function called on a XTS cipher will output an encrypted block based on the current supplied plaintext block. CMAC: ----- Everytime the function is called, the hash from the input data is calculated. No finalizing needed. The hashlength is equal to block size of the used block cipher. """ #self.ed = 'e' if chain is encrypting, 'd' if decrypting, # None if nothing happened with the chain yet #assert self.ed in ('e',None) # makes sure you don't encrypt with a cipher that has started decrypting self.ed = 'e' if self.mode == MODE_XTS: # data sequence number (or 'tweak') has to be provided when in XTS mode return self.chain.update(plaintext,'e',n) else: return self.chain.update(plaintext,'e') def decrypt(self,ciphertext,n=''): """Decrypt some ciphertext ciphertext = a string of binary data n = the 'tweak' value when the chaining mode is XTS The decrypt function will decrypt the supplied ciphertext. The behavior varies slightly depending on the chaining mode. ECB, CBC: --------- When the supplied ciphertext is not a multiple of the blocksize of the cipher, then the remaining ciphertext will be cached. The next time the decrypt function is called with some ciphertext, the new ciphertext will be concatenated to the cache and then cache+ciphertext will be decrypted. CFB, OFB, CTR: -------------- When the chaining mode allows the cipher to act as a stream cipher, the decrypt function will always decrypt all of the supplied ciphertext immediately. No cache will be kept. XTS: ---- Because the handling of the last two blocks is linked, it needs the whole block of ciphertext to be supplied at once. Every decrypt function called on a XTS cipher will output a decrypted block based on the current supplied ciphertext block. CMAC: ----- Mode not supported for decryption as this does not make sense. """ #self.ed = 'e' if chain is encrypting, 'd' if decrypting, # None if nothing happened with the chain yet #assert self.ed in ('d',None) # makes sure you don't decrypt with a cipher that has started encrypting self.ed = 'd' if self.mode == MODE_XTS: # data sequence number (or 'tweak') has to be provided when in XTS mode return self.chain.update(ciphertext,'d',n) else: return self.chain.update(ciphertext,'d') def final(self,padfct=padding.PKCS7): # TODO: after calling final, reset the IV? so the cipher is as good as new? """Finalizes the encryption by padding the cache padfct = padding function import from CryptoPlus.Util.padding For ECB, CBC: the remaining bytes in the cache will be padded and encrypted. For OFB,CFB, CTR: an encrypted padding will be returned, making the total outputed bytes since construction of the cipher a multiple of the blocksize of that cipher. If the cipher has been used for decryption, the final function won't do anything. You have to manually unpad if necessary. After finalization, the chain can still be used but the IV, counter etc aren't reset but just continue as they were after the last step (finalization step). """ assert self.mode not in (MODE_XTS, MODE_CMAC) # finalizing (=padding) doesn't make sense when in XTS or CMAC mode if self.ed == 'e': # when the chain is in encryption mode, finalizing will pad the cache and encrypt this last block if self.mode in (MODE_OFB,MODE_CFB,MODE_CTR): dummy = '0'*(self.chain.totalbytes%self.blocksize) # a dummy string that will be used to get a valid padding else: #ECB, CBC dummy = self.chain.cache pad = padfct(dummy,padding.PAD,self.blocksize)[len(dummy):] # construct the padding necessary return self.chain.update(pad,'e') # supply the padding to the update function => chain cache will be "cache+padding" else: # final function doesn't make sense when decrypting => padding should be removed manually pass class ECB: """ECB chaining mode """ def __init__(self, codebook, blocksize): if sys.version_info.major > 2: self.cache = b'' else: self.cache = '' self.codebook = codebook self.blocksize = blocksize def update(self, data, ed): """Processes the given ciphertext/plaintext Inputs: data: raw string of any length ed: 'e' for encryption, 'd' for decryption Output: processed raw string block(s), if any When the supplied data is not a multiple of the blocksize of the cipher, then the remaining input data will be cached. The next time the update function is called with some data, the new data will be concatenated to the cache and then cache+data will be processed and full blocks will be outputted. """ output_blocks = [] self.cache += data if len(self.cache) < self.blocksize: return '' for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize): #the only difference between encryption/decryption in the chain is the cipher block if ed == 'e': output_blocks.append(self.codebook.encrypt( self.cache[i:i + self.blocksize] )) else: output_blocks.append(self.codebook.decrypt( self.cache[i:i + self.blocksize] )) self.cache = self.cache[i+self.blocksize:] if sys.version_info.major < 3: return ''.join(output_blocks) else: return output_blocks[0] class CBC: """CBC chaining mode """ def __init__(self, codebook, blocksize, IV): self.IV = IV self.cache = '' self.codebook = codebook self.blocksize = blocksize def update(self, data, ed): """Processes the given ciphertext/plaintext Inputs: data: raw string of any length ed: 'e' for encryption, 'd' for decryption Output: processed raw string block(s), if any When the supplied data is not a multiple of the blocksize of the cipher, then the remaining input data will be cached. The next time the update function is called with some data, the new data will be concatenated to the cache and then cache+data will be processed and full blocks will be outputted. """ if ed == 'e': encrypted_blocks = '' self.cache += data if len(self.cache) < self.blocksize: return '' for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize): self.IV = self.codebook.encrypt(util.xorstring(self.cache[i:i+self.blocksize],self.IV)) encrypted_blocks += self.IV self.cache = self.cache[i+self.blocksize:] return encrypted_blocks else: decrypted_blocks = '' self.cache += data if len(self.cache) < self.blocksize: return '' for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize): plaintext = util.xorstring(self.IV,self.codebook.decrypt(self.cache[i:i + self.blocksize])) self.IV = self.cache[i:i + self.blocksize] decrypted_blocks+=plaintext self.cache = self.cache[i+self.blocksize:] return decrypted_blocks class CFB: # TODO: bit access instead of only byte level access """CFB Chaining Mode Can be accessed as a stream cipher. """ def __init__(self, codebook, blocksize, IV,segment_size): self.codebook = codebook self.IV = IV self.blocksize = blocksize self.segment_size = segment_size/8 self.keystream = [] self.totalbytes = 0 def update(self, data, ed): """Processes the given ciphertext/plaintext Inputs: data: raw string of any multiple of bytes ed: 'e' for encryption, 'd' for decryption Output: processed raw string The encrypt/decrypt functions will always process all of the supplied input data immediately. No cache will be kept. """ output = list(data) for i in range(len(data)): if ed =='e': if len(self.keystream) == 0: block = self.codebook.encrypt(self.IV) self.keystream = list(block)[:self.segment_size] # keystream consists of the s MSB's self.IV = self.IV[self.segment_size:] # keeping (b-s) LSB's output[i] = chr(ord(output[i]) ^ ord(self.keystream.pop(0))) self.IV += output[i] # the IV for the next block in the chain is being built byte per byte as the ciphertext flows in else: if len(self.keystream) == 0: block = self.codebook.encrypt(self.IV) self.keystream = list(block)[:self.segment_size] self.IV = self.IV[self.segment_size:] self.IV += output[i] output[i] = chr(ord(output[i]) ^ ord(self.keystream.pop(0))) self.totalbytes += len(output) return ''.join(output) class OFB: """OFB Chaining Mode Can be accessed as a stream cipher. """ def __init__(self, codebook, blocksize, IV): self.codebook = codebook self.IV = IV self.blocksize = blocksize self.keystream = [] self.totalbytes = 0 def update(self, data, ed): """Processes the given ciphertext/plaintext Inputs: data: raw string of any multiple of bytes ed: 'e' for encryption, 'd' for decryption Output: processed raw string The encrypt/decrypt functions will always process all of the supplied input data immediately. No cache will be kept. """ #no difference between encryption and decryption mode n = len(data) blocksize = self.blocksize output = list(data) for i in range(n): if len(self.keystream) == 0: #encrypt a new counter block when the current keystream is fully used self.IV = self.codebook.encrypt(self.IV) self.keystream = list(self.IV) output[i] = chr(ord(output[i]) ^ ord(self.keystream.pop(0))) #as long as an encrypted counter value is available, the output is just "input XOR keystream" self.totalbytes += len(output) return ''.join(output) class CTR: """CTR Chaining Mode Can be accessed as a stream cipher. """ # initial counter value can be choosen, decryption always starts from beginning # -> you can start from anywhere yourself: just feed the cipher encoded blocks and feed a counter with the corresponding value def __init__(self, codebook, blocksize, counter): self.codebook = codebook self.counter = counter self.blocksize = blocksize self.keystream = [] #holds the output of the current encrypted counter value self.totalbytes = 0 def update(self, data, ed): """Processes the given ciphertext/plaintext Inputs: data: raw string of any multiple of bytes ed: 'e' for encryption, 'd' for decryption Output: processed raw string The encrypt/decrypt functions will always process all of the supplied input data immediately. No cache will be kept. """ # no need for the encryption/decryption distinction: both are the same n = len(data) blocksize = self.blocksize output = list(data) for i in range(n): if len(self.keystream) == 0: #encrypt a new counter block when the current keystream is fully used block = self.codebook.encrypt(self.counter()) self.keystream = list(block) output[i] = chr(ord(output[i])^ord(self.keystream.pop(0))) #as long as an encrypted counter value is available, the output is just "input XOR keystream" self.totalbytes += len(output) return ''.join(output) class XTS: """XTS Chaining Mode Usable with blockciphers with a 16-byte blocksize """ # TODO: allow other blocksizes besides 16bytes? def __init__(self,codebook1, codebook2): self.cache = '' self.codebook1 = codebook1 self.codebook2 = codebook2 def update(self, data, ed,tweak=''): # supply n as a raw string # tweak = data sequence number """Perform a XTS encrypt/decrypt operation. Because the handling of the last two blocks is linked, it needs the whole block of ciphertext to be supplied at once. Every decrypt function called on a XTS cipher will output a decrypted block based on the current supplied ciphertext block. """ output = '' assert len(data) > 15, "At least one block of 128 bits needs to be supplied" assert len(data) < 128*pow(2,20) # initializing T # e_k2_n = E_K2(tweak) e_k2_n = self.codebook2.encrypt(tweak+ '\x00' * (16-len(tweak)))[::-1] self.T = util.string2number(e_k2_n) i=0 while i < ((len(data) // 16)-1): #Decrypt all the blocks but one last full block and opt one last partial block # C = E_K1(P xor T) xor T output += self.__xts_step(ed,data[i*16:(i+1)*16],self.T) # T = E_K2(n) mul (a pow i) self.__T_update() i+=1 # Check if the data supplied is a multiple of 16 bytes -> one last full block and we're done if len(data[i*16:]) == 16: # C = E_K1(P xor T) xor T output += self.__xts_step(ed,data[i*16:(i+1)*16],self.T) # T = E_K2(n) mul (a pow i) self.__T_update() else: T_temp = [self.T] self.__T_update() T_temp.append(self.T) if ed=='d': # Permutation of the last two indexes T_temp.reverse() # Decrypt/Encrypt the last two blocks when data is not a multiple of 16 bytes Cm1 = data[i*16:(i+1)*16] Cm = data[(i+1)*16:] PP = self.__xts_step(ed,Cm1,T_temp[0]) Cp = PP[len(Cm):] Pm = PP[:len(Cm)] CC = Cm+Cp Pm1 = self.__xts_step(ed,CC,T_temp[1]) output += Pm1 + Pm return output def __xts_step(self,ed,tocrypt,T): T_string = util.number2string_N(T,16)[::-1] # C = E_K1(P xor T) xor T if ed == 'd': return util.xorstring(T_string, self.codebook1.decrypt(util.xorstring(T_string, tocrypt))) else: return util.xorstring(T_string, self.codebook1.encrypt(util.xorstring(T_string, tocrypt))) def __T_update(self): # Used for calculating T for a certain step using the T value from the previous step self.T = self.T << 1 # if (Cout) if self.T >> (8*16): #T[0] ^= GF_128_FDBK; self.T = self.T ^ 0x100000000000000000000000000000087 class CMAC: """CMAC chaining mode Supports every cipher with a blocksize available in the list CMAC.supported_blocksizes. The hashlength is equal to block size of the used block cipher. Usable with blockciphers with a 8 or 16-byte blocksize """ # TODO: move to hash module? # TODO: change update behaviour to .update() and .digest() as for all hash modules? # -> other hash functions in pycrypto: calling update, concatenates current input with previous input and hashes everything __Rb_dictionary = {64:0x000000000000001b,128:0x00000000000000000000000000000087} supported_blocksizes = list(__Rb_dictionary.keys()) def __init__(self,codebook,blocksize,IV): # Purpose of init: calculate Lu & Lu2 #blocksize (in bytes): to select the Rb constant in the dictionary #Rb as a dictionary: adding support for other blocksizes is easy self.cache='' self.blocksize = blocksize self.codebook = codebook self.IV = IV #Rb_dictionary: holds values for Rb for different blocksizes # values for 64 and 128 bits found here: http://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html # explanation from: http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf # Rb is a representation of a certain irreducible binary polynomial of degree b, namely, # the lexicographically first among all such polynomials with the minimum possible number of # nonzero terms. If this polynomial is expressed as ub+cb-1ub-1+...+c2u2+c1u+c0, where the # coefficients cb-1, cb-2, ..., c2, c1, c0 are either 0 or 1, then Rb is the bit string cb-1cb-2...c2c1c0. self.Rb = self.__Rb_dictionary[blocksize*8] mask1 = int(('\xff'*blocksize).encode('hex'),16) mask2 = int(('\x80' + '\x00'*(blocksize-1) ).encode('hex'),16) L = int(self.codebook.encrypt('\x00'*blocksize).encode('hex'),16) if L & mask2: Lu = ((L << 1) & mask1) ^ self.Rb else: Lu = L << 1 Lu = Lu & mask1 if Lu & mask2: Lu2 = ((Lu << 1) & mask1)^ self.Rb else: Lu2 = Lu << 1 Lu2 = Lu2 & mask1 self.Lu =util.number2string_N(Lu,self.blocksize) self.Lu2=util.number2string_N(Lu2,self.blocksize) def update(self, data, ed): """Processes the given ciphertext/plaintext Inputs: data: raw string of any length ed: 'e' for encryption, 'd' for decryption Output: hashed data as raw string This is not really an update function: Everytime the function is called, the hash from the input data is calculated. No finalizing needed. """ assert ed == 'e' blocksize = self.blocksize m = (len(data)+blocksize-1)/blocksize #m = amount of datablocks i=0 for i in range(1,m): self.IV = self.codebook.encrypt( util.xorstring(data[(i-1)*blocksize:(i)*blocksize],self.IV) ) if len(data[(i)*blocksize:])==blocksize: X = util.xorstring(util.xorstring(data[(i)*blocksize:],self.IV),self.Lu) else: tmp = data[(i)*blocksize:] + '\x80' + '\x00'*(blocksize - len(data[(i)*blocksize:])-1) X = util.xorstring(util.xorstring(tmp,self.IV),self.Lu2) T = self.codebook.encrypt(X) return T