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:
parent
2e332a3526
commit
27692c26ea
15
package.json
15
package.json
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue