From 73f60e3ae8bf6258c2af48d497d237df6989b3bb Mon Sep 17 00:00:00 2001 From: viraladmin <00purple@gmail.com> Date: Mon, 9 Oct 2023 15:59:19 -0600 Subject: [PATCH] Add files via upload --- Cargo.toml | 17 +++++ src/char_mappings/maps.rs | 147 ++++++++++++++++++++++++++++++++++++++ src/char_mappings/mod.rs | 1 + src/decryption/images.rs | 46 ++++++++++++ src/decryption/mod.rs | 2 + src/decryption/text.rs | 49 +++++++++++++ src/encryption/images.rs | 96 +++++++++++++++++++++++++ src/encryption/mod.rs | 2 + src/encryption/text.rs | 61 ++++++++++++++++ src/lib.rs | 140 ++++++++++++++++++++++++++++++++++++ 10 files changed, 561 insertions(+) create mode 100644 Cargo.toml create mode 100644 src/char_mappings/maps.rs create mode 100644 src/char_mappings/mod.rs create mode 100644 src/decryption/images.rs create mode 100644 src/decryption/mod.rs create mode 100644 src/decryption/text.rs create mode 100644 src/encryption/images.rs create mode 100644 src/encryption/mod.rs create mode 100644 src/encryption/text.rs create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7405565 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "encrypted_images" +version = "1.1.0" +edition = "2021" +description = "Encrypt Text to Images and decrypt text from images." +authors = ["Bruce Bates | https://encryptedimages.com"] + +[dependencies] +openssl = { version = "0.10.57", features = ["vendored"] } +base64 = "0.21.4" +image = { version = "0.23", features = ["png"] } +native-dialog = "0.6.4" +hmac = "0.12.1" +sha2 = "0.10.7" +hex-literal = "0.4.1" +encoding = "0.2" +subtle = "2.4" diff --git a/src/char_mappings/maps.rs b/src/char_mappings/maps.rs new file mode 100644 index 0000000..d194a57 --- /dev/null +++ b/src/char_mappings/maps.rs @@ -0,0 +1,147 @@ + pub mod mappings { + use std::collections::HashMap; + pub(crate) fn get_color(char: char) -> Option<(u8, u8, u8)> { + match char { + 'a' => Some((204, 180, 194)), + 'A' => Some((255, 255, 255)), + 'b' => Some((197, 186, 201)), + 'B' => Some((221, 206, 212)), + 'c' => Some((181, 185, 193)), + 'C' => Some((184, 201, 223)), + 'd' => Some((224, 218, 192)), + 'D' => Some((185, 191, 195)), + 'e' => Some((181, 197, 198)), + 'E' => Some((193, 206, 255)), + 'f' => Some((252, 193, 211)), + 'F' => Some((183, 192, 229)), + 'g' => Some((180, 191, 192)), + 'G' => Some((187, 219, 189)), + 'h' => Some((195, 187, 234)), + 'H' => Some((182, 216, 189)), + 'i' => Some((197, 183, 248)), + 'I' => Some((200, 182, 204)), + 'j' => Some((255, 235, 196)), + 'J' => Some((194, 186, 228)), + 'k' => Some((199, 238, 239)), + 'K' => Some((208, 247, 234)), + 'l' => Some((244, 214, 189)), + 'L' => Some((187, 243, 239)), + 'm' => Some((188, 231, 238)), + 'M' => Some((187, 197, 227)), + 'n' => Some((186, 240, 191)), + 'N' => Some((187, 198, 206)), + 'o' => Some((205, 193, 184)), + 'O' => Some((191, 187, 197)), + 'p' => Some((194, 200, 206)), + 'P' => Some((195, 183, 229)), + 'q' => Some((182, 219, 196)), + 'Q' => Some((238, 216, 184)), + 'r' => Some((199, 181, 208)), + 'R' => Some((239, 231, 198)), + 's' => Some((189, 188, 230)), + 'S' => Some((242, 192, 230)), + 't' => Some((199, 199, 199)), + 'T' => Some((188, 190, 230)), + 'u' => Some((230, 180, 253)), + 'U' => Some((241, 247, 247)), + 'v' => Some((242, 190, 199)), + 'V' => Some((230, 247, 234)), + 'w' => Some((197, 186, 249)), + 'W' => Some((194, 247, 249)), + 'x' => Some((242, 182, 246)), + 'X' => Some((188, 222, 193)), + 'y' => Some((188, 194, 183)), + 'Y' => Some((197, 195, 197)), + 'z' => Some((187, 249, 240)), + 'Z' => Some((233, 231, 242)), + '0' => Some((195, 184, 218)), + '1' => Some((232, 180, 196)), + '2' => Some((191, 193, 196)), + '3' => Some((185, 186, 186)), + '4' => Some((191, 247, 180)), + '5' => Some((187, 199, 248)), + '6' => Some((248, 198, 184)), + '7' => Some((243, 195, 184)), + '8' => Some((232, 192, 208)), + '9' => Some((239, 197, 183)), + '/' => Some((199, 187, 241)), + '+' => Some((195, 216, 223)), + _ => None, + } + } + pub(crate) fn numbers_to_letter(r: u8, g: u8, b: u8) -> Option { + let color_map: HashMap<(u8, u8, u8), char> = [ + ((204, 180, 194), 'a'), + ((255, 255, 255), 'A'), + ((197, 186, 201), 'b'), + ((221, 206, 212), 'B'), + ((181, 185, 193), 'c'), + ((184, 201, 223), 'C'), + ((224, 218, 192), 'd'), + ((185, 191, 195), 'D'), + ((181, 197, 198), 'e'), + ((193, 206, 255), 'E'), + ((252, 193, 211), 'f'), + ((183, 192, 229), 'F'), + ((180, 191, 192), 'g'), + ((187, 219, 189), 'G'), + ((195, 187, 234), 'h'), + ((182, 216, 189), 'H'), + ((197, 183, 248), 'i'), + ((200, 182, 204), 'I'), + ((255, 235, 196), 'j'), + ((194, 186, 228), 'J'), + ((199, 238, 239), 'k'), + ((208, 247, 234), 'K'), + ((244, 214, 189), 'l'), + ((187, 243, 239), 'L'), + ((188, 231, 238), 'm'), + ((187, 197, 227), 'M'), + ((186, 240, 191), 'n'), + ((187, 198, 206), 'N'), + ((205, 193, 184), 'o'), + ((191, 187, 197), 'O'), + ((194, 200, 206), 'p'), + ((195, 183, 229), 'P'), + ((182, 219, 196), 'q'), + ((238, 216, 184), 'Q'), + ((199, 181, 208), 'r'), + ((239, 231, 198), 'R'), + ((189, 188, 230), 's'), + ((242, 192, 230), 'S'), + ((199, 199, 199), 't'), + ((188, 190, 230), 'T'), + ((230, 180, 253), 'u'), + ((241, 247, 247), 'U'), + ((242, 190, 199), 'v'), + ((230, 247, 234), 'V'), + ((197, 186, 249), 'w'), + ((194, 247, 249), 'W'), + ((242, 182, 246), 'x'), + ((188, 222, 193), 'X'), + ((188, 194, 183), 'y'), + ((197, 195, 197), 'Y'), + ((187, 249, 240), 'z'), + ((233, 231, 242), 'Z'), + ((195, 184, 218), '0'), + ((232, 180, 196), '1'), + ((191, 193, 196), '2'), + ((185, 186, 186), '3'), + ((191, 247, 180), '4'), + ((187, 199, 248), '5'), + ((248, 198, 184), '6'), + ((243, 195, 184), '7'), + ((232, 192, 208), '8'), + ((239, 197, 183), '9'), + ((199, 187, 241), '/'), + ((195, 216, 223), '+'), + ] + .iter() + .cloned() + .collect(); + match color_map.get(&(r, g, b)) { + Some(&letter) => Some(letter), + None => None, + } + } + } diff --git a/src/char_mappings/mod.rs b/src/char_mappings/mod.rs new file mode 100644 index 0000000..a98b9c8 --- /dev/null +++ b/src/char_mappings/mod.rs @@ -0,0 +1 @@ +pub mod maps; diff --git a/src/decryption/images.rs b/src/decryption/images.rs new file mode 100644 index 0000000..24187a6 --- /dev/null +++ b/src/decryption/images.rs @@ -0,0 +1,46 @@ + /// Decodes an encoded image and extracts text. + /// + /// This function takes an encoded image as input, decodes it, and extracts text from the image + /// based on the pixel colors. It returns the extracted text as an `Option`. + /// + /// # Arguments + /// + /// * `encoded_image` - The Base64 encoded image to be decoded and processed. + /// + /// # Returns + /// + /// An `Option` containing the extracted text if successful, or `None` if there was an + /// error during image decoding or text extraction. + /// + /// # Examples + /// + /// ``` + /// use encrypted_images::decryption::images::decode_image_and_extract_text; + /// + /// let encoded_image = "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"; + /// let extracted_text = decode_image_and_extract_text(encoded_image); + /// assert!(extracted_text.is_some()); + /// ``` + use image::io::Reader as ImageReader; + use crate::char_mappings::maps::mappings::numbers_to_letter; + pub fn decode_image_and_extract_text(encoded_image: &str) -> Option { + let image_data = base64::decode(encoded_image).ok()?; + let img = ImageReader::new(std::io::Cursor::new(image_data)) + .with_guessed_format() + .unwrap() + .decode() + .unwrap() + .to_rgba8(); + let width = img.width(); + let mut extracted_text = String::with_capacity(width as usize); + for x in 0..width { + let pixel = img.get_pixel(x, 0); + let [r, g, b, _] = pixel.0; + if let Some(c) = numbers_to_letter(r, g, b) { + extracted_text.push(c); + } + } + let first_char = extracted_text.remove(0); + extracted_text.push(first_char); + Some(extracted_text) + } diff --git a/src/decryption/mod.rs b/src/decryption/mod.rs new file mode 100644 index 0000000..36d8864 --- /dev/null +++ b/src/decryption/mod.rs @@ -0,0 +1,2 @@ +pub mod text; +pub mod images; diff --git a/src/decryption/text.rs b/src/decryption/text.rs new file mode 100644 index 0000000..536c1b6 --- /dev/null +++ b/src/decryption/text.rs @@ -0,0 +1,49 @@ + /// Decrypts an encoded result using an optional decryption key. + /// + /// This function takes an encoded result and an optional decryption key, and attempts to decrypt + /// the result using AES-128 CBC decryption. It also verifies the integrity of the data using HMAC. + /// + /// # Arguments + /// + /// * `encoded_result` - The Base64-encoded result to be decrypted. + /// * `key` - An optional decryption key. If not provided, the default key "welovenfts" is used. + /// + /// # Returns + /// + /// An `Option` containing the decrypted plaintext if successful, or `None` if decryption + /// fails or if the HMAC verification fails. + /// + /// # Examples + /// + /// ``` + /// use encrypted_images::decryption::text::decrypts; + /// + /// let encoded_result = "VkdocGMybHpiWGxqYnc9PbUWoPUFfy9Izm1wkCFZ8gSMWr6EUGW6UwYpnaounDkYmLNDjqWyvjcus2atCStKBOJSCnosjApRrcJrm44hatuaJHSYONbHNOmpk3Rja/xH"; + /// let key = "welovenfts"; + /// let decrypted_data = decrypts(encoded_result, Some(key)); + /// assert!(decrypted_data.is_some()); + /// ``` + use subtle::ConstantTimeEq; + use openssl::symm::{decrypt, Cipher}; + use base64::decode; + use crate::encryption::text::hmac::calculate_hmac; + pub fn decrypts(encoded_result: &str, key: Option<&str>) -> Option { + let key = key.unwrap_or("welovenfts"); + let mut padded_key = key.as_bytes().to_vec(); + while padded_key.len() < 16 { + padded_key.push(b'\0'); + } + let result_bytes = decode(encoded_result).ok()?; + let iv = &result_bytes[..16]; + let hmac = &result_bytes[16..48]; + let ciphertext = &result_bytes[48..]; + let hmac_calculated = calculate_hmac(ciphertext, &padded_key); + if hmac_calculated.ct_eq(hmac).unwrap_u8() == 1 { + let cipher = Cipher::aes_128_cbc(); + let decrypted_data = decrypt(cipher, &padded_key, Some(iv), ciphertext).ok()?; + Some(String::from_utf8_lossy(&decrypted_data).to_string()) + } else { + println!("Decryption Failed"); + None + } + } diff --git a/src/encryption/images.rs b/src/encryption/images.rs new file mode 100644 index 0000000..41bd0fd --- /dev/null +++ b/src/encryption/images.rs @@ -0,0 +1,96 @@ + /// Creates an image from ciphertext with optional watermark. + /// + /// This function takes ciphertext and an optional watermark as input and generates an image + /// where the ciphertext is visually represented. The generated image is encoded as a PNG image + /// and then Base64 encoded before being returned as an `Option`. + /// + /// # Arguments + /// + /// * `ciphertext` - The ciphertext to be represented in the image. + /// * `watermark` - An optional watermark to overlay on the image. (bitcoin, ethereum, cardano, + /// or none: + /// + /// # Returns + /// + /// An `Option` containing the Base64 encoded image if successful, or `None` if there + /// was an error during image creation or encoding. + /// + /// # Examples + /// + /// ``` + /// use encrypted_images::encryption::images::create_img; + /// + /// let ciphertext = "ThisIsCiphertext"; + /// let watermark = "Bitcoin"; + /// let image_data = create_img(ciphertext, watermark); + /// assert!(image_data.is_some()); + /// ``` + use image::{ColorType, DynamicImage, RgbaImage, Rgba}; + use image::png::PngEncoder; + use image::io::Reader as ImageReader; + use crate::char_mappings::maps::mappings::get_color; + use std::io::Cursor; + + fn load_watermark(watermark: &str) -> Option { + let watermark: &str = match watermark { + "empty" => "", + "bitcoin" => "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TxQ8qInYQUchQXbQgKuIoVSyChdJWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIq4uToouU+L+k0CLGg+N+vLv3uHsHCLUSU822CUDVLCMRjYjpzKrY8YouBNCHMQxLzNRjycUUPMfXPXx8vQvzLO9zf44eJWsywCcSzzHdsIg3iGc2LZ3zPnGQFSSF+Jx43KALEj9yXXb5jXPeYYFnBo1UYp44SCzmW1huYVYwVOJp4pCiapQvpF1WOG9xVksV1rgnf2Egq60kuU5zCFEsIYY4RMiooIgSLIRp1UgxkaD9iId/0PHHySWTqwhGjgWUoUJy/OB/8LtbMzc16SYFIkD7i21/jAAdu0C9atvfx7ZdPwH8z8CV1vSXa8DsJ+nVphY6Anq3gYvrpibvAZc7wMCTLhmSI/lpCrkc8H5G35QB+m+B7jW3t8Y+Th+AFHW1fAMcHAKjecpe93h3Z2tv/55p9PcDpKVyu4rx+1IAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfnCQwTBQINStQTAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAoZJREFUWMPtl81LVUEYxn9XKsRyKAejlZaWeSstwV0r21QE7fI/iKRVjYuIImxhqxohWhStIyIhIgpbFUgFUV0/Em8bQxSRaiQmslDktmikyzj36D3n0MoXzmKe886Z5zzzfszAusUwq2Wf1XLYanm/CDsU51sVMTk0A61Ak1u8FchZLQtWy1P/iwDAuDcGmCjnQxtWkboJeAXkgTFg2C2607nkAwRGyyGQWYXASeBxhMukI1QPZIEJoUxjagoA34G3wD5gS+B9vXuWrcFqOQncA24JZWYSKfBPiZpKyPQDJ8r4uXngilDmRuIgFGruN/DBg6eBOuAIcAl4572vAq5bLU8n2YJQ5C9bXigzBUwBL4BrVsszwG3Prxe4m0YariCwUilzB3jmwbVWy21pEGjxxqXSbWlFoFUUbCICVsuWADwS8OsAjnlwrvrc3FLSGGgOYG+sljngK/AFaAP2B/yuphGE2RJ4W8ScceCCUOZJGr3AV+AjoF2VnC4xpz4QN6ltwaBQprto7/cCXcBZYFNRHei1WlYJZS4nVcDv9cNe+n0SypwHjgbmdlstt8YmYLXMBkr2WLhimpfAkAdXAo1lbYHVUgINLtc7Ai75qMqdRjfsAB5GNJmfJYhfdMSLbTHqjFCKwOEI0lXAvNXyM2BcFtQCe4DtAf9HQpmFcglY4D1wMMJnl3vaV2nJPYnOA1bL3cBx4KaDZoEda9jeWaBTKDOYwoFEdgIP3LA9AyOFv6mYdcey5556r4EBocyv1AtRBkarlVm0WtY5aEAo0xX3jlFuKZ6oVmbBLb7ZYUNJLjlrVaAfmHHy+qV5PAmBTJxJP/pqNhYKmVbgAPBUKPNt/cIa1/4AtWS4hwYohc4AAAAASUVORK5CYII=", + "ethereum" => "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TxQ8qInYQUchQXbQgKuIoVSyChdJWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIq4uToouU+L+k0CLGg+N+vLv3uHsHCLUSU822CUDVLCMRjYjpzKrY8YouBNCHMQxLzNRjycUUPMfXPXx8vQvzLO9zf44eJWsywCcSzzHdsIg3iGc2LZ3zPnGQFSSF+Jx43KALEj9yXXb5jXPeYYFnBo1UYp44SCzmW1huYVYwVOJp4pCiapQvpF1WOG9xVksV1rgnf2Egq60kuU5zCFEsIYY4RMiooIgSLIRp1UgxkaD9iId/0PHHySWTqwhGjgWUoUJy/OB/8LtbMzc16SYFIkD7i21/jAAdu0C9atvfx7ZdPwH8z8CV1vSXa8DsJ+nVphY6Anq3gYvrpibvAZc7wMCTLhmSI/lpCrkc8H5G35QB+m+B7jW3t8Y+Th+AFHW1fAMcHAKjecpe93h3Z2tv/55p9PcDpKVyu4rx+1IAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfnCQwSFQ84+9DIAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAA1ZJREFUWMO9l01oXFUUx3/3vXkz+TYwpgQEF8ZN/IqSgrHXBI2LcWNJzUYskTpxIQimcayCQoSi3XTevAo2Wl1LoZsqLmpWWchtQ11odQilGSsGgk1N22RoOjO+9+a6MA3T5mvmZWYuvMU799xz/+fc8wm7WMmk6u7tVd27kRHazWEh+CIUQgCDQWUYQQ/atnpNawY9jxf7+tTrdQWQSqkmrXHu/rsuTiymmusGQGuOAp0lpD3LyxytCwDbVt1ac/h+uu8zLmXlDmkE0P4rwNyELlyXUzUFYNvqIDCw1b7v09/Xp0ZqAmByUjVrTWonPtfFHhlRLVUHkMvxKbCnDNaOuTk+qyoAx1GPa81YuUJdl3cHBtQTVQPg+5wCRCVvm8/zdVUA2LZ6A5D307NZSKfBuwWeuyno5/btU4d2TOfbbZ48qVpzOf4AOu7Slpbg6lWYn1/zjczaRhPQCqGGe0QsdXXx6JkzciVQMcrnOQZ0FItw7RpkMnD9+hbMd/7/vAjQBmYjCMGD8/McA96p2AKplHqyUOC3hQW4fBlu394iOjLbqPYAGE3Q3s7T09PyUkUWmJ1ldG4OCoWAZc4DbkDxFty8w5uwMX3v6AP9/epgsUjS9+8pPOVZAMBikTCJS3/JbyuOAsdRLwwNkfU8uiyL44ZBvmztLQq0cJwcjzQ/xOrgoIoFigLbVt9pjaU17509ixACx/d5eVsLRJiiyGGjExEOc0Jr/r14Ub4SNA/EgR4h+GV4mEMHDjAciTBkGGQ20TpDO0OhTl5teJh4KMSvWvOUYRAPnIgSCXkTeAto1JoPgSv799OUz9NjWUysB18bEyzT0xClxTD40/M4UiwSNk3iMzPyn8CJqOQpklqTKCH9ZJq8fe4cq4uLEI3Sms3ypdY8v/4SEU6cPy/Hq1ILXJcJIF0aIL5POhbj/cZGPlhZ4ffSy4VgdjXLx2V11uU6tuOox3yfn9eS7vo6fXpjTmxr49npaZmuaj8wPi5nheCjnfjCYT4p9/KKW7JEQn4O/LjNoDJ14YJM1rQptSziwAbPNgxuRKOM1rwrHhuTfwux8SLTZHRqSi7UZTBJJOQPwOR6RQvxzcyM/L7ew+kR4CXTRHje5pWu5iuZVM/s3at6dyPjP1yoK/XOy4qhAAAAAElFTkSuQmCC", + "cardano" => "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t8Y+Th+AFHW1fAMcHAKjecpe93h3Z2tv/55p9PcDpKVyu4IiMZUAAA12aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjJkOWJhMWJjLTU5ODUtNGI2YS04NWMxLWNiNDU3MmY1Y2EyOCIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpiMzMzZmM1ZS1kZTJjLTRkNDEtOGYwNy1mMmVlYzgzOWUwNGEiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplZDFjN2Q0OC01MmEwLTRhY2UtYWI0Ni1mOWQwOTk5ZGE0OGMiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJXaW5kb3dzIgogICBHSU1QOlRpbWVTdGFtcD0iMTY5NDU0NTY4Nzg3MTk2NSIKICAgR0lNUDpWZXJzaW9uPSIyLjEwLjMyIgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAgMi4xMCIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMzowOToxMlQxMzowODowNy0wNjowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjM6MDk6MTJUMTM6MDg6MDctMDY6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMGY3MGJmYy04OTJhLTQzNTktOWRhNi1lMTdkMmNkOTNkMTkiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkdpbXAgMi4xMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OndoZW49IjIwMjMtMDktMTJUMTM6MDg6MDciLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+P3y/GgAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+cJDBMIB8iOXtEAAALnSURBVFjD3ZdLSJVBFMd/hkHeRYty0YMem7BbUNlUO4tKslpENRGCkhGlbcIHEbSSoCh6eF300h4EXVOEySgio9ootEiHiiCDVi7F2mnZQ2zRuTV83jvffbjIDnx8M+c535z/nDkfTGtS5jzKnM7FxYyQAGUo89GjsRaIeuyHUWadL0R+yAItcCq1VJeG2NcD75n+pEwlyvSgzPI0dBXK9KNMZTqu89Ncwn6gBCgGPkigQqBH5BuwekTGUUCJfjw3EP6lA8AWrG53eEslWBRY7OAiDqyX/Ge81a0ocyYD/TqUqctAP4YyLb4UFAGRECc7gN1AG1Y3C28zUA50YXW3x3pOcNfzQgEFR4CzWD2IMhWBvJbJgrsc3j6sNihTDBwFbmJ1X7YgjAmYFgC7gJ0BuU5isw0wwAVgq2BkY7YL6BAHnTJ/l6RQzQrwBuR9B5gL3PYFyMsAQAuBceCwg4HYHzD+PqqPgFtAAVYPTmUhakaZCXmqPXo1jt71yfL7MyfXAWWaBDCJKjaKMlccp0VAbQAXPswkqAZloo6fuzAxhjIlTqy2GcA852hE5JntKVa+4jURmI8741FgDPgi85/AULJtLEjCu+ZsbY0nBdUoMy56TelkNxMQLgG+Cgj3AO1Y3SSyeqACeADckJI8lPsClDkI1AEXsTqOMg3AJUejFvgGuIA7idXn5DaMAY1YfTXby+gQsBqockq1SyulK3JplXODFkqJzvo2PA60Aidk/jwgf+IUqQQ9lvcxoCXsVswLbPk94AdWV3nSsl0w0InVL4RXIl/6EKufemzjwCKs3pSqFI8CI/4uUXcD3eKwAfiO1ZeB3jQwN+gcwwxOgTIRYAVW9zu8vXLpILIBR1YKvMbqz1PVEcWAvkCf1yPt2Vv5skTwcuAZ0DaVPWGv9HkDTio+pfgneCP6Hf9FOx5Jt732tPOFuWBgjXQ2qQK8QhnjsW+U1GWJAatfAvNDft2GPfbL/vks/wIuPeijyMLzBQAAAABJRU5ErkJggg==", + _ => "", + }; + let decoded = base64::decode(watermark).ok()?; + let cursor = Cursor::new(decoded); + ImageReader::with_format(cursor, image::ImageFormat::Png) + .decode() + .ok() + } + + pub fn create_img(ciphertext: &str, watermark: &str) -> Option { + let width = ciphertext.len() as u32; + let height = width; + let mut img: RgbaImage = image::ImageBuffer::new(width, height); + let last_column = ciphertext.chars().last(); + let shifted_ciphertext = if let Some(last) = last_column { + last.to_string() + &ciphertext[..width as usize - 1] + } else { + ciphertext.to_string() + }; + for x in 0..width { + let char = shifted_ciphertext.chars().nth(x as usize).unwrap_or('a'); + let color = get_color(char).unwrap_or((0, 0, 0)); + for y in 0..height { + let red = if y == 0 { + color.0 + } else { + (color.0 as i32 - (y as i32 + 100)).abs().min(255) as u8 + }; + let green = if y == 0 { + color.1 + } else { + (color.1 as i32 - (y as i32 + 134)).abs().min(255) as u8 + }; + let blue = if y == 0 { + color.2 + } else { + (color.2 as i32 - (y as i32 + 131)).abs().min(255) as u8 + }; + let rgba_color = Rgba([red, green, blue, 255]); + img.put_pixel(x as u32, y, rgba_color); + } + } + let watermark_img = load_watermark(watermark); + if let Some(watermark_img) = watermark_img { + let nw = (width / 2) - 16; + let nh = (height / 2) - 16; + image::imageops::overlay(&mut img, &watermark_img, nw, nh); + } + let mut buf = Vec::new(); + let encoder = PngEncoder::new(&mut buf); + let dyn_img: DynamicImage = DynamicImage::ImageRgba8(img); + encoder + .encode(&dyn_img.to_rgba8(), width, height, ColorType::Rgba8) + .ok()?; + let encoded_image = base64::encode(&buf); + Some(encoded_image) + } diff --git a/src/encryption/mod.rs b/src/encryption/mod.rs new file mode 100644 index 0000000..36d8864 --- /dev/null +++ b/src/encryption/mod.rs @@ -0,0 +1,2 @@ +pub mod text; +pub mod images; diff --git a/src/encryption/text.rs b/src/encryption/text.rs new file mode 100644 index 0000000..ebe4aa6 --- /dev/null +++ b/src/encryption/text.rs @@ -0,0 +1,61 @@ + use openssl::symm::{encrypt, Cipher}; + use base64::encode; + use crate::encryption::text::hmac::calculate_hmac; + /// Encrypts the input text with an optional encryption key. + /// + /// The `input` parameter is the text to be encrypted. + /// + /// The `key` parameter is an optional encryption key. If not provided, a default key is used. + /// + /// # Examples + /// + /// Encrypt a text with a custom key: + /// + /// ``` + /// use encrypted_images::encryption::text::encrypts; + /// + /// let input = "ThisIsJustaTestString"; + /// let key = Some("your_secret_key"); + /// let encrypted = encrypts(input, key); + /// + /// assert!(encrypted.len() > 0); + /// ``` + /// + /// Encrypt a text without providing a custom key: + /// + /// ``` + /// use encrypted_images::encryption::text::encrypts; + /// + /// let input = "ThisIsJustaTestString"; + /// let encrypted = encrypts(input, None); + /// + /// assert!(encrypted.len() > 0); + /// ``` + pub fn encrypts(input: &str, key: Option<&str>) -> String { + let cipher = Cipher::aes_128_cbc(); + let key = key.unwrap_or("welovenfts"); + let iv_bytes = &input.to_string()[..10]; + let iv = base64::encode(iv_bytes); + let mut padded_key = key.as_bytes().to_vec(); + while padded_key.len() < 16 { + padded_key.push(b'\0'); + } + let ciphertext = encrypt(cipher, &padded_key, Some(iv.as_bytes()), input.as_bytes()).unwrap(); + let hmac = calculate_hmac(&ciphertext, &padded_key); + let mut result = iv.into_bytes(); + result.extend_from_slice(&hmac); + result.extend_from_slice(&ciphertext); + let encoded_result = encode(&result); + encoded_result + } + pub mod hmac { + pub(crate) fn calculate_hmac(data: &[u8], key: &[u8]) -> Vec { + use openssl::hash::MessageDigest; + use openssl::pkey::PKey; + use openssl::sign::Signer; + let pkey = PKey::hmac(key).unwrap(); + let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + signer.update(data).unwrap(); + signer.sign_to_vec().unwrap() + } + } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..506b506 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,140 @@ + pub mod char_mappings; + pub mod encryption; + pub mod decryption; + + + #[cfg(test)] + mod tests { + use crate::char_mappings::maps::mappings::{get_color, numbers_to_letter}; + use crate::encryption::text::encrypts; + use crate::decryption::text::decrypts; + use crate::encryption::images::create_img; + use crate::decryption::images::decode_image_and_extract_text; + use std::time::{Instant, Duration}; + + #[test] + fn test_char_conversion() { + // Define valid characters for testing + let valid_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"; + for character in valid_characters.chars() { + // Get the color for the character + let color_result = get_color(character); + // Get the character back from the color + let character_result = numbers_to_letter(color_result.unwrap().0, color_result.unwrap().1, color_result.unwrap().2); + // Check if character_result matches the input character + assert_eq!(character_result.unwrap(), character); + } + + // Test invalid characters + let invalid_characters = "!@#$%^&*()_"; + for character in invalid_characters.chars() { + // Attempt to get a color for the invalid character + let color_result = get_color(character); + + // Ensure the result is None for invalid characters + assert_eq!(color_result, None); + } + } + + #[test] + fn test_encrypts_decrypts_with_key() { + // Input data to encrypt + let input = "ThisIsJustaTestString"; + // Your encryption key (replace with the actual key you use) + let key = Some("your_secret_key"); + // Encrypt the input + let encrypted = encrypts(input, key.clone()); + // Decrypt the encrypted data + let decrypted = decrypts(&encrypted, key); + // Assert that decryption matches the original input + assert_eq!(decrypted, Some(input.to_string())); + } + + #[test] + fn test_encrypts_decrypts_without_key() { + // Input data to encrypt + let input = "ThisIsJustsTestString"; + // Encrypt the input without a key + let encrypted = encrypts(input, None); + // Decrypt the encrypted data without a key + let decrypted = decrypts(&encrypted, None); + // Assert that decryption matches the original input + assert_eq!(decrypted, Some(input.to_string())); + } + + #[test] + fn test_maximum_length() { + // Your encryption key + let key = Some("your_secret_key"); + + // let mut length = 1844674406; // Start with an initial length + // let max_length = 1844674407; // Set the maximum length + let mut length = 10; // change when doing real tests but they take a long time + let max_length = 11; //change when doing real tests but they take a long time + while length <= max_length { + let encrypted = encrypts(&"A".repeat(length), key.clone()); + let decrypted = decrypts(&encrypted, key.clone()); + + if decrypted.is_none() { + println!("Maximum size: {} characters", length); + break; + } + length += 1; + } + } + + #[test] + fn test_decryption_timing() { + let encoded_result = "Your encrypted data"; // Replace with an actual encoded result + let key = Some("Your secret key"); // Replace with an actual key + // Measure the time for decryption + let start_time = Instant::now(); + let _ = decrypts(encoded_result, key); + let end_time = Instant::now(); + let decryption_time = end_time - start_time; + // Ensure that decryption time is within an acceptable range (e.g., less than 1 second) + assert!(decryption_time < Duration::from_secs(1)); + } + + #[test] + fn test_minimum_length() { + // minimum 10 characters required to encrypt + let input = "aaaaaaaaaa"; + // Your encryption key + let key = Some("your_secret_key"); + // Encrypt the input + let encrypted = encrypts(input, key.clone()); + // Decrypt the encrypted data + let decrypted = decrypts(&encrypted, key); + // Assert that decryption matches the original input + assert_eq!(decrypted, Some(input.to_string())); + } + + #[test] + fn test_create_img_and_decode() { + let ciphertext = "ThisIsJustaTestString"; + let watermark = "Bitcoin"; // Text watermark + // Create the image + let encoded_image = create_img(ciphertext, watermark); + // Decode the image and extract text + let extracted_text = decode_image_and_extract_text(&encoded_image.unwrap()); + // Check if extracted text matches the original ciphertext + assert_eq!(extracted_text, Some(ciphertext.to_string())); + } + + #[test] + fn test_encrypts_decrypts() { + // Input data to encrypt + let input = "ThisIsJustaTestString"; + // Your encryption key (replace with the actual key you use) + let key = Some("your_secret_key"); + // Encrypt the input + let encrypted = encrypts(input, key.clone()); + // Print the encrypted string for testing + println!("Encrypted: {}", encrypted); + // Decrypt the encrypted data + let decrypted = decrypts(&encrypted, key); + // Assert that decryption matches the original input + assert_eq!(decrypted, Some(input.to_string())); + } + }