Add files via upload
This commit is contained in:
parent
33311a9e5f
commit
73f60e3ae8
|
|
@ -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"
|
||||
|
|
@ -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<char> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
pub mod maps;
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,2 @@
|
|||
pub mod text;
|
||||
pub mod images;
|
||||
|
|
@ -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<String>` 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<String> {
|
||||
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
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,2 @@
|
|||
pub mod text;
|
||||
pub mod images;
|
||||
|
|
@ -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<u8> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue