Compare commits

...
9 Commits
Author SHA1 Message Date
KKlochko e3954804c8 Add the cracking option.
continuous-integration/drone/push Build is passing
2023-09-17 21:45:09 +03:00
KKlochko b1f3bb8585 Rename the symbol_frequences module to symbol-frequences. 2023-09-17 21:43:09 +03:00
KKlochko a6f3ca160f Update the nonsense for the is-nonsense? test.
continuous-integration/drone/push Build is passing
2023-09-16 22:16:46 +03:00
KKlochko 001b2a8b65 Rename the language_analyzer module to language-analyzer. 2023-09-16 22:16:08 +03:00
KKlochko e8a023fe61 Add the symbol option and its default. 2023-09-16 22:08:12 +03:00
KKlochko 6d282e5ad4 Add the action-type to distinguish the actions. 2023-09-16 22:05:53 +03:00
KKlochko 5ca5765e01 Add symbol factories for punctuation, space, defaults symbols. 2023-09-16 22:02:18 +03:00
KKlochko a7ccad8e1e Add the caesar-analyzers to get the plaintext from a ciphertext.
continuous-integration/drone/push Build is failing
2023-09-15 21:30:37 +03:00
KKlochko 79e2957402 Add Chi-squared Statistic function to analyze texts.
continuous-integration/drone/push Build is failing
2023-09-14 21:09:28 +03:00
9 changed files with 173 additions and 25 deletions
@@ -0,0 +1,34 @@
(ns cipher-analytical-machine.caesar-analyzers
(:require [cipher-analytical-machine.caesar :as caesar]
[cipher-analytical-machine.cipher_analyzers :as ca])
(:gen-class))
(defn get-all-texts
"Return a list of pairs which have a key and a second posible plaintext."
[ciphertext symbols]
(let [keys (range (count symbols))]
(reduce
(fn [acc key]
(conj acc [key (caesar/decrypt-message ciphertext key symbols)]))
'() keys)))
(defn get-key
"To find the key with frequencies of letters."
[ciphertext symbols letter-frequences]
(->> (get-all-texts ciphertext symbols)
(map (fn [[key text]]
[key (ca/chi-squared-statistic text letter-frequences)]))
(reduce
(fn [[key value] [new-key new-value]]
(if (> value new-value) [new-key new-value]
[key value]))
[0 Double/MAX_VALUE])
(first)))
(defn get-plaintext
"Return the plaintext from a ciphertext."
[ciphertext symbols letter-frequences]
(caesar/decrypt-message ciphertext
(get-key ciphertext symbols letter-frequences)
symbols))
@@ -0,0 +1,38 @@
(ns cipher-analytical-machine.cipher_analyzers
(:require [clojure.string :as cs])
(:gen-class))
(defn count-characters
"Count the letters and return map."
[text]
(reduce
(fn [acc val]
(assoc acc val
(inc (get acc val 0))))
{} text))
(defn chi-squared-letter-statistic
"To calculate the frequency distribution of a letter."
[letter-count letter-frequence length-text]
(let [letter-frequence (* length-text letter-frequence)]
(/ (Math/pow
(- letter-count letter-frequence) 2)
letter-frequence)))
(defn chi-squared-statistic
"To calculate the frequency distribution of a language. More detail about the algorithm here: http://practicalcryptography.com/cryptanalysis/text-characterisation/chi-squared-statistic/"
[text letter-frequences]
(let [length-text (count text)
letter-counts (count-characters (cs/lower-case text))]
;letter-counts
(reduce
(fn [acc [char count]]
(+ acc
(if (contains? letter-frequences char)
(chi-squared-letter-statistic count
(get letter-frequences char)
length-text)
0)))
0
letter-counts)))
+24 -6
View File
@@ -2,6 +2,9 @@
(:require
[cipher-analytical-machine.file :as file]
[cipher-analytical-machine.caesar :as caesar]
[cipher-analytical-machine.caesar-analyzers :as caesar-analyzers]
[cipher-analytical-machine.symbol-factories :as sf]
[cipher-analytical-machine.symbol-frequences :as symbol-frequences]
[clojure.string :as cs]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
@@ -17,6 +20,9 @@
:default "Caesar"]
["-e" "--encrypt" "Encrypt the message."]
["-d" "--decrypt" "Decrypt the message."]
["-s" "--symbols" "The string will be used as a set of symbols for a cipher."
:default (sf/default-symbol-factory "en")]
["-C" "--cracking" "Cracking the encrypted message."]
["-h" "--help"]])
(defn usage [options-summary]
@@ -62,7 +68,17 @@
(contains? options :message-file))
(or (contains? options :encrypt)
(contains? options :decrypt)))
{:options options :arguments arguments}
(cond
(contains? options :encrypt)
{:options options :arguments arguments :action-type :encrypt}
(contains? options :decrypt)
{:options options :arguments arguments :action-type :decrypt})
(and (map-has-keys? options [:cipher, :cracking])
(or (contains? options :message)
(contains? options :message-file)))
{:options options :arguments arguments :action-type :cracking}
:else
{:exit-message (usage summary)})))
@@ -83,16 +99,18 @@
(file/read-file (:message-file options))))
(defn actions
[options arguments]
(println (str options arguments))
[options arguments action-type]
(let [message (get-message options arguments)
key (Integer/parseInt (:key options))
symbols "abc"]
symbols (:symbols options)]
(println
(cond
(contains? options :encrypt)
(= action-type :cracking)
(caesar-analyzers/get-plaintext message symbols symbol-frequences/english-letter-frequences)
(= action-type :encrypt)
(caesar/encrypt-message message key symbols)
(contains? options :decrypt)
(= action-type :decrypt)
(caesar/decrypt-message message key symbols)))))
+2 -2
View File
@@ -5,8 +5,8 @@
(defn -main
[& args]
(let [{:keys [options arguments exit-message ok?]} (cli/validate-args args)]
(let [{:keys [options arguments action-type exit-message ok?]} (cli/validate-args args)]
(if exit-message
(cli/exit exit-message ok?)
(cli/actions options arguments))))
(cli/actions options arguments action-type))))
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.language_analyzer
(ns cipher-analytical-machine.language-analyzer
(:import [org.apache.tika.language LanguageIdentifier])
(:gen-class))
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.symbol_factories
(ns cipher-analytical-machine.symbol-factories
(:require [clojure.string :as cs])
(:gen-class))
@@ -32,3 +32,32 @@
[]
"0123456789")
(defn punctuation-symbol-factory
"Return punctuation symbols"
[]
".,:;-!?")
(defn space-symbol-factory
"Return punctuation symbols"
[]
" \t\n")
(defn default-symbol-factory
"Return a default string of symbols for a language"
[language-code]
(cond
(= language-code "en")
(str (english-alphabet-factory)
(punctuation-symbol-factory)
(digit-set-factory)
\ )
(= language-code "uk")
(str (ukrainian-alphabet-factory)
(punctuation-symbol-factory)
(digit-set-factory)
\ )
:else
(default-symbol-factory "en")))
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.symbol_frequences
(ns cipher-analytical-machine.symbol-frequences
(:gen-class))
(def english-letter-frequences {
@@ -1,7 +1,7 @@
(ns cipher-analytical-machine.language_analyzer_test
(ns cipher-analytical-machine.language-analyzer-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.language_analyzer :refer :all]
[cipher-analytical-machine.language-analyzer :refer :all]
))
(deftest detect-language-test
@@ -41,5 +41,5 @@
(is (is-nonsense? "dfgjldfjgdfl gjdfg idfjig hdgesr khs e." "en")))
(testing "Checking a gibberish that uses Ukrainian letters"
(is (is-nonsense? "іаврпшшцді врадів аргів аріл варї йцґ." "uk"))))
(is (is-nonsense? "іаврпшшцді івафі лоінїмч йцувіа івапґ." "uk"))))
@@ -1,46 +1,75 @@
(ns cipher-analytical-machine.symbol-factories-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.symbol_factories :refer :all]
[cipher-analytical-machine.symbol-factories :refer :all]
))
(deftest lower-english-alphabet-factory-test
(let [symbols (lower-english-alphabet-factory)]
(testing "It must have same order"
(testing "It must have the same order."
(is (= "abcdefghijklmnopqrstuvwxyz" symbols)))
(testing "It must have 26 letters"
(testing "It must have 26 letters."
(is (= 26 (count symbols))))))
(deftest english-alphabet-factory-test
(let [symbols (english-alphabet-factory)]
(testing "It must have same order"
(testing "It must have the same order."
(is (= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" symbols)))
(testing "It must have 2*26 letters"
(testing "It must have 2*26 letters."
(is (= (* 2 26) (count symbols))))))
(deftest lower-ukrainian-alphabet-factory-test
(let [symbols (lower-ukrainian-alphabet-factory)]
(testing "It must have same order"
(testing "It must have the same order."
(is (= "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" symbols)))
(testing "It must have 33 letters"
(testing "It must have 33 letters."
(is (= 33 (count symbols))))))
(deftest ukrainian-alphabet-factory-test
(let [symbols (ukrainian-alphabet-factory)]
(testing "It must have same order"
(testing "It must have the same order."
(is (= "абвгґдеєжзиіїйклмнопрстуфхцчшщьюяАБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ" symbols)))
(testing "It must have 2*33 letters"
(testing "It must have 2*33 letters."
(is (= (* 2 33) (count symbols))))))
(deftest digit-set-factory-test
(let [symbols (digit-set-factory)]
(testing "It must have same order"
(testing "It must have the same order."
(is (= "0123456789" symbols)))
(testing "It must have 10 digits"
(testing "It must have 10 digits."
(is (= 10 (count symbols))))))
(deftest punctuation-symbol-factory-test
(let [symbols (punctuation-symbol-factory)]
(testing "It must have the same order."
(is (= ".,:;-!?" symbols)))
(testing "It must have 7 symbols."
(is (= 7 (count symbols))))))
(deftest space-symbol-factory-test
(let [symbols (space-symbol-factory)]
(testing "It must have the same order."
(is (= " \t\n" symbols)))
(testing "It must have 3 symbols."
(is (= 3 (count symbols))))))
(deftest default-symbol-factory-test
(testing "If language code is 'en', then it must have symbols."
(let [symbols (default-symbol-factory "en")]
(is (= 70 (count symbols)))))
(testing "If language code is 'uk', then it must have symbols."
(let [symbols (default-symbol-factory "uk")]
(is (= 84 (count symbols)))))
(testing "If language code is unidentified, then it must have the 'en'."
(let [symbols (default-symbol-factory "Maybe a code")]
(is (= 70 (count symbols))))))