From 4ed44555f3cc0642c3091a215d01bb82a6377cab Mon Sep 17 00:00:00 2001 From: Avril Date: Thu, 25 Jun 2020 11:08:55 +0100 Subject: [PATCH] added single(); update README --- README.org | 63 ++++++++++++++++++++++++++++++++++------------------ node/kana.js | 16 +++++++++++++ node/test.js | 5 +++++ 3 files changed, 62 insertions(+), 22 deletions(-) create mode 100644 node/test.js diff --git a/README.org b/README.org index c5bd36b..171968e 100644 --- a/README.org +++ b/README.org @@ -73,20 +73,30 @@ 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. + Find the buffer length we need and allocate a buffer. #+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. + 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); + setlocale(LC_ALL, ""); //Ensure we can print UTF-8. + printf("Kana hash: %s\n", buffer); + #+END_SRC + + Alternatively, we can allocate the max length needed instead of calculating it. + #+BEGIN_SRC c + size_t length; + assert(khash_max_length(KHASH_ALGO_SHA256, strlen(input_data), &length) == KHASH_SUCCESS, "khash_max_length() failed."); + char* buffer = alloca(length+1); + memset(buffer, 0, length+1); //Ensure NUL terminators. #+END_SRC *** Definitions @@ -187,6 +197,14 @@ const new_ctx = ctx.clone(); #+END_SRC +**** Alternatively + To create a hash in one line you can do one of the following. + + #+BEGIN_SRC javascript + const hash1 = new Kana(Kana.ALGO_DEFAULT, new Salt("salt~")).once("input string~"); //Allocates the exact space required for the output string. + const hash2 = Kana.single(Kana.ALGO_DEFAULT, new Salt("salt~")).once("input string~"); //Allocates the max space required for the output string, instead of the exact. Might be faster. + #+END_SRC + *** Interface documentation The 2 exported objects are ~Kana~ and ~Salt~. ~Kana~'s constructor expects between 0 and 2 arguments. @@ -194,6 +212,7 @@ + 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. + ~Kana~ also has a static function ~single(algo, salt, input)~ which automatically creates a context, computes the hash, frees the context, and then returns the computed hash as a JavaScript string. **** Defined constants | Name | Type | Description | @@ -212,24 +231,24 @@ *** Digest algorithm The kana algorithm is a 16-bit block digest that works as follows: - - The most and least significant 8 bits are each seperated into /Stage 0/ and /Stage 1/ each operating on the first and second byte respectively. - - Stage 0: - 1. The byte is sign tested (bitwise ~AND~ =0x80=), store this as a boolean in /sign0/. - 2. The valid first character range is looked up using the result of the sign test (either 0 or 1), store the range in /range/, and the slice ~KANA~ taken from the range in /kana/. - 3. The first index is calculated as the unsigned first byte modulo the size (exclusive) of /range/. Store this as /index0/. - 4. The swap table is checked to see if /index0/ has an entry. Then each following step is checked in order: - + If the swap entry exists and the first byte bitwise ~AND~ =0x2= is not 0, set the first character of the output to the value found in the swap table. - + If the swap entry exists and the first byte bitwise ~AND~ =0x8= is not 0 and the index has an entry in the 2nd swap table, set the first character of the output to the value found in the 2nd swap table. - + In any other case, set the first character of the output to the value found in the /kana/ slice at the /index/. - - Stage 1: - 1. Compute a sub table for /index/ plus the start of /range/ using the ranges defined in ~KANA_SUB_VALID_FOR~ and store it in /sub/. If there is no sub table possible, skip to step 3. - 2. If there is an entry in /sub/ for the index of the 2nd byte modulo the size of ~KANA_SUB~, set the second output character to be that character. - 3. If there was no value set from the sub table, the 2nd output character becomes the first output character from inputting the 2nd byte back through /Stage 0/ as the first byte. - - Concatenate both characters and move to the next 16-bit block. + - The most and least significant 8 bits are each seperated into /Stage 0/ and /Stage 1/ each operating on the first and second byte respectively. + - Stage 0: + 1. The byte is sign tested (bitwise ~AND~ =0x80=), store this as a boolean in /sign0/. + 2. The valid first character range is looked up using the result of the sign test (either 0 or 1), store the range in /range/, and the slice ~KANA~ taken from the range in /kana/. + 3. The first index is calculated as the unsigned first byte modulo the size (exclusive) of /range/. Store this as /index0/. + 4. The swap table is checked to see if /index0/ has an entry. Then each following step is checked in order: + + If the swap entry exists and the first byte bitwise ~AND~ =0x2= is not 0, set the first character of the output to the value found in the swap table. + + If the swap entry exists and the first byte bitwise ~AND~ =0x8= is not 0 and the index has an entry in the 2nd swap table, set the first character of the output to the value found in the 2nd swap table. + + In any other case, set the first character of the output to the value found in the /kana/ slice at the /index/. + - Stage 1: + 1. Compute a sub table for /index/ plus the start of /range/ using the ranges defined in ~KANA_SUB_VALID_FOR~ and store it in /sub/. If there is no sub table possible, skip to step 3. + 2. If there is an entry in /sub/ for the index of the 2nd byte modulo the size of ~KANA_SUB~, set the second output character to be that character. + 3. If there was no value set from the sub table, the 2nd output character becomes the first output character from inputting the 2nd byte back through /Stage 0/ as the first byte. + - Concatenate both characters and move to the next 16-bit block. Notes: - - It is valid for a single iterator to produce between 0 and 2 characters but no more. - - If an input given to the algorithm that cannot be divided exactly into 16-bit blocks (i.e. one byte is left over), a padding byte of 0 is added as the 2nd byte to make it fit. + - It is valid for a single iterator to produce between 0 and 2 characters but no more. + - If an input given to the algorithm that cannot be divided exactly into 16-bit blocks (i.e. one byte is left over), a padding byte of 0 is added as the 2nd byte to make it fit. For more information see [[file:./src/mnemonic.rs][mnemonic.rs]]. ** License GPL'd with love <3 diff --git a/node/kana.js b/node/kana.js index 6adfd87..e20a8dd 100644 --- a/node/kana.js +++ b/node/kana.js @@ -11,6 +11,7 @@ const PSalt = ref.refType(Salt); const Context = struct({ 'algo': 'char', + 'flags': 'long', 'salt': Salt, }); const PContext = ref.refType(Context); @@ -23,6 +24,7 @@ const lib = ffi.Library('libkhash', { 'khash_length': ['int', [PContext, 'string', 'long', PLong]], 'khash_do': ['int', [PContext, 'string', 'long', 'string', 'long']], + 'khash_max_length': ['int', ['char', 'long', PLong]], }); const ctx_create = (algo, salt, salt_ref, salt_sz) => { @@ -51,6 +53,11 @@ const khash_do = (ctx, jsstring, len) => { lib.khash_do(ctx,string,string.length,buffer,len); return ref.readCString(buffer,0); }; +const khash_max_length = (algo, input) => { + let len = ref.alloc('long'); + lib.khash_max_length(algo, input, len); + return len.deref(); +}; const get_salt_type = (salt) => { if (salt && salt.tag) { @@ -83,6 +90,15 @@ function Kana(algo, salt) this.ctx = ctx_create(algo || 0, stype, fbuffer, fbuffer ? fbuffer.length : 0); } } +Kana.single = function(algo, salt, input) { + const mlen = khash_max_length(algo || 0, input.length); + + const stype = get_salt_type(salt); + const fbuffer = salt ? salt.buffer || null : null; + const ctx = ctx_create(algo || 0, stype, fbuffer, fbuffer ? fbuffer.length : 0); + + return khash_do(ctx, input, mlen); +}; const K = Kana.prototype; /// Free the associated context. diff --git a/node/test.js b/node/test.js new file mode 100644 index 0000000..da13523 --- /dev/null +++ b/node/test.js @@ -0,0 +1,5 @@ +const kana = require('./kana'); + +console.log(new kana(null, null).once("hello loli")); +console.log(kana.single(kana.ALGO_DEFAULT, null, "hello loli")); +