Guest User

Substitution Cryptanalysis

a guest
Jul 4th, 2015
229
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 6.27 KB | None | 0 0
  1. package practice.reddit;
  2.  
  3. import javafx.util.Pair;
  4.  
  5. import java.nio.file.Files;
  6. import java.nio.file.Paths;
  7. import java.util.*;
  8. import java.util.stream.Collectors;
  9.  
  10. public class SubstitutionCyptanalysis {
  11.  
  12.     public static void main(String[] args) throws Exception {
  13.         Scanner in = new Scanner(System.in);
  14.  
  15.         List<String> inputWords = Arrays.asList(in.nextLine().split(" "));
  16.  
  17.         int numLines = Integer.parseInt(in.nextLine());
  18.  
  19.         List<Pair<Character, Character>> substitutions = new ArrayList<>();
  20.         for (int i = 0; i < numLines; i++) {
  21.             String sub = in.nextLine();
  22.             substitutions.add(new Pair<>(sub.charAt(1), sub.charAt(0)));
  23.         }
  24.  
  25.         long start = new Date().getTime();
  26.  
  27.         Dictionary dictionary = new Dictionary(Files.readAllLines(Paths.get("src/words.txt")));
  28.  
  29.         List<String> cipherWords = new ArrayList<>(inputWords);
  30.         Collections.sort(cipherWords, (w1, w2) -> w2.length() - w1.length());
  31.  
  32.         List<List<Pair<Character, Character>>> subs = decrypt(dictionary, substitutions, cipherWords);
  33.         if (!subs.isEmpty()) {
  34.             subs.forEach(sub ->
  35.                 System.out.println(String.join(" ", inputWords.stream()
  36.                         .map(inputWord -> makeSubstitutions(sub, inputWord))
  37.                         .collect(Collectors.toList())))
  38.             );
  39.  
  40.         } else {
  41.             System.out.println("None found");
  42.         }
  43.         System.out.println(new Date().getTime() - start);
  44.     }
  45.  
  46.     private static List<List<Pair<Character, Character>>> decrypt(Dictionary dictionary, List<Pair<Character, Character>> substitutions, List<String> cipherWords) {
  47.         List<List<Pair<Character, Character>>> returnSubs = new ArrayList<>();
  48.  
  49.         List<String> subbedWords = cipherWords.stream()
  50.                 .map(cipherWord -> makeSubstitutions(substitutions, cipherWord))
  51.                 .collect(Collectors.toList());
  52.  
  53.         if (!isValidSubstitutions(dictionary, subbedWords)) {
  54.             return returnSubs;
  55.         }
  56.  
  57.         if (isFullySubstituted(subbedWords)) {
  58.             returnSubs.add(substitutions);
  59.             return returnSubs;
  60.         }
  61.  
  62.         List<List<Pair<Character, Character>>> possibleSubs = new ArrayList<>();
  63.         for (int i = 0; i < subbedWords.size(); i++) {
  64.             String subbedWord = subbedWords.get(i);
  65.             if (subbedWord.chars().allMatch(c -> Character.isLowerCase(c) || c == '\'')) {
  66.                 continue;
  67.             }
  68.             Set<String> possibleWords = dictionary.getPotentialWordMatches(subbedWord.length(), getKnownChars(subbedWord));
  69.             wordsLoop: for (String possibleWord : possibleWords) {
  70.                 List<Pair<Character, Character>> possibleWordSubs = new ArrayList<>();
  71.                 thisWordLoop: for (int j = 0; j < possibleWord.length(); j++) {
  72.                     if (possibleWord.charAt(j) != subbedWord.charAt(j)) {
  73.                         for (Pair<Character, Character> sub : possibleWordSubs) {
  74.                             if (sub.getKey() == subbedWord.charAt(j) && sub.getValue() == possibleWord.charAt(j)) {
  75.                                 continue thisWordLoop;
  76.                             }
  77.                             if (sub.getKey() == subbedWord.charAt(j) || sub.getValue() == possibleWord.charAt(j)) {
  78.                                 continue wordsLoop;
  79.                             }
  80.                         }
  81.                         for (Pair<Character, Character> sub : substitutions) {
  82.                             if (sub.getKey() == subbedWord.charAt(j) || sub.getValue() == possibleWord.charAt(j)) {
  83.                                 continue wordsLoop;
  84.                             }
  85.                         }
  86.                         possibleWordSubs.add(new Pair<>(subbedWord.charAt(j), possibleWord.charAt(j)));
  87.                     }
  88.                 }
  89.                 if (!possibleWordSubs.isEmpty()) {
  90.                     possibleSubs.add(possibleWordSubs);
  91.                 }
  92.             }
  93.             if (!possibleSubs.isEmpty()) {
  94.                 break;
  95.             }
  96.         }
  97.  
  98.  
  99.         for (List<Pair<Character, Character>> subs : possibleSubs) {
  100.             subs.addAll(substitutions);
  101.  
  102.             List<List<Pair<Character, Character>>> foundSubs = decrypt(dictionary, subs, subbedWords);
  103.             returnSubs.addAll(foundSubs);
  104.         }
  105.  
  106.         return returnSubs;
  107.     }
  108.  
  109.     private static String makeSubstitutions(List<Pair<Character, Character>> substitutions, String cipherWord) {
  110.         for (Pair<Character, Character> sub : substitutions) {
  111.             cipherWord = cipherWord.replace(sub.getKey(), sub.getValue());
  112.         }
  113.         return cipherWord;
  114.     }
  115.  
  116.     private static List<Pair<Integer, Character>> getKnownChars(String word) {
  117.         List<Pair<Integer, Character>> knownChars = new ArrayList<>();
  118.         for (int i = 0; i < word.length(); i++) {
  119.             if (Character.isLowerCase(word.charAt(i)) || word.charAt(i) == '\'') {
  120.                 knownChars.add(new Pair<>(i, word.charAt(i)));
  121.             }
  122.         }
  123.         return knownChars;
  124.     }
  125.  
  126.     private static boolean isFullySubstituted(List<String> words) {
  127.         return words.stream().flatMapToInt(String::chars).allMatch(c -> Character.isLowerCase(c) || c == '\'');
  128.     }
  129.  
  130.     private static boolean isValidSubstitutions(Dictionary dictionary, List<String> subbedWords) {
  131.         for (String word : subbedWords) {
  132.             List<Pair<Integer, Character>> knownChars = getKnownChars(word);
  133.             if (!knownChars.isEmpty()) {
  134.                 if (dictionary.getPotentialWordMatches(word.length(), knownChars).isEmpty()) {
  135.                     return false;
  136.                 }
  137.             }
  138.         }
  139.         return true;
  140.     }
  141.  
  142.     public static class Dictionary {
  143.         private List<List<Map<Character, Set<String>>>> wordLookupTable;
  144.  
  145.         public Dictionary(List<String> words) {
  146.             wordLookupTable = new ArrayList<>();
  147.             wordLookupTable.add(0, null);
  148.             int longestWordLength = words.stream().map(String::length).max(Integer::compare).get();
  149.             for (int i = 1; i <= longestWordLength; i++) {
  150.                 wordLookupTable.add(i, new ArrayList<>(i));
  151.                 for (int j = 0; j < i; j++) {
  152.                     wordLookupTable.get(i).add(j, new HashMap<>(27));
  153.                     for (char c = 'a'; c <= 'z'; c++) {
  154.                         wordLookupTable.get(i).get(j).put(c, new HashSet<>());
  155.                     }
  156.                     wordLookupTable.get(i).get(j).put('\'', new HashSet<>());
  157.                 }
  158.             }
  159.  
  160.             for (String word : words) {
  161.                 for (int i = 0; i < word.length(); i++) {
  162.                     wordLookupTable.get(word.length()).get(i).get(word.charAt(i)).add(word);
  163.                 }
  164.             }
  165.         }
  166.  
  167.         public Set<String> getPotentialWordMatches(int length, List<Pair<Integer, Character>> knownChars) {
  168.             if (knownChars == null || knownChars.isEmpty()) {
  169.                 return new HashSet<>();
  170.             }
  171.  
  172.             List<Set<String>> potentialWordsSets = new ArrayList<>();
  173.             for (Pair<Integer, Character> knownChar : knownChars) {
  174.                 potentialWordsSets.add(this.wordLookupTable.get(length).get(knownChar.getKey()).get(knownChar.getValue()));
  175.             }
  176.  
  177.             Set<String> potentialWords = new HashSet<>(potentialWordsSets.get(0));
  178.             for (Set<String> singleCharPotentialWords : potentialWordsSets) {
  179.                 potentialWords.retainAll(singleCharPotentialWords);
  180.             }
  181.  
  182.             return potentialWords;
  183.         }
  184.     }
  185. }
Advertisement
Add Comment
Please, Sign In to add comment