Added README. BLAKE2B close. No test vectors yet.

This commit is contained in:
DC 2016-01-08 15:36:09 -08:00
Родитель cb0acc0d65
Коммит a975d18c5b
4 изменённых файлов: 226 добавлений и 127 удалений

36
README.md Normal file
Просмотреть файл

@ -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).

Просмотреть файл

@ -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
}
*/

19
package.json Normal file
Просмотреть файл

@ -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) {