diff --git a/.gitignore b/.gitignore index b9b46a8..4b6a6da 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ /**/bin /**/lib /**/obj +/**/target /.vs /out diff --git a/binding/rust/Cargo.lock b/binding/rust/Cargo.lock new file mode 100644 index 0000000..ccd657b --- /dev/null +++ b/binding/rust/Cargo.lock @@ -0,0 +1,101 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "memx" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93022fcc0eab2dc9dd134e362592e06f0b3c0aa549ae91d8b3e539e56c2a0687" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "slunkcrypt-rs" +version = "1.0.0" +dependencies = [ + "hex", + "memx", + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/binding/rust/Cargo.toml b/binding/rust/Cargo.toml new file mode 100644 index 0000000..ed61f2f --- /dev/null +++ b/binding/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "slunkcrypt-rs" +version = "1.0.0" +edition = "2021" + +[dev-dependencies] +hex = "0.4.3" +memx = "0.1.32" +rand = "0.8.5" diff --git a/binding/rust/build.rs b/binding/rust/build.rs new file mode 100644 index 0000000..2ca0c35 --- /dev/null +++ b/binding/rust/build.rs @@ -0,0 +1,11 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +use std::env; + +fn main() { + println!(r"cargo:rustc-link-search={}/../../libslunkcrypt/lib", env::var("CARGO_MANIFEST_DIR").unwrap()); + println!(r"cargo:rustc-link-lib=slunkcrypt-1"); +} diff --git a/binding/rust/examples/slunkcrypt_example.rs b/binding/rust/examples/slunkcrypt_example.rs new file mode 100644 index 0000000..838fac7 --- /dev/null +++ b/binding/rust/examples/slunkcrypt_example.rs @@ -0,0 +1,31 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +use slunkcrypt_rs::{SlunkCrypt, SlunkCryptPasswd}; +use hex; +use rand::{thread_rng, RngCore}; +use std::mem; + +fn main() { + // Create passphrase from string + const PASSPHRASE: SlunkCryptPasswd = SlunkCryptPasswd::Str("OrpheanBeholderScryDoubt"); + + // Fill buffer with random data (plaintext) + let mut buffer = [ 0u8; 64 ]; + thread_rng().fill_bytes(&mut buffer); + println!("Plaintext: {}", hex::encode(&buffer)); + + // Encrypt the data in-place + let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + context_enc.inplace(&mut buffer).expect("Failed to encrypt data!"); + mem::drop(context_enc); + println!("Encrypted: {}", hex::encode(&buffer)); + + // Decrypt the data in-place + let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!"); + context_dec.inplace(&mut buffer).expect("Failed to decrypt data!"); + mem::drop(context_dec); + println!("Decrypted: {}", hex::encode(&buffer)); +} diff --git a/binding/rust/src/context.rs b/binding/rust/src/context.rs new file mode 100644 index 0000000..ef9ef53 --- /dev/null +++ b/binding/rust/src/context.rs @@ -0,0 +1,185 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +use std::num::NonZeroUsize; +use std::ptr; + +use crate::error::{SlunkCryptError, SlunkCryptErrorKind}; +use crate::functions::generate_nonce; +use crate::ffi::libslunkcrypt::{self, slunkcrypt_t, slunkparam_t, SLUNKCRYPT_SUCCESS, SLUNKCRYPT_NULL}; +use crate::passwd::SlunkCryptPasswd; + +/// SlunkCrypt context for encryption or decryption. +/// +/// This struct wraps the "native" SlunkCrypt context. It provides functions for allocating a new encryption or decryption context as well as functions for processing, i.e. encrypting or decrypting, the next chunk of data within a specific `SlunkCrypt` context. +/// +/// The wrapped "native" SlunkCrypt context is freed automatically when this struct is dropped. +/// +/// # Thread safety +/// +/// Separate `SlunkCrypt` contexts can safely be accessed from concurrent threads *without* the need for any kind if synchronization, provided that each context is only accessed by the *single* thread that has allocated the respective context. +/// +/// # Example +/// ``` +/// use slunkcrypt_rs::{SlunkCrypt, SlunkCryptPasswd}; +/// use hex; +/// use rand::{thread_rng, RngCore}; +/// use std::mem; +/// +/// fn main() { +/// // Create passphrase from string +/// const PASSPHRASE: SlunkCryptPasswd = SlunkCryptPasswd::Str("OrpheanBeholderScryDoubt"); +/// +/// // Fill buffer with random data (plaintext) +/// let mut buffer = [ 0u8; 64 ]; +/// thread_rng().fill_bytes(&mut buffer); +/// println!("Plaintext: {}", hex::encode(&buffer)); +/// +/// // Encrypt the data in-place +/// let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).unwrap(); +/// context_enc.inplace(&mut buffer).unwrap(); +/// mem::drop(context_enc); +/// println!("Encrypted: {}", hex::encode(&buffer)); +/// +/// // Decrypt the data in-place +/// let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).unwrap(); +/// context_dec.inplace(&mut buffer).unwrap(); +/// mem::drop(context_dec); +/// println!("Decrypted: {}", hex::encode(&buffer)); +/// } +/// ``` +#[derive(Debug)] +pub struct SlunkCrypt { + context: slunkcrypt_t +} + +impl SlunkCrypt { + /// Create a new SlunkCrypt context and initialize it for the specified mode with the given password and the given nonce. + /// + /// If successful, returns the new context; otherwise returns an [error code](SlunkCryptError). + fn new(passwd: &SlunkCryptPasswd, nonce: u64, mode: i32, threads: Option) -> Result { + if passwd.len() < libslunkcrypt::SLUNKCRYPT_PWDLEN_MIN { + return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too short!")); + } + if passwd.len() > libslunkcrypt::SLUNKCRYPT_PWDLEN_MAX { + return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too long!")); + } + + let param = slunkparam_t { + version: libslunkcrypt::SLUNKCRYPT_PARAM_VERSION, + thread_count: threads.map_or(usize::default(), NonZeroUsize::get), + legacy_compat: i32::default(), + debug_logging: i32::default() + }; + + let context: slunkcrypt_t = unsafe { + libslunkcrypt::slunkcrypt_alloc_ext(nonce, passwd.as_bytes().as_ptr() as *const u8, passwd.len(), mode, ¶m) + }; + + if context == SLUNKCRYPT_NULL { + Err(SlunkCryptError::new(SlunkCryptErrorKind::Failure, "failed to allocate slunkcrypt encryption context!")) + } else { + Ok(Self { context }) + } + } + + /// Create a new SlunkCrypt context and initialize it for *encryption* mode with the given password and a fresh random nonce. + /// + /// If successful, returns the new context and the generated nonce; otherwise returns an [error code](SlunkCryptError). + pub fn init_encrypt(passwd: &SlunkCryptPasswd, threads: Option) -> Result<(Self, u64), SlunkCryptError> { + let nonce = generate_nonce()?; + match Self::new(passwd, nonce, libslunkcrypt::SLUNKCRYPT_ENCRYPT, threads) { + Ok(context) => Ok((context, nonce)), + Err(error) => Err(error) + } + } + + /// Create a new SlunkCrypt context and initialize it for *decryption* mode with the given password and the given nonce. + /// + /// If successful, returns the new context; otherwise returns an [error code](SlunkCryptError). + pub fn init_decrypt(passwd: &SlunkCryptPasswd, nonce: u64, threads: Option) -> Result { + Self::new(passwd, nonce, libslunkcrypt::SLUNKCRYPT_DECRYPT, threads) + } + + pub fn reset_encrypt(&mut self, passwd: &SlunkCryptPasswd) -> Result { + if passwd.len() < libslunkcrypt::SLUNKCRYPT_PWDLEN_MIN { + return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too short!")); + } + if passwd.len() > libslunkcrypt::SLUNKCRYPT_PWDLEN_MAX { + return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too long!")); + } + + let nonce = generate_nonce()?; + + let retval = unsafe { + libslunkcrypt::slunkcrypt_reset(self.context, nonce, passwd.as_bytes().as_ptr(), passwd.len(), libslunkcrypt::SLUNKCRYPT_ENCRYPT) + }; + match retval { + SLUNKCRYPT_SUCCESS => Ok(nonce), + _ => Err(SlunkCryptError::from_retval(retval, "failed to reset encryption context!")) + } + } + + pub fn reset_decrypt(&mut self, passwd: &SlunkCryptPasswd, nonce: u64) -> Result<(), SlunkCryptError> { + if passwd.len() < libslunkcrypt::SLUNKCRYPT_PWDLEN_MIN { + return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too short!")); + } + if passwd.len() > libslunkcrypt::SLUNKCRYPT_PWDLEN_MAX { + return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "Passphrase is too long!")); + } + + let retval = unsafe { + libslunkcrypt::slunkcrypt_reset(self.context, nonce, passwd.as_bytes().as_ptr(), passwd.len(), libslunkcrypt::SLUNKCRYPT_DECRYPT) + }; + match retval { + SLUNKCRYPT_SUCCESS => Ok(()), + _ => Err(SlunkCryptError::from_retval(retval, "failed to reset decryption context!")) + } + } + + /// Process the next chunk of data. The given input data is encrypted or decrypted into the output buffer. + /// + /// Does **not** modify the data in the input buffer. + /// + /// If successful, returns nothing; otherwise returns an [error code](SlunkCryptError). + pub fn process(&mut self, data_in: &[u8], data_out: &mut[u8]) -> Result<(), SlunkCryptError> { + if data_out.len() < data_in.len() { + return Err(SlunkCryptError::new(SlunkCryptErrorKind::Invalid, "The output buffer is too small!")); + } + let retval = unsafe { + libslunkcrypt::slunkcrypt_process(self.context, data_in.as_ptr(), data_out.as_mut_ptr(), data_in.len()) + }; + match retval { + SLUNKCRYPT_SUCCESS => Ok(()), + _ => Err(SlunkCryptError::from_retval(retval, "failed to process data!")) + } + } + + /// Process the next chunk of data. The given data is encrypted or decrypted *in-place*. + /// + /// If successful, returns nothing; otherwise returns an [error code](SlunkCryptError). + pub fn inplace(&mut self, data: &mut[u8]) -> Result<(), SlunkCryptError> { + let retval = unsafe { + libslunkcrypt::slunkcrypt_inplace(self.context, data.as_mut_ptr(), data.len()) + }; + match retval { + SLUNKCRYPT_SUCCESS => Ok(()), + _ => Err(SlunkCryptError::from_retval(retval, "failed to process data!")) + } + } +} + +impl Drop for SlunkCrypt { + /// Free the SlunkCrypt context and release all of its associated ressources. + fn drop(&mut self) { + if self.context != SLUNKCRYPT_NULL { + unsafe { + libslunkcrypt::slunkcrypt_free(self.context); + } + let x : *const std::ffi::c_void = ptr::null(); + self.context = x as slunkcrypt_t; + } + } +} diff --git a/binding/rust/src/error.rs b/binding/rust/src/error.rs new file mode 100644 index 0000000..1bedc19 --- /dev/null +++ b/binding/rust/src/error.rs @@ -0,0 +1,61 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +use std::os::raw::c_int; +use crate::ffi::libslunkcrypt::{SLUNKCRYPT_ABORTED, SLUNKCRYPT_FAILURE, SLUNKCRYPT_SUCCESS}; + +/// SlunkCrypt error type. +/// +/// This struct provides information about the SlunkCrypt error that has occurred. +#[derive(Debug)] +pub struct SlunkCryptError { + kind: SlunkCryptErrorKind, + description: &'static str +} + +/// SlunkCrypt error kind enumeration. +/// +/// This enumeration defines the *kind* of the SlunkCrypt error that has occurred. +#[derive(Clone, Copy, Debug)] +pub enum SlunkCryptErrorKind { + /// The operation succeeded. + Success, + /// The operation was **not** executed because of invalid arguments. + Invalid, + /// The operation has failed. + Failure, + /// The operation was aborted by the user. + Aborted, + /// Unknown error. This is **not** supposed to happen. + Unknown +} + +impl SlunkCryptError { + /// Creates a new error from the given kind and description. + pub(crate) fn new(kind: SlunkCryptErrorKind, description: &'static str) -> Self { + Self { kind, description } + } + + /// Creates a new error from the given error code and description. + pub(crate) fn from_retval(retval: c_int, description: &'static str) -> Self { + let kind = match retval { + SLUNKCRYPT_SUCCESS => SlunkCryptErrorKind::Success, + SLUNKCRYPT_ABORTED => SlunkCryptErrorKind::Aborted, + SLUNKCRYPT_FAILURE => SlunkCryptErrorKind::Failure, + _ => SlunkCryptErrorKind::Unknown + }; + Self::new(kind, description) + } + + /// Returns the [*kind*](SlunkCryptErrorKind) of the SlunkCrypt error that has occurred. + pub fn kind(&self) -> SlunkCryptErrorKind { + self.kind + } + + /// Returns a detailed textual description of the SlunkCrypt error that has occurred. + pub fn description(&self) -> &'static str { + &self.description + } +} diff --git a/binding/rust/src/ffi/libslunkcrypt.rs b/binding/rust/src/ffi/libslunkcrypt.rs new file mode 100644 index 0000000..f28d0d4 --- /dev/null +++ b/binding/rust/src/ffi/libslunkcrypt.rs @@ -0,0 +1,50 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +pub type slunkcrypt_t = usize; + +const NULL_POINTER: *const ::std::ffi::c_void = ::std::ptr::null(); +pub const SLUNKCRYPT_NULL: slunkcrypt_t = unsafe { ::std::mem::transmute(NULL_POINTER) }; + +pub const SLUNKCRYPT_FALSE: ::std::os::raw::c_int = 0; +pub const SLUNKCRYPT_TRUE: ::std::os::raw::c_int = 1; +pub const SLUNKCRYPT_ENCRYPT: ::std::os::raw::c_int = 0; +pub const SLUNKCRYPT_DECRYPT: ::std::os::raw::c_int = 1; +pub const SLUNKCRYPT_SUCCESS: ::std::os::raw::c_int = 0; +pub const SLUNKCRYPT_FAILURE: ::std::os::raw::c_int = -1; +pub const SLUNKCRYPT_ABORTED: ::std::os::raw::c_int = -2; + +pub const SLUNKCRYPT_PWDLEN_MIN: usize = 8; +pub const SLUNKCRYPT_PWDLEN_MAX: usize = 256; + +pub const SLUNKCRYPT_PARAM_VERSION: u16 = 2; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct slunkparam_t { + pub version: u16, + pub thread_count: usize, + pub legacy_compat: ::std::os::raw::c_int, + pub debug_logging: ::std::os::raw::c_int, +} + +extern "C" { + pub static SLUNKCRYPT_VERSION_MAJOR: u16; + pub static SLUNKCRYPT_VERSION_MINOR: u16; + pub static SLUNKCRYPT_VERSION_PATCH: u16; + pub static SLUNKCRYPT_BUILD: *const ::std::os::raw::c_char; + pub static SLUNKCRYPT_HAVE_THREADS: ::std::os::raw::c_int; + pub static mut g_slunkcrypt_abort_flag: ::std::os::raw::c_int; + + pub fn slunkcrypt_generate_nonce(nonce: *mut u64) -> ::std::os::raw::c_int; + pub fn slunkcrypt_alloc_ext(nonce: u64, passwd: *const u8, passwd_len: usize, mode: ::std::os::raw::c_int, param: *const slunkparam_t) -> slunkcrypt_t; + pub fn slunkcrypt_reset(context: slunkcrypt_t, nonce: u64, passwd: *const u8, passwd_len: usize, mode: ::std::os::raw::c_int) -> ::std::os::raw::c_int; + pub fn slunkcrypt_free(context: slunkcrypt_t); + pub fn slunkcrypt_process(context: slunkcrypt_t, input: *const u8, output: *mut u8, length: usize) -> ::std::os::raw::c_int; + pub fn slunkcrypt_inplace(context: slunkcrypt_t, buffer: *mut u8, length: usize) -> ::std::os::raw::c_int; + pub fn slunkcrypt_random_bytes(buffer: *mut u8, length: usize) -> usize; + pub fn slunkcrypt_bzero(buffer: *mut ::std::os::raw::c_void, length: usize); + +} diff --git a/binding/rust/src/ffi/mod.rs b/binding/rust/src/ffi/mod.rs new file mode 100644 index 0000000..4a5cb74 --- /dev/null +++ b/binding/rust/src/ffi/mod.rs @@ -0,0 +1,13 @@ + +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +#![allow(dead_code)] +#![allow(unused)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +pub mod libslunkcrypt; diff --git a/binding/rust/src/functions.rs b/binding/rust/src/functions.rs new file mode 100644 index 0000000..d4f53ea --- /dev/null +++ b/binding/rust/src/functions.rs @@ -0,0 +1,38 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +use std::ffi::CStr; +use std::str; + +use crate::error::SlunkCryptError; +use crate::ffi::libslunkcrypt::{self, SLUNKCRYPT_SUCCESS}; + +/// Generate a fresh random nonce, using a Cryptographically secure pseudorandom number generator. +pub fn generate_nonce() -> Result { + let mut nonce: u64 = u64::default(); + let retval = unsafe { + libslunkcrypt::slunkcrypt_generate_nonce(&mut nonce) + }; + match retval { + SLUNKCRYPT_SUCCESS => Ok(nonce), + _ => Err(SlunkCryptError::from_retval(retval, "failed to generate nonce!")) + } +} + +/// Returns the version of the native SlunkCrypt library. +pub fn get_version() -> (u16,u16,u16) { + unsafe {( + libslunkcrypt::SLUNKCRYPT_VERSION_MAJOR, + libslunkcrypt::SLUNKCRYPT_VERSION_MINOR, + libslunkcrypt::SLUNKCRYPT_VERSION_PATCH + )} +} + +// Returns the version of the native SlunkCrypt library. +pub fn get_build() -> &'static str { + unsafe { + CStr::from_ptr(libslunkcrypt::SLUNKCRYPT_BUILD).to_str().unwrap() + } +} diff --git a/binding/rust/src/lib.rs b/binding/rust/src/lib.rs new file mode 100644 index 0000000..25a382e --- /dev/null +++ b/binding/rust/src/lib.rs @@ -0,0 +1,43 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +//! # SlunkCrypt Rust Wrapper +//! This crate exposes the functionality of the [**SlunkCrypt**](https://gitlab.com/lord_mulder/slunkcrypt) library to to Rust developers. +//! +//! Please see the [`SlunkCrypt`] struct for details! +//! +//! ## Build Instructions +//! +//! First the "native" SlunkCrypt library needs to be built, if not done yet: +//! ```sh +//! $ cd /home/JohnnyBeSlunk/dev/SlunkCrypt +//! $ make -B SHARED=1 +//! ``` +//! +//! Now build the SlunkCrypt Rust wrapper crate: +//! ```sh +//! $ cd binding/rust +//! $ cargo build --release +//! ``` +//! +//! ## Unit Tests +//! +//! In order to run the unit tests, please type: +//! +//! ```sh +//! $ export LD_LIBRARY_PATH=/home/JohnnyBeSlunk/dev/SlunkCryptlibslunkcrypt/lib +//! $cargo test --release +//! ``` + +mod context; +mod error; +mod ffi; +mod functions; +mod passwd; + +pub use context::SlunkCrypt; +pub use error::{SlunkCryptError, SlunkCryptErrorKind}; +pub use functions::{generate_nonce, get_version, get_build}; +pub use passwd::SlunkCryptPasswd; diff --git a/binding/rust/src/passwd.rs b/binding/rust/src/passwd.rs new file mode 100644 index 0000000..2594727 --- /dev/null +++ b/binding/rust/src/passwd.rs @@ -0,0 +1,36 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +/// Passphrase to be used with [SlunkCrypt](crate::SlunkCrypt) encryption or decryption. +pub enum SlunkCryptPasswd<'a> { + /// A passphrase constructed from a `&str` + Str(&'a str), + /// A passphrase constructed from a `String` + String(String), + /// A passphrase constructed from `&[u8]` + Bytes(&'a[u8]), + /// A passphrase constructed from `Vec` + Vec(Vec) +} + +impl<'a> SlunkCryptPasswd<'a> { + pub fn len(&self) -> usize { + match self { + Self::Str(str) => str.len(), + Self::String(str) => str.len(), + Self::Bytes(bin) => bin.len(), + Self::Vec(bin) => bin.len() + } + } + + pub fn as_bytes(&'a self) -> &'a[u8] { + match self { + Self::Str(str) => str.as_bytes(), + Self::String(str) => str.as_bytes(), + Self::Bytes(bin) => bin, + Self::Vec(bin) => bin.as_slice() + } + } +} diff --git a/binding/rust/tests/context_test.rs b/binding/rust/tests/context_test.rs new file mode 100644 index 0000000..2c9c197 --- /dev/null +++ b/binding/rust/tests/context_test.rs @@ -0,0 +1,188 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +use slunkcrypt_rs::{SlunkCryptPasswd, SlunkCrypt}; +use hex; +use memx; +use rand::{rngs::OsRng, RngCore}; +use std::{env, mem, cmp::Ordering}; + +const DEFAULT_TEST_LOOPS: usize = 8; +const PASSPHRASE: SlunkCryptPasswd = SlunkCryptPasswd::Str("eUGAwxOm0QwiVnDx"); +const PASSPHRASE_BAD: SlunkCryptPasswd = SlunkCryptPasswd::Str("eUGAwxOm1QwiVnDx"); + +#[test] +fn test_create_instance() { + run_test(|| { + let (context, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + println!("EncryptContext: {:?}", context); + let context = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!"); + println!("DecryptContext: {:?}", context); + }); +} + +#[test] +fn test_process() { + run_test(|| { + let (mut orig, mut encr, mut decr) = ([ 0u8; 5003 ], [ 0u8; 5003 ], [ 0u8; 5003 ]); + OsRng.fill_bytes(&mut orig); + println!("Plaintext: {}", hex::encode(&orig)); + + let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + context_enc.process(&orig, &mut encr).expect("Failed to encrypt data!"); + mem::drop(context_enc); + + println!("Encrypted: {}", hex::encode(&encr)); + assert!(memx::memcmp(&orig, &encr) != Ordering::Equal); + + let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!"); + context_dec.process(&encr, &mut decr).expect("Failed to encrypt data!"); + mem::drop(context_dec); + + println!("Decrypted: {}", hex::encode(&decr)); + }); +} + +#[test] +fn test_inplace() { + run_test(|| { + let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]); + OsRng.fill_bytes(&mut data); + memx::memcpy(&mut orig, &data).expect("Failed to copy original data!"); + + println!("Plaintext: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + + let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + context_enc.inplace(&mut data).expect("Failed to encrypt data!"); + mem::drop(context_enc); + + println!("Encrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + + let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!"); + context_dec.inplace(&mut data).expect("Failed to decrypt data!"); + mem::drop(context_dec); + + println!("Decrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + }); +} + +#[test] +fn test_reset() { + run_test(|| { + let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]); + OsRng.fill_bytes(&mut data); + memx::memcpy(&mut orig, &data).expect("Failed to copy original data!"); + + println!("Plaintext: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + + let (mut context, _nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + context.inplace(&mut data).expect("Failed to encrypt data!"); + + println!("Encrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + + memx::memcpy(&mut data, &orig).expect("Failed to copy original data!"); + println!("Plaintext: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + + let nonce = context.reset_encrypt(&PASSPHRASE).expect("Failed to reset encryption context!"); + context.inplace(&mut data).expect("Failed to encrypt data!"); + + println!("Encrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + + context.reset_decrypt(&PASSPHRASE, nonce).expect("Failed to reset decryption context!"); + context.inplace(&mut data).expect("Failed to decrypt data!"); + mem::drop(context); + + println!("Decrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + }); +} + +#[test] +fn test_fail_bad_nonce() { + run_test(|| { + let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]); + OsRng.fill_bytes(&mut data); + memx::memcpy(&mut orig, &data).expect("Failed to copy original data!"); + + println!("Plaintext: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + + let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + context_enc.inplace(&mut data).expect("Failed to encrypt data!"); + mem::drop(context_enc); + + println!("Encrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + + let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce + 1, None).expect("Failed to create decryption context!"); + context_dec.inplace(&mut data).expect("Failed to decrypt data!"); + mem::drop(context_dec); + + println!("Decrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + }); +} + +#[test] +fn test_fail_bad_passwd() { + run_test(|| { + let (mut data, mut orig) = ([ 0u8; 5003 ], [ 0u8; 5003 ]); + OsRng.fill_bytes(&mut data); + memx::memcpy(&mut orig, &data).expect("Failed to copy original data!"); + + println!("Plaintext: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + + let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + context_enc.inplace(&mut data).expect("Failed to encrypt data!"); + mem::drop(context_enc); + + println!("Encrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + + let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE_BAD, nonce, None).expect("Failed to create decryption context!"); + context_dec.inplace(&mut data).expect("Failed to decrypt data!"); + mem::drop(context_dec); + + println!("Decrypted: {}", hex::encode(&data)); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + }); +} + +#[test] +fn test_stress() { + let (mut data, mut orig) = ([ 0u8; u16::MAX as usize ], [ 0u8; u16::MAX as usize ]); + + let (mut context_enc, nonce) = SlunkCrypt::init_encrypt(&PASSPHRASE, None).expect("Failed to create encryption context!"); + let mut context_dec = SlunkCrypt::init_decrypt(&PASSPHRASE, nonce, None).expect("Failed to create decryption context!"); + for i in 0..u16::MAX { + OsRng.fill_bytes(&mut data); + memx::memcpy(&mut orig, &data).expect("Failed to copy original data!"); + + context_enc.inplace(&mut data).expect("Failed to encrypt data!"); + assert!(memx::memcmp(&data, &orig) != Ordering::Equal); + + context_dec.inplace(&mut data).expect("Failed to decrypt data!"); + assert!(memx::memcmp(&data, &orig) == Ordering::Equal); + + if i % 499 == 0 { + println!("{:.1}%", (i as f64 / u16::MAX as f64) * 100.0) + } + } +} + +fn run_test(func: F) where F: Fn() { + let loops = env::var("TEST_LOOPS").map_or(DEFAULT_TEST_LOOPS, |value| value.parse::().unwrap()); + for _ in 0..loops { + func(); + } +} diff --git a/binding/rust/tests/functions_test.rs b/binding/rust/tests/functions_test.rs new file mode 100644 index 0000000..23ce427 --- /dev/null +++ b/binding/rust/tests/functions_test.rs @@ -0,0 +1,37 @@ +/******************************************************************************/ +/* SlunkCrypt, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +use std::env; +use slunkcrypt_rs::{get_version, get_build, generate_nonce}; + +const DEFAULT_TEST_LOOPS: usize = 8; + +#[test] +fn test_get_version() { + let (major, minor, patch) = get_version(); + assert!(major > 0); + println!("Version: {}.{}.{}", major, minor, patch); +} + +#[test] +fn test_get_build() { + let build = get_build(); + println!("Build: \"{}\"", build); +} + +#[test] +fn test_init_generate_nonce() { + run_test(|| { + let nonce = generate_nonce().expect("Failed to generate nonce!"); + println!("Nonce: {:16X}", nonce); + }); +} + +fn run_test(func: F) where F: Fn() { + let loops = env::var("TEST_LOOPS").map_or(DEFAULT_TEST_LOOPS, |value| value.parse::().unwrap()); + for _ in 0..loops { + func(); + } +}