def repeat_match(password): matches = [] greedy = r"(.+)\1+" lazy = r"(.+?)\1+" lazy_anchored = r"^(.+?)\1+$" lastIndex = 0 while lastIndex < len(password): greedy_match = re.search(greedy, password[lastIndex:]) lazy_match = re.search(lazy, password[lastIndex:]) if greedy_match is None: break if len(greedy_match.group(0)) > len(lazy_match.group(0)): match = greedy_match base_token = re.search(lazy_anchored, match.group(0)).group(1) else: match = lazy_match base_token = match.group(1) i, j = [ match.start() + lastIndex, match.start() + len(match.group(0)) - 1 + lastIndex ] # TODO: Implement base analysis base_analysis = scoring.most_guessable_match_sequence( base_token, omnimatch(base_token)) base_matches = base_analysis[ "match_sequence"] if "match_sequence" in base_analysis and base_analysis[ "match_sequence"] is not None else None base_guesses = base_analysis["guesses"] matches.append({ "pattern": "repeat", "i": i, "j": j, "token": match.group(0), "base_token": base_token, "base_guesses": base_guesses, "base_matches": base_matches, "repeat_count": len(match.group(0)) / len(base_token) }) lastIndex = j + 1 return matches
def test_repeat_guesses(self): pattern_list = [["aa", "a", 2], ["999", "9", 3], ["$$$$", "$", 4], ["abab", "ab", 2], [ "batterystaplebatterystaplebatterystaple", "batterystaple", 3 ]] for [token, base_token, repeat_count] in pattern_list: base_guesses = scoring.most_guessable_match_sequence( base_token, matching.omnimatch(base_token))["guesses"] match = { "token": token, "base_token": base_token, "base_guesses": base_guesses, "repeat_count": repeat_count } expected_guesses = base_guesses * repeat_count msg = "the repeat pattern '{}' has guesses of {}".format( token, expected_guesses) self.assertEqual(scoring.repeat_guesses(match), expected_guesses, msg)
def test_repeat_guesses(self): pattern_list = [ ["aa", "a", 2], ["999", "9", 3], ["$$$$", "$", 4], ["abab", "ab", 2], ["batterystaplebatterystaplebatterystaple", "batterystaple", 3] ] for [token, base_token, repeat_count] in pattern_list: base_guesses = scoring.most_guessable_match_sequence( base_token, matching.omnimatch(base_token) )["guesses"] match = { "token": token, "base_token": base_token, "base_guesses": base_guesses, "repeat_count": repeat_count } expected_guesses = base_guesses * repeat_count msg = "the repeat pattern '{}' has guesses of {}".format(token, expected_guesses) self.assertEqual(scoring.repeat_guesses(match), expected_guesses, msg)
def test_search(self): def m(i, j, guesses): return { "i": i, "j": j, "guesses": guesses } password = "******" # for tests, set additive penalty to zero. exclude_additive = True # Case def msg1(s): return "returns one bruteforce match given an empty match sequence: {}".format(s) result = scoring.most_guessable_match_sequence(password, []) self.assertEqual(len(result["sequence"]), 1, msg1("len(result) == 1")) m0 = result["sequence"][0] self.assertEqual(m0["pattern"], "bruteforce", msg1("match['pattern'] == 'bruteforce'")) self.assertEqual(m0["token"], password, msg1("match['token'] == '{}'".format(password))) self.assertEqual([m0["i"], m0["j"]], [0, 9], msg1("[i, j] == [{}, {}]".format(m0["i"], m0["j"]))) # Case def msg2(s): return "returns match + bruteforce when match covers a prefix of password: {}".format(s) m0 = m(0, 5, 1) matches = [m0] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 2, msg2("len(result.match.sequence) == 2")) self.assertEqual(result["sequence"][0], m0, msg2("first match is the provided match object")) m1 = result["sequence"][1] self.assertEqual(m1["pattern"], "bruteforce", msg2("second match is bruteforce")) self.assertEqual([m1["i"], m1["j"]], [6, 9], msg2("second match covers full suffix after first match")) # Case def msg3(s): return "returns bruteforce + match when match covers a suffix: {}".format(s) m1 = m(3, 9, 1) matches = [m1] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 2, msg3("result.match.sequence.length == 2")) m0 = result["sequence"][0] self.assertEqual(m0["pattern"], "bruteforce", msg3("first match is bruteforce")) self.assertEqual([m0["i"], m0["j"]], [0, 2], msg3("first match covers full prefix before second match")) self.assertEqual(result["sequence"][1], m1, msg3("second match is the provided match object")) # Case def msg4(s): return "returns bruteforce + match + bruteforce when match covers an infix: {}".format(s) m1 = m(1, 8, 1) matches = [m1] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 3, msg4("result.length == 3")) self.assertEqual(result["sequence"][1], m1, msg4("middle match is the provided match object")) m0 = result["sequence"][0] m2 = result["sequence"][2] self.assertEqual([m0["i"], m0["j"]], [0, 0], msg4("first match covers full prefix before second match")) self.assertEqual([m2["i"], m2["j"]], [9, 9], msg4("third match covers full suffix after second match")) # Case def msg5(s): return "chooses lower-guesses match given two matches of the same span: {}".format(s) m0, m1 = (m(0, 9, 1), m(0, 9, 2)) matches = [m0, m1] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 1, msg5("result.length == 1")) self.assertEqual(result["sequence"][0], m0, msg5("result.sequence[0] == m0")) m0["guesses"] = 3 result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 1, msg5("result.length == 1")) self.assertEqual(result["sequence"][0], m1, msg5("result.sequence[0] == m1")) # Case def msg6(s): return "when m0 covers m1 and m2, choose [m0] when m0 < m1 * m2 * fact(2): {}".format(s) m0, m1, m2 = (m(0, 9, 3), m(0, 3, 2), m(4, 9, 1)) matches = [m0, m1, m2] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(result["guesses"], 3, msg6("total guesses == 3")) self.assertEqual(result["sequence"], [m0], msg6("sequence is [m0]")) # Case def msg7(s): return "when m0 covers m1 and m2, choose [m1, m2] when m0 > m1 * m2 * fact(2): {}".format(s) m0["guesses"] = 5 result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(result["guesses"], 4, msg7("total guesses == 3")) self.assertEqual(result["sequence"], [m1, m2], msg7("sequence is [m0]"))
def test_search(self): def m(i, j, guesses): return { "i": i, "j": j, "guesses": guesses } password = "******" # for tests, set additive penalty to zero. exclude_additive = True # Case def msg1(s): return "returns one bruteforce match given an empty match sequence: {}".format(s) result = scoring.most_guessable_match_sequence(password, []) self.assertEqual(len(result["sequence"]), 1, msg1("len(result) == 1")) m0 = result["sequence"][0] self.assertEqual(m0["pattern"], "bruteforce", msg1("match['pattern'] == 'bruteforce'")) self.assertEqual(m0["token"], password, msg1("match['token'] == '{}'".format(password))) self.assertEqual([m0["i"], m0["j"]], [0, 9], msg1("[i, j] == [{}, {}]".format(m0["i"], m0["j"]))) # Case def msg2(s): return "returns match + bruteforce when match covers a prefix of password: {}".format(s) m0 = m(0, 5, 1) matches = [m0] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 2, msg2("len(result.match.sequence) == 2")) self.assertEqual(result["sequence"][0], m0, msg2("first match is the provided match object")) m1 = result["sequence"][1] self.assertEqual(m1["pattern"], "bruteforce", msg2("second match is bruteforce")) self.assertEqual([m1["i"], m1["j"]], [6, 9], msg2("second match covers full suffix after first match")) # Case def msg3(s): return "returns bruteforce + match when match covers a suffix: {}".format(s) m1 = m(3, 9, 1) matches = [m1] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 2, msg3("result.match.sequence.length == 2")) m0 = result["sequence"][0] self.assertEqual(m0["pattern"], "bruteforce", msg3("first match is bruteforce")) self.assertEqual([m0["i"], m0["j"]], [0, 2], msg3("first match covers full prefix before second match")) self.assertEqual(result["sequence"][1], m1, msg3("second match is the provided match object")) # Case def msg4(s): return "returns bruteforce + match + bruteforce when match covers an infix: {}".format(s) m1 = m(1, 8, 1) matches = [m1] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 3, msg4("result.length == 3")) self.assertEqual(result["sequence"][1], m1, msg4("middle match is the provided match object")) m0 = result["sequence"][0] m2 = result["sequence"][2] self.assertEqual([m0["i"], m0["j"]], [0, 0], msg4("first match covers full prefix before second match")) self.assertEqual([m2["i"], m2["j"]], [9, 9], msg4("third match covers full suffix after second match")) # Case def msg5(s): return "chooses lower-guesses match given two matches of the same span: {}".format(s) m0, m1 = (m(0, 9, 1), m(0, 9, 2)) matches = [m0, m1] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 1, msg5("result.length == 1")) self.assertEqual(result["sequence"][0], m0, msg5("result.sequence[0] == m0")) m0["guesses"] = 3 result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(len(result["sequence"]), 1, msg5("result.length == 1")) self.assertEqual(result["sequence"][0], m1, msg5("result.sequence[0] == m1")) # Case def msg6(s): return "when m0 covers m1 and m2, choose [m0] when m0 < m1 * m2 * fact(2): {}".format(s) m0, m1, m2 = (m(0, 9, 3), m(0, 3, 2), m(4, 9, 1)) matches = [m0, m1, m2] result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(result["guesses"], 3, msg6("total guesses == 3")) self.assertEqual(result["sequence"], [m0], msg6("sequence is [m0]")) # Case def msg7(s): return "when m0 covers m1 and m2, choose [m1, m2] when m0 > m1 * m2 * fact(2): {}".format(s) m0["guesses"] = 5 result = scoring.most_guessable_match_sequence(password, matches, exclude_additive) self.assertEqual(result["guesses"], 4, msg7("total guesses == 3")) self.assertEqual(result["sequence"], [m1, m2], msg7("sequence is [m0]"))