start FFI function implementations

ffi
Avril 3 years ago
parent d9dd56faf0
commit 90d3c2615f
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -10,8 +10,17 @@ edition = "2018"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["rlib", "cdylib", "staticlib"]
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "unwind"
[features]
default = ["smallvec"]
default = ["smallvec", "ffi"]
# Enable async version with tokio v2.0 AsyncRead/AsyncWrite.
async = ["tokio", "pin-project"]
@ -19,9 +28,13 @@ async = ["tokio", "pin-project"]
# Explicitly clear in-memory buffers with `explicit_bzero()` instead of normal `bzero()`.
explicit_clear = []
# Add C interface bindings
ffi = ["libc"]
[dependencies]
base64 = "0.13"
getrandom = "0.2"
libc = {version = "0.2.97", optional = true}
openssl = "0.10"
pin-project = {version = "1.0.6", optional = true}
serde = {version = "1.0", features = ["derive"], optional = true}

@ -0,0 +1,115 @@
//! Raw C interface bindings
//!
//! Low level bindings to create in-memory translators for the cipher stream API.
//! Intended to be linked to the C wrapper object `wrapper.c`.
use super::*;
use std::ptr;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)]
#[repr(C)]
struct CKey([u8; key::KEY_SIZE]);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)]
#[repr(C)]
struct CIv([u8; key::IV_SIZE]);
/// Non-encrypted wrapper
#[derive(Debug)]
#[repr(C)]
struct CPassthrough
{
backing: *mut libc::FILE,
key: key::Key,
iv: key::IV,
mode: CMode
}
/// A sink wrapper of `CPassthrough`.
//TODO: Create a Custom Stream in `wrapper.c` that allows creating a FILE* object with `fopencookie()` from this.
#[derive(Debug)]
#[repr(C)]
struct CSink
{
sink: Sink<CPassthrough>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
#[repr(C)]
enum CMode
{
Encrypt,
Decrypt,
}
mod interop;
mod error;
use error::*;
#[no_mangle] extern "C" fn cc20_gen_meta(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode, output: *mut CPassthrough) -> i32
{
no_unwind!({
if file.is_null() {
return CErr::InvalidFile;
}
let key = nullchk!(<- key);
let iv = nullchk!(<- iv);
let write = CPassthrough {
backing: file,
key: key::Key::from_bytes(key.0),
iv: key::IV::from_bytes(iv.0),
mode,
};
unsafe {
nullchk!(output);
output.write(write);
}
CErr::Success
}).unwrap_or(CErr::Panic).into()
}
/// Create an encrypting `Sink` over a `FILE*`.
#[no_mangle] extern "C" fn cc20_gen_sink(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode) -> *mut CSink
{
let meta = {
// No need to `no_unwind` this, `cc20_gen_meta` already does it, and nothing else here can panic.
let mut meta: std::mem::MaybeUninit<CPassthrough> = std::mem::MaybeUninit::uninit();
match cc20_gen_meta(file, key, iv, mode, &mut meta as *mut _ as *mut CPassthrough) {
0 => unsafe { meta.assume_init() },
_ => return ptr::null_mut(),
}
};
no_unwind!({
//TODO: Create CSink from `meta` (`CPassthrough`).
let sink = CSink {
sink: match meta.mode {
CMode::Encrypt => Sink::encrypt(meta, meta.key, meta.iv),
CMode::Decrypt => Sink::decrypt(meta, meta.key, meta.iv),
},
};
interop::give(sink)
}).unwrap_or(ptr::null_mut())
}
/// Create a wrapper `FILE*` that acts as a `Sink` when written to.
#[no_mangle] extern "C" fn cc20_wrap(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode) -> *mut libc::FILE
{
// No need to `no_unwind` this, nothing here can panic.
let csink = cc20_gen_sink(file, key, iv, mode);
if csink.is_null() {
return ptr::null_mut();
}
cc20_wrap_sink(csink)
}
/// Closes the wrapper `sink`, and writes inner `FILE*` pointer to `file`, if `file` is non-null.
#[no_mangle] extern "C" fn cc20_close_sink(sink: *mut CSink, file: *mut *mut libc::FILE)
{
todo!()
}
/// Convert a `Sink` into a `FILE*`.
#[no_mangle] extern "C" fn cc20_wrap_sink(sink: *mut CSink) -> *mut libc::FILE
{
todo!("Create a Custom Stream in `wrapper.c` that allows creating a FILE* object from `sink`.")
}
//TODO: `impl io::Write for CPassthrough`...

@ -0,0 +1,67 @@
//! FFI errors
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
#[repr(C)]
pub enum CErr
{
Success = 0,
InvalidFile,
/// Unexpected null pointer
NullPointer,
Panic = -1,
}
impl CErr
{
#[inline] pub fn is_error(&self) -> bool
{
*self != Self::Success
}
#[inline] pub fn is_success(&self) -> bool
{
*self == Self::Success
}
}
impl Default for CErr
{
#[inline]
fn default() -> Self
{
Self::Success
}
}
impl From<CErr> for i32
{
fn from(from: CErr) -> Self
{
from as i32
}
}
/// Null check a pointer. If it's not null, dereference it.
#[macro_export] macro_rules! nullchk {
(<- $ptr:expr) => {
{
let ptr = $ptr;
if ptr.is_null() {
return From::from(CErr::NullPointer);
} else {
unsafe {
*ptr
}
}
}
};
($ptr:expr) => {
{
if $ptr.is_null() {
return From::from(CErr::NullPointer);
}
()
}
}
}

@ -0,0 +1,25 @@
use super::*;
#[macro_export] macro_rules! no_unwind {
($expr:expr) => {
::std::panic::catch_unwind(move || $expr).ok()
};
}
/// Expose an opaque pointer to FFI
#[inline] pub fn give<T>(val: T) -> *mut T
{
Box::into_raw(Box::new(val))
}
/// Take a value back from an opaque FFI pointer
///
/// # Panics
/// If the pointer is `null`.
#[inline] pub unsafe fn take<T>(val: *mut T) -> T
{
if val.is_null() {
panic!("null value in opaque take");
}
*Box::from_raw(val)
}

@ -64,3 +64,5 @@ pub use key::{
};
pub use cha::keygen;
#[cfg(feature="ffi")] pub mod ffi;

Loading…
Cancel
Save