1 | var fs = require('fs')
|
2 | var assert = require('nanoassert')
|
3 | var toUint8Array = require('base64-to-uint8array')
|
4 | var buf = toUint8Array(fs.readFileSync(__dirname + '/blake2b.wasm', 'base64'))
|
5 | var rdy
|
6 |
|
7 | var head = 64
|
8 | var mod = null
|
9 | var memory = null
|
10 | var freeList = []
|
11 |
|
12 | module.exports = Blake2b
|
13 | var BYTES_MIN = module.exports.BYTES_MIN = 16
|
14 | var BYTES_MAX = module.exports.BYTES_MAX = 64
|
15 | var BYTES = module.exports.BYTES = 32
|
16 | var KEYBYTES_MIN = module.exports.KEYBYTES_MIN = 16
|
17 | var KEYBYTES_MAX = module.exports.KEYBYTES_MAX = 64
|
18 | var KEYBYTES = module.exports.KEYBYTES = 32
|
19 | var SALTBYTES = module.exports.SALTBYTES = 16
|
20 | var PERSONALBYTES = module.exports.PERSONALBYTES = 16
|
21 |
|
22 | function Blake2b (digestLength, key, salt, personal, noAssert) {
|
23 | if (!(this instanceof Blake2b)) return new Blake2b(digestLength, key, salt, personal, noAssert)
|
24 | if (!mod) throw new Error('WASM not loaded. Wait for Blake2b.ready(cb)')
|
25 | if (!digestLength) digestLength = 32
|
26 |
|
27 | if (noAssert !== true) {
|
28 | assert(digestLength >= BYTES_MIN, 'digestLength must be at least ' + BYTES_MIN + ', was given ' + digestLength)
|
29 | assert(digestLength <= BYTES_MAX, 'digestLength must be at most ' + BYTES_MAX + ', was given ' + digestLength)
|
30 | if (key != null) assert(key.length >= KEYBYTES_MIN, 'key must be at least ' + KEYBYTES_MIN + ', was given ' + key.length)
|
31 | if (key != null) assert(key.length <= KEYBYTES_MAX, 'key must be at least ' + KEYBYTES_MAX + ', was given ' + key.length)
|
32 | if (salt != null) assert(salt.length === SALTBYTES, 'salt must be exactly ' + SALTBYTES + ', was given ' + salt.length)
|
33 | if (personal != null) assert(personal.length === PERSONALBYTES, 'personal must be exactly ' + PERSONALBYTES + ', was given ' + personal.length)
|
34 | }
|
35 |
|
36 | if (!freeList.length) {
|
37 | freeList.push(head)
|
38 | head += 216
|
39 | }
|
40 |
|
41 | this.digestLength = digestLength
|
42 | this.finalized = false
|
43 | this.pointer = freeList.pop()
|
44 |
|
45 | memory.fill(0, 0, 64)
|
46 | memory[0] = this.digestLength
|
47 | memory[1] = key ? key.length : 0
|
48 | memory[2] = 1
|
49 | memory[3] = 1
|
50 |
|
51 | if (salt) memory.set(salt, 32)
|
52 | if (personal) memory.set(personal, 48)
|
53 |
|
54 | mod.blake2b_init(this.pointer, this.digestLength)
|
55 |
|
56 | if (key) {
|
57 | this.update(key)
|
58 | memory.fill(0, head, head + key.length)
|
59 | memory[this.pointer + 200] = 128
|
60 | }
|
61 | }
|
62 |
|
63 | Blake2b.prototype.ready = Blake2b.ready
|
64 |
|
65 | Blake2b.prototype.update = function (input) {
|
66 | assert(this.finalized === false, 'Hash instance finalized')
|
67 | assert(input, 'input must be TypedArray or Buffer')
|
68 |
|
69 | memory.set(input, head)
|
70 | mod.blake2b_update(this.pointer, head, head + input.length)
|
71 | return this
|
72 | }
|
73 |
|
74 | Blake2b.prototype.digest = function (enc) {
|
75 | assert(this.finalized === false, 'Hash instance finalized')
|
76 | this.finalized = true
|
77 |
|
78 | freeList.push(this.pointer)
|
79 | mod.blake2b_final(this.pointer)
|
80 |
|
81 | if (!enc || enc === 'binary') {
|
82 | return memory.slice(this.pointer + 128, this.pointer + 128 + this.digestLength)
|
83 | }
|
84 |
|
85 | if (enc === 'hex') {
|
86 | return hexSlice(memory, this.pointer + 128, this.digestLength)
|
87 | }
|
88 |
|
89 | assert(enc.length >= this.digestLength, 'input must be TypedArray or Buffer')
|
90 | for (var i = 0; i < this.digestLength; i++) {
|
91 | enc[i] = memory[this.pointer + 128 + i]
|
92 | }
|
93 |
|
94 | return enc
|
95 | }
|
96 |
|
97 |
|
98 | Blake2b.prototype.final = Blake2b.prototype.digest
|
99 |
|
100 | Blake2b.WASM = buf
|
101 | Blake2b.SUPPORTED = typeof WebAssembly !== 'undefined'
|
102 |
|
103 | Blake2b.ready = function (cb) {
|
104 | if (!cb) cb = noop
|
105 | if (!Blake2b.SUPPORTED) return cb(new Error('WebAssembly not supported'))
|
106 |
|
107 | if (!rdy) {
|
108 | rdy = WebAssembly.instantiate(buf).then(setup)
|
109 | }
|
110 |
|
111 | return rdy.then(cb).catch(cb)
|
112 | }
|
113 |
|
114 | function noop () {}
|
115 |
|
116 | function hexSlice (buf, start, len) {
|
117 | var str = ''
|
118 | for (var i = 0; i < len; i++) str += toHex(buf[start + i])
|
119 | return str
|
120 | }
|
121 |
|
122 | function toHex (n) {
|
123 | if (n < 16) return '0' + n.toString(16)
|
124 | return n.toString(16)
|
125 | }
|
126 |
|
127 | function setup (w) {
|
128 | mod = w.instance.exports
|
129 | memory = new Uint8Array(w.instance.exports.memory.buffer)
|
130 | }
|