Last active
February 23, 2016 15:36
-
-
Save pugilist/18f9c5c7c173f93dcbd3 to your computer and use it in GitHub Desktop.
cryptofunctions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3.4 | |
| # | |
| # Title: mth_275 | |
| # | |
| # Author: Dave R. | |
| # | |
| # License: CC BY-NC-SA 3.0 | |
| # http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US | |
| # | |
| # Version: | |
| # 2.0 | |
| # | |
| # Description: | |
| # This class contains a number of basic cryptographic functions | |
| # It roughly follows the curriculum of the mth_257 course at | |
| # Champlain College | |
| # | |
| # Usage: | |
| # Import to use as a library. | |
| # | |
| # | |
| # Requirements: | |
| # python3.4 | |
| # | |
| # | |
| import string | |
| from collections import OrderedDict | |
| import copy | |
| import binascii | |
| class cipher(object): | |
| def __init__(self): | |
| self.available_charsets = { | |
| 'loweralpha':list(string.ascii_lowercase), | |
| 'upperalpha':list(string.ascii_uppercase), | |
| 'numeric':list(string.digits) } | |
| self.charset = [] | |
| self.set_charset() | |
| self.plainnumeric = list() | |
| self.ciphernumeric = list() | |
| self.plaintext = self._split_string(self.plaintext) | |
| self.ciphertext = self._split_string(self.ciphertext) | |
| ############################################# | |
| # | |
| # Private methods shared by all ciphers | |
| def _split_string(self,input_string): | |
| # since we don't want to deal with strings, we'll split them up into lists of characters | |
| return [ char for char in input_string ] | |
| def _concat_list(self,input_list): | |
| return ''.join(input_list) | |
| def _encrypt(self): | |
| # override these functions in sub-classes | |
| pass | |
| def _decrypt(self): | |
| # override these functions in sub-classes | |
| pass | |
| def _abc_to_num(self,input_list): | |
| # | |
| # A list of strings to numbers | |
| # | |
| # Returns a list of numbers | |
| # | |
| digits=[] | |
| for character in input_list: | |
| if character.isalpha(): | |
| digits.append(self.charset.index(character)) | |
| return digits | |
| def _num_to_abc(self,input_list): | |
| # | |
| # Takes a list of digits and translates to letters | |
| # | |
| # outputs a list of characters | |
| # | |
| abc=[] | |
| for element in input_list: | |
| abc.append(self.charset[element]) | |
| return abc | |
| def _gcd(self,a,b): | |
| # | |
| # takes two ints as arguments | |
| # | |
| # returns the GCD | |
| # | |
| if b == 0: | |
| return a | |
| else: | |
| self._gcd( b,a % b ) | |
| # Credit for _egcd and _modinv goes to https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm | |
| def _egcd(self,a, b): | |
| if a == 0: | |
| return (b, 0, 1) | |
| else: | |
| g, y, x = self._egcd(b % a, a) | |
| return (g, x - (b // a) * y, y) | |
| def _modinv(self,a, m): | |
| g, x, y = self._egcd(a, m) | |
| if g != 1: | |
| raise ValueError('[ ! ] Modular inverse does not exist, cannot continue.') | |
| else: | |
| return x % m | |
| def tabular_to_cycle(self,tabular_cycle): | |
| # | |
| # This function takes a dictionary as an argument with the tabular key | |
| # Ex.: tabular_to_cycle({'a':'d','b':'c','c':'a','d':'b','e':'e'}) | |
| # it returns a list of lists containing the disjointed cycles | |
| # [['a', 'd', 'b', 'c'], ['e']] | |
| # | |
| # create a copy of our input variable so as not to modify the original data passed to the function. | |
| tmp = copy.copy(tabular_cycle) | |
| cycle= list() | |
| num_cycles=0 | |
| while len(tmp) > 0: | |
| (key,value) = tmp.popitem() | |
| #print("[+] Starting key: %s" % key) | |
| #print("[+] tmp: %s" % tmp) | |
| cycle.append([]) | |
| cycle[num_cycles].append(key) | |
| while True: | |
| if key is not value: | |
| cycle[num_cycles].append(value) | |
| old_value = value | |
| try: | |
| value = tmp[value] | |
| del tmp[old_value] | |
| except: | |
| break | |
| if value == key: | |
| break | |
| num_cycles += 1 | |
| return cycle | |
| def get_cycle_type(self,tabular_cycle): | |
| cycle = self.tabular_to_cycle(tabular_cycle) | |
| result = list() | |
| for group in cycle: | |
| result.append(len(group)) | |
| return result.sort() | |
| ############################################# | |
| # | |
| # Pointers to private methods which will be used for encryption / decryption | |
| def encrypt(self): | |
| # do not override this function. | |
| if self.plaintext == '' or self.key == '': | |
| raise ValueError("[ ! ] Input error, make sure to specify a key and a plaintext string") | |
| else: | |
| #return self._concat_list( self._encrypt() ) | |
| return self._encrypt() | |
| def decrypt(self): | |
| # do not override this function. | |
| if self.ciphertext == '' or self.key == '': | |
| raise ValueError("[ ! ] Input error, make sure to specify a key and a ciphertext string") | |
| else: | |
| #return self._concat_list( self._decrypt() ) | |
| return self._decrypt() | |
| def set_charset(self,selection='loweralpha'): | |
| # | |
| # allows us to set the charset for our instance | |
| # | |
| # Pass the function a sinlge string (ex: loweralpha) to set the charset to loweralpha. | |
| # | |
| # You may also pass the function a list of strings (ex: ['loweralpha','upperalpha'] ) | |
| # to produce a list of both of them combined and set that as the charset. | |
| # | |
| # | |
| # if only one charset is specified... | |
| if type(selection) is str: | |
| # if we don't recognise the input, throw an error | |
| if selection not in self.available_charsets.keys(): | |
| print("Error, unknown input \'%s\' is not an available charset.\n Acceptable options are: %s") % (selection,self.available_charsets.keys()) | |
| else: | |
| self.charset = self.available_charsets[selection] | |
| elif type(selection) is list: | |
| for element in selection: | |
| if element not in self.available_charsets.keys(): | |
| print("Error, unknown input \'%s\' is not an available charset.\n Acceptable options are: %s") % (selection,self.available_charsets.keys()) | |
| else: | |
| self.charset += self.available_charsets[element] | |
| ############################################# | |
| # | |
| # Common Cryptanalysis class functions shared by all ciphers. | |
| def find_grams(self,min_length=2,max_length=None): | |
| # | |
| # Finds grams in our ciphertext | |
| # | |
| # min_length and max_length specifies the types of grams we are looking | |
| # for. | |
| # | |
| # returns a dict of dicts with the following structure: | |
| # | |
| # { 'gram_length' : { 'gram': number_of_occurances } } | |
| # | |
| # NOTE: this method was ported to the new version of this library without resolving it's major limitation: | |
| # It cannot find grams in numerical ciphertext, essentially, this only really works for mod 26 gram analysis. | |
| # concatinate our ciphertext list into a string so this function works. | |
| input_string = self._concat_list(self.ciphertext) | |
| # if we didn't specify a max length, use the length of the string divided by 2. This will find all possible grams. | |
| if max_length is None: | |
| max_length = int(len(input_string)/2) | |
| grams = {} | |
| # do this so that max_length actually retuns grams of that length. | |
| # otherwise max_length only produces grams _less than_ max_length | |
| max_length += 1 | |
| #set up our dict of dicts for each gram length we're looking for | |
| for i in range(min_length,max_length): | |
| grams[i] = {} | |
| for char in range(len(input_string)): | |
| for gram_length in range(min_length,max_length): | |
| #slice out text for a given length | |
| gram = input_string[char:char+gram_length] | |
| if gram in grams[gram_length].keys(): | |
| grams[gram_length][gram] += 1 | |
| else: | |
| grams[gram_length][gram] = 1 | |
| # now clean up any grams that only show up once and return the dict... | |
| for x in list(grams.keys()): | |
| for y in list(grams[x].keys()): | |
| if grams[x][y] == 1: | |
| del grams[x][y] | |
| if len(grams[x]) == 0: | |
| del grams[x] | |
| return grams | |
| class sub_cipher(cipher): | |
| def __init__(self, plaintext, ciphertext, key): | |
| self.plaintext = plaintext | |
| self.ciphertext = ciphertext | |
| self.key = key | |
| # call the parent class | |
| super().__init__() | |
| ############################################# | |
| # | |
| # Common Cryptanalysis class functions shared by all substitution ciphers. | |
| def _count_letters(self): | |
| # | |
| # count the occurences of each letter | |
| # in the given string | |
| # | |
| # returns a dict with the following structure | |
| # | |
| # { 'letter': number_of_occurances } | |
| # | |
| count= {} | |
| for character in self.ciphertext: | |
| if character in count: | |
| count[character] += 1 | |
| else: | |
| count[character] = 1 | |
| return count | |
| def _find_common_letters(self,limit=10): | |
| letter_count = self._count_letters() | |
| value_list = [] | |
| result = [] | |
| #grab the a lists of all of the values and make sure there's no duplicates. | |
| for key,value in letter_count.items(): | |
| if value not in value_list: | |
| value_list.append(value) | |
| # reverse sort the list ( highest -> lowest ) | |
| value_list.sort(reverse=True) | |
| for i in range(0,limit-1): | |
| for key,value in letter_count.items(): | |
| if len(result) == 10: | |
| break | |
| elif value == value_list[i]: | |
| result.append(key) | |
| return result | |
| def _count_letters_pct(self,accuracy=4): | |
| # | |
| # takes a string and an optional integer as args | |
| # | |
| # optional arg specifies decimal places to be displayed | |
| # | |
| # returns a dict similar to count_letters() except using percentage | |
| # | |
| count = self._count_letters() | |
| str_len = len(self.ciphertext) | |
| for key,val in count.items(): | |
| count[key] = round(float(val) / float(str_len),accuracy) | |
| return count | |
| def print_letter_count(self): | |
| count = self._count_letters() | |
| count_pct = self._count_letters_pct() | |
| for character in self.charset: | |
| try: | |
| print("{0:<6s} {1:^6d} {2:>6f}".format(character,count[character],count_pct[character])) | |
| except KeyError: | |
| # if the letter does no occur in the ciphertext, catch this exception and print zeroes. | |
| print("{0:<6s} {1:^6d} {2:>6f}".format(character,0,0)) | |
| class block_cipher(cipher): | |
| def __init__(self): | |
| # the key is probably going to be a matrix, here, so we'll need to derive the blocksize from the key dimensions... Right? | |
| # call the parent class | |
| #super().__init__() | |
| pass | |
| ############################################# | |
| # | |
| # Common Cryptanalysis class functions shared by all block ciphers. | |
| # nothing here yet :) | |
| class cesar_cipher(sub_cipher): | |
| def _encrypt(self): | |
| self.ciphertext = self._cipher(self.plaintext,self.key) | |
| return self.ciphertext | |
| def _decrypt(self): | |
| self.plaintext = self._cipher(self.ciphertext,self.key,False) | |
| return self.plaintext | |
| def _cipher(self, input_list,key,encrypt=True): | |
| result = [] | |
| for character in input_list: | |
| original_pos = self.charset.index(character) | |
| if encrypt: | |
| new_pos = (original_pos + key ) % len(self.charset) | |
| else: | |
| new_pos = (original_pos - key ) % len(self.charset) | |
| result.append( self.charset[new_pos] ) | |
| return result | |
| class affine(sub_cipher): | |
| def __init__(self, plaintext, ciphertext, key,beta,mod): | |
| self.beta = beta | |
| self.mod = mod | |
| super().__init__(plaintext,ciphertext,key) | |
| def _encrypt(self): | |
| self.plainnumeric = self._abc_to_num(self.plaintext) | |
| return [ ((number * self.key) + self.beta) % self.mod for number in self.plainnumeric ] | |
| def _decrypt(self): | |
| # catch bad options nicely | |
| try: | |
| modinv = self._modinv(self.key,self.mod) | |
| except ValueError as e: | |
| print(e) | |
| self.ciphernumeric = self._abc_to_num(self.ciphertext) | |
| return [ self.key * ( number - self.beta ) % self.mod for number in self.ciphernumeric ] | |
| class vigenere(sub_cipher): | |
| def __init__(self, plaintext, ciphertext, key): | |
| if not isinstance(key,str): | |
| raise TypeError("[ ! ] Key must be a string.") | |
| # call the parent class | |
| super().__init__( plaintext, ciphertext, key ) | |
| self.key = self._split_string(self.key) | |
| def _encrypt(self): | |
| self.ciphertext = self._cipher() | |
| return self.ciphertext | |
| def _decrypt(self): | |
| self.plaintext = self._cipher() | |
| return self.plaintext | |
| def _cipher(self,input_list,key,encrypt=True): | |
| output_list = [] | |
| if encrypt: | |
| numeric_list = self._abc_to_num(self.plaintext) | |
| else: | |
| numeric_list = self._abc_to_num(self.ciphertext) | |
| numeric_key = self._abc_to_num(self.key) | |
| key_len = len(self.key) | |
| for i in range(0, len(numeric_list) ): | |
| offset = numeric_key[ (i % key_len) ] | |
| if encrypt: | |
| output_list.append( (numeric_list[i] + key[offset] ) % len(self.charset) ) | |
| else: | |
| output_list.append( (numeric_list[i] - key[offset] ) % len(self.charset) ) | |
| ############################################# | |
| # | |
| # Common Cryptanalysis class functions. | |
| def kasiski(self): | |
| coincidences = self.kasiski_raw() | |
| # print a header | |
| print("{0:<9s} {1:<7s} {2:4<s}".format("offset","hits","graph")) | |
| for (key,value) in coincidences.items(): | |
| #make it look purdy. | |
| graph = '' | |
| for i in range(0,value): | |
| graph += "+" | |
| print("{0:<9d} {1:<7d} {2:4<s}".format(key,value,graph)) | |
| def kasiski_raw(self): | |
| # | |
| # Takes a string as an argument | |
| # | |
| # iterates through all positions and retuns a dict of | |
| # offsets and the number of coincidences for each. | |
| # | |
| # Data structure is as follows | |
| # { 'offset': number_of_coincidences } | |
| # | |
| coincidences={} | |
| starting_pos=0 | |
| str_len=len(self.ciphertext) | |
| for i in range(0,str_len): | |
| coincidences[i] = 0 | |
| for j in range(0,str_len): | |
| # the offset we'll use to compare the two strings, note this is mod str_len so that the search "wraps" around the end of the string and continues at the begining | |
| offset=(j+i) % str_len | |
| if self.ciphertext[offset] == self.ciphertext[j]: | |
| coincidences[i] += 1 | |
| return coincidences | |
| class permutation(sub_cipher): | |
| def __init__(self,plaintext,ciphertext,key): | |
| if not isinstance(key,dict): | |
| raise TypeError("[ ! ] Key must be a dictionary ( { plaintext : ciphertext } )") | |
| super().__init__(plaintext,ciphertext,key) | |
| def _cipher(self,input_list,highlight=False,hide_missing=False): | |
| # | |
| # This takes a string of ciphertext and a dictionary as arguments. | |
| # The dictionary is formatted as follows { 'cyphertext value':'plaintext value' } | |
| # | |
| # Highlight makes the plaintext output uppercase to make it more readable. Note | |
| # that this feature probably doesn't work if your charset is not loweralpha (oh well). | |
| # | |
| # hide_missing replaces the ciphertext with underscores to make it more readable | |
| # | |
| result = [] | |
| for char in input_list: | |
| if char in key.keys(): | |
| if highlight: | |
| result.append( key[char].upper() ) | |
| else: | |
| result.append( key[char] ) | |
| else: | |
| if hide_missing: | |
| result.append("_") | |
| else: | |
| result.append(char) | |
| return result | |
| def _encrypt(self): | |
| self.ciphertext = self._cipher(self.plaintext) | |
| return self.ciphertext | |
| def _decrypt(self): | |
| self.plaintext = self._cipher(self.ciphertext) | |
| return self.plaintext | |
| def modify_key(): | |
| # maybe implement this setter function to allow for easy modification of the key variable. | |
| pass | |
| ############################################# | |
| # | |
| # Common Cryptanalysis class functions. | |
| # | |
| # Fix me! | |
| # | |
| def print_bigram_matrix(self): | |
| # | |
| # Takes the cyphertext string and list of most common letters as an argument | |
| # | |
| # Prints a matrix for bigram analysis | |
| # | |
| matrix = self._get_bigram_matrix() | |
| most_common_letters = self._find_common_letters() | |
| #dump out our header | |
| print(''.join('\t{}'.format(*k) for k in matrix.keys())) | |
| print('----------'.join(''.format(*k) for k in matrix.keys())) | |
| # this is probably more complicated than it needed to be... | |
| # this prints out the table from the data returned by get_bigram_matrix() | |
| for key in matrix.keys(): | |
| print(' |\n%s | ' % key,end="") | |
| for subkey,value in matrix[key].items(): | |
| if subkey in most_common_letters: | |
| print('\t%d' % value, end="") | |
| print('\n |') | |
| # | |
| # Fix me! | |
| # | |
| def _get_bigram_matrix(self): | |
| # | |
| # Takes a string and a list as arguments, returns a big ugly dict of dicts indicating | |
| # The bigram matrix values. | |
| # | |
| # Items in this datastructure can be called by their row column, like so: | |
| # | |
| # var['row']['col] | |
| # | |
| # grab our most common letters. | |
| most_common_letters = self._find_common_letters() | |
| # build our matrix using ordered dictionaries | |
| matrix = OrderedDict( ( key, OrderedDict( (key2, 0) for key2 in most_common_letters )) for key in most_common_letters ) | |
| # populate our matrix with information given the common letters specified. | |
| for element in most_common_letters: | |
| #fix this... | |
| for key,value in self._adjacent_letter_count(element).items(): | |
| matrix[element][key] = value[1] | |
| if key in most_common_letters: | |
| matrix[key][element] = value[0] | |
| return matrix | |
| # | |
| # Fix me! | |
| # | |
| def _adjacent_letter_count(self,letter): | |
| # | |
| # Takes a string and a letter as arguments | |
| # | |
| # Returns a dict structured as follows | |
| # | |
| # { 'character': [ occurences_before, occurences_after ] } | |
| # | |
| #set up our results dict using the charaset of our chosing | |
| result=dict((element,[0,0]) for element in self.charset) | |
| # need to iterate by string position not letter, use range and len() instead | |
| for i in range(0,len(self.ciphertext)): | |
| if self.ciphertext[i] == letter: | |
| result[self.ciphertext[i-1]][0] += 1 | |
| try: | |
| result[self.ciphertext[i+1]][1] += 1 | |
| except IndexError: | |
| pass | |
| return result | |
| class enigma(sub_cipher): | |
| # | |
| # This is incomplete. I'm lazy and might never complete it. | |
| # | |
| def __init__(self, message='', rotor_settings=dict(), kenngruppen='', plug_settings=dict(), reflector='b'): | |
| # rotors | |
| self.available_rotors = { | |
| 1: OrderedDict([('a', 'e'), ('b', 'k'), ('c', 'm'), ('d', 'f'), ('e', 'l'), ('f', 'g'), ('g', 'd'), ('h', 'q'), ('i', 'v'), ('j', 'z'), ('k', 'n'), ('l', 't'), ('m', 'o'), ('n', 'w'), ('o', 'y'), ('p', 'h'), ('q', 'x'), ('r', 'u'), ('s', 's'), ('t', 'p'), ('u', 'a'), ('v', 'i'), ('w', 'b'), ('x', 'r'), ('y', 'c'), ('z', 'j')]), | |
| 2: OrderedDict([('a', 'a'), ('b', 'j'), ('c', 'd'), ('d', 'k'), ('e', 's'), ('f', 'i'), ('g', 'r'), ('h', 'u'), ('i', 'x'), ('j', 'b'), ('k', 'l'), ('l', 'h'), ('m', 'w'), ('n', 't'), ('o', 'm'), ('p', 'c'), ('q', 'q'), ('r', 'g'), ('s', 'z'), ('t', 'n'), ('u', 'p'), ('v', 'y'), ('w', 'f'), ('x', 'v'), ('y', 'o'), ('z', 'e')]), | |
| 3: OrderedDict([('a', 'b'), ('b', 'd'), ('c', 'f'), ('d', 'h'), ('e', 'j'), ('f', 'l'), ('g', 'c'), ('h', 'p'), ('i', 'r'), ('j', 't'), ('k', 'x'), ('l', 'v'), ('m', 'z'), ('n', 'n'), ('o', 'y'), ('p', 'e'), ('q', 'i'), ('r', 'w'), ('s', 'g'), ('t', 'a'), ('u', 'k'), ('v', 'm'), ('w', 'u'), ('x', 's'), ('y', 'q'), ('z', 'o')]), | |
| 4: OrderedDict([('a', 'e'), ('b', 's'), ('c', 'o'), ('d', 'v'), ('e', 'p'), ('f', 'z'), ('g', 'j'), ('h', 'a'), ('i', 'y'), ('j', 'q'), ('k', 'u'), ('l', 'i'), ('m', 'r'), ('n', 'h'), ('o', 'x'), ('p', 'l'), ('q', 'n'), ('r', 'f'), ('s', 't'), ('t', 'g'), ('u', 'k'), ('v', 'd'), ('w', 'c'), ('x', 'm'), ('y', 'w'), ('z', 'b')]), | |
| 5: OrderedDict([('a', 'v'), ('b', 'z'), ('c', 'b'), ('d', 'r'), ('e', 'g'), ('f', 'i'), ('g', 't'), ('h', 'y'), ('i', 'u'), ('j', 'p'), ('k', 's'), ('l', 'd'), ('m', 'n'), ('n', 'h'), ('o', 'l'), ('p', 'x'), ('q', 'a'), ('r', 'w'), ('s', 'm'), ('t', 'j'), ('u', 'q'), ('v', 'o'), ('w', 'f'), ('x', 'e'), ('y', 'c'), ('z', 'k')]) | |
| } | |
| self.available_reflectors = { | |
| 'b' : OrderedDict([('a', 'f'), ('b', 'v'), ('c', 'p'), ('d', 'j'), ('e', 'i'), ('f', 'a'), ('g', 'o'), ('h', 'y'), ('i', 'e'), ('j', 'd'), ('k', 'r'), ('l', 'z'), ('m', 'x'), ('n', 'w'), ('o', 'g'), ('p', 'c'), ('q', 't'), ('r', 'k'), ('s', 'u'), ('t', 'q'), ('u', 's'), ('v', 'b'), ('w', 'n'), ('x', 'm'), ('y', 'h'), ('z', 'l')]) | |
| } | |
| self.plug_settings = plug_settings | |
| self.message = message | |
| self.rotor_settings = rotor_settings | |
| self.kenngruppen = kenngruppen | |
| self.reflector = reflector | |
| ############################################# | |
| # | |
| # Private methods for enigma class | |
| # because the germans liked to start the alphabet with 1 instead of 0, we need to override these functions | |
| def _abc_to_num(self,input_list): | |
| # | |
| # A list of strings to numbers | |
| # | |
| # Returns a list of numbers | |
| # | |
| digits=[] | |
| for character in input_list: | |
| if character.isalpha(): | |
| digits.append(self.charset.index(character)) | |
| return digits+1 | |
| def _num_to_abc(self,input_list): | |
| # | |
| # Takes a list of digits and translates to letters | |
| # | |
| # outputs a list of characters | |
| # | |
| abc=[] | |
| for element in input_list: | |
| abc.append(self.charset[element+1]) | |
| return abc | |
| def _rearrange_rotor(self): | |
| for rotor,setting in rotor_settings.items(): | |
| tipping_point = self._num_to_abc() | |
| while rotor[ len(rotor)-1 ] != tipping_point: | |
| #take an item off the front and put it at the end | |
| tmp = rotor.popitem(last=False) | |
| rotor[ tmp[0] ] = tmp[1] | |
| def _plugboard(self,letter): | |
| for key,value in self.rotor_settings.items(): | |
| if key == letter: | |
| if value == '': | |
| return letter | |
| else: | |
| return value | |
| if value == letter: | |
| return key | |
| # | |
| # TODO: | |
| # | |
| # -> write some shit for the message preamble | |
| # -> write soem shit for the identifier | |
| # -> write some shit for rotor permutations | |
| # -> some shit about iterating the permutation shit. | |
| # | |
| # -> override _(en|de)cryption routines | |
| # -> pretty print function | |
| class des(sub_cipher): | |
| def __init__(message='', key='', blocksize=64, rounds=16): | |
| self.message = message | |
| self.key = key | |
| self.blocksize = blocksize | |
| self.rounds = rounds | |
| #define our IP too... | |
| def _xor(a,b): | |
| if len(a) != len(b): | |
| raise ValueError("Error, both binary strings must be of the same length") | |
| else: | |
| result = '' | |
| for char in range(0, len(a)): | |
| result += str( int(a[char]) ^ int(b[char]) ) | |
| return result | |
| class sdes(des): | |
| def __init__(self, message=str(), key=str(), blocksize=8, rounds=3): | |
| self.message = message | |
| self.key = key | |
| self.blocksize = blocksize | |
| self.rounds = rounds | |
| self.key_schedule_mask = { | |
| 1: [10, 4, 1, 5, 11], | |
| 2: [8, 9, 3, 7, 2]} | |
| self.ip = None | |
| self.ip_inv = None | |
| #define our IP too... | |
| def _shift_string(bin_string, shift, left=True): | |
| if left: | |
| return bin_string[shift:] + bin_string[0:shift] | |
| else: | |
| strlen = len(bin_string) | |
| return bin_string[strlen-shift: strlen] + bin_string[: strlen-1] | |
| def get_key_schedule(self): | |
| result = dict() | |
| for index,mask in self.key_schedule_mask.items(): | |
| result[index] = list() | |
| for value in mask: | |
| # since our place values start with 1, we need to adjust to make them work with the list index values | |
| result[index].append(self.key[value-1]) | |
| return result | |
| def _cipher(): | |
| result = str() | |
| for i in range(0, len(self.plaintext), self.blocksize): | |
| try: | |
| chunk = self.plaintext[i : i+self.blocksize] | |
| except IndexError: | |
| chunk = self.plaintext[i:] | |
| while len(chunk) < self.blocksize: | |
| chunk += "0" | |
| #call some function | |
| # return some value | |
| class rsa(sub_cipher): | |
| def __init__(self,message=str(),p=int(),q=int(),pub_key=int(),priv_key=int()): | |
| # mod = p * q | |
| # 1 == (pub_key * priv_key ) % (p - 1) * ( q -1 ) | |
| pass | |
| def _encrypt(): | |
| # plaintext ** public_key % mod n | |
| pass | |
| def _decypt(): | |
| # ciphertext ** private_key % mod n | |
| pass | |
| def _cipher(self,char, mod): | |
| pass | |
| ''' | |
| 'some dumb string'.encode() | |
| #then we can get the binary contents if we really want by doing this... | |
| # there's gotta be a better way... | |
| bin(int(str( string[character] ))) | |
| ''' | |
| ############################################# | |
| # | |
| # Private Functions used by RSA | |
| def _mod_exponentiation(self,base,exponent,mod): | |
| expanded = self._bin_expand(exponent) | |
| squares = self._successive_square(base=base, highest_value=exponent, mod=mod) | |
| result = 1 | |
| for number in expanded: | |
| result = ( result * squares[number] ) % mod | |
| return result | |
| def _successive_square(self, base, highest_value, mod, exponent=1, result=dict()): | |
| if exponent == 1: | |
| result[exponent] = base | |
| exponent +=1 | |
| if exponent > highest_value: | |
| return result | |
| else: | |
| #print("[+] Working with:") | |
| #print(locals()) | |
| reduced_value = (base ** 2) % mod | |
| result[exponent] = reduced_value | |
| exponent *= 2 | |
| # if we have more work to do, recurse! | |
| return self._successive_square(base=reduced_value, highest_value=highest_value, mod=mod, exponent=exponent, result=result) | |
| def _bin_expand(self,value): | |
| result = list() | |
| expanded = bin(value) | |
| # trim off the 0b prefix. | |
| expanded = expanded[2:] | |
| str_len = len(expanded) | |
| for i in range(0,str_len): | |
| if expanded[i] == '1': | |
| result.append(2 ** (str_len-i-1)) | |
| return result | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment