def encryptBlock(self, plainTextBlock): """ CBC block encryption, IV is set with 'encrypt' """ auto_IV = '' if self.encryptBlockCount == 0: if self.iv == None: # generate IV and use self.iv = ''.join([chr(self.r.randrange(256)) for i in range(self.blockSize)]) self.prior_encr_CT_block = self.iv auto_IV = self.prior_encr_CT_block # prepend IV if it's automatic else: # application provided IV assert(len(self.iv) == self.blockSize ),'IV must be same length as block' self.prior_encr_CT_block = self.iv """ encrypt the prior CT XORed with the PT """ ct = self.baseCipher.encryptBlock( xor(self.prior_encr_CT_block, plainTextBlock) ) self.prior_encr_CT_block = ct return auto_IV+ct
def testTKIP_crc_modify(self): """ TKIP crc modification test """ key = a2b_p( "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f") # the PMK ta = a2b_p( "10 22 33 44 55 66") # the TK (key) is created from the iv and ta keyID = 0 alg = TKIP_encr( key, ta, keyID) # this is just the encryption algorithm with no Michael MIC plainText = ''.join([chr(i) for i in range(20)]) # 20 octets (0 t 19) iv = a2b_p( "01 00 00 00 00 00") # the little-endian encoded PacketNumber cipherText = alg.encrypt(plainText, iv) ctHeader = cipherText[0:8] # encoded iv and keyId ctData = cipherText[8:-4] ctCrcEncrypted = cipherText[-4:] # just the encrypted crc fields # lets change the first octet of the data from 0x00 to 0x01 base = (len(ctData)) * chr(0) baseCrc = crc32(base) bitMask = chr(1) + (len(ctData) - 1) * chr(0) maskCrc = crc32(bitMask) maskedCt = xor(bitMask, ctData) maskedCtCrc = crc32(maskedCt) print "--------------- make a modified packet and MIC ------------" print "plainText = %s " % b2a_hex(plainText) print "cipherText= %s " % b2a_hex(cipherText) print "ctData = %s " % b2a_hex(ctData) print "ctxtCrc = %s " % b2a_hex(ctCrcEncrypted) print "base = %s " % b2a_hex(base) print "baseCrc = %0X" % baseCrc print "mask = %s " % b2a_hex(bitMask) print "maskCrc = %0X" % maskCrc print "maskedCt = %s " % b2a_hex(maskedCt) print "maskCtCrc= %0X" % maskedCtCrc maskDiff = maskCrc ^ baseCrc newCtCrc = pack('<I', (maskDiff ^ unpack('<I', ctCrcEncrypted)[0])) newCt = ctHeader + maskedCt + newCtCrc newPt = alg.decrypt( newCt) # this will raise an exception if the crc is 'bad'! print "newPt = %s " % b2a_hex(newPt)
def decryptBlock(self, encryptedBlock): """ Decrypt a single block """ if self.decryptBlockCount == 0: # first call, process IV if self.iv == None: # auto decrypt IV? self.prior_CT_block = encryptedBlock return '' else: assert(len(self.iv)==self.blockSize),"Bad IV size on CBC decryption" self.prior_CT_block = self.iv dct = self.baseCipher.decryptBlock(encryptedBlock) """ XOR the prior decrypted CT with the prior CT """ dct_XOR_priorCT = xor( self.prior_CT_block, dct ) self.prior_CT_block = encryptedBlock return dct_XOR_priorCT
def pbkdf2(password, salt, iterations, keySize, PRF=HMAC_SHA1): """ Create key of size keySize from password and salt """ if len(password)>63: raise 'Password too long for pbkdf2' #if len(password)<8 : raise 'Password too short for pbkdf2' if (keySize > 10000): # spec says >4294967295L*digestSize raise 'keySize too long for PBKDF2' prf = PRF(key=password) # HMAC_SHA1 numBlocks = ceil(1.*keySize/prf.digest_size) # ceiling function key = '' for block in range(1,numBlocks+1): # Calculate F(P, salt, iterations, i) F = prf(salt+pack('>i',block)) # i is packed into 4 big-endian bytes U = prf(salt+pack('>i',block)) # i is packed into 4 big-endian bytes for count in range(2,iterations+1): U = prf(U) F = xor(F,U) key = key + F return key[:keySize]
def pbkdf2(password, salt, iterations, keySize, PRF=HMAC_SHA1): """ Create key of size keySize from password and salt """ if len(password) > 63: raise ResolverError('Password too long for pbkdf2') # if len(password)<8 : raise 'Password too short for pbkdf2' if (keySize > 10000): # spec says >4294967295L*digestSize raise ResolverError('keySize too long for PBKDF2') prf = PRF(key=password) # HMAC_SHA1 numBlocks = int(ceil(1. * keySize / prf.digest_size)) # ceiling function key = '' for block in range(1, numBlocks + 1): # Calculate F(P, salt, iterations, i) F = prf(salt + pack('>i', block)) # i is packed into 4 big-endian bytes U = prf(salt + pack('>i', block)) # i is packed into 4 big-endian bytes for _ in range(2, iterations + 1): U = prf(U) F = xor(F, U) key = key + F return key[:keySize]
def testTKIP_crc_modify(self): """ TKIP crc modification test """ key = a2b_p( "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f" ) # the PMK ta = a2b_p( "10 22 33 44 55 66" ) # the TK (key) is created from the iv and ta keyID = 0 alg = TKIP_encr(key, ta, keyID) # this is just the encryption algorithm with no Michael MIC plainText = ''.join([chr(i) for i in range(20)]) # 20 octets (0 t 19) iv = a2b_p( "01 00 00 00 00 00" ) # the little-endian encoded PacketNumber cipherText = alg.encrypt(plainText, iv) ctHeader = cipherText[0:8] # encoded iv and keyId ctData = cipherText[8:-4] ctCrcEncrypted = cipherText[-4:] # just the encrypted crc fields # lets change the first octet of the data from 0x00 to 0x01 base = (len(ctData))*chr(0) baseCrc = crc32(base) bitMask = chr(1)+(len(ctData)-1)*chr(0) maskCrc = crc32(bitMask) maskedCt = xor(bitMask,ctData) maskedCtCrc = crc32(maskedCt) print "--------------- make a modified packet and MIC ------------" print "plainText = %s " % b2a_hex(plainText) print "cipherText= %s " % b2a_hex(cipherText) print "ctData = %s " % b2a_hex(ctData) print "ctxtCrc = %s " % b2a_hex(ctCrcEncrypted) print "base = %s " % b2a_hex(base) print "baseCrc = %0X" % baseCrc print "mask = %s " % b2a_hex(bitMask) print "maskCrc = %0X" % maskCrc print "maskedCt = %s " % b2a_hex(maskedCt) print "maskCtCrc= %0X" % maskedCtCrc maskDiff = maskCrc ^ baseCrc newCtCrc = pack('<I', (maskDiff ^ unpack('<I',ctCrcEncrypted)[0]) ) newCt = ctHeader + maskedCt + newCtCrc newPt = alg.decrypt(newCt) # this will raise an exception if the crc is 'bad'! print "newPt = %s " % b2a_hex(newPt)
class CCM(BlockCipherWithIntegrity): """ The CCM class wraps block ciphers to provide integrity and encryption. CCM provides both encryption and a strong integrity check. The integrity check can optionally include "additional authentication data" that is included in the message integrity check, but is not encrypted. CCM is composed of two passes of the same base cipher, first the instance calculates a CBC Message Authentication Check, and then the same algorithm instance is used for the CTR (counter) mode encryption. This algorithm mode does NOT support streams of data (moreData flag) since a full packet must be available for the two pass CBC_MAC and CTR encryption process. When decrypting, a 'DecryptIntegrityError' exception is raised if the integrity check fails. >> aes_ccm = CCM(AES(key)) >> cipherText = aes_ccm.encrypt(plainText, nonce) >> try: >> decryptedText = aes_ccm.decrypt(cipherText, nonce) >> except IntegrityCheckError: >> print 'failed integrity check' or ... >> cipherText = aes_ccm.encrypt(plainText, nonce, addAuthData=header) >> try: >> decryptedText = aes_ccm.decrypt(cipherText, nonce, addAuthData=header) >> except IntegrityCheckError: >> print 'failed integrity check' """ def __init__(self, blockCipherInstance, autoNonce=None, macSize=8, nonceSize=13): """ CCM algorithms are created by initializing with a BlockCipher instance blockCipherInstance -> typically AES_ECB autoNonce -> sets the intial value of a nonce for automatic nonce creation (not available yet) macSize -> size of MAC field can be = 4, 6, 8, 10, 12, 14, or 16 nonceSize -> size of nonce in bytes (default 13) the counter size is blockSize-nonceSize-1 """ self.baseCipher = blockCipherInstance self.name = self.baseCipher.name + '_CCM' self.blockSize = self.baseCipher.blockSize self.keySize = self.baseCipher.keySize self.baseCipher.padding = noPadding() # baseCipher should NOT pad!! self.M = macSize # Number of octets if not ((3 < self.M < 17) and (macSize % 2 == 0)): raise (InitCryptoError, 'CCM, M (size of auth field) is out of bounds') self.nonceSize = nonceSize self.L = self.baseCipher.blockSize - self.nonceSize - 1 if not (1 < self.L < 9): raise (InitCryptoError, 'CCM, L (size of length field) is out of bounds') self.reset() def setKey(self, key): self.baseCipher.setKey(key) self.reset() # Overload to reset both CCM state and the wrapped baseCipher def resetEncrypt(self): BlockCipherWithIntegrity.resetEncrypt( self) # reset CCM encrypt state (super class) self.baseCipher.resetEncrypt() # reset base cipher encrypt state def resetDecrypt(self): BlockCipherWithIntegrity.resetDecrypt( self) # reset CBC state (super class) self.baseCipher.resetEncrypt( ) # CCM uses encryption of base cipher to decrypt! def encrypt(self, plainText, nonce, addAuthData=''): """ CCM encryption of plainText nonce must be unique for each encryption, if set to none it will maintain it's own nonce creation addAuthData is optional """ # construct authentication block zero # flag byte fields Adata = ((len(addAuthData)) > 0) << 6 # bit 6 is 1 if auth Mfield = ((self.M - 2) / 2) << 3 # bits 5,4,3 encode macSize Lfield = self.L - 1 # bits 2,1,0 encode L size = blockSize-nonceSize-1 flagsByte = chr(Adata ^ Mfield ^ Lfield) if len(nonce) != self.nonceSize: raise (EncryptError, 'wrong sized nonce') lenMessage = len(plainText) if lenMessage >= 1L << (8 * self.L): raise (EncryptError, 'CCM plainText too long for given L field size') packedLenMessage = pack( '!Q', lenMessage)[-self.L:] # pack and truncate to L bytes blockZero = flagsByte + nonce + packedLenMessage if len(blockZero) != self.baseCipher.blockSize: raise (EncryptError, 'CCM bad size of first block') authLengthField = self._encodeAuthLength(len(addAuthData)) cbcInput = blockZero + authLengthField + addAuthData authPadSize = self.baseCipher.blockSize - ( (len(cbcInput) - 1) % self.baseCipher.blockSize) - 1 cbcInput = cbcInput + authPadSize * chr( 0) # pad to block size with zeros cbcInput = cbcInput + plainText cbcEndPad = chr(0x00) * ((self.blockSize - ( (len(cbcInput)) % self.blockSize)) % self.blockSize) cbcInput = cbcInput + cbcEndPad # Calculate CBC_MAC numCbcBlocks, extra = divmod(len(cbcInput), self.blockSize) assert (extra == 0), 'bad block size on cbc_mac calculation' cbcMicValue = self.blockSize * chr(0x00) for i in range(numCbcBlocks): cbcBlock = cbcInput[i * self.blockSize:(i + 1) * self.blockSize] cbcMicValue = self.baseCipher.encrypt(xor(cbcMicValue, cbcBlock)) counter = 0L # the counter mode preload with counter starting at zero ctrModePl = chr(self.L - 1) + nonce + pack('>Q', counter)[-self.L:] ccmMIC = xor(self.baseCipher.encrypt(ctrModePl), cbcMicValue)[:self.M] # first M bytes of xor ct = '' numCtrBlocks, extra = divmod( len(plainText) + self.blockSize, self.blockSize) while counter < numCtrBlocks: counter = counter + 1L ctrModePl = chr(self.L - 1) + nonce + pack('>Q', counter)[-self.L:] ct = ct + xor(self.baseCipher.encrypt(ctrModePl), plainText[(counter - 1) * 16:counter * 16]) ct = ct + ccmMIC return ct
'CCM cipherText too long for given L field size') if lenMessage < 0: raise (DecryptError, 'Too small of cipherText for MIC size') packedLenMessage = pack( '!Q', lenMessage)[-self.L:] # pack and truncate to L bytes pt = '' ct = cipherText[:-self.M] # trim of MIC field numCtrBlocks, extra = divmod(len(ct) + self.blockSize, self.blockSize) for counter in range(1, numCtrBlocks + 1): ctrModePl = chr(self.L - 1) + nonce + pack('>Q', counter)[-self.L:] ctr = self.baseCipher.encrypt(ctrModePl) ctBlock = ct[(counter - 1) * self.blockSize:counter * self.blockSize] pt = pt + xor(ctr, ctBlock) #------- CBC Mac Calculation blockZero = flagsByte + nonce + packedLenMessage if len(blockZero) != self.baseCipher.blockSize: raise (DecryptError, 'CCM bad size of first block') authLengthField = self._encodeAuthLength(len(addAuthData)) cbcInput = blockZero + authLengthField + addAuthData authPadSize = self.baseCipher.blockSize - ( (len(cbcInput) - 1) % self.baseCipher.blockSize) - 1 cbcInput = cbcInput + authPadSize * chr( 0) # pad to block size with zeros cbcInput = cbcInput + pt cbcEndPad = chr(0x00) * ((self.blockSize - ( (len(cbcInput)) % self.blockSize)) % self.blockSize) cbcInput = cbcInput + cbcEndPad
lenMessage = len(cipherText)-self.M if lenMessage >= 1L<<(8*self.L): raise DecryptError, 'CCM cipherText too long for given L field size' if lenMessage < 0 : raise DecryptError, 'Too small of cipherText for MIC size' packedLenMessage = pack('!Q', lenMessage)[-self.L:] # pack and truncate to L bytes pt = '' ct = cipherText[:-self.M] # trim of MIC field numCtrBlocks,extra = divmod(len(ct)+self.blockSize,self.blockSize) for counter in range(1, numCtrBlocks+1) : ctrModePl = chr(self.L-1) + nonce + pack('>Q', counter)[-self.L:] ctr = self.baseCipher.encrypt(ctrModePl) ctBlock = ct[(counter-1)*self.blockSize:counter*self.blockSize] pt = pt + xor( ctr, ctBlock ) #------- CBC Mac Calculation blockZero = flagsByte+nonce+packedLenMessage if len(blockZero) != self.baseCipher.blockSize: raise DecryptError, 'CCM bad size of first block' authLengthField = self._encodeAuthLength(len(addAuthData)) cbcInput = blockZero+authLengthField+addAuthData authPadSize = self.baseCipher.blockSize-((len(cbcInput)-1)%self.baseCipher.blockSize)-1 cbcInput = cbcInput + authPadSize*chr(0) # pad to block size with zeros cbcInput = cbcInput + pt cbcEndPad = chr(0x00)*((self.blockSize-((len(cbcInput))%self.blockSize))%self.blockSize) cbcInput = cbcInput + cbcEndPad # Calculate CBC_MAC numCbcBlocks,extra = divmod(len(cbcInput),self.blockSize)
def decrypt(self, cipherText, nonce, addAuthData=''): """ CCM decryption of cipherText nonce must be unique for each encryption, if set to none it will maintain it's own nonce creation the nonce is then included in the cipher text addAuthData is option """ # construct authentication block zero # flag byte fields Adata = ((len(addAuthData)) > 0) << 6 # bit 6 is 1 if auth Mfield = ((self.M - 2) / 2) << 3 # bits 5,4,3 encode macSize Lfield = self.L - 1 # bits 2,1,0 encode L size = blockSize-nonceSize-1 flagsByte = chr(Adata ^ Mfield ^ Lfield) if len(nonce) != self.nonceSize: raise DecryptError('wrong sized nonce') lenMessage = len(cipherText) - self.M if lenMessage >= 1 << (8 * self.L): raise DecryptError( 'CCM cipherText too long for given L field size') if lenMessage < 0: raise DecryptError('Too small of cipherText for MIC size') packedLenMessage = pack( '!Q', lenMessage)[-self.L:] # pack and truncate to L bytes pt = '' ct = cipherText[:-self.M] # trim of MIC field numCtrBlocks, extra = divmod(len(ct) + self.blockSize, self.blockSize) for counter in range(1, numCtrBlocks + 1): ctrModePl = chr(self.L - 1) + nonce + pack('>Q', counter)[-self.L:] ctr = self.baseCipher.encrypt(ctrModePl) ctBlock = ct[(counter - 1) * self.blockSize:counter * self.blockSize] pt = pt + xor(ctr, ctBlock) #------- CBC Mac Calculation blockZero = flagsByte + nonce + packedLenMessage if len(blockZero) != self.baseCipher.blockSize: raise DecryptError('CCM bad size of first block') cbcInput = blockZero # DB: don't encode auth length if there is no adata if len(addAuthData): authLengthField = self._encodeAuthLength(len(addAuthData)) cbcInput += authLengthField + addAuthData authPadSize = self.baseCipher.blockSize - ( (len(cbcInput) - 1) % self.baseCipher.blockSize) - 1 cbcInput = cbcInput + authPadSize * chr( 0) # pad to block size with zeros cbcInput = cbcInput + pt cbcEndPad = chr(0x00) * ((self.blockSize - ( (len(cbcInput)) % self.blockSize)) % self.blockSize) cbcInput = cbcInput + cbcEndPad # Calculate CBC_MAC numCbcBlocks, extra = divmod(len(cbcInput), self.blockSize) assert (extra == 0), 'bad block size on cbc_mac calculation' cbcMicValue = self.blockSize * chr(0x00) for i in range(numCbcBlocks): cbcBlock = cbcInput[i * self.blockSize:(i + 1) * self.blockSize] cbcMicValue = self.baseCipher.encrypt(xor(cbcMicValue, cbcBlock)) ctrModePl0 = chr(self.L - 1) + nonce + pack('>Q', 0)[-self.L:] ccmMIC = xor(self.baseCipher.encrypt(ctrModePl0), cbcMicValue)[:self.M] # first 8 bytes of xor if ccmMIC != cipherText[-self.M:]: raise IntegrityCheckError('CCM Integrity check failed on decrypt') return pt
def encrypt(self, plainText, nonce, addAuthData=''): """ CCM encryption of plainText nonce must be unique for each encryption, if set to none it will maintain it's own nonce creation addAuthData is optional """ # construct authentication block zero # flag byte fields Adata = ((len(addAuthData)) > 0) << 6 # bit 6 is 1 if auth Mfield = ((self.M - 2) / 2) << 3 # bits 5,4,3 encode macSize Lfield = self.L - 1 # bits 2,1,0 encode L size = blockSize-nonceSize-1 flagsByte = chr(Adata ^ Mfield ^ Lfield) if len(nonce) != self.nonceSize: raise EncryptError('wrong sized nonce') lenMessage = len(plainText) if lenMessage >= 1 << (8 * self.L): raise EncryptError('CCM plainText too long for given L field size') packedLenMessage = pack( '!Q', lenMessage)[-self.L:] # pack and truncate to L bytes blockZero = flagsByte + nonce + packedLenMessage if len(blockZero) != self.baseCipher.blockSize: raise EncryptError('CCM bad size of first block') cbcInput = blockZero # DB: don't encode auth length if there is no adata if len(addAuthData): authLengthField = self._encodeAuthLength(len(addAuthData)) cbcInput += authLengthField + addAuthData authPadSize = self.baseCipher.blockSize - ( (len(cbcInput) - 1) % self.baseCipher.blockSize) - 1 cbcInput = cbcInput + authPadSize * chr( 0) # pad to block size with zeros cbcInput = cbcInput + plainText cbcEndPad = chr(0x00) * ((self.blockSize - ( (len(cbcInput)) % self.blockSize)) % self.blockSize) cbcInput = cbcInput + cbcEndPad # Calculate CBC_MAC numCbcBlocks, extra = divmod(len(cbcInput), self.blockSize) assert (extra == 0), 'bad block size on cbc_mac calculation' cbcMicValue = self.blockSize * chr(0x00) for i in range(numCbcBlocks): cbcBlock = cbcInput[i * self.blockSize:(i + 1) * self.blockSize] cbcMicValue = self.baseCipher.encrypt(xor(cbcMicValue, cbcBlock)) counter = 0 # the counter mode preload with counter starting at zero ctrModePl = chr(self.L - 1) + nonce + pack('>Q', counter)[-self.L:] ccmMIC = xor(self.baseCipher.encrypt(ctrModePl), cbcMicValue)[:self.M] # first M bytes of xor ct = '' numCtrBlocks, extra = divmod( len(plainText) + self.blockSize, self.blockSize) while counter < numCtrBlocks: counter = counter + 1 ctrModePl = chr(self.L - 1) + nonce + pack('>Q', counter)[-self.L:] ct = ct + xor(self.baseCipher.encrypt(ctrModePl), plainText[(counter - 1) * 16:counter * 16]) ct = ct + ccmMIC return ct