Compare commits

..
48 Commits
Author SHA1 Message Date
KKlochko fcae272944 Add key validation for Caesar and Gamma ciphers.
continuous-integration/drone/push Build is passing
2023-11-01 15:07:13 +02:00
KKlochko 1e45771b3e Add key? to check if a string is a gamma key. 2023-11-01 15:05:08 +02:00
KKlochko e145382570 Add int-string? to check if a string is a number.
continuous-integration/drone/push Build is passing
2023-11-01 15:02:44 +02:00
KKlochko 91dc00ea0d Rename unsigned-int? to unsigned-int-string? 2023-11-01 15:01:22 +02:00
KKlochko 0261c97d76 Add the option for the gamma cipher.
continuous-integration/drone/push Build is passing
2023-10-31 16:50:25 +02:00
KKlochko 79b72fccca Fix the key parsing in the gamma actions. 2023-10-31 16:49:30 +02:00
KKlochko 5843695a40 Add the action of decryption for the gamma cipher. 2023-10-31 16:48:40 +02:00
KKlochko 4f89fede56 Add the action of encryption for the gamma cipher.
continuous-integration/drone/push Build is passing
2023-10-30 20:15:40 +02:00
KKlochko 53f89d4b4f Add a function to decrypt a message with the gamma cipher.
continuous-integration/drone/push Build is passing
2023-10-29 19:05:12 +02:00
KKlochko bcb347a8d9 Add a function to encrypt a message with the gamma cipher.
continuous-integration/drone/push Build is passing
2023-10-28 17:16:04 +03:00
KKlochko 124133a407 Refactor the simple substitute to use the decrypt-by-table function. 2023-10-28 17:14:55 +03:00
KKlochko e80e3a6cf0 Add the functions to encrypt and decrypt with the gamma cipher.
continuous-integration/drone/push Build is passing
2023-10-27 21:40:47 +03:00
KKlochko e2e49ec392 Add the gamma generator.
continuous-integration/drone/push Build is passing
2023-10-26 21:33:29 +03:00
KKlochko 3a3682f2c4 Add the main for the cli module.
continuous-integration/drone/push Build is passing
2023-10-25 22:03:02 +03:00
KKlochko e70b72f0ec Update the simple substitution cipher to split the decrypt-message.
continuous-integration/drone/push Build is passing
2023-10-24 21:39:51 +03:00
KKlochko 172f85ea6f Add functions to make a frequency map.
continuous-integration/drone/push Build is passing
2023-10-24 21:24:03 +03:00
KKlochko 6f70417b78 Add the simple substitution cipher.
continuous-integration/drone/push Build is passing
2023-10-23 15:55:14 +03:00
KKlochko d357e82b54 Add the parser to parse the string as an integer if it's needed. 2023-10-23 15:51:30 +03:00
KKlochko 553f4788e4 Add a function to invert the table in a decoded json. 2023-10-23 15:50:05 +03:00
KKlochko 5a4b88bd08 Add the parsers to parse keys.
continuous-integration/drone/push Build is passing
2023-10-22 17:54:16 +03:00
KKlochko 79ae13aa5a Add a generator for the sorted substitution table.
continuous-integration/drone/push Build is passing
2023-10-22 17:26:54 +03:00
KKlochko 126a706292 Update the simple substitution cipher to split the functions. 2023-10-22 17:26:04 +03:00
KKlochko 178c85b0cb Add the simple substitution cipher.
continuous-integration/drone/push Build is passing
2023-10-21 21:45:43 +03:00
KKlochko 2db631d8da Update the diffs in the CHANGELOG.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-21 16:56:32 +03:00
KKlochko 05009f1a96 Update the CI/CD configuration to publish the artifacts to the release.
continuous-integration/drone/push Build is passing
2023-10-21 16:47:22 +03:00
KKlochko 7c19e48828 Update the README. 2023-10-21 16:46:57 +03:00
KKlochko 40725f55a1 Update the version. 2023-10-21 16:46:32 +03:00
KKlochko 633cdc471f Update the CHANGELOG. 2023-10-21 16:46:15 +03:00
KKlochko dab131a32e Remove the option for FrequencyAnalyzer.
continuous-integration/drone/push Build is passing
2023-10-20 22:04:05 +03:00
KKlochko f465d1112e Remove the test for FrequencyAnalyzer. 2023-10-20 22:03:10 +03:00
KKlochko 5919ba8864 Refactor to move actions to separated files. 2023-10-20 22:01:20 +03:00
KKlochko a56704226b Add the IsNonsense interface for FrequencyAnalyzer.
continuous-integration/drone/push Build is failing
2023-10-14 21:32:56 +03:00
Kryls1fer b53559030f Update the frequency analyzer. 2023-10-14 21:18:50 +03:00
KKlochko ef87a719a3 Add validations to check if a cipher or an analyzer is available.
continuous-integration/drone/push Build is passing
2023-10-14 17:28:21 +03:00
KKlochko 2f0254f677 Add the available ciphers and analyzer for the help. 2023-10-14 17:26:20 +03:00
KKlochko 6339774bd1 Add the analyzer options and their actions. 2023-10-14 17:22:03 +03:00
KKlochko a44fc24bee Refactor to move options to a separated file. 2023-10-14 17:11:36 +03:00
KKlochko ac2cf2ac58 Fix the bug that the default language symbols override the symbols.
continuous-integration/drone/push Build is passing
2023-10-13 21:46:48 +03:00
KKlochko 043257bb64 Update the FrequencyAnalyzer to use the frequency string instead.
continuous-integration/drone/push Build is passing
2023-10-12 22:10:41 +03:00
KKlochko de35d4e2d3 Add the function to make a frequency string from a map. 2023-10-12 22:04:39 +03:00
KKlochko 476a24c900 Fix the typo "frequences" to frequencies. 2023-10-12 22:00:01 +03:00
KKlochko 4d7e9da1d1 Refactor the project structure.
continuous-integration/drone/push Build is passing
2023-10-11 21:12:10 +03:00
KKlochko 77efa81c54 Add the Gitlab CI/CD configuration.
continuous-integration/drone/push Build is passing
2023-10-09 20:00:50 +03:00
KKlochko 51609b1d27 Add the clojure wrapper for the frequency analizer and its test.
continuous-integration/drone/push Build is passing
2023-10-05 20:10:05 +03:00
KKlochko 40af8401b2 Add a frequency analyzer as a stub. 2023-10-05 20:08:41 +03:00
KKlochko 0a87ab9e91 Add the Caesar decrypted interface for Java. 2023-10-05 20:07:43 +03:00
KKlochko 9c1a8b1bae Add the java source path. 2023-10-05 20:07:11 +03:00
KKlochko c3ea58f867 Add the CHANGELOG.
continuous-integration/drone/push Build is passing
2023-10-05 17:50:22 +03:00
37 changed files with 1186 additions and 146 deletions
+36
View File
@@ -5,6 +5,42 @@ name: default
steps:
- name: test
image: clojure:temurin-11-lein-2.10.0-alpine
volumes:
- name: package_cache
path: /root/.m2/
commands:
- lein test
- name: build
image: clojure:temurin-11-lein-2.10.0-alpine
volumes:
- name: package_cache
path: /root/.m2/
commands:
- lein uberjar
when:
event: tag
- name: gitea_release
image: plugins/gitea-release
settings:
api_key:
from_secret: API_KEY
base_url:
from_secret: BASE_URL
files:
- target/uberjar/*.jar
checksum:
- md5
- sha1
- sha256
- sha512
- adler32
- crc32
when:
event: tag
volumes:
- name: package_cache
temp: {}
+10
View File
@@ -0,0 +1,10 @@
image: clojure:temurin-11-lein-2.10.0-alpine
stages:
- test
unit tests:
stage: test
script:
- lein test
+206
View File
@@ -0,0 +1,206 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.7.2] - 2023-10-21
### Changed
- Update the CHANGELOG.
- Update the version.
- Update the README.
- Update the CI/CD configuration to publish the artifacts to the release.
- Update the diffs in the CHANGELOG.
## [0.7.1] - 2023-10-20
### Removed
- Remove the test for FrequencyAnalyzer.
- Remove the option for FrequencyAnalyzer.
## [0.7.0] - 2023-10-20
### Changed
- Refactor to move actions to separated files.
## [0.6.3] - 2023-10-14
### Added
- Add the IsNonsense interface for FrequencyAnalyzer.
### Changed
- Update the frequency analyzer.
## [0.6.2] - 2023-10-14
### Added
- Add the analyzer options and their actions.
- Add the available ciphers and analyzer for the help.
- Add validations to check if a cipher or an analyzer is available.
### Changed
- Refactor to move options to a separated file.
## [0.6.1] - 2023-10-13
### Added
- Add the function to make a frequency string from a map.
### Fixed
- Fix the bug that the default language symbols override the symbols.
### Changed
- Update the FrequencyAnalyzer to use the frequency string instead.
## [0.6.0] - 2023-10-12
### Fixed
- Fix the typo "frequences" to frequencies.
### Changed
- Refactor the project structure.
## [0.5.5] - 2023-10-09
### Added
- Add the Gitlab CI/CD configuration.
## [0.5.4] - 2023-10-05
### Added
- Add the java source path.
- Add the Caesar decrypted interface for Java.
- Add a frequency analyzer as a stub.
- Add the clojure wrapper for the frequency analizer and its test.
## [0.5.3] - 2023-10-05
### Added
- Add the CHANGELOG.
## [0.5.2] - 2023-10-02
### Fixed
- Fix checking for file existence only when the options are set.
- Fix writing to a file only when the option is set.
## [0.5.1] - 2023-09-28
### Added
- Add the output option to save into a file.
- Add the file errors to prevent using an invalid file path.
## [0.5.0] - 2023-09-22
### Changed
- Update symbol defaults, factories to have only lower alphabets.
- Update the Caesar to be case-insensitive and skip unknown symbols.
- Update Caesar Analyzers to be case-insensitive and testable.
## [0.4.3] - 2023-09-21
### Added
- Add the language option for symbols.
### Changed
- Update the CLI to have separated actions and an option initializer.
## [0.4.2] - 2023-09-18
### Added
- Add tests for the cipher-analyzers module.
### Changed
- Rename the cipher_analyzer module to cipher-analyzers.
## [0.4.1] - 2023-09-17
### Added
- Add symbol factories for punctuation, space, defaults symbols.
- Add the action-type to distinguish the actions.
- Add the symbol option and its default.
- Add the cracking option.
### Changed
- Rename the language_analyzer module to language-analyzer.
- Update the nonsense for the is-nonsense? test.
- Rename the symbol_frequences module to symbol-frequences.
## [0.4.0] - 2023-09-15
### Added
- Add Chi-squared Statistic function to analyze texts.
- Add the caesar-analyzers to get the plaintext from a ciphertext.
## [0.3.5] - 2023-09-14
### Added
- Add the frequences of symbols as defaults.
## [0.3.4] - 2023-09-13
### Added
- Add a simple language analyzers.
## [0.3.3] - 2023-09-12
### Added
- Add an option to read a message from a file.
## [0.3.2] - 2023-09-11
### Added
- Add the CI/CD configuration.
## [0.3.1] - 2023-09-11
### Added
- Add factories for symbols.
- Add tests for factories.
## [0.3.0] - 2023-09-10
### Added
- Add a simple cli interface.
## [0.2.0] - 2023-09-09
### Added
- Add a implementation of Caesar cipher.
## [0.1.0] - 2023-09-09
### Added
- Project initialization.
[Unreleased]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.7.2...main?from_project_id=50218541&straight=false
[0.7.2]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.7.1...0.7.2?from_project_id=50218541&straight=false
[0.7.1]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.7.0...0.7.1?from_project_id=50218541&straight=false
[0.7.0]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.6.3...0.7.0?from_project_id=50218541&straight=false
[0.6.3]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.6.2...0.6.3?from_project_id=50218541&straight=false
[0.6.2]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.6.1...0.6.2?from_project_id=50218541&straight=false
[0.6.1]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.6.0...0.6.1?from_project_id=50218541&straight=false
[0.6.0]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.5.5...0.6.0?from_project_id=50218541&straight=false
[0.5.5]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.5.4...0.5.5?from_project_id=50218541&straight=false
[0.5.4]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.5.3...0.5.4?from_project_id=50218541&straight=false
[0.5.3]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.5.2...0.5.3?from_project_id=50218541&straight=false
[0.5.2]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.5.1...0.5.2?from_project_id=50218541&straight=false
[0.5.1]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.5.0...0.5.1?from_project_id=50218541&straight=false
[0.5.0]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.4.3...0.5.0?from_project_id=50218541&straight=false
[0.4.3]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.4.2...0.4.3?from_project_id=50218541&straight=false
[0.4.2]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.4.1...0.4.2?from_project_id=50218541&straight=false
[0.4.1]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.4.0...0.4.1?from_project_id=50218541&straight=false
[0.4.0]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.3.5...0.4.0?from_project_id=50218541&straight=false
[0.3.5]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.3.4...0.3.5?from_project_id=50218541&straight=false
[0.3.4]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.3.3...0.3.4?from_project_id=50218541&straight=false
[0.3.3]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.3.2...0.3.3?from_project_id=50218541&straight=false
[0.3.2]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.3.1...0.3.2?from_project_id=50218541&straight=false
[0.3.1]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.3.0...0.3.1?from_project_id=50218541&straight=false
[0.3.0]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.2.0...0.3.0?from_project_id=50218541&straight=false
[0.2.0]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/compare/0.1.0...0.2.0?from_project_id=50218541&straight=false
[0.1.0]: https://gitlab.com/KKlochko/cipher-analytical-machine/-/commit/a7a715b94276fc8f46fb6a44139636d27b551599
+3 -1
View File
@@ -11,10 +11,12 @@ Download from https://gitlab.com/KKlochko/cipher-analytical-machine.
You need install java, before run the application.
Run the application with a command:
$ java -jar cipher-analytical-machine-0.1.0-standalone.jar [args]
$ java -jar cipher-analytical-machine-VERSION-standalone.jar [args]
$ java -jar cipher-analytical-machine-0.7.2-standalone.jar [args]
## License
Copyright © 2023 Kostiantyn Klochko
Under the GNU Lesser General Public License v3.0 or later.
+4 -2
View File
@@ -1,4 +1,4 @@
(defproject cipher-analytical-machine "0.1.0-SNAPSHOT"
(defproject cipher-analytical-machine "0.7.2"
:description "A program helps to learn how ciphers works."
:url "https://gitlab.com/KKlochko/cipher-analytical-machine"
:license {:name "LGPL"
@@ -6,8 +6,10 @@
:dependencies [[org.clojure/clojure "1.11.1"]
[org.clojure/tools.cli "1.0.219"]
[org.apache.tika/tika-core "1.28.5"]
[org.apache.tika/tika-langdetect "1.28.5"]]
[org.apache.tika/tika-langdetect "1.28.5"]
[cheshire "5.12.0"]]
:main ^:skip-aot cipher-analytical-machine.core
:java-source-paths ["src/main/java"]
:target-path "target/%s"
:profiles {:uberjar {:aot :all
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.cipher-analyzers
(ns cipher-analytical-machine.analyzers.analyzers
(:require [clojure.string :as cs])
(:gen-class))
@@ -21,18 +21,37 @@
(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]
[text letter-frequencies]
(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)
(if (contains? letter-frequencies char)
(chi-squared-letter-statistic count
(get letter-frequences char)
(get letter-frequencies char)
length-text)
0)))
0
letter-counts)))
(defn sort-map-by-count
"Return a list of pairs from a map of pairs where a pair is a character and its count. The order is descending."
[count-map]
(->> count-map
(reduce (fn [acc [char count]] (conj acc [char count])) [])
(sort-by #(- (second %)))))
(defn table-to-string
"Return the string from a frequency table."
[table]
(->> (map first table)
(apply str)))
(defn symbol-frequency-table
"Return a table where pairs is a symbol from a cipher text and its value which is a symbol from frequency table."
[cipher-table frequency-table]
(zipmap (table-to-string (sort-map-by-count cipher-table))
(table-to-string (sort-map-by-count frequency-table))))
@@ -0,0 +1,64 @@
(ns cipher-analytical-machine.analyzers.caesar
(:require [cipher-analytical-machine.ciphers.caesar :as caesar]
[clojure.string :as cs]
[cipher-analytical-machine.analyzers.language :as language]
[cipher-analytical-machine.analyzers.analyzers :as ca])
(:import [cipher_analytical_machine.ciphers.caesar Decrypted]
[cipher_analytical_machine.analyzers.caesar IsNonsense]
[cipher_analytical_machine.analyzers.caesar FrequencyAnalyzer])
(: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-all-scores
"For pairs (key, plaintext) finds scores and return list of pairs [key, score]."
[letter-frequencies pairs]
(map (fn [[key text]]
[key (ca/chi-squared-statistic text letter-frequencies)])
pairs))
(defn get-min-score-pair
"For pairs (key, plaintext) finds scores and return list of pairs [key, score]."
[pairs]
(reduce
(fn [[key value] [new-key new-value]]
(if (> value new-value) [new-key new-value]
[key value]))
[0 Double/MAX_VALUE]
pairs))
(defn get-key
"To find the key with frequencies of letters."
[ciphertext symbols letter-frequencies]
(->> (get-all-texts ciphertext symbols)
(get-all-scores letter-frequencies)
(get-min-score-pair)
(first)))
(defn get-plaintext
"Return the plaintext from a ciphertext. The function is case-insensitive."
[ciphertext symbols letter-frequencies]
(let [ciphertext (cs/lower-case ciphertext)]
(caesar/decrypt-message ciphertext
(get-key ciphertext symbols letter-frequencies)
symbols)))
(defn frequency-analizer-get-plaintext
"Return the plaintext from a ciphertext using simple analizer. The function is case-insensitive."
[ciphertext symbols letter-frequencies-string language-code]
(let [decrypt (reify Decrypted
(decrypt [this message key symbols]
(caesar/decrypt-message message key symbols)))
is-nonsense (reify IsNonsense
(isNonsense [this message]
(language/is-nonsense? message language-code)))]
(-> (new FrequencyAnalyzer ciphertext symbols letter-frequencies-string decrypt is-nonsense)
.crack)))
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.language-analyzer
(ns cipher-analytical-machine.analyzers.language
(:import [org.apache.tika.language LanguageIdentifier])
(:gen-class))
@@ -1,48 +0,0 @@
(ns cipher-analytical-machine.caesar-analyzers
(:require [cipher-analytical-machine.caesar :as caesar]
[clojure.string :as cs]
[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-all-scores
"For pairs (key, plaintext) finds scores and return list of pairs [key, score]."
[letter-frequences pairs]
(map (fn [[key text]]
[key (ca/chi-squared-statistic text letter-frequences)])
pairs))
(defn get-min-score-pair
"For pairs (key, plaintext) finds scores and return list of pairs [key, score]."
[pairs]
(reduce
(fn [[key value] [new-key new-value]]
(if (> value new-value) [new-key new-value]
[key value]))
[0 Double/MAX_VALUE]
pairs))
(defn get-key
"To find the key with frequencies of letters."
[ciphertext symbols letter-frequences]
(->> (get-all-texts ciphertext symbols)
(get-all-scores letter-frequences)
(get-min-score-pair)
(first)))
(defn get-plaintext
"Return the plaintext from a ciphertext. The function is case-insensitive."
[ciphertext symbols letter-frequences]
(let [ciphertext (cs/lower-case ciphertext)]
(caesar/decrypt-message ciphertext
(get-key ciphertext symbols letter-frequences)
symbols)))
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.caesar
(ns cipher-analytical-machine.ciphers.caesar
(:require [clojure.string :as cs])
(:gen-class))
@@ -0,0 +1,78 @@
(ns cipher-analytical-machine.ciphers.gamma
(:require
[clojure.set :as set]
[clojure.string :as cs]
[cipher-analytical-machine.ciphers.simple-substitution :as ss])
(:gen-class))
(defn add-mod
[f s module]
(-> (+ f s)
(mod module)))
(defn generate-seq
"Generate a lazy sequence for a key (a b c)."
[a b c module]
((fn build-seq [a b c]
(lazy-seq (cons a (build-seq b c (add-mod a c module))))) a b c))
(defn generate-gamma-seq
"Generate the gamma seq from a sequence."
[acc seq module]
(if (= (second seq) nil) acc
(generate-gamma-seq
(conj acc (add-mod (first seq) (second seq) module))
(rest seq)
module)))
(defn generate-gamma
"Generate the gamma from a key (a b c)."
[a b c module size]
(generate-gamma-seq []
(->> (generate-seq a b c module)
(take (inc size)))
module))
(defn encrypt-array
"Encrypt an array using the gamma cipher. The function is case-insensitive. The key is an array of three elements [a b c]. The array contains integer in range [0, module)."
[key module array]
(let [size (count array)
gamma (->> (apply conj [key module size])
(apply generate-gamma))]
(->> gamma
(map vector array)
(map (fn [[a g]] (add-mod a g module))))))
(defn encrypt-message
"Encrypt a message using the gamma cipher. The function is case-insensitive. The key is an array of three elements [a b c]. The array contains integer in range [0, module)."
[key symbols message]
(let [module (count symbols)
table (ss/generate-sorted-substitution-table symbols)]
(->> message
(ss/decrypt-by-table (set/map-invert table))
(encrypt-array key module)
(ss/decrypt-by-table table)
(cs/join))))
(defn decrypt-array
"Decrypt an array using the gamma cipher. The function is case-insensitive. The key is an array of three elements [a b c]. The array contains integer in range [0, module)."
[key module array]
(let [size (count array)
gamma (->> (apply conj [key module size])
(apply generate-gamma))]
(->> gamma
(map -)
(map vector array)
(map (fn [[a g]] (add-mod a g module))))))
(defn decrypt-message
"Decrypt a message using the gamma cipher. The function is case-insensitive. The key is an array of three elements [a b c]. The array contains integer in range [0, module)."
[key symbols message]
(let [module (count symbols)
table (ss/generate-sorted-substitution-table symbols)]
(->> message
(ss/decrypt-by-table (set/map-invert table))
(decrypt-array key module)
(ss/decrypt-by-table table)
(cs/join))))
@@ -0,0 +1,74 @@
(ns cipher-analytical-machine.ciphers.simple-substitution
(:require [clojure.string :as cs]
[cipher-analytical-machine.ciphers.caesar :as caesar])
(:gen-class))
(defn shuffled-numbers
"Generate the shuffled order for integer list."
[size]
(-> size
(take (range))
(shuffle)))
(defn generate-substitution-table
"Generate the map (char, int) for the substittion."
[symbols]
(->> (count symbols)
(shuffled-numbers)
(zipmap symbols)))
(defn generate-sorted-substitution-table
"Generate the map (char, int) for the substittion. BUT a value is the index of a symbol (from 0)."
[symbols]
(-> (count symbols)
(take (range))
(zipmap symbols)))
(defn find-value-in-table
"It uses the substitution table to find the value of a char or a number."
[char substitution-table]
(get substitution-table char))
(defn decrypt-by-table
"Decrypt a message by the substitution-table"
[substitution-table message]
(map (fn [char] (find-value-in-table char substitution-table)) message))
(defn encrypt-message
"Encrypt a message using the simple substitution cipher. The function is case-insensitive. If a symbol isn't in the symbols list, then it will be removed."
[message substitution-table]
(->> message
(decrypt-by-table substitution-table)
(remove nil?)
(cs/join \,)))
(defn encrypt-message-with-caesar
"Encrypt a message using the simple substitution cipher and the Caesar cipher. The function is case-insensitive. If a symbol isn't in the symbols list, then it will be removed."
[message key substitution-table symbols]
(-> message
(caesar/encrypt-message key symbols)
(encrypt-message substitution-table)))
(defn decrypt-message-by-symbol-table
"Decrypt a message using the simple substitution cipher. The function is case-insensitive. The substitution-table must be (symbols, symbols)."
[substitution-table message]
(->> message
(decrypt-by-table substitution-table)
(remove nil?)
(cs/join)))
(defn decrypt-message
"Decrypt a message using the simple substitution cipher. The function is case-insensitive. The substitution-table must be (int, symbols)."
[message substitution-table]
(let [message (cs/split message #",")]
(->> message
(map #(Integer/parseInt %))
(decrypt-message-by-symbol-table substitution-table))))
(defn decrypt-message-with-caesar
"Decrypt a message using the simple substitution cipher and the Caesar cipher. The function is case-insensitive."
[message key substitution-table symbols]
(-> message
(decrypt-message substitution-table)
(caesar/decrypt-message key symbols)))
@@ -0,0 +1,26 @@
(ns cipher-analytical-machine.cli.actions.cracking
(:require
[cipher-analytical-machine.analyzers.caesar :as caesar-analyzers]
[cipher-analytical-machine.symbols.frequencies :as symbol-frequencies])
(:gen-class))
(defn cracking-caesar
[options arguments]
(let [analyzer (:analyzer options)
message (:message options)
symbols (:symbols options)
frequencies symbol-frequencies/english-letter-frequencies]
(cond
(= analyzer "Chi^2")
(caesar-analyzers/get-plaintext message symbols frequencies)
:else
(caesar-analyzers/get-plaintext message symbols frequencies))))
(defn cracking-actions
[options arguments action-type]
(let [cipher (:cipher options)]
(cond
(= cipher "Caesar")
(cracking-caesar options arguments))))
@@ -0,0 +1,69 @@
(ns cipher-analytical-machine.cli.actions.cryptography
(:require
[clojure.string :as cs]
[cipher-analytical-machine.ciphers.caesar :as caesar]
[cipher-analytical-machine.ciphers.simple-substitution :as ss]
[cipher-analytical-machine.ciphers.gamma :as gamma]
[cipher-analytical-machine.parsers.parsers :as ps]
[cipher-analytical-machine.parsers.simple-substitution :as pss])
(:gen-class))
(defn caesar-actions
[options arguments action-type]
(let [message (:message options)
key (Integer/parseInt (:key options))
symbols (:symbols options)]
(cond
(= action-type :encrypt)
(caesar/encrypt-message message key symbols)
(= action-type :decrypt)
(caesar/decrypt-message message key symbols))))
(defn simple-substitution-with-caesar-actions
[options arguments action-type]
(let [message (:message options)
key (:key options)
symbols (:symbols options)]
(cond
(= action-type :encrypt)
(let [data (pss/generate-table-or-decode-json key symbols)
key (ps/parse-unsigned-int (get data "key"))
substitution-table (get data "table")]
(println (pss/encode-key-and-substitution-table-to-json key substitution-table))
(ss/encrypt-message-with-caesar message key substitution-table symbols))
(= action-type :decrypt)
(let [data (-> (pss/decode-key-and-substitution-table-from-json key)
(pss/invert-table-in-map))
key (get data "key")
substitution-table (get data "table")]
(ss/decrypt-message-with-caesar message key substitution-table symbols)))))
(defn gamma-actions
[options arguments action-type]
(let [message (:message options)
key (->> (cs/split (:key options) #",")
(map #(Integer/parseInt %))
(into []))
symbols (:symbols options)]
(cond
(= action-type :encrypt)
(gamma/encrypt-message key symbols message)
(= action-type :decrypt)
(gamma/decrypt-message key symbols message))))
(defn cryptography-actions
[options arguments action-type]
(let [cipher (:cipher options)]
(cond
(= cipher "Caesar")
(caesar-actions options arguments action-type)
(= cipher "Simple substitution and Caesar")
(simple-substitution-with-caesar-actions options arguments action-type)
(= cipher "Gamma")
(gamma-actions options arguments action-type))))
@@ -1,36 +1,16 @@
(ns cipher-analytical-machine.cli
(ns cipher-analytical-machine.cli.cli
(: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]
[cipher-analytical-machine.parsers.parsers :as ps]
[cipher-analytical-machine.parsers.gamma :as gamma-ps]
[cipher-analytical-machine.cli.file :as file]
[cipher-analytical-machine.symbols.factories :as sf]
[cipher-analytical-machine.cli.options :as options]
[clojure.string :as cs]
[cipher-analytical-machine.cli.actions.cryptography :as cryptography]
[cipher-analytical-machine.cli.actions.cracking :as cracking]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
(def cipher-options
#{"Caesar"})
(def language-options
#{"en", "uk"})
(def cli-options
[["-m" "--message MESSAGE" "The message will be encrypted or decrypted."]
["-M" "--message-file MESSAGE-FILE" "The file contains the message that will be encrypted or decrypted."]
["-k" "--key KEY" "The key will be used to encrypt or decrypt."]
["-c" "--cipher CIPHER" "The cipher will be used to encrypt or decrypt a message."
: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")]
["-l" "--language CODE" "The string will be used to set a default symbols for a cipher."
:validate [#(contains? language-options %) "Must be a code from the list: en, uk!!!"]]
["-C" "--cracking" "Cracking the encrypted message."]
["-O" "--output-file OUTPUT-FILE" "Save the program output to a file."]
["-h" "--help"]])
(defn usage [options-summary]
(->> ["This is cipher-analytical-machine. It can help you to learn how ciphers works."
""
@@ -38,6 +18,12 @@
""
"Options:"
options-summary
""
"Available values"
""
(options/get-available-cipher-options-as-string)
""
(options/get-available-analyzers-options-as-string)
""]
(cs/join \newline)))
@@ -55,7 +41,7 @@
should exit (with an error message, and optional ok status), or a map
indicating the action the program should take and the options provided."
[args]
(let [{:keys [options arguments errors summary]} (parse-opts args cli-options)]
(let [{:keys [options arguments errors summary]} (parse-opts args options/cli-options)]
(cond
(:help options)
{:exit-message (usage summary) :ok? true}
@@ -77,12 +63,31 @@
(file/is-file-exists? (:output-file options)))
{:exit-message (error-msg ["Please, choose another file!!! Overwriting was prevented!!!"])}
(false? (contains? options/cipher-options (:cipher options)))
{:exit-message (error-msg ["Please, choose an available cipher!!!\n"
(options/get-available-cipher-options-as-string)])}
(and (map-has-keys? options [:cipher, :cracking, :analyzer])
(false? (contains?
(get options/analyzers-options (:cipher options)) (:analyzer options))))
{:exit-message (error-msg ["Please, choose an available analyzer!!!\n"
(options/get-available-analyzers-options-as-string)])}
(and (map-has-keys? options [:cipher, :key])
(or (contains? options :message)
(contains? options :message-file))
(or (contains? options :encrypt)
(contains? options :decrypt)))
(cond
(and (= (:cipher options) "Caesar")
(false? (ps/int-string? (:key options))))
{:exit-message (error-msg ["Please, use the key format: [+-](integer)!!! Example: 10, +9, -7."])}
(and (= (:cipher options) "Gamma")
(false? (gamma-ps/key? (:key options))))
{:exit-message (error-msg ["Please, use the key format: [+-](integer),[+-](integer),[+-](integer) !!! Example: 10,+9,-7."])}
(contains? options :encrypt)
{:options options :arguments arguments :action-type :encrypt}
@@ -122,8 +127,10 @@
(defn set-symbols
"Set defaults symbols for a language."
[options]
(set-option options :symbols
(sf/default-symbol-factory (:language options))))
(if (contains? options :language)
(set-option options :symbols
(sf/default-symbol-factory (:language options)))
options))
(defn load-and-set-option
"Load the option and set it."
@@ -151,38 +158,23 @@
(save-output output options))
(println output))
(defn cracking-actions
[options arguments action-type]
(let [message (:message options)
symbols (:symbols options)
frequencies symbol-frequences/english-letter-frequences]
(cond
(= action-type :cracking)
(caesar-analyzers/get-plaintext message symbols frequencies)
)))
(defn crypt-actions
[options arguments action-type]
(let [message (:message options)
key (Integer/parseInt (:key options))
symbols (:symbols options)]
(cond
(= action-type :encrypt)
(caesar/encrypt-message message key symbols)
(= action-type :decrypt)
(caesar/decrypt-message message key symbols))))
(defn actions
[options arguments action-type]
(let [options (load-all-options options)]
(->
(cond
(contains? #{:encrypt :decrypt} action-type)
(crypt-actions options arguments action-type)
(cryptography/cryptography-actions options arguments action-type)
(= action-type :cracking)
(cracking-actions options arguments action-type))
(cracking/cracking-actions options arguments action-type))
(show-and-save-output options))))
(defn cli-main
[args]
(let [{:keys [options arguments action-type exit-message ok?]} (validate-args args)]
(if exit-message
(exit exit-message ok?)
(actions options arguments action-type))))
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.file
(ns cipher-analytical-machine.cli.file
(:require [clojure.java.io :as io])
(:gen-class))
@@ -0,0 +1,64 @@
(ns cipher-analytical-machine.cli.options
(:require
[cipher-analytical-machine.symbols.factories :as sf]
[clojure.string :as cs])
(:gen-class))
(defn option-values-as-row
"Return the formatted string of values for an option."
[option values]
(str option ": " (cs/join ", " values)))
(defn option-values-as-list
"Return the formatted string of values for an option as a multiline list."
[option values]
(str option ": \n - " (cs/join "\n - " values)))
(defn map-of-option-value-as-list
"Return the formatted string of values for an option as a multiline list.
The values are a map of pairs (suboption and its options)."
[option values]
(let [values (map #(apply option-values-as-row %) values)]
(option-values-as-list option values)))
(def cipher-options
#{"Caesar" "Simple substitution and Caesar" "Gamma"})
(def default-cipher "Caesar")
(defn get-available-cipher-options-as-string
"Return the formatted string of analyzers options."
[]
(option-values-as-row "Ciphers" cipher-options))
(def analyzers-options
{"Caesar" #{"Chi^2"}})
(defn get-available-analyzers-options-as-string
"Return the formatted string of analyzers options."
[]
(map-of-option-value-as-list "Analyzers" analyzers-options))
(def language-options
#{"en", "uk"})
(def default-symbols
(sf/default-symbol-factory "en"))
(def cli-options
[["-m" "--message MESSAGE" "The message will be encrypted or decrypted."]
["-M" "--message-file MESSAGE-FILE" "The file contains the message that will be encrypted or decrypted."]
["-k" "--key KEY" "The key will be used to encrypt or decrypt."]
["-c" "--cipher CIPHER" "The cipher will be used to encrypt or decrypt a message."
:default default-cipher]
["-e" "--encrypt" "Encrypt the message."]
["-d" "--decrypt" "Decrypt the message."]
["-s" "--symbols SYMBOLS" "The string will be used as a set of symbols for a cipher."
:default default-symbols]
["-l" "--language CODE" "The string will be used to set a default symbols for a cipher."
:validate [#(contains? language-options %) "Must be a code from the list: en, uk!!!"]]
["-C" "--cracking" "Cracking the encrypted message."]
["-a" "--analyzer ANALYZER" "The way of cracking."]
["-O" "--output-file OUTPUT-FILE" "Save the program output to a file."]
["-h" "--help"]])
+2 -5
View File
@@ -1,12 +1,9 @@
(ns cipher-analytical-machine.core
(:require
[cipher-analytical-machine.cli :as cli])
[cipher-analytical-machine.cli.cli :as cli])
(:gen-class))
(defn -main
[& 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 action-type))))
(cli/cli-main args))
@@ -0,0 +1,9 @@
(ns cipher-analytical-machine.parsers.gamma
(:gen-class))
(defn key?
"Return true if the string is an key of three integers."
[str]
(if (re-matches #"[+-]?\b\d+\b,[+-]?\b\d+\b,[+-]?\b\d+\b" str)
true false))
@@ -0,0 +1,29 @@
(ns cipher-analytical-machine.parsers.parsers
(:require [cheshire.core :as cc])
(:gen-class))
(defn unsigned-int-string?
"Return true if the string is an unsigned integer."
[str]
(if (re-matches #"\d+" str)
true false))
(defn int-string?
"Return true if the string is an signed integer."
[str]
(if (re-matches #"[+-]?\b\d+\b" str)
true false))
(defn parse-unsigned-int
"Return an integer if the argument is an integer."
[str-or-int]
(cond
(int? str-or-int)
str-or-int
(unsigned-int-string? str-or-int)
(Integer/parseInt str-or-int)
:else
nil))
@@ -0,0 +1,54 @@
(ns cipher-analytical-machine.parsers.simple-substitution
(:require
[cipher-analytical-machine.ciphers.simple-substitution :as ss]
[cipher-analytical-machine.parsers.parsers :as ps]
[clojure.set :as set]
[cheshire.core :as cc])
(:gen-class))
(defn encode-key-and-substitution-table-to-json
[key substitution-table]
(-> {"key" key}
(assoc "table" substitution-table)
(cc/generate-string)))
(defn decode-key
"Returns the first character if the string is a char. Return the number if the string is a number."
[str]
(if (ps/unsigned-int-string? str)
(Integer/parseInt str)
(first str)))
(defn decode-pair
"Decode a numbers as a string and a char as a string"
[acc [key value]]
(if (ps/unsigned-int-string? key)
(assoc acc (decode-key key) (decode-key value))
(assoc acc (decode-key key) value)))
(defn decode-substitution-table
[substitution-table-map-from-json]
(reduce decode-pair {} substitution-table-map-from-json))
(defn decode-key-and-substitution-table-from-json
[json]
(let [data (cc/parse-string json)]
(assoc data "table" (decode-substitution-table (get data "table")))))
(defn generate-table-or-decode-json
"It generate the table and return map with the key and the table if the argument is a key. Else it decode the json."
[key-or-json symbols]
(if (ps/unsigned-int-string? key-or-json)
(-> {"key" key-or-json}
(assoc "table" (ss/generate-substitution-table symbols)))
(decode-key-and-substitution-table-from-json key-or-json)))
(defn invert-table-in-map
"Invert the table in a decoded json (key, table)"
[decoded-json]
(->>
(-> decoded-json
(get "table")
(set/map-invert))
(assoc decoded-json "table")))
@@ -1,4 +1,4 @@
(ns cipher-analytical-machine.symbol-factories
(ns cipher-analytical-machine.symbols.factories
(:require [clojure.string :as cs])
(:gen-class))
@@ -1,7 +1,17 @@
(ns cipher-analytical-machine.symbol-frequences
(ns cipher-analytical-machine.symbols.frequencies
(:gen-class))
(def english-letter-frequences {
(defn map-to-string
"Convert the map of frequencies to the frequency string. Letters are sorted by frequency in descending order."
[frequency-map]
(->> frequency-map
(reduce (fn [acc el] (conj acc el)) [])
(sort-by last)
(map first)
(reverse)
(apply str)))
(def english-letter-frequencies {
\a 0.0804
\b 0.0154
\c 0.0306
@@ -30,7 +40,7 @@
\z 0.0009
})
(def ukrainian-letter-frequences {
(def ukrainian-letter-frequencies {
\а 0.064
\б 0.013
0.046
@@ -0,0 +1,53 @@
package cipher_analytical_machine.analyzers.caesar;
import cipher_analytical_machine.analyzers.caesar.IsNonsense;
import cipher_analytical_machine.ciphers.caesar.Decrypted;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class FrequencyAnalyzer {
private String ciphertext;
private String symbols;
private String symbol_frequences;
private Decrypted decryptor;
private IsNonsense isNonsense;
public FrequencyAnalyzer(String ciphertext, String symbols, String symbol_frequences, Decrypted decryptor, IsNonsense isNonsense) {
this.ciphertext = ciphertext;
this.symbol_frequences = symbol_frequences;
this.symbols = symbols;
this.decryptor = decryptor;
this.isNonsense = isNonsense;
}
public String crack() {
// Знаходимо кількість літер
Map<Character, Integer> encryptedLetterCounts = new HashMap<>();
for (char c : ciphertext.toLowerCase().toCharArray()) {
encryptedLetterCounts.put(c, encryptedLetterCounts.getOrDefault(c, 0) + 1);
}
// Знаходимо букву, яка зустрічається найчастіше
char mostFrequentEncryptedLetter = ' ';
int mostFrequentEncryptedLetterCount = 0;
for (Entry<Character, Integer> entry : encryptedLetterCounts.entrySet()) {
if (entry.getValue() > mostFrequentEncryptedLetterCount) {
mostFrequentEncryptedLetter = entry.getKey();
mostFrequentEncryptedLetterCount = entry.getValue();
}
}
// Обчислимо зміщення
int size = symbols.length();
int indexOfMostFrequentEncryptedLetter = symbols.indexOf(mostFrequentEncryptedLetter);
int indexOfMostFrequentLetter = symbols.indexOf(symbol_frequences.charAt(0));
int shift = (indexOfMostFrequentEncryptedLetter - indexOfMostFrequentLetter) % size;
return decryptor.decrypt(ciphertext, shift, symbols);
}
}
@@ -0,0 +1,6 @@
package cipher_analytical_machine.analyzers.caesar;
public interface IsNonsense {
boolean isNonsense(String message);
}
@@ -0,0 +1,6 @@
package cipher_analytical_machine.ciphers.caesar;
public interface Decrypted {
String decrypt(String message, int key, String symbols);
}
@@ -1,7 +1,7 @@
(ns cipher-analytical-machine.cipher-analyzers-test
(ns cipher-analytical-machine.analyzers.analyzers-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.cipher-analyzers :refer :all]))
[cipher-analytical-machine.analyzers.analyzers :refer :all]))
(deftest count-characters-test
(testing "Count two dublicates and one uniq character from a string."
@@ -19,7 +19,22 @@
(deftest chi-squared-statistic-test
(let [text "aaaaabbbbb"]
(testing "If the frequences of a and b is 0.1, the text length is 10 and contains them 5 times, then score is 32."
(testing "If the frequencies of a and b is 0.1, the text length is 10 and contains them 5 times, then score is 32."
(is (= 32.0
(chi-squared-statistic text {\a 0.1 \b 0.1}))))))
(deftest sort-map-by-count-test
(testing "Test the sort in descending order."
(is (= [[\b 3] [\a 2] [\c 1]]
(sort-map-by-count {\a 2 \b 3 \c 1} )))))
(deftest table-to-string-test
(testing "Test the sort in descending order."
(is (= "bac"
(table-to-string [[\b 3] [\a 2] [\c 1]])))))
(deftest symbol-frequency-table-test
(testing "Test the sort in descending order."
(is (= {\d \b, \e \a, \f \c}
(symbol-frequency-table {\d 3 \e 2 \f 1} {\b 3 \a 2 \c 1})))))
@@ -1,9 +1,9 @@
(ns cipher-analytical-machine.caesar-analyzers-test
(ns cipher-analytical-machine.analyzers.caesar-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.caesar-analyzers :refer :all]
[cipher-analytical-machine.symbol-frequences :as sf]
[cipher-analytical-machine.cipher-analyzers :as ca]
[cipher-analytical-machine.analyzers.caesar :refer :all]
[cipher-analytical-machine.symbols.frequencies :as sf]
[cipher-analytical-machine.analyzers.analyzers :as ca]
[clojure.string :as cs]))
(deftest get-all-texts-test
@@ -25,9 +25,9 @@
(testing "There're 27 combinations for a 'Hello World'."
(let [ciphertext "khoorczruog"
symbols "abcdefghijklmnopqrstuvwxyz "
frequences sf/english-letter-frequences
frequencies sf/english-letter-frequencies
text-pairs (get-all-texts ciphertext symbols)]
(is (= 27 (count (get-all-scores frequences text-pairs)))))))
(is (= 27 (count (get-all-scores frequencies text-pairs)))))))
(deftest get-min-score-pair-test
(testing "If a key is one, then the min score pair is 15."
@@ -40,37 +40,37 @@
ciphertext "khoorczruog"
key 3
symbols "abcdefghijklmnopqrstuvwxyz "
frequences sf/english-letter-frequences]
frequencies sf/english-letter-frequencies]
(testing "The plaintext is encrypted with 3 as the key."
(is (= key (get-key ciphertext symbols frequences)))))
(is (= key (get-key ciphertext symbols frequencies)))))
(let [; З поеми "Кавказ" Тараса Григоровича Шевченка:
plaintext "Борітеся – поборете, Вам Бог помагає! За вас правда, за вас слава. І воля святая!"
ciphertext "жхчощйшде–ецхжхчйщй,езєуежхиецхуєиєк!емєезєшецчєзїє,емєезєшештєзє.еоезхтдешздщєд!"
key 7
symbols "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя "
frequences sf/ukrainian-letter-frequences]
frequencies sf/ukrainian-letter-frequencies]
(testing "The plaintext is encrypted with 3 as the key."
(is (= key (get-key ciphertext symbols frequences))))))
(is (= key (get-key ciphertext symbols frequencies))))))
(deftest get-plaintext-test
(let [plaintext "hello world"
ciphertext "khoorczruog"
symbols "abcdefghijklmnopqrstuvwxyz "
frequences sf/english-letter-frequences]
frequencies sf/english-letter-frequencies]
(testing "The plaintext is encrypted with 3 as the key."
(is (= plaintext (get-plaintext ciphertext symbols frequences))))
(is (= plaintext (get-plaintext ciphertext symbols frequencies))))
(testing "The ciphertext is case-insensitive."
(is (= plaintext (get-plaintext "KhoorcZruog" symbols frequences)))))
(is (= plaintext (get-plaintext "KhoorcZruog" symbols frequencies)))))
(let [; З поеми "Кавказ" Тараса Григоровича Шевченка:
plaintext "Борітеся – поборете, Вам Бог помагає! За вас правда, за вас слава. І воля святая!"
ciphertext "жхчощйшде–ецхжхчйщй,езєуежхиецхуєиєк!емєезєшецчєзїє,емєезєшештєзє.еоезхтдешздщєд!"
key 7
symbols "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя "
frequences sf/ukrainian-letter-frequences]
frequencies sf/ukrainian-letter-frequencies]
(testing "The ciphertext is case-insensitive."
(is (= (cs/lower-case plaintext)
(get-plaintext ciphertext symbols frequences))))))
(get-plaintext ciphertext symbols frequencies))))))
@@ -1,7 +1,7 @@
(ns cipher-analytical-machine.language-analyzer-test
(ns cipher-analytical-machine.analyzers.language-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.language-analyzer :refer :all]
[cipher-analytical-machine.analyzers.language :refer :all]
))
(deftest detect-language-test
@@ -1,7 +1,7 @@
(ns cipher-analytical-machine.caesar-test
(ns cipher-analytical-machine.ciphers.caesar-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.caesar :refer :all]
[cipher-analytical-machine.ciphers.caesar :refer :all]
))
(deftest calculate-char-index-test
@@ -0,0 +1,62 @@
(ns cipher-analytical-machine.ciphers.gamma-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.ciphers.gamma :refer :all]))
(deftest add-mod-test
(testing "The remainder must be as expected."
(are [a b module expected]
(= expected (add-mod a b module))
5 2 4 3
1 2 3 0
2 2 3 1)))
(deftest generate-seq-test
(testing "Checking that the next element of a sequence is calculated by (e[i-1] + e[i-3] % mod)."
(are [a b c module size expected]
(= expected (take size (generate-seq a b c module)))
4 32 15 33 10 [4 32 15 19 18 0 19 4 4 23]
1 2 3 3 9 [1 2 3 1 0 0 1 1 1])))
(deftest generate-gamma-seq-test
(testing "Checking that the next element of a sequence is calculated by (s[i] + s[i+1] % mod)."
(are [acc seq module expected]
(= expected (generate-gamma-seq acc seq module))
[] [4 32 15 19 18 0 19 4 4 23] 33 [3 14 1 4 18 19 23 8 27]
[] [1 2 3 1 0 0 1 1 1] 3 [0 2 1 1 0 1 2 2])))
(deftest generate-gamma-test
(testing "Checking that the gamma is generated as expected."
(are [a b c module size expected]
(= expected (generate-gamma a b c module size))
4 32 15 33 10 [3 14 1 4 18 19 23 8 27 17]
1 2 3 3 9 [0 2 1 1 0 1 2 2 0])))
(deftest encrypt-array-test
(testing "Checking that the message is encrypted as expected: (msg+gamma)%module."
(are [key module array expected]
(= expected (encrypt-array key module array))
[4 32 15] 33 [3 14 1 4 18 19 23 8 27 17] [6 28 2 8 3 5 13 16 21 1]
[1 2 3 ] 3 [0 2 1 1 0 1 2 2 0] [0 1 2 2 0 2 1 1 0])))
(deftest encrypt-message-test
(testing "Checking that the message is encrypted as expected"
(are [key symbols message expected]
(= expected (encrypt-message key symbols message))
[4 32 15] "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" "гкбґопужчн" "ешвжгдймсб"
[1 2 3 ] "абв" "авббабвва" "абввавбба")))
(deftest decrypt-array-test
(testing "Checking that the message is decrypted as expected."
(are [key module array expected]
(= expected (decrypt-array key module array))
[4 32 15] 33 [6 28 2 8 3 5 13 16 21 1] [3 14 1 4 18 19 23 8 27 17]
[1 2 3 ] 3 [0 1 2 2 0 2 1 1 0] [0 2 1 1 0 1 2 2 0])))
(deftest decrypt-message-test
(testing "Checking that the message is decrypted as expected"
(are [key symbols message expected]
(= expected (decrypt-message key symbols message))
[4 32 15] "абвгґдеєжзиіїйклмнопрстуфхцчшщьюя" "ешвжгдймсб" "гкбґопужчн"
[1 2 3 ] "абв" "абввавбба" "авббабвва")))
@@ -0,0 +1,85 @@
(ns cipher-analytical-machine.ciphers.simple-substitution-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.ciphers.simple-substitution :refer :all]))
(deftest find-value-in-table-test
(let [table {\a 1 \b 2 \c 3}
rtable {1 \a 2 \b 3 \c}]
(testing "If the symbol is in the table as a key, then the result won't nil."
(are [key expected]
(= expected (find-value-in-table key table))
\a 1
\d nil
1 nil
5 nil))
(testing "If the digit is in the reversed table as a key, then the result won't nil."
(are [key expected]
(= expected (find-value-in-table key rtable))
\a nil
\d nil
1 \a
5 nil))))
(deftest generate-sorted-substitution-table-test
(testing "If the symbol is in the table as a key, then the result won't nil."
(are [symbols expected-table]
(= expected-table (generate-sorted-substitution-table symbols))
"abc" {0 \a 1 \b 2 \c}
"a" {0 \a})))
(deftest encrypt-message-test
(let [symbols "abc"
table {\a 1 \b 2 \c 3}]
(testing "The function must encrypt the message and remove unknown symbols."
(are [message expected]
(= expected (encrypt-message message table))
"abc" "1,2,3"
"aDbdc" "1,2,3"))))
(deftest encrypt-message-with-caesar-test
(let [symbols "abc"
table {\a 1 \b 2 \c 3}]
(testing "The function must encrypt the message and remove unknown symbols."
(are [message key expected]
(= expected (encrypt-message-with-caesar message key table symbols))
"abc" 0 "1,2,3"
"abc" 1 "2,3,1"
"aDbdc" 0 "1,2,3"))))
(deftest decrypt-by-table-test
(let [table {\d \a \e \b \f \c}]
(testing "The function must decrypt the message by the table."
(are [message expected]
(= expected (decrypt-by-table table message))
"def" '(\a \b \c)
"abf" '(nil nil \c)
"fed" '(\c \b \a)))))
(deftest decrypt-message-by-symbol-table-test
(let [table {\d \a \e \b \f \c}]
(testing "The function must decrypt the message and remove unknown numbers."
(are [message expected]
(= expected (decrypt-message-by-symbol-table table message))
"def" "abc"
"daef" "abc"))))
(deftest decrypt-message-test
(let [table {1 \a 2 \b 3 \c}]
(testing "The function must decrypt the message and remove unknown numbers."
(are [message expected]
(= expected (decrypt-message message table))
"1,2,3" "abc"
"1,12,2,3" "abc"))))
(deftest decrypt-message-with-caesar-test
(let [symbols "abc"
table {1 \a 2 \b 3 \c}]
(testing "The function must decrypt the message and remove unknown numbers."
(are [message key expected]
(= expected (decrypt-message-with-caesar message key table symbols))
"1,2,3" 0 "abc"
"2,3,1" 1 "abc"
"1,12,2,3" 0 "abc"))))
@@ -0,0 +1,20 @@
(ns cipher-analytical-machine.parsers.gamma-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.parsers.gamma :refer :all]))
(deftest key?-test
(testing "The function return true only if the key in the format '%d,%d,%d'."
(are [str expected]
(= expected
(key? str))
"9" false
"9,10" false
"10,9,8" true
"-10,9,-8" true
"-10,-9,-8" true
"asd 10 " false
" 10 " false
"-10" false
"abc" false)))
@@ -0,0 +1,40 @@
(ns cipher-analytical-machine.parsers.parsers-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.parsers.parsers :refer :all]))
(deftest unsigned-int-string?-test
(testing "The function return true only for an unsingned integer as a string."
(are [str expected]
(= expected
(unsigned-int-string? str))
"9" true
"9" true
"10" true
"asd 10 " false
" 10 " false
"-10" false
"abc" false)))
(deftest int-string?-test
(testing "The function return true only for an singned integer as a string."
(are [str expected]
(= expected
(int-string? str))
"9" true
"10" true
"-10" true
"asd -10 " false
" -10 " false
"abc" false)))
(deftest parse-unsigned-int-test
(testing "The function parse the integer if the string is an integer."
(are [str-or-int expected]
(= expected
(parse-unsigned-int str-or-int))
"9" 9
9 9
"10" 10
"-10" nil
"abc" nil)))
@@ -0,0 +1,49 @@
(ns cipher-analytical-machine.parsers.simple-substitution-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.parsers.simple-substitution :refer :all]))
(deftest encode-key-and-substitution-table-to-json-test
(testing "The encoder must parse the map and create a json."
(are [key table expected-json]
(= expected-json
(encode-key-and-substitution-table-to-json key table))
1 {0 \a 1 \b 2 \c} "{\"key\":1,\"table\":{\"0\":\"a\",\"1\":\"b\",\"2\":\"c\"}}"
2 {\a 0 \b 1 \c 2} "{\"key\":2,\"table\":{\"a\":0,\"b\":1,\"c\":2}}")))
(deftest decode-key-and-substitution-table-from-json-test
(testing "The decoder must parse the json and create a map."
(are [json expected-data]
(= expected-data
(decode-key-and-substitution-table-from-json json))
"{\"key\":1, \"table\":{\"0\":\"a\",\"1\":\"b\",\"2\":\"c\"}}" {"key" 1 "table" {0 \a 1 \b 2 \c}}
"{\"key\":2, \"table\":{\"a\":0,\"b\":1,\"c\":2}}" {"key" 2 "table" {\a 0 \b 1 \c 2}})))
(deftest generate-table-or-decode-json-test
(let [symbols "abc"]
(testing "The function decodes the json."
(are [json expected-data]
(= expected-data
(generate-table-or-decode-json json symbols))
"{\"key\":1, \"table\":{\"0\":\"a\",\"1\":\"b\",\"2\":\"c\"}}" {"key" 1 "table" {0 \a 1 \b 2 \c}}
"{\"key\":2, \"table\":{\"a\":0,\"b\":1,\"c\":2}}" {"key" 2 "table" {\a 0 \b 1 \c 2}}))
(testing "The function generates the table, because the argument is a key."
(are [json expected-data]
(let [data (generate-table-or-decode-json json symbols)]
(and (= (get expected-data "key")
(get data "key"))
(= (keys (get expected-data "table"))
(keys (get data "table"))))
(generate-table-or-decode-json json symbols))
"1" {"key" 1 "table" {0 \a 1 \b 2 \c}}
"2" {"key" 2 "table" {\a 0 \b 1 \c 2}}))))
(deftest invert-table-in-map-test
(testing "The function invert the table in a decoded json."
(are [decoded-json expected-data]
(= expected-data
(invert-table-in-map decoded-json))
{"key" 1 "table" {0 \a 1 \b 2 \c}} {"key" 1 "table" {\a 0 \b 1 \c 2}}
{"key" 2 "table" {\a 0 \b 1 \c 2}} {"key" 2 "table" {0 \a 1 \b 2 \c}})))
@@ -1,7 +1,7 @@
(ns cipher-analytical-machine.symbol-factories-test
(ns cipher-analytical-machine.symbols.factories-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.symbol-factories :refer :all]
[cipher-analytical-machine.symbols.factories :refer :all]
))
(deftest english-alphabet-factory-test
@@ -0,0 +1,11 @@
(ns cipher-analytical-machine.symbols.frequencies-test
(:require
[clojure.test :refer :all]
[cipher-analytical-machine.symbols.frequencies :refer :all]))
(deftest calculate-char-index-test
(let [symbol-map english-letter-frequencies]
(testing "The map must be converted to 'etaoinsrhldcumfpgwybvkxjqz'"
(is (= "etaoinsrhldcumfpgwybvkxjqz"
(map-to-string symbol-map))))))