encodex/lib.rs
1// Copyright (C) 2022,2023 Fabian Moos
2// This file is part of encodex.
3//
4// encodex is free software: you can redistribute it and/or modify it under the terms of the GNU
5// General Public License as published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// encodex is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
9// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10// General Public License for more details.
11//
12// You should have received a copy of the GNU General Public License along with encodex. If not,
13// see <https://www.gnu.org/licenses/>.
14//
15
16//! This crate provides functionality for handling plaintexts and ciphertext for any of the
17//! following codes, ciphers or digests.
18//!
19//! Additionally a cryptanalysis tool is provided to perform cryptanalysis on any of the
20//! [`CryptographicAtom`]s implemented in this crate.
21//!
22//! ---
23//!
24//! # Features
25//!
26//! To enable any [`CryptographicAtom`], the corresponding feature must be enabled. Though building
27//! the crate without any feature is possible, it is not useful, because then the crate does not
28//! supply any functionality.
29//!
30//! | feature | enables ||| feature | enables |
31//! |-------------|------------------|||-------------|------------------|
32//! | base64 | Base64Context ||| base16, hex | Base16Context |
33//! | base64_url | Base64UrlContext ||| caesar | CaesarContext |
34//! | base32 | Base32Context ||| ui | Ui elements |
35//! | base32_hex | Base32HexContext ||| vigenere | VigenereContext |
36//!
37//! By enabling any of these features the respective modules will be compiled. Additional
38//! information for every [`CryptographicAtom`] can be found in their respective module.
39//!
40//! ---
41//!
42//! # Nomenclature
43//!
44//! This part contains explanations for some words that are frequently used throughout the crate.
45//! The meaning of most of the words might be obvious, but since some of them are used in cases
46//! where they are not normally used, an explanation is included in this crate.
47//!
48//! ## Ciphertext
49//!
50//! A byte vector after an `encrypt` or `encode` operation or before a `decrypt` or `decode`
51//! operation has been performed on the vector.
52//!
53//! ## Plaintext
54//!
55//! A byte vector after a `decrypt` or `decode` operation or before a `encrypt` or `encode`
56//! operation has been performed on the vector.
57//!
58//! ---
59//!
60//! # Please note
61//!
62//! I develop this library/cli-tool mostly for lectures at university and understanding how all the
63//! codes, ciphers and digest-algorithms work internally. It is not intended for use in production
64//! code.
65//!
66//! If you still decide to use this library in production code or to play around with it, every
67//! constructive feedback is appreciated.
68//!
69//! ---
70
71#[cfg(any(
72feature = "ui",
73feature = "cipher",
74feature = "code",
75feature = "digest"
76))]
77pub mod action;
78#[cfg(feature = "cipher")]
79pub mod cipher;
80#[cfg(feature = "ui")]
81pub mod ui;
82#[cfg(feature = "code")]
83pub mod code;
84#[cfg(feature = "digest")]
85pub mod digest;
86#[cfg(feature = "cryptanalysis")]
87pub mod cryptanalysis;
88mod util;
89
90use core::fmt;
91
92
93
94/// Creates a [`HashMap`](std::collections::hash_map::HashMap).
95///
96/// The map is created from an array of `m` `n-tuples`. The first element of each tuple is the key,
97/// the second element is the value. Handing a `1-tuple` to the macro is an error and will deny
98/// compilation. If more than two elements are supplied as a tuple, every tuple-element with an
99/// index greater than 1 will be ignored.
100///
101/// # Usage Example
102///
103/// ```
104/// use std::collections::HashMap;
105/// use encodex::map;
106///
107/// let map = map![("first", 3), ("second", 1), ("third", 0)];
108///
109/// assert_eq!(map.get("first"), Some(&3));
110/// assert_eq!(map.get("second"), Some(&1));
111/// assert_eq!(map.get("third"), Some(&0));
112/// ```
113#[macro_export]
114macro_rules! map {
115 ( $( $x:expr ),* ) => {
116 {
117 let mut temp_map = HashMap::new();
118 $(
119 temp_map.insert($x.0, $x.1);
120 )*
121 temp_map
122 }
123 };
124}
125
126
127
128/// Trait for case insensitive [`CryptographicAtom`]s.
129///
130/// Some ciphertexts of some cryptographic atoms can have either upper- or lowercase ciphertexts. If
131/// a cipher supports both, upper- and lowercase ciphertexts, without changing the meaning of the
132/// ciphertext, it is called case insensitive and should implement this trait in a way, that
133/// [`ciphertext_is_capitalized`](CaseInsensitiveCiphertext::ciphertext_is_capitalized) always
134/// returns a [`Some`] value.
135///
136/// Ciphers that do not support case insensitive ciphertexts should always return [`None`].
137pub trait CaseInsensitiveCiphertext {
138 /// Returns the capitalization state of this cryptographic atom.
139 ///
140 /// Not every atom needs this function. Atoms that do not need this function should always
141 /// return [`None`].
142 fn ciphertext_is_capitalized(
143 &self
144 ) -> Option<bool>;
145 /// Sets the capitalization state of this cryptographic atom.
146 ///
147 /// Some atoms can output capitalized or small case letters ciphertexts that are equivalent to
148 /// each other, e.g. the `Base32Hex` ciphertext `CPN MUO J1E 8== === =` can also be
149 /// `cpn muo j1e 8== === =` and will still be decoded to the same plaintext. This method should
150 /// update the capitalization state for such an atom.
151 fn set_ciphertext_capitalization(
152 &mut self,
153 capitalized: bool,
154 );
155}
156
157/// Trait for cryptographic atoms.
158///
159/// A cryptographic atom is any one `code`, `cipher` or `digest`. This trait defines all methods
160/// that apply to every cryptographic algorithm implemented in this crate. This assures that every
161/// cryptographic atom in this crate can be handled in a generic way.
162///
163/// Every struct in this crate that resembles a cryptographic algorithm implements this trait.
164pub trait CryptographicAtom: CaseInsensitiveCiphertext {
165 /// Returns the [class](AtomClass) of this cryptographic atom.
166 fn get_atom_class(
167 &self
168 ) -> AtomClass;
169 /// Returns the [type](AtomType) of this cryptographic atom.
170 ///
171 /// This function makes it possible to identify the type of an instance of any crypto item.
172 fn get_atom_type(
173 &self
174 ) -> AtomType;
175}
176
177
178
179/// Defines the three classes of [`CryptographicAtom`]s that can be encountered in this crate.
180#[derive(Clone, Copy, Debug, PartialEq)]
181pub enum AtomClass {
182 /// The class of all [`cipher`] contexts.
183 #[cfg(feature = "cipher")]
184 Cipher,
185 /// The class of all [`code`] contexts.
186 #[cfg(feature = "code")]
187 Code,
188 /// The class of all [`digest`] contexts.
189 #[cfg(feature = "digest")]
190 Digest,
191}
192
193
194
195/// Defines all types of [`CryptographicAtom`]s.
196///
197/// There is a value for every atom that is implemented in this crate.
198#[derive(Clone, Copy, Debug, PartialEq)]
199pub enum AtomType {
200 /// The instance type of the [`Base16Context`](code::base_encoding::Base16Context)
201 /// [`CryptographicAtom`].
202 #[cfg(any(feature = "base16", feature = "hex"))]
203 Base16,
204 /// The instance type of the [`Base32Context`](code::base_encoding::Base32Context)
205 /// [`CryptographicAtom`].
206 #[cfg(feature = "base32")]
207 Base32,
208 /// The instance type of the [`Base32HexContext`](code::base_encoding::Base32HexContext)
209 /// [`CryptographicAtom`].
210 #[cfg(feature = "base32hex")]
211 Base32Hex,
212 /// The instance type of the [`Base64Context`](code::base_encoding::Base64Context)
213 /// [`CryptographicAtom`].
214 #[cfg(feature = "base64")]
215 Base64,
216 /// The instance type of the [`Base64UrlContext`](code::base_encoding::Base64UrlContext)
217 /// [`CryptographicAtom`].
218 #[cfg(feature = "base64url")]
219 Base64Url,
220 /// The instance type of the [`Caesar`](cipher::shift_cipher::CaesarContext)
221 /// [`CryptographicAtom`].
222 #[cfg(feature = "caesar")]
223 Caesar,
224 /// The instances type of the [`VigenereContext`](cipher::shift_cipher::VigenereContext)
225 /// [`CryptographicAtom`].
226 #[cfg(feature = "vigenere")]
227 Vigenere,
228}
229
230impl fmt::Display for AtomType {
231 /// Prints the string representation of atom types.
232 ///
233 /// This is really just the identifier defined by the [`AtomType`] enum as a [string](std::str).
234 fn fmt(
235 &self,
236 f: &mut fmt::Formatter<'_>
237 ) -> fmt::Result {
238 match self {
239 #[cfg(any(
240 feature = "base16",
241 feature = "hex"
242 ))]
243 AtomType::Base16 => { write![f, "Base16" ] }
244 #[cfg(feature = "base32")]
245 AtomType::Base32 => { write![f, "Base32" ] }
246 #[cfg(feature = "base32hex")]
247 AtomType::Base32Hex => { write![f, "Base32Hex" ]}
248 #[cfg(feature = "base64")]
249 AtomType::Base64 => { write![f, "Base64"] }
250 #[cfg(feature = "base64url")]
251 AtomType::Base64Url => { write![f, "Base64Url"] }
252 #[cfg(feature = "caesar")]
253 AtomType::Caesar => { write![f, "Caesar"] }
254 #[cfg(feature = "sha256")]
255 AtomType::Sha256 => { write![f, "Sha256"] }
256 #[cfg(feature = "vigenere")]
257 AtomType::Vigenere => { write![f, "Vigenere"] }
258 }
259 }
260}
261
262/// The error used by all [`CryptographicAtom`]s.
263///
264/// Not all errors are required by all crypto atoms. The `InvalidKey` error for example is not
265/// necessary for any `code`, because `code`s as defined by this crate are just a remapping of bit
266/// representations.
267///
268/// Only the errors required by the currently activated features are compiled into the crate.
269#[derive(Debug, PartialEq)]
270pub enum CryptoError {
271 /// Is build when a byte is encountered at a position, where it can not be interpreted by a
272 /// [`CryptographicAtom`].
273 ///
274 /// The first argument is a [`String`] that describes the error in more detail. The second
275 /// argument is the index where the illegal character has been encountered. The last argument is
276 /// the byte stream that has caused the error.
277 ///
278 /// TODO: Change last argument: Implement a struct, that copies only a portion of the malformed
279 /// byte vector and saves it (e.g. the first 100 bytes before and behind the illegal
280 /// character). Additionally it saves the index of the original byte stream where the
281 /// illegal character has been encountered.
282 IllegalCharacter(String, usize, Vec<u8>),
283 /// Is build when a byte stream does not match the residue class (`a + mℤ`), that is required
284 /// for a specific plain- or ciphertext.
285 ///
286 /// The first argument is a [`String`] that describes the error in more detail. The second
287 /// argument is the `a` value of the residue class. The third argument is the `m` value of the
288 /// residue class.
289 #[cfg(any(
290 feature = "base16",
291 feature = "base32",
292 feature = "base32hex",
293 feature = "base64",
294 feature = "base64url",
295 feature = "hex"
296 ))]
297 IllegalResidueClass(String, u8, u8),
298 /// Is build when a [`cipher`] tries to set an invalid key.
299 ///
300 /// The first argument is a [`String`] that describes the error in more detail. The second
301 /// argument is the invalid key.
302 #[cfg(feature = "cipher")]
303 InvalidKey(String, Vec<u8>),
304 /// Is build when a [`CryptographicAtom`] that requires some kind of padding encounters a
305 /// malformed variant of the required padding.
306 ///
307 /// The first argument is a [`String`] that describes the error in more detail. The second
308 /// argument is the byte stream with the malformed padding.
309 ///
310 /// TODO: Replace the last argument here with the same struct as for the IllegalCharacter error.
311 #[cfg(any(
312 feature = "base32",
313 feature = "base32hex",
314 feature = "base64",
315 feature = "base64url"
316 ))]
317 MalformedPadding(String, Vec<u8>),
318 /// Is build when a [`cipher`] tries to perform a [`Decrypt`](cipher::Decrypt) or
319 /// [`Encrypt`](cipher::Encrypt) operation without any key set.
320 ///
321 /// The first argument is a [`String`] that describes the error in more detail.
322 #[cfg(feature = "cipher")]
323 MissingKey(String),
324}
325
326impl fmt::Display for CryptoError {
327 /// Formats every error variant so that it can be nicely printed to the terminal.
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329 if let Err(error) = write![f, ">>> Runtime Error:\n"] {
330 return Err(error);
331 }
332 match self {
333 CryptoError::IllegalCharacter(description, index, origin) => {
334 write![f, "... Class: Illegal Character\n\
335 ... Description: {}\n\
336 ... Origin: {:#?}\n\
337 ... Index: {}\n",
338 description, origin, index]
339 }
340 #[cfg(any(
341 feature = "base16",
342 feature = "base32",
343 feature = "base32hex",
344 feature = "base64",
345 feature = "base64url",
346 feature = "hex"
347 ))]
348 CryptoError::IllegalResidueClass(description, a, m) => {
349 write![f, "... Class: Illegal Residue Class\n\
350 ... Description: {}\n\
351 ... Residue Class: {} + {}ℤ\n",
352 description, a, m]
353 }
354 #[cfg(feature = "cipher")]
355 CryptoError::InvalidKey(description, key) => {
356 write![f, "... Class: Invalid Key\n\
357 ... Description: {}\n\
358 ... Invalid Key: {:#?}\n",
359 description, key]
360 }
361 #[cfg(any(feature = "base32", feature = "base32hex", feature = "base64",
362 feature = "base64url"))]
363 CryptoError::MalformedPadding(description, origin) => {
364 write![f, "... Class: Malformed Padding\n\
365 ... Description: {}\n\
366 ... Origin: {:#?}\n",
367 description, origin]
368 }
369 #[cfg(feature = "cipher")]
370 CryptoError::MissingKey(description) => {
371 write![f, "... Class: Missing Key\n\
372 ... Description: {}\n",
373 description]
374 }
375 }
376 }
377}