diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 209d68a..64ff8ea 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,5 +1,5 @@ name: CI -on: [push, pull_request] +on: [pull_request] env: CARGO_TERM_COLOR: always diff --git a/.gitignore b/.gitignore index 212de44..ffdfa4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -.DS_Store \ No newline at end of file +.DS_Store +.idea/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 6252750..8367ac1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,34 +2,20 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "cryptotools" -version = "0.1.0" -source = "git+https://github.com/heroesofcode/cryptotools#9f2112078830faa3ebadd42f89a27e6e4fdefb8b" -dependencies = [ - "md5 0.7.0", -] - [[package]] name = "cryptotools" version = "0.3.0" dependencies = [ - "md5 0.8.0", + "md5", ] [[package]] name = "example" version = "0.1.0" dependencies = [ - "cryptotools 0.1.0", + "cryptotools", ] -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - [[package]] name = "md5" version = "0.8.0" diff --git a/README.md b/README.md index 23a03a5..d1c412c 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,15 @@ [![Docs](https://docs.rs/cryptotools/badge.svg)](https://docs.rs/cryptotools) [![License](https://img.shields.io/github/license/heroesofcode/cryptotools.svg)](https://github.com/heroesofcode/cryptotools/blob/main/LICENSE) -cryptotools is a cryptography library, with it you can: +cryptotools is a simple, easy-to-use library for cryptographic utilities in Rust. It currently provides the following: -- [x] Encode to base64 -- [x] Decode the base64 value -- [x] Encrypt to md5 +- [x] Base64 encoding +- [x] Base64 decoding +- [x] MD5 encryption (hashing) ## Installing -In the file `Cargo.toml` +cargo add cryptotools ```toml [dependencies] @@ -22,28 +22,28 @@ cryptotools = "0.3.0" ## Usage -In the first example, if you want to encode and decode base64 +### Base64 Encoding & Decoding ```rust use cryptotools::encode_base64::Base64Encode; use cryptotools::decode_base64::Base64Decode; -// Encode -let encode = Base64Encode::encode("123456789"); -println!("{}", encode); +let input = "hello world"; +let encoded = Base64Encode::encode(input); +println!("Base64 Encoded: {}", encoded); -// Decode -let decode = Base64Decode::decode("MTIzNDU2Nzg5"); -println!("{}", decode); +let decoded = Base64Decode::decode(&encoded); +println!("Base64 Decoded: {}", decoded); ``` -To encrypt a value to md5 +### MD5 Hashing ```rust use cryptotools::encrypt_md5::MD5; -let md5 = MD5::encrypt("9999"); -println!("{}", md5); +let input = "password123"; +let hash = MD5::encrypt(input); +println!("MD5 Hash: {}", hash); ``` ## License diff --git a/examples/README.md b/examples/README.md index ebf80e2..7d7207e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,5 @@ # Example ```sh -cd example -cargo run +cargo run -p example ``` diff --git a/examples/example/Cargo.toml b/examples/example/Cargo.toml index 11bd099..90073b9 100644 --- a/examples/example/Cargo.toml +++ b/examples/example/Cargo.toml @@ -6,4 +6,4 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cryptotools = { git = "https://github.com/heroesofcode/cryptotools" } \ No newline at end of file +cryptotools = { path = "../.." } \ No newline at end of file diff --git a/examples/example/src/main.rs b/examples/example/src/main.rs index 5bd55f4..6a859d8 100644 --- a/examples/example/src/main.rs +++ b/examples/example/src/main.rs @@ -1,17 +1,17 @@ -use cryptotools::encode_base64::Base64Encode; use cryptotools::decode_base64::Base64Decode; +use cryptotools::encode_base64::Base64Encode; use cryptotools::encrypt_md5::MD5; fn main() { - // Encode - let encode = Base64Encode::encode("123456789"); - println!("{}", encode); + // Encode + let encode = Base64Encode::encode("123456789"); + println!("{}", encode); - // Decode - let decode = Base64Decode::decode("MTIzNDU2Nzg5"); - println!("{}", decode); + // Decode + let decode = Base64Decode::decode("MTIzNDU2Nzg5"); + println!("{}", decode); - // md5 - let md5 = MD5::encrypt("9999"); - println!("{}", md5); + // md5 + let md5 = MD5::encrypt("9999"); + println!("{}", md5); } diff --git a/src/decode_base64.rs b/src/decode_base64.rs index e253415..1f4cfd5 100644 --- a/src/decode_base64.rs +++ b/src/decode_base64.rs @@ -1,56 +1,84 @@ +/// Provides methods to decode Base64 encoded strings. +/// +/// # Example +/// ``` +/// use cryptotools::decode_base64::Base64Decode; +/// let decoded = Base64Decode::decode("aGVsbG8="); +/// println!("{decoded}"); +/// ``` pub struct Base64Decode; impl Base64Decode { - /// Convert the value of the "value" parameter to base64 decode - pub fn decode(value: &str) -> String { - let base64_chars: Vec = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".chars().collect(); - let mut result = Vec::new(); - let mut bytes = Vec::with_capacity(4); - let mut padding_count = 0; - - ValidationDecodeBase64::count_number_chars(value, &mut padding_count, base64_chars, &mut bytes); - ValidationDecodeBase64::remove_padding(&mut bytes); - ValidationDecodeBase64::decode_bytes(&mut result, &bytes); - ValidationDecodeBase64::remove_padding_from_result(&mut result, padding_count); - - let decode_string = String::from_utf8(result).unwrap(); - return decode_string; - } + /// Decodes a Base64 string slice into a regular string. + /// + /// # Arguments + /// + /// * `value` - The Base64-encoded string slice to decode. + /// + /// # Returns + /// + /// A decoded string. + pub fn decode(value: &str) -> String { + let base64_chars: Vec = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + .chars() + .collect(); + let mut result = Vec::new(); + let mut bytes = Vec::with_capacity(4); + let mut padding_count = 0; + + ValidationDecodeBase64::count_number_chars(value, &mut padding_count, base64_chars, &mut bytes); + ValidationDecodeBase64::remove_padding(&mut bytes); + ValidationDecodeBase64::decode_bytes(&mut result, &bytes); + ValidationDecodeBase64::remove_padding_from_result(&mut result, padding_count); + + let decode_string = String::from_utf8(result).unwrap(); + decode_string + } } +/// Collection of helper methods for validating and processing the Base64 decoding. struct ValidationDecodeBase64; -impl ValidationDecodeBase64 { - fn count_number_chars(data: &str, padding_count: &mut usize, base64_chars: Vec, bytes: &mut Vec) { - for ch in data.chars() { - if ch == '=' { - *padding_count += 1; - } else { - let index = base64_chars.iter().position(|&c| c == ch).unwrap(); - bytes.push(index as u8); - } - } - } - - fn remove_padding(bytes: &mut Vec) { - while bytes.len() % 4 != 0 { - bytes.pop(); - } - } - - fn decode_bytes(result: &mut Vec, bytes: &[u8]) { - for i in (0..bytes.len()).step_by(4) { - let byte1 = (bytes[i] << 2) | (bytes[i + 1] >> 4); - let byte2 = ((bytes[i + 1] & 0x0F) << 4) | (bytes[i + 2] >> 2); - let byte3 = ((bytes[i + 2] & 0x03) << 6) | bytes[i + 3]; - result.push(byte1); - result.push(byte2); - result.push(byte3); - } - } - - fn remove_padding_from_result(result: &mut Vec, padding_count: usize) { - result.truncate(result.len() - padding_count); - } -} \ No newline at end of file +impl ValidationDecodeBase64 { + /// Converts Base64 characters to indexes, tracks padding, and fills byte vector. + fn count_number_chars( + data: &str, + padding_count: &mut usize, + base64_chars: Vec, + bytes: &mut Vec, + ) { + for ch in data.chars() { + if ch == '=' { + *padding_count += 1; + } else { + let index = base64_chars.iter().position(|&c| c == ch).unwrap(); + bytes.push(index as u8); + } + } + } + + /// Removes any extra values so the number of bytes is a multiple of 4. + fn remove_padding(bytes: &mut Vec) { + while bytes.len() % 4 != 0 { + bytes.pop(); + } + } + + /// Decodes byte chunks from Base64 index representation into bytes. + fn decode_bytes(result: &mut Vec, bytes: &[u8]) { + for i in (0..bytes.len()).step_by(4) { + let byte1 = (bytes[i] << 2) | (bytes[i + 1] >> 4); + let byte2 = ((bytes[i + 1] & 0x0F) << 4) | (bytes[i + 2] >> 2); + let byte3 = ((bytes[i + 2] & 0x03) << 6) | bytes[i + 3]; + result.push(byte1); + result.push(byte2); + result.push(byte3); + } + } + + /// Truncates the last bytes according to the padding count to get the final decoded result. + fn remove_padding_from_result(result: &mut Vec, padding_count: usize) { + result.truncate(result.len() - padding_count); + } +} diff --git a/src/encode_base64.rs b/src/encode_base64.rs index 78c63c8..8dfc946 100644 --- a/src/encode_base64.rs +++ b/src/encode_base64.rs @@ -1,59 +1,100 @@ +/// Provides methods to encode strings into Base64 format. +/// +/// # Example +/// ``` +/// use cryptotools::encode_base64::Base64Encode; +/// +/// let encoded = Base64Encode::encode("hello"); +/// println!("{encoded}"); +/// ``` pub struct Base64Encode; impl Base64Encode { - /// Convert the value of the "value" parameter to base64 encode - /// Use: let encode = encode_base64("123456789"); - pub fn encode(value: &str) -> String { - let base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".as_bytes(); - let mut result = String::new(); - let mut index = 0; - - let data_bytes = value.as_bytes(); - - while index < data_bytes.len() { - let mut bytes = [0u8; 3]; - let mut base64_indexes = Vec::new(); - let mut padding_count = 0; - - index = ValidationEncodeBase64::count_number_bytes(index, data_bytes, &mut bytes, &mut padding_count); - ValidationEncodeBase64::calculate_base64_indexes(&bytes, &mut base64_indexes); - ValidationEncodeBase64::append_base64_chars(&mut result, &base64_chars, &base64_indexes, padding_count); - } - - return result; - } + /// Encodes a &str into a Base64 string. + /// + /// # Arguments + /// + /// * `value` - The string slice to encode. + /// + /// # Returns + /// + /// A Base64 encoded string. + pub fn encode(value: &str) -> String { + let base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".as_bytes(); + let mut result = String::new(); + let mut index = 0; + + let data_bytes = value.as_bytes(); + + while index < data_bytes.len() { + let mut bytes = [0u8; 3]; + let mut base64_indexes = Vec::new(); + let mut padding_count = 0; + + index = ValidationEncodeBase64::count_number_bytes( + index, + data_bytes, + &mut bytes, + &mut padding_count, + ); + ValidationEncodeBase64::calculate_base64_indexes(&bytes, &mut base64_indexes); + ValidationEncodeBase64::append_base64_chars( + &mut result, + &base64_chars, + &base64_indexes, + padding_count, + ); + } + + result + } } +/// Collection of helper methods for validating and building the Base64 result. struct ValidationEncodeBase64; impl ValidationEncodeBase64 { - fn count_number_bytes(mut index: usize, data_bytes: &[u8], bytes: &mut [u8; 3], padding_count: &mut usize) -> usize { - for i in 0..3 { - if index < data_bytes.len() { - bytes[i] = data_bytes[index]; - index += 1; - } else { - *padding_count += 1; - } - } - - return index; - } - - fn calculate_base64_indexes(bytes: &[u8; 3], base64_indexes: &mut Vec) { - base64_indexes.push((bytes[0] >> 2) as u8); - base64_indexes.push((((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xF0) >> 4)) as u8); - base64_indexes.push((((bytes[1] & 0x0F) << 2) | ((bytes[2] & 0xC0) >> 6)) as u8); - base64_indexes.push((bytes[2] & 0x3F) as u8); - } - - fn append_base64_chars(result: &mut String, base64_chars: &[u8], base64_indexes: &[u8], padding_count: usize) { - for i in 0..(4 - padding_count) { - result.push(base64_chars[base64_indexes[i] as usize] as char); - } - - for _ in 0..padding_count { - result.push('='); - } - } -} \ No newline at end of file + /// Takes 3 bytes from data_bytes starting at 'index', keeps track of padding, and returns the new index. + fn count_number_bytes( + mut index: usize, + data_bytes: &[u8], + bytes: &mut [u8; 3], + padding_count: &mut usize, + ) -> usize { + for i in 0..3 { + if index < data_bytes.len() { + bytes[i] = data_bytes[index]; + index += 1; + } else { + *padding_count += 1; + } + } + + index + } + + /// Computes the indexes into the Base64 character set for 3 input bytes. + fn calculate_base64_indexes(bytes: &[u8; 3], base64_indexes: &mut Vec) { + base64_indexes.push((bytes[0] >> 2) as u8); + base64_indexes.push((((bytes[0] & 0x03) << 4) | ((bytes[1] & 0xF0) >> 4)) as u8); + base64_indexes.push((((bytes[1] & 0x0F) << 2) | ((bytes[2] & 0xC0) >> 6)) as u8); + base64_indexes.push((bytes[2] & 0x3F) as u8); + } + + /// Appends the appropriate Base64 characters and padding to the result string. + fn append_base64_chars( + result: &mut String, + base64_chars: &[u8], + base64_indexes: &[u8], + padding_count: usize, + ) { + for i in 0..(4 - padding_count) { + result.push(base64_chars[base64_indexes[i] as usize] as char); + } + + for _ in 0..padding_count { + result.push('='); + } + } +} diff --git a/src/encrypt_md5.rs b/src/encrypt_md5.rs index 4d094e9..db2958c 100644 --- a/src/encrypt_md5.rs +++ b/src/encrypt_md5.rs @@ -1,10 +1,26 @@ +/// Provides methods to hash strings using the MD5 algorithm. +/// +/// # Example +/// ``` +/// use cryptotools::encrypt_md5::MD5; +/// let hash = MD5::encrypt("hello"); +/// println!("{hash}"); +/// ``` pub struct MD5; impl MD5 { - /// Function to convert string to md5 - pub fn encrypt(value: &str) -> String { - let digest = md5::compute(value); - let digest_to_string = format!("{:02x?}", digest); - return digest_to_string - } -} \ No newline at end of file + /// Hashes a string slice using MD5 and returns a hexadecimal representation. + /// + /// # Arguments + /// + /// * `value` - The string slice to hash. + /// + /// # Returns + /// + /// The MD5 hash as a hexadecimal string. + pub fn encrypt(value: &str) -> String { + let digest = md5::compute(value); + let digest_to_string = format!("{:02x?}", digest); + digest_to_string + } +} diff --git a/src/lib.rs b/src/lib.rs index ea83dbc..47dd05f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ -pub mod encode_base64; pub mod decode_base64; -pub mod encrypt_md5; \ No newline at end of file +pub mod encode_base64; +pub mod encrypt_md5; diff --git a/tests/decode_base64_tests.rs b/tests/decode_base64_tests.rs index 7e60a5f..bf20dc0 100644 --- a/tests/decode_base64_tests.rs +++ b/tests/decode_base64_tests.rs @@ -1,9 +1,8 @@ #[cfg(test)] - use cryptotools::decode_base64::*; #[test] fn test_decode_base64() { - let decode = Base64Decode::decode("MTIzNDU2Nzg5"); - assert_eq!(decode, "123456789"); -} \ No newline at end of file + let decode = Base64Decode::decode("MTIzNDU2Nzg5"); + assert_eq!(decode, "123456789"); +} diff --git a/tests/encode_base64_tests.rs b/tests/encode_base64_tests.rs index e90ed5d..fbb727c 100644 --- a/tests/encode_base64_tests.rs +++ b/tests/encode_base64_tests.rs @@ -1,9 +1,8 @@ #[cfg(test)] - use cryptotools::encode_base64::*; #[test] fn test_encode_base64() { - let encode = Base64Encode::encode("123456789"); - assert_eq!(encode, "MTIzNDU2Nzg5"); -} \ No newline at end of file + let encode = Base64Encode::encode("123456789"); + assert_eq!(encode, "MTIzNDU2Nzg5"); +} diff --git a/tests/encrypt_md5_tests.rs b/tests/encrypt_md5_tests.rs index 6412123..d4c66e0 100644 --- a/tests/encrypt_md5_tests.rs +++ b/tests/encrypt_md5_tests.rs @@ -1,9 +1,8 @@ #[cfg(test)] - use cryptotools::encrypt_md5::*; #[test] fn test_encrypt_md5() { - let md5 = MD5::encrypt("9999"); - assert_eq!(md5, "fa246d0262c3925617b0c72bb20eeb1d"); -} \ No newline at end of file + let md5 = MD5::encrypt("9999"); + assert_eq!(md5, "fa246d0262c3925617b0c72bb20eeb1d"); +}