diff --git a/README.org b/README.org index 9de2590..ddc60e2 100644 --- a/README.org +++ b/README.org @@ -1,15 +1,207 @@ * libkhash - kana-hash Kana mnemonic hashes +** Installation + The dynamic library is built with ~Cargo~ and ~Rust~, and the cli example program is built with ~gcc~. + +*** Build and install + The default build configuration builds both the dynamic library and the cli example program, and copies them to =/usr/lib/libkhash.so= and =/usr/bin/kana-hash= respectively. + + #+BEGIN_SRC shell + $ make && sudo make install + #+END_SRC + + The install path can be changed by editing the ~INSTALL~ and ~INSTALL-BIN~ paths in the [[file:./Makefile][Makefile]]. + +*** Uninstall + To remove installed binaries, run: + + #+BEGIN_SRC shell + $ sudo make uninstall + #+END_SRC + +*** Other build configurations + The Makefile contains some other build directives. + +**** Native code optimisations + By default =libkhash= builds the shared library with native architecture optimisations enabled. + If you are intending to move the binary to another architecture, this might not be desireable. + To build without such optimisations, run: + + #+BEGIN_SRC shell + $ make khash-nonative + #+END_SRC + +**** Tests + To build and run all tests, run: + + #+BEGIN_SRC shell + $ make test + #+END_SRC + +**** Building the CLI + The default =make= directive builds both the library and the CLI example program. + To build just the CLI example program, run: + + #+BEGIN_SRC shell + $ cd cli && make + #+END_SRC + ** TODO Rust interface + ** C header - Documented in =./include/khash.h= + A header file is provided for C programs wanting to use the khash interface. + Documented more fully in [[file:./include/khash.h][./include/khash.h]]. + All symbols defined here begin with either =KHASH_= (for macros) or =khash_=. + +*** Example + To create a context +#+BEGIN_SRC c + #include + + const char* input_salt = "salt!"; + const char* input_data = "some data to hash". + khash_context ctx; + assert(khash_new_context(KHASH_ALGO_SHA256, KHASH_SALT_TYPE_SPECIFIC, input_salt, strlen(input_salt), &ctx) == KHASH_SUCCESS, "khash_new_context() failed."); +#+END_SRC + Find the buffer length we need. + +#+BEGIN_SRC c + size_t length; + assert(khash_length(&ctx, input_data, strlen(input_data), &length) == KHASH_SUCCESS, "khash_length() failed."); +#+END_SRC + Create the buffer and hash, then print the result to ~stdout~. +#+BEGIN_SRC c + char* buffer = alloca(length+1); + assert(khash_do(&ctx, input_data, strlen(input_data), buffer, length) == KHASH_SUCCESS, "khash_do() failed."); + buffer[length] = 0; // Ensure we have a NUL terminator. + + setlocale(LC_ALL, ""); //Ensure we can print UTF-8. + printf("Kana hash: %s\n", buffer); +#+END_SRC + +*** Definitions + +**** Macros + All macros defined are for options. + They cannot be combied as flags. + The =KHASH_ALGO_= prefixed ones are for use as the /algo/ parameter in the ~khash_new_context()~ function. + The =KHASH_SALT_TYPE_= prefixed ones are for use as the /salt_type/ parameter. + The =KHASH_ERROR_= prefixed ones each indicate an error code returned by all of the functions. + | Name | Description | + |-------------------------------+--------------------------------------------------------------------------------------------| + | ~KHASH_ALGO_DEFAULT~ | The default algorithm used by the library (truncated SHA256) | + | ~KHASH_ALGO_CRC32~ | CRC32 checksum algorithm | + | ~KHASH_ALGO_CRC64~ | CRC64 checksum algorithm | + | ~KHASH_ALGO_SHA256~ | SHA256 hash algorithm | + | ~KHSAH_ALGO_SHA256_TRUNCATED~ | SHA256 truncated to 64-bits | + | ~KHASH_SALT_TYPE_NONE~ | No salt | + | ~KHASH_SALT_TYPE_DEFAULT~ | The default static salt used by the library | + | ~KHASH_SALT_TYPE_SPECIFIC~ | A provided salt, as the /data/ and of the /size/ parameter passed to ~khash_new_context()~ | + | ~KHASH_SALT_TYPE_RANDOM~ | A randomly generated salt | + | ~KHASH_SUCCESS~ | The code returned by all of the functions when the operation was successful | + | ~KHASH_ERROR_IO~ | There was an IO error | + | ~KHASH_ERROR_FORMAT~ | The was a text formatting related error | + | ~KHASH_ERROR_LENGTH~ | There was a hash length mismatch | + | ~KHASH_ERROR_RNG~ | The random number generator failed | + | ~KHASH_ERROR_UNKNOWN~ | There was an unknown error or the stack attempted to unwind past the FFI boundary. | + +**** Types + There are 2 exported structs, although you will rarely need to access their members directly. + | Name | Field | Description | + |-----------------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------| + | ~khash_salt~ | | A salt allocated into a context by ~khash_new_context()~ and released by ~khash_free_context()~. You shouldn't mess with its field directly. | + | | /salt_type/ | The type of the salt. | + | | /size/ | The size of the salt. | + | | /body/ | A pointer to the body of the salt. (The memory allocated here is not guaranteed to be of the provided /size/.) | + |-----------------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------| + | ~khash_context~ | | A context for the =khash_= functions. Allocated by ~khash_new_context()~. You can modify its fields if you want. | + | | /algo/ | The algorithm for this context. | + | | /flags/ | Placeholder for potential flags added in the future. Currently unused. | + | | /salt/ | The allocated salt. You shouldn't directly mess with this field. | +**** Functions + All defined functions return either ~KHASH_SUCCESS~ or one of the =KHASH_ERROR_= values [[Macros][above]]. + | Name | Parameters | Description | + |-----------------------+------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + | ~khash_new_context~ | /algo/, /salt_type/, /data/, /size/, /output/ | Creates a new context for use with other =libkhash= functions. /algo/ is expected to be one of the =KHASH_ALGO_= macros listed [[Macros][above.]] Likewise /salt_type/ is expected to be one of the =KHASH_SALT_TYPE_= macros. /data/ can be ~NULL~ unless /salt_type/ is set to ~KHASH_SALT_TYPE_SPECIFIC~, in which exactly /size/ bytes are read from /data/. /output/ is expected to be a valid pointer to a currently unused `khash_context` structure. | + | ~khash_free_context~ | /ctx/ | Free a context allocated with ~khash_new_context()~. /ctx/ is expected to be a valid pointer to a currently allocated context. | + | ~khash_clone_context~ | /src/, /dst/ | Clone a context allocated with ~khash_new_context()~ into another. The newly allocated /dst/ must be properly released (with ~khash_free_context()~ or ~khash_do()~) as well as the source. /src/ is expected to be a valid pointer to an allocated context, and /dst/ is expected to be a valid pointer to an unallocated context. | + | ~khash_length~ | /ctx/, /data/, /size/, /length/ | Compute the length required to hold the output string for ~khash_do()~ for a given input. Will read exactly /size/ bytes from /data/ and compute the value into what is pointed to by /length/ (which is expected to be a valid pointer to a type of ~size_t~.) The resulting length does not include a =NUL= terminator for the string. | + | ~khash_do~ | /ctx/, /data/, /size/, /output/, /output_size/ | Compute the kana-hash of /size/ bytes from /data/ and store no more than /output_size/ of the the result into the string pointed to by /output/. Each pointer is expected to be valid. This function frees the supplied /ctx/ after the hash has been computed, and thus /ctx/ is no longer valid afterwards. | ** Node FFI bindings - NPM package in =./node= + NPM package in [[file:./node/index.js][./node]] + +*** Installation (npm) + Follow the [[installation]] section first. + + #+BEGIN_SRC shell + $ npm install --save /path/to/repo/node + #+END_SRC + +*** Examples + +**** Import the package + #+BEGIN_SRC javascript + const hash = require('kana-hash'); + #+END_SRC + +**** Create a context + Create the context by specifying an algorithm identifier, and an optional salt. + If provided, the salt must be of type `Salt`. + #+BEGIN_SRC javascript + const ctx = new hash.Kana(hash.Kana.ALGO_DEFAULT, new hash.Salt("optional salt~")); + #+END_SRC + +**** Create a hash + The `once()` function consumes the context and outputs a hash string. + #+BEGIN_SRC javascript + const output = ctx.once("input string"); + #+END_SRC + +***** Creating a hash without consuming + If you want to reuse the context, use the `hash()` function. + #+BEGIN_SRC javascript + const output = ctx.hash("input string"); + #+END_SRC + +***** Freeing the context + The context must be release after use if you have not called `once()`. + #+BEGIN_SRC javascript + ctx.finish(); + #+END_SRC + +***** Cloning an existing context + The new context must also be freed with either `once()` or `finish()`. + #+BEGIN_SRC javascript + const new_ctx = ctx.clone(); + #+END_SRC + +*** Interface documentation + The 2 exported objects are ~Kana~ and ~Salt~. + ~Kana~'s constructor expects between 0 and 2 arguments. + + The first is either an [[Defined constants][algorithm definition]] or empty, if empty ~Kana~ uses the default algorithm (truncated SHA256). + + The second is either an instance of ~Salt~ or empty, if empty ~Kana~ uses the default library salt. + ~Salt~'s constructor expects 0 or 1 argument. + + Either a string to use as the specific salt or empty, if empty there is no salt. + +**** Defined constants + | Name | Type | Description | + |------------------------------+----------------------+--------------------------------------------------------------------------| + | ~Kana.ALGO_DEFAULT~ | Algorithm definition | The default algorithm specified by the library (set to sha256 truncated) | + | ~Kana.ALGO_CRC32~ | Algorithm definition | CRC32 checksum algorithm | + | ~Kana.ALGO_CRC64~ | Algorithm definition | CRC64 checksum algorithm | + | ~Kana.ALGO_SHA256~ | Algorithm definition | SHA256 hashing algorithm | + | ~Kana.ALGO_SHA256_TRUNCATED~ | Algorithm definition | Truncated SHA256 algorithm, to 64-bits | + | ~Salt.None~ | Salt | No salt | + | ~Salt.Random~ | Salt | A cryptographically secure random salt | + | ~Salt.Default~ | Salt | The library's default static salt | + +** Notes + The strings generated by this library are meant to be pretty, not secure. It is not a secure way of representing a hash as many collisions are possible. -** TODO Documentation +*** TODO Digest algorithm ** License GPL'd with <3 diff --git a/include/khash.h b/include/khash.h index 39b1a5d..5ed2782 100644 --- a/include/khash.h +++ b/include/khash.h @@ -84,4 +84,6 @@ extern "C" { } #endif +#undef _deprecated + #endif /* _KHASH_H */ diff --git a/node/index.js b/node/index.js index 5e1ec4e..6990a95 100644 --- a/node/index.js +++ b/node/index.js @@ -1,11 +1,5 @@ -const ffi = require('ffi'); -const struct = require('ref-struct'); -const ref = require('ref'); - const Kana = require('./kana'); const Salt = require('./salt'); -//console.log(new Kana(Kana.CRC64, new Salt("hello lolis")).once("hello worldノチそぬとね")); - exports.Kana = Kana; exports.Salt = Salt; diff --git a/node/kana.js b/node/kana.js index 4006503..6adfd87 100644 --- a/node/kana.js +++ b/node/kana.js @@ -69,44 +69,63 @@ const get_salt_type = (salt) => { else return Kana.SALT_DEFAULT; }; +/// Create a new kana-hash context. +/// This context must be disposed of correctly either by calling `finish()` or `once()`. +/// `algo` is expected to be one of the `Kana.ALGO_*` constants, or `undefined`. +/// `salt` is expected to be either an object of `Salt` or `undefined`. function Kana(algo, salt) { - const stype = get_salt_type(salt); - const fbuffer = salt ? salt.buffer || null : null; - this.ctx = ctx_create(algo || 0, stype, fbuffer, fbuffer ? fbuffer.length : 0); + if(algo && algo.ctx) { + this.ctx = algo.ctx; + } else { + const stype = get_salt_type(salt); + const fbuffer = salt ? salt.buffer || null : null; + this.ctx = ctx_create(algo || 0, stype, fbuffer, fbuffer ? fbuffer.length : 0); + } } const K = Kana.prototype; +/// Free the associated context. +/// The object is no longer valid after this call. K.finish = function() { ctx_free(this.ctx); + this.ctx = null; }; +/// Compute the kana-hash for `string` and then free the associated context. K.once = function(string) { let len = khash_length(this.ctx, string); return khash_do(this.ctx, string, len); }; +/// Compute the kana-hash for `string`. +K.hash = function(string) { + const ctx = ctx_clone(this.ctx); + let len = khash_length(ctx, string); + return khash_do(ctx, string, len); +}; + +/// Clone this kana-hash context. +K.clone = function() { + const ctx = ctx_clone(this.ctx); + return new Kana({ctx: ctx}); +}; + +/// The default algorithm used. (sha256 truncated.) Kana.ALGO_DEFAULT = 0; +/// CRC32 algorithm. Kana.ALGO_CRC32 = 1; +/// CRC64 algorithm. Kana.ALGO_CRC64 = 2; +/// SHA256 algorithm. Kana.ALGO_SHA256 = 3; +/// SHA256 truncated to 64-bits. Kana.ALGO_SHA256_TRUNCATED = 4; +// You don't need to reference these directly, use the `Salt` module instead. Kana.SALT_NONE = 0; Kana.SALT_DEFAULT = 1; Kana.SALT_SPECIFIC = 2; Kana.SALT_RANDOM = 3; - module.exports = Kana; -/* - const Kana = require('./kana'); - - let buffer= ref.alloc('long'); - let sz = ref.alloc('long'); - console.log(libhkana._kana_length(buffer, 2, null, sz)); - console.log(sz.deref()); - - let output = new Buffer(sz); - console.log(libhkana._kana_do(buffer, 2, null, output, sz)); - console.log(ref.readCString(output, 0));*/ diff --git a/node/package.json b/node/package.json index e1e1e84..0a88abd 100644 --- a/node/package.json +++ b/node/package.json @@ -1,6 +1,6 @@ { "name": "kana-hash", - "version": "1.1.0", + "version": "1.2.0", "description": "Kana hashes", "main": "index.js", "scripts": { diff --git a/node/salt.js b/node/salt.js index 9313e44..3a14bfb 100644 --- a/node/salt.js +++ b/node/salt.js @@ -1,5 +1,6 @@ const ref = require('ref'); +/// Create a new salt from the string `buffer`. function Salt(buffer, tag) { this.tag = tag || '__SPECIFIC'; @@ -7,11 +8,15 @@ function Salt(buffer, tag) this.buffer = ref.allocCString(buffer); } else { this.buffer = null; + this.tag = '__NONE'; } } +/// No salt. Salt.None = new Salt(null, '__NONE'); +/// A randomly generated salt. Salt.Random = new Salt(null, '__RANDOM'); +/// The static default salt. Salt.Default = new Salt(null, '__DEFAULT'); module.exports = Salt;