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}