Skip to content

Instantly share code, notes, and snippets.

@devi
Created July 2, 2014 08:49
Show Gist options
  • Save devi/cda4a12ab11fa7eeaa8f to your computer and use it in GitHub Desktop.
Save devi/cda4a12ab11fa7eeaa8f to your computer and use it in GitHub Desktop.

Revisions

  1. devi created this gist Jul 2, 2014.
    334 changes: 334 additions & 0 deletions blake2b.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,334 @@
    (function(exports) {
    'use strict';

    // Blake2b

    // Ported by Devi Mandiri. Public domain.

    var u64 = function (h, l) {
    h = h|0; l = l|0;
    this.hi = h >>> 0;
    this.lo = l >>> 0;
    };

    function new64(num) {
    var hi = 0, lo = num >>> 0;
    if ((+(Math.abs(num))) >= 1) {
    if (num > 0) {
    hi = ((Math.min((+(Math.floor(num/4294967296))), 4294967295))|0) >>> 0;
    } else {
    hi = (~~((+(Math.ceil((num - +(((~~(num)))>>>0))/4294967296))))) >>> 0;
    }
    }
    return new u64(hi, lo);
    }

    function add64() {
    var l = 0, h = 0, t;
    for (var i = 0; i < arguments.length; ++i) {
    t = l; l = (t + arguments[i].lo)>>>0;
    h = (h + arguments[i].hi + ((l < t) ? 1 : 0))>>>0;
    }
    return new u64(h, l);
    }

    function xor64(x, y) {
    return new u64(x.hi ^ y.hi, x.lo ^ y.lo);
    }

    function rotr64(x, c) {
    c = 64 - c;

    var h0 = 0, l0 = 0, h1 = 0, l1 = 0, c1 = 64 - c;

    // shl
    if (c < 32) {
    h0 = (x.hi << c) | ((x.lo & (((1 << c) - 1)|0) << (32 - c)) >>> (32 - c));
    l0 = x.lo << c;
    } else {
    h0 = x.lo << (c - 32);
    }

    // shr
    if (c1 < 32) {
    h1 = x.hi >>> c1;
    l1 = (x.lo >>> c1) | (x.hi & (((1 << c1) - 1)|0)) << (32 - c1);
    } else {
    l1 = x.hi >>> (c1 - 32);
    }

    return new u64(h0 | h1, l0 | l1);
    }

    function flatten64(x) {
    return (x.hi * 4294967296 + x.lo);
    }

    function load64(x, i) {
    var l = x[i] | (x[i+1]<<8) | (x[i+2]<<16) | (x[i+3]<<24);
    var h = x[i+4] | (x[i+5]<<8) | (x[i+6]<<16) | (x[i+7]<<24);
    return new u64(h, l);
    }

    function store64(x, i, u) {
    x[i] = (u.lo & 0xff); u.lo >>>= 8;
    x[i+1] = (u.lo & 0xff); u.lo >>>= 8;
    x[i+2] = (u.lo & 0xff); u.lo >>>= 8;
    x[i+3] = (u.lo & 0xff);
    x[i+4] = (u.hi & 0xff); u.hi >>>= 8;
    x[i+5] = (u.hi & 0xff); u.hi >>>= 8;
    x[i+6] = (u.hi & 0xff); u.hi >>>= 8;
    x[i+7] = (u.hi & 0xff);
    }

    var iv = [
    new u64(0x6a09e667, 0xf3bcc908), new u64(0xbb67ae85, 0x84caa73b),
    new u64(0x3c6ef372, 0xfe94f82b), new u64(0xa54ff53a, 0x5f1d36f1),
    new u64(0x510e527f, 0xade682d1), new u64(0x9b05688c, 0x2b3e6c1f),
    new u64(0x1f83d9ab, 0xfb41bd6b), new u64(0x5be0cd19, 0x137e2179)
    ];

    var State = function() {
    this.h = iv.slice(0);
    this.t = [new u64(0,0), new u64(0,0)];
    this.f = [new u64(0,0), new u64(0,0)];
    this.buf = new Array(256);
    for (var i = 256; i--;) this.buf[i] = 0;
    this.buflen = 0;
    };

    var sigma = [
    [ 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]
    ];

    function G(v, m, r, i, a, b, c, d) {
    v[a] = add64(v[a], v[b], m[sigma[r][2*i+0]]);
    v[d] = rotr64(xor64(v[d], v[a]), 32);
    v[c] = add64(v[c], v[d]);
    v[b] = rotr64(xor64(v[b], v[c]), 24);
    v[a] = add64(v[a], v[b], m[sigma[r][2*i+1]]);
    v[d] = rotr64(xor64(v[d], v[a]), 16);
    v[c] = add64(v[c], v[d]);
    v[b] = rotr64(xor64(v[b], v[c]), 63);
    }

    function compress(ctx, block) {
    var m = new Array(16);
    var v = new Array(16);
    var i = 0;

    for (i = 16; i--;) m[i] = load64(block, i*8);

    for (i = 8; i--;) v[i] = ctx.h[i];

    v[ 8] = iv[0];
    v[ 9] = iv[1];
    v[10] = iv[2];
    v[11] = iv[3];

    v[12] = xor64(ctx.t[0], iv[4]);
    v[13] = xor64(ctx.t[1], iv[5]);
    v[14] = xor64(ctx.f[0], iv[6]);
    v[15] = xor64(ctx.f[1], iv[7]);

    for (i = 0; i < 12; i++) {
    G(v, m, i, 0, 0, 4, 8,12);
    G(v, m, i, 1, 1, 5, 9,13);
    G(v, m, i, 2, 2, 6,10,14);
    G(v, m, i, 3, 3, 7,11,15);
    G(v, m, i, 4, 0, 5,10,15);
    G(v, m, i, 5, 1, 6,11,12);
    G(v, m, i, 6, 2, 7, 8,13);
    G(v, m, i, 7, 3, 4, 9,14);
    }

    for (i = 0; i < 8; i++) {
    ctx.h[i] = xor64(ctx.h[i], xor64(v[i], v[i+8]));
    }
    }

    function increment_counter(ctx, inc) {
    var t = new64(inc);
    ctx.t[0] = add64(ctx.t[0], t);
    if (flatten64(ctx.t[0]) < inc) {
    ctx.t[1] = add64(ctx.t[1], new64(1));
    }
    }

    function update(ctx, p, plen) {
    var i = 0, offset = 0, left = 0, fill = 0;
    while (plen > 0) {
    left = ctx.buflen;
    fill = 256 - left;

    if (plen > fill) {
    for (i = 0; i < fill; i++) {
    ctx.buf[i+left] = p[i+offset];
    }

    ctx.buflen += fill;

    increment_counter(ctx, 128);
    compress(ctx, ctx.buf);

    for (i = 128; i--;) {
    ctx.buf[i] = ctx.buf[i+128];
    }

    ctx.buflen -= 128;
    offset += fill;
    plen -= fill;
    } else {
    for (i = plen; i--;) {
    ctx.buf[i+left] = p[i+offset];
    }
    ctx.buflen += plen;
    offset += plen;
    plen -= plen;
    }
    }
    }

    function finish(ctx, out) {
    var i = 0;

    if (ctx.buflen > 128) {
    increment_counter(ctx, 128);
    compress(ctx, ctx.buf);
    ctx.buflen -= 128;
    for (i = ctx.buflen; i--;) {
    ctx.buf[i] = ctx.buf[i+128];
    }
    }

    increment_counter(ctx, ctx.buflen);
    ctx.f[0] = new u64(0xffffffff, 0xffffffff);

    for (i = 256 - ctx.buflen; i--;)
    ctx.buf[i+ctx.buflen] = 0;

    compress(ctx, ctx.buf);

    for (i = 0; i < 8; i++) {
    store64(out, i*8, ctx.h[i]);
    }
    }

    function init(key, outlen) {
    var ctx = new State();
    var p = new Array(64);
    var klen = 0;
    var dlen = 64;
    var i = 0;

    for (i = 64; i--;) p[i] = 0;

    if (typeof key !== 'undefined') {
    if (key.length > 64) return false;
    klen = key.length;
    }

    if (typeof outlen !== 'undefined') {
    if (outlen > 64) return false;
    dlen = outlen;
    }

    p[0] = dlen; // digest_length
    p[1] = klen; // key_length
    p[2] = 1; // fanout
    p[3] = 1; // depth

    ctx.h[0] = xor64(ctx.h[0], load64(p, 0));

    if (klen > 0) {
    var block = new Array(128);
    for (i = 128; i--;) block[i] = 0;
    for (i = klen; i--;) block[i] = key[i];
    update(ctx, block, 128);
    }

    return ctx;
    }

    var BLOCKBYTES = 128,
    OUTBYTES = 64,
    KEYBYTES = 64;

    // helper
    function decodeString(s) {
    var b = [];
    s = unescape(encodeURIComponent(s));
    for (var i = s.length; i--;) {
    b[i] = s.charCodeAt(i);
    }
    return b;
    }

    function encodeHex(arr) {
    var h = '0123456789abcdef', s = '';
    for (var i = 0; i< arr.length; i++) {
    s += h[(arr[i]>>4)&15];
    s += h[arr[i]&15];
    }
    return s;
    }

    function decodeHex(h) {
    h.replace(/([^0-9a-f])/g, '');
    var out = [], len = h.length, w = '';
    if ((len%2) === 1) {
    throw new Error('Odd length hex string');
    }
    for (var i = 0; i < len; i += 2) {
    w = h[i];
    if (((i+1) >= len) || typeof h[i+1] === 'undefined') {
    w += '0';
    } else {
    w += h[i+1];
    }
    out.push(parseInt(w, 16));
    }
    return out;
    }

    exports.BLOCKBYTES = BLOCKBYTES;
    exports.OUTBYTES = OUTBYTES;
    exports.KEYBYTES = KEYBYTES;

    exports.init = init;
    exports.update = update;
    exports.finish = finish;

    exports.decodeString = decodeString;
    exports.encodeHex = encodeHex;
    exports.decodeHex = decodeHex;

    exports.hash = function(data, key) {
    var d = (typeof data === 'string') ? decodeString(data) : data;
    if (typeof key !== 'undefined') {
    if (key.length !== KEYBYTES) {
    throw new Error('Invalid key size.');
    }
    var k = (typeof key === 'string') ? decodeString(key) : key;
    var s = init(key);
    } else {
    var s = init();
    }
    var out = [];
    update(s, d, d.length);
    finish(s, out);
    return encodeHex(out);
    };

    })(typeof exports !== 'undefined' ? exports : (window.blake2b = window.blake2b || {}));