зеркало из https://github.com/github/blakejs.git
BLAKE2B works! Only one test vector so far, from the RFC
This commit is contained in:
Родитель
a975d18c5b
Коммит
32c95bc124
38
README.md
38
README.md
|
@ -5,19 +5,37 @@ Pure Javascript implementation of the BLAKE2B hash function.
|
|||
|
||||
```js
|
||||
var Blake = require('blake')
|
||||
console.log(Blake.blake2bHex('hello world'))
|
||||
// prints ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923
|
||||
```
|
||||
|
||||
// prints 00000000000000000000000000000000000000000000000000000000000000
|
||||
console.log(Blake.blake2bStringToHex('hello world'))
|
||||
API
|
||||
---
|
||||
Exports two functions:
|
||||
|
||||
// ascii values for 'hello world'
|
||||
var input = new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])
|
||||
var output = Blake.blake2b(input)
|
||||
var hex = Array.prototype.map.call(output, function(n) {
|
||||
return (n < 16 ? '0' : '') + n.toString(16)
|
||||
}).join('')
|
||||
```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 Uint8Array or ASCII string
|
||||
// - key - optional key, either a 32 or 64-byte Uint8Array
|
||||
// - outlen - optional output length in bytes, default 64
|
||||
function blake2b(input, key, outlen) {
|
||||
[...]
|
||||
}
|
||||
|
||||
// prints the same thing again
|
||||
console.log(hex)
|
||||
// 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 Uint8Array or ASCII string
|
||||
// - outlen - optional output length in bytes, default 64
|
||||
function blake2bHex(input, outlen) {
|
||||
[...]
|
||||
}
|
||||
```
|
||||
|
||||
Limitations
|
||||
|
|
177
blake2b.js
177
blake2b.js
|
@ -1,29 +1,20 @@
|
|||
|
||||
// state context
|
||||
/*
|
||||
typedef struct {
|
||||
uint8_t b[128]; // input buffer
|
||||
uint64_t h[8]; // chained state
|
||||
uint64_t t[2]; // total number of bytes
|
||||
size_t c; // pointer for b[]
|
||||
size_t outlen; // digest size
|
||||
} blake2b_ctx;
|
||||
*/
|
||||
|
||||
// Blake2B in pure Javascript
|
||||
// Adapted from the reference implementation in RFC7693
|
||||
// Ported to Javascript by DC - https://github.com/dcposch
|
||||
|
||||
// Cyclic 64-bit right rotation. x0 is low 32 bits, x1 is high 32 bits
|
||||
// Returns [low 32 bit output, high 32 bit output]
|
||||
function ROTR64(x, y) {
|
||||
var x0 = x[0]
|
||||
var x1 = x[1]
|
||||
// uint64 rotation is NOT efficient if all you have is float64 :(
|
||||
// (JIT compilied to signed int32, but still pretty terrible)
|
||||
var o0 = (y < 32 ? x0 >> y : 0) ^
|
||||
(y > 32 ? x1 >> (y - 32) : 0) ^
|
||||
(y > 0 && y <= 32 ? x1 << (32 - y) : 0) ^
|
||||
(y > 32 ? x0 << (64 - y) : 0)
|
||||
var o1 = (y < 32 ? x1 >> y : 0) ^
|
||||
(y > 32 ? x0 >> (y - 32) : 0) ^
|
||||
// uint64 rotation is NOT efficient when all you have is float64 :(
|
||||
// (it will JIT compile to signed int32, but it's still pretty terrible)
|
||||
var o0 = (y < 32 ? x0 >>> y : 0) ^
|
||||
(y > 32 ? x1 >>> (y - 32) : 0) ^
|
||||
(y > 32 ? x0 << (64 - y) : 0) ^
|
||||
(y > 0 && y <= 32 ? x1 << (32 - y) : 0)
|
||||
var o1 = (y < 32 ? x1 >>> y : 0) ^
|
||||
(y > 32 ? x0 >>> (y - 32) : 0) ^
|
||||
(y > 32 ? x1 << (64 - y) : 0) ^
|
||||
(y > 0 && y <= 32 ? x0 << (32 - y) : 0)
|
||||
return [o0, o1]
|
||||
|
@ -31,29 +22,32 @@ function ROTR64(x, y) {
|
|||
|
||||
// For debugging: prints out a uint64 represented as an array of two int32s
|
||||
// Examples:
|
||||
// HEX64([0x4030201, 0x8070605]) = "0x0807060504030201"
|
||||
// HEX64(ROT64(0x4030201, 0x8070605, 16)) = "0x0201080706050403"
|
||||
// HEX64([0x4030201, 0x8070605]) = '0x0807060504030201'
|
||||
// HEX64(ROT64(0x4030201, 0x8070605, 16)) = '0x0201080706050403'
|
||||
function HEX64(arr) {
|
||||
var s0 = (arr[0] >= 0 ? arr[0] : 0x100000000 + arr[0]).toString(16)
|
||||
var s1 = (arr[1] >= 0 ? arr[1] : 0x100000000 + arr[1]).toString(16)
|
||||
return "0x" +
|
||||
return '0x' +
|
||||
new Array(8 - s1.length+1).join('0') + s1 +
|
||||
new Array(8 - s0.length+1).join('0') + s0
|
||||
}
|
||||
|
||||
// Little-endian byte access.
|
||||
/*
|
||||
#define B2B_GET64(p) \
|
||||
(((uint64_t) ((uint8_t *) (p))[0]) ^ \
|
||||
(((uint64_t) ((uint8_t *) (p))[1]) << 8) ^ \
|
||||
(((uint64_t) ((uint8_t *) (p))[2]) << 16) ^ \
|
||||
(((uint64_t) ((uint8_t *) (p))[3]) << 24) ^ \
|
||||
(((uint64_t) ((uint8_t *) (p))[4]) << 32) ^ \
|
||||
(((uint64_t) ((uint8_t *) (p))[5]) << 40) ^ \
|
||||
(((uint64_t) ((uint8_t *) (p))[6]) << 48) ^ \
|
||||
(((uint64_t) ((uint8_t *) (p))[7]) << 56))
|
||||
*/
|
||||
// For debugging: prints out hash state in the same format as the RFC
|
||||
// sample computation
|
||||
function DebugPrint(label, arr) {
|
||||
var msg = '\n' + label + ' = '
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
msg += HEX64(arr[i]).substring(2).toUpperCase()
|
||||
if (i % 3 === 2) {
|
||||
msg += '\n' + new Array(label.length + 4).join(' ')
|
||||
} else if (i < arr.length - 1) {
|
||||
msg += ' '
|
||||
}
|
||||
}
|
||||
console.log(msg)
|
||||
}
|
||||
|
||||
// Little-endian byte access
|
||||
function B2B_GET32(arr, i) {
|
||||
return arr[i] ^
|
||||
(arr[i+1]<<8) ^
|
||||
|
@ -61,10 +55,8 @@ function B2B_GET32(arr, i) {
|
|||
(arr[i+3]<<24)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two little endian uint64s, arr[a,a+1], arr[b,b+1], stores in arr[x,x+1]
|
||||
* Expects arr to be a Uint32Array
|
||||
*/
|
||||
//Adds two little endian uint64s, arr[a,a+1], arr[b,b+1], stores in arr[x,x+1]
|
||||
//Expects arr to be a Uint32Array
|
||||
function AADD64(arr, a, b, x) {
|
||||
var o0 = arr[a] + arr[b]
|
||||
var o1 = arr[a+1] + arr[b+1]
|
||||
|
@ -75,10 +67,9 @@ function AADD64(arr, a, b, x) {
|
|||
arr[x+1] = o1
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a small integer b to a little endian uint64 arr[a,a+1], stores in arr[x,x+1]
|
||||
* Expects arr to be a Uint32Array
|
||||
*/
|
||||
//Adds a small integer b to a little endian uint64 arr[a,a+1], stores in
|
||||
//arr[x,x+1]
|
||||
//Expects arr to be a Uint32Array
|
||||
function AADD64S(arr, a, b, x) {
|
||||
var o0 = arr[a] + b
|
||||
var o1 = arr[a+1]
|
||||
|
@ -89,10 +80,23 @@ function AADD64S(arr, a, b, x) {
|
|||
arr[x+1] = o1
|
||||
}
|
||||
|
||||
// Javascript is just disgusting...
|
||||
// var arr = new Uint32Array(10)
|
||||
// arr[0] = 0xffffffff
|
||||
// console.log(arr[0]) // prints 4 billion, all good
|
||||
// console.log(arr[0] | 0) // prints -1
|
||||
//
|
||||
// So when working with 64-bit #s, the two 32-bit halves are signed int32s
|
||||
function ADD64(a, b) {
|
||||
var o0 = a[0] + b[0]
|
||||
if(a[0] < 0) {
|
||||
o0 += 0x100000000
|
||||
}
|
||||
if(b[0] < 0) {
|
||||
o0 += 0x100000000
|
||||
}
|
||||
var o1 = a[1] + b[1]
|
||||
if (a[0] + b[0] >= 0x100000000) {
|
||||
if (o0 >= 0x100000000) {
|
||||
o1++
|
||||
}
|
||||
return new Uint32Array([o0, o1])
|
||||
|
@ -145,7 +149,7 @@ var SIGMA8 = new Uint8Array([
|
|||
])
|
||||
|
||||
|
||||
// Compression function. "last" flag indicates last block.
|
||||
// Compression function. 'last' flag indicates last block.
|
||||
function blake2b_compress(ctx, last) {
|
||||
var i = 0
|
||||
var v = new Array(16)
|
||||
|
@ -158,13 +162,12 @@ function blake2b_compress(ctx, last) {
|
|||
}
|
||||
|
||||
// low 64 bits of offset
|
||||
v[12] = XOR64(v[12], ctx.t)
|
||||
v[12] = XOR64(v[12], [ctx.t, 0])
|
||||
// high 64 bits
|
||||
//v[13] = XOR64(v[13], ctx.t[1])
|
||||
// last block flag set ?
|
||||
if (last) {
|
||||
v[14][0] = ~v[14][0]
|
||||
v[14][1] = ~v[14][1]
|
||||
v[14] = [~v[14][0], ~v[14][1]]
|
||||
}
|
||||
|
||||
// get little-endian words
|
||||
|
@ -176,23 +179,29 @@ function blake2b_compress(ctx, last) {
|
|||
}
|
||||
|
||||
// twelve rounds of mixing
|
||||
// uncomment the DebugPrint calls to log the computation
|
||||
// and match the RFC sample documentation
|
||||
// DebugPrint(' m[16]', m)
|
||||
for (i = 0; i < 12; i++) {
|
||||
B2B_G(v, 0, 4, 8, 12, m[SIGMA8[i*16 + 0]], m[SIGMA8[i*16 + 1]]);
|
||||
B2B_G(v, 1, 5, 9, 13, m[SIGMA8[i*16 + 2]], m[SIGMA8[i*16 + 3]]);
|
||||
B2B_G(v, 2, 6, 10, 14, m[SIGMA8[i*16 + 4]], m[SIGMA8[i*16 + 5]]);
|
||||
B2B_G(v, 3, 7, 11, 15, m[SIGMA8[i*16 + 6]], m[SIGMA8[i*16 + 7]]);
|
||||
B2B_G(v, 0, 5, 10, 15, m[SIGMA8[i*16 + 8]], m[SIGMA8[i*16 + 9]]);
|
||||
B2B_G(v, 1, 6, 11, 12, m[SIGMA8[i*16 +10]], m[SIGMA8[i*16 +11]]);
|
||||
B2B_G(v, 2, 7, 8, 13, m[SIGMA8[i*16 +12]], m[SIGMA8[i*16 +13]]);
|
||||
B2B_G(v, 3, 4, 9, 14, m[SIGMA8[i*16 +14]], m[SIGMA8[i*16 +15]]);
|
||||
// DebugPrint(' (i='+(i<10?' ':'')+i+') v[16]', v)
|
||||
B2B_G(v, 0, 4, 8, 12, m[SIGMA8[i*16 + 0]], m[SIGMA8[i*16 + 1]])
|
||||
B2B_G(v, 1, 5, 9, 13, m[SIGMA8[i*16 + 2]], m[SIGMA8[i*16 + 3]])
|
||||
B2B_G(v, 2, 6, 10, 14, m[SIGMA8[i*16 + 4]], m[SIGMA8[i*16 + 5]])
|
||||
B2B_G(v, 3, 7, 11, 15, m[SIGMA8[i*16 + 6]], m[SIGMA8[i*16 + 7]])
|
||||
B2B_G(v, 0, 5, 10, 15, m[SIGMA8[i*16 + 8]], m[SIGMA8[i*16 + 9]])
|
||||
B2B_G(v, 1, 6, 11, 12, m[SIGMA8[i*16 +10]], m[SIGMA8[i*16 +11]])
|
||||
B2B_G(v, 2, 7, 8, 13, m[SIGMA8[i*16 +12]], m[SIGMA8[i*16 +13]])
|
||||
B2B_G(v, 3, 4, 9, 14, m[SIGMA8[i*16 +14]], m[SIGMA8[i*16 +15]])
|
||||
}
|
||||
// DebugPrint(' (i=12) v[16]', v)
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
ctx.h[i] = XOR64(ctx.h[i], XOR64(v[i], v[i + 8]))
|
||||
}
|
||||
// DebugPrint('h[8]', ctx.h)
|
||||
}
|
||||
|
||||
// Initialize the hashing context with optional key "key"
|
||||
// Initialize the hashing context with optional key 'key'
|
||||
// Returns a newly created context
|
||||
function blake2b_init(outlen, key) {
|
||||
if (outlen == 0 || outlen > 64) {
|
||||
|
@ -202,7 +211,7 @@ function blake2b_init(outlen, key) {
|
|||
throw new Error('Illegal key, expected Uint8Array with 0 < length <= 64')
|
||||
}
|
||||
|
||||
// state, "param block"
|
||||
// state, 'param block'
|
||||
var ctx = {
|
||||
b: new Uint8Array(128),
|
||||
h: new Array(8),
|
||||
|
@ -228,7 +237,7 @@ function blake2b_init(outlen, key) {
|
|||
return ctx
|
||||
}
|
||||
|
||||
// Add "in" into the hash. Expects a Uint8Array
|
||||
// Add 'in' into the hash. Expects a Uint8Array
|
||||
function blake2b_update(ctx, input) {
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (ctx.c === 128) { // buffer full ?
|
||||
|
@ -253,30 +262,48 @@ function blake2b_final(ctx) {
|
|||
// little endian convert and store
|
||||
var out = new Uint8Array(ctx.outlen)
|
||||
for (var i = 0; i < ctx.outlen; i++) {
|
||||
out[i] = (ctx.h[i >> 3] >> (8 * (i & 7))) & 0xFF;
|
||||
out[i] = (ctx.h[i >> 3][(i >> 2) & 1] >> (8 * (i & 3))) & 0xFF;
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Convenience function for all-in-one computation.
|
||||
// Returns a outlen-byte Uint8Array
|
||||
function blake2b(outlen, key, input) {
|
||||
// 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 Uint8Array or ASCII string
|
||||
// - key - optional key, either a 32 or 64-byte Uint8Array
|
||||
// - outlen - optional output length in bytes, default 64
|
||||
function blake2b(input, key, outlen) {
|
||||
// preprocess inputs
|
||||
outlen = outlen || 64
|
||||
if (typeof(input) === 'string') {
|
||||
var str = input
|
||||
input = new Uint8Array(str.length)
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
if (str.charCodeAt(i) > 255) {
|
||||
throw new Error('Input must be an ASCII string or Uint8Array')
|
||||
}
|
||||
input[i] = str.charCodeAt(i)
|
||||
}
|
||||
}
|
||||
|
||||
// do the math
|
||||
var ctx = blake2b_init(outlen, key)
|
||||
blake2b_update(ctx, input)
|
||||
return blake2b_final(ctx)
|
||||
}
|
||||
|
||||
// More convenient convenience function
|
||||
// Input string must be ASCII. Returns 32 byte hex digest.
|
||||
function blake2bStringToHex(str, outlen) {
|
||||
var input = new Uint8Array(str.length)
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
if (str.charCodeAt(i) > 255) {
|
||||
throw new Error('Input string must be ASCII')
|
||||
}
|
||||
input[i] = str.charCodeAt(i)
|
||||
}
|
||||
var output = blake2b(32, null, input)
|
||||
// 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 Uint8Array or ASCII string
|
||||
// - outlen - optional output length in bytes, default 64
|
||||
function blake2bHex(input, outlen) {
|
||||
var output = blake2b(input, null, outlen)
|
||||
return Array.prototype.map.call(output, function(n) {
|
||||
return (n < 16 ? '0' : '') + n.toString(16)
|
||||
}).join('')
|
||||
|
@ -284,5 +311,5 @@ function blake2bStringToHex(str, outlen) {
|
|||
|
||||
module.exports = {
|
||||
blake2b: blake2b,
|
||||
blake2bStringToHex: blake2bStringToHex
|
||||
blake2bHex: blake2bHex
|
||||
}
|
||||
|
|
|
@ -1,20 +1,34 @@
|
|||
|
||||
function testROTR64() {
|
||||
assertEquals('0x0807060504030201', HEX64(ROTR64([0x4030201, 0x8070605], 0)))
|
||||
assertEquals('0x0201080706050403', HEX64(ROTR64([0x4030201, 0x8070605], 16)))
|
||||
assertEquals('0x4030201080706050', HEX64(ROTR64([0x4030201, 0x8070605], 28)))
|
||||
assertEquals('0x1080706050403020', HEX64(ROTR64([0x4030201, 0x8070605], 4)))
|
||||
assertEquals('0x0403020108070605', HEX64(ROTR64([0x4030201, 0x8070605], 32)))
|
||||
assertEquals('0x8070605040302010', HEX64(ROTR64([0x4030201, 0x8070605], 60)))
|
||||
assertEquals('0x7772fc2886a76c5f', HEX64(ROTR64([0x6c5f7772, 0xfc2886a7], 16)))
|
||||
}
|
||||
|
||||
function testADD64() {
|
||||
var ret = ADD64([0xffffffff, 1], [1, 0])
|
||||
assertEquals(0, ret[0])
|
||||
assertEquals(2, ret[1])
|
||||
assertEquals('0x0000000200000000', HEX64(ADD64([0xffffffff, 1], [1, 0])))
|
||||
assertEquals('0x00000002fffffffe', HEX64(ADD64([0xffffffff, 1], [0xffffffff, 0])))
|
||||
assertEquals('0x76eb1310ddd333a0', HEX64(ADD64([0xF3BCC908, 0x6A09E667], [0xEA166A98, 0x0CE12CA8])))
|
||||
assertEquals('0x76eb1310ddd333a0', HEX64(ADD64([-205731576, 1779033703], [-367629672, 216083624])))
|
||||
}
|
||||
|
||||
function testXOR64() {
|
||||
assertEquals('0x0000000600000002', HEX64(XOR64([1, 2], [3, 4])))
|
||||
assertEquals('0x1234567812121212', HEX64(XOR64([0x10101010, 0x12005600], [0x02020202, 0x00340078])))
|
||||
}
|
||||
|
||||
function testBLAKE2B() {
|
||||
assertEquals('todo', blake2bStringToHex('hello'))
|
||||
// From the example computation in the RFC
|
||||
assertEquals('ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1' +
|
||||
'7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923',
|
||||
blake2bHex('abc'))
|
||||
assertEquals('ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1' +
|
||||
'7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923',
|
||||
blake2bHex(new Uint8Array([97,98,99])))
|
||||
}
|
||||
|
||||
function assertEquals(expected, actual) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче