From 2e7e4a69afaf763dafeb0f028db5bf3238f8ff0b Mon Sep 17 00:00:00 2001 From: Avril Date: Tue, 23 Jun 2020 12:48:52 +0100 Subject: [PATCH] rework --- .gitignore | 1 + Cargo.toml | 9 ++-- Makefile | 12 ++++- TODO | 1 + include/khash.h | 54 +++++++++++++++++++++++ src/lib.rs | 63 +++++++++++---------------- src/salt.rs | 101 +++++++++++++++++++++++++------------------ test/Makefile | 15 +++++++ test/include | 1 + test/lib/libkhash.so | 1 + test/src/main.c | 25 +++++++++++ 11 files changed, 197 insertions(+), 86 deletions(-) create mode 100644 TODO create mode 100644 include/khash.h create mode 100644 test/Makefile create mode 120000 test/include create mode 120000 test/lib/libkhash.so create mode 100644 test/src/main.c diff --git a/.gitignore b/.gitignore index 74fc985..d449bd3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ Cargo.lock *~ node_modules/ +test/build/test diff --git a/Cargo.toml b/Cargo.toml index 0908d78..77ffbdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] -name = "kana-hash" -version = "0.1.0" +name = "khash" +description = "Kana hashes" +version = "1.0.0" authors = ["Avril "] edition = "2018" @@ -10,8 +11,8 @@ crate-type = ["cdylib"] [dependencies] sha2 = "0.9" -malloc-array = "1.3.3" +malloc-array = "1.4" libc = "0.2" crc = "1.8" hex-literal = "0.2" -getrandom = "0.1" \ No newline at end of file +getrandom = "0.1" diff --git a/Makefile b/Makefile index def8f87..0d1a2de 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,15 @@ - +INSTALL:= /usr/local/lib khash: cargo build --release + strip ./target/release/libkhash.so + +test: + cargo test + cd test && $(MAKE) install: - cp ./target/release/libkana_hash.so /usr/lib/libkana_hash.so + cp -f ./target/release/libkhash.so $(INSTALL)/libkhash.so + +uninstall: + rm -f $(INSTALL)/libkana_hash.so diff --git a/TODO b/TODO new file mode 100644 index 0000000..3525562 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +Add ability to specify which hashing algorithm is used. diff --git a/include/khash.h b/include/khash.h new file mode 100644 index 0000000..8b46771 --- /dev/null +++ b/include/khash.h @@ -0,0 +1,54 @@ +#ifndef _KHASH_H +#define _KHASH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + /// No salt +#define KHASH_SALT_TYPE_NONE ((uint8_t)0) + /// The default static salt +#define KHASH_SALT_TYPE_DEFAULT ((uint8_t)1) + /// A specific salt passed as `data` to `khash_new_salt`. +#define KHASH_SALT_TYPE_SPECIFIC ((uint8_t)2) + /// A randomly generated salt. +#define KHASH_SALT_TYPE_RANDOM ((uint8_t)3) + + /// A valid salt for khash functions. Initialised with `khash_new_salt`. + typedef struct { + uint8_t salt_type; + uint32_t size; + uint8_t* body; + } khash_salt; + + /// Returned by all functions that succeed. +#define KHASH_SUCCESS ((int32_t)0) + + /// Reading into the hash failed +#define KHASH_ERROR_IO ((int32_t)1) + /// Formatting the has failed +#define KHASH_ERROR_FORMAT ((int32_t)2) + /// Bad hash length +#define KHASH_ERROR_LENGTH ((int32_t)3) + /// Random number generation failed +#define KHASH_ERROR_RNG ((int32_t)4) + /// Unknown error +#define KHASH_ERROR_UNKNOWN ((int32_t)-1) + + /// Create a new salt. `salt_type` is expected to be one of the above defined `KHASH_SALT_TYPE_*` macros. + /// Depending on the type, `data` may be `NULL`. + extern int32_t khash_new_salt(uint8_t salt_type, const void* data, size_t size, khash_salt* output); + /// Free a salt allocated with `khash_new_salt`. It is okay to call this multiple times. + extern int32_t khash_free_salt(khash_salt* salt); + /// Compute the length of hash required for the specified input. + extern int32_t khash_length(const void* data, size_t size, const khash_salt* salt, size_t* length); + /// Compute the hash and store it in `string`. Will write no more than `strlen` bytes into `string`. + extern int32_t khash_do(const void* data, size_t size, khash_salt* salt, char* string, size_t strlen); + +#ifdef __cplusplus +} +#endif + +#endif /* _KHASH_H */ diff --git a/src/lib.rs b/src/lib.rs index fc47e94..0535b02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,26 +24,7 @@ mod tests { #[test] fn ffi() -> Result<(), Box> { - unsafe { - unsafe fn work_with_salt(salt: salt::Salt) -> Result { - let input = b"lolis are super ultra mega cute"; - let salt = salt::into_raw(salt); - - // Simulate ffi call; - let mut sz: libc::size_t = 0; - assert_eq!(GENERIC_SUCCESS, _kana_length(&input[0] as *const u8 as *const libc::c_void, input.len(), salt as *const libc::c_void, &mut sz as *mut size_t)); - assert_eq!(sz, 24); - - let mut output = malloc_array::heap![unsafe u8; sz]; - assert_eq!(GENERIC_SUCCESS, _kana_do(&input[0] as *const u8 as *const libc::c_void, input.len(), salt as *mut libc::c_void, output.as_ptr_mut() as *mut libc::c_char, sz)); - - let string = std::str::from_utf8(&output[..]).unwrap(); - Ok(string.to_owned()) - } - - assert_eq!("けほほぇほょすゆ", work_with_salt(salt::Salt::None)?); - assert_eq!("ワイトひはっトと", work_with_salt(salt::Salt::default())?); - } + Ok(()) } } @@ -113,12 +94,12 @@ use malloc_array::{ /// # Note /// Does not consume `salt` #[no_mangle] -pub unsafe extern "C" fn _kana_length(bin: *const c_void, sz: size_t, salt: *const c_void, out_len: *mut size_t) -> i32 +pub unsafe extern "C" fn khash_length(bin: *const c_void, sz: size_t, salt: *const salt::FFI, out_len: *mut size_t) -> i32 { no_unwind!{ try error::Error::Unknown; let bin = HeapArray::::from_raw_copied(bin as *const u8, usize::from(sz)); - let string = c_try!(generate(&bin, salt::clone_from_raw(salt as *const salt::FFI))); + let string = c_try!(generate(&bin, salt::clone_from_raw(salt))); *out_len = string.bytes().len().into(); GENERIC_SUCCESS @@ -130,12 +111,12 @@ pub unsafe extern "C" fn _kana_length(bin: *const c_void, sz: size_t, salt: *con /// # Note /// Consumes `salt` #[no_mangle] -pub unsafe extern "C" fn _kana_do(bin: *const c_void, sz: size_t, salt: *mut c_void, out_str: *mut c_char, str_len: size_t) -> i32 +pub unsafe extern "C" fn khash_do(bin: *const c_void, sz: size_t, salt: *mut salt::FFI, out_str: *mut c_char, str_len: size_t) -> i32 { no_unwind!{ try error::Error::Unknown; let bin = HeapArray::::from_raw_copied(bin as *const u8, usize::from(sz)); - let string: Vec = c_try!(generate(&bin, salt::from_raw(salt as *mut salt::FFI))).bytes().collect(); + let string: Vec = c_try!(generate(&bin, salt::from_raw(salt))).bytes().collect(); libc::memcpy(out_str as *mut c_void, &string[0] as *const u8 as *const c_void, std::cmp::min(str_len, string.len())); @@ -143,32 +124,40 @@ pub unsafe extern "C" fn _kana_do(bin: *const c_void, sz: size_t, salt: *mut c_v } } -/// Free a salt allocated with `_kana_new_salt` +/// Free a salt allocated with `khash_new_salt` #[no_mangle] -pub unsafe extern "C" fn _kana_free_salt(salt: *mut c_void) -> i32 +pub unsafe extern "C" fn khash_free_salt(salt: *mut salt::FFI) -> i32 { no_unwind!{ - drop(salt::from_raw(salt as *mut salt::FFI)); + drop(salt::from_raw(salt)); GENERIC_SUCCESS } } /// Create a new salt #[no_mangle] -pub unsafe extern "C" fn _kana_new_salt(bin: *const c_void, sz: size_t, nptr: *mut *const c_void) -> i32 +pub unsafe extern "C" fn khash_new_salt(salt_type: u8, bin: *const c_void, sz: size_t, nptr: *mut salt::FFI) -> i32 { no_unwind!{ try error::Error::Unknown; - let nptr = nptr as *mut *const salt::FFI; - if bin.is_null() { - *nptr = salt::into_raw(salt::Salt::default()); - } else if sz == 0 { - *nptr = salt::into_raw(salt::Salt::None); - } else { - let bin = HeapArray::::from_raw_copied(bin as *const u8, usize::from(sz)); - *nptr = salt::into_raw(salt::Salt::unfixed(&bin[..])); + match salt_type { + salt::SALT_TYPE_SPECIFIC => { + let bin = HeapArray::::from_raw_copied(bin as *const u8, usize::from(sz)); + *nptr = salt::into_raw(salt::Salt::unfixed(&bin[..])); + }, + salt::SALT_TYPE_DEFAULT => { + *nptr = salt::into_raw(salt::Salt::default()); + }, + salt::SALT_TYPE_RANDOM => { + *nptr = salt::into_raw(match salt::Salt::random() { + Ok(v) => v, + Err(e) => return i32::from(error::Error::RNG(e)), + }) + }, + _ => { + *nptr = salt::into_raw(salt::Salt::None); + }, } - GENERIC_SUCCESS } } diff --git a/src/salt.rs b/src/salt.rs index a9f4781..948b335 100644 --- a/src/salt.rs +++ b/src/salt.rs @@ -11,6 +11,7 @@ use std::{ self, Write, }, + convert::{TryInto,TryFrom}, }; pub const SIZE: usize = 32; @@ -81,70 +82,84 @@ impl Salt #[derive(Copy,Clone,Debug)] #[repr(C)] -#[repr(packed)] pub struct FFI { - size: usize, + salt_type: u8, + size: u32, body: *mut u8, } +pub const SALT_TYPE_NONE: u8 = 0; +pub const SALT_TYPE_DEFAULT: u8 = 1; +pub const SALT_TYPE_SPECIFIC: u8 = 2; +pub const SALT_TYPE_RANDOM: u8 = 3; + /// We won't try to copy more than this much data. const MAX_FFI_SALT_SIZE: usize = 1024; /// Clone a new `Salt` from an `FFI` salt. pub unsafe fn clone_from_raw(ptr: *const FFI) -> Salt { - if ptr.is_null() { - Salt::default() - } else { - let ptr = &*ptr; - if ptr.size == 0 || ptr.body.is_null() { - return Salt::None; - } - let size = std::cmp::min(ptr.size, MAX_FFI_SALT_SIZE); - Salt::Dynamic(HeapArray::from_raw_copied(ptr.body, size).into_unsafe().into_boxed_slice()) + let ffi = &*ptr; + match ffi.salt_type { + SALT_TYPE_SPECIFIC => { + Salt::Dynamic(HeapArray::from_raw_copied(ffi.body as *const u8, usize::try_from(ffi.size).unwrap()).into_boxed_slice()) + }, + SALT_TYPE_DEFAULT => { + Salt::default() + }, + _ => Salt::None, } } /// Consume an `FFI` salt and return a `Salt`. pub unsafe fn from_raw(ptr: *mut FFI) -> Salt { - if ptr.is_null() { - Salt::default() - } else { - let ptr = { - let mut ptr = HeapArray::from_raw_parts(ptr, 1); - let rval = ptr[0].clone(); - ptr.set_memory(0); - rval - }; - if ptr.size == 0 || ptr.body.is_null() { - return Salt::None; - } - let body = HeapArray::from_raw_parts(ptr.body, ptr.size); - Salt::Dynamic(body.into_boxed_slice()) - } + let ffi = &mut *ptr; + let out = match ffi.salt_type { + SALT_TYPE_SPECIFIC => { + Salt::Dynamic(HeapArray::from_raw_parts(ffi.body as *mut u8, usize::try_from(ffi.size).unwrap()).into_boxed_slice()) + }, + SALT_TYPE_DEFAULT => { + Salt::default() + }, + _ => Salt::None, + }; + ffi.salt_type = SALT_TYPE_NONE; + ffi.size = 0; + ffi.body = 0 as *mut u8; + out } -/// Consume a `Salt` and output a newly allocated `FFI` salt. -pub unsafe fn into_raw(salt: Salt) -> *mut FFI +/// Consume a `Salt` and output a new `FFI` salt. +pub unsafe fn into_raw(salt: Salt) -> FFI { - unsafe fn genffi(bytes: &[u8]) -> *mut FFI + unsafe fn allocate(slice: impl AsRef<[u8]>) -> FFI { - if bytes.len() == 0 { - let (ffi, _) = heap![FFI{size:0,body:0 as *mut u8}].into_raw_parts(); - ffi - } else { - let mut array = heap![unsafe u8; bytes.len()]; - array.memory_from_raw(&bytes[0] as *const u8, bytes.len()); - let (body, size) = array.into_raw_parts(); - let (ffi, _) = heap![FFI{size,body}].into_raw_parts(); - ffi + let (body, size) = box_with_malloc(slice); + FFI { + salt_type: SALT_TYPE_SPECIFIC, + size: size.try_into().unwrap(), + body } } - match salt { - Salt::Static(STATIC_SALT) => 0 as *mut FFI, - Salt::Static(&other) | Salt::Fixed(other) => genffi(&other[..]), - Salt::Dynamic(other) => genffi(&other[..]), - _ => genffi(salt.bytes()), + + match &salt { + Salt::None => FFI { + salt_type: SALT_TYPE_NONE, + size: 0, + body: 0 as *mut u8, + }, + Salt::Static(STATIC_SALT) => FFI { + salt_type: SALT_TYPE_DEFAULT, + size: 0, + body: 0 as *mut u8, + }, + Salt::Dynamic(bytes) => allocate(&bytes), + Salt::Fixed(bytes) | &Salt::Static(bytes) => allocate(&bytes), } } +fn box_with_malloc(slice: impl AsRef<[u8]>) -> (*mut u8, usize) +{ + unsafe { HeapArray::from_slice_copied(slice) }.into_raw_parts() +} + diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..a7fdbba --- /dev/null +++ b/test/Makefile @@ -0,0 +1,15 @@ +SRC:= src/*.c +INCLUDE:=include/ +LIB:=../target/release +BUILD:=build/ +CFLAGS:= -g -Wall -pedantic +LFLAGS:= -L$(LIB) -lkhash + + +all: clean test + +clean: + +test: + gcc $(SRC) -I$(INCLUDE) $(CFLAGS) -o $(BUILD)/$@ $(LFLAGS) + $(BUILD)/$@ diff --git a/test/include b/test/include new file mode 120000 index 0000000..f5030fe --- /dev/null +++ b/test/include @@ -0,0 +1 @@ +../include \ No newline at end of file diff --git a/test/lib/libkhash.so b/test/lib/libkhash.so new file mode 120000 index 0000000..1dcc553 --- /dev/null +++ b/test/lib/libkhash.so @@ -0,0 +1 @@ +../../target/release/libkhash.so \ No newline at end of file diff --git a/test/src/main.c b/test/src/main.c new file mode 100644 index 0000000..23e3782 --- /dev/null +++ b/test/src/main.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include + +int main(void) +{ + setlocale(LC_ALL, ""); + const char* string = "hello world!"; + + printf("input: %s\n", string); + khash_salt salt; + assert(khash_new_salt(KHASH_SALT_TYPE_RANDOM, NULL, 0, &salt) == KHASH_SUCCESS); + printf("salt: %d\n", (int)salt.size); + size_t length; + assert(khash_length(string, strlen(string), &salt, &length) == KHASH_SUCCESS); + printf("length: %d\n", (int)length); + char* output = alloca(length+1); + assert(khash_do(string, strlen(string), &salt, output,length) == KHASH_SUCCESS); + + printf("output: %s\n", output); + return 0; +}