All pastes #2093815 Raw Edit

gost_r_34.11-94.js

public javascript v1 · immutable
#2093815 ·published 2011-11-11 14:59 UTC
rendered paste body
/** * Javascript GOST R 34.11-94 hash function implementation. * Based on C-code by Alexei Kravchenko and Markku-Juhani Saarinen * and Utf8 class by Chris Veness. * * Usage: gosthash("<string>") returns a hash-string. * 						Alexander Okrugin (November, 11, 2011)*/var Utf8 = {}; // Utf8 namespaceUtf8.encode = function (strUni) {    // use regular expressions & String.replace callback function for better efficiency     // than procedural approaches    var strUtf = strUni.replace(/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz    function (c) {        var cc = c.charCodeAt(0);        return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);    });    strUtf = strUtf.replace(/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz    function (c) {        var cc = c.charCodeAt(0);        return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f);    });    return strUtf;}Utf8.decode = function (strUtf) {    // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char!    var strUni = strUtf.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars    function (c) { // (note parentheses for precence)        var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f);        return String.fromCharCode(cc);    });    strUni = strUni.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars    function (c) { // (note parentheses for precence)        var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f;        return String.fromCharCode(cc);    });    return strUni;}function rol(word, n) {    return ((word << (n & 0x1f)) | (word >>> (32 - (n & 0x1f))));}function ch2hex(val) {    var hext = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];    return hext[(val >>> 4) & 15] + hext[val & 15];}function str2bytes(str) {    var arr = Array(str.length);    for (var i = 0; i < str.length; ++i)    arr[i] = str.charCodeAt(i) & 255;    return arr;}var hash = new Array(8);var sum = new Array(8);var message = new Array(32);var length = 0,    r = 0,    l = 0;var gost_sbox = new Array(4);for (var j = 0; j < 4; j++) gost_sbox[j] = new Array(256);/* eight 4-bit S-Boxes as defined by RFC 4351 section 11.2 */var sbox = [    [4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3],    [14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9],    [5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11],    [7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3],    [6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2],    [4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14],    [13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12],    [1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12]];/** CryptoPro S-Box: *//*var sbox = [    [10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15],    [5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8],    [7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13],    [4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3],    [7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5],    [7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3],    [13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11],    [1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12]];*//** * Initialize algorithm context before calculaing hash. */function gost_init() {    for (var i = 0; i < 8; i++) {        hash[i] = 0;        sum[i] = 0;        message[4 * i] = 0;        message[4 * i + 1] = 0;        message[4 * i + 2] = 0;        message[4 * i + 3] = 0;    }    length = 0;}/* *  A full encryption round of GOST 28147-89. *  Temporary variables tmp assumed and variables r and l for left and right *  blocks. */function GOST_ENCRYPT_ROUND(key1, key2) {    var tmp = (key1) + r;    l ^= gost_sbox[0][tmp & 0xff] ^ gost_sbox[1][(tmp >>> 8) & 0xff] ^ gost_sbox[2][(tmp >>> 16) & 0xff] ^ gost_sbox[3][(tmp >>> 24) & 0xff];    tmp = (key2) + l;    r ^= gost_sbox[0][tmp & 0xff] ^ gost_sbox[1][(tmp >>> 8) & 0xff] ^ gost_sbox[2][(tmp >>> 16) & 0xff] ^ gost_sbox[3][(tmp >>> 24) & 0xff];}/* encrypt a block with the given key */function GOST_ENCRYPT(ii, key, varhash) {    r = varhash[ii], l = varhash[ii + 1];    GOST_ENCRYPT_ROUND(key[0], key[1]);    GOST_ENCRYPT_ROUND(key[2], key[3]);    GOST_ENCRYPT_ROUND(key[4], key[5]);    GOST_ENCRYPT_ROUND(key[6], key[7]);    GOST_ENCRYPT_ROUND(key[0], key[1]);    GOST_ENCRYPT_ROUND(key[2], key[3]);    GOST_ENCRYPT_ROUND(key[4], key[5]);    GOST_ENCRYPT_ROUND(key[6], key[7]);    GOST_ENCRYPT_ROUND(key[0], key[1]);    GOST_ENCRYPT_ROUND(key[2], key[3]);    GOST_ENCRYPT_ROUND(key[4], key[5]);    GOST_ENCRYPT_ROUND(key[6], key[7]);    GOST_ENCRYPT_ROUND(key[7], key[6]);    GOST_ENCRYPT_ROUND(key[5], key[4]);    GOST_ENCRYPT_ROUND(key[3], key[2]);    GOST_ENCRYPT_ROUND(key[1], key[0]);    return [l, r];}/** * The core transformation. Process a 512-bit block. * * @param block the message block to process */function gost_block_compress(block) {    var key = new Array(8);    var u = new Array(8);    var v = new Array(8);    var w = new Array(8);    var s = new Array(8);    /* u := hash, v := <256-bit message block> */    for (var i = 0; i < 8; i++) {        u[i] = hash[i];        v[i] = block[i];    }    /* w := u xor v */    w[0] = u[0] ^ v[0];    w[1] = u[1] ^ v[1];    w[2] = u[2] ^ v[2];    w[3] = u[3] ^ v[3];    w[4] = u[4] ^ v[4];    w[5] = u[5] ^ v[5];    w[6] = u[6] ^ v[6];    w[7] = u[7] ^ v[7];    /* calculate keys, encrypt hash and store result to the s[] array */    for (var i = 0;; i += 2) { /* key generation: key_i := P(w) */        key[0] = (w[0] & 0x000000ff) | ((w[2] & 0x000000ff) << 8) | ((w[4] & 0x000000ff) << 16) | ((w[6] & 0x000000ff) << 24);        key[1] = ((w[0] & 0x0000ff00) >>> 8) | (w[2] & 0x0000ff00) | ((w[4] & 0x0000ff00) << 8) | ((w[6] & 0x0000ff00) << 16);        key[2] = ((w[0] & 0x00ff0000) >>> 16) | ((w[2] & 0x00ff0000) >>> 8) | (w[4] & 0x00ff0000) | ((w[6] & 0x00ff0000) << 8);        key[3] = ((w[0] & 0xff000000) >>> 24) | ((w[2] & 0xff000000) >>> 16) | ((w[4] & 0xff000000) >>> 8) | (w[6] & 0xff000000);        key[4] = (w[1] & 0x000000ff) | ((w[3] & 0x000000ff) << 8) | ((w[5] & 0x000000ff) << 16) | ((w[7] & 0x000000ff) << 24);        key[5] = ((w[1] & 0x0000ff00) >>> 8) | (w[3] & 0x0000ff00) | ((w[5] & 0x0000ff00) << 8) | ((w[7] & 0x0000ff00) << 16);        key[6] = ((w[1] & 0x00ff0000) >>> 16) | ((w[3] & 0x00ff0000) >>> 8) | (w[5] & 0x00ff0000) | ((w[7] & 0x00ff0000) << 8);        key[7] = ((w[1] & 0xff000000) >>> 24) | ((w[3] & 0xff000000) >>> 16) | ((w[5] & 0xff000000) >>> 8) | (w[7] & 0xff000000);        /* encryption: s_i := E_{key_i} (h_i) */        var res = GOST_ENCRYPT(i, key, hash);        s[i] = res[0];        s[i + 1] = res[1];        if (i == 0) { /* w:= A(u) ^ A^2(v) */            w[0] = u[2] ^ v[4];            w[1] = u[3] ^ v[5];            w[2] = u[4] ^ v[6];            w[3] = u[5] ^ v[7];            w[4] = u[6] ^ (v[0] ^= v[2]);            w[5] = u[7] ^ (v[1] ^= v[3]);            w[6] = (u[0] ^= u[2]) ^ (v[2] ^= v[4]);            w[7] = (u[1] ^= u[3]) ^ (v[3] ^= v[5]);        } else if ((i & 2) != 0) {            if (i == 6) break; /* w := A^2(u) xor A^4(v) xor C_3; u := A(u) xor C_3 */            /* C_3=0xff00ffff000000ffff0000ff00ffff0000ff00ff00ff00ffff00ff00ff00ff00 */            u[2] ^= u[4] ^ 0x000000ff;            u[3] ^= u[5] ^ 0xff00ffff;            u[4] ^= 0xff00ff00;            u[5] ^= 0xff00ff00;            u[6] ^= 0x00ff00ff;            u[7] ^= 0x00ff00ff;            u[0] ^= 0x00ffff00;            u[1] ^= 0xff0000ff;            w[0] = u[4] ^ v[0];            w[2] = u[6] ^ v[2];            w[4] = u[0] ^ (v[4] ^= v[6]);            w[6] = u[2] ^ (v[6] ^= v[0]);            w[1] = u[5] ^ v[1];            w[3] = u[7] ^ v[3];            w[5] = u[1] ^ (v[5] ^= v[7]);            w[7] = u[3] ^ (v[7] ^= v[1]);        } else { /* i==4 here */            /* w:= A( A^2(u) xor C_3 ) xor A^6(v) */            w[0] = u[6] ^ v[4];            w[1] = u[7] ^ v[5];            w[2] = u[0] ^ v[6];            w[3] = u[1] ^ v[7];            w[4] = u[2] ^ (v[0] ^= v[2]);            w[5] = u[3] ^ (v[1] ^= v[3]);            w[6] = (u[4] ^= u[6]) ^ (v[2] ^= v[4]);            w[7] = (u[5] ^= u[7]) ^ (v[3] ^= v[5]);        }    }    /* step hash function: x(block, hash) := psi^61(hash xor psi(block xor psi^12(S))) */    /* 12 rounds of the LFSR and xor in <message block> */    u[0] = block[0] ^ s[6];    u[1] = block[1] ^ s[7];    u[2] = block[2] ^ (s[0] << 16) ^ (s[0] >>> 16) ^ (s[0] & 0xffff) ^ (s[1] & 0xffff) ^ (s[1] >>> 16) ^ (s[2] << 16) ^ s[6] ^ (s[6] << 16) ^ (s[7] & 0xffff0000) ^ (s[7] >>> 16);    u[3] = block[3] ^ (s[0] & 0xffff) ^ (s[0] << 16) ^ (s[1] & 0xffff) ^ (s[1] << 16) ^ (s[1] >>> 16) ^ (s[2] << 16) ^ (s[2] >>> 16) ^ (s[3] << 16) ^ s[6] ^ (s[6] << 16) ^ (s[6] >>> 16) ^ (s[7] & 0xffff) ^ (s[7] << 16) ^ (s[7] >>> 16);    u[4] = block[4] ^ (s[0] & 0xffff0000) ^ (s[0] << 16) ^ (s[0] >>> 16) ^ (s[1] & 0xffff0000) ^ (s[1] >>> 16) ^ (s[2] << 16) ^ (s[2] >>> 16) ^ (s[3] << 16) ^ (s[3] >>> 16) ^ (s[4] << 16) ^ (s[6] << 16) ^ (s[6] >>> 16) ^ (s[7] & 0xffff) ^ (s[7] << 16) ^ (s[7] >>> 16);    u[5] = block[5] ^ (s[0] << 16) ^ (s[0] >>> 16) ^ (s[0] & 0xffff0000) ^ (s[1] & 0xffff) ^ s[2] ^ (s[2] >>> 16) ^ (s[3] << 16) ^ (s[3] >>> 16) ^ (s[4] << 16) ^ (s[4] >>> 16) ^ (s[5] << 16) ^ (s[6] << 16) ^ (s[6] >>> 16) ^ (s[7] & 0xffff0000) ^ (s[7] << 16) ^ (s[7] >>> 16);    u[6] = block[6] ^ s[0] ^ (s[1] >>> 16) ^ (s[2] << 16) ^ s[3] ^ (s[3] >>> 16) ^ (s[4] << 16) ^ (s[4] >>> 16) ^ (s[5] << 16) ^ (s[5] >>> 16) ^ s[6] ^ (s[6] << 16) ^ (s[6] >>> 16) ^ (s[7] << 16);    u[7] = block[7] ^ (s[0] & 0xffff0000) ^ (s[0] << 16) ^ (s[1] & 0xffff) ^ (s[1] << 16) ^ (s[2] >>> 16) ^ (s[3] << 16) ^ s[4] ^ (s[4] >>> 16) ^ (s[5] << 16) ^ (s[5] >>> 16) ^ (s[6] >>> 16) ^ (s[7] & 0xffff) ^ (s[7] << 16) ^ (s[7] >>> 16);    /* 1 round of the LFSR (a mixing transformation) and xor with <hash> */    v[0] = hash[0] ^ (u[1] << 16) ^ (u[0] >>> 16);    v[1] = hash[1] ^ (u[2] << 16) ^ (u[1] >>> 16);    v[2] = hash[2] ^ (u[3] << 16) ^ (u[2] >>> 16);    v[3] = hash[3] ^ (u[4] << 16) ^ (u[3] >>> 16);    v[4] = hash[4] ^ (u[5] << 16) ^ (u[4] >>> 16);    v[5] = hash[5] ^ (u[6] << 16) ^ (u[5] >>> 16);    v[6] = hash[6] ^ (u[7] << 16) ^ (u[6] >>> 16);    v[7] = hash[7] ^ (u[0] & 0xffff0000) ^ (u[0] << 16) ^ (u[1] & 0xffff0000) ^ (u[1] << 16) ^ (u[6] << 16) ^ (u[7] & 0xffff0000) ^ (u[7] >>> 16);    /* 61 rounds of LFSR, mixing up hash */    hash[0] = (v[0] & 0xffff0000) ^ (v[0] << 16) ^ (v[0] >>> 16) ^ (v[1] >>> 16) ^ (v[1] & 0xffff0000) ^ (v[2] << 16) ^ (v[3] >>> 16) ^ (v[4] << 16) ^ (v[5] >>> 16) ^ v[5] ^ (v[6] >>> 16) ^ (v[7] << 16) ^ (v[7] >>> 16) ^ (v[7] & 0xffff);    hash[1] = (v[0] << 16) ^ (v[0] >>> 16) ^ (v[0] & 0xffff0000) ^ (v[1] & 0xffff) ^ v[2] ^ (v[2] >>> 16) ^ (v[3] << 16) ^ (v[4] >>> 16) ^ (v[5] << 16) ^ (v[6] << 16) ^ v[6] ^ (v[7] & 0xffff0000) ^ (v[7] >>> 16);    hash[2] = (v[0] & 0xffff) ^ (v[0] << 16) ^ (v[1] << 16) ^ (v[1] >>> 16) ^ (v[1] & 0xffff0000) ^ (v[2] << 16) ^ (v[3] >>> 16) ^ v[3] ^ (v[4] << 16) ^ (v[5] >>> 16) ^ v[6] ^ (v[6] >>> 16) ^ (v[7] & 0xffff) ^ (v[7] << 16) ^ (v[7] >>> 16);    hash[3] = (v[0] << 16) ^ (v[0] >>> 16) ^ (v[0] & 0xffff0000) ^ (v[1] & 0xffff0000) ^ (v[1] >>> 16) ^ (v[2] << 16) ^ (v[2] >>> 16) ^ v[2] ^ (v[3] << 16) ^ (v[4] >>> 16) ^ v[4] ^ (v[5] << 16) ^ (v[6] << 16) ^ (v[7] & 0xffff) ^ (v[7] >>> 16);    hash[4] = (v[0] >>> 16) ^ (v[1] << 16) ^ v[1] ^ (v[2] >>> 16) ^ v[2] ^ (v[3] << 16) ^ (v[3] >>> 16) ^ v[3] ^ (v[4] << 16) ^ (v[5] >>> 16) ^ v[5] ^ (v[6] << 16) ^ (v[6] >>> 16) ^ (v[7] << 16);    hash[5] = (v[0] << 16) ^ (v[0] & 0xffff0000) ^ (v[1] << 16) ^ (v[1] >>> 16) ^ (v[1] & 0xffff0000) ^ (v[2] << 16) ^ v[2] ^ (v[3] >>> 16) ^ v[3] ^ (v[4] << 16) ^ (v[4] >>> 16) ^ v[4] ^ (v[5] << 16) ^ (v[6] << 16) ^ (v[6] >>> 16) ^ v[6] ^ (v[7] << 16) ^ (v[7] >>> 16) ^ (v[7] & 0xffff0000);    hash[6] = v[0] ^ v[2] ^ (v[2] >>> 16) ^ v[3] ^ (v[3] << 16) ^ v[4] ^ (v[4] >>> 16) ^ (v[5] << 16) ^ (v[5] >>> 16) ^ v[5] ^ (v[6] << 16) ^ (v[6] >>> 16) ^ v[6] ^ (v[7] << 16) ^ v[7];    hash[7] = v[0] ^ (v[0] >>> 16) ^ (v[1] << 16) ^ (v[1] >>> 16) ^ (v[2] << 16) ^ (v[3] >>> 16) ^ v[3] ^ (v[4] << 16) ^ v[4] ^ (v[5] >>> 16) ^ v[5] ^ (v[6] << 16) ^ (v[6] >>> 16) ^ (v[7] << 16) ^ v[7];}/** * Update block sum and message hash. * * @param block the 256-bit message block to process */function gost_compute_sum_and_hash(block) { /* compute the 256-bit sum */    var carry = 0;    var hb = 0;    for (var i = 0; i < 8; i++) {        hb = (sum[i] >>> 24);        sum[i] = (sum[i] & 0x00ffffff) + (block[i] & 0x00ffffff) + carry;        hb = hb + (block[i] >>> 24) + (sum[i] >>> 24);        sum[i] = (sum[i] & 0x00ffffff) | ((hb & 0xff) << 24);        carry = ((hb & 0x100) != 0 ? 1 : 0);    } /* update message hash */    gost_block_compress(block);}/** * Calculate message hash. * Can be called repeatedly with chunks of the message to be hashed. * * @param _msg message chunk * @param _size length of the message chunk */function gost_update(_msg, _size) {    var index = length & 31;    var msg = _msg,        pmsg = 0;    var size = _size;    var msg32 = new Array(8);    length += size;    /* fill partial block */    if (index != 0) {        left = 32 - index;        if (size < left) {            for (var i = 0; i < size; i++) message[index + i] = msg[i];            return;        } else for (var i = 0; i < left; i++) message[index + i] = msg[i];        /* process partitial block */        for (var i = 0; i < 8; i++) msg32[i] = ((message[4 * i] & 0xff)) | ((message[4 * i + 1] & 0xff) << 8) | ((message[4 * i + 2] & 0xff) << 16) | ((message[4 * i + 3] & 0xff) << 24);        gost_compute_sum_and_hash(msg32);        pmsg += left;        size -= left;    }    while (size >= 32) {        for (var i = 0; i < 8; i++) {            message[4 * i] = msg[pmsg + 4 * i];            message[4 * i + 1] = msg[pmsg + 4 * i + 1];            message[4 * i + 2] = msg[pmsg + 4 * i + 2];            message[4 * i + 3] = msg[pmsg + 4 * i + 3];            msg32[i] = ((message[4 * i] & 0xff)) | ((message[4 * i + 1] & 0xff) << 8) | ((message[4 * i + 2] & 0xff) << 16) | ((message[4 * i + 3] & 0xff) << 24);        }        gost_compute_sum_and_hash(msg32);        pmsg += 32;        size -= 32;    }    if (size != 0) {        for (var i = 0; i < size; i++) {            message[i] = msg[pmsg + i];        }    }}/** * Finish hashing and store message digest into given array. * * @param result calculated hash in binary form */function gost_final() {    var index = length & 31;    var msg32 = new Array(8);    /* pad the last block with zeroes and hash it */    if (index > 0) {        for (var i = 0; i < 32 - index; i++) {            message[index + i] = 0;        }        for (var i = 0; i < 8; i++) {            msg32[i] = ((message[4 * i] & 0xff)) | ((message[4 * i + 1] & 0xff) << 8) | ((message[4 * i + 2] & 0xff) << 16) | ((message[4 * i + 3] & 0xff) << 24);        }        gost_compute_sum_and_hash(msg32);    }    /* hash the message length and the sum */    msg32[0] = (length << 3);    msg32[1] = (length >>> 29);    for (var i = 2; i < 8; i++) {        msg32[i] = 0;    }    gost_block_compress(msg32);    gost_block_compress(sum);}/* initialize the lookup tables */function gost_init_table() {    var ax, bx, cx, dx;    for (var i = 0, a = 0; a < 16; a++) {        ax = sbox[1][a] << 15;        bx = sbox[3][a] << 23;        cx = rol(sbox[5][a], 31);        dx = sbox[7][a] << 7;        for (var b = 0; b < 16; b++, i++) {            gost_sbox[0][i] = ax | (sbox[0][b] << 11);            gost_sbox[1][i] = bx | (sbox[2][b] << 19);            gost_sbox[2][i] = cx | (sbox[4][b] << 27);            gost_sbox[3][i] = dx | (sbox[6][b] << 3);        }    }}function gosthash(v) {    var rx = new Array(r.length);    var x = str2bytes(Utf8.encode(v));    gost_init();    gost_init_table();    gost_update(x, x.length);    gost_final();    for (var i = 0; i < hash.length; i++) rx[i] = ch2hex(hash[i] & 0xff) + ch2hex((hash[i] >>> 8) & 0xff) + ch2hex((hash[i] >>> 16) & 0xff) + ch2hex((hash[i] >>> 24) & 0xff);    return (rx.join(""));}