diff --git a/CHANGELOG.md b/CHANGELOG.md index abeb70a..3282f15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.9.15] - 2023-11-26 + +### Changed +- Update the CHANGELOG. +- Update the version. +- Update the README. + +## [0.9.14] - 2023-11-26 + +### Added +- Add analyzer for the simple substitution cipher. +- Add the analyzer options and their actions for the simple substiotution. + +## [0.9.13] - 2023-11-26 + +### Fixed +- Fix the test name. + ## [0.9.12] - 2023-11-20 + +### Changed - Update the CHANGELOG. - Update the version. - Update the README. @@ -291,7 +311,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Project initialization. -[Unreleased]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.12...main?from_project_id=50218541&straight=false +[Unreleased]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.15...main?from_project_id=50218541&straight=false +[0.9.15]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.14...0.0.9.15?from_project_id=50218541&straight=false +[0.9.14]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.13...0.0.9.14?from_project_id=50218541&straight=false +[0.9.13]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.12...0.0.9.13?from_project_id=50218541&straight=false [0.9.12]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.11...0.0.9.12?from_project_id=50218541&straight=false [0.9.11]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.10...0.0.9.11?from_project_id=50218541&straight=false [0.9.10]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.9.9...0.9.10?from_project_id=50218541&straight=false diff --git a/README.md b/README.md index 4a7f018..c83e7ee 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ You need install java, before run the application. Run the application with a command: $ java -jar cipher-analytical-machine-VERSION-standalone.jar [args] - $ java -jar cipher-analytical-machine-0.9.12-standalone.jar [args] + $ java -jar cipher-analytical-machine-0.9.15-standalone.jar [args] ## License diff --git a/project.clj b/project.clj index 45bb729..895b5cf 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject cipher-analytical-machine "0.9.12" +(defproject cipher-analytical-machine "0.9.15" :description "A program helps to learn how ciphers works." :url "https://gitlab.com/KKlochko/cipher-analytical-machine" :license {:name "LGPL" diff --git a/src/cipher_analytical_machine/analyzers/simple_substitution.clj b/src/cipher_analytical_machine/analyzers/simple_substitution.clj index 717b49b..802887b 100644 --- a/src/cipher_analytical_machine/analyzers/simple_substitution.clj +++ b/src/cipher_analytical_machine/analyzers/simple_substitution.clj @@ -1,8 +1,10 @@ (ns cipher-analytical-machine.analyzers.simple-substitution (:require [clojure.string :as cs] [clojure.math.combinatorics :as comb] + [cipher-analytical-machine.symbols.frequencies :as frequencies] [cipher-analytical-machine.ciphers.simple-substitution :as ss] [cipher-analytical-machine.analyzers.analyzers :as analyzers] + [cipher-analytical-machine.analyzers.analyzers :as ca] [cipher-analytical-machine.analyzers.caesar :as caesar]) (:gen-class)) @@ -42,3 +44,35 @@ (->> (map get-all-permutation-for-block possible-key-vector) (get-all-combinations-for-blocks))) +(defn get-possible-keys + "Return keys for a cipher text. A key is a map {\\a \\e, ...} to decrypt a text." + [cipher-text frequency-table] + (let [freq-symbol-str (frequencies/map-to-string frequency-table) ; "etaoinsrhldcumfpgwybvkxjqz" + char-map (-> (analyzers/count-characters cipher-text) + (analyzers/reverse-count-characters))] + (map (fn [comb] (zipmap comb freq-symbol-str)) + (-> (get-possible-key-vector-from-reversed-count-map char-map) + (get-possible-key-combinations))))) + + +(defn get-plain-data + "Return the plain data (score, text, key) from a ciphertext (a string of character). The function is case-insensitive." + [cipher-text letter-frequencies] + (let [cipher-text (cs/lower-case cipher-text) + keys (get-possible-keys cipher-text letter-frequencies)] + (->> (map (fn [key] [(ss/decrypt-message-by-symbol-table key cipher-text) key]) keys) + (map (fn [[text key]] [(ca/chi-squared-statistic text letter-frequencies) text key])) + ))) + +(defn get-plaintext-and-key + "Return the possible plaintext from a ciphertext that encoded with numbers. The function is case-insensitive." + [cipher-text letter-frequencies] + (let [maybe-key (->> (frequencies/map-to-string letter-frequencies) + (ss/generate-sorted-substitution-table)) + cipher-text (->> (cs/split cipher-text #",") + (map #(Integer/parseInt %)) + (ss/decrypt-message-by-symbol-table maybe-key))] + + (->> (get-plain-data cipher-text letter-frequencies) + (map (fn [el] (drop 1 el)) )))) + diff --git a/src/cipher_analytical_machine/cli/actions/cracking.clj b/src/cipher_analytical_machine/cli/actions/cracking.clj index db4a8ab..4162727 100644 --- a/src/cipher_analytical_machine/cli/actions/cracking.clj +++ b/src/cipher_analytical_machine/cli/actions/cracking.clj @@ -1,6 +1,7 @@ (ns cipher-analytical-machine.cli.actions.cracking (:require [cipher-analytical-machine.analyzers.caesar :as caesar-analyzers] + [cipher-analytical-machine.analyzers.simple-substitution :as ss-analyzers] [cipher-analytical-machine.symbols.frequencies :as symbol-frequencies]) (:gen-class)) @@ -18,10 +19,27 @@ :else (caesar-analyzers/get-plaintext message symbols frequencies)))) +(defn cracking-simple-substitution-with-caesar + [options arguments] + (let [analyzer (:analyzer options) + message (:message options) + symbols (:symbols options) + frequencies (-> (get options :language "en") + (symbol-frequencies/default-frequency-factory))] + (cond + (= analyzer "Chi^2") + (ss-analyzers/get-plaintext-and-key message frequencies) + + :else + (ss-analyzers/get-plaintext-and-key message frequencies)))) + (defn cracking-actions [options arguments action-type] (let [cipher (:cipher options)] (cond (= cipher "Caesar") - (cracking-caesar options arguments)))) + (cracking-caesar options arguments) + + (= cipher "Simple substitution and Caesar") + (cracking-simple-substitution-with-caesar options arguments)))) diff --git a/src/cipher_analytical_machine/cli/options.clj b/src/cipher_analytical_machine/cli/options.clj index dd93152..61624d7 100644 --- a/src/cipher_analytical_machine/cli/options.clj +++ b/src/cipher_analytical_machine/cli/options.clj @@ -32,7 +32,8 @@ (option-values-as-row "Ciphers" cipher-options)) (def analyzers-options - {"Caesar" #{"Chi^2"}}) + {"Caesar" #{"Chi^2" "Frequency"} + "Simple substitution and Caesar" #{"Chi^2" "Frequency"}}) (defn get-available-analyzers-options-as-string "Return the formatted string of analyzers options." diff --git a/test/cipher_analytical_machine/symbols/frequencies_test.clj b/test/cipher_analytical_machine/symbols/frequencies_test.clj index 6f6e098..edb79b0 100644 --- a/test/cipher_analytical_machine/symbols/frequencies_test.clj +++ b/test/cipher_analytical_machine/symbols/frequencies_test.clj @@ -3,7 +3,7 @@ [clojure.test :refer :all] [cipher-analytical-machine.symbols.frequencies :refer :all])) -(deftest calculate-char-index-test +(deftest map-to-string-test (let [symbol-map english-letter-frequencies] (testing "The map must be converted to 'etaoinsrhldcumfpgwybvkxjqz'" (is (= "etaoinsrhldcumfpgwybvkxjqz"