|
|
|
var assert = require('nanoassert')
|
|
|
|
var b4a = require('b4a')
|
|
|
|
|
|
|
|
var wasm = null
|
|
|
|
var wasmPromise = typeof WebAssembly !== "undefined" && require('./blake2b')().then(mod => {
|
|
|
|
wasm = mod
|
|
|
|
})
|
|
|
|
|
|
|
|
var head = 64
|
|
|
|
var freeList = []
|
|
|
|
|
|
|
|
module.exports = Blake2b
|
|
|
|
var BYTES_MIN = module.exports.BYTES_MIN = 16
|
|
|
|
var BYTES_MAX = module.exports.BYTES_MAX = 64
|
|
|
|
var BYTES = module.exports.BYTES = 32
|
|
|
|
var KEYBYTES_MIN = module.exports.KEYBYTES_MIN = 16
|
|
|
|
var KEYBYTES_MAX = module.exports.KEYBYTES_MAX = 64
|
|
|
|
var KEYBYTES = module.exports.KEYBYTES = 32
|
|
|
|
var SALTBYTES = module.exports.SALTBYTES = 16
|
|
|
|
var PERSONALBYTES = module.exports.PERSONALBYTES = 16
|
|
|
|
|
|
|
|
function Blake2b (digestLength, key, salt, personal, noAssert) {
|
|
|
|
if (!(this instanceof Blake2b)) return new Blake2b(digestLength, key, salt, personal, noAssert)
|
|
|
|
if (!wasm) throw new Error('WASM not loaded. Wait for Blake2b.ready(cb)')
|
|
|
|
if (!digestLength) digestLength = 32
|
|
|
|
|
|
|
|
if (noAssert !== true) {
|
|
|
|
assert(digestLength >= BYTES_MIN, 'digestLength must be at least ' + BYTES_MIN + ', was given ' + digestLength)
|
|
|
|
assert(digestLength <= BYTES_MAX, 'digestLength must be at most ' + BYTES_MAX + ', was given ' + digestLength)
|
|
|
|
if (key != null) {
|
|
|
|
assert(key instanceof Uint8Array, 'key must be Uint8Array or Buffer')
|
|
|
|
assert(key.length >= KEYBYTES_MIN, 'key must be at least ' + KEYBYTES_MIN + ', was given ' + key.length)
|
|
|
|
assert(key.length <= KEYBYTES_MAX, 'key must be at least ' + KEYBYTES_MAX + ', was given ' + key.length)
|
|
|
|
}
|
|
|
|
if (salt != null) {
|
|
|
|
assert(salt instanceof Uint8Array, 'salt must be Uint8Array or Buffer')
|
|
|
|
assert(salt.length === SALTBYTES, 'salt must be exactly ' + SALTBYTES + ', was given ' + salt.length)
|
|
|
|
}
|
|
|
|
if (personal != null) {
|
|
|
|
assert(personal instanceof Uint8Array, 'personal must be Uint8Array or Buffer')
|
|
|
|
assert(personal.length === PERSONALBYTES, 'personal must be exactly ' + PERSONALBYTES + ', was given ' + personal.length)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!freeList.length) {
|
|
|
|
freeList.push(head)
|
|
|
|
head += 216
|
|
|
|
}
|
|
|
|
|
|
|
|
this.digestLength = digestLength
|
|
|
|
this.finalized = false
|
|
|
|
this.pointer = freeList.pop()
|
|
|
|
this._memory = new Uint8Array(wasm.memory.buffer)
|
|
|
|
|
|
|
|
this._memory.fill(0, 0, 64)
|
|
|
|
this._memory[0] = this.digestLength
|
|
|
|
this._memory[1] = key ? key.length : 0
|
|
|
|
this._memory[2] = 1 // fanout
|
|
|
|
this._memory[3] = 1 // depth
|
|
|
|
|
|
|
|
if (salt) this._memory.set(salt, 32)
|
|
|
|
if (personal) this._memory.set(personal, 48)
|
|
|
|
|
|
|
|
if (this.pointer + 216 > this._memory.length) this._realloc(this.pointer + 216) // we need 216 bytes for the state
|
|
|
|
wasm.blake2b_init(this.pointer, this.digestLength)
|
|
|
|
|
|
|
|
if (key) {
|
|
|
|
this.update(key)
|
|
|
|
this._memory.fill(0, head, head + key.length) // whiteout key
|
|
|
|
this._memory[this.pointer + 200] = 128
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Blake2b.prototype._realloc = function (size) {
|
|
|
|
wasm.memory.grow(Math.max(0, Math.ceil(Math.abs(size - this._memory.length) / 65536)))
|
|
|
|
this._memory = new Uint8Array(wasm.memory.buffer)
|
|
|
|
}
|
|
|
|
|
|
|
|
Blake2b.prototype.update = function (input) {
|
|
|
|
assert(this.finalized === false, 'Hash instance finalized')
|
|
|
|
assert(input instanceof Uint8Array, 'input must be Uint8Array or Buffer')
|
|
|
|
|
|
|
|
if (head + input.length > this._memory.length) this._realloc(head + input.length)
|
|
|
|
this._memory.set(input, head)
|
|
|
|
wasm.blake2b_update(this.pointer, head, head + input.length)
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
Blake2b.prototype.digest = function (enc) {
|
|
|
|
assert(this.finalized === false, 'Hash instance finalized')
|
|
|
|
this.finalized = true
|
|
|
|
|
|
|
|
freeList.push(this.pointer)
|
|
|
|
wasm.blake2b_final(this.pointer)
|
|
|
|
|
|
|
|
if (!enc || enc === 'binary') {
|
|
|
|
return this._memory.slice(this.pointer + 128, this.pointer + 128 + this.digestLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof enc === 'string') {
|
|
|
|
return b4a.toString(this._memory, enc, this.pointer + 128, this.pointer + 128 + this.digestLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(enc instanceof Uint8Array && enc.length >= this.digestLength, 'input must be Uint8Array or Buffer')
|
|
|
|
for (var i = 0; i < this.digestLength; i++) {
|
|
|
|
enc[i] = this._memory[this.pointer + 128 + i]
|
|
|
|
}
|
|
|
|
|
|
|
|
return enc
|
|
|
|
}
|
|
|
|
|
|
|
|
// libsodium compat
|
|
|
|
Blake2b.prototype.final = Blake2b.prototype.digest
|
|
|
|
|
|
|
|
Blake2b.WASM = wasm
|
|
|
|
Blake2b.SUPPORTED = typeof WebAssembly !== 'undefined'
|
|
|
|
|
|
|
|
Blake2b.ready = function (cb) {
|
|
|
|
if (!cb) cb = noop
|
|
|
|
if (!wasmPromise) return cb(new Error('WebAssembly not supported'))
|
|
|
|
return wasmPromise.then(() => cb(), cb)
|
|
|
|
}
|
|
|
|
|
|
|
|
Blake2b.prototype.ready = Blake2b.ready
|
|
|
|
|
|
|
|
Blake2b.prototype.getPartialHash = function () {
|
|
|
|
return this._memory.slice(this.pointer, this.pointer + 216);
|
|
|
|
}
|
|
|
|
|
|
|
|
Blake2b.prototype.setPartialHash = function (ph) {
|
|
|
|
this._memory.set(ph, this.pointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
function noop () {}
|