зеркало из https://github.com/github/blakejs.git
Added README. BLAKE2B close. No test vectors yet.
This commit is contained in:
Родитель
cb0acc0d65
Коммит
a975d18c5b
|
@ -0,0 +1,36 @@
|
|||
BLAKE.js
|
||||
====
|
||||
|
||||
Pure Javascript implementation of the BLAKE2B hash function.
|
||||
|
||||
```js
|
||||
var Blake = require('blake')
|
||||
|
||||
// prints 00000000000000000000000000000000000000000000000000000000000000
|
||||
console.log(Blake.blake2bStringToHex('hello world'))
|
||||
|
||||
// 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('')
|
||||
|
||||
// prints the same thing again
|
||||
console.log(hex)
|
||||
```
|
||||
|
||||
Limitations
|
||||
---
|
||||
* Can only handle up to 2**53 bytes of input
|
||||
|
||||
Performance
|
||||
---
|
||||
```
|
||||
¯\_(ツ)_/¯
|
||||
```
|
||||
|
||||
License
|
||||
---
|
||||
Creative Commons CC0. Ported from the reference C implementation in
|
||||
[RFC 7693](https://tools.ietf.org/html/rfc7693).
|
273
blake2b.js
273
blake2b.js
|
@ -13,7 +13,9 @@ typedef struct {
|
|||
|
||||
// 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(x0, x1, y) {
|
||||
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) ^
|
||||
|
@ -52,11 +54,18 @@ function HEX64(arr) {
|
|||
(((uint64_t) ((uint8_t *) (p))[7]) << 56))
|
||||
*/
|
||||
|
||||
function B2B_GET32(arr, i) {
|
||||
return arr[i] ^
|
||||
(arr[i+1]<<8) ^
|
||||
(arr[i+2]<<16) ^
|
||||
(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
|
||||
*/
|
||||
function ADD64(arr, a, b, x) {
|
||||
function AADD64(arr, a, b, x) {
|
||||
var o0 = arr[a] + arr[b]
|
||||
var o1 = arr[a+1] + arr[b+1]
|
||||
if (arr[a] + arr[b] >= 0x100000000) {
|
||||
|
@ -70,28 +79,41 @@ function ADD64(arr, a, b, x) {
|
|||
* 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 ADD64S(arr, a, b, x) {
|
||||
arr[x] = arr[a] + b
|
||||
arr[x+1] = arr[a+1] + ((arr[a] + b) >> 32)
|
||||
function AADD64S(arr, a, b, x) {
|
||||
var o0 = arr[a] + b
|
||||
var o1 = arr[a+1]
|
||||
if (arr[a] + b >= 0x100000000) {
|
||||
o1++
|
||||
}
|
||||
arr[x] = o0
|
||||
arr[x+1] = o1
|
||||
}
|
||||
|
||||
function ADD64(a, b) {
|
||||
var o0 = a[0] + b[0]
|
||||
var o1 = a[1] + b[1]
|
||||
if (a[0] + b[0] >= 0x100000000) {
|
||||
o1++
|
||||
}
|
||||
return new Uint32Array([o0, o1])
|
||||
}
|
||||
|
||||
function XOR64(a, b) {
|
||||
var o0 = a[0] ^ b[0]
|
||||
var o1 = a[1] ^ b[1]
|
||||
return new Uint32Array([o0, o1])
|
||||
}
|
||||
|
||||
// G Mixing function.
|
||||
function B2B_G(v, a, b, c, d, x, y) {
|
||||
ADD64(v, a, b, a)
|
||||
ADD64S(v, a, x, a)
|
||||
|
||||
/*
|
||||
v[a] = ADD64(ADD64(v[a], v[b]), x)
|
||||
v[d] = ROT64(XOR64(v[d], v[a]), 32)
|
||||
v[d] = ROTR64(XOR64(v[d], v[a]), 32)
|
||||
v[c] = ADD64(v[c], v[d])
|
||||
v[b] = ROT64(XOR64(v[b], v[c]), 24)
|
||||
v[b] = ROTR64(XOR64(v[b], v[c]), 24)
|
||||
v[a] = ADD64(ADD64(v[a], v[b]), y)
|
||||
v[d] = ROT64(XOR64(v[d], v[a]), 16)
|
||||
v[d] = ROTR64(XOR64(v[d], v[a]), 16)
|
||||
v[c] = ADD64(v[c], v[d])
|
||||
v[b] = ROT64(XOR64(v[b], v[c]), 63)
|
||||
*/
|
||||
v[b] = ROTR64(XOR64(v[b], v[c]), 63)
|
||||
}
|
||||
|
||||
// Initialization Vector.
|
||||
|
@ -102,6 +124,11 @@ var BLAKE2B_IV32 = new Uint32Array([
|
|||
0x1F83D9AB, 0xFB41BD6B, 0x5BE0CD19, 0x137E2179
|
||||
])
|
||||
|
||||
var BLAKE2B_IV64 = []
|
||||
for(var i = 0; i < BLAKE2B_IV32.length; i += 2) {
|
||||
BLAKE2B_IV64.push([BLAKE2B_IV32[i+1], BLAKE2B_IV32[i]])
|
||||
}
|
||||
|
||||
var SIGMA8 = 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,
|
||||
|
@ -117,127 +144,145 @@ var SIGMA8 = new Uint8Array([
|
|||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
||||
])
|
||||
|
||||
/*
|
||||
|
||||
// Compression function. "last" flag indicates last block.
|
||||
function blake2b_compress(ctx, last) {
|
||||
var i = 0
|
||||
//uint64_t v[16], m[16];
|
||||
var v = new Uint32Array(32)
|
||||
var m = new Uint32Array(16)
|
||||
var i = 0
|
||||
var v = new Array(16)
|
||||
var m = new Array(16)
|
||||
|
||||
for (i = 0; i < 8; i++) { // init work variables
|
||||
v[i] = ctx->h[i];
|
||||
v[i + 8] = blake2b_iv[i];
|
||||
}
|
||||
// init work variables
|
||||
for (i = 0; i < 8; i++) {
|
||||
v[i] = ctx.h[i]
|
||||
v[i + 8] = BLAKE2B_IV64[i];
|
||||
}
|
||||
|
||||
v[12] ^= ctx->t[0]; // low 64 bits of offset
|
||||
v[13] ^= ctx->t[1]; // high 64 bits
|
||||
if (last) // last block flag set ?
|
||||
v[14] = ~v[14];
|
||||
// low 64 bits of offset
|
||||
v[12] = XOR64(v[12], ctx.t)
|
||||
// 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]
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) // get little-endian words
|
||||
m[i] = B2B_GET64(&ctx->b[8 * i]);
|
||||
// get little-endian words
|
||||
for (i = 0; i < 16; i++) {
|
||||
m[i] = [
|
||||
B2B_GET32(ctx.b, 8 * i),
|
||||
B2B_GET32(ctx.b, 8 * i + 4)
|
||||
]
|
||||
}
|
||||
|
||||
for (i = 0; i < 12; i++) { // twelve rounds
|
||||
B2B_G( 0, 4, 8, 12, m[sigma[i<<4 + 0]], m[sigma[i<<4 + 1]]);
|
||||
B2B_G( 1, 5, 9, 13, m[sigma[i<<4 + 2]], m[sigma[i<<4 + 3]]);
|
||||
B2B_G( 2, 6, 10, 14, m[sigma[i<<4 + 4]], m[sigma[i<<4 + 5]]);
|
||||
B2B_G( 3, 7, 11, 15, m[sigma[i<<4 + 6]], m[sigma[i<<4 + 7]]);
|
||||
B2B_G( 0, 5, 10, 15, m[sigma[i<<4 + 8]], m[sigma[i<<4 + 9]]);
|
||||
B2B_G( 1, 6, 11, 12, m[sigma[i<<4 +10]], m[sigma[i<<4 +11]]);
|
||||
B2B_G( 2, 7, 8, 13, m[sigma[i<<4 +12]], m[sigma[i<<4 +13]]);
|
||||
B2B_G( 3, 4, 9, 14, m[sigma[i<<4 +14]], m[sigma[i<<4 +15]]);
|
||||
}
|
||||
// twelve rounds of mixing
|
||||
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]]);
|
||||
}
|
||||
|
||||
for( i = 0; i < 8; ++i )
|
||||
ctx->h[i] ^= v[i] ^ v[i + 8];
|
||||
for (i = 0; i < 8; i++) {
|
||||
ctx.h[i] = XOR64(ctx.h[i], XOR64(v[i], v[i + 8]))
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the hashing context "ctx" with optional key "key".
|
||||
// 1 <= outlen <= 64 gives the digest size in bytes.
|
||||
// Secret key (also <= 64 bytes) is optional (keylen = 0).
|
||||
// Initialize the hashing context with optional key "key"
|
||||
// Returns a newly created context
|
||||
function blake2b_init(outlen, key) {
|
||||
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')
|
||||
}
|
||||
|
||||
int blake2b_init(blake2b_ctx *ctx, size_t outlen,
|
||||
const void *key, size_t keylen) // (keylen=0: no key)
|
||||
{
|
||||
size_t i;
|
||||
// state, "param block"
|
||||
var ctx = {
|
||||
b: new Uint8Array(128),
|
||||
h: new Array(8),
|
||||
t: 0, // input count
|
||||
c: 0, // pointer within buffer
|
||||
outlen: outlen // output length in bytes
|
||||
}
|
||||
|
||||
if (outlen == 0 || outlen > 64 || keylen > 64)
|
||||
return -1; // illegal parameters
|
||||
// initialize hash state
|
||||
for (var i = 0; i < 8; i++) {
|
||||
ctx.h[i] = BLAKE2B_IV64[i]
|
||||
}
|
||||
var keylen = key ? key.length : 0
|
||||
ctx.h[0] = XOR64(ctx.h[0], [0x01010000 ^ (keylen << 8) ^ outlen, 0])
|
||||
|
||||
for (i = 0; i < 8; i++) // state, "param block"
|
||||
ctx->h[i] = blake2b_iv[i];
|
||||
ctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;
|
||||
// key the hash, if applicable
|
||||
if (key) {
|
||||
blake2b_update(ctx, key)
|
||||
// at the end
|
||||
ctx.c = 128
|
||||
}
|
||||
|
||||
ctx->t[0] = 0; // input count low word
|
||||
ctx->t[1] = 0; // input count high word
|
||||
ctx->c = 0; // pointer within buffer
|
||||
ctx->outlen = outlen;
|
||||
|
||||
for (i = keylen; i < 128; i++) // zero input block
|
||||
ctx->b[i] = 0;
|
||||
if (keylen > 0) {
|
||||
blake2b_update(ctx, key, keylen);
|
||||
ctx->c = 128; // at the end
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Add "inlen" bytes from "in" into the hash.
|
||||
|
||||
void blake2b_update(blake2b_ctx *ctx,
|
||||
const void *in, size_t inlen) // data bytes
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < inlen; i++) {
|
||||
if (ctx->c == 128) { // buffer full ?
|
||||
ctx->t[0] += ctx->c; // add counters
|
||||
if (ctx->t[0] < ctx->c) // carry overflow ?
|
||||
ctx->t[1]++; // high word
|
||||
blake2b_compress(ctx, 0); // compress (not last)
|
||||
ctx->c = 0; // counter to zero
|
||||
}
|
||||
ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
|
||||
// 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 ?
|
||||
ctx.t += ctx.c // add counters
|
||||
blake2b_compress(ctx, false) // compress (not last)
|
||||
ctx.c = 0 // counter to zero
|
||||
}
|
||||
ctx.b[ctx.c++] = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the message digest (size given in init).
|
||||
// Result placed in "out".
|
||||
// Generate the message digest.
|
||||
// Returns a Uint8Array
|
||||
function blake2b_final(ctx) {
|
||||
ctx.t += ctx.c // mark last block offset
|
||||
|
||||
void blake2b_final(blake2b_ctx *ctx, void *out)
|
||||
{
|
||||
size_t i;
|
||||
while (ctx.c < 128) { // fill up with zeros
|
||||
ctx.b[ctx.c++] = 0
|
||||
}
|
||||
blake2b_compress(ctx, true) // final block flag = 1
|
||||
|
||||
ctx->t[0] += ctx->c; // mark last block offset
|
||||
if (ctx->t[0] < ctx->c) // carry overflow
|
||||
ctx->t[1]++; // high word
|
||||
|
||||
while (ctx->c < 128) // fill up with zeros
|
||||
ctx->b[ctx->c++] = 0;
|
||||
blake2b_compress(ctx, 1); // final block flag = 1
|
||||
|
||||
// little endian convert and store
|
||||
for (i = 0; i < ctx->outlen; i++) {
|
||||
((uint8_t *) out)[i] =
|
||||
(ctx->h[i >> 3] >> (8 * (i & 7))) & 0xFF;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Convenience function for all-in-one computation.
|
||||
|
||||
int blake2b(void *out, size_t outlen,
|
||||
const void *key, size_t keylen,
|
||||
const void *in, size_t inlen)
|
||||
{
|
||||
blake2b_ctx ctx;
|
||||
|
||||
if (blake2b_init(&ctx, outlen, key, keylen))
|
||||
return -1;
|
||||
blake2b_update(&ctx, in, inlen);
|
||||
blake2b_final(&ctx, out);
|
||||
|
||||
return 0;
|
||||
// Returns a outlen-byte Uint8Array
|
||||
function blake2b(outlen, key, input) {
|
||||
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)
|
||||
return Array.prototype.map.call(output, function(n) {
|
||||
return (n < 16 ? '0' : '') + n.toString(16)
|
||||
}).join('')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
blake2b: blake2b,
|
||||
blake2bStringToHex: blake2bStringToHex
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "blakejs",
|
||||
"version": "0.0.1",
|
||||
"description": "Pure Javascript implementation of the BLAKE2B hash function",
|
||||
"main": "blake2b.js",
|
||||
"scripts": {
|
||||
"test": "cat blake2b.js test_blake2b.js | node"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/dcposch/blakejs.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/dcposch/blakejs/issues"
|
||||
},
|
||||
"homepage": "https://github.com/dcposch/blakejs#readme"
|
||||
}
|
|
@ -1,21 +1,20 @@
|
|||
|
||||
function testROTR64() {
|
||||
assertEquals('0x0807060504030201', HEX64(ROTR64(0x4030201, 0x8070605, 0)))
|
||||
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('0x0807060504030201', HEX64(ROTR64([0x4030201, 0x8070605], 0)))
|
||||
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)))
|
||||
}
|
||||
|
||||
function testADD64() {
|
||||
var arr = new Uint32Array([0xffffffff, 1, 1, 0, 0, 0])
|
||||
ADD64(arr, 0, 2, 4)
|
||||
assertEquals(0xffffffff, arr[0])
|
||||
assertEquals(1, arr[1])
|
||||
assertEquals(1, arr[2])
|
||||
assertEquals(0, arr[3])
|
||||
assertEquals(0, arr[4])
|
||||
assertEquals(2, arr[5])
|
||||
var ret = ADD64([0xffffffff, 1], [1, 0])
|
||||
assertEquals(0, ret[0])
|
||||
assertEquals(2, ret[1])
|
||||
}
|
||||
|
||||
function testBLAKE2B() {
|
||||
assertEquals('todo', blake2bStringToHex('hello'))
|
||||
}
|
||||
|
||||
function assertEquals(expected, actual) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче