def HMAC(self, key_bytes, message_bytes): '''Provides the bytes of the hashed message authentication code. To convert to hexadecimal, the bytes need to be passed through MySHA256.hexdigest(result). Returns the bytes of the hashed message authentication code.''' # Convert the ipad and the opad to bitarrays ipadBitArray = self.ipadToBits() opadBitArray = self.opadToBits() # Pad the key paddedKey = self.padKey(key_bytes) # Fuse the key with the ipad and opad fusedIpad = self.fusePadWithKey(ipadBitArray, paddedKey) fusedOpad = self.fusePadWithKey(opadBitArray, paddedKey) # Prepend the message with the fused ipad inputMessage = bytearray(fusedIpad.ToBytes()) inputMessage.extend(message_bytes) # Hash the ipad to begin hashing the message mySHA = MySHA256() ipadHashPart = mySHA.hash(BytesToWords(inputMessage)) ipadHashPartBits = MyBitArray() ipadHashPartBits.FromBytes(ipadHashPart) # Take the result hash bytes and prepend the fused opad finalHashInput = MyBitArray() finalHashInput.FromBytes(fusedOpad.ToBytes()) finalHashInput.extend(ipadHashPartBits) # Hash the resulting concatenation result = mySHA.hash(BytesToWords(finalHashInput.ToBytes())) return result
def padMessage(message_bytes): ''' Pads a given byte array of a message with a 1 followed by 0s and finally the length of the original message in the last 8 bytes of the last message block. ''' lastMessageBitIndex = 448 # Guard conditions if MySHA256.blockLength < 0: raise ValueError("MySHA256 block length should be set to 512, but is set to a negative number.") originalMessageLengthBytes = MySHA256.messageLengthToBytes(message_bytes) if len(originalMessageLengthBytes) > (MySHA256.blockLength - lastMessageBitIndex): raise ValueError("MySHA256 cannot process a message of this length.") # Get length of block in bytes lastBlockLength = (len(message_bytes) % (int(MySHA256.blockLength/8))) nZeroes = lastMessageBitIndex - 1 - (lastBlockLength * 8) if nZeroes < 0: # Add a whole block of padding if necessary, to fit with the padding scheme nZeroes = MySHA256.blockLength + nZeroes - 1 # Add padding messageBitArray = MyBitArray() messageBitArray.FromBytes(message_bytes) messageBitArray.extend([1]) messageBitArray.extend([0] * nZeroes) # Append length of the original message result = bytearray(messageBitArray.ToBytes()) result.extend(originalMessageLengthBytes) return bytes(result)
def padKey(self, key_bytes): '''Pads the used key with zeroes on the right until it meets the expected hash input length. Doesn't return the padded key, just changes it in-place.''' paddedKey = MyBitArray() paddedKey.FromBytes(key_bytes) if len(paddedKey) < BrownThomasHMAC.expectedBlockLength: padding = [0] * (len(paddedKey) - BrownThomasHMAC.expectedBlockLength) padding.extend(paddedKey.bits) paddedKey.FromBits(padding) elif len(paddedKey) > BrownThomasHMAC.expectedBlockLength: paddedKey.bits = paddedKey.bits[:BrownThomasHMAC.expectedBlockLength] return paddedKey
def fusePadWithKey(self, pad_bit_array, padded_key_bit_array): '''Fuses a key with the given ipad or opad. XORs the key with the leftmost bits of the given pad. The pad will not be the same size as the key. The key will be a smaller size. Returns the padded key as a bit array.''' padFirstPart = MyBitArray() padFirstPart.FromBits(pad_bit_array.bits[:len(padded_key_bit_array)]) resultFirstPart = padFirstPart ^ padded_key_bit_array result = MyBitArray() result.FromBits(padFirstPart.bits) result.extend(pad_bit_array[len(padFirstPart):]) return result
def ProcessPermutation(self, bit_array, permutation_box, is_reversed = False): # Make resulting bit array result = MyBitArray() # Process the permutation if is_reversed == False: for x in permutation_box: result.append(bit_array[x - 1]) else: # Reverse the permutation result.bits = [0] * len(bit_array) for i in range(len(permutation_box)): result[permutation_box[i] - 1] = bit_array[i] return result
def RemovePadding(self, padded_block_bit_array): blockBytes = padded_block_bit_array.ToBytes() # Search for existence of padding padByte = self.GetPaddingByte(blockBytes) if padByte != -1: endIndex = len(blockBytes) - int(padByte) unpaddedBytes = bytes(blockBytes[:endIndex]) unpaddedBits = MyBitArray() unpaddedBits.FromBytes(unpaddedBytes) return unpaddedBits else: originalBitArray = MyBitArray() originalBitArray.extend(padded_block_bit_array) return originalBitArray
def ProcessKeySchedule(self, feistel_round_info, is_encryption): if len(feistel_round_info.KeyLeft) != 28 or len(feistel_round_info.KeyRight) != 28: raise ValueError("Inappropriate key encountered when processing key schedule.") if is_encryption == True: self.RotateKeyHalves(feistel_round_info, is_encryption) # Get the subkey preSubkey = MyBitArray() preSubkey.extend(feistel_round_info.KeyLeft) preSubkey.extend(feistel_round_info.KeyRight) subkey = self.ProcessPermutation(preSubkey, self.pc2Box) if is_encryption == False: self.RotateKeyHalves(feistel_round_info, is_encryption) return subkey
def CreateInitializationVector(self): bitLength = 0 initializationVector = bytearray() while bitLength < self.expectedBlockLength: appendage = randint(0, 255) initializationVector.append(appendage) bitLength += appendage.bit_length() if bitLength >= self.expectedBlockLength: break resultBitArray = MyBitArray() resultBitArray.FromBytes(initializationVector) # Truncate result to expectedBlockLength # of bits resultBitArray.bits = resultBitArray.bits[:self.expectedBlockLength] return resultBitArray
def FeistelFunction(self, data_input_right_half, subkey): # E box (Expansion) expandedData = self.ProcessPermutation(data_input_right_half, self.expansionBox) # XOR Mr with round's subkey sBoxInput = expandedData ^ subkey # Process through S-boxes joinedSBoxOutput = MyBitArray() sBoxes = [self.sBox1, self.sBox2, self.sBox3, self.sBox4, self.sBox5, self.sBox6, self.sBox7, self.sBox8] for i in range(len(sBoxes)): index = i * self.sBoxInputLength sBoxOutput = self.ProcessSBox(sBoxInput[index : index + self.sBoxInputLength], sBoxes[i]) joinedSBoxOutput.extend(sBoxOutput) # Permute result result = self.ProcessPermutation(joinedSBoxOutput, self.permutationBox) return result