Instantly share code, notes, and snippets.

# GrantTrebbin/LockBox.py

Created May 19, 2017 10:23
Show Gist options
• Save GrantTrebbin/c3997bb2f07f897af25156e06ba0675c to your computer and use it in GitHub Desktop.
Generate sequences to brute force locks with mechaincal pin codes. Works for locks where order of numbers in PIN doesn't matter and numbers can't be repeated.
This file contains 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
 from itertools import combinations, chain # https://stackoverflow.com/questions/5920643/add-an-item-between-each-item-already-in-the-list def intersperse(lst, item): result = [item] * (len(lst) * 2) result[0::2] = lst return result pins = [] number_of_digits = 10 # Generate all the possible PIN combinations for each PIN length for length in range(number_of_digits + 1): new_pins = [list(pin) for pin in list(combinations(range(number_of_digits), length))] # ensure that each pin is in order from smallest number to largest for pin in new_pins: pin.sort() # sort the pins element by element (lexicographically) new_pins.sort() pins.append(new_pins) # Start with all the pins of length half the number of buttons sequences = [[None, pin, None] for pin in pins[int(number_of_digits/2)]] # Add pins one shorter and one longer to the sequence of pins # Place them in the first logical spot found for offset in range(1, int(number_of_digits/2+1)): for short_pin in pins[int(number_of_digits/2) - offset]: for pin in sequences: difference = set(pin[1]) - set(short_pin) number_of_different_digits = len(difference) if (number_of_different_digits == 1) and (pin[0] is None): pin[0] = short_pin break for long_pin in pins[int(number_of_digits/2) + offset]: for pin in sequences: difference = set(long_pin) - set(pin[-2]) number_of_different_digits = len(difference) if (number_of_different_digits == 1) and (pin[-1] is None): pin[-1] = long_pin break # Make sure each sequence starts and ends with a None to allow the # iteration to work. for sequence in sequences: if sequence[0] is not None: sequence.insert(0, None) if sequence[-1] is not None: sequence.append(None) # Trim the None values from the start and end and then pad to the correct length for sequence in sequences: if sequence[0] is None: sequence.pop(0) if sequence[-1] is None: sequence.pop(-1) # Sort the sequences by the number of pins they cover sequences.sort(key=lambda x: -len(x)) aligned_sequences = [] for sequence in sequences: # Find the next button to press in each sequence next_digit_lists = [list(set(j) - set(i)) for i, j in zip(sequence[:-1], sequence[1:])] # Merge all the button presses together next_digits = list(chain(*next_digit_lists)) # Line up the pins for presentation new_sequence = [""] * (number_of_digits + 1) for pin in sequence: new_sequence[len(pin)] = pin # <> represents resetting the lock and _ represents testing if it will open # The output will be generated as a hash separated file for easy import into # a spreadsheet setup = ['<> '] setup.extend(intersperse(sequence[0], " ")) setup.append('_') setup.extend(intersperse(next_digits, "_")) button_sequence = ''.join(map(str, setup)) new_sequence = [button_sequence] + new_sequence aligned_sequences.append(new_sequence) # Print the sequences for sequence in aligned_sequences: pass print(*sequence, sep='#')