Last active
April 16, 2022 13:25
-
-
Save SebastianPoell/8bdfc50654af00456572edfacbfaf870 to your computer and use it in GitHub Desktop.
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
# This script calculates all possible paths through mobile phone unlock codes. | |
# Results are sorted in a depth first manner. There are exactly 389,498 | |
# valid paths. However, Android only allows paths with length > 3, resulting | |
# in only 389,112 paths[1]. | |
# | |
# [1]: Dissecting pattern unlock: The effect of pattern strength meter on | |
# pattern selection; Chen Sun and Yang Wang and Jun Zheng; J. Inf. Secur. | |
# Appl.; Volume 19; 2014 | |
# | |
module UnlockPatterns | |
# Reachable indices in dense representation. The indices of the nodes are: | |
# 0 1 2 | |
# 3 4 5 | |
# 6 7 8 | |
EDGES = { | |
0 => [1, 3, 4, 5, 7], | |
1 => [0, 2, 3, 4, 5, 6, 8], | |
2 => [1, 3, 4, 5, 7], | |
3 => [0, 1, 2, 4, 6, 7, 8], | |
4 => [0, 1, 2, 3, 5, 6, 7, 8], | |
5 => [0, 1, 2, 4, 6, 7, 8], | |
6 => [1, 3, 4, 5, 7], | |
7 => [0, 2, 3, 4, 5, 6, 8], | |
8 => [1, 3, 4, 5, 7] | |
} | |
# After some indices have been visited, allow additional edges | |
# E.g. path [1, 0, 2], where index 1 gets permeable | |
ADDITIONAL_EDGES = { | |
1 => [[0, 2]], | |
3 => [[0, 6]], | |
4 => [[0, 8], [2, 6], [3, 5], [1, 7]], | |
5 => [[2, 8]], | |
7 => [[6, 8]] | |
} | |
def self.paths(min_length: 3) | |
# Hold the result, which looks like: | |
# [[0], [0, 1], [0, 3], ..., [8, 7, 6, 5, 4, 3, 2, 1, 0]] | |
@visited_paths = [[]] | |
# Run algorithm with all possible starting indices | |
(0..8).each { |i| visit_path([i]) } | |
# Filter results by path lengths (for Anroid min length is 3) and | |
# return result paths | |
@visited_paths.select { |p| p.size >= min_length } | |
end | |
private | |
# Recursively visits all possible paths depth first | |
# Results are appended to the visisted_paths list | |
def self.visit_path(current_path) | |
@visited_paths << current_path | |
# Compute which target indices are in reach | |
possible_target_indices = ( | |
# All indices from the base graph are always in reach | |
EDGES[current_path.last] + | |
# Indices get permeable after they got visited, therefore we | |
# have to compute which additional edges are reachable | |
ADDITIONAL_EDGES.select do |key, _| | |
current_path.include?(key) | |
# Filter indices which are reachable from the current index | |
end.values.flatten(1).select do |edge| | |
edge.include?(current_path.last) | |
end | |
# Format possible target indices and subtract all indices of | |
# current path. We do not want to visit already visisted indices | |
).flatten.uniq.sort - current_path | |
# Use recursion to iterate all possible target indices | |
possible_target_indices.each { |i| visit_path(current_path + [i]) } | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Use it like that:
Or use it with ruby2d:
Screen.Recording.2022-01-10.at.06.32.55.mov