Added language bindings for Rust.
This commit is contained in:
parent
2505493534
commit
d7f741c28c
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,5 +3,6 @@
|
||||
/**/bin
|
||||
/**/lib
|
||||
/**/obj
|
||||
/**/target
|
||||
/.vs
|
||||
/out
|
||||
|
101
binding/rust/Cargo.lock
generated
Normal file
101
binding/rust/Cargo.lock
generated
Normal file
@ -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"
|
9
binding/rust/Cargo.toml
Normal file
9
binding/rust/Cargo.toml
Normal file
@ -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"
|
11
binding/rust/build.rs
Normal file
11
binding/rust/build.rs
Normal file
@ -0,0 +1,11 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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");
|
||||
}
|
31
binding/rust/examples/slunkcrypt_example.rs
Normal file
31
binding/rust/examples/slunkcrypt_example.rs
Normal file
@ -0,0 +1,31 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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));
|
||||
}
|
185
binding/rust/src/context.rs
Normal file
185
binding/rust/src/context.rs
Normal file
@ -0,0 +1,185 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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<NonZeroUsize>) -> Result<Self, 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 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<NonZeroUsize>) -> 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<NonZeroUsize>) -> Result<Self, SlunkCryptError> {
|
||||
Self::new(passwd, nonce, libslunkcrypt::SLUNKCRYPT_DECRYPT, threads)
|
||||
}
|
||||
|
||||
pub fn reset_encrypt(&mut self, passwd: &SlunkCryptPasswd) -> Result<u64, 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 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;
|
||||
}
|
||||
}
|
||||
}
|
61
binding/rust/src/error.rs
Normal file
61
binding/rust/src/error.rs
Normal file
@ -0,0 +1,61 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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
|
||||
}
|
||||
}
|
50
binding/rust/src/ffi/libslunkcrypt.rs
Normal file
50
binding/rust/src/ffi/libslunkcrypt.rs
Normal file
@ -0,0 +1,50 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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);
|
||||
|
||||
}
|
13
binding/rust/src/ffi/mod.rs
Normal file
13
binding/rust/src/ffi/mod.rs
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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;
|
38
binding/rust/src/functions.rs
Normal file
38
binding/rust/src/functions.rs
Normal file
@ -0,0 +1,38 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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<u64, SlunkCryptError> {
|
||||
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()
|
||||
}
|
||||
}
|
43
binding/rust/src/lib.rs
Normal file
43
binding/rust/src/lib.rs
Normal file
@ -0,0 +1,43 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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;
|
36
binding/rust/src/passwd.rs
Normal file
36
binding/rust/src/passwd.rs
Normal file
@ -0,0 +1,36 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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<u8>`
|
||||
Vec(Vec<u8>)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
188
binding/rust/tests/context_test.rs
Normal file
188
binding/rust/tests/context_test.rs
Normal file
@ -0,0 +1,188 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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<F>(func: F) where F: Fn() {
|
||||
let loops = env::var("TEST_LOOPS").map_or(DEFAULT_TEST_LOOPS, |value| value.parse::<usize>().unwrap());
|
||||
for _ in 0..loops {
|
||||
func();
|
||||
}
|
||||
}
|
37
binding/rust/tests/functions_test.rs
Normal file
37
binding/rust/tests/functions_test.rs
Normal file
@ -0,0 +1,37 @@
|
||||
/******************************************************************************/
|
||||
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||
/* 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<F>(func: F) where F: Fn() {
|
||||
let loops = env::var("TEST_LOOPS").map_or(DEFAULT_TEST_LOOPS, |value| value.parse::<usize>().unwrap());
|
||||
for _ in 0..loops {
|
||||
func();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user