def search(self, ctext: Any) -> List[SearchLevel]: deadline = (datetime.now() + self._config().objs["timeout"] if self._config().timeout is not None else datetime.max) success, expand_res = self.expand( [SearchLevel(name="input", result=CrackResult(value=ctext))]) if success: return expand_res nodes = set(expand_res) while datetime.now() < deadline: # logger.trace(f"Have node tree {nodes}") if len(nodes) == 0: raise LookupError("Could not find any solutions") best_node = self.findBestNode(nodes) nodes.remove(best_node) success, eval_res = self.evaluate(best_node) if success: # logger.trace(f"Success with node {best_node}") return eval_res nodes.update(eval_res) raise TimeoutError("Search ran out of time")
def cracker(config: Config, edge_template: "Edge", result: CrackResult) -> "Node": if not config.cache.mark_ctext(result.value): raise DuplicateNode() checker: Checker = config.objs["checker"] # Edges do not directly contain containers, so this is fine edge = copy(edge_template) ret = Node( parent=edge, level=SearchLevel(name=type(edge.route).__name__.lower(), result=result), depth=edge.source.depth + 1, ) edge.dest = ret check_res = checker(result.value) if check_res is not None: raise AuSearchSuccessful(target=ret, info=check_res) return ret
def decoding(config: Config, route: Union[Cracker, Decoder], result: Any, source: "Node") -> "Node": if not config.cache.mark_ctext(result): raise DuplicateNode() checker: Checker = config.objs["checker"] ret = Node( parent=None, level=SearchLevel(name=type(route).__name__.lower(), result=CrackResult(value=result)), depth=source.depth + 1, ) edge = Edge(source=source, route=route, dest=ret) ret.parent = edge check_res = checker(result) if check_res is not None: raise AuSearchSuccessful(target=ret, info=check_res) return ret
def evaluate(self, node: Node) -> (bool, Union[List[SearchLevel], List[Node]]): # logger.debug(f"Evaluating {node}") res = node.cracker.attemptCrack(node.parents[-1].result.value) # Detect if we succeeded, and if deduplication is needed logger.trace(f"Got {len(res)} results") ret = [] for i in res: success, res = self.expand(node.parents + [ SearchLevel(name=type(node.cracker).__name__.lower(), result=i) ]) if success: return True, res ret.extend(res) return False, ret
def handleDecodings( self, target: Any ) -> (bool, Union[Tuple[SearchLevel, str], List[SearchLevel]]): """ If there exists a decoding that the checker returns true on, returns (True, result). Otherwise, returns (False, names and successful decodings) The CrackResult object should only have the value field filled in MUST NOT recurse into decodings! evaluate does that for you! """ # This tag is necessary, as we could have a list as a decoding target, which would then screw over type checks ret = [] decoders = [] for decoder_type, decoder_class in registry[Decoder][type( target)].items(): for decoder in decoder_class: decoders.append(DecoderComparer(decoder)) # Fun fact: # with Python's glorious lists, inserting n elements into the right position (with bisect) is O(n^2) decoders.sort(reverse=True) for decoder_cmp in decoders: logger.trace(f"Inspecting {decoder_cmp}") res = self._config()(decoder_cmp.value).decode(target) if res is None: continue level = SearchLevel( name=decoder_cmp.value.__name__.lower(), result=CrackResult(value=res), ) if type(res) == self._final_type: check_res = self._checker(res) if check_res is not None: return True, (level, check_res) ret.append(level) return False, ret