Implement wrapper for crypto_generichash_blake2b_salt_personal

This will be replaced in future by an upstream implementation in libsodium.
This commit is contained in:
Jack Grigg 2017-06-02 14:35:51 +12:00
parent 2e332a3526
commit 27692c26ea
No known key found for this signature in database
GPG Key ID: 665DBCD284F7DAFF
2 changed files with 217 additions and 0 deletions

View File

@ -18,6 +18,21 @@
"test": "npm run standard && npm run coverage",
"unit": "mocha"
},
"standard": {
"ignore": [
"src/blake2b.js"
]
},
"nyc": {
"exclude": [
"src/blake2b.js",
"test",
"test{,-*}.js",
"**/*.test.js",
"**/__tests__/**",
"**/node_modules/**"
]
},
"devDependencies": {
"mocha": "3.4.2",
"nyc": "10.3.2",

202
src/blake2b.js Normal file
View File

@ -0,0 +1,202 @@
var libsodium = require('libsodium-sumo')
var sodium = require('libsodium-wrappers-sumo')
var output_format = 'uint8array'
function _format_output (output, optionalOutputFormat) {
var selectedOutputFormat = optionalOutputFormat || output_format
if (!_is_output_format(selectedOutputFormat)) {
throw new Error(selectedOutputFormat + ' output format is not available')
}
if (output instanceof AllocatedBuf) {
if (selectedOutputFormat === 'uint8array') {
return output.to_Uint8Array()
} else if (selectedOutputFormat === 'text') {
return sodium.to_string(output.to_Uint8Array())
} else if (selectedOutputFormat === 'hex') {
return sodium.to_hex(output.to_Uint8Array())
} else if (selectedOutputFormat === 'base64') {
return sodium.to_base64(output.to_Uint8Array())
} else {
throw new Error('What is output format \"' + selectedOutputFormat + '\"?')
}
} else if (typeof output === 'object') { // Composed output. Example : key pairs
var props = Object.keys(output)
var formattedOutput = {}
for (var i = 0; i < props.length; i++) {
formattedOutput[props[i]] = _format_output(output[props[i]], selectedOutputFormat)
}
return formattedOutput
} else if (typeof output === 'string') {
return output
} else {
throw new TypeError('Cannot format output')
}
}
function _is_output_format (format) {
var formats = sodium.output_formats()
for (var i = 0; i < formats.length; i++) {
if (formats[i] === format) {
return true
}
}
return false
}
function _check_output_format (format) {
if (!format) {
return
} else if (typeof format !== 'string') {
throw new TypeError('When defined, the output format must be a string')
} else if (!_is_output_format(format)) {
throw new Error(format + ' is not a supported output format')
}
}
// --------------------------------------------------------------------------
// Memory management
//
// AllocatedBuf: address allocated using _malloc() + length
function AllocatedBuf (length) {
this.length = length
this.address = _malloc(length)
}
// Copy the content of a AllocatedBuf (_malloc()'d memory) into a Uint8Array
AllocatedBuf.prototype.to_Uint8Array = function () {
var result = new Uint8Array(this.length)
result.set(libsodium.HEAPU8.subarray(this.address, this.address + this.length))
return result
}
// _malloc() a region and initialize it with the content of a Uint8Array
function _to_allocated_buf_address (bytes) {
var address = _malloc(bytes.length)
libsodium.HEAPU8.set(bytes, address)
return address
}
function _malloc (length) {
var result = libsodium._malloc(length)
if (result === 0) {
var err = {
message: '_malloc() failed',
length: length
}
throw err
}
return result
}
function _free (address) {
libsodium._free(address)
}
function _free_all (addresses) {
for (var i = 0; i < addresses.length; i++) {
_free(addresses[i])
}
}
function _free_and_throw_error (address_pool, err) {
_free_all(address_pool)
throw new Error(err)
}
function _free_and_throw_type_error (address_pool, err) {
_free_all(address_pool)
throw new TypeError(err)
}
function _require_defined (address_pool, varValue, varName) {
if (varValue === undefined) {
_free_and_throw_type_error(address_pool, varName + ' cannot be null or undefined')
}
}
function _any_to_Uint8Array (address_pool, varValue, varName) {
_require_defined(address_pool, varValue, varName)
if (varValue instanceof Uint8Array) {
return varValue
} else if (typeof varValue === 'string') {
return sodium.from_string(varValue)
}
_free_and_throw_type_error(address_pool, 'unsupported input type for ' + varName)
}
function crypto_generichash_blake2b_salt_personal (hash_length, message, key, salt, personal, outputFormat) {
var address_pool = []
_check_output_format(outputFormat)
// ---------- input: hash_length (uint)
_require_defined(address_pool, hash_length, 'hash_length')
if (!(typeof hash_length === 'number' && (hash_length | 0) === hash_length) && (hash_length | 0) > 0) {
_free_and_throw_type_error(address_pool, 'hash_length must be an unsigned integer')
}
// ---------- input: message (unsized_buf)
message = _any_to_Uint8Array(address_pool, message, 'message')
var message_address = _to_allocated_buf_address(message)
var message_length = message.length
address_pool.push(message_address)
// ---------- input: key (unsized_buf_optional)
var key_address = null
var key_length = 0
if (key !== undefined) {
key = _any_to_Uint8Array(address_pool, key, 'key')
key_address = _to_allocated_buf_address(key)
key_length = key.length
address_pool.push(key_address)
}
// ---------- input: salt (buf_optional)
var salt_address = null
if (salt !== undefined) {
salt = _any_to_Uint8Array(address_pool, salt, 'salt')
var salt_length = (libsodium._crypto_generichash_blake2b_saltbytes()) | 0
if (key.length !== salt_length) {
_free_and_throw_type_error(address_pool, 'invalid salt length')
}
salt_address = _to_allocated_buf_address(salt)
address_pool.push(salt_address)
}
// ---------- input: personal (buf_optional)
var personal_address = null
if (personal !== undefined) {
personal = _any_to_Uint8Array(address_pool, personal, 'personal')
var personal_length = (libsodium._crypto_generichash_blake2b_personalbytes()) | 0
if (personal.length !== personal_length) {
_free_and_throw_type_error(address_pool, 'invalid personal length')
}
personal_address = _to_allocated_buf_address(personal)
address_pool.push(personal_address)
}
// ---------- output hash (buf)
hash_length = (hash_length) | 0
var hash = new AllocatedBuf(hash_length)
var hash_address = hash.address
address_pool.push(hash_address)
if ((libsodium._crypto_generichash_blake2b_salt_personal(hash_address, hash_length, message_address, message_length, 0, key_address, key_length, salt_address, personal_address) | 0) === 0) {
var ret = _format_output(hash, outputFormat)
_free_all(address_pool)
return ret
}
_free_and_throw_error(address_pool)
}
module.exports = {
crypto_generichash_blake2b_salt_personal: crypto_generichash_blake2b_salt_personal
}