From b7c8c64733c1bbf5b50db60409def13d9198a3dc Mon Sep 17 00:00:00 2001 From: Avril Date: Thu, 24 Jun 2021 02:31:35 +0100 Subject: [PATCH] cookie config alowing backing stream ownership transfer to be optional --- include/cc20.h | 12 ++++++-- src/ffi.rs | 14 ++++++--- src/ffi/cookie.rs | 30 ++++++++++++++++--- test.c | 73 ++++++++++++++++++++++++++++++++++++++++------- test.sh | 2 +- 5 files changed, 108 insertions(+), 23 deletions(-) diff --git a/include/cc20.h b/include/cc20.h index 9904259..ec38702 100644 --- a/include/cc20.h +++ b/include/cc20.h @@ -34,6 +34,11 @@ typedef struct cc20_metadata { enum cc20_mode mode; } cc20_meta_t; +struct cc20_wrap_cfg { + // default: false (0) + int keep_alive; +}; + typedef struct cc20_sink cc20_sink_t; // Functions // @@ -48,7 +53,8 @@ cc20_result_t cc20_gen_meta(FILE* file, enum cc20_mode mode, struct cc20_metadata _cc20_OUT output); -cc20_result_t cc20_gen_sink(const struct cc20_metadata* meta, cc20_sink_t* _cc20_OUT output); +cc20_result_t cc20_gen_sink(const struct cc20_metadata* meta, + cc20_sink_t* _cc20_OUT output); cc20_result_t cc20_gen_sink_full(FILE* file, @@ -69,8 +75,8 @@ cc20_result_t cc20_gen(const struct cc20_metadata* meta, cc20_result_t cc20_close_sink(cc20_sink_t* sink, struct cc20_metadata* restrict meta); -FILE* cc20_wrap_sink(cc20_sink_t* sink); - cc20_result_t cc20_write(const void* ptr, size_t * restrict bytes, cc20_sink_t* restrict sink); +FILE* cc20_wrap_sink(cc20_sink_t* sink, const struct cc20_wrap_cfg* cfg); + #endif /* _CC20_H */ diff --git a/src/ffi.rs b/src/ffi.rs index 02f821f..9d9dd1c 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -32,6 +32,7 @@ pub struct CSink { sink: Sink, + cookie_settings: cookie::Config, //last_err: () //TODO: how to implement this? } @@ -123,6 +124,7 @@ pub use error::*; CMode::Encrypt => Sink::encrypt(meta.clone(), meta.key, meta.iv).map_err(|_| CErr::SslError).unwrap(), CMode::Decrypt => Sink::decrypt(meta.clone(), meta.key, meta.iv).map_err(|_| CErr::SslError).unwrap(), }, + cookie_settings: Default::default(), }; *output = interop::give(sink); CErr::Success @@ -147,7 +149,7 @@ pub use error::*; { let mut sink: *mut CSink = ptr::null_mut(); errchk!(cc20_gen_sink(meta, &mut sink as *mut *mut CSink)); - *output = cc20_wrap_sink(sink); + *output = cc20_wrap_sink(sink, ptr::null()); CErr::Success } @@ -157,7 +159,7 @@ pub use error::*; // No need to `no_unwind` this, nothing here can panic. let mut csink: *mut CSink = ptr::null_mut(); errchk!(cc20_gen_sink_full(file, key, iv, mode, &mut csink as *mut *mut CSink)); - *output = cc20_wrap_sink(csink); + *output = cc20_wrap_sink(csink, ptr::null()); CErr::Success } /// Closes and frees the wrapper `sink`, and writes inner metadata struct to `meta`, if `file` is non-null. @@ -172,12 +174,16 @@ pub use error::*; }).unwrap_or(CErr::Panic) } -/// Convert a `Sink` into a `FILE*`. -#[no_mangle] pub unsafe extern "C" fn cc20_wrap_sink(sink: *mut CSink) -> *mut libc::FILE +/// Convert a `Sink` into a `FILE*` with specific config (if not `NULL`). +#[no_mangle] pub unsafe extern "C" fn cc20_wrap_sink(sink: *mut CSink, config: *const cookie::Config) -> *mut libc::FILE { if sink.is_null() { return ptr::null_mut(); } + if !config.is_null() { + let sink = &mut *sink; + sink.cookie_settings = *config; + } cookie::create(sink) } diff --git a/src/ffi/cookie.rs b/src/ffi/cookie.rs index d5c8c7a..f8c5b6f 100644 --- a/src/ffi/cookie.rs +++ b/src/ffi/cookie.rs @@ -15,7 +15,25 @@ use libc::{ FILE, }; -//TODO: Remove `wrapper.c`, implement it in Rust here +/// Configuration for the `FILE*` wrapper +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] +#[repr(C)] +pub struct Config +{ + pub keep_alive: c_int, +} + +impl Default for Config +{ + #[inline] + fn default() -> Self + { + Self { + keep_alive: 0, + } + } +} + type cookie_read_function_t = Option ssize_t>; type cookie_write_function_t = Option ssize_t>; @@ -98,9 +116,13 @@ extern "C" fn close(cookie: *mut c_void) -> c_int } else { cookie as *mut CSink }; - let sink = unsafe { interop::take(sink) }; - drop(sink); - 0 + let CSink { sink, cookie_settings } = unsafe { interop::take(sink) }; + let mut meta = sink.into_inner(); + if cookie_settings.keep_alive == 0 && !meta.backing.is_null() { + unsafe { (libc::fclose(meta.backing), meta.backing = ptr::null_mut()).0 } + } else { + 0 + } } #[inline(always)] pub unsafe fn create(raw_sink: *mut CSink) -> *mut FILE diff --git a/test.c b/test.c index 20f9138..a5598d4 100644 --- a/test.c +++ b/test.c @@ -8,23 +8,74 @@ const char write_string[] = "Hello world?"; +FILE* wrap_stream(FILE* wrap, enum cc20_mode mode, const cc20_key_t* k, const cc20_iv_t* i, cc20_meta_t *restrict meta) +{ + cc20_meta_t _meta = {0}; + if(!meta) meta = &_meta; + + if(CC20_OK(cc20_gen_meta(wrap, k, i, mode, meta))) + { + cc20_sink_t* sink; + if(CC20_OK(cc20_gen_sink(meta, &sink))) + return cc20_wrap_sink(sink, (struct cc20_wrap_cfg[1]) {{ .keep_alive = 1 }} ); + else perror("cc20_gen_sink()"); + } else perror("cc20_gen_meta()"); + exit(-1); +} + +FILE* wrap_file(const char* filename, enum cc20_mode mode, cc20_meta_t* restrict meta) +{ + cc20_meta_t _meta = {0}; + if(!meta) meta = &_meta; + + FILE* wrap = fopen(filename, "w+b"); + if(!wrap) { + perror("fopen()"); + exit(-1); + } + return wrap_stream(wrap, mode, NULL, NULL, meta); +/* + if(CC20_OK(cc20_gen_meta(wrap, NULL, NULL, mode, meta)) + && CC20_OK(cc20_gen(meta, &wrap))) return wrap; + perror("cc20_gen()"); + exit(-1);*/ +} + int main(int argc, char** argv) { + ((void)argc); + if(!argv[1]) return 1; + cc20_meta_t meta; - cc20_result_t res; - FILE* output = argv[1] ? fopen(argv[1], "wb") : stdout; - if(!output) { perror("failed to open output"); return -1; } + FILE* output = wrap_file(argv[1], CC20_ENCRYPT, &meta); + size_t wsz; + printf("written %lu bytes\n", (wsz=fwrite(write_string, 1, strlen(write_string), output))); + + fclose(output); + + FILE* input = meta.backing; + long sz = ftell(input); + + printf(" -> backing stream tell: %ld\n", sz); + if(sz<=0) return (perror("ftell()"), -1); + else if(wsz != (size_t)sz) return (fprintf(stderr, "incorrect ftell(): (expected %lu, got %ld)\n", wsz, sz), -2); - TRY(cc20_gen_meta(output, NULL, NULL, CC20_ENCRYPT, &meta)); - TRY(cc20_gen(&meta, &output)); + if(fseek(input, 0L, SEEK_SET)!=0) return (perror("fseek()"), -3); + + unsigned char encbuf[wsz]; + if(fread(encbuf, 1, wsz, input)!=wsz) return (perror("fread()"), -4); - printf("written %lu bytes\n", fwrite(write_string, 1, strlen(write_string), output)); + printf("decrypted: '"); { + /* owning stdout */ + output = wrap_stream(stdout, CC20_DECRYPT, (const cc20_key_t*) &meta.key, (const cc20_iv_t*) &meta.iv, NULL); + wsz = fwrite(encbuf, 1, wsz, output); + fclose(output); + /* releases stdout */ + printf("'\n"); } + printf("written %lu bytes\n", wsz); - //fprintf(output, "Hello world!"); + fclose(input); - res = CC20_ERR_NONE; - fail: - if(output) fclose(output); - return (int)res; + return 0; } diff --git a/test.sh b/test.sh index 130e3b4..cebf150 100755 --- a/test.sh +++ b/test.sh @@ -1,6 +1,6 @@ #!/bin/bash -gcc test.c -Iinclude -Wall --std=gnu11 -pedantic -Wextra -O3 -o test-ffi -l:target/release/libchacha20stream.a -lssl -lcrypto -lpthread -ldl || exit +gcc test.c -Iinclude -Wall --std=gnu11 -pedantic -Wextra -Og -g -o test-ffi -l:target/debug/libchacha20stream.a -lssl -lcrypto -lpthread -ldl || exit valgrind ./test-ffi test-ffi-output hexview test-ffi-output rm -f test-ffi{,-output}