|
|
|
const ffi = require('ffi');
|
|
|
|
const struct = require('ref-struct');
|
|
|
|
const ref = require('ref');
|
|
|
|
|
|
|
|
const Salt = struct({
|
|
|
|
'type': 'char',
|
|
|
|
'size': 'int',
|
|
|
|
'body': 'pointer',
|
|
|
|
});
|
|
|
|
const PSalt = ref.refType(Salt);
|
|
|
|
|
|
|
|
const Context = struct({
|
|
|
|
'algo': 'char',
|
|
|
|
'flags': 'long',
|
|
|
|
'salt': Salt,
|
|
|
|
});
|
|
|
|
const PContext = ref.refType(Context);
|
|
|
|
const PLong = ref.refType(ref.types.long);
|
|
|
|
|
|
|
|
const lib = ffi.Library('libkhash', {
|
|
|
|
'khash_new_context': ['int', ['char', 'char', 'string', 'long', PContext]],
|
|
|
|
'khash_free_context': ['int', [PContext]],
|
|
|
|
'khash_clone_context': ['int', [PContext, PContext]],
|
|
|
|
|
|
|
|
'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) => {
|
|
|
|
let ctx_ptr = ref.alloc(Context);
|
|
|
|
lib.khash_new_context(algo, salt, salt_ref, salt_sz, ctx_ptr);
|
|
|
|
return ctx_ptr;
|
|
|
|
};
|
|
|
|
const ctx_free = (ptr) => {
|
|
|
|
lib.khash_free_context(ptr);
|
|
|
|
return ptr.deref();
|
|
|
|
};
|
|
|
|
const ctx_clone = (src) => {
|
|
|
|
let dst = ref.alloc(Context);
|
|
|
|
lib.khash_clone_context(src,dst);
|
|
|
|
return dst;
|
|
|
|
};
|
|
|
|
const khash_length = (ctx, jsstring) => {
|
|
|
|
let string = ref.allocCString(jsstring);
|
|
|
|
let len = ref.alloc('long');
|
|
|
|
lib.khash_length(ctx, string, string.length, len);
|
|
|
|
return len.deref();
|
|
|
|
};
|
|
|
|
const khash_do = (ctx, jsstring, len) => {
|
|
|
|
let string = ref.allocCString(jsstring);
|
|
|
|
let buffer = Buffer.alloc(len+1);
|
|
|
|
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) {
|
|
|
|
switch(salt.tag)
|
|
|
|
{
|
|
|
|
case '__NONE':
|
|
|
|
return Kana.SALT_NONE;
|
|
|
|
case '__RANDOM':
|
|
|
|
return Kana.SALT_RANDOM;
|
|
|
|
case '__SPECIFIC':
|
|
|
|
return Kana.SALT_SPECIFIC;
|
|
|
|
default:
|
|
|
|
return Kana.SALT_DEFAULT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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.
|
|
|
|
/// 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;
|