CyberChef/src/core/vendor/gost/gostDigest.mjs

1261 lines
61 KiB
JavaScript

/**
* GOST R 34.11-94 / GOST R 34.11-12 implementation
* 1.76
* 2014-2016, Rudolf Nickolaev. All rights reserved.
*
* Exported for CyberChef by mshwed [m@ttshwed.com]
*/
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Converted to JavaScript from source https://www.streebog.net/
* Copyright (c) 2013, Alexey Degtyarev.
* All rights reserved.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
import GostRandom from './gostRandom.mjs';
import GostCipher from './gostCipher.mjs';
import crypto from 'crypto';
/*
* GOST R 34.11
* Common methods
*
*/ // <editor-fold defaultstate="collapsed">
var root = {};
var rootCrypto = crypto
var DataError = Error,
NotSupportedError = Error;
// Copy len values from s[sOfs] to d[dOfs]
function arraycopy(s, sOfs, d, dOfs, len) {
for (var i = 0; i < len; i++)
d[dOfs + i] = s[sOfs + i];
}
// Swap bytes in buffer
function swap(s) {
var src = new Uint8Array(s),
dst = new Uint8Array(src.length);
for (var i = 0, n = src.length; i < n; i++)
dst[n - i - 1] = src[i];
return dst.buffer;
}
// Convert BASE64 string to Uint8Array
// for decompression of constants and precalc values
function b64decode(s) {
// s = s.replace(/[^A-Za-z0-9\+\/]/g, '');
var n = s.length,
k = n * 3 + 1 >> 2, r = new Uint8Array(k);
for (var m3, m4, u24 = 0, j = 0, i = 0; i < n; i++) {
m4 = i & 3;
var c = s.charCodeAt(i);
c = c > 64 && c < 91 ?
c - 65 : c > 96 && c < 123 ?
c - 71 : c > 47 && c < 58 ?
c + 4 : c === 43 ?
62 : c === 47 ?
63 : 0;
u24 |= c << 18 - 6 * m4;
if (m4 === 3 || n - i === 1) {
for (m3 = 0; m3 < 3 && j < k; m3++, j++) {
r[j] = u24 >>> (16 >>> m3 & 24) & 255;
}
u24 = 0;
}
}
return r.buffer;
}
// Random seed
function getSeed(length) {
GostRandom = GostRandom || root.GostRandom;
var randomSource = GostRandom ? new (GostRandom || root.GostRandom) : rootCrypto;
if (randomSource.getRandomValues) {
var d = new Uint8Array(Math.ceil(length / 8));
randomSource.getRandomValues(d);
return d;
} else
throw new NotSupportedError('Random generator not found');
}
// Check buffer
function buffer(d) {
if (d instanceof ArrayBuffer)
return d;
else if (d && d?.buffer instanceof ArrayBuffer)
return d.byteOffset === 0 && d.byteLength === d.buffer.byteLength ?
d.buffer : new Uint8Array(new Uint8Array(d, d.byteOffset, d.byteLength)).buffer;
else
throw new DataError('ArrayBuffer or ArrayBufferView required');
} // </editor-fold>
/**
* Algorithm name GOST R 34.11 or GOST R 34.11-12<br><br>
*
* http://tools.ietf.org/html/rfc6986
*
* The digest method returns digest data in according to GOST R 4311-2012.<br>
* Size of digest also defines in algorithm name.
* <ul>
* <li>GOST R 34.11-256-12 - 256 bits digest</li>
* <li>GOST R 34.11-512-12 - 512 bits digest</li>
* </ul>
*
* @memberOf GostDigest
* @method digest
* @instance
* @param {(ArrayBuffer|TypedArray)} data Data
* @returns {ArrayBuffer} Digest of data
*/
var digest2012 = (function () // <editor-fold defaultstate="collapsed">
{
// Constants
var buffer0 = new Int32Array(16); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var buffer512 = new Int32Array(16); // [512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
buffer512[0] = 512;
// Constant C
var C = (function (s) {
var h = new Int32Array(b64decode(s)),
r = new Array(12);
for (var i = 0; i < 12; i++)
r[i] = new Int32Array(h.buffer, i * 64, 16);
return r;
})(
'B0Wm8lllgN0jTXTMNnR2BRXTYKQIKkKiAWlnkpHgfEv8xIV1jbhOcRbQRS5DdmovH3xlwIEvy+vp2soe2lsIsbebsSFwBHnmVs3L1xui3VXKpwrbwmG1XFiZ1hJrF7WaMQG1Fg9e1WGYKyMKcur+89e1cA9GneNPGi+dqYq1o2+yCroK9ZYemTHbeoZD9LbCCdtiYDc6ycGxnjWQ5A/i03t7KbEUderyix+cUl9e8QY1hD1qKPw5Cscvzius3HT1LtHjhLy+DCLxN+iToepTNL4DUpMzE7fYddYD7YIs16k/NV5orRxynX08XDN+hY5I3eRxXaDhSPnSZhXos98f71f+bHz9WBdg9WPqqX6iVnoWGicjtwD/36P1OiVHF82/vf8PgNc1njVKEIYWHxwVf2MjqWwMQT+amUdHraxr6ktufWRGekBo+jVPkDZyxXG/tsa+wmYf8gq0t5oct6b6z8aO8Jq0mn8YbKRCUfnEZi3AOTB6O8Okb9nTOh2urk+uk9QUOk1WhojzSjyiTEUXNQQFSiiDaUcGNyyCLcWrkgnJk3oZMz5H08mHv+bHxp45VAkkv/6GrFHsxaruFg7H9B7nAr/UDX+k' +
'2ahRWTXCrDYvxKXRK43RaZAGm5LLK4n0msTbTTtEtIke3jaccfi3TkFBbgwCqucDp8mTTUJbH5vbWiODUURhcmAqH8uS3DgOVJwHppqKK3uxzrLbC0QKgIQJDeC3Vdk8JEKJJRs6fTreXxbs2JpMlJsiMRZUWo837ZxFmPvHtHTDtjsV0fqYNvRSdjswbB56SzNprwJn558DYTMbiuH/H9t4iv8c50GJ8/PkskjlKjhSbwWApt6+qxst84HNpMprXdhvwEpZot6Ybkd9Hc2678q5SOrvcR2KeWaEFCGAASBhB6vru2v62JT+WmPNxgIw+4nI79CezXsg1xvxSpK8SJkbstnVF/T6UijhiKqkHeeGzJEYne+AXZufITDUEiD4dx3fvDI8pM16sUkEsIAT0roxFvFn5443');
// Precalc Ax
var Ax = (function (s) {
return new Int32Array(b64decode(s));
})(
'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'); //==
// Variables
var sigma, N, h;
// 64bit tools
function get8(x, i) {
return (x[i >> 2] >> ((i & 3) << 3)) & 0xff;
}
// 512bit tools
function add512(x, y) {
var CF = 0, w0, w1;
for (var i = 0; i < 16; i++) {
w0 = (x[i] & 0xffff) + (y[i] & 0xffff) + (CF || 0);
w1 = (x[i] >>> 16) + (y[i] >>> 16) + (w0 >>> 16);
x[i] = (w0 & 0xffff) | (w1 << 16);
CF = (w1 >>> 16);
}
}
function get512(d) {
return new Int32Array(d.buffer, d.byteOffset, 16);
}
function copy512(r, d) {
for (var i = 0; i < 16; i++)
r[i] = d[i];
}
function new512() {
return new Int32Array(16);
}
// Core private algorithms
function xor512(x, y) {
for (var i = 0; i < 16; i++)
x[i] = x[i] ^ y[i];
}
var r = new512();
function XLPS(x, y) {
copy512(r, x);
xor512(r, y);
for (var i = 0; i < 8; i++) {
var z0, z1, k = get8(r, i) << 1;
z0 = Ax[k];
z1 = Ax[k + 1];
for (var j = 1; j < 8; j++) {
k = (j << 9) + (get8(r, (j << 3) + i) << 1);
z0 = z0 ^ Ax[k];
z1 = z1 ^ Ax[k + 1];
}
x[i << 1] = z0;
x[(i << 1) + 1] = z1;
}
}
var data = new512(), Ki = new512();
function g(h, N, m)
{
var i;
copy512(data, h);
XLPS(data, N);
/* Starting E() */
copy512(Ki, data);
XLPS(data, m);
for (i = 0; i < 11; i++) {
XLPS(Ki, C[i]);
XLPS(data, Ki);
}
XLPS(Ki, C[11]);
xor512(data, Ki);
/* E() done */
xor512(h, data);
xor512(h, m);
}
// Stages
function stage2(d) {
var m = get512(d);
g(h, N, m);
add512(N, buffer512);
add512(sigma, m);
}
function stage3(d) {
var n = d.length;
if (n > 63)
return;
var b0 = new Int32Array(16);
b0[0] = n << 3;
var b = new Uint8Array(64);
for (var i = 0; i < n; i++)
b[i] = d[i];
b[n] = 0x01;
var m = get512(b), m0 = get512(b0);
g(h, N, m);
add512(N, m0);
add512(sigma, m);
g(h, buffer0, N);
g(h, buffer0, sigma);
}
return function (data) {
// Cleanup
sigma = new512();
N = new512();
// Initial vector
h = new512();
for (var i = 0; i < 16; i++)
if (this.bitLength === 256)
h[i] = 0x01010101;
// Make data
var d = new Uint8Array(buffer(data));
var n = d.length;
var r = n % 64, q = (n - r) / 64;
for (var i = 0; i < q; i++)
stage2.call(this, new Uint8Array(d.buffer, i * 64, 64));
stage3.call(this, new Uint8Array(d.buffer, q * 64, r));
var digest;
if (this.bitLength === 256) {
digest = new Int32Array(8);
for (var i = 0; i < 8; i++)
digest[i] = h[8 + i];
} else {
digest = new Int32Array(16);
for (var i = 0; i < 16; i++)
digest[i] = h[i];
}
// Swap hash for SignalCom
if (this.procreator === 'SC' || this.procreator === 'VN')
return swap(digest.buffer);
else
return digest.buffer;
};
} // </editor-fold>
)();
/**
* Algorithm name GOST R 34.11-94<br><br>
*
* http://tools.ietf.org/html/rfc5831
*
* The digest method returns digest data in according to GOST R 34.11-94.
* @memberOf GostDigest
* @method digest
* @instance
* @param {(ArrayBuffer|TypedArray)} data Data
* @returns {ArrayBuffer} Digest of data
*/
var digest94 = (function () // <editor-fold defaultstate="collapsed">
{
var C, H, M, Sum;
// (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8
function P(d) {
var K = new Uint8Array(32);
for (var k = 0; k < 8; k++) {
K[4 * k] = d[k];
K[1 + 4 * k] = d[ 8 + k];
K[2 + 4 * k] = d[16 + k];
K[3 + 4 * k] = d[24 + k];
}
return K;
}
//A (x) = (x0 ^ x1) || x3 || x2 || x1
function A(d)
{
var a = new Uint8Array(8);
for (var j = 0; j < 8; j++)
{
a[j] = (d[j] ^ d[j + 8]);
}
arraycopy(d, 8, d, 0, 24);
arraycopy(a, 0, d, 24, 8);
return d;
}
// (in:) n16||..||n1 ==> (out:) n1^n2^n3^n4^n13^n16||n16||..||n2
function fw(d) {
var wS = new Uint16Array(d.buffer, 0, 16);
var wS15 = wS[0] ^ wS[1] ^ wS[2] ^ wS[3] ^ wS[12] ^ wS[15];
arraycopy(wS, 1, wS, 0, 15);
wS[15] = wS15;
}
//Encrypt function, ECB mode
function encrypt(key, s, sOff, d, dOff) {
var t = new Uint8Array(8);
arraycopy(d, dOff, t, 0, 8);
var r = new Uint8Array(this.cipher.encrypt(key, t));
arraycopy(r, 0, s, sOff, 8);
}
// block processing
function process(d, dOff) {
var S = new Uint8Array(32), U = new Uint8Array(32),
V = new Uint8Array(32), W = new Uint8Array(32);
arraycopy(d, dOff, M, 0, 32);
//key step 1
// H = h3 || h2 || h1 || h0
// S = s3 || s2 || s1 || s0
arraycopy(H, 0, U, 0, 32);
arraycopy(M, 0, V, 0, 32);
for (var j = 0; j < 32; j++)
{
W[j] = (U[j] ^ V[j]);
}
// Encrypt GOST 28147-ECB
encrypt.call(this, P(W), S, 0, H, 0); // s0 = EK0 [h0]
//keys step 2,3,4
for (var i = 1; i < 4; i++) {
var tmpA = A(U);
for (var j = 0; j < 32; j++) {
U[j] = (tmpA[j] ^ C[i][j]);
}
V = A(A(V));
for (var j = 0; j < 32; j++) {
W[j] = (U[j] ^ V[j]);
}
// Encrypt GOST 28147-ECB
encrypt.call(this, P(W), S, i * 8, H, i * 8); // si = EKi [hi]
}
// x(M, H) = y61(H^y(M^y12(S)))
for (var n = 0; n < 12; n++) {
fw(S);
}
for (var n = 0; n < 32; n++) {
S[n] = (S[n] ^ M[n]);
}
fw(S);
for (var n = 0; n < 32; n++) {
S[n] = (H[n] ^ S[n]);
}
for (var n = 0; n < 61; n++) {
fw(S);
}
arraycopy(S, 0, H, 0, H.length);
}
// 256 bitsblock modul -> (Sum + a mod (2^256))
function summing(d)
{
var carry = 0;
for (var i = 0; i < Sum.length; i++)
{
var sum = (Sum[i] & 0xff) + (d[i] & 0xff) + carry;
Sum[i] = sum;
carry = sum >>> 8;
}
}
// reset the chaining variables to the IV values.
var C2 = new Uint8Array([
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF
]);
return function (data) {
// Reset buffers
H = new Uint8Array(32);
M = new Uint8Array(32);
Sum = new Uint8Array(32);
// Reset IV value
C = new Array(4);
for (var i = 0; i < 4; i++)
C[i] = new Uint8Array(32);
arraycopy(C2, 0, C[2], 0, C2.length);
// Make data
var d = new Uint8Array(buffer(data));
var n = d.length;
var r = n % 32, q = (n - r) / 32;
// Proccess full blocks
for (var i = 0; i < q; i++) {
var b = new Uint8Array(d.buffer, i * 32, 32);
summing.call(this, b); // calc sum M
process.call(this, b, 0);
}
// load d the remadder with padding zero;
if (r > 0) {
var b = new Uint8Array(d.buffer, q * 32),
c = new Uint8Array(32);
arraycopy(b, 0, c, 0, r);
summing.call(this, c); // calc sum M
process.call(this, c, 0);
}
// get length into L (byteCount * 8 = bitCount) in little endian.
var L = new Uint8Array(32), n8 = n * 8, k = 0;
while (n8 > 0) {
L[k++] = n8 & 0xff;
n8 = Math.floor(n8 / 256);
}
process.call(this, L, 0);
process.call(this, Sum, 0);
var h = H.buffer;
// Swap hash for SignalCom
if (this.procreator === 'SC')
h = swap(h);
return h;
};
} // </editor-fold>
)();
/**
* Algorithm name SHA-1<br><br>
*
* https://tools.ietf.org/html/rfc3174
*
* The digest method returns digest data in according to SHA-1.<br>
*
* @memberOf GostDigest
* @method digest
* @instance
* @param {(ArrayBuffer|TypedArray)} data Data
* @returns {ArrayBuffer} Digest of data
*/
var digestSHA1 = (function () // <editor-fold defaultstate="collapsed">
{
// Create a buffer for each 80 word block.
var state, block = new Uint32Array(80);
function common(a, e, w, k, f) {
return (f + e + w + k + ((a << 5) | (a >>> 27))) >>> 0;
}
function f1(a, b, c, d, e, w) {
return common(a, e, w, 0x5A827999, d ^ (b & (c ^ d)));
}
function f2(a, b, c, d, e, w) {
return common(a, e, w, 0x6ED9EBA1, b ^ c ^ d);
}
function f3(a, b, c, d, e, w) {
return common(a, e, w, 0x8F1BBCDC, (b & c) | (d & (b | c)));
}
function f4(a, b, c, d, e, w) {
return common(a, e, w, 0xCA62C1D6, b ^ c ^ d);
}
function cycle(state, block) {
var a = state[0],
b = state[1],
c = state[2],
d = state[3],
e = state[4];
// Partially unroll loops so we don't have to shift variables.
var fn = f1;
for (var i = 0; i < 80; i += 5) {
if (i === 20) {
fn = f2;
}
else if (i === 40) {
fn = f3;
}
else if (i === 60) {
fn = f4;
}
e = fn(a, b, c, d, e, block[i]);
b = ((b << 30) | (b >>> 2)) >>> 0;
d = fn(e, a, b, c, d, block[i + 1]);
a = ((a << 30) | (a >>> 2)) >>> 0;
c = fn(d, e, a, b, c, block[i + 2]);
e = ((e << 30) | (e >>> 2)) >>> 0;
b = fn(c, d, e, a, b, block[i + 3]);
d = ((d << 30) | (d >>> 2)) >>> 0;
a = fn(b, c, d, e, a, block[i + 4]);
c = ((c << 30) | (c >>> 2)) >>> 0;
}
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
}
// Swap bytes for 32bits word
function swap32(b) {
return ((b & 0xff) << 24)
| ((b & 0xff00) << 8)
| ((b >> 8) & 0xff00)
| ((b >> 24) & 0xff);
}
// input is a Uint8Array bitstream of the data
return function (data) {
var d = new Uint8Array(buffer(data)), dlen = d.length;
// Pad the input string length.
var len = dlen + 9;
if (len % 64) {
len += 64 - (len % 64);
}
state = new Uint32Array(5);
state[0] = 0x67452301;
state[1] = 0xefcdab89;
state[2] = 0x98badcfe;
state[3] = 0x10325476;
state[4] = 0xc3d2e1f0;
for (var ofs = 0; ofs < len; ofs += 64) {
// Copy input to block and write padding as needed
for (var i = 0; i < 64; i++) {
var b = 0,
o = ofs + i;
if (o < dlen) {
b = d[o];
}
else if (o === dlen) {
b = 0x80;
}
else {
// Write original bit length as a 64bit big-endian integer to the end.
var x = len - o - 1;
if (x >= 0 && x < 4) {
b = (dlen << 3 >>> (x * 8)) & 0xff;
}
}
// Interpret the input bytes as big-endian per the spec
if (i % 4 === 0) {
block[i >> 2] = b << 24;
}
else {
block[i >> 2] |= b << ((3 - (i % 4)) * 8);
}
}
// Extend the block
for (var i = 16; i < 80; i++) {
var w = block[i - 3] ^ block[i - 8] ^ block[i - 14] ^ block[i - 16];
block[i] = (w << 1) | (w >>> 31);
}
cycle(state, block);
}
// Swap the bytes around since they are big endian internally
for (var i = 0; i < 5; i++)
state[i] = swap32(state[i]);
return state.buffer;
};
} // </editor-fold>
)();
/**
* Algorithm name GOST R 34.11-HMAC<br><br>
*
* HMAC with the specified hash function.
* @memberOf GostDigest
* @method sign
* @instance
* @param {ArrayBuffer} key The key for HMAC.
* @param {Hash} data Data
*/
function signHMAC(key, data) // <editor-fold defaultstate="collapsed">
{
// GOST R 34.11-94 - B=32b, L=32b
// GOST R 34.11-256 - B=64b, L=32b
// GOST R 34.11-512 - B=64b, L=64b
var b = (this.digest === digest94) ? 32 : 64,
l = this.bitLength / 8,
k = buffer(key),
d = buffer(data), k0;
if (k.byteLength === b)
k0 = new Uint8Array(k);
else {
var k0 = new Uint8Array(b);
if (k.byteLength > b) {
k0.set(new Uint8Array(this.digest(k)));
} else {
k0.set(new Uint8Array(k));
}
}
var s0 = new Uint8Array(b + d.byteLength),
s1 = new Uint8Array(b + l);
for (var i = 0; i < b; i++) {
s0[i] = k0[i] ^ 0x36;
s1[i] = k0[i] ^ 0x5C;
}
s0.set(new Uint8Array(d), b);
s1.set(new Uint8Array(this.digest(s0)), b);
return this.digest(s1);
} // </editor-fold>
/**
* Algorithm name GOST R 34.11-HMAC<br><br>
*
* Verify HMAC based on GOST R 34.11 hash
*
* @memberOf GostDigest
* @method verify
* @instance
* @param {(ArrayBuffer|TypedArray)} key Key which used for HMAC generation
* @param {(ArrayBuffer|TypedArray)} signature generated HMAC
* @param {(ArrayBuffer|TypedArray)} data Data
* @returns {boolean} HMAC verified = true
*/
function verifyHMAC(key, signature, data) // <editor-fold defaultstate="collapsed">
{
var hmac = new Uint8Array(this.sign(key, data)),
test = new Uint8Array(signature);
if (hmac.length !== test.length)
return false;
for (var i = 0, n = hmac.length; i < n; i++)
if (hmac[i] !== test[i])
return false;
return true;
} // </editor-fold>
/**
* Algorithm name GOST R 34.11-KDF<br><br>
*
* Simple generate key 256/512 bit random seed for derivation algorithms
*
* @memberOf GostDigest
* @method generateKey
* @instance
* @returns {ArrayBuffer} Generated key
*/
function generateKey() // <editor-fold defaultstate="collapsed">
{
return getSeed(this.bitLength).buffer;
} // </editor-fold>
/**
* Algorithm name GOST R 34.11-PFXKDF<br><br>
*
* Derive bits from password (PKCS12 mode)
* <ul>
* <li>algorithm.salt - random value, salt</li>
* <li>algorithm.iterations - number of iterations</li>
* </ul>
* @memberOf GostDigest
* @method deriveBits
* @instance
* @param {ArrayBuffer} baseKey - password after UTF-8 decoding
* @param {number} length output bit-length
* @returns {ArrayBuffer} result
*/
function deriveBitsPFXKDF(baseKey, length) // <editor-fold defaultstate="collapsed">
{
if (length % 8 > 0)
throw new DataError('Length must multiple of 8');
var u = this.bitLength / 8, v = (this.digest === digest94) ? 32 : 64,
n = length / 8, r = this.iterations;
// 1. Construct a string, D (the "diversifier"), by concatenating v/8
// copies of ID.
var ID = this.diversifier, D = new Uint8Array(v);
for (var i = 0; i < v; i++)
D[i] = ID;
// 2. Concatenate copies of the salt together to create a string S of
// length v(ceiling(s/v)) bits (the final copy of the salt may be
// truncated to create S). Note that if the salt is the empty
// string, then so is S.
var S0 = new Uint8Array(buffer(this.salt)), s = S0.length,
slen = v * Math.ceil(s / v), S = new Uint8Array(slen);
for (var i = 0; i < slen; i++)
S[i] = S0[i % s];
// 3. Concatenate copies of the password together to create a string P
// of length v(ceiling(p/v)) bits (the final copy of the password
// may be truncated to create P). Note that if the password is the
// empty string, then so is P.
var P0 = new Uint8Array(buffer(baseKey)), p = P0.length,
plen = v * Math.ceil(p / v), P = new Uint8Array(plen);
for (var i = 0; i < plen; i++)
P[i] = P0[i % p];
// 4. Set I=S||P to be the concatenation of S and P.
var I = new Uint8Array(slen + plen);
arraycopy(S, 0, I, 0, slen);
arraycopy(P, 0, I, slen, plen);
// 5. Set c=ceiling(n/u).
var c = Math.ceil(n / u);
// 6. For i=1, 2, ..., c, do the following:
var A = new Uint8Array(c * u);
for (var i = 0; i < c; i++) {
// A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1,
// H(H(H(... H(D||I))))
var H = new Uint8Array(v + slen + plen);
arraycopy(D, 0, H, 0, v);
arraycopy(I, 0, H, v, slen + plen);
for (var j = 0; j < r; j++)
H = new Uint8Array(this.digest(H));
arraycopy(H, 0, A, i * u, u);
// B. Concatenate copies of Ai to create a string B of length v
// bits (the final copy of Ai may be truncated to create B).
var B = new Uint8Array(v);
for (var j = 0; j < v; j++)
B[j] = H[j % u];
// C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit
// blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by
// setting I_j=(I_j+B+1) mod 2^v for each j.
var k = (slen + plen) / v;
for (j = 0; j < k; j++) {
var cf = 1, w;
for (var l = v - 1; l >= 0; --l) {
w = I[v * j + l] + B[l] + cf;
cf = w >>> 8;
I[v * j + l] = w & 0xff;
}
}
}
// 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom
// bit string, A.
// 8. Use the first n bits of A as the output of this entire process.
var R = new Uint8Array(n);
arraycopy(A, 0, R, 0, n);
return R.buffer;
} // </editor-fold>
/**
* Algorithm name GOST R 34.11-KDF<br><br>
*
* Derive bits for KEK deversification in 34.10-2012 algorithm
* KDF(KEK, UKM, label) = HMAC256 (KEK, 0x01|label|0x00|UKM|0x01|0x00)
* Default label = 0x26|0xBD|0xB8|0x78
*
* @memberOf GostDigest
* @method deriveBits
* @instance
* @param {(ArrayBuffer|TypedArray)} baseKey base key for deriviation
* @param {number} length output bit-length
* @returns {ArrayBuffer} result
*/
function deriveBitsKDF(baseKey, length) // <editor-fold defaultstate="collapsed">
{
if (length % 8 > 0)
throw new DataError('Length must be multiple of 8');
var rlen = length / 8, label, context = new Uint8Array(buffer(this.context)),
blen = this.bitLength / 8, n = Math.ceil(rlen / blen);
if (this.label)
label = new Uint8Array(buffer(this.label));
else
label = new Uint8Array([0x26, 0xBD, 0xB8, 0x78]);
var result = new Uint8Array(rlen);
for (var i = 0; i < n; i++) {
var data = new Uint8Array(label.length + context.length + 4);
data[0] = i + 1;
data.set(label, 1);
data[label.length + 1] = 0x00;
data.set(context, label.length + 2);
data[data.length - 2] = length >>> 8;
data[data.length - 1] = length & 0xff;
result.set(new Uint8Array(signHMAC.call(this, baseKey, data), 0,
i < n - 1 ? blen : rlen - i * blen), i * blen);
}
return result.buffer;
} // </editor-fold>
/**
* Algorithm name GOST R 34.11-PBKDF1<br><br>
*
* Derive bits from password
* <ul>
* <li>algorithm.salt - random value, salt</li>
* <li>algorithm.iterations - number of iterations</li>
* </ul>
* @memberOf GostDigest
* @method deriveBits
* @instance
* @param {ArrayBuffer} baseKey - password after UTF-8 decoding
* @param {number} length output bit-length
* @returns {ArrayBuffer} result
*/
function deriveBitsPBKDF1(baseKey, length) // <editor-fold defaultstate="collapsed">
{
if (length < this.bitLength / 2 || length % 8 > 0)
throw new DataError('Length must be more than ' + this.bitLength / 2 + ' bits and multiple of 8');
var hLen = this.bitLength / 8, dkLen = length / 8,
c = this.iterations,
P = new Uint8Array(buffer(baseKey)),
S = new Uint8Array(buffer(this.salt)),
slen = S.length, plen = P.length,
T = new Uint8Array(plen + slen),
DK = new Uint8Array(dkLen);
if (dkLen > hLen)
throw new DataError('Invalid parameters: Length value');
arraycopy(P, 0, T, 0, plen);
arraycopy(S, 0, T, plen, slen);
for (var i = 0; i < c; i++)
T = new Uint8Array(this.digest(T));
arraycopy(T, 0, DK, 0, dkLen);
return DK.buffer;
} // </editor-fold>
/**
* Algorithm name GOST R 34.11-PBKDF2<br><br>
*
* Derive bits from password
* <ul>
* <li>algorithm.salt - random value, salt</li>
* <li>algorithm.iterations - number of iterations</li>
* </ul>
* @memberOf GostDigest
* @method deriveBits
* @instance
* @param {ArrayBuffer} baseKey - password after UTF-8 decoding
* @param {number} length output bit-length
* @returns {ArrayBuffer} result
*/
function deriveBitsPBKDF2(baseKey, length) // <editor-fold defaultstate="collapsed">
{
var diversifier = this.diversifier || 1; // For PKCS12 MAC required 3*length
length = length * diversifier;
if (length < this.bitLength / 2 || length % 8 > 0)
throw new DataError('Length must be more than ' + this.bitLength / 2 + ' bits and multiple of 8');
var hLen = this.bitLength / 8, dkLen = length / 8,
c = this.iterations,
P = new Uint8Array(buffer(baseKey)),
S = new Uint8Array(buffer(this.salt));
var slen = S.byteLength,
data = new Uint8Array(slen + 4);
arraycopy(S, 0, data, 0, slen);
if (dkLen > (0xffffffff - 1) * 32)
throw new DataError('Invalid parameters: Length value');
var n = Math.ceil(dkLen / hLen),
DK = new Uint8Array(dkLen);
for (var i = 1; i <= n; i++) {
data[slen] = i >>> 24 & 0xff;
data[slen + 1] = i >>> 16 & 0xff;
data[slen + 2] = i >>> 8 & 0xff;
data[slen + 3] = i & 0xff;
var U = new Uint8Array(signHMAC.call(this, P, data)), Z = U;
for (var j = 1; j < c; j++) {
U = new Uint8Array(signHMAC.call(this, P, U));
for (var k = 0; k < hLen; k++)
Z[k] = U[k] ^ Z[k];
}
var ofs = (i - 1) * hLen;
arraycopy(Z, 0, DK, ofs, Math.min(hLen, dkLen - ofs));
}
if (diversifier > 1) {
var rLen = dkLen / diversifier, R = new Uint8Array(rLen);
arraycopy(DK, dkLen - rLen, R, 0, rLen);
return R.buffer;
} else
return DK.buffer;
} // </editor-fold>
/**
* Algorithm name GOST R 34.11-CPKDF<br><br>
*
* Derive bits from password. CryptoPro algorithm
* <ul>
* <li>algorithm.salt - random value, salt</li>
* <li>algorithm.iterations - number of iterations</li>
* </ul>
* @memberOf GostDigest
* @method deriveBits
* @instance
* @param {ArrayBuffer} baseKey - password after UTF-8 decoding
* @param {number} length output bit-length
* @returns {ArrayBuffer} result
*/
function deriveBitsCP(baseKey, length) {
if (length > this.bitLength || length % 8 > 0)
throw new DataError('Length can\'t be more than ' + this.bitLength + ' bits and multiple of 8');
// GOST R 34.11-94 - B=32b, L=32b
// GOST R 34.11-256 - B=64b, L=32b
// GOST R 34.11-512 - B=64b, L=64b
var b = (this.digest === digest94) ? 32 : 64,
l = this.bitLength / 8,
p = baseKey && baseKey.byteLength > 0 ? new Uint8Array(buffer(baseKey)) : false,
plen = p ? p.length : 0,
iterations = this.iterations,
salt = new Uint8Array(buffer(this.salt)),
slen = salt.length,
d = new Uint8Array(slen + plen);
arraycopy(salt, 0, d, 0, slen);
if (p)
arraycopy(p, 0, d, slen, plen);
var h = new Uint8Array(this.digest(d)),
k = new Uint8Array(b),
s0 = new Uint8Array(b),
s1 = new Uint8Array(b);
var c = 'DENEFH028.760246785.IUEFHWUIO.EF';
for (var i = 0; i < c.length; i++)
k[i] = c.charCodeAt(i);
d = new Uint8Array(2 * (b + l));
for (var j = 0; j < iterations; j++) {
for (var i = 0; i < b; i++) {
s0[i] = k[i] ^ 0x36;
s1[i] = k[i] ^ 0x5C;
k[i] = 0;
}
arraycopy(s0, 0, d, 0, b);
arraycopy(h, 0, d, b, l);
arraycopy(s1, 0, d, b + l, b);
arraycopy(h, 0, d, b + l + b, l);
arraycopy(new Uint8Array(this.digest(d)), 0, k, 0, l);
}
for (var i = 0; i < l; i++) {
s0[i] = k[i] ^ 0x36;
s1[i] = k[i] ^ 0x5C;
k[i] = 0;
}
d = new Uint8Array(2 * l + slen + plen);
arraycopy(s0, 0, d, 0, l);
arraycopy(salt, 0, d, l, slen);
arraycopy(s1, 0, d, l + slen, l);
if (p)
arraycopy(p, 0, d, l + slen + l, plen);
h = this.digest(this.digest(d));
if (length === this.bitLength)
return h;
else {
var rlen = length / 8, r = new Uint8Array(rlen);
arraycopy(h, 0, r, 0, rlen);
return r.buffer;
}
}
/**
* Algorithm name GOST R 34.11-KDF or GOST R 34.11-PBKDF2 or other<br><br>
*
* Derive key from derive bits subset
*
* @memberOf GostDigest
* @method deriveKey
* @instance
* @param {ArrayBuffer} baseKey
* @returns {ArrayBuffer}
*/
function deriveKey(baseKey) // <editor-fold defaultstate="collapsed">
{
return this.deriveBits(baseKey, this.keySize * 8);
} // </editor-fold>
/**
* GOST R 34.11 Algorithm<br><br>
*
* References: {@link http://tools.ietf.org/html/rfc6986} and {@link http://tools.ietf.org/html/rfc5831}<br><br>
*
* Normalized algorithm identifier common parameters:
*
* <ul>
* <li><b>name</b> Algorithm name 'GOST R 34.11'</li>
* <li><b>version</b> Algorithm version
* <ul>
* <li><b>1994</b> old-style 256 bits digest based on GOST 28147-89</li>
* <li><b>2012</b> 256 ro 512 bits digest algorithm "Streebog" GOST R 34.11-2012 (default)</li>
* </ul>
* </li>
* <li><b>length</b> Digest length
* <ul>
* <li><b>256</b> 256 bits digest</li>
* <li><b>512</b> 512 bits digest, valid only for algorithm "Streebog"</li>
* </ul>
* </li>
* <li><b>mode</b> Algorithm mode
* <ul>
* <li><b>HASH</b> simple digest mode (default)</li>
* <li><b>HMAC</b> HMAC algorithm based on GOST R 34.11</li>
* <li><b>KDF</b> Derive bits for KEK deversification</li>
* <li><b>PBKDF2</b> Password based key dirivation algorithms PBKDF2 (based on HMAC)</li>
* <li><b>PFXKDF</b> Password based PFX key dirivation algorithms</li>
* <li><b>CPKDF</b> CpyptoPro Password based key dirivation algorithms</li>
* </ul>
* </li>
* <li><b>sBox</b> Paramset sBox for GOST 28147-89. Used only if version = 1994</li>
* </ul>
*
* Supported algorithms, modes and parameters:
*
* <ul>
* <li>Digest HASH mode (default)</li>
* <li>Sign/Verify HMAC modes parameters depends on version and length
* <ul>
* <li><b>version: 1994</b> HMAC parameters (B = 32, L = 32)</li>
* <li><b>version: 2012, length: 256</b> HMAC parameters (B = 64, L = 32)</li>
* <li><b>version: 2012, length: 512</b> HMAC parameters (B = 64, L = 64)</li>
* </ul>
* </li>
* <li>DeriveBits/DeriveKey KDF mode
* <ul>
* <li><b>context</b> {@link CryptoOperationData} Context of the key derivation</li>
* <li><b>label</b> {@link CryptoOperationData} Label that identifies the purpose for the derived keying material</li>
* </ul>
* </li>
* <li>DeriveBits/DeriveKey PBKDF2 mode
* <ul>
* <li><b>salt</b> {@link CryptoOperationData} Random salt as input for HMAC algorithm</li>
* <li><b>iterations</b> Iteration count. GOST recomended value 1000 (default) or 2000</li>
* <li><b>diversifier</b> Deversifier, ID=1 - key material for performing encryption or decryption, ID=3 - integrity key for MACing</li>
* </ul>
* </li>
* <li>DeriveBits/DeriveKey PFXKDF mode
* <ul>
* <li><b>salt</b> {@link CryptoOperationData} Random salt as input for HMAC algorithm</li>
* <li><b>iterations</b> Iteration count. GOST recomended value 1000 (default) or 2000</li>
* <li><b>diversifier</b> Deversifier, ID=1 - key material for performing encryption or decryption,
* ID=2 - IV (Initial Value) for encryption or decryption, ID=3 - integrity key for MACing</li>
* </ul>
* </li>
* <li>DeriveBits/DeriveKey CPKDF mode
* <ul>
* <li><b>salt</b> {@link CryptoOperationData} Random salt as input for HMAC algorithm</li>
* <li><b>iterations</b> Iteration count. GOST recomended value 1000 (default) or 2000</li>
* </ul>
* </li>
* </ul>
*
* @class GostDigest
* @param {AlgorithmIdentifier} algorithm WebCryptoAPI algorithm identifier
*/
function GostDigest(algorithm) // <editor-fold defaultstate="collapsed">
{
algorithm = algorithm || {};
this.name = (algorithm.name || 'GOST R 34.10') + '-' + ((algorithm.version || 2012) % 100) +
((algorithm.version || 2012) > 1 ? '-' + (algorithm.length || 256) : '') +
(((algorithm.mode || 'HASH') !== 'HASH') ? '-' + algorithm.mode : '') +
(algorithm.procreator ? '/' + algorithm.procreator : '') +
(typeof algorithm.sBox === 'string' ? '/' + algorithm.sBox : '');
// Algorithm procreator
this.procreator = algorithm.procreator;
// Bit length
this.bitLength = algorithm.length || 256;
switch (algorithm.version || 2012) {
case 1: // SHA-1
this.digest = digestSHA1;
this.bitLength = 160;
break;
case 1994:
this.digest = digest94;
// Define chiper algorithm
this.sBox = (algorithm.sBox || (algorithm.procreator === 'SC' ? 'D-SC' : 'D-A')).toUpperCase();
//if (!GostCipher)
// GostCipher = root.GostCipher;
if (!GostCipher)
throw new NotSupportedError('Object GostCipher not found');
this.cipher = new GostCipher({
name: 'GOST 28147',
block: 'ECB',
sBox: this.sBox,
procreator: this.procreator
});
break;
case 2012:
this.digest = digest2012;
break;
default:
throw new NotSupportedError('Algorithm version ' + algorithm.version + ' not supported');
}
// Key size
this.keySize = algorithm.keySize || (algorithm.version <= 2 ? this.bitLength / 8 : 32);
switch (algorithm.mode || 'HASH') {
case 'HASH':
break;
case 'HMAC':
this.sign = signHMAC;
this.verify = verifyHMAC;
this.generateKey = generateKey;
break;
case 'KDF':
this.deriveKey = deriveKey;
this.deriveBits = deriveBitsKDF;
this.label = algorithm.label;
this.context = algorithm.context;
break;
case 'PBKDF2':
this.deriveKey = deriveKey;
this.deriveBits = deriveBitsPBKDF2;
this.generateKey = generateKey;
this.salt = algorithm.salt;
this.iterations = algorithm.iterations || 2000;
this.diversifier = algorithm.diversifier || 1;
break;
case 'PFXKDF':
this.deriveKey = deriveKey;
this.deriveBits = deriveBitsPFXKDF;
this.generateKey = generateKey;
this.salt = algorithm.salt;
this.iterations = algorithm.iterations || 2000;
this.diversifier = algorithm.diversifier || 1;
break;
case 'CPKDF':
this.deriveKey = deriveKey;
this.deriveBits = deriveBitsCP;
this.generateKey = generateKey;
this.salt = algorithm.salt;
this.iterations = algorithm.iterations || 2000;
break;
default:
throw new NotSupportedError('Algorithm mode ' + algorithm.mode + ' not supported');
}
} // </editor-fold>
export default GostDigest;