[wip] adding message signing functionality
Problem is this:
Uncaught ReferenceError: require is not defined
<anonymous> moz-extension://b4685fc7-232c-44d9-b653-f51b7c7ea868/dist/jex_include/local-vdk_aecrypt-0.1.0/dist/jex_include/local-blakejs-1.2.1/dist/index.js:1
index.js:1:13
The issue is that blakejs is written for node and needs to be modified to work
in a browser.
Next task is doing that, but did a ton of work so taking a break
This commit is contained in:
+3
-1
@@ -4,4 +4,6 @@
|
|||||||
{version, "0.1.0"}.
|
{version, "0.1.0"}.
|
||||||
{deps, ["local-tweetnacl-1.0.3",
|
{deps, ["local-tweetnacl-1.0.3",
|
||||||
"local-awcp-0.2.2",
|
"local-awcp-0.2.2",
|
||||||
"local-vdk_aeser-0.1.0"]}.
|
"local-vdk_aecrypt-0.1.0",
|
||||||
|
"local-vdk_aeser-0.1.0",
|
||||||
|
"local-vdk_binary-0.1.0"]}.
|
||||||
|
|||||||
+5
-2
@@ -51,8 +51,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import * as awcp from './jex_include/local-awcp-0.2.2/dist/awcp.js';
|
import * as awcp from './jex_include/local-awcp-0.2.2/dist/awcp.js';
|
||||||
import * as vdk_aeser from './jex_include/local-vdk_aeser-0.1.0/dist/vdk_aeser.js';
|
import * as vdk_aecrypt from './jex_include/local-vdk_aecrypt-0.1.0/dist/vdk_aecrypt.js';
|
||||||
|
import * as vdk_aeser from './jex_include/local-vdk_aeser-0.1.0/dist/vdk_aeser.js';
|
||||||
|
import * as vdk_binary from './jex_include/local-vdk_binary-0.1.0/dist/vdk_binary.js';
|
||||||
|
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
@@ -313,6 +315,7 @@ bi_msg_handler_content
|
|||||||
|
|
||||||
// right now just sign message
|
// right now just sign message
|
||||||
case "message.sign":
|
case "message.sign":
|
||||||
|
console.log('jr bg content message handler message sign');
|
||||||
let secret_key : Uint8Array = i_state.keypairs[0].secretKey;
|
let secret_key : Uint8Array = i_state.keypairs[0].secretKey;
|
||||||
let msg_str : string = msg.data.params.message;
|
let msg_str : string = msg.data.params.message;
|
||||||
// use nacl detached signatures
|
// use nacl detached signatures
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2022 Blakejs contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
blakejs
|
||||||
|
====
|
||||||
|
|
||||||
|
[](https://travis-ci.org/dcposch/blakejs)
|
||||||
|
[](https://badge.fury.io/js/blakejs)
|
||||||
|
|
||||||
|
**blakejs is a pure Javascript implementation of the BLAKE2b and BLAKE2s hash functions.**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[RFC 7693: The BLAKE Cryptographic Hash and MAC](https://tools.ietf.org/html/rfc7693)
|
||||||
|
|
||||||
|
BLAKE is the default family of hash functions in the venerable NaCl crypto library. Like SHA2 and SHA3 but unlike MD5 and SHA1, BLAKE offers solid security. With an optimized assembly implementation, BLAKE can be faster than all of those other hash functions.
|
||||||
|
|
||||||
|
Of course, this implementation is in Javascript, so it won't be winning any speed records. More under Performance below. It's short and sweet, less than 500 LOC.
|
||||||
|
|
||||||
|
**As far as I know, this package is the easiest way to compute Blake2 in the browser.**
|
||||||
|
|
||||||
|
Other options to consider:
|
||||||
|
- [@nazar-pc](https://github.com/nazar-pc) has WebAssembly implementation for higher performance where supported: [blake2.wasm](https://github.com/nazar-pc/blake2.wasm)
|
||||||
|
- [@emilbayes](https://github.com/emilbayes) has a Blake2b-only implementation with salt support; WASM with automatic JS fallback: [blake2b](https://github.com/emilbayes/blake2b)
|
||||||
|
- On node, you probably want the native wrapper [node-blake2](https://github.com/ludios/node-blake2)
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
---
|
||||||
|
```
|
||||||
|
$ npm install --save blakejs
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
var blake = require('blakejs')
|
||||||
|
console.log(blake.blake2bHex('abc'))
|
||||||
|
// prints ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923
|
||||||
|
console.log(blake.blake2sHex('abc'))
|
||||||
|
// prints 508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982
|
||||||
|
```
|
||||||
|
|
||||||
|
API
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1. Use `blake2b` to compute a BLAKE2b hash
|
||||||
|
|
||||||
|
Pass it a string, `Buffer`, or `Uint8Array` containing bytes to hash, and it will return a `Uint8Array` containing the hash.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Computes the BLAKE2B hash of a string or byte array, and returns a Uint8Array
|
||||||
|
//
|
||||||
|
// Returns a n-byte Uint8Array
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
// Strings are converted to UTF8 bytes
|
||||||
|
// - key - optional key Uint8Array, up to 64 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
function blake2b(input, key, outlen) {
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For convenience, `blake2bHex` takes the same arguments and works the same way, but returns a hex string.
|
||||||
|
|
||||||
|
### 2. Use `blake2b[Init,Update,Final]` to compute a streaming hash
|
||||||
|
|
||||||
|
```js
|
||||||
|
var KEY = null // optional key
|
||||||
|
var OUTPUT_LENGTH = 64 // bytes
|
||||||
|
var context = blake2bInit(OUTPUT_LENGTH, KEY)
|
||||||
|
...
|
||||||
|
// each time you get a byte array from the stream:
|
||||||
|
blake2bUpdate(context, bytes)
|
||||||
|
...
|
||||||
|
// finally, once the stream has been exhausted
|
||||||
|
var hash = blake2bFinal(context)
|
||||||
|
// returns a 64-byte hash, as a Uint8Array
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. All `blake2b*` functions have `blake2s*` equivalents
|
||||||
|
|
||||||
|
BLAKE2b: `blake2b`, `blake2bHex`, `blake2bInit`, `blake2bUpdate`, and `blake2bFinal`
|
||||||
|
|
||||||
|
BLAKE2s: `blake2s`, `blake2sHex`, `blake2sInit`, `blake2sUpdate`, and `blake2sFinal`
|
||||||
|
|
||||||
|
The inputs are identical except that maximum key size and maximum output size are 32 bytes instead of 64.
|
||||||
|
|
||||||
|
Limitations
|
||||||
|
---
|
||||||
|
* Can only handle up to 2**53 bytes of input
|
||||||
|
|
||||||
|
If your webapp is hashing more than 8 petabytes, you may have other problems :)
|
||||||
|
|
||||||
|
Testing
|
||||||
|
---
|
||||||
|
* Examples from the RFC
|
||||||
|
* BLAKE2s self-test from the RFC
|
||||||
|
* Examples from http://pythonhosted.org/pyblake2/examples.html
|
||||||
|
* A longer set of test vectors generated by https://github.com/jedisct1/crypto-test-vectors/tree/master/crypto/hash/blake2/blake2b/nosalt-nopersonalization/generators/libsodium
|
||||||
|
|
||||||
|
Performance
|
||||||
|
---
|
||||||
|
```
|
||||||
|
BLAKE2b: 15.2 MB / second on a 2.2GHz i7-4770HQ
|
||||||
|
BLAKE2s: 20.4 MB / second
|
||||||
|
|
||||||
|
¯\_(ツ)_/¯
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're using BLAKE2b in server side node.js code, you probably want the [native wrapper](https://www.npmjs.com/package/blake2) which should be able to do several hundred MB / second on the same processor.
|
||||||
|
|
||||||
|
If you're using BLAKE2b in a web app, 15 MB/sec might be fine.
|
||||||
|
|
||||||
|
Javascript doesn't have 64-bit integers, and BLAKE2b is a 64-bit integer algorithm. Writing it with`Uint32Array` is not that fast. BLAKE2s is a 32-bit algorithm, so it's a bit faster.
|
||||||
|
|
||||||
|
If we want better machine code at the expense of gross-looking Javascript, we could use asm.js
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
---
|
||||||
|
Creative Commons CC0. Ported from the reference C implementation in
|
||||||
|
[RFC 7693](https://tools.ietf.org/html/rfc7693).
|
||||||
Vendored
+365
@@ -0,0 +1,365 @@
|
|||||||
|
// Blake2B in pure Javascript
|
||||||
|
// Adapted from the reference implementation in RFC7693
|
||||||
|
// Ported to Javascript by DC - https://github.com/dcposch
|
||||||
|
|
||||||
|
const util = require('./util')
|
||||||
|
|
||||||
|
// 64-bit unsigned addition
|
||||||
|
// Sets v[a,a+1] += v[b,b+1]
|
||||||
|
// v should be a Uint32Array
|
||||||
|
function ADD64AA (v, a, b) {
|
||||||
|
const o0 = v[a] + v[b]
|
||||||
|
let o1 = v[a + 1] + v[b + 1]
|
||||||
|
if (o0 >= 0x100000000) {
|
||||||
|
o1++
|
||||||
|
}
|
||||||
|
v[a] = o0
|
||||||
|
v[a + 1] = o1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 64-bit unsigned addition
|
||||||
|
// Sets v[a,a+1] += b
|
||||||
|
// b0 is the low 32 bits of b, b1 represents the high 32 bits
|
||||||
|
function ADD64AC (v, a, b0, b1) {
|
||||||
|
let o0 = v[a] + b0
|
||||||
|
if (b0 < 0) {
|
||||||
|
o0 += 0x100000000
|
||||||
|
}
|
||||||
|
let o1 = v[a + 1] + b1
|
||||||
|
if (o0 >= 0x100000000) {
|
||||||
|
o1++
|
||||||
|
}
|
||||||
|
v[a] = o0
|
||||||
|
v[a + 1] = o1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Little-endian byte access
|
||||||
|
function B2B_GET32 (arr, i) {
|
||||||
|
return arr[i] ^ (arr[i + 1] << 8) ^ (arr[i + 2] << 16) ^ (arr[i + 3] << 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// G Mixing function
|
||||||
|
// The ROTRs are inlined for speed
|
||||||
|
function B2B_G (a, b, c, d, ix, iy) {
|
||||||
|
const x0 = m[ix]
|
||||||
|
const x1 = m[ix + 1]
|
||||||
|
const y0 = m[iy]
|
||||||
|
const y1 = m[iy + 1]
|
||||||
|
|
||||||
|
ADD64AA(v, a, b) // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s
|
||||||
|
ADD64AC(v, a, x0, x1) // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits
|
||||||
|
|
||||||
|
// v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits
|
||||||
|
let xor0 = v[d] ^ v[a]
|
||||||
|
let xor1 = v[d + 1] ^ v[a + 1]
|
||||||
|
v[d] = xor1
|
||||||
|
v[d + 1] = xor0
|
||||||
|
|
||||||
|
ADD64AA(v, c, d)
|
||||||
|
|
||||||
|
// v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits
|
||||||
|
xor0 = v[b] ^ v[c]
|
||||||
|
xor1 = v[b + 1] ^ v[c + 1]
|
||||||
|
v[b] = (xor0 >>> 24) ^ (xor1 << 8)
|
||||||
|
v[b + 1] = (xor1 >>> 24) ^ (xor0 << 8)
|
||||||
|
|
||||||
|
ADD64AA(v, a, b)
|
||||||
|
ADD64AC(v, a, y0, y1)
|
||||||
|
|
||||||
|
// v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits
|
||||||
|
xor0 = v[d] ^ v[a]
|
||||||
|
xor1 = v[d + 1] ^ v[a + 1]
|
||||||
|
v[d] = (xor0 >>> 16) ^ (xor1 << 16)
|
||||||
|
v[d + 1] = (xor1 >>> 16) ^ (xor0 << 16)
|
||||||
|
|
||||||
|
ADD64AA(v, c, d)
|
||||||
|
|
||||||
|
// v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits
|
||||||
|
xor0 = v[b] ^ v[c]
|
||||||
|
xor1 = v[b + 1] ^ v[c + 1]
|
||||||
|
v[b] = (xor1 >>> 31) ^ (xor0 << 1)
|
||||||
|
v[b + 1] = (xor0 >>> 31) ^ (xor1 << 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization Vector
|
||||||
|
const BLAKE2B_IV32 = new Uint32Array([
|
||||||
|
0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372,
|
||||||
|
0x5f1d36f1, 0xa54ff53a, 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c,
|
||||||
|
0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19
|
||||||
|
])
|
||||||
|
|
||||||
|
const SIGMA8 = [
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13,
|
||||||
|
6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1,
|
||||||
|
9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4,
|
||||||
|
10, 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5,
|
||||||
|
15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, 13, 11, 7,
|
||||||
|
14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2,
|
||||||
|
13, 7, 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0,
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6,
|
||||||
|
1, 12, 0, 2, 11, 7, 5, 3
|
||||||
|
]
|
||||||
|
|
||||||
|
// These are offsets into a uint64 buffer.
|
||||||
|
// Multiply them all by 2 to make them offsets into a uint32 buffer,
|
||||||
|
// because this is Javascript and we don't have uint64s
|
||||||
|
const SIGMA82 = new Uint8Array(
|
||||||
|
SIGMA8.map(function (x) {
|
||||||
|
return x * 2
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compression function. 'last' flag indicates last block.
|
||||||
|
// Note we're representing 16 uint64s as 32 uint32s
|
||||||
|
const v = new Uint32Array(32)
|
||||||
|
const m = new Uint32Array(32)
|
||||||
|
function blake2bCompress (ctx, last) {
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
// init work variables
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
v[i] = ctx.h[i]
|
||||||
|
v[i + 16] = BLAKE2B_IV32[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// low 64 bits of offset
|
||||||
|
v[24] = v[24] ^ ctx.t
|
||||||
|
v[25] = v[25] ^ (ctx.t / 0x100000000)
|
||||||
|
// high 64 bits not supported, offset may not be higher than 2**53-1
|
||||||
|
|
||||||
|
// last block flag set ?
|
||||||
|
if (last) {
|
||||||
|
v[28] = ~v[28]
|
||||||
|
v[29] = ~v[29]
|
||||||
|
}
|
||||||
|
|
||||||
|
// get little-endian words
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
m[i] = B2B_GET32(ctx.b, 4 * i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// twelve rounds of mixing
|
||||||
|
// uncomment the DebugPrint calls to log the computation
|
||||||
|
// and match the RFC sample documentation
|
||||||
|
// util.debugPrint(' m[16]', m, 64)
|
||||||
|
for (i = 0; i < 12; i++) {
|
||||||
|
// util.debugPrint(' (i=' + (i < 10 ? ' ' : '') + i + ') v[16]', v, 64)
|
||||||
|
B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1])
|
||||||
|
B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3])
|
||||||
|
B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5])
|
||||||
|
B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7])
|
||||||
|
B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9])
|
||||||
|
B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11])
|
||||||
|
B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13])
|
||||||
|
B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15])
|
||||||
|
}
|
||||||
|
// util.debugPrint(' (i=12) v[16]', v, 64)
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
ctx.h[i] = ctx.h[i] ^ v[i] ^ v[i + 16]
|
||||||
|
}
|
||||||
|
// util.debugPrint('h[8]', ctx.h, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reusable parameterBlock
|
||||||
|
const parameterBlock = new Uint8Array([
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 0: outlen, keylen, fanout, depth
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 4: leaf length, sequential mode
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 8: node offset
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 12: node offset
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 16: node depth, inner length, rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 20: rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 24: rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 28: rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 32: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 36: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 40: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 44: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 48: personal
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 52: personal
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 56: personal
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0 // 60: personal
|
||||||
|
])
|
||||||
|
|
||||||
|
// Creates a BLAKE2b hashing context
|
||||||
|
// Requires an output length between 1 and 64 bytes
|
||||||
|
// Takes an optional Uint8Array key
|
||||||
|
// Takes an optinal Uint8Array salt
|
||||||
|
// Takes an optinal Uint8Array personal
|
||||||
|
function blake2bInit (outlen, key, salt, personal) {
|
||||||
|
if (outlen === 0 || outlen > 64) {
|
||||||
|
throw new Error('Illegal output length, expected 0 < length <= 64')
|
||||||
|
}
|
||||||
|
if (key && key.length > 64) {
|
||||||
|
throw new Error('Illegal key, expected Uint8Array with 0 < length <= 64')
|
||||||
|
}
|
||||||
|
if (salt && salt.length !== 16) {
|
||||||
|
throw new Error('Illegal salt, expected Uint8Array with length is 16')
|
||||||
|
}
|
||||||
|
if (personal && personal.length !== 16) {
|
||||||
|
throw new Error('Illegal personal, expected Uint8Array with length is 16')
|
||||||
|
}
|
||||||
|
|
||||||
|
// state, 'param block'
|
||||||
|
const ctx = {
|
||||||
|
b: new Uint8Array(128),
|
||||||
|
h: new Uint32Array(16),
|
||||||
|
t: 0, // input count
|
||||||
|
c: 0, // pointer within buffer
|
||||||
|
outlen: outlen // output length in bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize parameterBlock before usage
|
||||||
|
parameterBlock.fill(0)
|
||||||
|
parameterBlock[0] = outlen
|
||||||
|
if (key) parameterBlock[1] = key.length
|
||||||
|
parameterBlock[2] = 1 // fanout
|
||||||
|
parameterBlock[3] = 1 // depth
|
||||||
|
if (salt) parameterBlock.set(salt, 32)
|
||||||
|
if (personal) parameterBlock.set(personal, 48)
|
||||||
|
|
||||||
|
// initialize hash state
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
ctx.h[i] = BLAKE2B_IV32[i] ^ B2B_GET32(parameterBlock, i * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// key the hash, if applicable
|
||||||
|
if (key) {
|
||||||
|
blake2bUpdate(ctx, key)
|
||||||
|
// at the end
|
||||||
|
ctx.c = 128
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates a BLAKE2b streaming hash
|
||||||
|
// Requires hash context and Uint8Array (byte array)
|
||||||
|
function blake2bUpdate (ctx, input) {
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
if (ctx.c === 128) {
|
||||||
|
// buffer full ?
|
||||||
|
ctx.t += ctx.c // add counters
|
||||||
|
blake2bCompress(ctx, false) // compress (not last)
|
||||||
|
ctx.c = 0 // counter to zero
|
||||||
|
}
|
||||||
|
ctx.b[ctx.c++] = input[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completes a BLAKE2b streaming hash
|
||||||
|
// Returns a Uint8Array containing the message digest
|
||||||
|
function blake2bFinal (ctx) {
|
||||||
|
ctx.t += ctx.c // mark last block offset
|
||||||
|
|
||||||
|
while (ctx.c < 128) {
|
||||||
|
// fill up with zeros
|
||||||
|
ctx.b[ctx.c++] = 0
|
||||||
|
}
|
||||||
|
blake2bCompress(ctx, true) // final block flag = 1
|
||||||
|
|
||||||
|
// little endian convert and store
|
||||||
|
const out = new Uint8Array(ctx.outlen)
|
||||||
|
for (let i = 0; i < ctx.outlen; i++) {
|
||||||
|
out[i] = ctx.h[i >> 2] >> (8 * (i & 3))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2B hash of a string or byte array, and returns a Uint8Array
|
||||||
|
//
|
||||||
|
// Returns a n-byte Uint8Array
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 64 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
// - salt - optional salt bytes, string, Buffer or Uint8Array
|
||||||
|
// - personal - optional personal bytes, string, Buffer or Uint8Array
|
||||||
|
function blake2b (input, key, outlen, salt, personal) {
|
||||||
|
// preprocess inputs
|
||||||
|
outlen = outlen || 64
|
||||||
|
input = util.normalizeInput(input)
|
||||||
|
if (salt) {
|
||||||
|
salt = util.normalizeInput(salt)
|
||||||
|
}
|
||||||
|
if (personal) {
|
||||||
|
personal = util.normalizeInput(personal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the math
|
||||||
|
const ctx = blake2bInit(outlen, key, salt, personal)
|
||||||
|
blake2bUpdate(ctx, input)
|
||||||
|
return blake2bFinal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2B hash of a string or byte array
|
||||||
|
//
|
||||||
|
// Returns an n-byte hash in hex, all lowercase
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 64 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
// - salt - optional salt bytes, string, Buffer or Uint8Array
|
||||||
|
// - personal - optional personal bytes, string, Buffer or Uint8Array
|
||||||
|
function blake2bHex (input, key, outlen, salt, personal) {
|
||||||
|
const output = blake2b(input, key, outlen, salt, personal)
|
||||||
|
return util.toHex(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
blake2b: blake2b,
|
||||||
|
blake2bHex: blake2bHex,
|
||||||
|
blake2bInit: blake2bInit,
|
||||||
|
blake2bUpdate: blake2bUpdate,
|
||||||
|
blake2bFinal: blake2bFinal
|
||||||
|
}
|
||||||
Vendored
+353
@@ -0,0 +1,353 @@
|
|||||||
|
// BLAKE2s hash function in pure Javascript
|
||||||
|
// Adapted from the reference implementation in RFC7693
|
||||||
|
// Ported to Javascript by DC - https://github.com/dcposch
|
||||||
|
|
||||||
|
const util = require('./util')
|
||||||
|
|
||||||
|
// Little-endian byte access.
|
||||||
|
// Expects a Uint8Array and an index
|
||||||
|
// Returns the little-endian uint32 at v[i..i+3]
|
||||||
|
function B2S_GET32 (v, i) {
|
||||||
|
return v[i] ^ (v[i + 1] << 8) ^ (v[i + 2] << 16) ^ (v[i + 3] << 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mixing function G.
|
||||||
|
function B2S_G (a, b, c, d, x, y) {
|
||||||
|
v[a] = v[a] + v[b] + x
|
||||||
|
v[d] = ROTR32(v[d] ^ v[a], 16)
|
||||||
|
v[c] = v[c] + v[d]
|
||||||
|
v[b] = ROTR32(v[b] ^ v[c], 12)
|
||||||
|
v[a] = v[a] + v[b] + y
|
||||||
|
v[d] = ROTR32(v[d] ^ v[a], 8)
|
||||||
|
v[c] = v[c] + v[d]
|
||||||
|
v[b] = ROTR32(v[b] ^ v[c], 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32-bit right rotation
|
||||||
|
// x should be a uint32
|
||||||
|
// y must be between 1 and 31, inclusive
|
||||||
|
function ROTR32 (x, y) {
|
||||||
|
return (x >>> y) ^ (x << (32 - y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization Vector.
|
||||||
|
const BLAKE2S_IV = new Uint32Array([
|
||||||
|
0x6a09e667,
|
||||||
|
0xbb67ae85,
|
||||||
|
0x3c6ef372,
|
||||||
|
0xa54ff53a,
|
||||||
|
0x510e527f,
|
||||||
|
0x9b05688c,
|
||||||
|
0x1f83d9ab,
|
||||||
|
0x5be0cd19
|
||||||
|
])
|
||||||
|
|
||||||
|
const SIGMA = new Uint8Array([
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
10,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
15,
|
||||||
|
13,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
11,
|
||||||
|
7,
|
||||||
|
5,
|
||||||
|
3,
|
||||||
|
11,
|
||||||
|
8,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
2,
|
||||||
|
15,
|
||||||
|
13,
|
||||||
|
10,
|
||||||
|
14,
|
||||||
|
3,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
4,
|
||||||
|
7,
|
||||||
|
9,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
13,
|
||||||
|
12,
|
||||||
|
11,
|
||||||
|
14,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
7,
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
1,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
6,
|
||||||
|
8,
|
||||||
|
3,
|
||||||
|
13,
|
||||||
|
2,
|
||||||
|
12,
|
||||||
|
6,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
11,
|
||||||
|
8,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
13,
|
||||||
|
7,
|
||||||
|
5,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
12,
|
||||||
|
5,
|
||||||
|
1,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
13,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
7,
|
||||||
|
6,
|
||||||
|
3,
|
||||||
|
9,
|
||||||
|
2,
|
||||||
|
8,
|
||||||
|
11,
|
||||||
|
13,
|
||||||
|
11,
|
||||||
|
7,
|
||||||
|
14,
|
||||||
|
12,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
9,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
6,
|
||||||
|
2,
|
||||||
|
10,
|
||||||
|
6,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
9,
|
||||||
|
11,
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
12,
|
||||||
|
2,
|
||||||
|
13,
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
2,
|
||||||
|
8,
|
||||||
|
4,
|
||||||
|
7,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
15,
|
||||||
|
11,
|
||||||
|
9,
|
||||||
|
14,
|
||||||
|
3,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
0
|
||||||
|
])
|
||||||
|
|
||||||
|
// Compression function. "last" flag indicates last block
|
||||||
|
const v = new Uint32Array(16)
|
||||||
|
const m = new Uint32Array(16)
|
||||||
|
function blake2sCompress (ctx, last) {
|
||||||
|
let i = 0
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
// init work variables
|
||||||
|
v[i] = ctx.h[i]
|
||||||
|
v[i + 8] = BLAKE2S_IV[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
v[12] ^= ctx.t // low 32 bits of offset
|
||||||
|
v[13] ^= ctx.t / 0x100000000 // high 32 bits
|
||||||
|
if (last) {
|
||||||
|
// last block flag set ?
|
||||||
|
v[14] = ~v[14]
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
// get little-endian words
|
||||||
|
m[i] = B2S_GET32(ctx.b, 4 * i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ten rounds of mixing
|
||||||
|
// uncomment the DebugPrint calls to log the computation
|
||||||
|
// and match the RFC sample documentation
|
||||||
|
// util.debugPrint(' m[16]', m, 32)
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
// util.debugPrint(' (i=' + i + ') v[16]', v, 32)
|
||||||
|
B2S_G(0, 4, 8, 12, m[SIGMA[i * 16 + 0]], m[SIGMA[i * 16 + 1]])
|
||||||
|
B2S_G(1, 5, 9, 13, m[SIGMA[i * 16 + 2]], m[SIGMA[i * 16 + 3]])
|
||||||
|
B2S_G(2, 6, 10, 14, m[SIGMA[i * 16 + 4]], m[SIGMA[i * 16 + 5]])
|
||||||
|
B2S_G(3, 7, 11, 15, m[SIGMA[i * 16 + 6]], m[SIGMA[i * 16 + 7]])
|
||||||
|
B2S_G(0, 5, 10, 15, m[SIGMA[i * 16 + 8]], m[SIGMA[i * 16 + 9]])
|
||||||
|
B2S_G(1, 6, 11, 12, m[SIGMA[i * 16 + 10]], m[SIGMA[i * 16 + 11]])
|
||||||
|
B2S_G(2, 7, 8, 13, m[SIGMA[i * 16 + 12]], m[SIGMA[i * 16 + 13]])
|
||||||
|
B2S_G(3, 4, 9, 14, m[SIGMA[i * 16 + 14]], m[SIGMA[i * 16 + 15]])
|
||||||
|
}
|
||||||
|
// util.debugPrint(' (i=10) v[16]', v, 32)
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
ctx.h[i] ^= v[i] ^ v[i + 8]
|
||||||
|
}
|
||||||
|
// util.debugPrint('h[8]', ctx.h, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a BLAKE2s hashing context
|
||||||
|
// Requires an output length between 1 and 32 bytes
|
||||||
|
// Takes an optional Uint8Array key
|
||||||
|
function blake2sInit (outlen, key) {
|
||||||
|
if (!(outlen > 0 && outlen <= 32)) {
|
||||||
|
throw new Error('Incorrect output length, should be in [1, 32]')
|
||||||
|
}
|
||||||
|
const keylen = key ? key.length : 0
|
||||||
|
if (key && !(keylen > 0 && keylen <= 32)) {
|
||||||
|
throw new Error('Incorrect key length, should be in [1, 32]')
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
h: new Uint32Array(BLAKE2S_IV), // hash state
|
||||||
|
b: new Uint8Array(64), // input block
|
||||||
|
c: 0, // pointer within block
|
||||||
|
t: 0, // input count
|
||||||
|
outlen: outlen // output length in bytes
|
||||||
|
}
|
||||||
|
ctx.h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen
|
||||||
|
|
||||||
|
if (keylen > 0) {
|
||||||
|
blake2sUpdate(ctx, key)
|
||||||
|
ctx.c = 64 // at the end
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates a BLAKE2s streaming hash
|
||||||
|
// Requires hash context and Uint8Array (byte array)
|
||||||
|
function blake2sUpdate (ctx, input) {
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
if (ctx.c === 64) {
|
||||||
|
// buffer full ?
|
||||||
|
ctx.t += ctx.c // add counters
|
||||||
|
blake2sCompress(ctx, false) // compress (not last)
|
||||||
|
ctx.c = 0 // counter to zero
|
||||||
|
}
|
||||||
|
ctx.b[ctx.c++] = input[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completes a BLAKE2s streaming hash
|
||||||
|
// Returns a Uint8Array containing the message digest
|
||||||
|
function blake2sFinal (ctx) {
|
||||||
|
ctx.t += ctx.c // mark last block offset
|
||||||
|
while (ctx.c < 64) {
|
||||||
|
// fill up with zeros
|
||||||
|
ctx.b[ctx.c++] = 0
|
||||||
|
}
|
||||||
|
blake2sCompress(ctx, true) // final block flag = 1
|
||||||
|
|
||||||
|
// little endian convert and store
|
||||||
|
const out = new Uint8Array(ctx.outlen)
|
||||||
|
for (let i = 0; i < ctx.outlen; i++) {
|
||||||
|
out[i] = (ctx.h[i >> 2] >> (8 * (i & 3))) & 0xff
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2S hash of a string or byte array, and returns a Uint8Array
|
||||||
|
//
|
||||||
|
// Returns a n-byte Uint8Array
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 32 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
function blake2s (input, key, outlen) {
|
||||||
|
// preprocess inputs
|
||||||
|
outlen = outlen || 32
|
||||||
|
input = util.normalizeInput(input)
|
||||||
|
|
||||||
|
// do the math
|
||||||
|
const ctx = blake2sInit(outlen, key)
|
||||||
|
blake2sUpdate(ctx, input)
|
||||||
|
return blake2sFinal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2S hash of a string or byte array
|
||||||
|
//
|
||||||
|
// Returns an n-byte hash in hex, all lowercase
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 32 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
function blake2sHex (input, key, outlen) {
|
||||||
|
const output = blake2s(input, key, outlen)
|
||||||
|
return util.toHex(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
blake2s: blake2s,
|
||||||
|
blake2sHex: blake2sHex,
|
||||||
|
blake2sInit: blake2sInit,
|
||||||
|
blake2sUpdate: blake2sUpdate,
|
||||||
|
blake2sFinal: blake2sFinal
|
||||||
|
}
|
||||||
Vendored
+96
@@ -0,0 +1,96 @@
|
|||||||
|
export interface Blake2bCTX {
|
||||||
|
b: Uint8Array;
|
||||||
|
h: Uint32Array;
|
||||||
|
t: number;
|
||||||
|
c: number;
|
||||||
|
outlen: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Blake2b hashing context
|
||||||
|
* @param outlen between 1 and 64
|
||||||
|
* @param key optional
|
||||||
|
* @returns the hashing context
|
||||||
|
*/
|
||||||
|
export declare function blake2bInit(outlen?: number, key?: Uint8Array): Blake2bCTX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a Blake2b streaming hash
|
||||||
|
* @param ctx hashing context from blake2bInit()
|
||||||
|
* @param input Byte array
|
||||||
|
*/
|
||||||
|
export declare function blake2bUpdate(ctx: Blake2bCTX, input: ArrayLike<number>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes a Blake2b streaming hash
|
||||||
|
* @param ctx hashing context from blake2bInit()
|
||||||
|
* @returns the final hash
|
||||||
|
*/
|
||||||
|
export declare function blake2bFinal(ctx: Blake2bCTX): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 64 bytes
|
||||||
|
* @param outlen optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte Uint8Array
|
||||||
|
*/
|
||||||
|
export declare function blake2b(input: string | Uint8Array, key?: Uint8Array, outlen?: number): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the Blake2b hash of a string or byte array
|
||||||
|
*
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 64 bytes
|
||||||
|
* @param outlen outlen - optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte hash in hex, all lowercase
|
||||||
|
*/
|
||||||
|
export declare function blake2bHex(input: string | Uint8Array, key?: Uint8Array, outlen?: number): string;
|
||||||
|
|
||||||
|
export interface Blake2sCTX {
|
||||||
|
h: Uint32Array;
|
||||||
|
b: Uint8Array;
|
||||||
|
c: number;
|
||||||
|
t: number;
|
||||||
|
outlen: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Blake2s hashing context
|
||||||
|
* @param outlen between 1 and 32
|
||||||
|
* @param key optional Uint8Array key
|
||||||
|
* @returns the hashing context
|
||||||
|
*/
|
||||||
|
export declare function blake2sInit(outlen: number, key?: Uint8Array): Blake2sCTX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a Blake2s streaming hash
|
||||||
|
* @param ctx hashing context from blake2sinit()
|
||||||
|
* @param input byte array
|
||||||
|
*/
|
||||||
|
export declare function blake2sUpdate(ctx: Blake2sCTX, input: ArrayLike<number>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes a Blake2s streaming hash
|
||||||
|
* @param ctx hashing context from blake2sinit()
|
||||||
|
* @returns Uint8Array containing the message digest
|
||||||
|
*/
|
||||||
|
export declare function blake2sFinal(ctx: Blake2sCTX): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the Blake2s hash of a string or byte array, and returns a Uint8Array
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 32 bytes
|
||||||
|
* @param outlen optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte Uint8Array
|
||||||
|
*/
|
||||||
|
export declare function blake2s(input: string | Uint8Array, key?: Uint8Array, outlen?: number): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 32 bytes
|
||||||
|
* @param outlen optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte hash in hex, all lowercase
|
||||||
|
*/
|
||||||
|
export declare function blake2sHex(input: string | Uint8Array, key?: Uint8Array, outlen?: number): string;
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
const b2b = require('./blake2b')
|
||||||
|
const b2s = require('./blake2s')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
blake2b: b2b.blake2b,
|
||||||
|
blake2bHex: b2b.blake2bHex,
|
||||||
|
blake2bInit: b2b.blake2bInit,
|
||||||
|
blake2bUpdate: b2b.blake2bUpdate,
|
||||||
|
blake2bFinal: b2b.blake2bFinal,
|
||||||
|
blake2s: b2s.blake2s,
|
||||||
|
blake2sHex: b2s.blake2sHex,
|
||||||
|
blake2sInit: b2s.blake2sInit,
|
||||||
|
blake2sUpdate: b2s.blake2sUpdate,
|
||||||
|
blake2sFinal: b2s.blake2sFinal
|
||||||
|
}
|
||||||
Vendored
+84
@@ -0,0 +1,84 @@
|
|||||||
|
const ERROR_MSG_INPUT = 'Input must be an string, Buffer or Uint8Array'
|
||||||
|
|
||||||
|
// For convenience, let people hash a string, not just a Uint8Array
|
||||||
|
function normalizeInput (input) {
|
||||||
|
let ret
|
||||||
|
if (input instanceof Uint8Array) {
|
||||||
|
ret = input
|
||||||
|
} else if (typeof input === 'string') {
|
||||||
|
const encoder = new TextEncoder()
|
||||||
|
ret = encoder.encode(input)
|
||||||
|
} else {
|
||||||
|
throw new Error(ERROR_MSG_INPUT)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a Uint8Array to a hexadecimal string
|
||||||
|
// For example, toHex([255, 0, 255]) returns "ff00ff"
|
||||||
|
function toHex (bytes) {
|
||||||
|
return Array.prototype.map
|
||||||
|
.call(bytes, function (n) {
|
||||||
|
return (n < 16 ? '0' : '') + n.toString(16)
|
||||||
|
})
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts any value in [0...2^32-1] to an 8-character hex string
|
||||||
|
function uint32ToHex (val) {
|
||||||
|
return (0x100000000 + val).toString(16).substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging: prints out hash state in the same format as the RFC
|
||||||
|
// sample computation exactly, so that you can diff
|
||||||
|
function debugPrint (label, arr, size) {
|
||||||
|
let msg = '\n' + label + ' = '
|
||||||
|
for (let i = 0; i < arr.length; i += 2) {
|
||||||
|
if (size === 32) {
|
||||||
|
msg += uint32ToHex(arr[i]).toUpperCase()
|
||||||
|
msg += ' '
|
||||||
|
msg += uint32ToHex(arr[i + 1]).toUpperCase()
|
||||||
|
} else if (size === 64) {
|
||||||
|
msg += uint32ToHex(arr[i + 1]).toUpperCase()
|
||||||
|
msg += uint32ToHex(arr[i]).toUpperCase()
|
||||||
|
} else throw new Error('Invalid size ' + size)
|
||||||
|
if (i % 6 === 4) {
|
||||||
|
msg += '\n' + new Array(label.length + 4).join(' ')
|
||||||
|
} else if (i < arr.length - 2) {
|
||||||
|
msg += ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For performance testing: generates N bytes of input, hashes M times
|
||||||
|
// Measures and prints MB/second hash performance each time
|
||||||
|
function testSpeed (hashFn, N, M) {
|
||||||
|
let startMs = new Date().getTime()
|
||||||
|
|
||||||
|
const input = new Uint8Array(N)
|
||||||
|
for (let i = 0; i < N; i++) {
|
||||||
|
input[i] = i % 256
|
||||||
|
}
|
||||||
|
const genMs = new Date().getTime()
|
||||||
|
console.log('Generated random input in ' + (genMs - startMs) + 'ms')
|
||||||
|
startMs = genMs
|
||||||
|
|
||||||
|
for (let i = 0; i < M; i++) {
|
||||||
|
const hashHex = hashFn(input)
|
||||||
|
const hashMs = new Date().getTime()
|
||||||
|
const ms = hashMs - startMs
|
||||||
|
startMs = hashMs
|
||||||
|
console.log('Hashed in ' + ms + 'ms: ' + hashHex.substring(0, 20) + '...')
|
||||||
|
console.log(
|
||||||
|
Math.round((N / (1 << 20) / (ms / 1000)) * 100) / 100 + ' MB PER SECOND'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
normalizeInput: normalizeInput,
|
||||||
|
toHex: toHex,
|
||||||
|
debugPrint: debugPrint,
|
||||||
|
testSpeed: testSpeed
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{type, external}.
|
||||||
|
{realm, local}.
|
||||||
|
{name, blakejs}.
|
||||||
|
{version, "1.2.1"}.
|
||||||
|
{deps, []}.
|
||||||
+365
@@ -0,0 +1,365 @@
|
|||||||
|
// Blake2B in pure Javascript
|
||||||
|
// Adapted from the reference implementation in RFC7693
|
||||||
|
// Ported to Javascript by DC - https://github.com/dcposch
|
||||||
|
|
||||||
|
const util = require('./util')
|
||||||
|
|
||||||
|
// 64-bit unsigned addition
|
||||||
|
// Sets v[a,a+1] += v[b,b+1]
|
||||||
|
// v should be a Uint32Array
|
||||||
|
function ADD64AA (v, a, b) {
|
||||||
|
const o0 = v[a] + v[b]
|
||||||
|
let o1 = v[a + 1] + v[b + 1]
|
||||||
|
if (o0 >= 0x100000000) {
|
||||||
|
o1++
|
||||||
|
}
|
||||||
|
v[a] = o0
|
||||||
|
v[a + 1] = o1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 64-bit unsigned addition
|
||||||
|
// Sets v[a,a+1] += b
|
||||||
|
// b0 is the low 32 bits of b, b1 represents the high 32 bits
|
||||||
|
function ADD64AC (v, a, b0, b1) {
|
||||||
|
let o0 = v[a] + b0
|
||||||
|
if (b0 < 0) {
|
||||||
|
o0 += 0x100000000
|
||||||
|
}
|
||||||
|
let o1 = v[a + 1] + b1
|
||||||
|
if (o0 >= 0x100000000) {
|
||||||
|
o1++
|
||||||
|
}
|
||||||
|
v[a] = o0
|
||||||
|
v[a + 1] = o1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Little-endian byte access
|
||||||
|
function B2B_GET32 (arr, i) {
|
||||||
|
return arr[i] ^ (arr[i + 1] << 8) ^ (arr[i + 2] << 16) ^ (arr[i + 3] << 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// G Mixing function
|
||||||
|
// The ROTRs are inlined for speed
|
||||||
|
function B2B_G (a, b, c, d, ix, iy) {
|
||||||
|
const x0 = m[ix]
|
||||||
|
const x1 = m[ix + 1]
|
||||||
|
const y0 = m[iy]
|
||||||
|
const y1 = m[iy + 1]
|
||||||
|
|
||||||
|
ADD64AA(v, a, b) // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s
|
||||||
|
ADD64AC(v, a, x0, x1) // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits
|
||||||
|
|
||||||
|
// v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits
|
||||||
|
let xor0 = v[d] ^ v[a]
|
||||||
|
let xor1 = v[d + 1] ^ v[a + 1]
|
||||||
|
v[d] = xor1
|
||||||
|
v[d + 1] = xor0
|
||||||
|
|
||||||
|
ADD64AA(v, c, d)
|
||||||
|
|
||||||
|
// v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits
|
||||||
|
xor0 = v[b] ^ v[c]
|
||||||
|
xor1 = v[b + 1] ^ v[c + 1]
|
||||||
|
v[b] = (xor0 >>> 24) ^ (xor1 << 8)
|
||||||
|
v[b + 1] = (xor1 >>> 24) ^ (xor0 << 8)
|
||||||
|
|
||||||
|
ADD64AA(v, a, b)
|
||||||
|
ADD64AC(v, a, y0, y1)
|
||||||
|
|
||||||
|
// v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits
|
||||||
|
xor0 = v[d] ^ v[a]
|
||||||
|
xor1 = v[d + 1] ^ v[a + 1]
|
||||||
|
v[d] = (xor0 >>> 16) ^ (xor1 << 16)
|
||||||
|
v[d + 1] = (xor1 >>> 16) ^ (xor0 << 16)
|
||||||
|
|
||||||
|
ADD64AA(v, c, d)
|
||||||
|
|
||||||
|
// v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits
|
||||||
|
xor0 = v[b] ^ v[c]
|
||||||
|
xor1 = v[b + 1] ^ v[c + 1]
|
||||||
|
v[b] = (xor1 >>> 31) ^ (xor0 << 1)
|
||||||
|
v[b + 1] = (xor0 >>> 31) ^ (xor1 << 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization Vector
|
||||||
|
const BLAKE2B_IV32 = new Uint32Array([
|
||||||
|
0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372,
|
||||||
|
0x5f1d36f1, 0xa54ff53a, 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c,
|
||||||
|
0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19
|
||||||
|
])
|
||||||
|
|
||||||
|
const SIGMA8 = [
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13,
|
||||||
|
6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1,
|
||||||
|
9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4,
|
||||||
|
10, 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5,
|
||||||
|
15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, 13, 11, 7,
|
||||||
|
14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2,
|
||||||
|
13, 7, 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0,
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6,
|
||||||
|
1, 12, 0, 2, 11, 7, 5, 3
|
||||||
|
]
|
||||||
|
|
||||||
|
// These are offsets into a uint64 buffer.
|
||||||
|
// Multiply them all by 2 to make them offsets into a uint32 buffer,
|
||||||
|
// because this is Javascript and we don't have uint64s
|
||||||
|
const SIGMA82 = new Uint8Array(
|
||||||
|
SIGMA8.map(function (x) {
|
||||||
|
return x * 2
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compression function. 'last' flag indicates last block.
|
||||||
|
// Note we're representing 16 uint64s as 32 uint32s
|
||||||
|
const v = new Uint32Array(32)
|
||||||
|
const m = new Uint32Array(32)
|
||||||
|
function blake2bCompress (ctx, last) {
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
// init work variables
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
v[i] = ctx.h[i]
|
||||||
|
v[i + 16] = BLAKE2B_IV32[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// low 64 bits of offset
|
||||||
|
v[24] = v[24] ^ ctx.t
|
||||||
|
v[25] = v[25] ^ (ctx.t / 0x100000000)
|
||||||
|
// high 64 bits not supported, offset may not be higher than 2**53-1
|
||||||
|
|
||||||
|
// last block flag set ?
|
||||||
|
if (last) {
|
||||||
|
v[28] = ~v[28]
|
||||||
|
v[29] = ~v[29]
|
||||||
|
}
|
||||||
|
|
||||||
|
// get little-endian words
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
m[i] = B2B_GET32(ctx.b, 4 * i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// twelve rounds of mixing
|
||||||
|
// uncomment the DebugPrint calls to log the computation
|
||||||
|
// and match the RFC sample documentation
|
||||||
|
// util.debugPrint(' m[16]', m, 64)
|
||||||
|
for (i = 0; i < 12; i++) {
|
||||||
|
// util.debugPrint(' (i=' + (i < 10 ? ' ' : '') + i + ') v[16]', v, 64)
|
||||||
|
B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1])
|
||||||
|
B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3])
|
||||||
|
B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5])
|
||||||
|
B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7])
|
||||||
|
B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9])
|
||||||
|
B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11])
|
||||||
|
B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13])
|
||||||
|
B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15])
|
||||||
|
}
|
||||||
|
// util.debugPrint(' (i=12) v[16]', v, 64)
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
ctx.h[i] = ctx.h[i] ^ v[i] ^ v[i + 16]
|
||||||
|
}
|
||||||
|
// util.debugPrint('h[8]', ctx.h, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reusable parameterBlock
|
||||||
|
const parameterBlock = new Uint8Array([
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 0: outlen, keylen, fanout, depth
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 4: leaf length, sequential mode
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 8: node offset
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 12: node offset
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 16: node depth, inner length, rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 20: rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 24: rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 28: rfu
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 32: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 36: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 40: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 44: salt
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 48: personal
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 52: personal
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, // 56: personal
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0 // 60: personal
|
||||||
|
])
|
||||||
|
|
||||||
|
// Creates a BLAKE2b hashing context
|
||||||
|
// Requires an output length between 1 and 64 bytes
|
||||||
|
// Takes an optional Uint8Array key
|
||||||
|
// Takes an optinal Uint8Array salt
|
||||||
|
// Takes an optinal Uint8Array personal
|
||||||
|
function blake2bInit (outlen, key, salt, personal) {
|
||||||
|
if (outlen === 0 || outlen > 64) {
|
||||||
|
throw new Error('Illegal output length, expected 0 < length <= 64')
|
||||||
|
}
|
||||||
|
if (key && key.length > 64) {
|
||||||
|
throw new Error('Illegal key, expected Uint8Array with 0 < length <= 64')
|
||||||
|
}
|
||||||
|
if (salt && salt.length !== 16) {
|
||||||
|
throw new Error('Illegal salt, expected Uint8Array with length is 16')
|
||||||
|
}
|
||||||
|
if (personal && personal.length !== 16) {
|
||||||
|
throw new Error('Illegal personal, expected Uint8Array with length is 16')
|
||||||
|
}
|
||||||
|
|
||||||
|
// state, 'param block'
|
||||||
|
const ctx = {
|
||||||
|
b: new Uint8Array(128),
|
||||||
|
h: new Uint32Array(16),
|
||||||
|
t: 0, // input count
|
||||||
|
c: 0, // pointer within buffer
|
||||||
|
outlen: outlen // output length in bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize parameterBlock before usage
|
||||||
|
parameterBlock.fill(0)
|
||||||
|
parameterBlock[0] = outlen
|
||||||
|
if (key) parameterBlock[1] = key.length
|
||||||
|
parameterBlock[2] = 1 // fanout
|
||||||
|
parameterBlock[3] = 1 // depth
|
||||||
|
if (salt) parameterBlock.set(salt, 32)
|
||||||
|
if (personal) parameterBlock.set(personal, 48)
|
||||||
|
|
||||||
|
// initialize hash state
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
ctx.h[i] = BLAKE2B_IV32[i] ^ B2B_GET32(parameterBlock, i * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// key the hash, if applicable
|
||||||
|
if (key) {
|
||||||
|
blake2bUpdate(ctx, key)
|
||||||
|
// at the end
|
||||||
|
ctx.c = 128
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates a BLAKE2b streaming hash
|
||||||
|
// Requires hash context and Uint8Array (byte array)
|
||||||
|
function blake2bUpdate (ctx, input) {
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
if (ctx.c === 128) {
|
||||||
|
// buffer full ?
|
||||||
|
ctx.t += ctx.c // add counters
|
||||||
|
blake2bCompress(ctx, false) // compress (not last)
|
||||||
|
ctx.c = 0 // counter to zero
|
||||||
|
}
|
||||||
|
ctx.b[ctx.c++] = input[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completes a BLAKE2b streaming hash
|
||||||
|
// Returns a Uint8Array containing the message digest
|
||||||
|
function blake2bFinal (ctx) {
|
||||||
|
ctx.t += ctx.c // mark last block offset
|
||||||
|
|
||||||
|
while (ctx.c < 128) {
|
||||||
|
// fill up with zeros
|
||||||
|
ctx.b[ctx.c++] = 0
|
||||||
|
}
|
||||||
|
blake2bCompress(ctx, true) // final block flag = 1
|
||||||
|
|
||||||
|
// little endian convert and store
|
||||||
|
const out = new Uint8Array(ctx.outlen)
|
||||||
|
for (let i = 0; i < ctx.outlen; i++) {
|
||||||
|
out[i] = ctx.h[i >> 2] >> (8 * (i & 3))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2B hash of a string or byte array, and returns a Uint8Array
|
||||||
|
//
|
||||||
|
// Returns a n-byte Uint8Array
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 64 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
// - salt - optional salt bytes, string, Buffer or Uint8Array
|
||||||
|
// - personal - optional personal bytes, string, Buffer or Uint8Array
|
||||||
|
function blake2b (input, key, outlen, salt, personal) {
|
||||||
|
// preprocess inputs
|
||||||
|
outlen = outlen || 64
|
||||||
|
input = util.normalizeInput(input)
|
||||||
|
if (salt) {
|
||||||
|
salt = util.normalizeInput(salt)
|
||||||
|
}
|
||||||
|
if (personal) {
|
||||||
|
personal = util.normalizeInput(personal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the math
|
||||||
|
const ctx = blake2bInit(outlen, key, salt, personal)
|
||||||
|
blake2bUpdate(ctx, input)
|
||||||
|
return blake2bFinal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2B hash of a string or byte array
|
||||||
|
//
|
||||||
|
// Returns an n-byte hash in hex, all lowercase
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 64 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
// - salt - optional salt bytes, string, Buffer or Uint8Array
|
||||||
|
// - personal - optional personal bytes, string, Buffer or Uint8Array
|
||||||
|
function blake2bHex (input, key, outlen, salt, personal) {
|
||||||
|
const output = blake2b(input, key, outlen, salt, personal)
|
||||||
|
return util.toHex(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
blake2b: blake2b,
|
||||||
|
blake2bHex: blake2bHex,
|
||||||
|
blake2bInit: blake2bInit,
|
||||||
|
blake2bUpdate: blake2bUpdate,
|
||||||
|
blake2bFinal: blake2bFinal
|
||||||
|
}
|
||||||
+353
@@ -0,0 +1,353 @@
|
|||||||
|
// BLAKE2s hash function in pure Javascript
|
||||||
|
// Adapted from the reference implementation in RFC7693
|
||||||
|
// Ported to Javascript by DC - https://github.com/dcposch
|
||||||
|
|
||||||
|
const util = require('./util')
|
||||||
|
|
||||||
|
// Little-endian byte access.
|
||||||
|
// Expects a Uint8Array and an index
|
||||||
|
// Returns the little-endian uint32 at v[i..i+3]
|
||||||
|
function B2S_GET32 (v, i) {
|
||||||
|
return v[i] ^ (v[i + 1] << 8) ^ (v[i + 2] << 16) ^ (v[i + 3] << 24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mixing function G.
|
||||||
|
function B2S_G (a, b, c, d, x, y) {
|
||||||
|
v[a] = v[a] + v[b] + x
|
||||||
|
v[d] = ROTR32(v[d] ^ v[a], 16)
|
||||||
|
v[c] = v[c] + v[d]
|
||||||
|
v[b] = ROTR32(v[b] ^ v[c], 12)
|
||||||
|
v[a] = v[a] + v[b] + y
|
||||||
|
v[d] = ROTR32(v[d] ^ v[a], 8)
|
||||||
|
v[c] = v[c] + v[d]
|
||||||
|
v[b] = ROTR32(v[b] ^ v[c], 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32-bit right rotation
|
||||||
|
// x should be a uint32
|
||||||
|
// y must be between 1 and 31, inclusive
|
||||||
|
function ROTR32 (x, y) {
|
||||||
|
return (x >>> y) ^ (x << (32 - y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization Vector.
|
||||||
|
const BLAKE2S_IV = new Uint32Array([
|
||||||
|
0x6a09e667,
|
||||||
|
0xbb67ae85,
|
||||||
|
0x3c6ef372,
|
||||||
|
0xa54ff53a,
|
||||||
|
0x510e527f,
|
||||||
|
0x9b05688c,
|
||||||
|
0x1f83d9ab,
|
||||||
|
0x5be0cd19
|
||||||
|
])
|
||||||
|
|
||||||
|
const SIGMA = new Uint8Array([
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
10,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
15,
|
||||||
|
13,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
11,
|
||||||
|
7,
|
||||||
|
5,
|
||||||
|
3,
|
||||||
|
11,
|
||||||
|
8,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
2,
|
||||||
|
15,
|
||||||
|
13,
|
||||||
|
10,
|
||||||
|
14,
|
||||||
|
3,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
4,
|
||||||
|
7,
|
||||||
|
9,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
13,
|
||||||
|
12,
|
||||||
|
11,
|
||||||
|
14,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
7,
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
1,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
6,
|
||||||
|
8,
|
||||||
|
3,
|
||||||
|
13,
|
||||||
|
2,
|
||||||
|
12,
|
||||||
|
6,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
11,
|
||||||
|
8,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
13,
|
||||||
|
7,
|
||||||
|
5,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
12,
|
||||||
|
5,
|
||||||
|
1,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
13,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
7,
|
||||||
|
6,
|
||||||
|
3,
|
||||||
|
9,
|
||||||
|
2,
|
||||||
|
8,
|
||||||
|
11,
|
||||||
|
13,
|
||||||
|
11,
|
||||||
|
7,
|
||||||
|
14,
|
||||||
|
12,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
9,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
6,
|
||||||
|
2,
|
||||||
|
10,
|
||||||
|
6,
|
||||||
|
15,
|
||||||
|
14,
|
||||||
|
9,
|
||||||
|
11,
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
12,
|
||||||
|
2,
|
||||||
|
13,
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
2,
|
||||||
|
8,
|
||||||
|
4,
|
||||||
|
7,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
15,
|
||||||
|
11,
|
||||||
|
9,
|
||||||
|
14,
|
||||||
|
3,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
0
|
||||||
|
])
|
||||||
|
|
||||||
|
// Compression function. "last" flag indicates last block
|
||||||
|
const v = new Uint32Array(16)
|
||||||
|
const m = new Uint32Array(16)
|
||||||
|
function blake2sCompress (ctx, last) {
|
||||||
|
let i = 0
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
// init work variables
|
||||||
|
v[i] = ctx.h[i]
|
||||||
|
v[i + 8] = BLAKE2S_IV[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
v[12] ^= ctx.t // low 32 bits of offset
|
||||||
|
v[13] ^= ctx.t / 0x100000000 // high 32 bits
|
||||||
|
if (last) {
|
||||||
|
// last block flag set ?
|
||||||
|
v[14] = ~v[14]
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
// get little-endian words
|
||||||
|
m[i] = B2S_GET32(ctx.b, 4 * i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ten rounds of mixing
|
||||||
|
// uncomment the DebugPrint calls to log the computation
|
||||||
|
// and match the RFC sample documentation
|
||||||
|
// util.debugPrint(' m[16]', m, 32)
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
// util.debugPrint(' (i=' + i + ') v[16]', v, 32)
|
||||||
|
B2S_G(0, 4, 8, 12, m[SIGMA[i * 16 + 0]], m[SIGMA[i * 16 + 1]])
|
||||||
|
B2S_G(1, 5, 9, 13, m[SIGMA[i * 16 + 2]], m[SIGMA[i * 16 + 3]])
|
||||||
|
B2S_G(2, 6, 10, 14, m[SIGMA[i * 16 + 4]], m[SIGMA[i * 16 + 5]])
|
||||||
|
B2S_G(3, 7, 11, 15, m[SIGMA[i * 16 + 6]], m[SIGMA[i * 16 + 7]])
|
||||||
|
B2S_G(0, 5, 10, 15, m[SIGMA[i * 16 + 8]], m[SIGMA[i * 16 + 9]])
|
||||||
|
B2S_G(1, 6, 11, 12, m[SIGMA[i * 16 + 10]], m[SIGMA[i * 16 + 11]])
|
||||||
|
B2S_G(2, 7, 8, 13, m[SIGMA[i * 16 + 12]], m[SIGMA[i * 16 + 13]])
|
||||||
|
B2S_G(3, 4, 9, 14, m[SIGMA[i * 16 + 14]], m[SIGMA[i * 16 + 15]])
|
||||||
|
}
|
||||||
|
// util.debugPrint(' (i=10) v[16]', v, 32)
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
ctx.h[i] ^= v[i] ^ v[i + 8]
|
||||||
|
}
|
||||||
|
// util.debugPrint('h[8]', ctx.h, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a BLAKE2s hashing context
|
||||||
|
// Requires an output length between 1 and 32 bytes
|
||||||
|
// Takes an optional Uint8Array key
|
||||||
|
function blake2sInit (outlen, key) {
|
||||||
|
if (!(outlen > 0 && outlen <= 32)) {
|
||||||
|
throw new Error('Incorrect output length, should be in [1, 32]')
|
||||||
|
}
|
||||||
|
const keylen = key ? key.length : 0
|
||||||
|
if (key && !(keylen > 0 && keylen <= 32)) {
|
||||||
|
throw new Error('Incorrect key length, should be in [1, 32]')
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
h: new Uint32Array(BLAKE2S_IV), // hash state
|
||||||
|
b: new Uint8Array(64), // input block
|
||||||
|
c: 0, // pointer within block
|
||||||
|
t: 0, // input count
|
||||||
|
outlen: outlen // output length in bytes
|
||||||
|
}
|
||||||
|
ctx.h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen
|
||||||
|
|
||||||
|
if (keylen > 0) {
|
||||||
|
blake2sUpdate(ctx, key)
|
||||||
|
ctx.c = 64 // at the end
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates a BLAKE2s streaming hash
|
||||||
|
// Requires hash context and Uint8Array (byte array)
|
||||||
|
function blake2sUpdate (ctx, input) {
|
||||||
|
for (let i = 0; i < input.length; i++) {
|
||||||
|
if (ctx.c === 64) {
|
||||||
|
// buffer full ?
|
||||||
|
ctx.t += ctx.c // add counters
|
||||||
|
blake2sCompress(ctx, false) // compress (not last)
|
||||||
|
ctx.c = 0 // counter to zero
|
||||||
|
}
|
||||||
|
ctx.b[ctx.c++] = input[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completes a BLAKE2s streaming hash
|
||||||
|
// Returns a Uint8Array containing the message digest
|
||||||
|
function blake2sFinal (ctx) {
|
||||||
|
ctx.t += ctx.c // mark last block offset
|
||||||
|
while (ctx.c < 64) {
|
||||||
|
// fill up with zeros
|
||||||
|
ctx.b[ctx.c++] = 0
|
||||||
|
}
|
||||||
|
blake2sCompress(ctx, true) // final block flag = 1
|
||||||
|
|
||||||
|
// little endian convert and store
|
||||||
|
const out = new Uint8Array(ctx.outlen)
|
||||||
|
for (let i = 0; i < ctx.outlen; i++) {
|
||||||
|
out[i] = (ctx.h[i >> 2] >> (8 * (i & 3))) & 0xff
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2S hash of a string or byte array, and returns a Uint8Array
|
||||||
|
//
|
||||||
|
// Returns a n-byte Uint8Array
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 32 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
function blake2s (input, key, outlen) {
|
||||||
|
// preprocess inputs
|
||||||
|
outlen = outlen || 32
|
||||||
|
input = util.normalizeInput(input)
|
||||||
|
|
||||||
|
// do the math
|
||||||
|
const ctx = blake2sInit(outlen, key)
|
||||||
|
blake2sUpdate(ctx, input)
|
||||||
|
return blake2sFinal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the BLAKE2S hash of a string or byte array
|
||||||
|
//
|
||||||
|
// Returns an n-byte hash in hex, all lowercase
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - input - the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
// - key - optional key Uint8Array, up to 32 bytes
|
||||||
|
// - outlen - optional output length in bytes, default 64
|
||||||
|
function blake2sHex (input, key, outlen) {
|
||||||
|
const output = blake2s(input, key, outlen)
|
||||||
|
return util.toHex(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
blake2s: blake2s,
|
||||||
|
blake2sHex: blake2sHex,
|
||||||
|
blake2sInit: blake2sInit,
|
||||||
|
blake2sUpdate: blake2sUpdate,
|
||||||
|
blake2sFinal: blake2sFinal
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
export interface Blake2bCTX {
|
||||||
|
b: Uint8Array;
|
||||||
|
h: Uint32Array;
|
||||||
|
t: number;
|
||||||
|
c: number;
|
||||||
|
outlen: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Blake2b hashing context
|
||||||
|
* @param outlen between 1 and 64
|
||||||
|
* @param key optional
|
||||||
|
* @returns the hashing context
|
||||||
|
*/
|
||||||
|
export declare function blake2bInit(outlen?: number, key?: Uint8Array): Blake2bCTX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a Blake2b streaming hash
|
||||||
|
* @param ctx hashing context from blake2bInit()
|
||||||
|
* @param input Byte array
|
||||||
|
*/
|
||||||
|
export declare function blake2bUpdate(ctx: Blake2bCTX, input: ArrayLike<number>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes a Blake2b streaming hash
|
||||||
|
* @param ctx hashing context from blake2bInit()
|
||||||
|
* @returns the final hash
|
||||||
|
*/
|
||||||
|
export declare function blake2bFinal(ctx: Blake2bCTX): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 64 bytes
|
||||||
|
* @param outlen optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte Uint8Array
|
||||||
|
*/
|
||||||
|
export declare function blake2b(input: string | Uint8Array, key?: Uint8Array, outlen?: number): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the Blake2b hash of a string or byte array
|
||||||
|
*
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 64 bytes
|
||||||
|
* @param outlen outlen - optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte hash in hex, all lowercase
|
||||||
|
*/
|
||||||
|
export declare function blake2bHex(input: string | Uint8Array, key?: Uint8Array, outlen?: number): string;
|
||||||
|
|
||||||
|
export interface Blake2sCTX {
|
||||||
|
h: Uint32Array;
|
||||||
|
b: Uint8Array;
|
||||||
|
c: number;
|
||||||
|
t: number;
|
||||||
|
outlen: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Blake2s hashing context
|
||||||
|
* @param outlen between 1 and 32
|
||||||
|
* @param key optional Uint8Array key
|
||||||
|
* @returns the hashing context
|
||||||
|
*/
|
||||||
|
export declare function blake2sInit(outlen: number, key?: Uint8Array): Blake2sCTX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a Blake2s streaming hash
|
||||||
|
* @param ctx hashing context from blake2sinit()
|
||||||
|
* @param input byte array
|
||||||
|
*/
|
||||||
|
export declare function blake2sUpdate(ctx: Blake2sCTX, input: ArrayLike<number>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes a Blake2s streaming hash
|
||||||
|
* @param ctx hashing context from blake2sinit()
|
||||||
|
* @returns Uint8Array containing the message digest
|
||||||
|
*/
|
||||||
|
export declare function blake2sFinal(ctx: Blake2sCTX): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the Blake2s hash of a string or byte array, and returns a Uint8Array
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 32 bytes
|
||||||
|
* @param outlen optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte Uint8Array
|
||||||
|
*/
|
||||||
|
export declare function blake2s(input: string | Uint8Array, key?: Uint8Array, outlen?: number): Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param input the input bytes, as a string, Buffer, or Uint8Array
|
||||||
|
* @param key optional key Uint8Array, up to 32 bytes
|
||||||
|
* @param outlen optional output length in bytes, defaults to 64
|
||||||
|
* @returns an n-byte hash in hex, all lowercase
|
||||||
|
*/
|
||||||
|
export declare function blake2sHex(input: string | Uint8Array, key?: Uint8Array, outlen?: number): string;
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
const b2b = require('./blake2b')
|
||||||
|
const b2s = require('./blake2s')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
blake2b: b2b.blake2b,
|
||||||
|
blake2bHex: b2b.blake2bHex,
|
||||||
|
blake2bInit: b2b.blake2bInit,
|
||||||
|
blake2bUpdate: b2b.blake2bUpdate,
|
||||||
|
blake2bFinal: b2b.blake2bFinal,
|
||||||
|
blake2s: b2s.blake2s,
|
||||||
|
blake2sHex: b2s.blake2sHex,
|
||||||
|
blake2sInit: b2s.blake2sInit,
|
||||||
|
blake2sUpdate: b2s.blake2sUpdate,
|
||||||
|
blake2sFinal: b2s.blake2sFinal
|
||||||
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
const ERROR_MSG_INPUT = 'Input must be an string, Buffer or Uint8Array'
|
||||||
|
|
||||||
|
// For convenience, let people hash a string, not just a Uint8Array
|
||||||
|
function normalizeInput (input) {
|
||||||
|
let ret
|
||||||
|
if (input instanceof Uint8Array) {
|
||||||
|
ret = input
|
||||||
|
} else if (typeof input === 'string') {
|
||||||
|
const encoder = new TextEncoder()
|
||||||
|
ret = encoder.encode(input)
|
||||||
|
} else {
|
||||||
|
throw new Error(ERROR_MSG_INPUT)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts a Uint8Array to a hexadecimal string
|
||||||
|
// For example, toHex([255, 0, 255]) returns "ff00ff"
|
||||||
|
function toHex (bytes) {
|
||||||
|
return Array.prototype.map
|
||||||
|
.call(bytes, function (n) {
|
||||||
|
return (n < 16 ? '0' : '') + n.toString(16)
|
||||||
|
})
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts any value in [0...2^32-1] to an 8-character hex string
|
||||||
|
function uint32ToHex (val) {
|
||||||
|
return (0x100000000 + val).toString(16).substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging: prints out hash state in the same format as the RFC
|
||||||
|
// sample computation exactly, so that you can diff
|
||||||
|
function debugPrint (label, arr, size) {
|
||||||
|
let msg = '\n' + label + ' = '
|
||||||
|
for (let i = 0; i < arr.length; i += 2) {
|
||||||
|
if (size === 32) {
|
||||||
|
msg += uint32ToHex(arr[i]).toUpperCase()
|
||||||
|
msg += ' '
|
||||||
|
msg += uint32ToHex(arr[i + 1]).toUpperCase()
|
||||||
|
} else if (size === 64) {
|
||||||
|
msg += uint32ToHex(arr[i + 1]).toUpperCase()
|
||||||
|
msg += uint32ToHex(arr[i]).toUpperCase()
|
||||||
|
} else throw new Error('Invalid size ' + size)
|
||||||
|
if (i % 6 === 4) {
|
||||||
|
msg += '\n' + new Array(label.length + 4).join(' ')
|
||||||
|
} else if (i < arr.length - 2) {
|
||||||
|
msg += ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For performance testing: generates N bytes of input, hashes M times
|
||||||
|
// Measures and prints MB/second hash performance each time
|
||||||
|
function testSpeed (hashFn, N, M) {
|
||||||
|
let startMs = new Date().getTime()
|
||||||
|
|
||||||
|
const input = new Uint8Array(N)
|
||||||
|
for (let i = 0; i < N; i++) {
|
||||||
|
input[i] = i % 256
|
||||||
|
}
|
||||||
|
const genMs = new Date().getTime()
|
||||||
|
console.log('Generated random input in ' + (genMs - startMs) + 'ms')
|
||||||
|
startMs = genMs
|
||||||
|
|
||||||
|
for (let i = 0; i < M; i++) {
|
||||||
|
const hashHex = hashFn(input)
|
||||||
|
const hashMs = new Date().getTime()
|
||||||
|
const ms = hashMs - startMs
|
||||||
|
startMs = hashMs
|
||||||
|
console.log('Hashed in ' + ms + 'ms: ' + hashHex.substring(0, 20) + '...')
|
||||||
|
console.log(
|
||||||
|
Math.round((N / (1 << 20) / (ms / 1000)) * 100) / 100 + ' MB PER SECOND'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
normalizeInput: normalizeInput,
|
||||||
|
toHex: toHex,
|
||||||
|
debugPrint: debugPrint,
|
||||||
|
testSpeed: testSpeed
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
dist
|
||||||
|
jex_mindist
|
||||||
|
jex_include
|
||||||
|
*.beam
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
test/testgen/b58_cases_2.eterms
|
||||||
|
test/testgen/b58_cases_3.eterms
|
||||||
|
__pycache__
|
||||||
|
docs/
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{type, library}.
|
||||||
|
{realm, local}.
|
||||||
|
{name, vdk_aecrypt}.
|
||||||
|
{version, "0.1.0"}.
|
||||||
|
{deps, ["local-blakejs-1.2.1",
|
||||||
|
"local-vdk_binary-0.1.0"]}.
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
/**
|
||||||
|
* Black boxes away all the AE-specific cryptography routines that call other
|
||||||
|
* libs
|
||||||
|
*
|
||||||
|
* Refs:
|
||||||
|
* 1. https://github.com/aeternity/aepp-sdk-js/blob/5df22dd297abebc0607710793a7234e6761570d4/src/utils/crypto.ts#L141-L
|
||||||
|
* 2. https://github.com/aeternity/aepp-sdk-js/blob/5df22dd297abebc0607710793a7234e6761570d4/src/utils/crypto.ts#L160-L
|
||||||
|
* 3. https://github.com/aeternity/Vanillae/blob/f054744bbceb957afa8fbcf31d93055dd277396b/sidekick/src/sidekick.ts#L926-L1015
|
||||||
|
*
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import * as blakejs from './jex_include/local-blakejs-1.2.1/dist/index.js';
|
||||||
|
import * as vdk_binary from './jex_include/local-vdk_binary-0.1.0/dist/vdk_binary.js';
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
hash_and_salt_msg,
|
||||||
|
salt_msg,
|
||||||
|
btc_varuint_encode
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quoth https://github.com/aeternity/Vanillae/blob/f054744bbceb957afa8fbcf31d93055dd277396b/sidekick/src/sidekick.ts#L926-L1015
|
||||||
|
*
|
||||||
|
* > In order to exclude the possibility of someone using this functionality to
|
||||||
|
* > trick the user into signing a transaction, the wallet salts and hashes the
|
||||||
|
* > message, and *then* signs the salted/hashed message.
|
||||||
|
* >
|
||||||
|
* > Therefore, naively attempting to verify the signature will not work. You
|
||||||
|
* > must apply the same preprocessing steps as the wallet, **THEN** check the
|
||||||
|
* > signature against the salted/hashed message.
|
||||||
|
* >
|
||||||
|
* > ```erlang
|
||||||
|
* > -spec hashed_salted_msg(Message) -> HashedSaltedMessage
|
||||||
|
* > when Message :: binary(),
|
||||||
|
* > HashedSaltedMessage :: binary().
|
||||||
|
* > % @doc Salt the message then hash with blake2b. See:
|
||||||
|
* > % 1. https://github.com/aeternity/aepp-sdk-js/blob/370f1e30064ad0239ba59931908d9aba0a2e86b6/src/utils/crypto.ts#L83-L85
|
||||||
|
* > % 2. https://github.com/aeternity/eblake2/blob/60a079f00d72d1bfcc25de8e6996d28f912db3fd/src/eblake2.erl#L23-L25
|
||||||
|
* >
|
||||||
|
* > hashed_salted_msg(Msg) ->
|
||||||
|
* > {ok, HSMsg} = eblake2:blake2b(32, salted_msg(Msg)),
|
||||||
|
* > HSMsg.
|
||||||
|
* >
|
||||||
|
* >
|
||||||
|
* >
|
||||||
|
* > -spec salted_msg(Message) -> SaltedMessage
|
||||||
|
* > when Message :: binary(),
|
||||||
|
* > SaltedMessage :: binary().
|
||||||
|
* > % @doc Salt the message the way Superhero does before signing.
|
||||||
|
* > %
|
||||||
|
* > % See: https://github.com/aeternity/aepp-sdk-js/blob/370f1e30064ad0239ba59931908d9aba0a2e86b6/src/utils/crypto.ts#L171-L175
|
||||||
|
* >
|
||||||
|
* > salted_msg(Msg) when is_binary(Msg) ->
|
||||||
|
* > P = <<"aeternity Signed Message:\n">>,
|
||||||
|
* > {ok, SP} = btc_varuint_encode(byte_size(P)),
|
||||||
|
* > {ok, SMsg} = btc_varuint_encode(byte_size(Msg)),
|
||||||
|
* > <<SP/binary,
|
||||||
|
* > P/binary,
|
||||||
|
* > SMsg/binary,
|
||||||
|
* > Msg/binary>>.
|
||||||
|
* >
|
||||||
|
* >
|
||||||
|
* >
|
||||||
|
* > -spec btc_varuint_encode(Integer) -> Result
|
||||||
|
* > when Integer :: integer(),
|
||||||
|
* > Result :: {ok, Encoded :: binary()}
|
||||||
|
* > | {error, Reason :: term()}.
|
||||||
|
* > % @doc Bitcoin varuint encode
|
||||||
|
* > %
|
||||||
|
* > % See: https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
|
||||||
|
* >
|
||||||
|
* > btc_varuint_encode(N) when N < 0 ->
|
||||||
|
* > {error, {negative_N, N}};
|
||||||
|
* > btc_varuint_encode(N) when N < 16#FD ->
|
||||||
|
* > {ok, <<N>>};
|
||||||
|
* > btc_varuint_encode(N) when N =< 16#FFFF ->
|
||||||
|
* > NBytes = eu(N, 2),
|
||||||
|
* > {ok, <<16#FD, NBytes/binary>>};
|
||||||
|
* > btc_varuint_encode(N) when N =< 16#FFFF_FFFF ->
|
||||||
|
* > NBytes = eu(N, 4),
|
||||||
|
* > {ok, <<16#FE, NBytes/binary>>};
|
||||||
|
* > btc_varuint_encode(N) when N < (2 bsl 64) ->
|
||||||
|
* > NBytes = eu(N, 8),
|
||||||
|
* > {ok, <<16#FF, NBytes/binary>>}.
|
||||||
|
* >
|
||||||
|
* > % eu = encode unsigned (little endian with a given byte width)
|
||||||
|
* > % means add zero bytes to the end as needed
|
||||||
|
* > eu(N, Size) ->
|
||||||
|
* > Bytes = binary:encode_unsigned(N, little),
|
||||||
|
* > NExtraZeros = Size - byte_size(Bytes),
|
||||||
|
* > ExtraZeros = << <<0>> || _ <- lists:seq(1, NExtraZeros) >>,
|
||||||
|
* > <<Bytes/binary, ExtraZeros/binary>>.
|
||||||
|
* > ```
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
hash_and_salt_msg
|
||||||
|
(message_str : string)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
|
let message_bytes : Uint8Array = vdk_binary.encode_utf8(message_str);
|
||||||
|
return blakejs.blake2b(salt_msg(message_bytes), // bytes to hash
|
||||||
|
undefined, // key (optional)
|
||||||
|
32); // resulting byte length
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* salt the message
|
||||||
|
*
|
||||||
|
* ```erlang
|
||||||
|
* -spec salted_msg(Message) -> SaltedMessage
|
||||||
|
* when Message :: binary(),
|
||||||
|
* SaltedMessage :: binary().
|
||||||
|
* % @doc Salt the message the way Superhero does before signing.
|
||||||
|
* %
|
||||||
|
* % See: https://github.com/aeternity/aepp-sdk-js/blob/370f1e30064ad0239ba59931908d9aba0a2e86b6/src/utils/crypto.ts#L171-L175
|
||||||
|
*
|
||||||
|
* salted_msg(Msg) when is_binary(Msg) ->
|
||||||
|
* P = <<"aeternity Signed Message:\n">>,
|
||||||
|
* {ok, SP} = btc_varuint_encode(byte_size(P)),
|
||||||
|
* {ok, SMsg} = btc_varuint_encode(byte_size(Msg)),
|
||||||
|
* <<SP/binary,
|
||||||
|
* P/binary,
|
||||||
|
* SMsg/binary,
|
||||||
|
* Msg/binary>>.
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
salt_msg
|
||||||
|
(msg_bytes : Uint8Array)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
|
let prefix_str : string = 'aeternity Signed Message:\n';
|
||||||
|
let prefix_bytes : Uint8Array = vdk_binary.encode_utf8(prefix_str);
|
||||||
|
let prefix_size_n : number = prefix_bytes.byteLength;
|
||||||
|
let prefix_size_bytes : Uint8Array = btc_varuint_encode(prefix_size_n);
|
||||||
|
let msg_size_n : number = msg_bytes.byteLength;
|
||||||
|
let msg_size_bytes : Uint8Array = btc_varuint_encode(msg_size_n);
|
||||||
|
|
||||||
|
return vdk_binary.bytes_concat_arr([prefix_size_bytes,
|
||||||
|
prefix_bytes,
|
||||||
|
msg_size_bytes,
|
||||||
|
msg_bytes]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* btc varuint encoding
|
||||||
|
*
|
||||||
|
* function is the following Erlang code translated into TS
|
||||||
|
*
|
||||||
|
* ```erlang
|
||||||
|
* -spec btc_varuint_encode(Integer) -> Result
|
||||||
|
* when Integer :: integer(),
|
||||||
|
* Result :: {ok, Encoded :: binary()}
|
||||||
|
* | {error, Reason :: term()}.
|
||||||
|
* % @doc Bitcoin varuint encode
|
||||||
|
* %
|
||||||
|
* % See: https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
|
||||||
|
*
|
||||||
|
* btc_varuint_encode(N) when N < 0 ->
|
||||||
|
* {error, {negative_N, N}};
|
||||||
|
* btc_varuint_encode(N) when N < 16#FD ->
|
||||||
|
* {ok, <<N>>};
|
||||||
|
* btc_varuint_encode(N) when N =< 16#FFFF ->
|
||||||
|
* NBytes = eu(N, 2),
|
||||||
|
* {ok, <<16#FD, NBytes/binary>>};
|
||||||
|
* btc_varuint_encode(N) when N =< 16#FFFF_FFFF ->
|
||||||
|
* NBytes = eu(N, 4),
|
||||||
|
* {ok, <<16#FE, NBytes/binary>>};
|
||||||
|
* btc_varuint_encode(N) when N < (2 bsl 64) ->
|
||||||
|
* NBytes = eu(N, 8),
|
||||||
|
* {ok, <<16#FF, NBytes/binary>>}.
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
btc_varuint_encode
|
||||||
|
(n : number)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
|
if
|
||||||
|
(n < 0) {
|
||||||
|
throw new Error('n < 0');
|
||||||
|
}
|
||||||
|
else if
|
||||||
|
(n < 0xFD) {
|
||||||
|
return new Uint8Array([n]);
|
||||||
|
}
|
||||||
|
else if
|
||||||
|
(n < 0xFFFF) {
|
||||||
|
let prefix : Uint8Array = new Uint8Array([0xFD]);
|
||||||
|
let n_bytes : Uint8Array = eu(n, 2);
|
||||||
|
return vdk_binary.bytes_concat(prefix, n_bytes);
|
||||||
|
}
|
||||||
|
else if
|
||||||
|
(n < 0xFFFF_FFFF) {
|
||||||
|
let prefix : Uint8Array = new Uint8Array([0xFE]);
|
||||||
|
let n_bytes : Uint8Array = eu(n, 4);
|
||||||
|
return vdk_binary.bytes_concat(prefix, n_bytes);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let prefix : Uint8Array = new Uint8Array([0xFF]);
|
||||||
|
let n_bytes : Uint8Array = eu(n, 8);
|
||||||
|
return vdk_binary.bytes_concat(prefix, n_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unsigned integer little endian encoding with a given byte width
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
eu
|
||||||
|
(n : number,
|
||||||
|
byte_width : number)
|
||||||
|
: Uint8Array
|
||||||
|
{
|
||||||
|
// endianness is byte-level not bit-level
|
||||||
|
// 3> binary:encode_unsigned(258, big).
|
||||||
|
// <<1,2>>
|
||||||
|
// 4> binary:encode_unsigned(258, little).
|
||||||
|
// <<2,1>>
|
||||||
|
|
||||||
|
// first encode n as little endian
|
||||||
|
let n_bytes : Uint8Array = vdk_binary.bigint_to_bytes_little(BigInt(n));
|
||||||
|
// figure out how much padding we need
|
||||||
|
let num_extra_zeros : number = byte_width - n_bytes.byteLength;
|
||||||
|
let extra_zeros : Uint8Array = vdk_binary.bytes_zeros(num_extra_zeros);
|
||||||
|
|
||||||
|
// in little endian, padding 0s go on the right
|
||||||
|
return vdk_binary.bytes_concat(n_bytes, extra_zeros);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{"compilerOptions" : {"target" : "es2022",
|
||||||
|
"strict" : true,
|
||||||
|
"esModuleInterop" : true,
|
||||||
|
"skipLibCheck" : true,
|
||||||
|
"forceConsistentCasingInFileNames" : true,
|
||||||
|
"noImplicitAny" : true,
|
||||||
|
"strictNullChecks" : true,
|
||||||
|
"strictPropertyInitialization" : true,
|
||||||
|
"sourceMap" : true,
|
||||||
|
"outDir" : "dist",
|
||||||
|
"declaration" : true},
|
||||||
|
"$schema" : "https://json.schemastore.org/tsconfig",
|
||||||
|
"display" : "Recommended",
|
||||||
|
"include" : ["src/**/*"],
|
||||||
|
"exclude" : ["src/jex_include"],
|
||||||
|
"composite" : true}
|
||||||
Reference in New Issue
Block a user